Updated libopenmpt to version 0.3.6.

This commit is contained in:
Christopher Snowhill 2018-02-18 20:25:43 -08:00
parent 200701a2a3
commit f9d9a492ad
338 changed files with 157732 additions and 4 deletions

3
.gitmodules vendored
View file

@ -4,9 +4,6 @@
[submodule "Frameworks/mGBA/mGBA/mgba"]
path = Frameworks/mGBA/mGBA/mgba
url = https://github.com/kode54/mgba.git
[submodule "Frameworks/OpenMPT/OpenMPT"]
path = Frameworks/OpenMPT/OpenMPT
url = https://gitlab.kode54.net/kode54/OpenMPT.git
[submodule "Frameworks/AdPlug/AdPlug/adplug"]
path = Frameworks/AdPlug/AdPlug/adplug
url = https://github.com/adplug/adplug.git

@ -1 +0,0 @@
Subproject commit bf3d5ea380157da7aa0b7bc58c3d889ac7e31b37

View file

@ -0,0 +1,27 @@
The OpenMPT code is licensed under the BSD license.
Copyright (c) 2004-2018, OpenMPT contributors
Copyright (c) 1997-2003, Olivier Lapicque
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the OpenMPT project nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,370 @@
README
======
OpenMPT and libopenmpt
======================
How to compile
--------------
### OpenMPT
- Supported Visual Studio versions:
- Visual Studio 2015 Update 3 Community/Professional/Enterprise
To compile the project, open `build/vs2015/OpenMPT.sln` and hit the
compile button.
- Visual Studio 2017 Community/Professional/Enterprise
To compile the project, open `build/vs2017/OpenMPT.sln` and hit the
compile button.
- The Windows 8.1 SDK and Microsoft Foundation Classes (MFC) are required to
build OpenMPT (both are included with Visual Studio, however may need to be
selected explicitly during setup). In order to build OpenMPT for Windows XP,
the XP targetting toolset also needs to be installed.
- The VST and ASIO SDKs are needed for compiling with VST and ASIO support.
If you don't want this, uncomment `#define NO_VST` and comment out
`#define MPT_WITH_ASIO` in the file `common/BuildSettings.h`.
The ASIO and VST SDKs can be downloaded automatically on Windows 7 or later
with 7-Zip installed by just running the `build/download_externals.cmd`
script.
If you do not want to or cannot use this script, you may follow these manual
steps instead:
- ASIO:
If you use `#define MPT_WITH_ASIO`, you will need to put the ASIO SDK in
the `include/ASIOSDK2` folder. The top level directory of the SDK is
already named `ASIOSDK2`, so simply move that directory in the include
folder.
Please visit
[steinberg.net](http://www.steinberg.net/en/company/developers.html) to
download the SDK.
- VST:
If you don't use `#define NO_VST`, you will need to put the VST SDK in
the `include/vstsdk2.4` folder.
Simply copy all files from the `VST3 SDK` folder in the SDK .zip file to
`include/vstsdk2.4/`.
Note: OpenMPT makes use of the VST 2.4 specification only. The VST3 SDK
still contains all necessary files in the right locations. If you still
have the old VST 2.4 SDK laying around, this should also work fine.
Please visit
[steinberg.net](http://www.steinberg.net/en/company/developers.html) to
download the SDK.
If you need further help with the VST and ASIO SDKs, get in touch with the
main OpenMPT developers.
- 7-Zip is required to be installed in the default path in order to build the
required files for OpenMPT Wine integration.
Please visit [7-zip.org](http://www.7-zip.org/) to download 7-Zip.
### libopenmpt and openmpt123
For detailed requirements, see `libopenmpt/dox/quickstart.md`.
- Autotools
Grab a `libopenmpt-VERSION-autotools.tar.gz` tarball.
./configure
make
make check
sudo make install
- Visual Studio:
- You will find solutions for Visual Studio 2015 to 2017 in the
corresponding `build/vsVERSION/` folder.
Projects that target Windows versions before Windows 7 are available in
`build/vsVERSIONxp/`
Most projects are supported with any of the mentioned Visual Studio
verions, with the following exceptions:
- in_openmpt: Requires Visual Studio with MFC.
- xmp-openmpt: Requires Visual Studio with MFC.
- You will need the Winamp 5 SDK and the XMPlay SDK if you want to
compile the plugins for these 2 players. They can be downloaded
automatically on Windows 7 or later with 7-Zip installed by just running
the `build/download_externals.cmd` script.
If you do not want to or cannot use this script, you may follow these
manual steps instead:
- Winamp 5 SDK:
To build libopenmpt as a winamp input plugin, copy the contents of
`WA5.55_SDK.exe` to include/winamp/.
Please visit
[winamp.com](http://wiki.winamp.com/wiki/Plug-in_Developer) to
download the SDK.
You can disable in_openmpt in the solution configuration.
- XMPlay SDK:
To build libopenmpt with XMPlay input plugin support, copy the
contents of xmp-sdk.zip into include/xmplay/.
Please visit [un4seen.com](http://www.un4seen.com/xmplay.html) to
download the SDK.
You can disable xmp-openmpt in the solution configuration.
- Makefile
The makefile supports different build environments and targets via the
`CONFIG=` parameter directly to the make invocation.
Use `make CONFIG=$newconfig clean` when switching between different configs
because the makefile cleans only intermediates and target that are active
for the current config and no configuration state is kept around across
invocations.
- mingw-w64:
The required version is at least 4.8.
make CONFIG=mingw64-win32 # for win32
make CONFIG=mingw64-win64 # for win64
- gcc or clang (on Unix-like systems, including Mac OS X with MacPorts):
The minimum required compiler versions are:
- gcc 4.8
- clang 3.4
The Makefile requires pkg-config for native builds.
For sound output in openmpt123, PortAudio or SDL is required.
openmpt123 can optionally use libflac and libsndfile to render PCM
files to disk.
When using gcc, run:
make CONFIG=gcc
When using clang, it is recommended to do:
make CONFIG=clang
Otherwise, simply run
make
which will try to guess the compiler based on your operating system.
- emscripten (on Unix-like systems):
libopenmpt has been tested and verified to work with emscripten 1.31 or
later (earlier versions might or might not work).
Run:
make CONFIG=emscripten
Running the test suite on the command line is also supported by using
node.js. Version 0.10.25 or greater has been tested. Earlier versions
might or might not work. Depending on how your distribution calls the
`node.js` binary, you might have to edit
`build/make/config-emscripten.mk`.
- Haiku:
To compile libopenmpt on Haiku (using the 32-bit gcc2h), run:
make CONFIG=haiku
- American Fuzzy Lop:
To compile libopenmpt with fuzzing instrumentation for afl-fuzz, run:
make CONFIG=afl
For more detailed instructions, read contrib/fuzzing/readme.md
- other compilers:
To compiler libopenmpt with other C++11 compliant compilers, run:
make CONFIG=generic
The `Makefile` supports some customizations. You might want to read the top
which should get you some possible make settings, like e.g.
`make DYNLINK=0` or similar. Cross compiling or different compiler would
best be implemented via new `config-*.mk` files.
The `Makefile` also supports building doxygen documentation by using
make doc
Binaries and documentation can be installed systen-wide with
make PREFIX=/yourprefix install
make PREFIX=/yourprefix install-doc
Some systems (i.e. Linux) require running
sudo ldconfig
in order for the system linker to be able to pick up newly installed
libraries.
`PREFIX` defaults to `/usr/local`. A `DESTDIR=` parameter is also
supported.
- Android NDK
See `build/android_ndk/README.AndroidNDK.txt`.
Coding conventions
------------------
### OpenMPT
(see below for an example)
- Place curly braces at the beginning of the line, not at the end
- Generally make use of the custom index types like `SAMPLEINDEX` or
`ORDERINDEX` when referring to samples, orders, etc.
- When changing playback behaviour, make sure that you use the function
`CSoundFile::IsCompatibleMode()` so that modules made with previous versions
of MPT still sound correct (if the change is extremely small, this might be
unnecessary)
- `CamelCase` function and variable names are preferred.
#### OpenMPT code example
~~~~{.cpp}
void Foo::Bar(int foobar)
{
while(true)
{
// some code
}
}
~~~~
### libopenmpt
**Note:**
**This applies to `libopenmpt/` and `openmpt123/` directories only.**
**Use OpenMPT style (see above) otherwise.**
The code generally tries to follow these conventions, but they are not
strictly enforced and there are valid reasons to diverge from these
conventions. Using common sense is recommended.
- In general, the most important thing is to keep style consistent with
directly surrounding code.
- Use C++ std types when possible, prefer `std::size_t` and `std::int32_t`
over `long` or `int`. Do not use C99 std types (e.g. no pure `int32_t`)
- Qualify namespaces explicitly, do not use `using`.
Members of `namespace openmpt` can be named without full namespace
qualification.
- Prefer the C++ version in `namespace std` if the same functionality is
provided by the C standard library as well. Also, include the C++
version of C standard library headers (e.g. use `<cstdio>` instead of
`<stdio.h>`.
- Do not use ANY locale-dependant C functions. For locale-dependant C++
functionaly (especially iostream), always imbue the
`std::locale::classic()` locale.
- Prefer kernel_style_names over CamelCaseNames.
- If a folder (or one of its parent folders) contains .clang-format,
use clang-format v3.5 for indenting C++ and C files, otherwise:
- `{` are placed at the end of the opening line.
- Enclose even single statements in curly braces.
- Avoid placing single statements on the same line as the `if`.
- Opening parentheses are separated from keywords with a space.
- Opening parentheses are not separated from function names.
- Place spaces around operators and inside parentheses.
- Align `:` and `,` when inheriting or initializing members in a
constructor.
- The pointer `*` is separated from both the type and the variable name.
- Use tabs for identation, spaces for formatting.
Tabs should only appear at the very beginning of a line.
Do not assume any particular width of the TAB character. If width is
important for formatting reasons, use spaces.
- Use empty lines at will.
- API documentation is done with doxygen.
Use general C doxygen for the C API.
Use QT-style doxygen for the C++ API.
#### libopenmpt indentation example
~~~~{.cpp}
namespace openmpt {
// This is totally meaningless code and just illustrates indentation.
class foo
: public base
, public otherbase
{
private:
std::int32_t x;
std::int16_t y;
public:
foo()
: x(0)
, y(-1)
{
return;
}
int bar() const;
}; // class foo
int foo::bar() const {
for ( int i = 0; i < 23; ++i ) {
switch ( x ) {
case 2:
something( y );
break;
default:
something( ( y - 1 ) * 2 );
break;
}
}
if ( x == 12 ) {
return -1;
} else if ( x == 42 ) {
return 1;
}
return 42;
}
} // namespace openmpt
~~~~

View file

@ -0,0 +1,736 @@
/*
* BuildSettings.h
* ---------------
* Purpose: Global, user settable compile time flags (and some global system header configuration)
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "CompilerDetect.h"
// set windows version early so that we can deduce dependencies from SDK version
#if MPT_OS_WINDOWS
#if defined(MPT_BUILD_MSVC)
#if defined(MPT_BUILD_TARGET_XP)
#if defined(_M_X64)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0502 // _WIN32_WINNT_WS03
#endif
#else // !_M_X64
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP
#endif
#endif // _M_X64
#else // MPT_BUILD_TARGET
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
#endif
#endif // MPT_BUILD_TARGET
#else // !MPT_BUILD_MSVC
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // _WIN32_WINNT_WINXP
#endif
#endif // MPT_BUILD_MSVC
#ifndef WINVER
#define WINVER _WIN32_WINNT
#endif
#endif // MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && defined(LIBOPENMPT_BUILD)
#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined"
#elif defined(MODPLUG_TRACKER)
// nothing
#elif defined(LIBOPENMPT_BUILD)
// nothing
#else
#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined"
#endif // MODPLUG_TRACKER || LIBOPENMPT_BUILD
// wrapper for autoconf macros
#if defined(HAVE_CONFIG_H)
#include "config.h"
// Fixup dependencies which are currently not used in libopenmpt itself
#ifdef MPT_WITH_FLAC
#undef MPT_WITH_FLAC
#endif
#endif // HAVE_CONFIG_H
// Dependencies from the MSVC build system
#if defined(MPT_BUILD_MSVC)
// This section defines which dependencies are available when building with
// MSVC. Other build systems provide MPT_WITH_* macros via command-line or other
// means.
// OpenMPT and libopenmpt should compile and run successfully (albeit with
// reduced functionality) with any or all dependencies missing/disabled.
// The defaults match the bundled third-party libraries with the addition of
// ASIO and VST SDKs.
#if defined(MODPLUG_TRACKER)
// OpenMPT-only dependencies
#define MPT_WITH_ASIO
#define MPT_WITH_DSOUND
#define MPT_WITH_LHASA
#define MPT_WITH_MINIZIP
#define MPT_WITH_OPUS
#define MPT_WITH_OPUSENC
#define MPT_WITH_OPUSFILE
#define MPT_WITH_PICOJSON
#define MPT_WITH_PORTAUDIO
//#define MPT_WITH_PULSEAUDIO
//#define MPT_WITH_PULSEAUDIOSIMPLE
#define MPT_WITH_SMBPITCHSHIFT
#define MPT_WITH_UNRAR
#define MPT_WITH_VORBISENC
// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples)
//#define MPT_WITH_DL
#define MPT_WITH_FLAC
//#define MPT_WITH_ICONV
//#define MPT_WITH_LTDL
#if MPT_OS_WINDOWS
#if (_WIN32_WINNT >= 0x0601)
#define MPT_WITH_MEDIAFOUNDATION
#endif
#endif
//#define MPT_WITH_MINIMP3
//#define MPT_WITH_MINIZ
#define MPT_WITH_MPG123
#define MPT_WITH_OGG
//#define MPT_WITH_STBVORBIS
#define MPT_WITH_VORBIS
#define MPT_WITH_VORBISFILE
#define MPT_WITH_ZLIB
#endif // MODPLUG_TRACKER
#if defined(LIBOPENMPT_BUILD)
// OpenMPT and libopenmpt dependencies (not for openmp123, player plugins or examples)
#if defined(LIBOPENMPT_BUILD_FULL) && defined(LIBOPENMPT_BUILD_SMALL)
#error "only one of LIBOPENMPT_BUILD_FULL or LIBOPENMPT_BUILD_SMALL can be defined"
#endif // LIBOPENMPT_BUILD_FULL && LIBOPENMPT_BUILD_SMALL
#if defined(LIBOPENMPT_BUILD_FULL)
//#define MPT_WITH_DL
//#define MPT_WITH_FLAC
//#define MPT_WITH_ICONV
//#define MPT_WITH_LTDL
#if MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT >= 0x0601)
#define MPT_WITH_MEDIAFOUNDATION
#endif
#endif
//#define MPT_WITH_MINIMP3
//#define MPT_WITH_MINIZ
#define MPT_WITH_MPG123
#define MPT_WITH_OGG
//#define MPT_WITH_STBVORBIS
#define MPT_WITH_VORBIS
#define MPT_WITH_VORBISFILE
#define MPT_WITH_ZLIB
#elif defined(LIBOPENMPT_BUILD_SMALL)
//#define MPT_WITH_DL
//#define MPT_WITH_FLAC
//#define MPT_WITH_ICONV
//#define MPT_WITH_LTDL
//#define MPT_WITH_MEDIAFOUNDATION
#define MPT_WITH_MINIMP3
#define MPT_WITH_MINIZ
//#define MPT_WITH_MPG123
//#define MPT_WITH_OGG
#define MPT_WITH_STBVORBIS
//#define MPT_WITH_VORBIS
//#define MPT_WITH_VORBISFILE
//#define MPT_WITH_ZLIB
#else // !LIBOPENMPT_BUILD_SMALL
//#define MPT_WITH_DL
//#define MPT_WITH_FLAC
//#define MPT_WITH_ICONV
//#define MPT_WITH_LTDL
//#define MPT_WITH_MEDIAFOUNDATION
//#define MPT_WITH_MINIMP3
//#define MPT_WITH_MINIZ
#define MPT_WITH_MPG123
#define MPT_WITH_OGG
//#define MPT_WITH_STBVORBIS
#define MPT_WITH_VORBIS
#define MPT_WITH_VORBISFILE
#define MPT_WITH_ZLIB
#endif // LIBOPENMPT_BUILD_SMALL
#endif // LIBOPENMPT_BUILD
#endif // MPT_BUILD_MSVC
#if defined(MODPLUG_TRACKER)
// Enable built-in test suite.
#ifdef _DEBUG
#define ENABLE_TESTS
#endif
// Disable any file saving functionality (not really useful except for the player library)
//#define MODPLUG_NO_FILESAVE
// Disable any debug logging
//#define NO_LOGGING
#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT)
#define MPT_LOG_GLOBAL_LEVEL_STATIC
#define MPT_LOG_GLOBAL_LEVEL 0
#endif
// Disable all runtime asserts
#if !defined(_DEBUG) && !defined(MPT_BUILD_WINESUPPORT)
#define NO_ASSERTS
#endif
// Enable std::istream support in class FileReader (this is generally not needed for the tracker, local files can easily be mmapped as they have been before introducing std::istream support)
//#define MPT_FILEREADER_STD_ISTREAM
// Enable callback stream wrapper for FileReader (required by libopenmpt C API).
//#define MPT_FILEREADER_CALLBACK_STREAM
// Support for externally linked samples e.g. in MPTM files
#define MPT_EXTERNAL_SAMPLES
// Support mpt::ChartsetLocale
#define MPT_ENABLE_CHARSET_LOCALE
// Use inline assembly
#define ENABLE_ASM
// Disable unarchiving support
//#define NO_ARCHIVE_SUPPORT
// Disable the built-in reverb effect
//#define NO_REVERB
// Disable built-in miscellaneous DSP effects (surround, mega bass, noise reduction)
//#define NO_DSP
// Disable the built-in equalizer.
//#define NO_EQ
// Disable the built-in automatic gain control
//#define NO_AGC
// Define to build without VST plugin support; makes build possible without VST SDK.
//#define NO_VST
// Define to build without DMO plugin support
//#define NO_DMO
// (HACK) Define to build without any plugin support
//#define NO_PLUGINS
// Do not build libopenmpt C api
#define NO_LIBOPENMPT_C
// Do not build libopenmpt C++ api
#define NO_LIBOPENMPT_CXX
#endif // MODPLUG_TRACKER
#if defined(LIBOPENMPT_BUILD)
#if (defined(_DEBUG) || defined(DEBUG)) && !defined(MPT_BUILD_DEBUG)
#define MPT_BUILD_DEBUG
#endif
#if defined(LIBOPENMPT_BUILD_TEST)
#define ENABLE_TESTS
#else
#define MODPLUG_NO_FILESAVE
#endif
#if defined(MPT_BUILD_ANALZYED) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS)
// enable asserts
#else
#define NO_ASSERTS
#endif
//#define NO_LOGGING
#define MPT_FILEREADER_STD_ISTREAM
#define MPT_FILEREADER_CALLBACK_STREAM
//#define MPT_EXTERNAL_SAMPLES
#if defined(ENABLE_TESTS) || defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT)
#define MPT_ENABLE_CHARSET_LOCALE
#else
//#define MPT_ENABLE_CHARSET_LOCALE
#endif
// Do not use inline asm in library builds. There is just about no codepath which would use it anyway.
//#define ENABLE_ASM
#if defined(MPT_BUILD_HACK_ARCHIVE_SUPPORT)
//#define NO_ARCHIVE_SUPPORT
#else
#define NO_ARCHIVE_SUPPORT
#endif
//#define NO_REVERB
#define NO_DSP
#define NO_EQ
#define NO_AGC
#define NO_VST
//#if !MPT_OS_WINDOWS || MPT_OS_WINDOWS_WINRT || !MPT_COMPILER_MSVC || !defined(LIBOPENMPT_BUILD_FULL)
#define NO_DMO
//#endif
//#define NO_PLUGINS
//#define NO_LIBOPENMPT_C
//#define NO_LIBOPENMPT_CXX
#endif // LIBOPENMPT_BUILD
#if MPT_OS_WINDOWS
#define MPT_CHARSET_WIN32
#elif MPT_OS_LINUX
#define MPT_CHARSET_ICONV
#elif MPT_OS_ANDROID
#define MPT_CHARSET_INTERNAL
#elif MPT_OS_EMSCRIPTEN
#define MPT_CHARSET_INTERNAL
#ifndef MPT_LOCALE_ASSUME_CHARSET
#define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8
#endif
#elif MPT_OS_MACOSX_OR_IOS
#if defined(MPT_WITH_ICONV)
#define MPT_CHARSET_ICONV
#ifndef MPT_ICONV_NO_WCHAR
#define MPT_ICONV_NO_WCHAR
#endif
#else
#define MPT_CHARSET_INTERNAL
#endif
//#ifndef MPT_LOCALE_ASSUME_CHARSET
//#define MPT_LOCALE_ASSUME_CHARSET CharsetUTF8
//#endif
#elif defined(MPT_WITH_ICONV)
#define MPT_CHARSET_ICONV
#endif
#if MPT_COMPILER_MSVC
// Use wide strings for MSVC because this is the native encoding on
// microsoft platforms.
#define MPT_USTRING_MODE_WIDE 1
#define MPT_USTRING_MODE_UTF8 0
#else // !MPT_COMPILER_MSVC
#define MPT_USTRING_MODE_WIDE 0
#define MPT_USTRING_MODE_UTF8 1
#endif // MPT_COMPILER_MSVC
#if MPT_USTRING_MODE_UTF8
// MPT_USTRING_MODE_UTF8 mpt::ustring is implemented via mpt::u8string
#define MPT_ENABLE_U8STRING 1
#else
#define MPT_ENABLE_U8STRING 0
#endif
#if defined(MODPLUG_TRACKER) || MPT_USTRING_MODE_WIDE
// mpt::ToWString, mpt::wfmt, ConvertStrTo<std::wstring>
// Required by the tracker to ease interfacing with WinAPI.
// Required by MPT_USTRING_MODE_WIDE to ease type tunneling in mpt::format.
#define MPT_WSTRING_FORMAT 1
#else
#define MPT_WSTRING_FORMAT 0
#endif
#if MPT_OS_WINDOWS || MPT_USTRING_MODE_WIDE || MPT_WSTRING_FORMAT
// mpt::ToWide
// Required on Windows by mpt::PathString.
// Required by MPT_USTRING_MODE_WIDE as they share the conversion functions.
// Required by MPT_WSTRING_FORMAT because of std::string<->std::wstring conversion in mpt::ToString and mpt::ToWString.
#define MPT_WSTRING_CONVERT 1
#else
#define MPT_WSTRING_CONVERT 0
#endif
// fixing stuff up
#if defined(MPT_BUILD_TARGET_XP)
// Also support Wine 1.6 in addition to Windows XP
#ifndef MPT_QUIRK_NO_CPP_THREAD
#define MPT_QUIRK_NO_CPP_THREAD
#endif
#endif
#if defined(MPT_BUILD_ANALYZED) || defined(MPT_BUILD_CHECKED)
#ifdef NO_ASSERTS
#undef NO_ASSERTS // static or dynamic analyzers want assertions on
#endif
#endif
#if defined(MPT_BUILD_FUZZER)
#ifndef MPT_FUZZ_TRACKER
#define MPT_FUZZ_TRACKER
#endif
#endif
#if !MPT_COMPILER_MSVC && defined(ENABLE_ASM)
#undef ENABLE_ASM // inline assembly requires MSVC compiler
#endif
#if defined(ENABLE_ASM)
#if MPT_COMPILER_MSVC && defined(_M_IX86)
// Generate general x86 inline assembly / intrinsics.
#define ENABLE_X86
// Generate inline assembly using MMX instructions (only used when the CPU supports it).
#define ENABLE_MMX
// Generate inline assembly using SSE instructions (only used when the CPU supports it).
#define ENABLE_SSE
// Generate inline assembly using SSE2 instructions (only used when the CPU supports it).
#define ENABLE_SSE2
// Generate inline assembly using SSE3 instructions (only used when the CPU supports it).
#define ENABLE_SSE3
// Generate inline assembly using SSE4 instructions (only used when the CPU supports it).
#define ENABLE_SSE4
// Generate inline assembly using AMD specific instruction set extensions (only used when the CPU supports it).
#define ENABLE_X86_AMD
#elif MPT_COMPILER_MSVC && defined(_M_X64)
// Generate general x64 inline assembly / intrinsics.
#define ENABLE_X64
// Generate inline assembly using SSE2 instructions (only used when the CPU supports it).
#define ENABLE_SSE2
// Generate inline assembly using SSE3 instructions (only used when the CPU supports it).
#define ENABLE_SSE3
// Generate inline assembly using SSE4 instructions (only used when the CPU supports it).
#define ENABLE_SSE4
#endif // arch
#endif // ENABLE_ASM
#if defined(MPT_WITH_MPG123) && defined(MPT_BUILD_MSVC) && defined(MPT_BUILD_MSVC_STATIC) && !MPT_OS_WINDOWS_WINRT
#define MPT_ENABLE_MPG123_DELAYLOAD
#endif
#if defined(ENABLE_TESTS) && defined(MODPLUG_NO_FILESAVE)
#undef MODPLUG_NO_FILESAVE // tests recommend file saving
#endif
#if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZ)
// Only one deflate implementation should be used. Prefer zlib.
#undef MPT_WITH_MINIZ
#endif
#if !MPT_OS_WINDOWS && defined(MPT_WITH_MEDIAFOUNDATION)
#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires Windows
#endif
#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_TEMPFILE)
#define MPT_ENABLE_TEMPFILE
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_TEMPFILE)
#define MPT_ENABLE_TEMPFILE
#endif
#if !defined(MPT_CHARSET_WIN32) && !defined(MPT_CHARSET_ICONV) && !defined(MPT_CHARSET_CODECVTUTF8) && !defined(MPT_CHARSET_INTERNAL)
#define MPT_CHARSET_INTERNAL
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_DYNBIND)
#define MPT_ENABLE_DYNBIND // Tracker requires dynamic library loading for export codecs
#endif
#if defined(MPT_ENABLE_MPG123_DELAYLOAD) && !defined(MPT_ENABLE_DYNBIND)
#define MPT_ENABLE_DYNBIND // static MSVC builds require dynbind to load delay-loaded DLLs
#endif
#if defined(MPT_WITH_MEDIAFOUNDATION) && !defined(MPT_ENABLE_DYNBIND)
#define MPT_ENABLE_DYNBIND // MediaFoundation needs dynamic loading in order to test availability of delay loaded libs
#endif
#if (defined(MPT_WITH_MPG123) || defined(MPT_WITH_MINIMP3)) && !defined(MPT_ENABLE_MP3_SAMPLES)
#define MPT_ENABLE_MP3_SAMPLES
#endif
#if defined(ENABLE_TESTS)
#define MPT_ENABLE_FILEIO // Test suite requires PathString for file loading.
#endif
#if !MPT_OS_WINDOWS && !defined(MPT_FILEREADER_STD_ISTREAM)
#define MPT_FILEREADER_STD_ISTREAM // MMAP is only supported on Windows
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_FILEIO)
#define MPT_ENABLE_FILEIO // Tracker requires disk file io
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_ENABLE_THREAD)
#define MPT_ENABLE_THREAD // Tracker requires threads
#endif
#if defined(MPT_EXTERNAL_SAMPLES) && !defined(MPT_ENABLE_FILEIO)
#define MPT_ENABLE_FILEIO // External samples require disk file io
#endif
#if !defined(MODPLUG_NO_FILESAVE) && !defined(MPT_ENABLE_FILEIO_STDIO)
#define MPT_ENABLE_FILEIO_STDIO // file saving requires FILE*
#endif
#if defined(NO_PLUGINS)
// Any plugin type requires NO_PLUGINS to not be defined.
#define NO_VST
#define NO_DMO
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT) && !defined(MPT_BUILD_WINESUPPORT_WRAPPER)
#ifndef MPT_NO_NAMESPACE
#define MPT_NO_NAMESPACE
#endif
#endif
#if defined(MPT_NO_NAMESPACE)
#ifdef OPENMPT_NAMESPACE
#undef OPENMPT_NAMESPACE
#endif
#define OPENMPT_NAMESPACE
#ifdef OPENMPT_NAMESPACE_BEGIN
#undef OPENMPT_NAMESPACE_BEGIN
#endif
#define OPENMPT_NAMESPACE_BEGIN
#ifdef OPENMPT_NAMESPACE_END
#undef OPENMPT_NAMESPACE_END
#endif
#define OPENMPT_NAMESPACE_END
#else
#ifndef OPENMPT_NAMESPACE
#define OPENMPT_NAMESPACE OpenMPT
#endif
#ifndef OPENMPT_NAMESPACE_BEGIN
#define OPENMPT_NAMESPACE_BEGIN namespace OPENMPT_NAMESPACE {
#endif
#ifndef OPENMPT_NAMESPACE_END
#define OPENMPT_NAMESPACE_END }
#endif
#endif
// platform configuration
#if MPT_OS_WINDOWS
#define WIN32_LEAN_AND_MEAN
// windows.h excludes
#define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
#define NOMINMAX // Macros min(a,b) and max(a,b)
#define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
#define NOCOMM // COMM driver routines
#define NOKANJI // Kanji support stuff.
#define NOPROFILER // Profiler interface.
#define NOMCX // Modem Configuration Extensions
// mmsystem.h excludes
#define MMNODRV
//#define MMNOSOUND
//#define MMNOWAVE
//#define MMNOMIDI
#define MMNOAUX
#define MMNOMIXER
//#define MMNOTIMER
#define MMNOJOY
#define MMNOMCI
//#define MMNOMMIO
//#define MMNOMMSYSTEM
// mmreg.h excludes
#define NOMMIDS
//#define NONEWWAVE
#define NONEWRIFF
#define NOJPEGDIB
#define NONEWIC
#define NOBITMAP
#endif // MPT_OS_WINDOWS
// stdlib configuration
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#define _USE_MATH_DEFINES
#if !MPT_OS_ANDROID
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#endif // !MPT_OS_ANDROID
// compiler configuration
#if MPT_COMPILER_MSVC
#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS // Define to disable the "This function or variable may be unsafe" warnings.
#endif
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT 1
#ifndef _SCL_SECURE_NO_WARNINGS
#define _SCL_SECURE_NO_WARNINGS
#endif
#ifndef NO_WARN_MBCS_MFC_DEPRECATION
#define NO_WARN_MBCS_MFC_DEPRECATION
#endif
#pragma warning(disable:4355) // 'this' : used in base member initializer list
// happens for immutable classes (i.e. classes containing const members)
#pragma warning(disable:4512) // assignment operator could not be generated
#pragma warning(error:4309) // Treat "truncation of constant value"-warning as error.
#ifdef MPT_BUILD_ANALYZED
// Disable Visual Studio static analyzer warnings that generate too many false positives in VS2010.
//#pragma warning(disable:6246)
//#pragma warning(disable:6262)
#pragma warning(disable:6326) // Potential comparison of a constant with another constant
//#pragma warning(disable:6385)
//#pragma warning(disable:6386)
#endif // MPT_BUILD_ANALYZED
#endif // MPT_COMPILER_MSVC
#if MPT_COMPILER_MSVCCLANGC2
#if MPT_OS_WINDOWS
// As Clang defines __STDC__ 1, Windows headers will use named union fields. The MediaFoundation headers do not support this, though.
// Clang supports nameless union fields just fine, and luckily there is a way to override the Windows headers behaviour.
#define _FORCENAMELESSUNION
#endif // MPT_OS_WINDOWS
#endif // MPT_COMPILER_MSVCCLANGC2
// third-party library configuration
#ifdef MPT_WITH_FLAC
#ifdef MPT_BUILD_MSVC_STATIC
#define FLAC__NO_DLL
#endif
#endif
#ifdef MPT_WITH_PICOJSON
#define PICOJSON_USE_INT64
#endif
#ifdef MPT_WITH_SMBPITCHSHIFT
#ifdef MPT_BUILD_MSVC_SHARED
#define SMBPITCHSHIFT_USE_DLL
#endif
#endif
#ifdef MPT_WITH_STBVORBIS
#define STB_VORBIS_HEADER_ONLY
#ifndef STB_VORBIS_NO_PULLDATA_API
#define STB_VORBIS_NO_PULLDATA_API
#endif
#ifndef STB_VORBIS_NO_STDIO
#define STB_VORBIS_NO_STDIO
#endif
#endif
#ifdef MPT_WITH_VORBISFILE
#ifndef OV_EXCLUDE_STATIC_CALLBACKS
#define OV_EXCLUDE_STATIC_CALLBACKS
#endif
#endif
#ifdef MPT_WITH_ZLIB
#ifdef MPT_BUILD_MSVC_SHARED
#define ZLIB_DLL
#endif
#endif

View file

@ -0,0 +1,367 @@
/*
* CompilerDetect.h
* ----------------
* Purpose: Detect current compiler and provide readable version test macros.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#define MPT_COMPILER_MAKE_VERSION2(version,sp) ((version) * 100 + (sp))
#define MPT_COMPILER_MAKE_VERSION3(major,minor,patch) ((major) * 10000 + (minor) * 100 + (patch))
#define MPT_COMPILER_MAKE_VERSION3_BUILD(major,minor,build) ((major) * 10000000 + (minor) * 100000 + (patch))
#if defined(MPT_COMPILER_GENERIC)
#undef MPT_COMPILER_GENERIC
#define MPT_COMPILER_GENERIC 1
#elif defined(__clang__) && defined(_MSC_VER) && defined(__c2__)
#define MPT_COMPILER_MSVCCLANGC2 1
#define MPT_COMPILER_MSVCCLANGC2_VERSION (__c2_version__)
#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION >= MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build)))
#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) (MPT_COMPILER_MSVCCLANGC2_VERSION < MPT_COMPILER_MAKE_VERSION3_BUILD((major),(minor),(build)))
#elif defined(__clang__)
#define MPT_COMPILER_CLANG 1
#define MPT_COMPILER_CLANG_VERSION MPT_COMPILER_MAKE_VERSION3(__clang_major__,__clang_minor__,__clang_patchlevel__)
#define MPT_CLANG_AT_LEAST(major,minor,patch) (MPT_COMPILER_CLANG_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch)))
#define MPT_CLANG_BEFORE(major,minor,patch) (MPT_COMPILER_CLANG_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch)))
#if MPT_CLANG_BEFORE(3,4,0)
#error "clang version 3.4 required"
#endif
#if defined(__clang_analyzer__)
#ifndef MPT_BUILD_ANALYZED
#define MPT_BUILD_ANALYZED
#endif
#endif
#elif defined(__GNUC__)
#define MPT_COMPILER_GCC 1
#define MPT_COMPILER_GCC_VERSION MPT_COMPILER_MAKE_VERSION3(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
#define MPT_GCC_AT_LEAST(major,minor,patch) (MPT_COMPILER_GCC_VERSION >= MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch)))
#define MPT_GCC_BEFORE(major,minor,patch) (MPT_COMPILER_GCC_VERSION < MPT_COMPILER_MAKE_VERSION3((major),(minor),(patch)))
#if MPT_GCC_BEFORE(4,8,0)
#error "GCC version 4.8 required"
#endif
#elif defined(_MSC_VER)
#define MPT_COMPILER_MSVC 1
#if (_MSC_VER >= 1912)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,5)
#elif (_MSC_VER >= 1911)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,3)
#elif (_MSC_VER >= 1910)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2017,0)
#elif (_MSC_VER >= 1900)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2015,0)
#elif (_MSC_VER >= 1800)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2013,0)
#elif (_MSC_VER >= 1700)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2012,0)
#elif (_MSC_VER >= 1600)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2010,0)
#elif (_MSC_VER >= 1500)
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2008,0)
#else
#define MPT_COMPILER_MSVC_VERSION MPT_COMPILER_MAKE_VERSION2(2005,0)
#endif
#define MPT_MSVC_AT_LEAST(version,sp) (MPT_COMPILER_MSVC_VERSION >= MPT_COMPILER_MAKE_VERSION2((version),(sp)))
#define MPT_MSVC_BEFORE(version,sp) (MPT_COMPILER_MSVC_VERSION < MPT_COMPILER_MAKE_VERSION2((version),(sp)))
#if MPT_MSVC_BEFORE(2015,0)
#error "MSVC version 2015 required"
#endif
#if defined(_PREFAST_)
#ifndef MPT_BUILD_ANALYZED
#define MPT_BUILD_ANALYZED
#endif
#endif
#else
#define MPT_COMPILER_GENERIC 1
#endif
#ifndef MPT_COMPILER_GENERIC
#define MPT_COMPILER_GENERIC 0
#endif
#ifndef MPT_COMPILER_MSVCCLANGC2
#define MPT_COMPILER_MSVCCLANGC2 0
#define MPT_MSVCCLANGC2_AT_LEAST(major,minor,build) 0
#define MPT_MSVCCLANGC2_BEFORE(major,minor,build) 0
#endif
#ifndef MPT_COMPILER_CLANG
#define MPT_COMPILER_CLANG 0
#define MPT_CLANG_AT_LEAST(major,minor,patch) 0
#define MPT_CLANG_BEFORE(major,minor,patch) 0
#endif
#ifndef MPT_COMPILER_GCC
#define MPT_COMPILER_GCC 0
#define MPT_GCC_AT_LEAST(major,minor,patch) 0
#define MPT_GCC_BEFORE(major,minor,patch) 0
#endif
#ifndef MPT_COMPILER_MSVC
#define MPT_COMPILER_MSVC 0
#define MPT_MSVC_AT_LEAST(version,sp) 0
#define MPT_MSVC_BEFORE(version,sp) 0
#endif
#if MPT_COMPILER_GENERIC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#if (__cplusplus >= 201703)
#define MPT_CXX 17
#elif (__cplusplus >= 201402)
#define MPT_CXX 14
#else
#define MPT_CXX 11
#endif
#elif MPT_COMPILER_MSVC
#if MPT_MSVC_AT_LEAST(2017,0)
#if (_MSVC_LANG >= 201402)
#define MPT_CXX 14
#else
#define MPT_CXX 11
#endif
#else
#define MPT_CXX 11
#endif
#else
#define MPT_CXX 11
#endif
// MPT_CXX is stricter than just using __cplusplus directly.
// We will only claim a language version as supported IFF all core language and
// library fatures that we need are actually supported AND working correctly
// (to our needs).
#define MPT_CXX_AT_LEAST(version) (MPT_CXX >= (version))
#define MPT_CXX_BEFORE(version) (MPT_CXX < (version))
#if MPT_COMPILER_MSVC
#define MPT_PLATFORM_LITTLE_ENDIAN
#elif MPT_COMPILER_GCC
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define MPT_PLATFORM_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#define MPT_PLATFORM_BIG_ENDIAN
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#endif
// fallback:
#if !defined(MPT_PLATFORM_BIG_ENDIAN) && !defined(MPT_PLATFORM_LITTLE_ENDIAN)
// taken from boost/detail/endian.hpp
#if (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) \
|| (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) \
|| (defined(_STLP_BIG_ENDIAN) && !defined(_STLP_LITTLE_ENDIAN))
#define MPT_PLATFORM_BIG_ENDIAN
#elif (defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN)) \
|| (defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) \
|| (defined(_STLP_LITTLE_ENDIAN) && !defined(_STLP_BIG_ENDIAN))
#define MPT_PLATFORM_LITTLE_ENDIAN
#elif defined(__sparc) || defined(__sparc__) \
|| defined(_POWER) || defined(__powerpc__) \
|| defined(__ppc__) || defined(__hpux) || defined(__hppa) \
|| defined(_MIPSEB) || defined(_POWER) \
|| defined(__s390__)
#define MPT_PLATFORM_BIG_ENDIAN
#elif defined(__i386__) || defined(__alpha__) \
|| defined(__ia64) || defined(__ia64__) \
|| defined(_M_IX86) || defined(_M_IA64) \
|| defined(_M_ALPHA) || defined(__amd64) \
|| defined(__amd64__) || defined(_M_AMD64) \
|| defined(__x86_64) || defined(__x86_64__) \
|| defined(_M_X64) || defined(__bfin__)
#define MPT_PLATFORM_LITTLE_ENDIAN
#endif
#endif
#if defined(MPT_PLATFORM_BIG_ENDIAN) || defined(MPT_PLATFORM_LITTLE_ENDIAN)
#define MPT_PLATFORM_ENDIAN_KNOWN 1
#else
#define MPT_PLATFORM_ENDIAN_KNOWN 0
#endif
// This should really be based on __STDCPP_THREADS__, but that is not defined by
// GCC or clang. Stupid.
// Just assume multithreaded and disable for platforms we know are
// singlethreaded later on.
#define MPT_PLATFORM_MULTITHREADED 1
// specific C++ features
// C++11 constexpr
#if MPT_COMPILER_MSVC
#define MPT_COMPILER_QUIRK_CONSTEXPR_NO_STRING_LITERALS
#endif
#if MPT_COMPILER_MSVC
// Compiler has multiplication/division semantics when shifting signed integers.
#define MPT_COMPILER_SHIFT_SIGNED 1
#endif
#ifndef MPT_COMPILER_SHIFT_SIGNED
#define MPT_COMPILER_SHIFT_SIGNED 0
#endif
#if MPT_COMPILER_GCC || MPT_COMPILER_MSVC
// Compiler supports type-punning through unions. This is not stricly standard-conforming.
// For GCC, this is documented, for MSVC this is apparently not documented, but we assume it.
#define MPT_COMPILER_UNION_TYPE_ALIASES 1
#endif
#ifndef MPT_COMPILER_UNION_TYPE_ALIASES
// Compiler does not support type-punning through unions. std::memcpy is used instead.
// This is the safe fallback and strictly standard-conforming.
// Another standard-compliant alternative would be casting pointers to a character type pointer.
// This results in rather unreadable code and,
// in most cases, compilers generate better code by just inlining the memcpy anyway.
// (see <http://blog.regehr.org/archives/959>).
#define MPT_COMPILER_UNION_TYPE_ALIASES 0
#endif
// The order of the checks matters!
#if defined(__EMSCRIPTEN__)
#define MPT_OS_EMSCRIPTEN 1
#if defined(__EMSCRIPTEN_major__) && defined(__EMSCRIPTEN_minor__)
#if (__EMSCRIPTEN_major__ > 1)
#define MPT_OS_EMSCRIPTEN_ANCIENT 0
#elif (__EMSCRIPTEN_major__ == 1) && (__EMSCRIPTEN_minor__ >= 36)
#define MPT_OS_EMSCRIPTEN_ANCIENT 0
#else
#define MPT_OS_EMSCRIPTEN_ANCIENT 1
#endif
#else
#define MPT_OS_EMSCRIPTEN_ANCIENT 1
#endif
#elif defined(_WIN32)
#define MPT_OS_WINDOWS 1
#if defined(WINAPI_FAMILY)
#include <winapifamily.h>
#if (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)
#define MPT_OS_WINDOWS_WINRT 0
#else
#define MPT_OS_WINDOWS_WINRT 1
#endif
#else // !WINAPI_FAMILY
#define MPT_OS_WINDOWS_WINRT 0
#endif // WINAPI_FAMILY
#elif defined(__APPLE__)
#define MPT_OS_MACOSX_OR_IOS 1
//#include "TargetConditionals.h"
//#if TARGET_IPHONE_SIMULATOR
//#elif TARGET_OS_IPHONE
//#elif TARGET_OS_MAC
//#else
//#endif
#elif defined(__ANDROID__) || defined(ANDROID)
#define MPT_OS_ANDROID 1
#elif defined(__linux__)
#define MPT_OS_LINUX 1
#elif defined(__DragonFly__)
#define MPT_OS_DRAGONFLYBSD 1
#elif defined(__FreeBSD__)
#define MPT_OS_FREEBSD 1
#elif defined(__OpenBSD__)
#define MPT_OS_OPENBSD 1
#elif defined(__NetBSD__)
#define MPT_OS_NETBSD 1
#elif defined(__unix__)
#define MPT_OS_GENERIC_UNIX 1
#else
#define MPT_OS_UNKNOWN 1
#endif
#ifndef MPT_OS_EMSCRIPTEN
#define MPT_OS_EMSCRIPTEN 0
#endif
#ifndef MPT_OS_WINDOWS
#define MPT_OS_WINDOWS 0
#endif
#ifndef MPT_OS_WINDOWS_WINRT
#define MPT_OS_WINDOWS_WINRT 0
#endif
#ifndef MPT_OS_MACOSX_OR_IOS
#define MPT_OS_MACOSX_OR_IOS 0
#endif
#ifndef MPT_OS_ANDROID
#define MPT_OS_ANDROID 0
#endif
#ifndef MPT_OS_LINUX
#define MPT_OS_LINUX 0
#endif
#ifndef MPT_OS_DRAGONFLYBSD
#define MPT_OS_DRAGONFLYBSD 0
#endif
#ifndef MPT_OS_FREEBSD
#define MPT_OS_FREEBSD 0
#endif
#ifndef MPT_OS_OPENBSD
#define MPT_OS_OPENBSD 0
#endif
#ifndef MPT_OS_NETBSD
#define MPT_OS_NETBSD 0
#endif
#ifndef MPT_OS_GENERIC_UNIX
#define MPT_OS_GENERIC_UNIX 0
#endif
#ifndef MPT_OS_UNKNOWN
#define MPT_OS_UNKNOWN 0
#endif
#ifndef MPT_OS_EMSCRIPTEN_ANCIENT
#define MPT_OS_EMSCRIPTEN_ANCIENT 0
#endif
#if MPT_OS_EMSCRIPTEN
#undef MPT_PLATFORM_MULTITHREADED
#define MPT_PLATFORM_MULTITHREADED 0
#endif

View file

@ -0,0 +1,465 @@
/*
* ComponentManager.cpp
* --------------------
* Purpose: Manages loading of optional components.
* Notes : (currently none)
* Authors: Joern Heusipp
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "ComponentManager.h"
#include "Logging.h"
#include "mptMutex.h"
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_COMPONENTS)
ComponentBase::ComponentBase(ComponentType type)
: m_Type(type)
, m_Initialized(false)
, m_Available(false)
{
return;
}
ComponentBase::~ComponentBase()
{
return;
}
void ComponentBase::SetInitialized()
{
m_Initialized = true;
}
void ComponentBase::SetAvailable()
{
m_Available = true;
}
ComponentType ComponentBase::GetType() const
{
return m_Type;
}
bool ComponentBase::IsInitialized() const
{
return m_Initialized;
}
bool ComponentBase::IsAvailable() const
{
return m_Initialized && m_Available;
}
mpt::ustring ComponentBase::GetVersion() const
{
return mpt::ustring();
}
void ComponentBase::Initialize()
{
if(IsInitialized())
{
return;
}
if(DoInitialize())
{
SetAvailable();
}
SetInitialized();
}
#if defined(MPT_ENABLE_DYNBIND)
ComponentLibrary::ComponentLibrary(ComponentType type)
: ComponentBase(type)
, m_BindFailed(false)
{
return;
}
ComponentLibrary::~ComponentLibrary()
{
return;
}
bool ComponentLibrary::AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath)
{
if(m_Libraries[libName].IsValid())
{
// prefer previous
return true;
}
mpt::Library lib(libPath);
if(!lib.IsValid())
{
return false;
}
m_Libraries[libName] = lib;
return true;
}
void ComponentLibrary::ClearLibraries()
{
m_Libraries.clear();
}
void ComponentLibrary::SetBindFailed()
{
m_BindFailed = true;
}
void ComponentLibrary::ClearBindFailed()
{
m_BindFailed = false;
}
bool ComponentLibrary::HasBindFailed() const
{
return m_BindFailed;
}
mpt::Library ComponentLibrary::GetLibrary(const std::string &libName) const
{
const auto it = m_Libraries.find(libName);
if(it == m_Libraries.end())
{
return mpt::Library();
}
return it->second;
}
#endif // MPT_ENABLE_DYNBIND
#if MPT_COMPONENT_MANAGER
ComponentFactoryBase::ComponentFactoryBase(const std::string &id, const std::string &settingsKey)
: m_ID(id)
, m_SettingsKey(settingsKey)
{
return;
}
ComponentFactoryBase::~ComponentFactoryBase()
{
return;
}
std::string ComponentFactoryBase::GetID() const
{
return m_ID;
}
std::string ComponentFactoryBase::GetSettingsKey() const
{
return m_SettingsKey;
}
void ComponentFactoryBase::PreConstruct() const
{
MPT_LOG(LogInformation, "Components",
mpt::format(MPT_USTRING("Constructing Component %1"))
( mpt::ToUnicode(mpt::CharsetASCII, m_ID)
)
);
}
void ComponentFactoryBase::Initialize(ComponentManager &componentManager, std::shared_ptr<IComponent> component) const
{
if(componentManager.IsComponentBlocked(GetSettingsKey()))
{
return;
}
componentManager.InitializeComponent(component);
}
// Global list of component register functions.
// We do not use a global scope static list head because the corresponding
// mutex would be no POD type and would thus not be safe to be usable in
// zero-initialized state.
// Function scope static initialization is guaranteed to be thread safe
// in C++11.
// We use this implementation to be future-proof.
// MSVC currently does not exploit the possibility of using multiple threads
// for global lifetime object's initialization.
// An implementation with a simple global list head and no mutex at all would
// thus work fine for MSVC (currently).
static mpt::mutex & ComponentListMutex()
{
static mpt::mutex g_ComponentListMutex;
return g_ComponentListMutex;
}
static ComponentListEntry * & ComponentListHead()
{
static ComponentListEntry *g_ComponentListHead = nullptr;
return g_ComponentListHead;
}
bool ComponentListPush(ComponentListEntry *entry)
{
MPT_LOCK_GUARD<mpt::mutex> guard(ComponentListMutex());
entry->next = ComponentListHead();
ComponentListHead() = entry;
return true;
}
static std::shared_ptr<ComponentManager> g_ComponentManager;
void ComponentManager::Init(const IComponentManagerSettings &settings)
{
MPT_LOG(LogInformation, "Components", MPT_USTRING("Init"));
// cannot use make_shared because the constructor is private
g_ComponentManager = std::shared_ptr<ComponentManager>(new ComponentManager(settings));
}
void ComponentManager::Release()
{
MPT_LOG(LogInformation, "Components", MPT_USTRING("Release"));
g_ComponentManager = nullptr;
}
std::shared_ptr<ComponentManager> ComponentManager::Instance()
{
return g_ComponentManager;
}
ComponentManager::ComponentManager(const IComponentManagerSettings &settings)
: m_Settings(settings)
{
MPT_LOCK_GUARD<mpt::mutex> guard(ComponentListMutex());
for(ComponentListEntry *entry = ComponentListHead(); entry; entry = entry->next)
{
entry->reg(*this);
}
}
void ComponentManager::Register(const IComponentFactory &componentFactory)
{
if(m_Components.find(componentFactory.GetID()) != m_Components.end())
{
return;
}
RegisteredComponent registeredComponent;
registeredComponent.settingsKey = componentFactory.GetSettingsKey();
registeredComponent.factoryMethod = componentFactory.GetStaticConstructor();
registeredComponent.instance = nullptr;
registeredComponent.weakInstance = std::weak_ptr<IComponent>();
m_Components.insert(std::make_pair(componentFactory.GetID(), registeredComponent));
}
void ComponentManager::Startup()
{
MPT_LOG(LogDebug, "Components", MPT_USTRING("Startup"));
if(m_Settings.LoadOnStartup())
{
for(auto &it : m_Components)
{
it.second.instance = it.second.factoryMethod(*this);
it.second.weakInstance = it.second.instance;
}
}
if(!m_Settings.KeepLoaded())
{
for(auto &it : m_Components)
{
it.second.instance = nullptr;
}
}
}
bool ComponentManager::IsComponentBlocked(const std::string &settingsKey) const
{
return m_Settings.IsBlocked(settingsKey);
}
void ComponentManager::InitializeComponent(std::shared_ptr<IComponent> component) const
{
if(!component)
{
return;
}
if(component->IsInitialized())
{
return;
}
component->Initialize();
}
std::shared_ptr<const IComponent> ComponentManager::GetComponent(const IComponentFactory &componentFactory)
{
std::shared_ptr<IComponent> component = nullptr;
auto it = m_Components.find(componentFactory.GetID());
if(it != m_Components.end())
{ // registered component
if((*it).second.instance)
{ // loaded
component = (*it).second.instance;
} else
{ // not loaded
component = (*it).second.weakInstance.lock();
if(!component)
{
component = (*it).second.factoryMethod(*this);
}
if(m_Settings.KeepLoaded())
{ // keep the component loaded
(*it).second.instance = component;
}
(*it).second.weakInstance = component;
}
} else
{ // unregistered component
component = componentFactory.Construct(*this);
}
MPT_ASSERT(component);
return component;
}
std::shared_ptr<const IComponent> ComponentManager::ReloadComponent(const IComponentFactory &componentFactory)
{
std::shared_ptr<IComponent> component = nullptr;
auto it = m_Components.find(componentFactory.GetID());
if(it != m_Components.end())
{ // registered component
if((*it).second.instance)
{ // loaded
(*it).second.instance = nullptr;
if(!(*it).second.weakInstance.expired())
{
throw std::runtime_error("Component not completely unloaded. Cannot reload.");
}
(*it).second.weakInstance = std::weak_ptr<IComponent>();
}
// not loaded
component = (*it).second.factoryMethod(*this);
if(m_Settings.KeepLoaded())
{ // keep the component loaded
(*it).second.instance = component;
}
(*it).second.weakInstance = component;
} else
{ // unregistered component
component = componentFactory.Construct(*this);
}
MPT_ASSERT(component);
return component;
}
std::vector<std::string> ComponentManager::GetRegisteredComponents() const
{
std::vector<std::string> result;
result.reserve(m_Components.size());
for(const auto &it : m_Components)
{
result.push_back(it.first);
}
return result;
}
ComponentInfo ComponentManager::GetComponentInfo(std::string name) const
{
ComponentInfo result;
result.name = name;
result.state = ComponentStateUnregistered;
result.settingsKey = "";
result.type = ComponentTypeUnknown;
const auto it = m_Components.find(name);
if(it == m_Components.end())
{
result.state = ComponentStateUnregistered;
return result;
}
result.settingsKey = it->second.settingsKey;
if(IsComponentBlocked(it->second.settingsKey))
{
result.state = ComponentStateBlocked;
return result;
}
std::shared_ptr<IComponent> component = it->second.instance;
if(!component)
{
component = it->second.weakInstance.lock();
}
if(!component)
{
result.state = ComponentStateUnintialized;
return result;
}
result.type = component->GetType();
if(!component->IsInitialized())
{
result.state = ComponentStateUnintialized;
return result;
}
if(!component->IsAvailable())
{
result.state = ComponentStateUnavailable;
return result;
}
result.state = ComponentStateAvailable;
return result;
}
mpt::PathString ComponentManager::GetComponentPath() const
{
return m_Settings.Path();
}
#endif // MPT_COMPONENT_MANAGER
#endif // MPT_ENABLE_COMPONENTS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,507 @@
/*
* ComponentManager.h
* ------------------
* Purpose: Manages loading of optional components.
* Notes : (currently none)
* Authors: Joern Heusipp
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <map>
#include <vector>
#include "../common/misc_util.h"
#include "../common/mptMutex.h"
OPENMPT_NAMESPACE_BEGIN
#define MPT_ENABLE_COMPONENTS
#if defined(MPT_ENABLE_COMPONENTS)
#if defined(MODPLUG_TRACKER)
#define MPT_COMPONENT_MANAGER 1
#else
#define MPT_COMPONENT_MANAGER 0
#endif
enum ComponentType
{
ComponentTypeUnknown = 0,
ComponentTypeBuiltin, // PortAudio
ComponentTypeSystem, // mf.dll
ComponentTypeSystemInstallable, // acm mp3 codec
ComponentTypeBundled, // libsoundtouch
ComponentTypeForeign, // libmp3lame
};
class ComponentFactoryBase;
class IComponent
{
friend class ComponentFactoryBase;
protected:
IComponent() { }
public:
virtual ~IComponent() { }
public:
virtual ComponentType GetType() const = 0;
virtual bool IsInitialized() const = 0; // Initialize() has been called
virtual bool IsAvailable() const = 0; // Initialize() has been successfull
virtual mpt::ustring GetVersion() const = 0;
virtual void Initialize() = 0; // try to load the component
};
class ComponentBase
: public IComponent
{
private:
ComponentType m_Type;
bool m_Initialized;
bool m_Available;
protected:
ComponentBase(ComponentType type);
public:
virtual ~ComponentBase();
protected:
void SetInitialized();
void SetAvailable();
public:
virtual ComponentType GetType() const;
virtual bool IsInitialized() const;
virtual bool IsAvailable() const;
virtual mpt::ustring GetVersion() const;
public:
virtual void Initialize();
protected:
virtual bool DoInitialize() = 0;
};
class ComponentBuiltin : public ComponentBase
{
public:
ComponentBuiltin()
: ComponentBase(ComponentTypeBuiltin)
{
return;
}
virtual bool DoInitialize()
{
return true;
}
};
#define MPT_GLOBAL_BIND(lib, name) name = &::name;
#if defined(MPT_ENABLE_DYNBIND)
class ComponentLibrary
: public ComponentBase
{
private:
typedef std::map<std::string, mpt::Library> TLibraryMap;
TLibraryMap m_Libraries;
bool m_BindFailed;
protected:
ComponentLibrary(ComponentType type);
public:
virtual ~ComponentLibrary();
protected:
bool AddLibrary(const std::string &libName, const mpt::LibraryPath &libPath);
void ClearLibraries();
void SetBindFailed();
void ClearBindFailed();
bool HasBindFailed() const;
public:
virtual mpt::Library GetLibrary(const std::string &libName) const;
template <typename Tfunc>
bool Bind(Tfunc * & f, const std::string &libName, const std::string &symbol) const
{
return GetLibrary(libName).Bind(f, symbol);
}
protected:
virtual bool DoInitialize() = 0;
};
#define MPT_COMPONENT_BIND(libName, func) MPT_DO { if(!Bind( func , libName , #func )) { SetBindFailed(); } } MPT_WHILE_0
#define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func )
#define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } MPT_WHILE_0
#define MPT_COMPONENT_BIND_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol )
class ComponentSystemDLL : public ComponentLibrary
{
private:
mpt::PathString m_BaseName;
public:
ComponentSystemDLL(const mpt::PathString &baseName)
: ComponentLibrary(ComponentTypeSystem)
, m_BaseName(baseName)
{
return;
}
virtual bool DoInitialize()
{
AddLibrary(m_BaseName.ToUTF8(), mpt::LibraryPath::System(m_BaseName));
return GetLibrary(m_BaseName.ToUTF8()).IsValid();
}
};
class ComponentBundledDLL : public ComponentLibrary
{
private:
mpt::PathString m_FullName;
public:
ComponentBundledDLL(const mpt::PathString &fullName)
: ComponentLibrary(ComponentTypeBundled)
, m_FullName(fullName)
{
return;
}
virtual bool DoInitialize()
{
AddLibrary(m_FullName.ToUTF8(), mpt::LibraryPath::AppFullName(m_FullName));
return GetLibrary(m_FullName.ToUTF8()).IsValid();
}
};
#endif // MPT_ENABLE_DYNBIND
#if MPT_COMPONENT_MANAGER
class ComponentManager;
typedef std::shared_ptr<IComponent> (*ComponentFactoryMethod)(ComponentManager &componentManager);
class IComponentFactory
{
protected:
IComponentFactory() { }
public:
virtual ~IComponentFactory() { }
public:
virtual std::string GetID() const = 0;
virtual std::string GetSettingsKey() const = 0;
virtual std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const = 0;
virtual ComponentFactoryMethod GetStaticConstructor() const = 0;
};
class ComponentFactoryBase
: public IComponentFactory
{
private:
std::string m_ID;
std::string m_SettingsKey;
protected:
ComponentFactoryBase(const std::string &id, const std::string &settingsKey);
void PreConstruct() const;
void Initialize(ComponentManager &componentManager, std::shared_ptr<IComponent> component) const;
public:
virtual ~ComponentFactoryBase();
virtual std::string GetID() const;
virtual std::string GetSettingsKey() const;
virtual std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const = 0;
virtual ComponentFactoryMethod GetStaticConstructor() const = 0;
};
template <typename T>
class ComponentFactory
: public ComponentFactoryBase
{
public:
ComponentFactory()
: ComponentFactoryBase(T::g_ID, T::g_SettingsKey)
{
return;
}
virtual ~ComponentFactory()
{
return;
}
public:
virtual std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const
{
PreConstruct();
std::shared_ptr<IComponent> component = std::make_shared<T>();
Initialize(componentManager, component);
return component;
}
static std::shared_ptr<IComponent> StaticConstruct(ComponentManager &componentManager)
{
return ComponentFactory().Construct(componentManager);
}
virtual ComponentFactoryMethod GetStaticConstructor() const
{
return &StaticConstruct;
}
};
class IComponentManagerSettings
{
public:
virtual bool LoadOnStartup() const = 0;
virtual bool KeepLoaded() const = 0;
virtual bool IsBlocked(const std::string &key) const = 0;
virtual mpt::PathString Path() const = 0;
};
class ComponentManagerSettingsDefault
: public IComponentManagerSettings
{
public:
virtual bool LoadOnStartup() const { return false; }
virtual bool KeepLoaded() const { return true; }
virtual bool IsBlocked(const std::string & /*key*/ ) const { return false; }
virtual mpt::PathString Path() const { return mpt::PathString(); }
};
enum ComponentState
{
ComponentStateUnregistered,
ComponentStateBlocked,
ComponentStateUnintialized,
ComponentStateUnavailable,
ComponentStateAvailable,
};
struct ComponentInfo
{
std::string name;
ComponentState state;
std::string settingsKey;
ComponentType type;
};
class ComponentManager
{
friend class ComponentFactoryBase;
public:
static void Init(const IComponentManagerSettings &settings);
static void Release();
static std::shared_ptr<ComponentManager> Instance();
private:
ComponentManager(const IComponentManagerSettings &settings);
private:
struct RegisteredComponent
{
std::string settingsKey;
ComponentFactoryMethod factoryMethod;
std::shared_ptr<IComponent> instance;
std::weak_ptr<IComponent> weakInstance;
};
typedef std::map<std::string, RegisteredComponent> TComponentMap;
const IComponentManagerSettings &m_Settings;
TComponentMap m_Components;
private:
bool IsComponentBlocked(const std::string &settingsKey) const;
void InitializeComponent(std::shared_ptr<IComponent> component) const;
public:
void Register(const IComponentFactory &componentFactory);
void Startup();
std::shared_ptr<const IComponent> GetComponent(const IComponentFactory &componentFactory);
std::shared_ptr<const IComponent> ReloadComponent(const IComponentFactory &componentFactory);
std::vector<std::string> GetRegisteredComponents() const;
ComponentInfo GetComponentInfo(std::string name) const;
mpt::PathString GetComponentPath() const;
};
struct ComponentListEntry
{
ComponentListEntry *next;
void (*reg)(ComponentManager &componentManager);
};
bool ComponentListPush(ComponentListEntry *entry);
#define MPT_DECLARE_COMPONENT_MEMBERS public: static const char * const g_ID; static const char * const g_SettingsKey;
#define MPT_REGISTERED_COMPONENT(name, settingsKey) \
static void RegisterComponent ## name (ComponentManager &componentManager) \
{ \
componentManager.Register(ComponentFactory< name >()); \
} \
static ComponentListEntry Component ## name ## ListEntry = { nullptr, & RegisterComponent ## name }; \
bool Component ## name ## Registered = ComponentListPush(& Component ## name ## ListEntry ); \
const char * const name :: g_ID = #name ; \
const char * const name :: g_SettingsKey = settingsKey ; \
/**/
template <typename type>
std::shared_ptr<const type> GetComponent()
{
return std::dynamic_pointer_cast<const type>(ComponentManager::Instance()->GetComponent(ComponentFactory<type>()));
}
template <typename type>
std::shared_ptr<const type> ReloadComponent()
{
return std::dynamic_pointer_cast<const type>(ComponentManager::Instance()->ReloadComponent(ComponentFactory<type>()));
}
static inline mpt::PathString GetComponentPath()
{
return ComponentManager::Instance()->GetComponentPath();
}
#else // !MPT_COMPONENT_MANAGER
#define MPT_DECLARE_COMPONENT_MEMBERS
#define MPT_REGISTERED_COMPONENT(name, settingsKey)
template <typename type>
std::shared_ptr<const type> GetComponent()
{
static std::weak_ptr<type> cache;
static mpt::mutex m;
MPT_LOCK_GUARD<mpt::mutex> l(m);
std::shared_ptr<type> component = cache.lock();
if(!component)
{
component = std::make_shared<type>();
component->Initialize();
cache = component;
}
return component;
}
static inline mpt::PathString GetComponentPath()
{
return mpt::PathString();
}
#endif // MPT_COMPONENT_MANAGER
// Simple wrapper around std::shared_ptr<ComponentType> which automatically
// gets a reference to the component (or constructs it) on initialization.
template <typename T>
class ComponentHandle
{
private:
std::shared_ptr<const T> component;
public:
ComponentHandle()
: component(GetComponent<T>())
{
return;
}
~ComponentHandle()
{
return;
}
bool IsAvailable() const
{
return component && component->IsAvailable();
}
const T *get() const
{
return component.get();
}
const T &operator*() const
{
return *component;
}
const T *operator->() const
{
return &*component;
}
#if MPT_COMPONENT_MANAGER
void Reload()
{
component = nullptr;
component = ReloadComponent<T>();
}
#endif
};
template <typename T>
bool IsComponentAvailable(const ComponentHandle<T> &handle)
{
return handle.IsAvailable();
}
#endif // MPT_ENABLE_COMPONENTS
OPENMPT_NAMESPACE_END

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,154 @@
/*
* FileReader.cpp
* --------------
* Purpose: A basic class for transparent reading of memory-based files.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "FileReader.h"
#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_TEMPFILE) && MPT_OS_WINDOWS
OnDiskFileWrapper::OnDiskFileWrapper(FileReader &file, const mpt::PathString &fileNameExtension)
: m_IsTempFile(false)
{
try
{
file.Rewind();
if(file.GetFileName().empty())
{
const mpt::PathString tempName = mpt::CreateTempFileName(MPT_PATHSTRING("OpenMPT"), fileNameExtension);
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT < 0x0602)
#define MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
#endif
#endif
#ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
FILE * f = _wfopen(tempName.AsNative().c_str(), L"wb");
if(!f)
{
throw std::runtime_error("");
}
while(!file.EndOfFile())
{
FileReader::PinnedRawDataView view = file.ReadPinnedRawDataView(mpt::IO::BUFFERSIZE_NORMAL);
std::size_t towrite = view.size();
std::size_t written = 0;
do
{
std::size_t chunkSize = mpt::saturate_cast<std::size_t>(towrite);
std::size_t chunkDone = 0;
chunkDone = fwrite(view.data() + written, 1, chunkSize, f);
if(chunkDone != chunkSize)
{
fclose(f);
f = NULL;
throw std::runtime_error("");
}
towrite -= chunkDone;
written += chunkDone;
} while(towrite > 0);
}
fclose(f);
f = NULL;
#else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
HANDLE hFile = NULL;
#if MPT_OS_WINDOWS_WINRT
hFile = CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL);
#else
hFile = CreateFileW(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL);
#endif
if(hFile == NULL || hFile == INVALID_HANDLE_VALUE)
{
throw std::runtime_error("");
}
while(!file.EndOfFile())
{
FileReader::PinnedRawDataView view = file.ReadPinnedRawDataView(mpt::IO::BUFFERSIZE_NORMAL);
std::size_t towrite = view.size();
std::size_t written = 0;
do
{
DWORD chunkSize = mpt::saturate_cast<DWORD>(towrite);
DWORD chunkDone = 0;
WriteFile(hFile, view.data() + written, chunkSize, &chunkDone, NULL);
if(chunkDone != chunkSize)
{
CloseHandle(hFile);
hFile = NULL;
throw std::runtime_error("");
}
towrite -= chunkDone;
written += chunkDone;
} while(towrite > 0);
}
CloseHandle(hFile);
hFile = NULL;
#endif // MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
m_Filename = tempName;
m_IsTempFile = true;
} else
{
m_Filename = file.GetFileName();
}
} catch (const std::runtime_error &)
{
m_IsTempFile = false;
m_Filename = mpt::PathString();
}
}
OnDiskFileWrapper::~OnDiskFileWrapper()
{
if(m_IsTempFile)
{
DeleteFileW(m_Filename.AsNative().c_str());
m_IsTempFile = false;
}
m_Filename = mpt::PathString();
}
bool OnDiskFileWrapper::IsValid() const
{
return !m_Filename.empty();
}
mpt::PathString OnDiskFileWrapper::GetFilename() const
{
return m_Filename;
}
#else
MPT_MSVC_WORKAROUND_LNK4221(FileReader)
#endif // MPT_ENABLE_TEMPFILE && MPT_OS_WINDOWS
OPENMPT_NAMESPACE_END

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
/*
* FileReaderFwd.h
* ---------------
* Purpose: Forward declaration for class FileReader.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "typedefs.h"
OPENMPT_NAMESPACE_BEGIN
class FileReaderTraitsMemory;
#if defined(MPT_FILEREADER_STD_ISTREAM)
class FileReaderTraitsStdStream;
typedef FileReaderTraitsStdStream FileReaderTraitsDefault;
#else // !MPT_FILEREADER_STD_ISTREAM
typedef FileReaderTraitsMemory FileReaderTraitsDefault;
#endif // MPT_FILEREADER_STD_ISTREAM
namespace detail {
template <typename Ttraits>
class FileReader;
} // namespace detail
typedef detail::FileReader<FileReaderTraitsDefault> FileReader;
typedef detail::FileReader<FileReaderTraitsMemory> MemoryFileReader;
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,412 @@
/*
* FlagSet.h
* ---------
* Purpose: A flexible and typesafe flag set class.
* Notes : Originally based on http://stackoverflow.com/questions/4226960/type-safer-bitflags-in-c .
* Rewritten to be standard-conforming.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <string>
OPENMPT_NAMESPACE_BEGIN
// Be aware of the required size when specializing this.
// We cannot assert the minimum size because some compilers always allocate an 'int',
// even for enums that would fit in smaller integral types.
template <typename Tenum>
struct enum_traits
{
typedef typename std::make_unsigned<Tenum>::type store_type;
};
// Type-safe wrapper around an enum, that can represent all enum values and bitwise compositions thereof.
// Conversions to and from plain integers as well as conversions to the base enum are always explicit.
template <typename enum_t>
class enum_value_type
{
public:
typedef enum_t enum_type;
typedef enum_value_type value_type;
typedef typename enum_traits<enum_t>::store_type store_type;
private:
store_type bits;
public:
MPT_CONSTEXPR11_FUN enum_value_type() : bits(0) { }
MPT_CONSTEXPR11_FUN enum_value_type(const enum_value_type &x) : bits(x.bits) { }
MPT_CONSTEXPR11_FUN enum_value_type(enum_type x) : bits(static_cast<store_type>(x)) { }
private:
explicit MPT_CONSTEXPR11_FUN enum_value_type(store_type x) : bits(x) { } // private in order to prevent accidental conversions. use from_bits.
MPT_CONSTEXPR11_FUN operator store_type () const { return bits; } // private in order to prevent accidental conversions. use as_bits.
public:
static MPT_CONSTEXPR11_FUN enum_value_type from_bits(store_type bits) { return value_type(bits); }
MPT_CONSTEXPR11_FUN enum_type as_enum() const { return static_cast<enum_t>(bits); }
MPT_CONSTEXPR11_FUN store_type as_bits() const { return bits; }
public:
MPT_CONSTEXPR11_FUN operator bool () const { return bits != store_type(); }
MPT_CONSTEXPR11_FUN bool operator ! () const { return bits == store_type(); }
MPT_CONSTEXPR11_FUN const enum_value_type operator ~ () const { return enum_value_type(~bits); }
friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_value_type b) { return a.bits == b.bits; }
friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_value_type b) { return a.bits != b.bits; }
friend MPT_CONSTEXPR11_FUN bool operator == (enum_value_type a, enum_t b) { return a == enum_value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (enum_value_type a, enum_t b) { return a != enum_value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (enum_t a, enum_value_type b) { return enum_value_type(a) == b; }
friend MPT_CONSTEXPR11_FUN bool operator != (enum_t a, enum_value_type b) { return enum_value_type(a) != b; }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits | b.bits); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits & b.bits); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_value_type b) { return enum_value_type(a.bits ^ b.bits); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_value_type a, enum_t b) { return a | enum_value_type(b); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_value_type a, enum_t b) { return a & enum_value_type(b); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_value_type a, enum_t b) { return a ^ enum_value_type(b); }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator | (enum_t a, enum_value_type b) { return enum_value_type(a) | b; }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator & (enum_t a, enum_value_type b) { return enum_value_type(a) & b; }
friend MPT_CONSTEXPR11_FUN const enum_value_type operator ^ (enum_t a, enum_value_type b) { return enum_value_type(a) ^ b; }
MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_value_type b) { *this = *this | b; return *this; }
MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_value_type b) { *this = *this & b; return *this; }
MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_value_type b) { *this = *this ^ b; return *this; }
MPT_CONSTEXPR14_FUN enum_value_type &operator |= (enum_t b) { *this = *this | b; return *this; }
MPT_CONSTEXPR14_FUN enum_value_type &operator &= (enum_t b) { *this = *this & b; return *this; }
MPT_CONSTEXPR14_FUN enum_value_type &operator ^= (enum_t b) { *this = *this ^ b; return *this; }
};
// Type-safe enum wrapper that allows type-safe bitwise testing.
template <typename enum_t>
class Enum
{
public:
typedef Enum self_type;
typedef enum_t enum_type;
typedef enum_value_type<enum_t> value_type;
typedef typename value_type::store_type store_type;
private:
enum_type value;
public:
explicit MPT_CONSTEXPR11_FUN Enum(enum_type val) : value(val) { }
MPT_CONSTEXPR11_FUN operator enum_type () const { return value; }
MPT_CONSTEXPR14_FUN Enum &operator = (enum_type val) { value = val; return *this; }
public:
MPT_CONSTEXPR11_FUN const value_type operator ~ () const { return ~value_type(value); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return value_type(a) == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return value_type(a) != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return value_type(a) == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return value_type(a) != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return value_type(a) == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return value_type(a) != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return value_type(a) | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return value_type(a) & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return value_type(a) ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return value_type(a) | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return value_type(a) & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return value_type(a) ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return value_type(a) | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return value_type(a) & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return value_type(a) ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ value_type(b); }
};
template <typename enum_t, typename store_t = typename enum_value_type<enum_t>::store_type >
class FlagSet
{
public:
typedef FlagSet self_type;
typedef enum_t enum_type;
typedef enum_value_type<enum_t> value_type;
typedef store_t store_type;
private:
// support truncated store_type ... :
store_type bits_;
static MPT_CONSTEXPR11_FUN store_type store_from_value(value_type bits) { return static_cast<store_type>(bits.as_bits()); }
static MPT_CONSTEXPR11_FUN value_type value_from_store(store_type bits) { return value_type::from_bits(static_cast<typename value_type::store_type>(bits)); }
MPT_CONSTEXPR14_FUN FlagSet & store(value_type bits) { bits_ = store_from_value(bits); return *this; }
MPT_CONSTEXPR11_FUN value_type load() const { return value_from_store(bits_); }
public:
// Default constructor (no flags set)
MPT_CONSTEXPR11_FUN FlagSet() : bits_(store_from_value(value_type()))
{
}
// Value constructor
MPT_CONSTEXPR11_FUN FlagSet(value_type flags) : bits_(store_from_value(value_type(flags)))
{
}
MPT_CONSTEXPR11_FUN FlagSet(enum_type flag) : bits_(store_from_value(value_type(flag)))
{
}
explicit MPT_CONSTEXPR11_FUN FlagSet(store_type flags) : bits_(store_from_value(value_type::from_bits(flags)))
{
}
MPT_CONSTEXPR11_FUN operator bool () const
{
return load();
}
// In order to catch undesired conversions to bool in integer contexts,
// add a deprecated conversion operator to store_type.
// C++11 explicit conversion cast operators ('explicit operator bool ();')
// would solve this in a better way and always fail at compile-time instead of this
// solution which just warns in some cases.
// The macro-based extended instrument fields writer in InstrumentExtensions.cpp currently needs this conversion,
// so it is not marked deprecated (for now).
/*MPT_DEPRECATED*/ MPT_CONSTEXPR11_FUN operator store_type () const
{
return load().as_bits();
}
MPT_CONSTEXPR11_FUN value_type value() const
{
return load();
}
MPT_CONSTEXPR11_FUN operator value_type () const
{
return load();
}
// Test if one or more flags are set. Returns true if at least one of the given flags is set.
MPT_CONSTEXPR11_FUN bool operator[] (value_type flags) const
{
return test(flags);
}
// String representation of flag set
std::string to_string() const
{
std::string str(size_bits(), '0');
for(size_t x = 0; x < size_bits(); ++x)
{
str[size_bits() - x - 1] = (load() & (1 << x) ? '1' : '0');
}
return str;
}
// Set one or more flags.
MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags)
{
return store(load() | flags);
}
// Set or clear one or more flags.
MPT_CONSTEXPR14_FUN FlagSet &set(value_type flags, bool val)
{
return store((val ? (load() | flags) : (load() & ~flags)));
}
// Clear or flags.
MPT_CONSTEXPR14_FUN FlagSet &reset()
{
return store(value_type());
}
// Clear one or more flags.
MPT_CONSTEXPR14_FUN FlagSet &reset(value_type flags)
{
return store(load() & ~flags);
}
// Toggle all flags.
MPT_CONSTEXPR14_FUN FlagSet &flip()
{
return store(~load());
}
// Toggle one or more flags.
MPT_CONSTEXPR14_FUN FlagSet &flip(value_type flags)
{
return store(load() ^ flags);
}
// Returns the size of the flag set in bytes
MPT_CONSTEXPR11_FUN std::size_t size() const
{
return sizeof(store_type);
}
// Returns the size of the flag set in bits
MPT_CONSTEXPR11_FUN std::size_t size_bits() const
{
return size() * 8;
}
// Test if one or more flags are set. Returns true if at least one of the given flags is set.
MPT_CONSTEXPR11_FUN bool test(value_type flags) const
{
return (load() & flags);
}
// Test if all specified flags are set.
MPT_CONSTEXPR11_FUN bool test_all(value_type flags) const
{
return (load() & flags) == flags;
}
// Test if any but the specified flags are set.
MPT_CONSTEXPR11_FUN bool test_any_except(value_type flags) const
{
return (load() & ~flags);
}
// Test if any flag is set.
MPT_CONSTEXPR11_FUN bool any() const
{
return load();
}
// Test if no flags are set.
MPT_CONSTEXPR11_FUN bool none() const
{
return !load();
}
MPT_CONSTEXPR11_FUN store_type GetRaw() const
{
return bits_;
}
MPT_CONSTEXPR14_FUN FlagSet & SetRaw(store_type flags)
{
bits_ = flags;
return *this;
}
MPT_CONSTEXPR14_FUN FlagSet &operator = (value_type flags)
{
return store(flags);
}
MPT_CONSTEXPR14_FUN FlagSet &operator = (enum_type flag)
{
return store(flag);
}
MPT_CONSTEXPR14_FUN FlagSet &operator = (FlagSet flags)
{
return store(flags.load());
}
MPT_CONSTEXPR14_FUN FlagSet &operator &= (value_type flags)
{
return store(load() & flags);
}
MPT_CONSTEXPR14_FUN FlagSet &operator |= (value_type flags)
{
return store(load() | flags);
}
MPT_CONSTEXPR14_FUN FlagSet &operator ^= (value_type flags)
{
return store(load() ^ flags);
}
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, self_type b) { return a.load() == b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, self_type b) { return a.load() != b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, value_type b) { return a.load() == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, value_type b) { return a.load() != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (value_type a, self_type b) { return value_type(a) == b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator != (value_type a, self_type b) { return value_type(a) != b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, enum_type b) { return a.load() == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, enum_type b) { return a.load() != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (enum_type a, self_type b) { return value_type(a) == b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator != (enum_type a, self_type b) { return value_type(a) != b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator == (self_type a, Enum<enum_type> b) { return a.load() == value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator != (self_type a, Enum<enum_type> b) { return a.load() != value_type(b); }
friend MPT_CONSTEXPR11_FUN bool operator == (Enum<enum_type> a, self_type b) { return value_type(a) == b.load(); }
friend MPT_CONSTEXPR11_FUN bool operator != (Enum<enum_type> a, self_type b) { return value_type(a) != b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, self_type b) { return a.load() | b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, self_type b) { return a.load() & b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, self_type b) { return a.load() ^ b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, value_type b) { return a.load() | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, value_type b) { return a.load() & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, value_type b) { return a.load() ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (value_type a, self_type b) { return value_type(a) | b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (value_type a, self_type b) { return value_type(a) & b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (value_type a, self_type b) { return value_type(a) ^ b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, enum_type b) { return a.load() | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, enum_type b) { return a.load() & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, enum_type b) { return a.load() ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (enum_type a, self_type b) { return value_type(a) | b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (enum_type a, self_type b) { return value_type(a) & b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (enum_type a, self_type b) { return value_type(a) ^ b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (self_type a, Enum<enum_type> b) { return a.load() | value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (self_type a, Enum<enum_type> b) { return a.load() & value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (self_type a, Enum<enum_type> b) { return a.load() ^ value_type(b); }
friend MPT_CONSTEXPR11_FUN const value_type operator | (Enum<enum_type> a, self_type b) { return value_type(a) | b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator & (Enum<enum_type> a, self_type b) { return value_type(a) & b.load(); }
friend MPT_CONSTEXPR11_FUN const value_type operator ^ (Enum<enum_type> a, self_type b) { return value_type(a) ^ b.load(); }
};
// Declare typesafe logical operators for enum_t
#define MPT_DECLARE_ENUM(enum_t) \
MPT_CONSTEXPR11_FUN enum_value_type<enum_t> operator | (enum_t a, enum_t b) { return enum_value_type<enum_t>(a) | enum_value_type<enum_t>(b); } \
MPT_CONSTEXPR11_FUN enum_value_type<enum_t> operator & (enum_t a, enum_t b) { return enum_value_type<enum_t>(a) & enum_value_type<enum_t>(b); } \
MPT_CONSTEXPR11_FUN enum_value_type<enum_t> operator ^ (enum_t a, enum_t b) { return enum_value_type<enum_t>(a) ^ enum_value_type<enum_t>(b); } \
MPT_CONSTEXPR11_FUN enum_value_type<enum_t> operator ~ (enum_t a) { return ~enum_value_type<enum_t>(a); } \
/**/
// backwards compatibility
#define DECLARE_FLAGSET MPT_DECLARE_ENUM
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,423 @@
/*
* Logging.cpp
* -----------
* Purpose: General logging
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "Logging.h"
#include "mptFileIO.h"
#if defined(MODPLUG_TRACKER)
#include <atomic>
#endif
#include "version.h"
#include <iostream>
#include <cstdarg>
#include <cstring>
#include <stdarg.h>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace log
{
#ifndef NO_LOGGING
#if !defined(MPT_LOG_GLOBAL_LEVEL_STATIC)
#if defined(MPT_LOG_GLOBAL_LEVEL)
int GlobalLogLevel = static_cast<int>(MPT_LOG_GLOBAL_LEVEL);
#else
int GlobalLogLevel = static_cast<int>(LogDebug);
#endif
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED)
bool FileEnabled = false;
bool DebuggerEnabled = true;
bool ConsoleEnabled = false;
static char g_FacilitySolo[1024] = {0};
static char g_FacilityBlocked[1024] = {0};
void SetFacilities(const std::string &solo, const std::string &blocked)
{
std::strcpy(g_FacilitySolo, solo.c_str());
std::strcpy(g_FacilityBlocked, blocked.c_str());
}
bool IsFacilityActive(const char *facility)
{
if(facility)
{
if(std::strlen(g_FacilitySolo) > 0)
{
if(std::strcmp(facility, g_FacilitySolo) != 0)
{
return false;
}
}
if(std::strlen(g_FacilityBlocked) > 0)
{
if(std::strcmp(facility, g_FacilitySolo) == 0)
{
return false;
}
}
}
return true;
}
#endif
void Logger::SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text)
{
#ifdef MPT_LOG_IS_DISABLED
MPT_UNREFERENCED_PARAMETER(context);
MPT_UNREFERENCED_PARAMETER(level);
MPT_UNREFERENCED_PARAMETER(facility);
MPT_UNREFERENCED_PARAMETER(text);
#else // !MPT_LOG_IS_DISABLED
MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < level)
{
return;
}
#if defined(MODPLUG_TRACKER)
if(!IsFacilityActive(facility))
{
return;
}
#else // !MODPLUG_TRACKER
MPT_UNREFERENCED_PARAMETER(facility);
#endif // MODPLUG_TRACKER
// remove eol if already present and add log level prefix
const mpt::ustring message = LogLevelToString(level) + MPT_USTRING(": ") + mpt::String::RTrim(text, MPT_USTRING("\r\n"));
const mpt::ustring file = mpt::ToUnicode(mpt::CharsetASCII, context.file);
const mpt::ustring function = mpt::ToUnicode(mpt::CharsetASCII, context.function);
const mpt::ustring line = mpt::ufmt::dec(context.line);
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
#if MPT_OS_WINDOWS
static uint64 s_lastlogtime = 0;
uint64 cur = mpt::Date::ANSI::Now();
uint64 diff = cur/10000 - s_lastlogtime;
s_lastlogtime = cur/10000;
#else
uint64 cur = 0;
uint64 diff = 0;
#endif
if(mpt::log::FileEnabled)
{
static FILE * s_logfile = nullptr;
if(!s_logfile)
{
s_logfile = mpt_fopen(MPT_PATHSTRING("mptrack.log"), "a");
}
if(s_logfile)
{
fprintf(s_logfile, mpt::ToCharset(mpt::CharsetUTF8, mpt::format(MPT_USTRING("%1+%2 %3(%4): %5 [%6]\n"))
( mpt::Date::ANSI::ToString(cur)
, mpt::ufmt::dec<6>(diff)
, file
, line
, message
, function
)).c_str());
fflush(s_logfile);
}
}
if(mpt::log::DebuggerEnabled)
{
OutputDebugStringW(mpt::ToWide(mpt::format(MPT_USTRING("%1(%2): +%3 %4 [%5]\n"))
( file
, line
, mpt::ufmt::dec<6>(diff)
, message
, function
)).c_str());
}
if(mpt::log::ConsoleEnabled)
{
static bool consoleInited = false;
if(!consoleInited)
{
AllocConsole();
consoleInited = true;
}
std::wstring consoletext = mpt::ToWide(message) + L"\r\n";
DWORD dummy = 0;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), consoletext.c_str(), consoletext.length(), &dummy, NULL);
}
#elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT)
std::clog
<< "NativeSupport: "
<< mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": "
<< mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message)
<< " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]"
<< std::endl;
#else // !MODPLUG_TRACKER
std::clog
<< "libopenmpt: "
<< mpt::ToCharset(mpt::CharsetLocaleOrUTF8, file) << "(" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, line) << ")" << ": "
<< mpt::ToCharset(mpt::CharsetLocaleOrUTF8, message)
<< " [" << mpt::ToCharset(mpt::CharsetLocaleOrUTF8, function) << "]"
<< std::endl;
#endif // MODPLUG_TRACKER
#endif // MPT_LOG_IS_DISABLED
}
void LegacyLogger::operator () (const AnyStringLocale &text)
{
SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", text);
}
void LegacyLogger::operator () (const char *format, ...)
{
static const std::size_t LOGBUF_SIZE = 1024;
char message[LOGBUF_SIZE];
va_list va;
va_start(va, format);
vsnprintf(message, LOGBUF_SIZE, format, va);
va_end(va);
message[LOGBUF_SIZE - 1] = '\0';
SendLogMessage(context, MPT_LEGACY_LOGLEVEL, "", mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, message));
}
void LegacyLogger::operator () (LogLevel level, const mpt::ustring &text)
{
SendLogMessage(context, level, "", text);
}
#endif // !NO_LOGGING
#if defined(MODPLUG_TRACKER)
namespace Trace {
#if MPT_OS_WINDOWS
// Debugging functionality will use simple globals.
bool volatile g_Enabled = false;
static bool g_Sealed = false;
struct Entry {
uint32 Index;
uint32 ThreadId;
uint64 Timestamp;
const char * Function;
const char * File;
int Line;
};
inline bool operator < (const Entry &a, const Entry &b)
{
/*
return false
|| (a.Timestamp < b.Timestamp)
|| (a.ThreadID < b.ThreadID)
|| (a.File < b.File)
|| (a.Line < b.Line)
|| (a.Function < b.Function)
;
*/
return false
|| (a.Index < b.Index)
;
}
static std::vector<mpt::log::Trace::Entry> Entries;
static std::atomic<uint32> NextIndex(0);
static uint32 ThreadIdGUI = 0;
static uint32 ThreadIdAudio = 0;
static uint32 ThreadIdNotify = 0;
void Enable(std::size_t numEntries)
{
if(g_Sealed)
{
return;
}
Entries.clear();
Entries.resize(numEntries);
NextIndex.store(0);
g_Enabled = true;
}
void Disable()
{
if(g_Sealed)
{
return;
}
g_Enabled = false;
}
MPT_NOINLINE void Trace(const mpt::log::Context & context)
{
// This will get called in realtime contexts and hot paths.
// No blocking allowed here.
const uint32 index = NextIndex.fetch_add(1);
#if 1
LARGE_INTEGER time;
time.QuadPart = 0;
QueryPerformanceCounter(&time);
const uint64 timestamp = time.QuadPart;
#else
FILETIME time = FILETIME();
GetSystemTimeAsFileTime(&time);
const uint64 timestamp = (static_cast<uint64>(time.dwHighDateTime) << 32) | (static_cast<uint64>(time.dwLowDateTime) << 0);
#endif
const uint32 threadid = static_cast<uint32>(GetCurrentThreadId());
mpt::log::Trace::Entry & entry = Entries[index % Entries.size()];
entry.Index = index;
entry.ThreadId = threadid;
entry.Timestamp = timestamp;
entry.Function = context.function;
entry.File = context.file;
entry.Line = context.line;
}
void Seal()
{
if(!g_Enabled)
{
return;
}
g_Enabled = false;
g_Sealed = true;
uint32 count = NextIndex.fetch_add(0);
if(count < Entries.size())
{
Entries.resize(count);
}
}
bool Dump(const mpt::PathString &filename)
{
if(!g_Sealed)
{
return false;
}
LARGE_INTEGER qpcNow;
qpcNow.QuadPart = 0;
QueryPerformanceCounter(&qpcNow);
uint64 ftNow = mpt::Date::ANSI::Now();
// sort according to index in case of overflows
std::stable_sort(Entries.begin(), Entries.end());
mpt::ofstream f(filename, std::ios::out);
f << "Build: OpenMPT " << MptVersion::GetVersionStringExtended() << std::endl;
bool qpcValid = false;
LARGE_INTEGER qpcFreq;
qpcFreq.QuadPart = 0;
QueryPerformanceFrequency(&qpcFreq);
if(qpcFreq.QuadPart > 0)
{
qpcValid = true;
}
f << "Dump: " << mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString(ftNow)) << std::endl;
f << "Captured events: " << Entries.size() << std::endl;
if(qpcValid && (Entries.size() > 0))
{
double period = static_cast<double>(Entries[Entries.size() - 1].Timestamp - Entries[0].Timestamp) / static_cast<double>(qpcFreq.QuadPart);
double eventsPerSecond = Entries.size() / period;
f << "Period [s]: " << mpt::fmt::fix(period) << std::endl;
f << "Events/second: " << mpt::fmt::fix(eventsPerSecond) << std::endl;
}
for(std::size_t i = 0; i < Entries.size(); ++i)
{
mpt::log::Trace::Entry & entry = Entries[i];
if(!entry.Function) entry.Function = "";
if(!entry.File) entry.File = "";
std::string time;
if(qpcValid)
{
time = mpt::ToCharset(mpt::CharsetUTF8, mpt::Date::ANSI::ToString( ftNow - static_cast<int64>( static_cast<double>(qpcNow.QuadPart - entry.Timestamp) * (10000000.0 / static_cast<double>(qpcFreq.QuadPart) ) ) ) );
} else
{
time = mpt::format("0x%1")(mpt::fmt::hex0<16>(entry.Timestamp));
}
f << time;
if(entry.ThreadId == ThreadIdGUI)
{
f << " -----GUI ";
} else if(entry.ThreadId == ThreadIdAudio)
{
f << " ---Audio ";
} else if(entry.ThreadId == ThreadIdNotify)
{
f << " --Notify ";
} else
{
f << " " << mpt::fmt::hex0<8>(entry.ThreadId) << " ";
}
f << entry.File << "(" << entry.Line << "): " << entry.Function;
f << std::endl;
}
return true;
}
void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id)
{
if(id == 0)
{
return;
}
switch(kind)
{
case ThreadKindGUI:
ThreadIdGUI = id;
break;
case ThreadKindAudio:
ThreadIdAudio = id;
break;
case ThreadKindNotify:
ThreadIdNotify = id;
break;
}
}
#endif // MPT_OS_WINDOWS
} // namespace Trace
#endif // MODPLUG_TRACKER
} // namespace log
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,264 @@
/*
* Logging.h
* ---------
* Purpose: General logging
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
/*
Build time logging configuration (in BuildSettings.h):
* #define NO_LOGGING
Disables all logging completely.
MPT_LOG calls are not even compiled but instead completely removed via the
preprocessor.
* #define MPT_LOG_GLOBAL_LEVEL_STATIC
#define MPT_LOG_GLOBAL_LEVEL #
Define the former (to anything) and the latter (to one of the log levels
below) in order to statically select the verbosity of logging at build time.
MPT_LOG calls that exceed the specified logging level will get dead-code
eliminated at compile time.
This especially means that, when setting MPT_LOG_GLOBAL_LEVEL to 0, no
MPT_LOG call (with a constant level parameter) remains in the resulting
binary, however, they still do get parsed and properly type checked by the
compiler.
Logging:
If the context is related to a particular CSoundfile instance, use
CSoundfile::AddToLog.
Logging a simple message:
MPT_LOG(LogWarning, "sounddev", "some message");
MPT_LOG(LogWarning, "sounddev", MPT_USTRING("some message"));
Facility is some course grained code section identifier (more coarse grained
than the current file name probably), useful to do some selective logging.
Logging a more complex message:
MPT_LOG(LogWarning, "sounddev", mpt::format(MPT_USTRING("Some message: foo=%1, bar=0x%2"))(foo, mpt::ufmt::hex0<8>(bar)));
Note that even with full enabled logging and a runtime configurable logging
level, the runtime overhead of a MPT_LOG(level, facility, text) call is just a
single conditional in case the verbosity does not require logging the respective
message. Even the expression "text" is not evaluated.
*/
enum LogLevel
{
LogDebug = 5,
LogInformation = 4,
LogNotification = 3,
LogWarning = 2,
LogError = 1
};
inline mpt::ustring LogLevelToString(LogLevel level)
{
switch(level)
{
case LogError: return MPT_USTRING("error"); break;
case LogWarning: return MPT_USTRING("warning"); break;
case LogNotification: return MPT_USTRING("notify"); break;
case LogInformation: return MPT_USTRING("info"); break;
case LogDebug: return MPT_USTRING("debug"); break;
}
return MPT_USTRING("unknown");
}
class ILog
{
protected:
virtual ~ILog() { }
public:
virtual void AddToLog(LogLevel level, const mpt::ustring &text) const = 0;
};
namespace mpt
{
namespace log
{
#ifndef NO_LOGGING
#if defined(MPT_LOG_GLOBAL_LEVEL_STATIC)
#if (MPT_LOG_GLOBAL_LEVEL <= 0)
// Logging framework is enabled (!NO_LOGGING) but all logging has beeen statically disabled.
// All logging code gets compiled and immediately dead-code eliminated.
#define MPT_LOG_IS_DISABLED
#endif
static const int GlobalLogLevel = MPT_LOG_GLOBAL_LEVEL ;
#else
extern int GlobalLogLevel;
#endif
#if defined(MODPLUG_TRACKER) && !defined(MPT_LOG_IS_DISABLED)
extern bool FileEnabled;
extern bool DebuggerEnabled;
extern bool ConsoleEnabled;
void SetFacilities(const std::string &solo, const std::string &blocked);
bool IsFacilityActive(const char *facility);
#else
static MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; }
#endif
#endif // !NO_LOGGING
struct Context
{
const char * const file;
const int line;
const char * const function;
MPT_FORCEINLINE Context(const char *file, int line, const char *function)
: file(file)
, line(line)
, function(function)
{
return;
}
MPT_FORCEINLINE Context(const Context &c)
: file(c.file)
, line(c.line)
, function(c.function)
{
return;
}
}; // class Context
#define MPT_LOG_CURRENTCONTEXT() mpt::log::Context( __FILE__ , __LINE__ , __FUNCTION__ )
#ifndef NO_LOGGING
class Logger
{
public:
// facility:ASCII
void SendLogMessage(const Context &context, LogLevel level, const char *facility, const mpt::ustring &text);
public:
// facility:ASCII, text:ASCII (only string literals)
template <std::size_t size> MPT_FORCEINLINE void SendLogMessage(const Context &context, LogLevel level, const char *facility, const char (&text)[size])
{
SendLogMessage(context, level, facility, mpt::ToUnicode(mpt::CharsetASCII, text));
}
};
#define MPT_LOG(level, facility, text) \
MPT_DO \
{ \
MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel >= ( level )) \
{ \
MPT_MAYBE_CONSTANT_IF(mpt::log::IsFacilityActive(( facility ))) \
{ \
mpt::log::Logger().SendLogMessage( MPT_LOG_CURRENTCONTEXT() , ( level ), ( facility ), ( text )); \
} \
} \
} MPT_WHILE_0 \
/**/
#define MPT_LEGACY_LOGLEVEL LogDebug
class LegacyLogger : public Logger
{
private:
const Context context;
public:
LegacyLogger(const Context &context) : context(context) {}
/* MPT_DEPRECATED */ void MPT_PRINTF_FUNC(2,3) operator () (const char *format, ...); // migrate to type-safe MPT_LOG
/* MPT_DEPRECATED */ void operator () (const AnyStringLocale &text); // migrate to properly namespaced MPT_LOG
/* MPT_DEPRECATED */ void operator () (LogLevel level, const mpt::ustring &text); // migrate to properly namespaced MPT_LOG
};
#define Log MPT_MAYBE_CONSTANT_IF(mpt::log::GlobalLogLevel < MPT_LEGACY_LOGLEVEL) { } else MPT_MAYBE_CONSTANT_IF(!mpt::log::IsFacilityActive("")) { } else mpt::log::LegacyLogger(MPT_LOG_CURRENTCONTEXT())
#else // !NO_LOGGING
#define MPT_LOG(level, facility, text) MPT_DO { } MPT_WHILE_0
struct LegacyLogger
{
inline void MPT_PRINTF_FUNC(2,3) operator () (const char * /*format*/ , ...) {}
inline void operator () (const AnyStringLocale & /*text*/ ) {}
inline void operator () (LogLevel /*level*/ , const mpt::ustring & /*text*/ ) {}
};
#define Log MPT_CONSTANT_IF(true) {} else mpt::log::LegacyLogger() // completely compile out arguments to Log() so that they do not even get evaluated
#endif // NO_LOGGING
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
namespace Trace {
// This is not strictly thread safe in all corner cases because of missing barriers.
// We do not care in order to not harm the fast path with additional barriers.
// Enabled tracing incurs a runtime overhead with multiple threads as a global atomic variable
// gets modified.
// This cacheline bouncing does not matter at all
// if there are not multiple thread adding trace points at high frequency (way greater than 1000Hz),
// which, in OpenMPT, is only ever the case for just a single thread (the audio thread), if at all.
extern bool volatile g_Enabled;
static inline bool IsEnabled() { return g_Enabled; }
MPT_NOINLINE void Trace(const mpt::log::Context & contexxt);
enum ThreadKind {
ThreadKindGUI,
ThreadKindAudio,
ThreadKindNotify,
};
void Enable(std::size_t numEntries);
void Disable();
void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id);
void Seal();
bool Dump(const mpt::PathString &filename);
#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_LOG_CURRENTCONTEXT()); } } MPT_WHILE_0
} // namespace Trace
#else // !MODPLUG_TRACKER
#define MPT_TRACE() MPT_DO { } MPT_WHILE_0
#endif // MODPLUG_TRACKER
} // namespace log
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,221 @@
/*
* Profiler.cpp
* ------------
* Purpose: Performance measuring
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "Profiler.h"
OPENMPT_NAMESPACE_BEGIN
#ifdef USE_PROFILER
class Statistics
{
public:
Profile &profile;
Profile::Data data;
double usage;
Statistics(Profile &p) : profile(p)
{
usage = 0.0;
Update();
}
void Update()
{
data = profile.GetAndResetData();
uint64 now = profile.GetTime();
uint64 timewindow = now - data.Start;
if(data.Calls > 0 && timewindow > 0)
{
usage = (double)data.Sum / (double)timewindow;
} else
{
usage = 0.0;
}
}
};
struct ProfileBlock
{
class Profile * profile;
const char * name;
class Statistics * stats;
};
static const std::size_t MAX_PROFILES = 1024;
static ProfileBlock Profiles[ MAX_PROFILES ];
static std::size_t NextProfile = 0;
static void RegisterProfile(Profile *newprofile)
{
if(NextProfile < MAX_PROFILES)
{
Profiles[NextProfile].profile = newprofile;
Profiles[NextProfile].stats = 0;
NextProfile++;
}
}
static void UnregisterProfile(Profile *oldprofile)
{
for(std::size_t i=0; i<NextProfile; i++) {
if(Profiles[i].profile == oldprofile) {
Profiles[i].profile = 0;
delete Profiles[i].stats;
Profiles[i].stats = 0;
}
}
}
void Profiler::Update()
{
for(std::size_t i=0; i<NextProfile; i++)
{
if(!Profiles[i].stats)
{
Profiles[i].stats = new Statistics(*Profiles[i].profile);
} else
{
Profiles[i].stats->Update();
}
}
}
std::string Profiler::DumpProfiles()
{
std::string ret;
for(std::size_t i=0; i<NextProfile; i++)
{
if(Profiles[i].stats)
{
Statistics &stats = *Profiles[i].stats;
std::string cat;
switch(stats.profile.Category)
{
case Profiler::GUI: cat = "GUI"; break;
case Profiler::Audio: cat = "Audio"; break;
case Profiler::Notify: cat = "Notify"; break;
}
ret += cat + " " + std::string(stats.profile.Name) + ": " + mpt::fmt::f("%6.3f", stats.usage * 100.0) + "%\r\n";
}
}
ret += "\r\n";
return ret;
}
std::vector<double> Profiler::DumpCategories()
{
std::vector<double> ret;
ret.resize(Profiler::CategoriesCount);
for(std::size_t i=0; i<NextProfile; i++)
{
if(Profiles[i].stats)
{
ret[Profiles[i].profile->Category] += Profiles[i].stats->usage;
}
}
return ret;
}
uint64 Profile::GetTime() const
{
LARGE_INTEGER ret;
ret.QuadPart = 0;
QueryPerformanceCounter(&ret);
return ret.QuadPart;
}
uint64 Profile::GetFrequency() const
{
LARGE_INTEGER ret;
ret.QuadPart = 0;
QueryPerformanceFrequency(&ret);
return ret.QuadPart;
}
Profile::Profile(Profiler::Category category, const char *name) : Category(category), Name(name)
{
data.Calls = 0;
data.Sum = 0;
data.Overhead = 0;
data.Start = GetTime();
EnterTime = 0;
RegisterProfile(this);
}
Profile::~Profile()
{
UnregisterProfile(this);
}
Profile::Data Profile::GetAndResetData()
{
Profile::Data ret;
datamutex.lock();
ret = data;
data.Calls = 0;
data.Sum = 0;
data.Overhead = 0;
data.Start = GetTime();
datamutex.unlock();
return ret;
}
void Profile::Reset()
{
datamutex.lock();
data.Calls = 0;
data.Sum = 0;
data.Overhead = 0;
data.Start = GetTime();
datamutex.unlock();
}
void Profile::Enter()
{
EnterTime = GetTime();
}
void Profile::Leave()
{
uint64 LeaveTime = GetTime();
datamutex.lock();
data.Calls += 1;
data.Sum += LeaveTime - EnterTime;
datamutex.unlock();
}
#else // !USE_PROFILER
MPT_MSVC_WORKAROUND_LNK4221(Profiler)
#endif // USE_PROFILER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,125 @@
/*
* Profiler.h
* ----------
* Purpose: Performance measuring
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "../common/mptMutex.h"
#include <string>
#include <vector>
OPENMPT_NAMESPACE_BEGIN
#if defined(MODPLUG_TRACKER)
//#define USE_PROFILER
#endif
#ifdef USE_PROFILER
class Profiler
{
public:
enum Category
{
GUI,
Audio,
Notify,
CategoriesCount
};
static std::vector<std::string> GetCategoryNames()
{
std::vector<std::string> ret;
ret.push_back("GUI");
ret.push_back("Audio");
ret.push_back("Notify");
return ret;
}
public:
static void Update();
static std::string DumpProfiles();
static std::vector<double> DumpCategories();
};
class Profile
{
private:
mutable mpt::mutex datamutex;
public:
struct Data
{
uint64 Calls;
uint64 Sum;
int64 Overhead;
uint64 Start;
};
public:
Data data;
uint64 EnterTime;
Profiler::Category Category;
const char * const Name;
uint64 GetTime() const;
uint64 GetFrequency() const;
public:
Profile(Profiler::Category category, const char *name);
~Profile();
void Reset();
void Enter();
void Leave();
class Scope
{
private:
Profile &profile;
public:
Scope(Profile &p) : profile(p) { profile.Enter(); }
~Scope() { profile.Leave(); }
};
public:
Data GetAndResetData();
};
#define OPENMPT_PROFILE_SCOPE(cat, name) \
static Profile OPENMPT_PROFILE_VAR(cat, name);\
Profile::Scope OPENMPT_PROFILE_SCOPE_VAR(OPENMPT_PROFILE_VAR); \
/**/
#define OPENMPT_PROFILE_FUNCTION(cat) OPENMPT_PROFILE_SCOPE(cat, __FUNCTION__)
#else // !USE_PROFILER
class Profiler
{
public:
enum Category
{
CategoriesCount
};
static std::vector<std::string> GetCategoryNames() { return std::vector<std::string>(); }
public:
static void Update() { }
static std::string DumpProfiles() { return std::string(); }
static std::vector<double> DumpCategories() { return std::vector<double>(); }
};
#define OPENMPT_PROFILE_SCOPE(cat, name) MPT_DO { } MPT_WHILE_0
#define OPENMPT_PROFILE_FUNCTION(cat) MPT_DO { } MPT_WHILE_0
#endif // USE_PROFILER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,410 @@
/*
* StringFixer.h
* -------------
* Purpose: Various functions for "fixing" char array strings for writing to or
* reading from module files, or for securing char arrays in general.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <algorithm>
#include <string>
#include <vector>
OPENMPT_NAMESPACE_BEGIN
namespace mpt { namespace String
{
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4127) // conditional expression is constant
#endif // MPT_COMPILER_MSVC
// Sets last character to null in given char array.
// Size of the array must be known at compile time.
template <size_t size>
void SetNullTerminator(char (&buffer)[size])
{
STATIC_ASSERT(size > 0);
buffer[size - 1] = 0;
}
inline void SetNullTerminator(char *buffer, size_t size)
{
MPT_ASSERT(size > 0);
buffer[size - 1] = 0;
}
template <size_t size>
void SetNullTerminator(wchar_t (&buffer)[size])
{
STATIC_ASSERT(size > 0);
buffer[size - 1] = 0;
}
inline void SetNullTerminator(wchar_t *buffer, size_t size)
{
MPT_ASSERT(size > 0);
buffer[size - 1] = 0;
}
// Remove any chars after the first null char
template <size_t size>
void FixNullString(char (&buffer)[size])
{
STATIC_ASSERT(size > 0);
SetNullTerminator(buffer);
size_t pos = 0;
// Find the first null char.
while(pos < size && buffer[pos] != '\0')
{
pos++;
}
// Remove everything after the null char.
while(pos < size)
{
buffer[pos++] = '\0';
}
}
inline void FixNullString(std::string & str)
{
for(std::size_t i = 0; i < str.length(); ++i)
{
if(str[i] == '\0')
{
// if we copied \0 in the middle of the buffer, terminate std::string here
str.resize(i);
break;
}
}
}
enum ReadWriteMode
{
// Reading / Writing: Standard null-terminated string handling.
nullTerminated,
// Reading: Source string is not guaranteed to be null-terminated (if it fills the whole char array).
// Writing: Destination string is not guaranteed to be null-terminated (if it fills the whole char array).
maybeNullTerminated,
// Reading: String may contain null characters anywhere. They should be treated as spaces.
// Writing: A space-padded string is written.
spacePadded,
// Reading: String may contain null characters anywhere. The last character is ignored (it is supposed to be 0).
// Writing: A space-padded string with a trailing null is written.
spacePaddedNull
};
namespace detail
{
static inline char NullToSpace(const char &c)
{
return (c != '\0') ? c : ' ';
}
}
// Copy a string from srcBuffer to destBuffer using a given read mode.
// Used for reading strings from files.
// Only use this version of the function if the size of the source buffer is variable.
template <ReadWriteMode mode, typename Tbyte>
void Read(std::string &dest, const Tbyte *srcBuffer, size_t srcSize)
{
const char *src = mpt::byte_cast<const char*>(srcBuffer);
dest.clear();
if(mode == nullTerminated || mode == spacePaddedNull)
{
// We assume that the last character of the source buffer is null.
if(srcSize > 0)
{
srcSize -= 1;
}
}
if(mode == nullTerminated || mode == maybeNullTerminated)
{
// Copy null-terminated string, stopping at null.
try
{
dest.assign(src, std::find(src, src + srcSize, '\0'));
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
}
} else if(mode == spacePadded || mode == spacePaddedNull)
{
try
{
// Copy string over.
dest.assign(src, src + srcSize);
// Convert null characters to spaces.
std::transform(dest.begin(), dest.end(), dest.begin(), detail::NullToSpace);
// Trim trailing spaces.
dest = mpt::String::RTrim(dest, std::string(" "));
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e);
}
}
}
// Copy a charset encoded string from srcBuffer to destBuffer using a given read mode.
// Used for reading strings from files.
// Only use this version of the function if the size of the source buffer is variable.
template <ReadWriteMode mode, typename Tbyte>
void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte *srcBuffer, size_t srcSize)
{
std::string tmp;
Read<mode>(tmp, srcBuffer, srcSize);
dest = mpt::ToUnicode(charset, tmp);
}
// Used for reading strings from files.
// Preferrably use this version of the function, it is safer.
template <ReadWriteMode mode, size_t srcSize, typename Tbyte>
void Read(std::string &dest, const Tbyte (&srcBuffer)[srcSize])
{
STATIC_ASSERT(srcSize > 0);
Read<mode>(dest, srcBuffer, srcSize);
}
// Used for reading charset encoded strings from files.
// Preferrably use this version of the function, it is safer.
template <ReadWriteMode mode, size_t srcSize, typename Tbyte>
void Read(mpt::ustring &dest, mpt::Charset charset, const Tbyte(&srcBuffer)[srcSize])
{
std::string tmp;
Read<mode>(tmp, srcBuffer);
dest = mpt::ToUnicode(charset, tmp);
}
// Copy a string from srcBuffer to destBuffer using a given read mode.
// Used for reading strings from files.
// Only use this version of the function if the size of the source buffer is variable.
template <ReadWriteMode mode, size_t destSize, typename Tbyte>
void Read(char (&destBuffer)[destSize], const Tbyte *srcBuffer, size_t srcSize)
{
STATIC_ASSERT(destSize > 0);
char *dst = destBuffer;
const char *src = mpt::byte_cast<const char*>(srcBuffer);
if(mode == nullTerminated || mode == spacePaddedNull)
{
// We assume that the last character of the source buffer is null.
if(srcSize > 0)
{
srcSize -= 1;
}
}
if(mode == nullTerminated || mode == maybeNullTerminated)
{
// Copy string and leave one character space in the destination buffer for null.
dst = std::copy(src, std::find(src, src + std::min(srcSize, destSize - 1), '\0'), dst);
} else if(mode == spacePadded || mode == spacePaddedNull)
{
// Copy string and leave one character space in the destination buffer for null.
// Convert nulls to spaces while copying.
dst = std::replace_copy(src, src + std::min(srcSize, destSize - 1), dst, '\0', ' ');
// Rewind dst to the first of any trailing spaces.
while(dst - destBuffer > 0)
{
dst--;
char c = *dst;
if(c != ' ')
{
dst++;
break;
}
}
}
// Fill rest of string with nulls.
std::fill(dst, destBuffer + destSize, '\0');
}
// Used for reading strings from files.
// Preferrably use this version of the function, it is safer.
template <ReadWriteMode mode, size_t destSize, size_t srcSize, typename Tbyte>
void Read(char (&destBuffer)[destSize], const Tbyte (&srcBuffer)[srcSize])
{
STATIC_ASSERT(destSize > 0);
STATIC_ASSERT(srcSize > 0);
Read<mode, destSize>(destBuffer, srcBuffer, srcSize);
}
// Copy a string from srcBuffer to destBuffer using a given write mode.
// You should only use this function if src and dest are dynamically sized,
// otherwise use one of the safer overloads below.
template <ReadWriteMode mode>
void Write(char *destBuffer, const size_t destSize, const char *srcBuffer, const size_t srcSize)
{
MPT_ASSERT(destSize > 0);
const size_t maxSize = std::min(destSize, srcSize);
char *dst = destBuffer;
const char *src = srcBuffer;
// First, copy over null-terminated string.
size_t pos = maxSize;
while(pos > 0)
{
if((*dst = *src) == '\0')
{
break;
}
pos--;
dst++;
src++;
}
if(mode == nullTerminated || mode == maybeNullTerminated)
{
// Fill rest of string with nulls.
std::fill(dst, dst + destSize - maxSize + pos, '\0');
} else if(mode == spacePadded || mode == spacePaddedNull)
{
// Fill the rest of the destination string with spaces.
std::fill(dst, dst + destSize - maxSize + pos, ' ');
}
if(mode == nullTerminated || mode == spacePaddedNull)
{
// Make sure that destination is really null-terminated.
SetNullTerminator(destBuffer, destSize);
}
}
// Copy a string from srcBuffer to a dynamically sized std::vector destBuffer using a given write mode.
// Used for writing strings to files.
// Only use this version of the function if the size of the source buffer is variable and the destination buffer also has variable size.
template <ReadWriteMode mode>
void Write(std::vector<char> &destBuffer, const char *srcBuffer, const size_t srcSize)
{
MPT_ASSERT(destBuffer.size() > 0);
Write<mode>(destBuffer.data(), destBuffer.size(), srcBuffer, srcSize);
}
// Copy a string from srcBuffer to destBuffer using a given write mode.
// Used for writing strings to files.
// Only use this version of the function if the size of the source buffer is variable.
template <ReadWriteMode mode, size_t destSize>
void Write(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize)
{
STATIC_ASSERT(destSize > 0);
Write<mode>(destBuffer, destSize, srcBuffer, srcSize);
}
// Copy a string from srcBuffer to destBuffer using a given write mode.
// Used for writing strings to files.
// Preferrably use this version of the function, it is safer.
template <ReadWriteMode mode, size_t destSize, size_t srcSize>
void Write(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize])
{
STATIC_ASSERT(destSize > 0);
STATIC_ASSERT(srcSize > 0);
Write<mode, destSize>(destBuffer, srcBuffer, srcSize);
}
template <ReadWriteMode mode>
void Write(char *destBuffer, const size_t destSize, const std::string &src)
{
MPT_ASSERT(destSize > 0);
Write<mode>(destBuffer, destSize, src.c_str(), src.length());
}
template <ReadWriteMode mode>
void Write(std::vector<char> &destBuffer, const std::string &src)
{
MPT_ASSERT(destBuffer.size() > 0);
Write<mode>(destBuffer, src.c_str(), src.length());
}
template <ReadWriteMode mode, size_t destSize>
void Write(char (&destBuffer)[destSize], const std::string &src)
{
STATIC_ASSERT(destSize > 0);
Write<mode, destSize>(destBuffer, src.c_str(), src.length());
}
// Copy from a char array to a fixed size char array.
template <size_t destSize>
void CopyN(char (&destBuffer)[destSize], const char *srcBuffer, const size_t srcSize = std::numeric_limits<size_t>::max())
{
const size_t copySize = std::min(destSize - 1u, srcSize);
std::strncpy(destBuffer, srcBuffer, copySize);
destBuffer[copySize] = '\0';
}
// Copy at most srcSize characters from srcBuffer to a std::string.
static inline void CopyN(std::string &dest, const char *srcBuffer, const size_t srcSize = std::numeric_limits<size_t>::max())
{
dest.assign(srcBuffer, srcBuffer + mpt::strnlen(srcBuffer, srcSize));
}
// Copy from one fixed size char array to another one.
template <size_t destSize, size_t srcSize>
void Copy(char (&destBuffer)[destSize], const char (&srcBuffer)[srcSize])
{
CopyN(destBuffer, srcBuffer, srcSize);
}
// Copy from a std::string to a fixed size char array.
template <size_t destSize>
void Copy(char (&destBuffer)[destSize], const std::string &src)
{
CopyN(destBuffer, src.c_str(), src.length());
}
// Copy from a fixed size char array to a std::string.
template <size_t srcSize>
void Copy(std::string &dest, const char (&srcBuffer)[srcSize])
{
CopyN(dest, srcBuffer, srcSize);
}
// Copy from a std::string to a std::string.
static inline void Copy(std::string &dest, const std::string &src)
{
dest.assign(src);
}
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
} } // namespace mpt::String
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,71 @@
/*
* WriteMemoryDump.h
* -----------------
* Purpose: Code for writing memory dumps to a file.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared
#endif // MPT_COMPILER_MSVC
#include <dbghelp.h>
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
OPENMPT_NAMESPACE_BEGIN
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);
static bool WriteMemoryDump(_EXCEPTION_POINTERS *pExceptionInfo, const WCHAR *filename, bool fullMemDump)
{
bool result = false;
HMODULE hDll = ::LoadLibraryW(L"DBGHELP.DLL");
if (hDll)
{
MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
if (pDump)
{
HANDLE hFile = ::CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
if(pExceptionInfo)
{
ExInfo.ThreadId = ::GetCurrentThreadId();
ExInfo.ExceptionPointers = pExceptionInfo;
ExInfo.ClientPointers = NULL;
}
pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
fullMemDump ?
(MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo
#if MPT_COMPILER_MSVC
| MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation
#endif
)
:
MiniDumpNormal,
pExceptionInfo ? &ExInfo : NULL, NULL, NULL);
::CloseHandle(hFile);
result = true;
}
}
::FreeLibrary(hDll);
}
return result;
}
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,135 @@
/*
* misc_util.cpp
* -------------
* Purpose: Various useful utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "misc_util.h"
OPENMPT_NAMESPACE_BEGIN
#ifdef MODPLUG_TRACKER
namespace Util
{
#if MPT_OS_WINDOWS
#endif // MPT_OS_WINDOWS
} // namespace Util
#endif // MODPLUG_TRACKER
namespace Util
{
static const MPT_UCHAR_TYPE EncodeNibble[16] = {
MPT_UCHAR('0'), MPT_UCHAR('1'), MPT_UCHAR('2'), MPT_UCHAR('3'),
MPT_UCHAR('4'), MPT_UCHAR('5'), MPT_UCHAR('6'), MPT_UCHAR('7'),
MPT_UCHAR('8'), MPT_UCHAR('9'), MPT_UCHAR('A'), MPT_UCHAR('B'),
MPT_UCHAR('C'), MPT_UCHAR('D'), MPT_UCHAR('E'), MPT_UCHAR('F') };
static inline bool DecodeByte(uint8 &byte, MPT_UCHAR_TYPE c1, MPT_UCHAR_TYPE c2)
{
byte = 0;
if(MPT_UCHAR('0') <= c1 && c1 <= MPT_UCHAR('9'))
{
byte += static_cast<uint8>((c1 - MPT_UCHAR('0')) << 4);
} else if(MPT_UCHAR('A') <= c1 && c1 <= MPT_UCHAR('F'))
{
byte += static_cast<uint8>((c1 - MPT_UCHAR('A') + 10) << 4);
} else if(MPT_UCHAR('a') <= c1 && c1 <= MPT_UCHAR('f'))
{
byte += static_cast<uint8>((c1 - MPT_UCHAR('a') + 10) << 4);
} else
{
return false;
}
if(MPT_UCHAR('0') <= c2 && c2 <= MPT_UCHAR('9'))
{
byte += static_cast<uint8>(c2 - MPT_UCHAR('0'));
} else if(MPT_UCHAR('A') <= c2 && c2 <= MPT_UCHAR('F'))
{
byte += static_cast<uint8>(c2 - MPT_UCHAR('A') + 10);
} else if(MPT_UCHAR('a') <= c2 && c2 <= MPT_UCHAR('f'))
{
byte += static_cast<uint8>(c2 - MPT_UCHAR('a') + 10);
} else
{
return false;
}
return true;
}
mpt::ustring BinToHex(mpt::const_byte_span src)
{
mpt::ustring result;
result.reserve(src.size() * 2);
for(uint8 byte : src)
{
result.push_back(EncodeNibble[(byte & 0xf0) >> 4]);
result.push_back(EncodeNibble[byte & 0x0f]);
}
return result;
}
std::vector<mpt::byte> HexToBin(const mpt::ustring &src)
{
std::vector<mpt::byte> result;
result.reserve(src.size() / 2);
for(std::size_t i = 0; (i + 1) < src.size(); i += 2)
{
uint8 byte = 0;
if(!DecodeByte(byte, src[i], src[i + 1]))
{
return result;
}
result.push_back(byte);
}
return result;
}
} // namespace Util
#if defined(MODPLUG_TRACKER) || (defined(LIBOPENMPT_BUILD) && defined(LIBOPENMPT_BUILD_TEST))
namespace mpt
{
std::string getenv(const std::string &env_var, const std::string &def)
{
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
MPT_UNREFERENCED_PARAMETER(env_var);
return def;
#else
const char *val = std::getenv(env_var.c_str());
if(!val)
{
return def;
}
return val;
#endif
}
} // namespace mpt
#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST)
OPENMPT_NAMESPACE_END

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,175 @@
/*
* mptBufferIO.h
* -------------
* Purpose: A wrapper around std::stringstream, fixing MSVC tell/seek problems with empty streams.
* Notes : You should only ever use these wrappers instead of plain std::stringstream classes.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <ios>
#include <istream>
#include <ostream>
#include <sstream>
#include <streambuf>
OPENMPT_NAMESPACE_BEGIN
// MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and
// std::stringstream) fail seekpos() and seekoff() when the stringbuf is
// currently empty.
// seekpos() and seekoff() can get called via tell*() or seek*() iostream
// members. seekoff() (and thereby tell*()), but not seekpos(), has been fixed
// from VS2010 onwards to handle this specific case and changed to not fail
// when the stringbuf is empty.
// Work-around strategy:
// As re-implementing or duplicating the whole std::stringbuf semantics would be
// rather convoluted, we make use of the knowledge of specific inner workings of
// the MSVC implementation here and just fix-up where it causes problems. This
// keeps the additional code at a minimum size.
namespace mpt
{
#if MPT_COMPILER_MSVC
class stringbuf
: public std::stringbuf
{
public:
stringbuf(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: std::stringbuf(mode)
{
return;
}
stringbuf(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: std::stringbuf(str, mode)
{
return;
}
public:
virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, std::ios_base::openmode which = std::ios_base::in | std::ios_base::out)
{
pos_type result = std::stringbuf::seekoff(off, way, which);
if(result == pos_type(-1))
{
if((which & std::ios_base::in) || (which & std::ios_base::out))
{
if(off == 0)
{
result = 0;
}
}
}
return result;
}
virtual pos_type seekpos(pos_type ptr, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
pos_type result = std::stringbuf::seekpos(ptr, mode);
if(result == pos_type(-1))
{
if((mode & std::ios_base::in) || (mode & std::ios_base::out))
{
if(static_cast<std::streamoff>(ptr) == 0)
{
result = 0;
}
}
}
return result;
}
};
class istringstream
: public std::basic_istream<char>
{
private:
mpt::stringbuf buf;
public:
istringstream(std::ios_base::openmode mode = std::ios_base::in)
: std::basic_istream<char>(&buf)
, buf(mode | std::ios_base::in)
{
}
istringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in)
: std::basic_istream<char>(&buf)
, buf(str, mode | std::ios_base::in)
{
}
~istringstream()
{
}
public:
mpt::stringbuf *rdbuf() const { return const_cast<mpt::stringbuf*>(&buf); }
std::string str() const { return buf.str(); }
void str(const std::string &str) { buf.str(str); }
};
class ostringstream
: public std::basic_ostream<char>
{
private:
mpt::stringbuf buf;
public:
ostringstream(std::ios_base::openmode mode = std::ios_base::out)
: std::basic_ostream<char>(&buf)
, buf(mode | std::ios_base::out)
{
}
ostringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::out)
: std::basic_ostream<char>(&buf)
, buf(str, mode | std::ios_base::out)
{
}
~ostringstream()
{
}
public:
mpt::stringbuf *rdbuf() const { return const_cast<mpt::stringbuf*>(&buf); }
std::string str() const { return buf.str(); }
void str(const std::string &str) { buf.str(str); }
};
class stringstream
: public std::basic_iostream<char>
{
private:
mpt::stringbuf buf;
public:
stringstream(std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: std::basic_iostream<char>(&buf)
, buf(mode | std::ios_base::in | std::ios_base::out)
{
}
stringstream(const std::string &str, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: std::basic_iostream<char>(&buf)
, buf(str, mode | std::ios_base::in | std::ios_base::out)
{
}
~stringstream()
{
}
public:
mpt::stringbuf *rdbuf() const { return const_cast<mpt::stringbuf*>(&buf); }
std::string str() const { return buf.str(); }
void str(const std::string &str) { buf.str(str); }
};
#else
typedef std::stringbuf stringbuf;
typedef std::istringstream istringstream;
typedef std::ostringstream ostringstream;
typedef std::stringstream stringstream;
#endif
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,376 @@
/*
* mptCPU.cpp
* ----------
* Purpose: CPU feature detection.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptCPU.h"
OPENMPT_NAMESPACE_BEGIN
#if defined(ENABLE_ASM)
uint32 RealProcSupport = 0;
uint32 ProcSupport = 0;
char ProcVendorID[16+1] = "";
uint16 ProcFamily = 0;
uint8 ProcModel = 0;
uint8 ProcStepping = 0;
#if MPT_COMPILER_MSVC && (defined(ENABLE_X86) || defined(ENABLE_X64))
#include <intrin.h>
typedef char cpuid_result_string[12];
struct cpuid_result {
uint32 a;
uint32 b;
uint32 c;
uint32 d;
std::string as_string() const
{
cpuid_result_string result;
result[0+0] = (b >> 0) & 0xff;
result[0+1] = (b >> 8) & 0xff;
result[0+2] = (b >>16) & 0xff;
result[0+3] = (b >>24) & 0xff;
result[4+0] = (d >> 0) & 0xff;
result[4+1] = (d >> 8) & 0xff;
result[4+2] = (d >>16) & 0xff;
result[4+3] = (d >>24) & 0xff;
result[8+0] = (c >> 0) & 0xff;
result[8+1] = (c >> 8) & 0xff;
result[8+2] = (c >>16) & 0xff;
result[8+3] = (c >>24) & 0xff;
return std::string(result, result + 12);
}
};
static cpuid_result cpuid(uint32 function)
{
cpuid_result result;
int CPUInfo[4];
__cpuid(CPUInfo, function);
result.a = CPUInfo[0];
result.b = CPUInfo[1];
result.c = CPUInfo[2];
result.d = CPUInfo[3];
return result;
}
#if 0
static cpuid_result cpuidex(uint32 function_a, uint32 function_c)
{
cpuid_result result;
int CPUInfo[4];
__cpuidex(CPUInfo, function_a, function_c);
result.a = CPUInfo[0];
result.b = CPUInfo[1];
result.c = CPUInfo[2];
result.d = CPUInfo[3];
return result;
}
#endif
static MPT_NOINLINE bool has_cpuid()
{
const size_t eflags_cpuid = 1 << 21;
size_t eflags_old = __readeflags();
size_t eflags_flipped = eflags_old ^ eflags_cpuid;
__writeeflags(eflags_flipped);
size_t eflags_testchanged = __readeflags();
__writeeflags(eflags_old);
return ((eflags_testchanged ^ eflags_old) & eflags_cpuid) != 0;
}
void InitProcSupport()
{
RealProcSupport = 0;
ProcSupport = 0;
MemsetZero(ProcVendorID);
ProcFamily = 0;
ProcModel = 0;
ProcStepping = 0;
if(has_cpuid())
{
ProcSupport |= PROCSUPPORT_CPUID;
cpuid_result VendorString = cpuid(0x00000000u);
std::strcpy(ProcVendorID, VendorString.as_string().c_str());
// Cyrix 6x86 and 6x86MX do not specify the value returned in eax.
// They both support 0x00000001u however.
if((VendorString.as_string() == "CyrixInstead") || (VendorString.a >= 0x00000001u))
{
cpuid_result StandardFeatureFlags = cpuid(0x00000001u);
uint32 Stepping = (StandardFeatureFlags.a >> 0) & 0x0f;
uint32 BaseModel = (StandardFeatureFlags.a >> 4) & 0x0f;
uint32 BaseFamily = (StandardFeatureFlags.a >> 8) & 0x0f;
uint32 ExtModel = (StandardFeatureFlags.a >> 16) & 0x0f;
uint32 ExtFamily = (StandardFeatureFlags.a >> 20) & 0xff;
if(VendorString.as_string() == "GenuineIntel")
{
if(BaseFamily == 0xf)
{
ProcFamily = static_cast<uint16>(ExtFamily + BaseFamily);
} else
{
ProcFamily = static_cast<uint16>(BaseFamily);
}
if(BaseFamily == 0x6 || BaseFamily == 0xf)
{
ProcModel = static_cast<uint8>((ExtModel << 4) | (BaseModel << 0));
} else
{
ProcModel = static_cast<uint8>(BaseModel);
}
} else if(VendorString.as_string() == "AuthenticAMD")
{
if(BaseFamily == 0xf)
{
ProcFamily = static_cast<uint16>(ExtFamily + BaseFamily);
ProcModel = static_cast<uint8>((ExtModel << 4) | (BaseModel << 0));
} else
{
ProcFamily = static_cast<uint16>(BaseFamily);
ProcModel = static_cast<uint8>(BaseModel);
}
} else
{
ProcFamily = static_cast<uint16>(BaseFamily);
ProcModel = static_cast<uint8>(BaseModel);
}
ProcStepping = static_cast<uint8>(Stepping);
if(StandardFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC;
if(StandardFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV;
if(StandardFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU;
if(StandardFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX;
if(StandardFeatureFlags.d & (1<<25)) ProcSupport |= PROCSUPPORT_SSE;
if(StandardFeatureFlags.d & (1<<26)) ProcSupport |= PROCSUPPORT_SSE2;
if(StandardFeatureFlags.c & (1<< 0)) ProcSupport |= PROCSUPPORT_SSE3;
if(StandardFeatureFlags.c & (1<< 9)) ProcSupport |= PROCSUPPORT_SSSE3;
if(StandardFeatureFlags.c & (1<<19)) ProcSupport |= PROCSUPPORT_SSE4_1;
if(StandardFeatureFlags.c & (1<<20)) ProcSupport |= PROCSUPPORT_SSE4_2;
}
// 3DNow! manual recommends to just execute 0x80000000u.
// It is totally unknown how earlier CPUs from other vendors
// would behave.
// Thus we only execute 0x80000000u on other vendors CPUs for the earliest
// that we found it documented for and that actually supports 3DNow!.
// We only need 0x80000000u in order to detect 3DNow!.
// Thus, this is enough for us.
if((VendorString.as_string() == "AuthenticAMD") || (VendorString.as_string() == "AMDisbetter!"))
{ // AMD
if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 8)))
{ // >= K6-2 (K6 = Family 5, K6-2 = Model 8)
// Not sure if earlier AMD CPUs support 0x80000000u.
// AMD 5k86 and AMD K5 manuals do not mention it.
cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u)
{
cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
if(ExtendedFeatureFlags.d & (1<< 4)) ProcSupport |= PROCSUPPORT_TSC;
if(ExtendedFeatureFlags.d & (1<<15)) ProcSupport |= PROCSUPPORT_CMOV;
if(ExtendedFeatureFlags.d & (1<< 0)) ProcSupport |= PROCSUPPORT_FPU;
if(ExtendedFeatureFlags.d & (1<<23)) ProcSupport |= PROCSUPPORT_MMX;
if(ExtendedFeatureFlags.d & (1<<22)) ProcSupport |= PROCSUPPORT_AMD_MMXEXT;
if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW;
if(ExtendedFeatureFlags.d & (1<<30)) ProcSupport |= PROCSUPPORT_AMD_3DNOWEXT;
}
}
} else if(VendorString.as_string() == "CentaurHauls")
{ // Centaur (IDT WinChip or VIA C3)
if(ProcFamily == 5)
{ // IDT
if(ProcModel >= 8)
{ // >= WinChip 2
cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u)
{
cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW;
}
}
} else if(ProcFamily >= 6)
{ // VIA
if((ProcFamily >= 7) || ((ProcFamily == 6) && (ProcModel >= 7)))
{ // >= C3 Samuel 2
cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u)
{
cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW;
}
}
}
} else if(VendorString.as_string() == "CyrixInstead")
{ // Cyrix
// 6x86 : 5.2.x
// 6x86L : 5.2.x
// MediaGX : 4.4.x
// 6x86MX : 6.0.x
// MII : 6.0.x
// MediaGXm: 5.4.x
// well, doh ...
if((ProcFamily == 5) && (ProcModel >= 4))
{ // Cyrix MediaGXm
cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u)
{
cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW;
}
}
} else if(VendorString.as_string() == "Geode by NSC")
{ // National Semiconductor
if((ProcFamily > 5) || ((ProcFamily == 5) && (ProcModel >= 5)))
{ // >= Geode GX2
cpuid_result ExtendedVendorString = cpuid(0x80000000u);
if(ExtendedVendorString.a >= 0x80000001u)
{
cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
if(ExtendedFeatureFlags.d & (1<<31)) ProcSupport |= PROCSUPPORT_AMD_3DNOW;
}
}
}
} else
{
ProcSupport |= PROCSUPPORT_FPU; // We assume FPU because we require it.
}
// We do not have to check if SSE got enabled by the OS because we only do
// support Windows >= 98 SE which will always enable SSE if available.
RealProcSupport = ProcSupport;
}
#else // !( MPT_COMPILER_MSVC && ENABLE_X86 )
void InitProcSupport()
{
ProcSupport = 0;
}
#endif // MPT_COMPILER_MSVC && ENABLE_X86
#endif // ENABLE_ASM
#ifdef MODPLUG_TRACKER
uint32 GetMinimumProcSupportFlags()
{
uint32 flags = 0;
#ifdef ENABLE_ASM
#if MPT_COMPILER_MSVC
#if defined(_M_X64)
flags |= PROCSUPPORT_AMD64;
#elif defined(_M_IX86)
#if defined(_M_IX86_FP)
#if (_M_IX86_FP >= 2)
flags |= PROCSUPPORT_x86_SSE2;
#elif (_M_IX86_FP == 1)
flags |= PROCSUPPORT_x86_SSE;
#endif
#else
flags |= PROCSUPPORT_i586;
#endif
#endif
#endif
#endif // ENABLE_ASM
return flags;
}
int GetMinimumSSEVersion()
{
int minimumSSEVersion = 0;
#if MPT_COMPILER_MSVC
#if defined(_M_X64)
minimumSSEVersion = 2;
#elif defined(_M_IX86)
#if defined(_M_IX86_FP)
#if (_M_IX86_FP >= 2)
minimumSSEVersion = 2;
#elif (_M_IX86_FP == 1)
minimumSSEVersion = 1;
#endif
#endif
#endif
#endif
return minimumSSEVersion;
}
int GetMinimumAVXVersion()
{
int minimumAVXVersion = 0;
#if MPT_COMPILER_MSVC
#if defined(_M_IX86_FP)
#if defined(__AVX2__)
minimumAVXVersion = 2;
#elif defined(__AVX__)
minimumAVXVersion = 1;
#endif
#endif
#endif
return minimumAVXVersion;
}
#endif
#if !defined(MODPLUG_TRACKER) && !defined(ENABLE_ASM)
MPT_MSVC_WORKAROUND_LNK4221(mptCPU)
#endif
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,72 @@
/*
* mptCPU.h
* --------
* Purpose: CPU feature detection.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
#ifdef ENABLE_ASM
#define PROCSUPPORT_CPUID 0x00001 // Processor supports CPUID instruction (i586)
#define PROCSUPPORT_TSC 0x00002 // Processor supports RDTSC instruction (i586)
#define PROCSUPPORT_CMOV 0x00004 // Processor supports conditional move instructions (i686)
#define PROCSUPPORT_FPU 0x00008 // Processor supports x87 instructions
#define PROCSUPPORT_MMX 0x00010 // Processor supports MMX instructions
#define PROCSUPPORT_AMD_MMXEXT 0x00020 // Processor supports AMD MMX extensions
#define PROCSUPPORT_AMD_3DNOW 0x00040 // Processor supports AMD 3DNow! instructions
#define PROCSUPPORT_AMD_3DNOWEXT 0x00080 // Processor supports AMD 3DNow!2 instructions
#define PROCSUPPORT_SSE 0x00100 // Processor supports SSE instructions
#define PROCSUPPORT_SSE2 0x00200 // Processor supports SSE2 instructions
#define PROCSUPPORT_SSE3 0x00400 // Processor supports SSE3 instructions
#define PROCSUPPORT_SSSE3 0x00800 // Processor supports SSSE3 instructions
#define PROCSUPPORT_SSE4_1 0x01000 // Processor supports SSE4.1 instructions
#define PROCSUPPORT_SSE4_2 0x02000 // Processor supports SSE4.2 instructions
static const uint32 PROCSUPPORT_i486 = 0u | PROCSUPPORT_FPU ;
static const uint32 PROCSUPPORT_i586 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_FPU ;
static const uint32 PROCSUPPORT_i686 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU ;
static const uint32 PROCSUPPORT_x86_SSE = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE ;
static const uint32 PROCSUPPORT_x86_SSE2 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_FPU | PROCSUPPORT_SSE | PROCSUPPORT_SSE2;
static const uint32 PROCSUPPORT_AMD64 = 0u | PROCSUPPORT_CPUID | PROCSUPPORT_CMOV | PROCSUPPORT_SSE | PROCSUPPORT_SSE2;
extern uint32 RealProcSupport;
extern uint32 ProcSupport;
extern char ProcVendorID[16+1];
extern uint16 ProcFamily;
extern uint8 ProcModel;
extern uint8 ProcStepping;
void InitProcSupport();
// enabled processor features for inline asm and intrinsics
static inline uint32 GetProcSupport()
{
return ProcSupport;
}
// available processor features
static inline uint32 GetRealProcSupport()
{
return RealProcSupport;
}
#endif // ENABLE_ASM
#ifdef MODPLUG_TRACKER
uint32 GetMinimumProcSupportFlags();
int GetMinimumSSEVersion();
int GetMinimumAVXVersion();
#endif
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,245 @@
/*
* mptCRC.h
* --------
* Purpose: generic CRC implementation
* Notes : (currently none)
* Authors: Joern Heusipp
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace checksum
{
template <typename T, T polynomial, T initial, T resultXOR, bool reverseData>
class crc
{
public:
typedef crc self_type;
typedef T value_type;
typedef uint8 byte_type;
static const std::size_t size_bytes = sizeof(value_type);
static const std::size_t size_bits = sizeof(value_type) * 8;
static const value_type top_bit = static_cast<value_type>(1) << ((sizeof(value_type) * 8) - 1);
private:
template <typename Tint>
static inline Tint reverse(Tint value)
{
const std::size_t bits = sizeof(Tint) * 8;
Tint result = 0;
for(std::size_t i = 0; i < bits; ++i)
{
result <<= 1;
result |= static_cast<Tint>(value & 0x1);
value >>= 1;
}
return result;
}
static inline value_type calculate_table_entry(byte_type pos)
{
value_type value = 0;
value = (static_cast<value_type>(reverseData ? reverse(pos) : pos) << (size_bits - 8));
for(std::size_t bit = 0; bit < 8; ++bit)
{
if(value & top_bit)
{
value = (value << 1) ^ polynomial;
} else
{
value = (value << 1);
}
}
value = (reverseData ? reverse(value) : value);
return value;
}
private:
static value_type table[256];
static inline void fill_table()
{
for(std::size_t i = 0; i < 256; ++i)
{
table[i] = calculate_table_entry(static_cast<byte_type>(i));
}
}
struct table_filler
{
inline table_filler()
{
self_type::fill_table();
}
};
static inline void init()
{
static table_filler table_filler;
}
private:
inline value_type read_table(byte_type pos) const
{
return table[pos];
}
private:
value_type value;
public:
crc()
: value(initial)
{
init();
}
inline void processByte(byte_type byte)
{
MPT_CONSTANT_IF(reverseData)
{
value = (value >> 8) ^ read_table(static_cast<byte_type>((value & 0xff) ^ byte));
} else
{
value = (value << 8) ^ read_table(static_cast<byte_type>(((value >> (size_bits - 8)) & 0xff) ^ byte));
}
}
inline value_type result() const
{
return (value ^ resultXOR);
}
public:
inline operator value_type () const
{
return result();
}
inline crc & process(char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & process(signed char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & process(unsigned char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
template <typename InputIt>
crc & process(InputIt beg, InputIt end)
{
for(InputIt it = beg; it != end; ++it)
{
static_assert(sizeof(*it) == 1, "1 byte type required");
process(*it);
}
return *this;
}
template <typename Container>
inline crc & process(const Container &data)
{
operator () (data.begin(), data.end());
return *this;
}
inline crc & operator () (char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & operator () (signed char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
inline crc & operator () (unsigned char c)
{
processByte(static_cast<byte_type>(c));
return *this;
}
template <typename InputIt>
crc & operator () (InputIt beg, InputIt end)
{
for(InputIt it = beg; it != end; ++it)
{
static_assert(sizeof(*it) == 1, "1 byte type required");
operator () (*it);
}
return *this;
}
template <typename Container>
inline crc & operator () (const Container &data)
{
operator () (data.begin(), data.end());
return *this;
}
template <typename InputIt>
crc(InputIt beg, InputIt end)
: value(initial)
{
init();
for(InputIt it = beg; it != end; ++it)
{
static_assert(sizeof(*it) == 1, "1 byte type required");
process(*it);
}
}
template <typename Container>
inline crc(const Container &data)
: value(initial)
{
init();
process(data.begin(), data.end());
}
};
template <typename T, T polynomial, T initial, T resultXOR, bool reverseData>
typename crc<T, polynomial, initial, resultXOR, reverseData>::value_type crc<T, polynomial, initial, resultXOR, reverseData>::table[256];
typedef crc<uint16, 0x8005, 0, 0, true> crc16;
typedef crc<uint32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true> crc32;
typedef crc<uint32, 0x04C11DB7, 0, 0, false> crc32_ogg;
typedef crc<uint32, 0x1EDC6F41, 0xFFFFFFFF, 0xFFFFFFFF, true> crc32c;
typedef crc<uint64, 0xAD93D23594C935A9ull, 0xFFFFFFFFFFFFFFFFull, 0, true> crc64_jones;
} // namespace checksum
using mpt::checksum::crc32;
using mpt::checksum::crc32_ogg;
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,383 @@
/*
* mptFileIO.cpp
* -------------
* Purpose: File I/O wrappers
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptFileIO.h"
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
#include <WinIoCtl.h>
#include <io.h>
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_FILEIO)
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
bool SetFilesystemCompression(HANDLE hFile)
{
if(hFile == INVALID_HANDLE_VALUE)
{
return false;
}
USHORT format = COMPRESSION_FORMAT_DEFAULT;
DWORD dummy = 0;
BOOL result = DeviceIoControl(hFile, FSCTL_SET_COMPRESSION, (LPVOID)&format, sizeof(format), NULL, 0, &dummy /*required*/ , NULL);
return result ? true : false;
}
bool SetFilesystemCompression(int fd)
{
if(fd < 0)
{
return false;
}
uintptr_t fhandle = _get_osfhandle(fd);
HANDLE hFile = (HANDLE)fhandle;
if(hFile == INVALID_HANDLE_VALUE)
{
return false;
}
return SetFilesystemCompression(hFile);
}
#if defined(MPT_ENABLE_FILEIO_STDIO)
bool SetFilesystemCompression(FILE *file)
{
if(!file)
{
return false;
}
int fd = _fileno(file);
if(fd == -1)
{
return false;
}
return SetFilesystemCompression(fd);
}
#endif // MPT_ENABLE_FILEIO_STDIO
bool SetFilesystemCompression(const mpt::PathString &filename)
{
DWORD attributes = GetFileAttributesW(filename.AsNativePrefixed().c_str());
if(attributes == INVALID_FILE_ATTRIBUTES)
{
return false;
}
if(attributes & FILE_ATTRIBUTE_COMPRESSED)
{
return true;
}
HANDLE hFile = CreateFileW(filename.AsNativePrefixed().c_str(), GENERIC_ALL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
return false;
}
bool result = SetFilesystemCompression(hFile);
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
return result;
}
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
namespace mpt {
LazyFileRef & LazyFileRef::operator = (const std::vector<mpt::byte> &data)
{
mpt::ofstream file(m_Filename, std::ios::binary);
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::WriteRaw(file, data.data(), data.size());
mpt::IO::Flush(file);
return *this;
}
LazyFileRef & LazyFileRef::operator = (const std::vector<char> &data)
{
mpt::ofstream file(m_Filename, std::ios::binary);
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::WriteRaw(file, data.data(), data.size());
mpt::IO::Flush(file);
return *this;
}
LazyFileRef & LazyFileRef::operator = (const std::string &data)
{
mpt::ofstream file(m_Filename, std::ios::binary);
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::WriteRaw(file, data.data(), data.size());
mpt::IO::Flush(file);
return *this;
}
LazyFileRef::operator std::vector<mpt::byte> () const
{
mpt::ifstream file(m_Filename, std::ios::binary);
if(!mpt::IO::IsValid(file))
{
return std::vector<mpt::byte>();
}
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::SeekEnd(file);
std::vector<mpt::byte> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file)));
mpt::IO::SeekBegin(file);
mpt::IO::ReadRaw(file, buf.data(), buf.size());
return buf;
}
LazyFileRef::operator std::vector<char> () const
{
mpt::ifstream file(m_Filename, std::ios::binary);
if(!mpt::IO::IsValid(file))
{
return std::vector<char>();
}
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::SeekEnd(file);
std::vector<char> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file)));
mpt::IO::SeekBegin(file);
mpt::IO::ReadRaw(file, buf.data(), buf.size());
return buf;
}
LazyFileRef::operator std::string () const
{
mpt::ifstream file(m_Filename, std::ios::binary);
if(!mpt::IO::IsValid(file))
{
return std::string();
}
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::SeekEnd(file);
std::vector<char> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file)));
mpt::IO::SeekBegin(file);
mpt::IO::ReadRaw(file, buf.data(), buf.size());
return std::string(buf.begin(), buf.end());
}
} // namespace mpt
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
CMappedFile::~CMappedFile()
{
Close();
}
bool CMappedFile::Open(const mpt::PathString &filename)
{
m_hFile = CreateFileW(
filename.AsNativePrefixed().c_str(),
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(m_hFile == INVALID_HANDLE_VALUE)
{
m_hFile = nullptr;
return false;
}
m_FileName = filename;
return true;
}
void CMappedFile::Close()
{
m_FileName = mpt::PathString();
// Unlock file
if(m_hFMap)
{
if(m_pData)
{
UnmapViewOfFile(m_pData);
m_pData = nullptr;
}
CloseHandle(m_hFMap);
m_hFMap = nullptr;
} else if(m_pData)
{
free(m_pData);
m_pData = nullptr;
}
// Close file handle
if(m_hFile)
{
CloseHandle(m_hFile);
m_hFile = nullptr;
}
}
size_t CMappedFile::GetLength()
{
LARGE_INTEGER size;
if(GetFileSizeEx(m_hFile, &size) == FALSE)
{
return 0;
}
return mpt::saturate_cast<size_t>(size.QuadPart);
}
const mpt::byte *CMappedFile::Lock()
{
size_t length = GetLength();
if(!length) return nullptr;
void *lpStream;
HANDLE hmf = CreateFileMapping(
m_hFile,
NULL,
PAGE_READONLY,
0, 0,
NULL);
// Try memory-mapping first
if(hmf)
{
lpStream = MapViewOfFile(
hmf,
FILE_MAP_READ,
0, 0,
length);
if(lpStream)
{
m_hFMap = hmf;
m_pData = lpStream;
return mpt::void_cast<const mpt::byte*>(lpStream);
}
CloseHandle(hmf);
hmf = nullptr;
}
// Fallback if memory-mapping fails for some weird reason
if((lpStream = malloc(length)) == nullptr) return nullptr;
memset(lpStream, 0, length);
size_t bytesToRead = length;
size_t bytesRead = 0;
while(bytesToRead > 0)
{
DWORD chunkToRead = mpt::saturate_cast<DWORD>(length);
DWORD chunkRead = 0;
if(ReadFile(m_hFile, mpt::void_cast<mpt::byte*>(lpStream) + bytesRead, chunkToRead, &chunkRead, NULL) == FALSE)
{
// error
free(lpStream);
return nullptr;
}
bytesRead += chunkRead;
bytesToRead -= chunkRead;
}
m_pData = lpStream;
return mpt::void_cast<const mpt::byte*>(lpStream);
}
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
InputFile::InputFile()
{
return;
}
InputFile::InputFile(const mpt::PathString &filename)
: m_Filename(filename)
{
#if defined(MPT_FILEREADER_STD_ISTREAM)
m_File.open(m_Filename, std::ios::binary | std::ios::in);
#else
m_File.Open(m_Filename);
#endif
}
InputFile::~InputFile()
{
return;
}
bool InputFile::Open(const mpt::PathString &filename)
{
m_Filename = filename;
#if defined(MPT_FILEREADER_STD_ISTREAM)
m_File.open(m_Filename, std::ios::binary | std::ios::in);
return m_File.good();
#else
return m_File.Open(m_Filename);
#endif
}
bool InputFile::IsValid() const
{
#if defined(MPT_FILEREADER_STD_ISTREAM)
return m_File.good();
#else
return m_File.IsOpen();
#endif
}
#if defined(MPT_FILEREADER_STD_ISTREAM)
InputFile::ContentsRef InputFile::Get()
{
InputFile::ContentsRef result;
result.first = &m_File;
result.second = m_File.good() ? &m_Filename : nullptr;
return result;
}
#else
InputFile::ContentsRef InputFile::Get()
{
InputFile::ContentsRef result;
result.first.data = nullptr;
result.first.size = 0;
result.second = nullptr;
if(!m_File.IsOpen())
{
return result;
}
result.first.data = m_File.Lock();
result.first.size = m_File.GetLength();
result.second = &m_Filename;
return result;
}
#endif
#else // !MPT_ENABLE_FILEIO
MPT_MSVC_WORKAROUND_LNK4221(mptFileIO)
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,589 @@
/*
* mptFileIO.h
* -----------
* Purpose: A wrapper around std::fstream, fixing VS2008 charset conversion braindamage, and enforcing usage of mpt::PathString.
* Notes : You should only ever use these wrappers instead of plain std::fstream classes.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#if defined(MPT_ENABLE_FILEIO)
#include "../common/mptString.h"
#include "../common/mptPathString.h"
#include "../common/mptIO.h"
#include <fstream>
#include <ios>
#include <ostream>
#include <streambuf>
#include <utility>
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_FILEIO)
#if defined(MPT_ENABLE_FILEIO_STDIO)
static inline FILE * mpt_fopen(const mpt::PathString &filename, const char *mode)
{
#if MPT_OS_WINDOWS
return _wfopen(filename.AsNativePrefixed().c_str(), mode ? mpt::ToWide(mpt::CharsetASCII, mode).c_str() : L"");
#else // !MPT_OS_WINDOWS
return fopen(filename.AsNative().c_str(), mode);
#endif // MPT_OS_WINDOWS
}
#endif // MPT_ENABLE_FILEIO_STDIO
// Sets the NTFS compression attribute on the file or directory.
// Requires read and write permissions for already opened files.
// Returns true if the attribute has been set.
// In almost all cases, the return value should be ignored because most filesystems other than NTFS do not support compression.
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
bool SetFilesystemCompression(HANDLE hFile);
bool SetFilesystemCompression(int fd);
#if defined(MPT_ENABLE_FILEIO_STDIO)
bool SetFilesystemCompression(FILE *file);
#endif // MPT_ENABLE_FILEIO_STDIO
bool SetFilesystemCompression(const mpt::PathString &filename);
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
namespace mpt
{
#if MPT_COMPILER_GCC
#if MPT_OS_WINDOWS
// GCC C++ library has no wchar_t overloads
#define MPT_FSTREAM_DO_CONVERSIONS
#define MPT_FSTREAM_DO_CONVERSIONS_ANSI
#endif
#endif
#ifdef MPT_FSTREAM_DO_CONVERSIONS
#define MPT_FSTREAM_OPEN(filename, mode) detail::fstream_open<Tbase>(*this, (filename), (mode))
#else
#define MPT_FSTREAM_OPEN(filename, mode) Tbase::open((filename), (mode))
#endif
namespace detail
{
template<typename Tbase>
inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode)
{
#if defined(MPT_FSTREAM_DO_CONVERSIONS_ANSI)
base.open(mpt::ToCharset(mpt::CharsetLocale, filename.AsNative()).c_str(), mode);
#else
base.open(filename.AsNativePrefixed().c_str(), mode);
#endif
}
#ifdef MPT_FSTREAM_DO_CONVERSIONS
template<typename Tbase>
inline void fstream_open(Tbase & base, const std::wstring & filename, std::ios_base::openmode mode)
{
detail::fstream_open<Tbase>(base, mpt::PathString::FromWide(filename), mode);
}
template<typename Tbase>
inline void fstream_open(Tbase & base, const wchar_t * filename, std::ios_base::openmode mode)
{
detail::fstream_open<Tbase>(base, mpt::PathString::FromWide(filename ? std::wstring(filename) : std::wstring()), mode);
}
template<typename Tbase>
inline void fstream_open(Tbase & base, const std::string & filename, std::ios_base::openmode mode)
{
detail::fstream_open<Tbase>(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename)), mode);
}
template<typename Tbase>
inline void fstream_open(Tbase & base, const char * filename, std::ios_base::openmode mode)
{
detail::fstream_open<Tbase>(base, mpt::PathString::FromWide(mpt::ToWide(mpt::CharsetLocale, filename ? std::string(filename) : std::string())), mode);
}
#endif
} // namespace detail
class fstream
: public std::fstream
{
private:
typedef std::fstream Tbase;
public:
fstream() {}
fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#if MPT_OS_WINDOWS
MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#endif
};
class ifstream
: public std::ifstream
{
private:
typedef std::ifstream Tbase;
public:
ifstream() {}
ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#if MPT_OS_WINDOWS
MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#endif
};
class ofstream
: public std::ofstream
{
private:
typedef std::ofstream Tbase;
public:
ofstream() {}
ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
MPT_DEPRECATED_PATH void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#if MPT_OS_WINDOWS
MPT_DEPRECATED_PATH void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename, mode);
}
MPT_DEPRECATED_PATH void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out)
{
MPT_FSTREAM_OPEN(filename.c_str(), mode);
}
#endif
};
#undef MPT_FSTREAM_OPEN
// LazyFileRef is a simple reference to an on-disk file by the means of a
// filename which allows easy assignment of the whole file contents to and from
// byte buffers.
class LazyFileRef {
private:
const mpt::PathString m_Filename;
public:
LazyFileRef(const mpt::PathString &filename)
: m_Filename(filename)
{
return;
}
public:
LazyFileRef & operator = (const std::vector<mpt::byte> &data);
LazyFileRef & operator = (const std::vector<char> &data);
LazyFileRef & operator = (const std::string &data);
operator std::vector<mpt::byte> () const;
operator std::vector<char> () const;
operator std::string () const;
};
#if defined(MPT_ENABLE_FILEIO_STDIO)
// class FILE_ostream, FILE_output_streambuf and FILE_output_buffered_streambuf
// provide a portable way of wrapping a std::ostream around an FILE* opened for output.
// They offer similar functionality to the badly documented
// MSVC std::fstream(FILE*) constructor or GCC libstdc++ __gnu_cxx::stdio_sync_filebuf class,
// and, for other compilers, provide a race-free alternative to
// closing the FILE* and opening it again as a std::ofstream.
//
// Only output functionality is implemented because we have no need for an input wrapper.
//
// During the whole lifetime of the iostream wrappers, the FILE* object is assumend to be
// either
// - NULL
// or
// - valid
// - opened for writing in non-append mode
// - opened in binary mode
// - seekable
// Some of these preconditions cannot be verified,
// and even the others do not get verified.
// Behaviour in case of any unmet preconditions is undefined.
//
// The buffered streambuf and the ostream use a buffer of 64KiB by default.
//
// For FILE_output_streambuf, coherency with the underlying FILE* is always guaranteed.
// For FILE_ostream and FILE_output_buffered_streambuf, coherence is only
// guaranteed when flush() or pubsync() get called.
// The constructors and destructors take care to not violate coherency.
// When mixing FILE* and iostream I/O during the lifetime of the iostream objects,
// the user is responsible for providing coherency via the appropriate
// flush and sync functions.
// Behaviour in case of incoherent access is undefined.
class FILE_output_streambuf : public std::streambuf
{
public:
typedef std::streambuf::char_type char_type;
typedef std::streambuf::traits_type traits_type;
typedef traits_type::int_type int_type;
typedef traits_type::pos_type pos_type;
typedef traits_type::off_type off_type;
protected:
FILE *f;
public:
FILE_output_streambuf(FILE *f)
: f(f)
{
return;
}
~FILE_output_streambuf()
{
return;
}
protected:
virtual int_type overflow(int_type ch)
{
if(!mpt::IO::IsValid(f))
{
return traits_type::eof();
}
if(traits_type::eq_int_type(ch, traits_type::eof()))
{
return traits_type::eof();
}
char_type c = traits_type::to_char_type(ch);
if(!mpt::IO::WriteRaw(f, &c, 1))
{
return traits_type::eof();
}
return ch;
}
virtual int sync()
{
if(!mpt::IO::IsValid(f))
{
return -1;
}
if(!mpt::IO::Flush(f))
{
return -1;
}
return 0;
}
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which)
{
if(!mpt::IO::IsValid(f))
{
return pos_type(off_type(-1));
}
return seekoff(pos, std::ios_base::beg, which);
}
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
{
if(!mpt::IO::IsValid(f))
{
return pos_type(off_type(-1));
}
if(which & std::ios_base::in)
{
return pos_type(off_type(-1));
}
if(!(which & std::ios_base::out))
{
return pos_type(off_type(-1));
}
mpt::IO::Offset oldpos = mpt::IO::TellWrite(f);
if(dir == std::ios_base::beg)
{
if(!mpt::IO::SeekAbsolute(f, off))
{
mpt::IO::SeekAbsolute(f, oldpos);
return pos_type(off_type(-1));
}
} else if(dir == std::ios_base::cur)
{
if(!mpt::IO::SeekRelative(f, off))
{
mpt::IO::SeekAbsolute(f, oldpos);
return pos_type(off_type(-1));
}
} else if(dir == std::ios_base::end)
{
if(!(mpt::IO::SeekEnd(f) && mpt::IO::SeekRelative(f, off)))
{
mpt::IO::SeekAbsolute(f, oldpos);
return pos_type(off_type(-1));
}
} else
{
return pos_type(off_type(-1));
}
mpt::IO::Offset newpos = mpt::IO::TellWrite(f);
if(!mpt::IO::OffsetFits<off_type>(newpos))
{
mpt::IO::SeekAbsolute(f, oldpos);
return pos_type(off_type(-1));
}
return pos_type(static_cast<off_type>(newpos));
}
}; // class FILE_output_streambuf
class FILE_output_buffered_streambuf : public FILE_output_streambuf
{
public:
typedef std::streambuf::char_type char_type;
typedef std::streambuf::traits_type traits_type;
typedef traits_type::int_type int_type;
typedef traits_type::pos_type pos_type;
typedef traits_type::off_type off_type;
private:
typedef FILE_output_streambuf Tparent;
std::vector<char_type> buf;
public:
FILE_output_buffered_streambuf(FILE *f, std::size_t bufSize = 64*1024)
: FILE_output_streambuf(f)
, buf((bufSize > 0) ? bufSize : 1)
{
setp(buf.data(), buf.data() + buf.size());
}
~FILE_output_buffered_streambuf()
{
if(!mpt::IO::IsValid(f))
{
return;
}
WriteOut();
}
private:
bool IsDirty() const
{
return ((pptr() - pbase()) > 0);
}
bool WriteOut()
{
std::ptrdiff_t n = pptr() - pbase();
std::ptrdiff_t left = n;
while(left > 0)
{
int backchunk = mpt::saturate_cast<int>(-left);
pbump(backchunk);
left += backchunk;
}
return mpt::IO::WriteRaw(f, pbase(), n);
}
protected:
virtual int_type overflow(int_type ch)
{
if(!mpt::IO::IsValid(f))
{
return traits_type::eof();
}
if(traits_type::eq_int_type(ch, traits_type::eof()))
{
return traits_type::eof();
}
if(!WriteOut())
{
return traits_type::eof();
}
char_type c = traits_type::to_char_type(ch);
*pptr() = c;
pbump(1);
return ch;
}
virtual int sync()
{
if(!mpt::IO::IsValid(f))
{
return -1;
}
if(!WriteOut())
{
return -1;
}
return Tparent::sync();
}
virtual pos_type seekpos(pos_type pos, std::ios_base::openmode which)
{
if(!mpt::IO::IsValid(f))
{
return pos_type(off_type(-1));
}
if(!WriteOut())
{
return pos_type(off_type(-1));
}
return Tparent::seekpos(pos, which);
}
virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which)
{
if(!mpt::IO::IsValid(f))
{
return pos_type(off_type(-1));
}
if(!WriteOut())
{
return pos_type(off_type(-1));
}
return Tparent::seekoff(off, dir, which);
}
}; // class FILE_output_buffered_streambuf
class FILE_ostream : public std::ostream {
private:
FILE *f;
FILE_output_buffered_streambuf buf;
public:
FILE_ostream(FILE *f, std::size_t bufSize = 64*1024)
: std::ostream(&buf)
, f(f)
, buf(f, bufSize)
{
if(mpt::IO::IsValid(f)) mpt::IO::Flush(f);
}
~FILE_ostream()
{
flush();
buf.pubsync();
if(mpt::IO::IsValid(f)) mpt::IO::Flush(f);
}
}; // class FILE_ostream
#endif // MPT_ENABLE_FILEIO_STDIO
} // namespace mpt
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
class CMappedFile
{
protected:
HANDLE m_hFile;
HANDLE m_hFMap;
void *m_pData;
mpt::PathString m_FileName;
public:
CMappedFile() : m_hFile(nullptr), m_hFMap(nullptr), m_pData(nullptr) { }
~CMappedFile();
public:
bool Open(const mpt::PathString &filename);
bool IsOpen() const { return m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE; }
const mpt::PathString * GetpFilename() const { return &m_FileName; }
void Close();
size_t GetLength();
const mpt::byte *Lock();
};
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
class InputFile
{
private:
mpt::PathString m_Filename;
#ifdef MPT_FILEREADER_STD_ISTREAM
mpt::ifstream m_File;
#else
CMappedFile m_File;
#endif
public:
InputFile();
InputFile(const mpt::PathString &filename);
~InputFile();
bool Open(const mpt::PathString &filename);
bool IsValid() const;
#if defined(MPT_FILEREADER_STD_ISTREAM)
typedef std::pair<std::istream*, const mpt::PathString*> ContentsRef;
#else
struct Data
{
const mpt::byte *data;
std::size_t size;
};
typedef std::pair<InputFile::Data, const mpt::PathString*> ContentsRef;
#endif
InputFile::ContentsRef Get();
};
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,827 @@
/*
* mptIO.cpp
* ---------
* Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams.
* Notes : This is work-in-progress.
* Some useful functions for reading and writing are still missing.
* Authors: Joern Heusipp
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptIO.h"
#include <ios>
#include <istream>
#include <ostream>
#include <sstream>
#if MPT_COMPILER_MSVC
#include <typeinfo>
#endif // MPT_COMPILER_MSVC
#if defined(MPT_ENABLE_FILEIO_STDIO)
#include <cstdio>
#include <stdio.h>
#endif // MPT_ENABLE_FILEIO_STDIO
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
namespace IO {
#if MPT_COMPILER_MSVC
// MSVC std::stringbuf (and thereby std::ostringstream, std::istringstream and
// std::stringstream) fail seekoff() when the stringbuf is currently empty.
// seekoff() can get called via tell*() or seek*() iostream members. tell*() has
// been special cased from VS2010 onwards to handle this specific case and
// changed to not fail when the stringbuf is empty.
// In addition to using out own wrapper around std::stringstream and
// std::stringbuf, we also work-around the plain native type's problem in case
// we get handed such an object from third party code. This mitigation of course
// requires using our consolidated and normalized IO functions.
// We use the following work-around strategy:
// * If the stream is already in failed state, we do not do any work-around
// and bail out early.
// * If the underlying streambuf is not a std::stringbuf, the work-around is
// not necessary and we skip it.
// * If querying the current position does not fail and returns a
// position > 0, the underlying stringbuf is not empty and we also bail out.
// * Otherwise, we actually query the string contained in the stringbuf to be
// empty. This operation is slow as it has to copy the string into a
// temporary.
// Note, however, that this is only ever necessary if the current position
// is 0. If it always has been 0, the stringbuf will be empty anyway and the
// copy does not cost anything measurable. If it got seeked to position 0,
// we have to pay the price. However, this should be relatively uncommmon in
// pratice.
// * The actual work-around consists of performing or emulating the requested
// operation and resetting the failed state afterwards.
static bool StreamIsStringStreamAndValidAndEmpty(std::ostream & f)
{
if(f.fail() || !f.rdbuf())
{ // failed
return false;
}
if(!dynamic_cast<std::stringbuf*>(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf)))
{ // no stringbuf
return false;
}
std::streampos pos = f.tellp();
f.clear(f.rdstate() & ~std::ios::failbit);
if(pos != std::streampos(-1) && pos > 0)
{ // if the position is not 0, the streambuf is not empty
return false;
}
return dynamic_cast<std::stringbuf*>(f.rdbuf())->str().empty(); // slow
}
static bool StreamIsStringStreamAndValidAndEmpty(std::istream & f)
{
if(f.fail() || !f.rdbuf())
{ // failed
return false;
}
if(!dynamic_cast<std::stringbuf*>(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf)))
{ // no stringbuf
return false;
}
std::streampos pos = f.tellg();
f.clear(f.rdstate() & ~std::ios::failbit);
if(pos != std::streampos(-1) && pos > 0)
{ // if the position is not 0, the streambuf is not empty
return false;
}
return dynamic_cast<std::stringbuf*>(f.rdbuf())->str().empty(); // slow
}
static bool StreamIsStringStreamAndValidAndEmpty(std::iostream & f)
{
if(f.fail() || !f.rdbuf())
{ // failed
return false;
}
if(!dynamic_cast<std::stringbuf*>(f.rdbuf()) || (typeid(*(f.rdbuf())) != typeid(std::stringbuf)))
{ // no stringbuf
return false;
}
std::streampos ipos = f.tellg();
f.clear(f.rdstate() & ~std::ios::failbit);
std::streampos opos = f.tellp();
f.clear(f.rdstate() & ~std::ios::failbit);
if((ipos != std::streampos(-1) && ipos > 0) || (opos != std::streampos(-1) && opos > 0))
{ // if the position is not 0, the streambuf is not empty
return false;
}
return dynamic_cast<std::stringbuf*>(f.rdbuf())->str().empty(); // slow
}
#endif // MPT_COMPILER_MSVC
//STATIC_ASSERT(sizeof(std::streamoff) == 8); // Assert 64bit file support.
bool IsValid(std::ostream & f) { return !f.fail(); }
bool IsValid(std::istream & f) { return !f.fail(); }
bool IsValid(std::iostream & f) { return !f.fail(); }
IO::Offset TellRead(std::istream & f)
{
return f.tellg();
}
IO::Offset TellWrite(std::ostream & f)
{
return f.tellp();
}
bool SeekBegin(std::ostream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{ // VS std::stringbuf fail seek when the internal buffer is empty. Work-around it in case the stream is not already in failed state.
f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekp(0); return !f.fail();
}
bool SeekBegin(std::istream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekg(0); return !f.fail();
}
bool SeekBegin(std::iostream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekg(0); f.seekp(0); return !f.fail();
}
bool SeekEnd(std::ostream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekp(0, std::ios::end); return !f.fail();
}
bool SeekEnd(std::istream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekg(0, std::ios::end); return !f.fail();
}
bool SeekEnd(std::iostream & f)
{
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
f.seekg(0); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(0); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
#endif
f.seekg(0, std::ios::end); f.seekp(0, std::ios::end); return !f.fail();
}
bool SeekAbsolute(std::ostream & f, IO::Offset pos)
{
if(!OffsetFits<std::streamoff>(pos)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(pos == 0)
{
f.seekp(static_cast<std::streamoff>(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekp(static_cast<std::streamoff>(pos), std::ios::beg); return !f.fail();
}
bool SeekAbsolute(std::istream & f, IO::Offset pos)
{
if(!OffsetFits<std::streamoff>(pos)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(pos == 0)
{
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg); return !f.fail();
}
bool SeekAbsolute(std::iostream & f, IO::Offset pos)
{
if(!OffsetFits<std::streamoff>(pos)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(pos == 0)
{
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast<std::streamoff>(pos), std::ios::beg); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekg(static_cast<std::streamoff>(pos), std::ios::beg); f.seekp(static_cast<std::streamoff>(pos), std::ios::beg); return !f.fail();
}
bool SeekRelative(std::ostream & f, IO::Offset off)
{
if(!OffsetFits<std::streamoff>(off)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(off == 0)
{
f.seekp(static_cast<std::streamoff>(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekp(static_cast<std::streamoff>(off), std::ios::cur); return !f.fail();
}
bool SeekRelative(std::istream & f, IO::Offset off)
{
if(!OffsetFits<std::streamoff>(off)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(off == 0)
{
f.seekg(static_cast<std::streamoff>(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekg(static_cast<std::streamoff>(off), std::ios::cur); return !f.fail();
}
bool SeekRelative(std::iostream & f, IO::Offset off)
{
if(!OffsetFits<std::streamoff>(off)) { return false; }
#if MPT_COMPILER_MSVC
if(StreamIsStringStreamAndValidAndEmpty(f))
{
if(off == 0)
{
f.seekg(static_cast<std::streamoff>(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); f.seekp(static_cast<std::streamoff>(off), std::ios::cur); f.clear(f.rdstate() & ~std::ios::failbit); return true;
}
}
#endif
f.seekg(static_cast<std::streamoff>(off), std::ios::cur); f.seekp(static_cast<std::streamoff>(off), std::ios::cur); return !f.fail();
}
IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size) { return f.read(mpt::byte_cast<char *>(data), size) ? f.gcount() : std::streamsize(0); }
bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size) { f.write(mpt::byte_cast<const char *>(data), size); return !f.fail(); }
bool IsEof(std::istream & f) { return f.eof(); }
bool Flush(std::ostream & f) { f.flush(); return !f.fail(); }
#if defined(MPT_ENABLE_FILEIO_STDIO)
bool IsValid(FILE* & f) { return f != NULL; }
#if MPT_COMPILER_MSVC
IO::Offset TellRead(FILE* & f) { return _ftelli64(f); }
IO::Offset TellWrite(FILE* & f) { return _ftelli64(f); }
bool SeekBegin(FILE* & f) { return _fseeki64(f, 0, SEEK_SET) == 0; }
bool SeekEnd(FILE* & f) { return _fseeki64(f, 0, SEEK_END) == 0; }
bool SeekAbsolute(FILE* & f, IO::Offset pos) { return _fseeki64(f, pos, SEEK_SET) == 0; }
bool SeekRelative(FILE* & f, IO::Offset off) { return _fseeki64(f, off, SEEK_CUR) == 0; }
#elif defined(_POSIX_SOURCE) && (_POSIX_SOURCE > 0)
//STATIC_ASSERT(sizeof(off_t) == 8);
IO::Offset TellRead(FILE* & f) { return ftello(f); }
IO::Offset TellWrite(FILE* & f) { return ftello(f); }
bool SeekBegin(FILE* & f) { return fseeko(f, 0, SEEK_SET) == 0; }
bool SeekEnd(FILE* & f) { return fseeko(f, 0, SEEK_END) == 0; }
bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits<off_t>(pos) && (fseek(f, mpt::saturate_cast<off_t>(pos), SEEK_SET) == 0); }
bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits<off_t>(off) && (fseek(f, mpt::saturate_cast<off_t>(off), SEEK_CUR) == 0); }
#else
//STATIC_ASSERT(sizeof(long) == 8); // Fails on 32bit non-POSIX systems for now.
IO::Offset TellRead(FILE* & f) { return ftell(f); }
IO::Offset TellWrite(FILE* & f) { return ftell(f); }
bool SeekBegin(FILE* & f) { return fseek(f, 0, SEEK_SET) == 0; }
bool SeekEnd(FILE* & f) { return fseek(f, 0, SEEK_END) == 0; }
bool SeekAbsolute(FILE* & f, IO::Offset pos) { return OffsetFits<long>(pos) && (fseek(f, mpt::saturate_cast<long>(pos), SEEK_SET) == 0); }
bool SeekRelative(FILE* & f, IO::Offset off) { return OffsetFits<long>(off) && (fseek(f, mpt::saturate_cast<long>(off), SEEK_CUR) == 0); }
#endif
IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size) { return fread(mpt::void_cast<void*>(data), 1, size, f); }
bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size) { return fwrite(mpt::void_cast<const void*>(data), 1, size, f) == size; }
bool IsEof(FILE * & f) { return feof(f) != 0; }
bool Flush(FILE* & f) { return fflush(f) == 0; }
#endif // MPT_ENABLE_FILEIO_STDIO
} // namespace IO
} // namespace mpt
#if defined(MPT_FILEREADER_STD_ISTREAM)
FileDataContainerSeekable::FileDataContainerSeekable(off_t streamLength)
: streamLength(streamLength)
, cached(false)
{
return;
}
FileDataContainerSeekable::~FileDataContainerSeekable()
{
return;
}
void FileDataContainerSeekable::CacheStream() const
{
if(cached)
{
return;
}
cache.resize(streamLength);
InternalRead(cache.data(), 0, streamLength);
cached = true;
}
bool FileDataContainerSeekable::IsValid() const
{
return true;
}
bool FileDataContainerSeekable::HasFastGetLength() const
{
return true;
}
bool FileDataContainerSeekable::HasPinnedView() const
{
return cached;
}
const mpt::byte *FileDataContainerSeekable::GetRawData() const
{
CacheStream();
return cache.data();
}
IFileDataContainer::off_t FileDataContainerSeekable::GetLength() const
{
return streamLength;
}
IFileDataContainer::off_t FileDataContainerSeekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const
{
if(cached)
{
IFileDataContainer::off_t cache_avail = std::min<IFileDataContainer::off_t>(IFileDataContainer::off_t(cache.size()) - pos, count);
std::copy(cache.begin() + pos, cache.begin() + pos + cache_avail, dst);
return cache_avail;
} else
{
return InternalRead(dst, pos, count);
}
}
bool FileDataContainerStdStreamSeekable::IsSeekable(std::istream *stream)
{
stream->clear();
std::streampos oldpos = stream->tellg();
if(stream->fail() || oldpos == std::streampos(-1))
{
stream->clear();
return false;
}
stream->seekg(0, std::ios::beg);
if(stream->fail())
{
stream->clear();
stream->seekg(oldpos);
stream->clear();
return false;
}
stream->seekg(0, std::ios::end);
if(stream->fail())
{
stream->clear();
stream->seekg(oldpos);
stream->clear();
return false;
}
std::streampos length = stream->tellg();
if(stream->fail() || length == std::streampos(-1))
{
stream->clear();
stream->seekg(oldpos);
stream->clear();
return false;
}
stream->seekg(oldpos);
stream->clear();
return true;
}
IFileDataContainer::off_t FileDataContainerStdStreamSeekable::GetLength(std::istream *stream)
{
stream->clear();
std::streampos oldpos = stream->tellg();
stream->seekg(0, std::ios::end);
std::streampos length = stream->tellg();
stream->seekg(oldpos);
return mpt::saturate_cast<IFileDataContainer::off_t>(static_cast<int64>(length));
}
FileDataContainerStdStreamSeekable::FileDataContainerStdStreamSeekable(std::istream *s)
: FileDataContainerSeekable(GetLength(s))
, stream(s)
{
return;
}
FileDataContainerStdStreamSeekable::~FileDataContainerStdStreamSeekable()
{
return;
}
IFileDataContainer::off_t FileDataContainerStdStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const
{
stream->clear(); // tellg needs eof and fail bits unset
std::streampos currentpos = stream->tellg();
if(currentpos == std::streampos(-1) || static_cast<int64>(pos) != currentpos)
{ // inefficient istream implementations might invalidate their buffer when seeking, even when seeking to the current position
stream->seekg(pos);
}
stream->read(mpt::byte_cast<char*>(dst), count);
return static_cast<IFileDataContainer::off_t>(stream->gcount());
}
FileDataContainerUnseekable::FileDataContainerUnseekable()
: cachesize(0), streamFullyCached(false)
{
return;
}
FileDataContainerUnseekable::~FileDataContainerUnseekable()
{
return;
}
void FileDataContainerUnseekable::EnsureCacheBuffer(std::size_t requiredbuffersize) const
{
if(cache.size() >= cachesize + requiredbuffersize)
{
return;
}
if(cache.size() == 0)
{
cache.resize(Util::AlignUp<std::size_t>(cachesize + requiredbuffersize, BUFFER_SIZE));
} else if(Util::ExponentialGrow(cache.size()) < cachesize + requiredbuffersize)
{
cache.resize(Util::AlignUp<std::size_t>(cachesize + requiredbuffersize, BUFFER_SIZE));
} else
{
cache.resize(Util::ExponentialGrow(cache.size()));
}
}
void FileDataContainerUnseekable::CacheStream() const
{
if(streamFullyCached)
{
return;
}
while(!InternalEof())
{
EnsureCacheBuffer(BUFFER_SIZE);
std::size_t readcount = InternalRead(&cache[cachesize], BUFFER_SIZE);
cachesize += readcount;
}
streamFullyCached = true;
}
void FileDataContainerUnseekable::CacheStreamUpTo(off_t pos, off_t length) const
{
if(streamFullyCached)
{
return;
}
if(length > std::numeric_limits<off_t>::max() - pos)
{
length = std::numeric_limits<off_t>::max() - pos;
}
std::size_t target = mpt::saturate_cast<std::size_t>(pos + length);
if(target <= cachesize)
{
return;
}
std::size_t alignedpos = Util::AlignUp<std::size_t>(target, QUANTUM_SIZE);
std::size_t needcount = alignedpos - cachesize;
EnsureCacheBuffer(needcount);
std::size_t readcount = InternalRead(&cache[cachesize], alignedpos - cachesize);
cachesize += readcount;
if(!InternalEof())
{
// can read further
return;
}
streamFullyCached = true;
}
void FileDataContainerUnseekable::ReadCached(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const
{
std::copy(cache.begin() + pos, cache.begin() + pos + count, dst);
}
bool FileDataContainerUnseekable::IsValid() const
{
return true;
}
bool FileDataContainerUnseekable::HasFastGetLength() const
{
return false;
}
bool FileDataContainerUnseekable::HasPinnedView() const
{
return true; // we have the cache which is required for seeking anyway
}
const mpt::byte *FileDataContainerUnseekable::GetRawData() const
{
CacheStream();
return cache.data();
}
IFileDataContainer::off_t FileDataContainerUnseekable::GetLength() const
{
CacheStream();
return cachesize;
}
IFileDataContainer::off_t FileDataContainerUnseekable::Read(mpt::byte *dst, IFileDataContainer::off_t pos, IFileDataContainer::off_t count) const
{
CacheStreamUpTo(pos, count);
if(pos >= IFileDataContainer::off_t(cachesize))
{
return 0;
}
IFileDataContainer::off_t cache_avail = std::min<IFileDataContainer::off_t>(IFileDataContainer::off_t(cachesize) - pos, count);
ReadCached(dst, pos, cache_avail);
return cache_avail;
}
bool FileDataContainerUnseekable::CanRead(IFileDataContainer::off_t pos, IFileDataContainer::off_t length) const
{
CacheStreamUpTo(pos, length);
if((pos == IFileDataContainer::off_t(cachesize)) && (length == 0))
{
return true;
}
if(pos >= IFileDataContainer::off_t(cachesize))
{
return false;
}
return length <= IFileDataContainer::off_t(cachesize) - pos;
}
IFileDataContainer::off_t FileDataContainerUnseekable::GetReadableLength(IFileDataContainer::off_t pos, IFileDataContainer::off_t length) const
{
CacheStreamUpTo(pos, length);
if(pos >= cachesize)
{
return 0;
}
return std::min<IFileDataContainer::off_t>(cachesize - pos, length);
}
FileDataContainerStdStream::FileDataContainerStdStream(std::istream *s)
: stream(s)
{
return;
}
FileDataContainerStdStream::~FileDataContainerStdStream()
{
return;
}
bool FileDataContainerStdStream::InternalEof() const
{
if(*stream)
{
return false;
} else
{
return true;
}
}
IFileDataContainer::off_t FileDataContainerStdStream::InternalRead(mpt::byte *dst, off_t count) const
{
stream->read(mpt::byte_cast<char*>(dst), count);
return static_cast<std::size_t>(stream->gcount());
}
#if defined(MPT_FILEREADER_CALLBACK_STREAM)
bool FileDataContainerCallbackStreamSeekable::IsSeekable(CallbackStream stream)
{
if(!stream.stream)
{
return false;
}
if(!stream.seek)
{
return false;
}
if(!stream.tell)
{
return false;
}
int64 oldpos = stream.tell(stream.stream);
if(oldpos < 0)
{
return false;
}
if(stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return false;
}
if(stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return false;
}
int64 length = stream.tell(stream.stream);
if(length < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return false;
}
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return true;
}
IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::GetLength(CallbackStream stream)
{
if(!stream.stream)
{
return 0;
}
if(!stream.seek)
{
return false;
}
if(!stream.tell)
{
return false;
}
int64 oldpos = stream.tell(stream.stream);
if(oldpos < 0)
{
return 0;
}
if(stream.seek(stream.stream, 0, CallbackStream::SeekSet) < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return 0;
}
if(stream.seek(stream.stream, 0, CallbackStream::SeekEnd) < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return 0;
}
int64 length = stream.tell(stream.stream);
if(length < 0)
{
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return 0;
}
stream.seek(stream.stream, oldpos, CallbackStream::SeekSet);
return mpt::saturate_cast<IFileDataContainer::off_t>(length);
}
FileDataContainerCallbackStreamSeekable::FileDataContainerCallbackStreamSeekable(CallbackStream s)
: FileDataContainerSeekable(GetLength(s))
, stream(s)
{
return;
}
FileDataContainerCallbackStreamSeekable::~FileDataContainerCallbackStreamSeekable()
{
return;
}
IFileDataContainer::off_t FileDataContainerCallbackStreamSeekable::InternalRead(mpt::byte *dst, off_t pos, off_t count) const
{
if(!stream.read)
{
return 0;
}
if(stream.seek(stream.stream, pos, CallbackStream::SeekSet) < 0)
{
return 0;
}
int64 totalread = 0;
while(count > 0)
{
int64 readcount = stream.read(stream.stream, dst, count);
if(readcount <= 0)
{
break;
}
dst += static_cast<std::size_t>(readcount);
count -= static_cast<IFileDataContainer::off_t>(readcount);
totalread += readcount;
}
return static_cast<IFileDataContainer::off_t>(totalread);
}
FileDataContainerCallbackStream::FileDataContainerCallbackStream(CallbackStream s)
: FileDataContainerUnseekable()
, stream(s)
, eof_reached(false)
{
return;
}
FileDataContainerCallbackStream::~FileDataContainerCallbackStream()
{
return;
}
bool FileDataContainerCallbackStream::InternalEof() const
{
return eof_reached;
}
IFileDataContainer::off_t FileDataContainerCallbackStream::InternalRead(mpt::byte *dst, off_t count) const
{
if(eof_reached)
{
return 0;
}
if(!stream.read)
{
eof_reached = true;
return 0;
}
int64 totalread = 0;
while(count > 0)
{
int64 readcount = stream.read(stream.stream, dst, count);
if(readcount <= 0)
{
eof_reached = true;
break;
}
dst += static_cast<std::size_t>(readcount);
count -= static_cast<IFileDataContainer::off_t>(readcount);
totalread += readcount;
}
return static_cast<IFileDataContainer::off_t>(totalread);
}
#endif // MPT_FILEREADER_CALLBACK_STREAM
#endif
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,926 @@
/*
* mptIO.h
* -------
* Purpose: Basic functions for reading/writing binary and endian safe data to/from files/streams.
* Notes : This is work-in-progress.
* Some useful functions for reading and writing are still missing.
* Authors: Joern Heusipp
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "../common/typedefs.h"
#include "../common/mptTypeTraits.h"
#include "../common/Endianness.h"
#include <algorithm>
#include <iosfwd>
#include <limits>
#include <cstring>
#if defined(MPT_ENABLE_FILEIO_STDIO)
#include <cstdio>
#include <stdio.h>
#endif // MPT_ENABLE_FILEIO_STDIO
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
namespace IO {
typedef int64 Offset;
static const std::size_t BUFFERSIZE_TINY = 1 * 1024; // on stack usage
static const std::size_t BUFFERSIZE_SMALL = 4 * 1024; // on heap
static const std::size_t BUFFERSIZE_NORMAL = 64 * 1024; // FILE I/O
static const std::size_t BUFFERSIZE_LARGE = 1024 * 1024;
// Returns true iff 'off' fits into 'Toff'.
template < typename Toff >
inline bool OffsetFits(IO::Offset off)
{
return (static_cast<IO::Offset>(mpt::saturate_cast<Toff>(off)) == off);
}
bool IsValid(std::ostream & f);
bool IsValid(std::istream & f);
bool IsValid(std::iostream & f);
IO::Offset TellRead(std::istream & f);
IO::Offset TellWrite(std::ostream & f);
bool SeekBegin(std::ostream & f);
bool SeekBegin(std::istream & f);
bool SeekBegin(std::iostream & f);
bool SeekEnd(std::ostream & f);
bool SeekEnd(std::istream & f);
bool SeekEnd(std::iostream & f);
bool SeekAbsolute(std::ostream & f, IO::Offset pos);
bool SeekAbsolute(std::istream & f, IO::Offset pos);
bool SeekAbsolute(std::iostream & f, IO::Offset pos);
bool SeekRelative(std::ostream & f, IO::Offset off);
bool SeekRelative(std::istream & f, IO::Offset off);
bool SeekRelative(std::iostream & f, IO::Offset off);
IO::Offset ReadRawImpl(std::istream & f, mpt::byte * data, std::size_t size);
bool WriteRawImpl(std::ostream & f, const mpt::byte * data, std::size_t size);
bool IsEof(std::istream & f);
bool Flush(std::ostream & f);
#if defined(MPT_ENABLE_FILEIO_STDIO)
bool IsValid(FILE* & f);
IO::Offset TellRead(FILE* & f);
IO::Offset TellWrite(FILE* & f);
bool SeekBegin(FILE* & f);
bool SeekEnd(FILE* & f);
bool SeekAbsolute(FILE* & f, IO::Offset pos);
bool SeekRelative(FILE* & f, IO::Offset off);
IO::Offset ReadRawImpl(FILE * & f, mpt::byte * data, std::size_t size);
bool WriteRawImpl(FILE* & f, const mpt::byte * data, std::size_t size);
bool IsEof(FILE * & f);
bool Flush(FILE* & f);
#endif // MPT_ENABLE_FILEIO_STDIO
template <typename Tbyte> bool IsValid(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
return (f.second >= 0);
}
template <typename Tbyte> IO::Offset TellRead(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
return f.second;
}
template <typename Tbyte> IO::Offset TellWrite(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
return f.second;
}
template <typename Tbyte> bool SeekBegin(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
f.second = 0;
return true;
}
template <typename Tbyte> bool SeekEnd(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
f.second = f.first.size();
return true;
}
template <typename Tbyte> bool SeekAbsolute(std::pair<mpt::span<Tbyte>, IO::Offset> & f, IO::Offset pos)
{
f.second = pos;
return true;
}
template <typename Tbyte> bool SeekRelative(std::pair<mpt::span<Tbyte>, IO::Offset> & f, IO::Offset off)
{
if(f.second < 0)
{
return false;
}
f.second += off;
return true;
}
template <typename Tbyte> IO::Offset ReadRawImpl(std::pair<mpt::span<Tbyte>, IO::Offset> & f, mpt::byte * data, std::size_t size)
{
if(f.second < 0)
{
return 0;
}
if(f.second >= static_cast<IO::Offset>(f.first.size()))
{
return 0;
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min<IO::Offset>(f.first.size() - f.second, size));
std::copy(mpt::byte_cast<const mpt::byte*>(f.first.data() + f.second), mpt::byte_cast<const mpt::byte*>(f.first.data() + f.second + num), data);
f.second += num;
return num;
}
template <typename Tbyte> bool WriteRawImpl(std::pair<mpt::span<Tbyte>, IO::Offset> & f, const mpt::byte * data, std::size_t size)
{
if(f.second < 0)
{
return false;
}
if(f.second >= static_cast<IO::Offset>(f.first.size()))
{
return false;
}
std::size_t num = mpt::saturate_cast<std::size_t>(std::min<IO::Offset>(f.first.size() - f.second, size));
if(num != size)
{
return false;
}
std::copy(data, data + num, mpt::byte_cast<mpt::byte*>(f.first.data() + f.second));
f.second += num;
return true;
}
template <typename Tbyte> bool IsEof(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
return (f.second >= static_cast<IO::Offset>(f.first.size()));
}
template <typename Tbyte> bool Flush(std::pair<mpt::span<Tbyte>, IO::Offset> & f)
{
MPT_UNREFERENCED_PARAMTER(f);
return true;
}
template <typename Tbyte, typename Tfile>
inline IO::Offset ReadRaw(Tfile & f, Tbyte * data, std::size_t size)
{
return IO::ReadRawImpl(f, mpt::byte_cast<mpt::byte*>(data), size);
}
template <typename Tbyte, typename Tfile>
inline bool WriteRaw(Tfile & f, const Tbyte * data, std::size_t size)
{
return IO::WriteRawImpl(f, mpt::byte_cast<const mpt::byte*>(data), size);
}
template <typename Tbinary, typename Tfile>
inline bool Read(Tfile & f, Tbinary & v)
{
return IO::ReadRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary)) == sizeof(Tbinary);
}
template <typename Tbinary, typename Tfile>
inline bool Write(Tfile & f, const Tbinary & v)
{
return IO::WriteRaw(f, mpt::as_raw_memory(v), sizeof(Tbinary));
}
template <typename T, typename Tfile>
inline bool WritePartial(Tfile & f, const T & v, size_t size = sizeof(T))
{
MPT_ASSERT(size <= sizeof(T));
return IO::WriteRaw(f, mpt::as_raw_memory(v), size);
}
template <typename T, typename Tfile>
inline bool ReadBinaryTruncatedLE(Tfile & f, T & v, std::size_t size)
{
bool result = false;
MPT_STATIC_ASSERT(std::numeric_limits<T>::is_integer);
mpt::byte bytes[sizeof(T)];
std::memset(bytes, 0, sizeof(T));
const IO::Offset readResult = IO::ReadRaw(f, bytes, std::min(size, sizeof(T)));
if(readResult < 0)
{
result = false;
} else
{
result = (static_cast<uint64>(readResult) == std::min(size, sizeof(T)));
}
#ifdef MPT_PLATFORM_BIG_ENDIAN
std::reverse(bytes, bytes + sizeof(T));
#endif
std::memcpy(&v, bytes, sizeof(T));
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntLE(Tfile & f, T & v)
{
bool result = false;
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
mpt::byte bytes[sizeof(T)];
std::memset(bytes, 0, sizeof(T));
const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T));
if(readResult < 0)
{
result = false;
} else
{
result = (static_cast<uint64>(readResult) == sizeof(T));
}
T val = 0;
std::memcpy(&val, bytes, sizeof(T));
v = SwapBytesLE(val);
return result;
}
template <typename T, typename Tfile>
inline bool ReadIntBE(Tfile & f, T & v)
{
bool result = false;
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
mpt::byte bytes[sizeof(T)];
std::memset(bytes, 0, sizeof(T));
const IO::Offset readResult = IO::ReadRaw(f, bytes, sizeof(T));
if(readResult < 0)
{
result = false;
} else
{
result = (static_cast<uint64>(readResult) == sizeof(T));
}
T val = 0;
std::memcpy(&val, bytes, sizeof(T));
v = SwapBytesBE(val);
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt16LE(Tfile & f, uint16 & v)
{
bool result = true;
mpt::byte byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
additionalBytes = (byte & 0x01);
v = byte >> 1;
for(std::size_t i = 0; i < additionalBytes; ++i)
{
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
v |= (static_cast<uint16>(byte) << (((i+1)*8) - 1));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt32LE(Tfile & f, uint32 & v)
{
bool result = true;
mpt::byte byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
additionalBytes = (byte & 0x03);
v = byte >> 2;
for(std::size_t i = 0; i < additionalBytes; ++i)
{
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
v |= (static_cast<uint32>(byte) << (((i+1)*8) - 2));
}
return result;
}
template <typename Tfile>
inline bool ReadAdaptiveInt64LE(Tfile & f, uint64 & v)
{
bool result = true;
mpt::byte byte = 0;
std::size_t additionalBytes = 0;
v = 0;
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
additionalBytes = (1 << (byte & 0x03)) - 1;
v = byte >> 2;
for(std::size_t i = 0; i < additionalBytes; ++i)
{
byte = 0;
if(!IO::ReadIntLE<mpt::byte>(f, byte)) result = false;
v |= (static_cast<uint64>(byte) << (((i+1)*8) - 2));
}
return result;
}
template <typename Tsize, typename Tfile>
inline bool ReadSizedStringLE(Tfile & f, std::string & str, Tsize maxSize = std::numeric_limits<Tsize>::max())
{
STATIC_ASSERT(std::numeric_limits<Tsize>::is_integer);
str.clear();
Tsize size = 0;
if(!mpt::IO::ReadIntLE(f, size))
{
return false;
}
if(size > maxSize)
{
return false;
}
for(Tsize i = 0; i != size; ++i)
{
char c = '\0';
if(!mpt::IO::ReadIntLE(f, c))
{
return false;
}
str.push_back(c);
}
return true;
}
template <typename T, typename Tfile>
inline bool WriteIntLE(Tfile & f, const T v)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
const T val = SwapBytesLE(v);
mpt::byte bytes[sizeof(T)];
std::memcpy(bytes, &val, sizeof(T));
return IO::WriteRaw(f, bytes, sizeof(T));
}
template <typename T, typename Tfile>
inline bool WriteIntBE(Tfile & f, const T v)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
const T val = SwapBytesBE(v);
mpt::byte bytes[sizeof(T)];
std::memcpy(bytes, &val, sizeof(T));
return IO::WriteRaw(f, bytes, sizeof(T));
}
template <typename Tfile>
inline bool WriteAdaptiveInt16LE(Tfile & f, const uint16 v, std::size_t minSize = 0, std::size_t maxSize = 0)
{
MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2);
MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2);
MPT_ASSERT(maxSize == 0 || maxSize >= minSize);
if(v < 0x80 && minSize <= 1 && (1 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 1) | 0x00);
} else if(v < 0x8000 && minSize <= 2 && (2 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 1) | 0x01);
} else
{
MPT_ASSERT_NOTREACHED();
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt32LE(Tfile & f, const uint32 v, std::size_t minSize = 0, std::size_t maxSize = 0)
{
MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 3 || minSize == 4);
MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 3 || maxSize == 4);
MPT_ASSERT(maxSize == 0 || maxSize >= minSize);
if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if(v < 0x400000 && minSize <= 3 && (3 <= maxSize || maxSize == 0))
{
uint32 value = static_cast<uint32>(v << 2) | 0x02;
mpt::byte bytes[3];
bytes[0] = static_cast<mpt::byte>(value >> 0);
bytes[1] = static_cast<mpt::byte>(value >> 8);
bytes[2] = static_cast<mpt::byte>(value >> 16);
return IO::WriteRaw(f, bytes, 3);
} else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x03);
} else
{
MPT_ASSERT_NOTREACHED();
return false;
}
}
template <typename Tfile>
inline bool WriteAdaptiveInt64LE(Tfile & f, const uint64 v, std::size_t minSize = 0, std::size_t maxSize = 0)
{
MPT_ASSERT(minSize == 0 || minSize == 1 || minSize == 2 || minSize == 4 || minSize == 8);
MPT_ASSERT(maxSize == 0 || maxSize == 1 || maxSize == 2 || maxSize == 4 || maxSize == 8);
MPT_ASSERT(maxSize == 0 || maxSize >= minSize);
if(v < 0x40 && minSize <= 1 && (1 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint8>(f, static_cast<uint8>(v << 2) | 0x00);
} else if(v < 0x4000 && minSize <= 2 && (2 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint16>(f, static_cast<uint16>(v << 2) | 0x01);
} else if(v < 0x40000000 && minSize <= 4 && (4 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint32>(f, static_cast<uint32>(v << 2) | 0x02);
} else if(v < 0x4000000000000000ull && minSize <= 8 && (8 <= maxSize || maxSize == 0))
{
return IO::WriteIntLE<uint64>(f, static_cast<uint64>(v << 2) | 0x03);
} else
{
MPT_ASSERT_NOTREACHED();
return false;
}
}
// Write a variable-length integer, as found in MIDI files. The number of written bytes is placed in the bytesWritten parameter.
template <typename Tfile, typename T>
bool WriteVarInt(Tfile & f, const T v, size_t *bytesWritten = nullptr)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
STATIC_ASSERT(!std::numeric_limits<T>::is_signed);
mpt::byte out[(sizeof(T) * 8 + 6) / 7];
size_t numBytes = 0;
for(uint32 n = (sizeof(T) * 8) / 7; n > 0; n--)
{
if(v >= (static_cast<T>(1) << (n * 7u)))
{
out[numBytes++] = static_cast<mpt::byte>(((v >> (n * 7u)) & 0x7F) | 0x80);
}
}
out[numBytes++] = static_cast<mpt::byte>(v & 0x7F);
MPT_ASSERT(numBytes <= mpt::size(out));
if(bytesWritten != nullptr) *bytesWritten = numBytes;
return mpt::IO::WriteRaw(f, out, numBytes);
}
template <typename Tsize, typename Tfile>
inline bool WriteSizedStringLE(Tfile & f, const std::string & str)
{
STATIC_ASSERT(std::numeric_limits<Tsize>::is_integer);
if(str.size() > std::numeric_limits<Tsize>::max())
{
return false;
}
Tsize size = static_cast<Tsize>(str.size());
if(!mpt::IO::WriteIntLE(f, size))
{
return false;
}
if(!mpt::IO::WriteRaw(f, str.data(), str.size()))
{
return false;
}
return true;
}
template <typename Tfile>
inline bool WriteText(Tfile &f, const std::string &s)
{
return mpt::IO::WriteRaw(f, s.data(), s.size());
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile &f)
{
return mpt::IO::WriteText(f, "\r\n");
}
template <typename Tfile>
inline bool WriteTextLF(Tfile &f)
{
return mpt::IO::WriteText(f, "\n");
}
template <typename Tfile>
inline bool WriteTextCRLF(Tfile &f, const std::string &s)
{
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextCRLF(f);
}
template <typename Tfile>
inline bool WriteTextLF(Tfile &f, const std::string &s)
{
return mpt::IO::WriteText(f, s) && mpt::IO::WriteTextLF(f);
}
} // namespace IO
} // namespace mpt
#if defined(MPT_FILEREADER_STD_ISTREAM)
class IFileDataContainer {
public:
typedef std::size_t off_t;
protected:
IFileDataContainer() { }
public:
virtual ~IFileDataContainer() { }
public:
virtual bool IsValid() const = 0;
virtual bool HasFastGetLength() const = 0;
virtual bool HasPinnedView() const = 0;
virtual const mpt::byte *GetRawData() const = 0;
virtual off_t GetLength() const = 0;
virtual off_t Read(mpt::byte *dst, off_t pos, off_t count) const = 0;
virtual bool CanRead(off_t pos, off_t length) const
{
off_t dataLength = GetLength();
if((pos == dataLength) && (length == 0))
{
return true;
}
if(pos >= dataLength)
{
return false;
}
return length <= dataLength - pos;
}
virtual off_t GetReadableLength(off_t pos, off_t length) const
{
off_t dataLength = GetLength();
if(pos >= dataLength)
{
return 0;
}
return std::min<off_t>(length, dataLength - pos);
}
};
class FileDataContainerDummy : public IFileDataContainer {
public:
FileDataContainerDummy() { }
virtual ~FileDataContainerDummy() { }
public:
bool IsValid() const
{
return false;
}
bool HasFastGetLength() const
{
return true;
}
bool HasPinnedView() const
{
return true;
}
const mpt::byte *GetRawData() const
{
return nullptr;
}
off_t GetLength() const
{
return 0;
}
off_t Read(mpt::byte * /*dst*/, off_t /*pos*/, off_t /*count*/) const
{
return 0;
}
};
class FileDataContainerWindow : public IFileDataContainer
{
private:
std::shared_ptr<const IFileDataContainer> data;
const off_t dataOffset;
const off_t dataLength;
public:
FileDataContainerWindow(std::shared_ptr<const IFileDataContainer> src, off_t off, off_t len) : data(src), dataOffset(off), dataLength(len) { }
virtual ~FileDataContainerWindow() { }
bool IsValid() const
{
return data->IsValid();
}
bool HasFastGetLength() const
{
return data->HasFastGetLength();
}
bool HasPinnedView() const
{
return data->HasPinnedView();
}
const mpt::byte *GetRawData() const {
return data->GetRawData() + dataOffset;
}
off_t GetLength() const {
return dataLength;
}
off_t Read(mpt::byte *dst, off_t pos, off_t count) const
{
if(pos >= dataLength)
{
return 0;
}
return data->Read(dst, dataOffset + pos, std::min(count, dataLength - pos));
}
bool CanRead(off_t pos, off_t length) const {
if((pos == dataLength) && (length == 0))
{
return true;
}
if(pos >= dataLength)
{
return false;
}
return (length <= dataLength - pos);
}
off_t GetReadableLength(off_t pos, off_t length) const
{
if(pos >= dataLength)
{
return 0;
}
return std::min(length, dataLength - pos);
}
};
class FileDataContainerSeekable : public IFileDataContainer {
private:
off_t streamLength;
mutable bool cached;
mutable std::vector<mpt::byte> cache;
protected:
FileDataContainerSeekable(off_t length);
virtual ~FileDataContainerSeekable();
private:
void CacheStream() const;
public:
bool IsValid() const;
bool HasFastGetLength() const;
bool HasPinnedView() const;
const mpt::byte *GetRawData() const;
off_t GetLength() const;
off_t Read(mpt::byte *dst, off_t pos, off_t count) const;
private:
virtual off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const = 0;
};
class FileDataContainerStdStreamSeekable : public FileDataContainerSeekable {
private:
std::istream *stream;
public:
FileDataContainerStdStreamSeekable(std::istream *s);
virtual ~FileDataContainerStdStreamSeekable();
static bool IsSeekable(std::istream *stream);
static off_t GetLength(std::istream *stream);
private:
off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const;
};
class FileDataContainerUnseekable : public IFileDataContainer {
private:
mutable std::vector<mpt::byte> cache;
mutable std::size_t cachesize;
mutable bool streamFullyCached;
protected:
FileDataContainerUnseekable();
virtual ~FileDataContainerUnseekable();
private:
static const std::size_t QUANTUM_SIZE = mpt::IO::BUFFERSIZE_SMALL;
static const std::size_t BUFFER_SIZE = mpt::IO::BUFFERSIZE_NORMAL;
void EnsureCacheBuffer(std::size_t requiredbuffersize) const;
void CacheStream() const;
void CacheStreamUpTo(off_t pos, off_t length) const;
private:
void ReadCached(mpt::byte *dst, off_t pos, off_t count) const;
public:
bool IsValid() const;
bool HasFastGetLength() const;
bool HasPinnedView() const;
const mpt::byte *GetRawData() const;
off_t GetLength() const;
off_t Read(mpt::byte *dst, off_t pos, off_t count) const;
bool CanRead(off_t pos, off_t length) const;
off_t GetReadableLength(off_t pos, off_t length) const;
private:
virtual bool InternalEof() const = 0;
virtual off_t InternalRead(mpt::byte *dst, off_t count) const = 0;
};
class FileDataContainerStdStream : public FileDataContainerUnseekable {
private:
std::istream *stream;
public:
FileDataContainerStdStream(std::istream *s);
virtual ~FileDataContainerStdStream();
private:
bool InternalEof() const;
off_t InternalRead(mpt::byte *dst, off_t count) const;
};
#if defined(MPT_FILEREADER_CALLBACK_STREAM)
struct CallbackStream
{
static const int SeekSet = 0;
static const int SeekCur = 1;
static const int SeekEnd = 2;
void *stream;
std::size_t (*read)( void * stream, void * dst, std::size_t bytes );
int (*seek)( void * stream, int64 offset, int whence );
int64 (*tell)( void * stream );
};
class FileDataContainerCallbackStreamSeekable : public FileDataContainerSeekable
{
private:
CallbackStream stream;
public:
static bool IsSeekable(CallbackStream stream);
static off_t GetLength(CallbackStream stream);
FileDataContainerCallbackStreamSeekable(CallbackStream s);
virtual ~FileDataContainerCallbackStreamSeekable();
private:
off_t InternalRead(mpt::byte *dst, off_t pos, off_t count) const;
};
class FileDataContainerCallbackStream : public FileDataContainerUnseekable
{
private:
CallbackStream stream;
mutable bool eof_reached;
public:
FileDataContainerCallbackStream(CallbackStream s);
virtual ~FileDataContainerCallbackStream();
private:
bool InternalEof() const;
off_t InternalRead(mpt::byte *dst, off_t count) const;
};
#endif // MPT_FILEREADER_CALLBACK_STREAM
#endif
class FileDataContainerMemory
#if defined(MPT_FILEREADER_STD_ISTREAM)
: public IFileDataContainer
#endif
{
#if !defined(MPT_FILEREADER_STD_ISTREAM)
public:
typedef std::size_t off_t;
#endif
private:
const mpt::byte *streamData; // Pointer to memory-mapped file
off_t streamLength; // Size of memory-mapped file in bytes
public:
FileDataContainerMemory() : streamData(nullptr), streamLength(0) { }
FileDataContainerMemory(mpt::const_byte_span data) : streamData(data.data()), streamLength(data.size()) { }
#if defined(MPT_FILEREADER_STD_ISTREAM)
virtual
#endif
~FileDataContainerMemory() { }
public:
bool IsValid() const
{
return streamData != nullptr;
}
bool HasFastGetLength() const
{
return true;
}
bool HasPinnedView() const
{
return true;
}
const mpt::byte *GetRawData() const
{
return streamData;
}
off_t GetLength() const
{
return streamLength;
}
off_t Read(mpt::byte *dst, off_t pos, off_t count) const
{
if(pos >= streamLength)
{
return 0;
}
off_t avail = std::min<off_t>(streamLength - pos, count);
std::copy(streamData + pos, streamData + pos + avail, dst);
return avail;
}
bool CanRead(off_t pos, off_t length) const
{
if((pos == streamLength) && (length == 0))
{
return true;
}
if(pos >= streamLength)
{
return false;
}
return (length <= streamLength - pos);
}
off_t GetReadableLength(off_t pos, off_t length) const
{
if(pos >= streamLength)
{
return 0;
}
return std::min<off_t>(length, streamLength - pos);
}
};
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,550 @@
/*
* mptLibrary.cpp
* --------------
* Purpose: Shared library handling.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptLibrary.h"
#if defined(MPT_ENABLE_DYNBIND)
#if MPT_OS_WINDOWS
#include <windows.h>
#elif MPT_OS_ANDROID
#include <dlfcn.h>
#elif defined(MPT_WITH_LTDL)
#include <ltdl.h>
#elif defined(MPT_WITH_DL)
#include <dlfcn.h>
#endif
#endif
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_DYNBIND)
namespace mpt
{
#if MPT_OS_WINDOWS
// KB2533623 / Win8
#ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
#endif
#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200
#endif
#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#endif
#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#endif
class LibraryHandle
{
private:
HMODULE hModule;
public:
LibraryHandle(const mpt::LibraryPath &path)
: hModule(NULL)
{
#if MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT < 0x0602)
(void)path;
hModule = NULL; // unsupported
#else
switch(path.GetSearchPath())
{
case mpt::LibrarySearchPathDefault:
hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0);
break;
case mpt::LibrarySearchPathApplication:
hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0);
break;
case mpt::LibrarySearchPathSystem:
hModule = NULL; // Only application packaged libraries can be loaded dynamically in WinRT
break;
case mpt::LibrarySearchPathFullPath:
hModule = NULL; // Absolute path is not supported in WinRT
break;
case mpt::LibrarySearchPathInvalid:
MPT_ASSERT_NOTREACHED();
break;
}
#endif
#else // !MPT_OS_WINDOWS_WINRT
mpt::Windows::Version WindowsVersion = mpt::Windows::Version::Current();
// Check for KB2533623:
bool hasKB2533623 = false;
if(WindowsVersion.IsAtLeast(mpt::Windows::Version::Win8))
{
hasKB2533623 = true;
} else if(WindowsVersion.IsAtLeast(mpt::Windows::Version::WinVista))
{
HMODULE hKernel32DLL = LoadLibraryW(L"kernel32.dll");
if(hKernel32DLL)
{
if(::GetProcAddress(hKernel32DLL, "SetDefaultDllDirectories") != nullptr)
{
hasKB2533623 = true;
}
FreeLibrary(hKernel32DLL);
hKernel32DLL = NULL;
}
}
if(hasKB2533623)
{
switch(path.GetSearchPath())
{
case mpt::LibrarySearchPathDefault:
hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
break;
case mpt::LibrarySearchPathSystem:
hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
break;
#if defined(MODPLUG_TRACKER)
// Using restricted search paths applies to potential DLL dependencies
// recursively.
// This fails loading for e.g. Codec or Plugin DLLs in application
// directory if they depend on the MSVC C or C++ runtime (which is
// located in the system directory).
// Just rely on the default search path here.
case mpt::LibrarySearchPathApplication:
{
const mpt::PathString dllPath = mpt::GetAppPath();
if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
{
hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str());
}
}
break;
case mpt::LibrarySearchPathFullPath:
hModule = LoadLibraryW(path.GetFileName().AsNative().c_str());
break;
#else
// For libopenmpt, do the safe thing.
case mpt::LibrarySearchPathApplication:
hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
break;
case mpt::LibrarySearchPathFullPath:
hModule = LoadLibraryExW(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
break;
#endif
case mpt::LibrarySearchPathInvalid:
MPT_ASSERT_NOTREACHED();
break;
}
} else
{
switch(path.GetSearchPath())
{
case mpt::LibrarySearchPathDefault:
hModule = LoadLibraryW(path.GetFileName().AsNative().c_str());
break;
case mpt::LibrarySearchPathApplication:
{
const mpt::PathString dllPath = mpt::GetAppPath();
if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
{
hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str());
}
}
break;
case mpt::LibrarySearchPathSystem:
{
const mpt::PathString dllPath = mpt::GetSystemPath();
if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
{
hModule = LoadLibraryW((dllPath + path.GetFileName()).AsNative().c_str());
}
}
break;
case mpt::LibrarySearchPathFullPath:
hModule = LoadLibraryW(path.GetFileName().AsNative().c_str());
break;
case mpt::LibrarySearchPathInvalid:
MPT_ASSERT_NOTREACHED();
break;
}
}
#endif // MPT_OS_WINDOWS_WINRT
}
~LibraryHandle()
{
if(IsValid())
{
FreeLibrary(hModule);
}
hModule = NULL;
}
public:
bool IsValid() const
{
return (hModule != NULL);
}
FuncPtr GetProcAddress(const std::string &symbol) const
{
if(!IsValid())
{
return nullptr;
}
return reinterpret_cast<FuncPtr>(::GetProcAddress(hModule, symbol.c_str()));
}
};
#elif MPT_OS_ANDROID
// Fake implementation.
// Load shared objects from the JAVA side of things.
class LibraryHandle
{
public:
LibraryHandle(const mpt::LibraryPath &path)
{
return;
}
~LibraryHandle()
{
return;
}
public:
bool IsValid() const
{
return true;
}
FuncPtr GetProcAddress(const std::string &symbol) const
{
if(!IsValid())
{
return nullptr;
}
return reinterpret_cast<FuncPtr>(dlsym(0, symbol.c_str()));
}
};
#elif defined(MPT_WITH_LTDL)
class LibraryHandle
{
private:
bool inited;
lt_dlhandle handle;
public:
LibraryHandle(const mpt::LibraryPath &path)
: inited(false)
, handle(0)
{
if(lt_dlinit() != 0)
{
return;
}
inited = true;
handle = lt_dlopenext(path.GetFileName().AsNative().c_str());
}
~LibraryHandle()
{
if(IsValid())
{
lt_dlclose(handle);
}
handle = 0;
if(inited)
{
lt_dlexit();
inited = false;
}
}
public:
bool IsValid() const
{
return handle != 0;
}
FuncPtr GetProcAddress(const std::string &symbol) const
{
if(!IsValid())
{
return nullptr;
}
return reinterpret_cast<FuncPtr>(lt_dlsym(handle, symbol.c_str()));
}
};
#elif defined(MPT_WITH_DL)
class LibraryHandle
{
private:
void* handle;
public:
LibraryHandle(const mpt::LibraryPath &path)
: handle(NULL)
{
handle = dlopen(path.GetFileName().AsNative().c_str(), RTLD_NOW);
}
~LibraryHandle()
{
if(IsValid())
{
dlclose(handle);
}
handle = NULL;
}
public:
bool IsValid() const
{
return handle != NULL;
}
FuncPtr GetProcAddress(const std::string &symbol) const
{
if(!IsValid())
{
return NULL;
}
return reinterpret_cast<FuncPtr>(dlsym(handle, symbol.c_str()));
}
};
#else // MPT_OS
// dummy implementation
class LibraryHandle
{
public:
LibraryHandle(const mpt::LibraryPath &path)
{
MPT_UNREFERENCED_PARAMETER(path);
return;
}
~LibraryHandle()
{
return;
}
public:
bool IsValid() const
{
return false;
}
FuncPtr GetProcAddress(const std::string &symbol) const
{
MPT_UNREFERENCED_PARAMETER(symbol);
if(!IsValid())
{
return nullptr;
}
return nullptr;
}
};
#endif // MPT_OS
LibraryPath::LibraryPath(mpt::LibrarySearchPath searchPath, class mpt::PathString const &fileName)
: searchPath(searchPath)
, fileName(fileName)
{
return;
}
mpt::LibrarySearchPath LibraryPath::GetSearchPath() const
{
return searchPath;
}
mpt::PathString LibraryPath::GetFileName() const
{
return fileName;
}
mpt::PathString LibraryPath::GetDefaultPrefix()
{
#if MPT_OS_WINDOWS
return MPT_PATHSTRING("");
#elif MPT_OS_ANDROID
return MPT_PATHSTRING("lib");
#elif defined(MPT_WITH_LTDL)
return MPT_PATHSTRING("lib");
#elif defined(MPT_WITH_DL)
return MPT_PATHSTRING("lib");
#else
return MPT_PATHSTRING("lib");
#endif
}
mpt::PathString LibraryPath::GetDefaultSuffix()
{
#if MPT_OS_WINDOWS
return MPT_PATHSTRING(".dll");
#elif MPT_OS_ANDROID
return MPT_PATHSTRING(".so");
#elif defined(MPT_WITH_LTDL)
return MPT_PATHSTRING(""); // handled by libltdl
#elif defined(MPT_WITH_DL)
return MPT_PATHSTRING(".so");
#else
return mpt::PathString();
#endif
}
LibraryPath LibraryPath::App(const mpt::PathString &basename)
{
return LibraryPath(mpt::LibrarySearchPathApplication, GetDefaultPrefix() + basename + GetDefaultSuffix());
}
LibraryPath LibraryPath::AppFullName(const mpt::PathString &fullname)
{
return LibraryPath(mpt::LibrarySearchPathApplication, fullname + GetDefaultSuffix());
}
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
LibraryPath LibraryPath::AppDataFullName(const mpt::PathString &fullname, const mpt::PathString &appdata)
{
if(appdata.empty())
{
return LibraryPath(mpt::LibrarySearchPathInvalid, MPT_PATHSTRING(""));
}
return LibraryPath(mpt::LibrarySearchPathFullPath, appdata.WithTrailingSlash() + fullname + GetDefaultSuffix());
}
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
LibraryPath LibraryPath::System(const mpt::PathString &basename)
{
return LibraryPath(mpt::LibrarySearchPathSystem, GetDefaultPrefix() + basename + GetDefaultSuffix());
}
LibraryPath LibraryPath::FullPath(const mpt::PathString &path)
{
return LibraryPath(mpt::LibrarySearchPathFullPath, path);
}
Library::Library()
{
return;
}
Library::Library(const mpt::LibraryPath &path)
{
if(path.GetSearchPath() == mpt::LibrarySearchPathInvalid)
{
return;
}
if(path.GetFileName().empty())
{
return;
}
m_Handle = std::make_shared<LibraryHandle>(path);
}
void Library::Unload()
{
*this = mpt::Library();
}
bool Library::IsValid() const
{
return m_Handle && m_Handle->IsValid();
}
FuncPtr Library::GetProcAddress(const std::string &symbol) const
{
if(!IsValid())
{
return nullptr;
}
return m_Handle->GetProcAddress(symbol);
}
} // namespace mpt
#endif // MPT_ENABLE_DYNBIND
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,118 @@
/*
* mptLibrary.h
* ------------
* Purpose: Shared library handling.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_DYNBIND)
namespace mpt
{
typedef void* (*FuncPtr)(); // pointer to function returning void*
class LibraryHandle;
enum LibrarySearchPath
{
LibrarySearchPathInvalid,
LibrarySearchPathDefault,
LibrarySearchPathApplication,
LibrarySearchPathSystem,
LibrarySearchPathFullPath,
};
class LibraryPath
{
private:
mpt::LibrarySearchPath searchPath;
mpt::PathString fileName;
private:
LibraryPath(mpt::LibrarySearchPath searchPath, const mpt::PathString &fileName);
public:
mpt::LibrarySearchPath GetSearchPath() const;
mpt::PathString GetFileName() const;
public:
// "lib" on Unix-like systems, "" on Windows
static mpt::PathString GetDefaultPrefix();
// ".so" or ".dylib" or ".dll"
static mpt::PathString GetDefaultSuffix();
// Returns the library path in the application directory, with os-specific prefix and suffix added to basename.
// e.g.: basename = "unmo3" --> "libunmo3.so" / "apppath/unmo3.dll"
static LibraryPath App(const mpt::PathString &basename);
// Returns the library path in the application directory, with os-specific suffix added to fullname.
// e.g.: fullname = "libunmo3" --> "libunmo3.so" / "apppath/libunmo3.dll"
static LibraryPath AppFullName(const mpt::PathString &fullname);
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
// Returns the library path in the application data directory, with os-specific suffix added to fullname.
// e.g.: fullname = "libunmo3" --> "libunmo3.so" / "appdata/libunmo3.dll" (appdata == C:\Users\SOMEUSER\AppData\OpenMPT\Components\)
static LibraryPath AppDataFullName(const mpt::PathString &fullname, const mpt::PathString &appdata);
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
// Returns a system library name with os-specific prefix and suffix added to basename, but without any full path in order to be searched in the default search path.
// e.g.: basename = "unmo3" --> "libunmo3.so" / "unmo3.dll"
static LibraryPath System(const mpt::PathString &basename);
// Returns a system library name with os-specific suffix added to path.
// e.g.: path = "somepath/foo" --> "somepath/foo.so" / "somepath/foo.dll"
static LibraryPath FullPath(const mpt::PathString &path);
};
class Library
{
protected:
std::shared_ptr<LibraryHandle> m_Handle;
public:
Library();
Library(const mpt::LibraryPath &path);
public:
void Unload();
bool IsValid() const;
FuncPtr GetProcAddress(const std::string &symbol) const;
template <typename Tfunc>
bool Bind(Tfunc * & f, const std::string &symbol) const
{
#if !(MPT_OS_WINDOWS && MPT_COMPILER_GCC)
// MinGW64 std::is_function is always false for non __cdecl functions.
// See https://connect.microsoft.com/VisualStudio/feedback/details/774720/stl-is-function-bug .
STATIC_ASSERT(std::is_function<Tfunc>::value);
#endif
const FuncPtr addr = GetProcAddress(symbol);
f = reinterpret_cast<Tfunc*>(addr);
return (addr != nullptr);
}
};
} // namespace mpt
#endif // MPT_ENABLE_DYNBIND
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,233 @@
/*
* mptMutex.h
* ----------
* Purpose: Partially implement c++ mutexes as far as openmpt needs them. Can eventually go away when we only support c++11 compilers some time.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <vector> // some C++ header in order to have the C++ standard library version information available
#if !MPT_PLATFORM_MULTITHREADED
#define MPT_MUTEX_STD 0
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_OS_EMSCRIPTEN
#define MPT_MUTEX_STD 0
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_COMPILER_GENERIC && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_COMPILER_MSVC && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__) && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION) && !defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_MUTEX_STD 1
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#elif MPT_OS_WINDOWS
#define MPT_MUTEX_STD 0
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 1
#else
#define MPT_MUTEX_STD 0
#define MPT_MUTEX_PTHREAD 0
#define MPT_MUTEX_WIN32 0
#endif
#if !MPT_MUTEX_STD && !MPT_MUTEX_PTHREAD && !MPT_MUTEX_WIN32
#define MPT_MUTEX_NONE 1
#else
#define MPT_MUTEX_NONE 0
#endif
#if defined(MODPLUG_TRACKER) && MPT_MUTEX_NONE
#error "OpenMPT requires mutexes."
#endif
#if MPT_MUTEX_STD
#include <mutex>
#elif MPT_MUTEX_WIN32
#include <windows.h>
#elif MPT_MUTEX_PTHREAD
#include <pthread.h>
#endif // MPT_MUTEX
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
#if MPT_MUTEX_STD
typedef std::mutex mutex;
typedef std::recursive_mutex recursive_mutex;
#elif MPT_MUTEX_WIN32
// compatible with c++11 std::mutex, can eventually be replaced without touching any usage site
class mutex {
private:
CRITICAL_SECTION impl;
public:
mutex() { InitializeCriticalSection(&impl); }
~mutex() { DeleteCriticalSection(&impl); }
void lock() { EnterCriticalSection(&impl); }
bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; }
void unlock() { LeaveCriticalSection(&impl); }
};
// compatible with c++11 std::recursive_mutex, can eventually be replaced without touching any usage site
class recursive_mutex {
private:
CRITICAL_SECTION impl;
public:
recursive_mutex() { InitializeCriticalSection(&impl); }
~recursive_mutex() { DeleteCriticalSection(&impl); }
void lock() { EnterCriticalSection(&impl); }
bool try_lock() { return TryEnterCriticalSection(&impl) ? true : false; }
void unlock() { LeaveCriticalSection(&impl); }
};
#elif MPT_MUTEX_PTHREAD
class mutex {
private:
pthread_mutex_t hLock;
public:
mutex()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
pthread_mutex_init(&hLock, &attr);
pthread_mutexattr_destroy(&attr);
}
~mutex() { pthread_mutex_destroy(&hLock); }
void lock() { pthread_mutex_lock(&hLock); }
bool try_lock() { return (pthread_mutex_trylock(&hLock) == 0); }
void unlock() { pthread_mutex_unlock(&hLock); }
};
class recursive_mutex {
private:
pthread_mutex_t hLock;
public:
recursive_mutex()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&hLock, &attr);
pthread_mutexattr_destroy(&attr);
}
~recursive_mutex() { pthread_mutex_destroy(&hLock); }
void lock() { pthread_mutex_lock(&hLock); }
bool try_lock() { return (pthread_mutex_trylock(&hLock) == 0); }
void unlock() { pthread_mutex_unlock(&hLock); }
};
#else // MPT_MUTEX_NONE
class mutex {
public:
mutex() { }
~mutex() { }
void lock() { }
bool try_lock() { return true; }
void unlock() { }
};
class recursive_mutex {
public:
recursive_mutex() { }
~recursive_mutex() { }
void lock() { }
bool try_lock() { return true; }
void unlock() { }
};
#endif // MPT_MUTEX
#if MPT_MUTEX_STD
#define MPT_LOCK_GUARD std::lock_guard
#else // !MPT_MUTEX_STD
// compatible with c++11 std::lock_guard, can eventually be replaced without touching any usage site
template< typename mutex_type >
class lock_guard {
private:
mutex_type & mutex;
public:
lock_guard( mutex_type & m ) : mutex(m) { mutex.lock(); }
~lock_guard() { mutex.unlock(); }
};
#define MPT_LOCK_GUARD mpt::lock_guard
#endif // MPT_MUTEX_STD
#ifdef MODPLUG_TRACKER
class recursive_mutex_with_lock_count {
private:
mpt::recursive_mutex mutex;
long lockCount;
public:
recursive_mutex_with_lock_count()
: lockCount(0)
{
return;
}
~recursive_mutex_with_lock_count()
{
return;
}
void lock()
{
mutex.lock();
lockCount++;
}
void unlock()
{
lockCount--;
mutex.unlock();
}
public:
bool IsLockedByCurrentThread() // DEBUGGING only
{
bool islocked = false;
if(mutex.try_lock())
{
islocked = (lockCount > 0);
mutex.unlock();
}
return islocked;
}
};
#endif // MODPLUG_TRACKER
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,564 @@
/*
* mptOS.cpp
* ---------
* Purpose: Operating system version information.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptOS.h"
#if MPT_OS_WINDOWS
#include <windows.h>
#endif
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace Windows
{
#if MPT_OS_WINDOWS
#if !MPT_OS_WINDOWS_WINRT
static uint32 VersionDecimalTo_WIN32_WINNT(uint32 major, uint32 minor)
{
// GetVersionEx returns decimal.
// _WIN32_WINNT macro uses BCD for the minor byte (see Windows 98 / ME).
// We use what _WIN32_WINNT does.
uint32 result = 0;
minor = mpt::clamp<uint32>(minor, 0, 99);
result |= major;
result <<= 8;
result |= minor/10*0x10 + minor%10;
return result;
}
#endif // !MPT_OS_WINDOWS_WINRT
static void GatherWindowsVersion(uint32 & SystemVersion)
{
// Initialize to used SDK version
SystemVersion =
#if NTDDI_VERSION >= 0x0A000000 // NTDDI_WIN10
mpt::Windows::Version::Win10
#elif NTDDI_VERSION >= 0x06030000 // NTDDI_WINBLUE
mpt::Windows::Version::Win81
#elif NTDDI_VERSION >= 0x06020000 // NTDDI_WIN8
mpt::Windows::Version::Win8
#elif NTDDI_VERSION >= 0x06010000 // NTDDI_WIN7
mpt::Windows::Version::Win7
#elif NTDDI_VERSION >= 0x06000000 // NTDDI_VISTA
mpt::Windows::Version::WinVista
#elif NTDDI_VERSION >= 0x05020000 // NTDDI_WS03
mpt::Windows::Version::WinXP64
#elif NTDDI_VERSION >= NTDDI_WINXP
mpt::Windows::Version::WinXP
#elif NTDDI_VERSION >= NTDDI_WIN2K
mpt::Windows::Version::Win2000
#else
mpt::Windows::Version::WinNT4
#endif
;
#if !MPT_OS_WINDOWS_WINRT
OSVERSIONINFOEXW versioninfoex;
MemsetZero(versioninfoex);
versioninfoex.dwOSVersionInfoSize = sizeof(versioninfoex);
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4996) // 'GetVersionExW': was declared deprecated
#endif // MPT_COMPILER_MSVC
#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif // MPT_COMPILER_CLANG
GetVersionExW((LPOSVERSIONINFOW)&versioninfoex);
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#pragma clang diagnostic pop
#endif // MPT_COMPILER_CLANG
SystemVersion = VersionDecimalTo_WIN32_WINNT(versioninfoex.dwMajorVersion, versioninfoex.dwMinorVersion);
#endif // !MPT_OS_WINDOWS_WINRT
}
#ifdef MODPLUG_TRACKER
namespace {
struct WindowsVersionCache
{
uint32 SystemVersion;
WindowsVersionCache()
: SystemVersion(mpt::Windows::Version::WinNT4)
{
GatherWindowsVersion(SystemVersion);
}
};
}
static void GatherWindowsVersionFromCache(uint32 & SystemVersion)
{
static WindowsVersionCache gs_WindowsVersionCache;
SystemVersion = gs_WindowsVersionCache.SystemVersion;
}
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
Version::Version()
: SystemIsWindows(false)
, SystemVersion(mpt::Windows::Version::WinNT4)
{
return;
}
mpt::Windows::Version Version::Current()
{
mpt::Windows::Version result;
#if MPT_OS_WINDOWS
result.SystemIsWindows = true;
#ifdef MODPLUG_TRACKER
GatherWindowsVersionFromCache(result.SystemVersion);
#else // !MODPLUG_TRACKER
GatherWindowsVersion(result.SystemVersion);
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
return result;
}
bool Version::IsWindows() const
{
return SystemIsWindows;
}
bool Version::IsBefore(mpt::Windows::Version::Number version) const
{
if(!SystemIsWindows)
{
return false;
}
return (SystemVersion < static_cast<uint32>(version));
}
bool Version::IsAtLeast(mpt::Windows::Version::Number version) const
{
if(!SystemIsWindows)
{
return false;
}
return (SystemVersion >= static_cast<uint32>(version));
}
static MPT_CONSTEXPR11_VAR struct { Version::Number version; const MPT_UCHAR_TYPE * name; } versionMap[] =
{
{ mpt::Windows::Version::WinNewer, MPT_ULITERAL("Windows 10 (or newer)") },
{ mpt::Windows::Version::Win10, MPT_ULITERAL("Windows 10") },
{ mpt::Windows::Version::Win81, MPT_ULITERAL("Windows 8.1") },
{ mpt::Windows::Version::Win8, MPT_ULITERAL("Windows 8") },
{ mpt::Windows::Version::Win7, MPT_ULITERAL("Windows 7") },
{ mpt::Windows::Version::WinVista, MPT_ULITERAL("Windows Vista") },
{ mpt::Windows::Version::WinXP64, MPT_ULITERAL("Windows XP x64 / Windows Server 2003") },
{ mpt::Windows::Version::WinXP, MPT_ULITERAL("Windows XP") },
{ mpt::Windows::Version::Win2000, MPT_ULITERAL("Windows 2000") },
{ mpt::Windows::Version::WinME, MPT_ULITERAL("Windows ME") },
{ mpt::Windows::Version::Win98, MPT_ULITERAL("Windows 98") },
{ mpt::Windows::Version::WinNT4, MPT_ULITERAL("Windows NT4") }
};
mpt::ustring Version::VersionToString(uint16 version)
{
mpt::ustring result;
for(const auto &v : versionMap)
{
if(version > v.version)
{
result = MPT_USTRING("> ") + v.name;
break;
} else if(version == v.version)
{
result = v.name;
break;
}
}
if(result.empty())
{
result = mpt::format(MPT_USTRING("0x%1"))(mpt::ufmt::dec0<4>(version));
}
return result;
}
mpt::ustring Version::VersionToString(Number version)
{
return VersionToString(static_cast<uint16>(version));
}
mpt::ustring Version::GetName() const
{
mpt::ustring name = MPT_USTRING("Generic Windows NT");
for(const auto &v : versionMap)
{
if(mpt::Windows::Version::IsAtLeast(v.version))
{
name = v.name;
break;
}
}
mpt::ustring result = name;
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
if(mpt::Windows::IsWine())
{
mpt::Wine::VersionContext v;
if(v.Version().IsValid())
{
result = mpt::format(MPT_USTRING("Wine %1 (%2)"))(
v.Version().AsString()
, name
);
} else
{
result = mpt::format(MPT_USTRING("Wine (unknown version: '%1') (%2)"))(
mpt::ToUnicode(mpt::CharsetUTF8, v.RawVersion())
, name
);
}
}
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
return result;
}
#ifdef MODPLUG_TRACKER
mpt::ustring Version::GetNameShort() const
{
mpt::ustring name;
if(mpt::Windows::IsWine())
{
mpt::Wine::VersionContext v;
if(v.Version().IsValid())
{
name = mpt::format(MPT_USTRING("wine-%1"))(v.Version().AsString());
} else if(v.RawVersion().length() > 0)
{
name = MPT_USTRING("wine-") + Util::BinToHex(mpt::as_span(v.RawVersion()));
} else
{
name = MPT_USTRING("wine-");
}
name += MPT_USTRING("-") + Util::BinToHex(mpt::as_span(v.RawHostSysName()));
} else
{
name = mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::dec(SystemVersion >> 8), mpt::ufmt::HEX0<2>(SystemVersion & 0xFF));
}
return name;
}
#endif // MODPLUG_TRACKER
mpt::Windows::Version::Number Version::GetMinimumKernelLevel()
{
uint16 minimumKernelVersion = 0;
#if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
#if !defined(MPT_BUILD_TARGET_XP)
minimumKernelVersion = std::max<uint16>(minimumKernelVersion, mpt::Windows::Version::WinVista);
#else
minimumKernelVersion = std::max<uint16>(minimumKernelVersion, mpt::Windows::Version::WinXP);
#endif
#endif
return static_cast<mpt::Windows::Version::Number>(minimumKernelVersion);
}
mpt::Windows::Version::Number Version::GetMinimumAPILevel()
{
uint16 minimumApiVersion = 0;
#if MPT_OS_WINDOWS && defined(_WIN32_WINNT)
minimumApiVersion = std::max<uint16>(minimumApiVersion, _WIN32_WINNT);
#endif
return static_cast<mpt::Windows::Version::Number>(minimumApiVersion);
}
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
static bool GatherSystemIsWine()
{
bool SystemIsWine = false;
HMODULE hNTDLL = LoadLibraryW(L"ntdll.dll");
if(hNTDLL)
{
SystemIsWine = (GetProcAddress(hNTDLL, "wine_get_version") != NULL);
FreeLibrary(hNTDLL);
hNTDLL = NULL;
}
return SystemIsWine;
}
#endif // MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
namespace {
struct SystemIsWineCache
{
bool SystemIsWine;
SystemIsWineCache()
: SystemIsWine(GatherSystemIsWine())
{
return;
}
SystemIsWineCache(bool isWine)
: SystemIsWine(isWine)
{
return;
}
};
}
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
static bool SystemIsWine(bool allowDetection = true)
{
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
static SystemIsWineCache gs_SystemIsWineCache = allowDetection ? SystemIsWineCache() : SystemIsWineCache(false);
if(!allowDetection)
{ // catch too late calls of PreventWineDetection
MPT_ASSERT(!gs_SystemIsWineCache.SystemIsWine);
}
return gs_SystemIsWineCache.SystemIsWine;
#elif MPT_OS_WINDOWS
MPT_UNREFERENCED_PARAMETER(allowDetection);
return GatherSystemIsWine();
#else
MPT_UNREFERENCED_PARAMETER(allowDetection);
return false;
#endif
}
void PreventWineDetection()
{
SystemIsWine(false);
}
bool IsOriginal()
{
return mpt::Windows::Version::Current().IsWindows() && !SystemIsWine();
}
bool IsWine()
{
return mpt::Windows::Version::Current().IsWindows() && SystemIsWine();
}
#endif // MODPLUG_TRACKER
} // namespace Windows
} // namespace mpt
#if defined(MODPLUG_TRACKER)
namespace mpt
{
namespace Wine
{
Version::Version()
: valid(false)
, vmajor(0)
, vminor(0)
, vupdate(0)
{
return;
}
Version::Version(const mpt::ustring &rawVersion)
: valid(false)
, vmajor(0)
, vminor(0)
, vupdate(0)
{
if(rawVersion.empty())
{
return;
}
std::vector<uint8> version = mpt::String::Split<uint8>(rawVersion, MPT_USTRING("."));
if(version.size() < 2)
{
return;
}
mpt::ustring parsedVersion = mpt::String::Combine(version, MPT_USTRING("."));
std::size_t len = std::min(parsedVersion.length(), rawVersion.length());
if(len == 0)
{
return;
}
if(parsedVersion.substr(0, len) != rawVersion.substr(0, len))
{
return;
}
valid = true;
vmajor = version[0];
vminor = version[1];
vupdate = (version.size() >= 3) ? version[2] : 0;
}
Version::Version(uint8 vmajor, uint8 vminor, uint8 vupdate)
: valid((vmajor > 0) || (vminor > 0) || (vupdate > 0))
, vmajor(vmajor)
, vminor(vminor)
, vupdate(vupdate)
{
return;
}
mpt::Wine::Version Version::FromInteger(uint32 version)
{
mpt::Wine::Version result;
result.valid = (version <= 0xffffff);
result.vmajor = static_cast<uint8>(version >> 16);
result.vminor = static_cast<uint8>(version >> 8);
result.vupdate = static_cast<uint8>(version >> 0);
return result;
}
bool Version::IsValid() const
{
return valid;
}
mpt::ustring Version::AsString() const
{
return mpt::ufmt::dec(vmajor) + MPT_USTRING(".") + mpt::ufmt::dec(vminor) + MPT_USTRING(".") + mpt::ufmt::dec(vupdate);
}
uint32 Version::AsInteger() const
{
uint32 version = 0;
version |= static_cast<uint32>(vmajor) << 16;
version |= static_cast<uint32>(vminor) << 8;
version |= static_cast<uint32>(vupdate) << 0;
return version;
}
bool Version::IsBefore(mpt::Wine::Version other) const
{
if(!IsValid())
{
return false;
}
return (AsInteger() < other.AsInteger());
}
bool Version::IsAtLeast(mpt::Wine::Version other) const
{
if(!IsValid())
{
return false;
}
return (AsInteger() >= other.AsInteger());
}
mpt::Wine::Version GetMinimumWineVersion()
{
mpt::Wine::Version minimumWineVersion = mpt::Wine::Version(0,0,0);
#if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
#if !defined(MPT_BUILD_TARGET_XP)
minimumWineVersion = mpt::Wine::Version(1,8,0);
#else
minimumWineVersion = mpt::Wine::Version(1,6,0);
#endif
#endif
return minimumWineVersion;
}
VersionContext::VersionContext()
: m_IsWine(false)
, m_HostIsLinux(false)
, m_HostIsBSD(false)
{
#if MPT_OS_WINDOWS
m_IsWine = mpt::Windows::IsWine();
if(!m_IsWine)
{
return;
}
m_NTDLL = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("ntdll.dll")));
if(m_NTDLL.IsValid())
{
const char * (__cdecl * wine_get_version)(void) = nullptr;
const char * (__cdecl * wine_get_build_id)(void) = nullptr;
void (__cdecl * wine_get_host_version)(const char * *, const char * *) = nullptr;
m_NTDLL.Bind(wine_get_version, "wine_get_version");
m_NTDLL.Bind(wine_get_build_id, "wine_get_build_id");
m_NTDLL.Bind(wine_get_host_version, "wine_get_host_version");
const char * wine_version = nullptr;
const char * wine_build_id = nullptr;
const char * wine_host_sysname = nullptr;
const char * wine_host_release = nullptr;
wine_version = wine_get_version ? wine_get_version() : "";
wine_build_id = wine_get_build_id ? wine_get_build_id() : "";
if(wine_get_host_version)
{
wine_get_host_version(&wine_host_sysname, &wine_host_release);
}
m_RawVersion = wine_version ? wine_version : "";
m_RawBuildID = wine_build_id ? wine_build_id : "";
m_RawHostSysName = wine_host_sysname ? wine_host_sysname : "";
m_RawHostRelease = wine_host_release ? wine_host_release : "";
}
m_Version = mpt::Wine::Version(mpt::ToUnicode(mpt::CharsetUTF8, m_RawVersion));
m_HostIsLinux = (m_RawHostSysName == "Linux");
m_HostIsBSD = (m_RawHostSysName == "FreeBSD" || m_RawHostSysName == "DragonFly" || m_RawHostSysName == "NetBSD" || m_RawHostSysName == "OpenBSD");
#endif // MPT_OS_WINDOWS
}
} // namespace Wine
} // namespace mpt
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,163 @@
/*
* mptOS.h
* -------
* Purpose: Operating system version information.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "mptLibrary.h"
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace Windows
{
class Version
{
public:
enum Number
{
WinNT4 = 0x0400,
Win98 = 0x0410,
WinME = 0x0490,
Win2000 = 0x0500,
WinXP = 0x0501,
WinXP64 = 0x0502,
WinVista = 0x0600,
Win7 = 0x0601,
Win8 = 0x0602,
Win81 = 0x0603,
Win10 = 0x0a00,
WinNewer = Win10 + 1
};
static mpt::ustring VersionToString(uint16 version);
static mpt::ustring VersionToString(Number version);
private:
bool SystemIsWindows;
uint32 SystemVersion;
private:
Version();
public:
static mpt::Windows::Version Current();
public:
bool IsWindows() const;
bool IsBefore(mpt::Windows::Version::Number version) const;
bool IsAtLeast(mpt::Windows::Version::Number version) const;
mpt::ustring GetName() const;
#ifdef MODPLUG_TRACKER
mpt::ustring GetNameShort() const;
#endif // MODPLUG_TRACKER
public:
static mpt::Windows::Version::Number GetMinimumKernelLevel();
static mpt::Windows::Version::Number GetMinimumAPILevel();
}; // class Version
#if defined(MODPLUG_TRACKER)
void PreventWineDetection();
bool IsOriginal();
bool IsWine();
#endif // MODPLUG_TRACKER
} // namespace Windows
} // namespace mpt
#if defined(MODPLUG_TRACKER)
namespace mpt
{
namespace Wine
{
class Version
{
private:
bool valid;
uint8 vmajor;
uint8 vminor;
uint8 vupdate;
public:
Version();
Version(uint8 vmajor, uint8 vminor, uint8 vupdate);
explicit Version(const mpt::ustring &version);
public:
bool IsValid() const;
mpt::ustring AsString() const;
private:
static mpt::Wine::Version FromInteger(uint32 version);
uint32 AsInteger() const;
public:
bool IsBefore(mpt::Wine::Version other) const;
bool IsAtLeast(mpt::Wine::Version other) const;
};
mpt::Wine::Version GetMinimumWineVersion();
class VersionContext
{
protected:
bool m_IsWine;
mpt::Library m_NTDLL;
std::string m_RawVersion;
std::string m_RawBuildID;
std::string m_RawHostSysName;
std::string m_RawHostRelease;
mpt::Wine::Version m_Version;
bool m_HostIsLinux;
bool m_HostIsBSD;
public:
VersionContext();
public:
bool IsWine() const { return m_IsWine; }
mpt::Library NTDLL() const { return m_NTDLL; }
std::string RawVersion() const { return m_RawVersion; }
std::string RawBuildID() const { return m_RawBuildID; }
std::string RawHostSysName() const { return m_RawHostSysName; }
std::string RawHostRelease() const { return m_RawHostRelease; }
mpt::Wine::Version Version() const { return m_Version; }
bool HostIsLinux() const { return m_HostIsLinux; }
bool HostIsBSD() const { return m_HostIsBSD; }
};
} // namespace Wine
} // namespace mpt
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,889 @@
/*
* mptPathString.cpp
* -----------------
* Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptPathString.h"
#include "misc_util.h"
#include "mptUUID.h"
#if MPT_OS_WINDOWS
#include <windows.h>
#if defined(MODPLUG_TRACKER)
#include <shlwapi.h>
#endif
#endif
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if defined(__MINGW32__) || defined(__MINGW64__)
// MinGW-w64 headers do not declare this for WinRT, which is wrong.
extern "C" {
WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart);
}
#endif
#endif
OPENMPT_NAMESPACE_BEGIN
#if MPT_OS_WINDOWS
#define MPT_PATHSTRING_LITERAL(x) ( L ## x )
#else
#define MPT_PATHSTRING_LITERAL(x) ( x )
#endif
#if MPT_OS_WINDOWS
namespace mpt
{
RawPathString PathString::AsNativePrefixed() const
{
if(path.length() <= MAX_PATH || path.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\"))
{
// Path is short enough or already in prefixed form
return path;
}
const RawPathString absPath = mpt::GetAbsolutePath(path).AsNative();
if(absPath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\"))
{
// Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar
return MPT_PATHSTRING_LITERAL("\\\\?\\UNC") + absPath.substr(1);
} else
{
// Regular file: C:\foo.bar -> \\?\C:\foo.bar
return MPT_PATHSTRING_LITERAL("\\\\?\\") + absPath;
}
}
#if !MPT_OS_WINDOWS_WINRT
int PathString::CompareNoCase(const PathString & a, const PathString & b)
{
return lstrcmpiW(a.path.c_str(), b.path.c_str());
}
#endif // !MPT_OS_WINDOWS_WINRT
// Convert a path to its simplified form, i.e. remove ".\" and "..\" entries
// Note: We use our own implementation as PathCanonicalize is limited to MAX_PATH
// and unlimited versions are only available on Windows 8 and later.
// Furthermore, we also convert forward-slashes to backslashes and always remove trailing slashes.
PathString PathString::Simplify() const
{
if(path.empty())
return PathString();
std::vector<RawPathString> components;
RawPathString root;
RawPathString::size_type startPos = 0;
if(path.size() >= 2 && path[1] == MPT_PATHSTRING_LITERAL(':'))
{
// Drive letter
root = path.substr(0, 2) + MPT_PATHSTRING_LITERAL('\\');
startPos = 2;
} else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\"))
{
// Network share
root = MPT_PATHSTRING_LITERAL("\\\\");
startPos = 2;
} else if(path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\") || path.substr(0, 2) == MPT_PATHSTRING_LITERAL("./"))
{
// Special case for relative paths
root = MPT_PATHSTRING_LITERAL(".\\");
startPos = 2;
} else if(path.size() >= 1 && (path[0] == MPT_PATHSTRING_LITERAL('\\') || path[0] == MPT_PATHSTRING_LITERAL('/')))
{
// Special case for relative paths
root = MPT_PATHSTRING_LITERAL("\\");
startPos = 1;
}
while(startPos < path.size())
{
auto pos = path.find_first_of(MPT_PATHSTRING_LITERAL("\\/"), startPos);
if(pos == RawPathString::npos)
pos = path.size();
mpt::RawPathString dir = path.substr(startPos, pos - startPos);
if(dir == MPT_PATHSTRING_LITERAL(".."))
{
// Go back one directory
if(!components.empty())
{
components.pop_back();
}
} else if(dir == MPT_PATHSTRING_LITERAL("."))
{
// nop
} else if(!dir.empty())
{
components.push_back(std::move(dir));
}
startPos = pos + 1;
}
RawPathString result = root;
result.reserve(path.size());
for(const auto &component : components)
{
result += component + MPT_PATHSTRING_LITERAL("\\");
}
if(!components.empty())
result.pop_back();
return result;
}
} // namespace mpt
#endif // MPT_OS_WINDOWS
namespace mpt
{
#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE))
void PathString::SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const
{
// We cannot use CRT splitpath here, because:
// * limited to _MAX_PATH or similar
// * no support for UNC paths
// * no support for \\?\ prefixed paths
if(drive) *drive = mpt::PathString();
if(dir) *dir = mpt::PathString();
if(fname) *fname = mpt::PathString();
if(ext) *ext = mpt::PathString();
mpt::RawPathString p = path;
// remove \\?\\ prefix
if(p.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\"))
{
p = MPT_PATHSTRING_LITERAL("\\\\") + p.substr(8);
} else if(p.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\"))
{
p = p.substr(4);
}
if (p.length() >= 2 && (
p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\")
|| p.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\/")
|| p.substr(0, 2) == MPT_PATHSTRING_LITERAL("/\\")
|| p.substr(0, 2) == MPT_PATHSTRING_LITERAL("//")
))
{ // UNC
mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(MPT_PATHSTRING_LITERAL("\\/"));
if(first_slash != mpt::RawPathString::npos)
{
mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(MPT_PATHSTRING_LITERAL("\\/"));
if(second_slash != mpt::RawPathString::npos)
{
if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2 + first_slash + 1 + second_slash));
p = p.substr(2 + first_slash + 1 + second_slash);
} else
{
if(drive) *drive = mpt::PathString::FromNative(p);
p = mpt::RawPathString();
}
} else
{
if(drive) *drive = mpt::PathString::FromNative(p);
p = mpt::RawPathString();
}
} else
{ // local
if(p.length() >= 2 && (p[1] == MPT_PATHSTRING_LITERAL(':')))
{
if(drive) *drive = mpt::PathString::FromNative(p.substr(0, 2));
p = p.substr(2);
} else
{
if(drive) *drive = mpt::PathString();
}
}
mpt::RawPathString::size_type last_slash = p.find_last_of(MPT_PATHSTRING_LITERAL("\\/"));
if(last_slash != mpt::RawPathString::npos)
{
if(dir) *dir = mpt::PathString::FromNative(p.substr(0, last_slash + 1));
p = p.substr(last_slash + 1);
} else
{
if(dir) *dir = mpt::PathString();
}
mpt::RawPathString::size_type last_dot = p.find_last_of(MPT_PATHSTRING_LITERAL("."));
if(last_dot == mpt::RawPathString::npos)
{
if(fname) *fname = mpt::PathString::FromNative(p);
if(ext) *ext = mpt::PathString();
} else
{
if(fname) *fname = mpt::PathString::FromNative(p.substr(0, last_dot));
if(ext) *ext = mpt::PathString::FromNative(p.substr(last_dot));
}
}
PathString PathString::GetDrive() const
{
PathString drive;
SplitPath(&drive, nullptr, nullptr, nullptr);
return drive;
}
PathString PathString::GetDir() const
{
PathString dir;
SplitPath(nullptr, &dir, nullptr, nullptr);
return dir;
}
PathString PathString::GetPath() const
{
PathString drive, dir;
SplitPath(&drive, &dir, nullptr, nullptr);
return drive + dir;
}
PathString PathString::GetFileName() const
{
PathString fname;
SplitPath(nullptr, nullptr, &fname, nullptr);
return fname;
}
PathString PathString::GetFileExt() const
{
PathString ext;
SplitPath(nullptr, nullptr, nullptr, &ext);
return ext;
}
PathString PathString::GetFullFileName() const
{
PathString name, ext;
SplitPath(nullptr, nullptr, &name, &ext);
return name + ext;
}
bool PathString::IsDirectory() const
{
// Using PathIsDirectoryW here instead would increase libopenmpt dependencies by shlwapi.dll.
// GetFileAttributesW also does the job just fine.
#if MPT_OS_WINDOWS_WINRT
WIN32_FILE_ATTRIBUTE_DATA data;
MemsetZero(data);
if(::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0)
{
return false;
}
DWORD dwAttrib = data.dwFileAttributes;
#else // !MPT_OS_WINDOWS_WINRT
DWORD dwAttrib = ::GetFileAttributesW(path.c_str());
#endif // MPT_OS_WINDOWS_WINRT
return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
bool PathString::IsFile() const
{
#if MPT_OS_WINDOWS_WINRT
WIN32_FILE_ATTRIBUTE_DATA data;
MemsetZero(data);
if (::GetFileAttributesExW(path.c_str(), GetFileExInfoStandard, &data) == 0)
{
return false;
}
DWORD dwAttrib = data.dwFileAttributes;
#else // !MPT_OS_WINDOWS_WINRT
DWORD dwAttrib = ::GetFileAttributesW(path.c_str());
#endif // MPT_OS_WINDOWS_WINRT
return ((dwAttrib != INVALID_FILE_ATTRIBUTES) && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE)
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
bool PathString::FileOrDirectoryExists() const
{
return ::PathFileExistsW(path.c_str()) != FALSE;
}
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
PathString PathString::ReplaceExt(const mpt::PathString &newExt) const
{
return GetDrive() + GetDir() + GetFileName() + newExt;
}
PathString PathString::SanitizeComponent() const
{
PathString result = *this;
SanitizeFilename(result);
return result;
}
// Convert an absolute path to a path that's relative to "&relativeTo".
PathString PathString::AbsolutePathToRelative(const PathString &relativeTo) const
{
mpt::PathString result = path;
if(path.empty())
{
return result;
}
if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length()))
{
// Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath")
result = MPT_PATHSTRING(".\\"); // ".\"
result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length()));
} else if(!_wcsnicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), 2))
{
// Path is on the same drive as OpenMPT ("C:\Somepath" => "\Somepath")
result = mpt::PathString::FromNative(AsNative().substr(2));
}
return result;
}
// Convert a path that is relative to "&relativeTo" to an absolute path.
PathString PathString::RelativePathToAbsolute(const PathString &relativeTo) const
{
mpt::PathString result = path;
if(path.empty())
{
return result;
}
if(path.length() >= 2 && path.at(0) == MPT_PATHSTRING_LITERAL('\\') && path.at(1) != MPT_PATHSTRING_LITERAL('\\'))
{
// Path is on the same drive as OpenMPT ("\Somepath\" => "C:\Somepath\"), but ignore network paths starting with "\\"
result = mpt::PathString::FromNative(relativeTo.AsNative().substr(0, 2));
result += path;
} else if(path.length() >= 2 && path.substr(0, 2) == MPT_PATHSTRING_LITERAL(".\\"))
{
// Path is OpenMPT's directory or a sub directory (".\Somepath\" => "C:\OpenMPT\Somepath\")
result = relativeTo; // "C:\OpenMPT\"
result += mpt::PathString::FromNative(AsNative().substr(2));
}
return result;
}
#if defined(_MFC_VER)
mpt::PathString PathString::TunnelOutofCString(const CString &path)
{
#ifdef UNICODE
return mpt::PathString::FromWide(path.GetString());
#else
// Since MFC code can call into our code from a lot of places, we cannot assume
// that filenames we get from MFC are always encoded in our hacked UTF8-in-CString encoding.
// Instead, we use a rough heuristic: if the string is parseable as UTF8, we assume it is.
// This fails for CP_ACP strings, that are also valid UTF8. That's the trade-off here.
if(mpt::IsUTF8(path.GetString()))
{
// utf8
return mpt::PathString::FromUTF8(path.GetString());
} else
{
// ANSI
return mpt::PathString::FromWide(mpt::ToWide(path));
}
#endif
}
CString PathString::TunnelIntoCString(const mpt::PathString &path)
{
#ifdef UNICODE
return path.ToWide().c_str();
#else
return path.ToUTF8().c_str();
#endif
}
#endif // MFC
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
} // namespace mpt
namespace mpt
{
bool IsPathSeparator(mpt::RawPathString::value_type c) {
#if MPT_OS_WINDOWS
return (c == MPT_PATHSTRING_LITERAL('\\')) || (c == MPT_PATHSTRING_LITERAL('/'));
#else
return c == MPT_PATHSTRING_LITERAL('/');
#endif
}
bool PathIsAbsolute(const mpt::PathString &path) {
mpt::RawPathString rawpath = path.AsNative();
#if MPT_OS_WINDOWS
if(rawpath.substr(0, 8) == MPT_PATHSTRING_LITERAL("\\\\?\\UNC\\"))
{
return true;
}
if(rawpath.substr(0, 4) == MPT_PATHSTRING_LITERAL("\\\\?\\"))
{
return true;
}
if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("\\\\"))
{
return true; // UNC
}
if(rawpath.substr(0, 2) == MPT_PATHSTRING_LITERAL("//"))
{
return true; // UNC
}
return (rawpath.length()) >= 3 && (rawpath[1] == ':') && IsPathSeparator(rawpath[2]);
#else
return (rawpath.length() >= 1) && IsPathSeparator(rawpath[0]);
#endif
}
#if MPT_OS_WINDOWS
mpt::PathString GetAbsolutePath(const mpt::PathString &path)
{
DWORD size = GetFullPathNameW(path.AsNative().c_str(), 0, nullptr, nullptr);
if(size == 0)
{
return path;
}
std::vector<WCHAR> fullPathName(size, L'\0');
if(GetFullPathNameW(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0)
{
return path;
}
return mpt::PathString::FromNative(fullPathName.data());
}
#ifdef MODPLUG_TRACKER
bool DeleteWholeDirectoryTree(mpt::PathString path)
{
if(path.AsNative().empty())
{
return false;
}
if(PathIsRelativeW(path.AsNative().c_str()) == TRUE)
{
return false;
}
if(!path.FileOrDirectoryExists())
{
return true;
}
if(!path.IsDirectory())
{
return false;
}
path.EnsureTrailingSlash();
HANDLE hFind = NULL;
WIN32_FIND_DATAW wfd;
MemsetZero(wfd);
hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd);
if(hFind != NULL && hFind != INVALID_HANDLE_VALUE)
{
do
{
mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName);
if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING(".."))
{
filename = path + filename;
if(filename.IsDirectory())
{
if(!DeleteWholeDirectoryTree(filename))
{
return false;
}
} else if(filename.IsFile())
{
if(DeleteFileW(filename.AsNative().c_str()) == 0)
{
return false;
}
}
}
} while(FindNextFileW(hFind, &wfd));
FindClose(hFind);
}
if(RemoveDirectoryW(path.AsNative().c_str()) == 0)
{
return false;
}
return true;
}
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
#if MPT_OS_WINDOWS
#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)
mpt::PathString GetAppPath()
{
std::vector<WCHAR> exeFileName(MAX_PATH);
while(GetModuleFileNameW(0, exeFileName.data(), mpt::saturate_cast<DWORD>(exeFileName.size())) >= exeFileName.size())
{
if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return mpt::PathString();
}
exeFileName.resize(exeFileName.size() * 2);
}
return mpt::GetAbsolutePath(mpt::PathString::FromNative(exeFileName.data()).GetPath());
}
#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE
#if defined(MPT_ENABLE_DYNBIND)
#if !MPT_OS_WINDOWS_WINRT
mpt::PathString GetSystemPath()
{
DWORD size = GetSystemDirectoryW(nullptr, 0);
std::vector<WCHAR> path(size + 1);
if(!GetSystemDirectoryW(path.data(), size + 1))
{
return mpt::PathString();
}
return mpt::PathString::FromNative(path.data()) + MPT_PATHSTRING("\\");
}
#endif // !MPT_OS_WINDOWS_WINRT
#endif // MPT_ENABLE_DYNBIND
#endif // MPT_OS_WINDOWS
#if defined(MPT_ENABLE_TEMPFILE)
#if MPT_OS_WINDOWS
mpt::PathString GetTempDirectory()
{
DWORD size = GetTempPathW(0, nullptr);
if(size)
{
std::vector<WCHAR> tempPath(size + 1);
if(GetTempPathW(size + 1, tempPath.data()))
{
return mpt::PathString::FromNative(tempPath.data());
}
}
// use app directory as fallback
return mpt::GetAppPath();
}
mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension)
{
mpt::PathString filename = mpt::GetTempDirectory();
filename += (!fileNamePrefix.empty() ? fileNamePrefix + MPT_PATHSTRING("_") : mpt::PathString());
filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly().ToUString());
filename += (!fileNameExtension.empty() ? MPT_PATHSTRING(".") + fileNameExtension : mpt::PathString());
return filename;
}
TempFileGuard::TempFileGuard(const mpt::PathString &filename)
: filename(filename)
{
return;
}
mpt::PathString TempFileGuard::GetFilename() const
{
return filename;
}
TempFileGuard::~TempFileGuard()
{
if(!filename.empty())
{
DeleteFileW(filename.AsNative().c_str());
}
}
#ifdef MODPLUG_TRACKER
TempDirGuard::TempDirGuard(const mpt::PathString &dirname_)
: dirname(dirname_.WithTrailingSlash())
{
if(dirname.empty())
{
return;
}
if(::CreateDirectoryW(dirname.AsNative().c_str(), NULL) == 0)
{ // fail
dirname = mpt::PathString();
}
}
mpt::PathString TempDirGuard::GetDirname() const
{
return dirname;
}
TempDirGuard::~TempDirGuard()
{
if(!dirname.empty())
{
DeleteWholeDirectoryTree(dirname);
}
}
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
#endif // MPT_ENABLE_TEMPFILE
} // namespace mpt
#if defined(MODPLUG_TRACKER)
static inline char SanitizeFilenameChar(char c)
{
if( c == '\\' ||
c == '\"' ||
c == '/' ||
c == ':' ||
c == '?' ||
c == '<' ||
c == '>' ||
c == '|' ||
c == '*')
{
c = '_';
}
return c;
}
static inline wchar_t SanitizeFilenameChar(wchar_t c)
{
if( c == L'\\' ||
c == L'\"' ||
c == L'/' ||
c == L':' ||
c == L'?' ||
c == L'<' ||
c == L'>' ||
c == L'|' ||
c == L'*')
{
c = L'_';
}
return c;
}
void SanitizeFilename(mpt::PathString &filename)
{
mpt::RawPathString tmp = filename.AsNative();
for(auto &c : tmp)
{
c = SanitizeFilenameChar(c);
}
filename = mpt::PathString::FromNative(tmp);
}
void SanitizeFilename(char *beg, char *end)
{
for(char *it = beg; it != end; ++it)
{
*it = SanitizeFilenameChar(*it);
}
}
void SanitizeFilename(wchar_t *beg, wchar_t *end)
{
for(wchar_t *it = beg; it != end; ++it)
{
*it = SanitizeFilenameChar(*it);
}
}
void SanitizeFilename(std::string &str)
{
for(size_t i = 0; i < str.length(); i++)
{
str[i] = SanitizeFilenameChar(str[i]);
}
}
void SanitizeFilename(std::wstring &str)
{
for(size_t i = 0; i < str.length(); i++)
{
str[i] = SanitizeFilenameChar(str[i]);
}
}
#if MPT_USTRING_MODE_UTF8
void SanitizeFilename(mpt::u8string &str)
{
for(size_t i = 0; i < str.length(); i++)
{
str[i] = SanitizeFilenameChar(str[i]);
}
}
#endif // MPT_USTRING_MODE_UTF8
#if defined(_MFC_VER)
void SanitizeFilename(CString &str)
{
for(int i = 0; i < str.GetLength(); i++)
{
str.SetAt(i, SanitizeFilenameChar(str.GetAt(i)));
}
}
#endif // MFC
#endif // MODPLUG_TRACKER
#if defined(MODPLUG_TRACKER)
mpt::PathString FileType::AsFilterString(FlagSet<FileTypeFormat> format) const
{
mpt::PathString filter;
if(GetShortName().empty() || GetExtensions().empty())
{
return filter;
}
if(!GetDescription().empty())
{
filter += mpt::PathString::FromUnicode(GetDescription());
} else
{
filter += mpt::PathString::FromUnicode(GetShortName());
}
const auto extensions = GetExtensions();
if(format[FileTypeFormatShowExtensions])
{
filter += MPT_PATHSTRING(" (");
bool first = true;
for(const auto &ext : extensions)
{
if(first)
{
first = false;
} else
{
filter += MPT_PATHSTRING(",");
}
filter += MPT_PATHSTRING("*.");
filter += ext;
}
filter += MPT_PATHSTRING(")");
}
filter += MPT_PATHSTRING("|");
{
bool first = true;
for(const auto &ext : extensions)
{
if(first)
{
first = false;
} else
{
filter += MPT_PATHSTRING(";");
}
filter += MPT_PATHSTRING("*.");
filter += ext;
}
}
filter += MPT_PATHSTRING("|");
return filter;
}
mpt::PathString FileType::AsFilterOnlyString() const
{
mpt::PathString filter;
const auto extensions = GetExtensions();
{
bool first = true;
for(const auto &ext : extensions)
{
if(first)
{
first = false;
} else
{
filter += MPT_PATHSTRING(";");
}
filter += MPT_PATHSTRING("*.");
filter += ext;
}
}
return filter;
}
mpt::PathString ToFilterString(const FileType &fileType, FlagSet<FileTypeFormat> format)
{
return fileType.AsFilterString(format);
}
mpt::PathString ToFilterString(const std::vector<FileType> &fileTypes, FlagSet<FileTypeFormat> format)
{
mpt::PathString filter;
for(const auto &type : fileTypes)
{
filter += type.AsFilterString(format);
}
return filter;
}
mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty)
{
mpt::PathString filter = fileType.AsFilterOnlyString();
return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? MPT_PATHSTRING(";") : MPT_PATHSTRING("")) + filter;
}
mpt::PathString ToFilterOnlyString(const std::vector<FileType> &fileTypes, bool prependSemicolonWhenNotEmpty)
{
mpt::PathString filter;
for(const auto &type : fileTypes)
{
filter += type.AsFilterOnlyString();
}
return filter.empty() ? filter : (prependSemicolonWhenNotEmpty ? MPT_PATHSTRING(";") : MPT_PATHSTRING("")) + filter;
}
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,503 @@
/*
* mptPathString.h
* ---------------
* Purpose: Wrapper class around the platform-native representation of path names. Should be the only type that is used to store path names.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <vector>
#include "FlagSet.h"
OPENMPT_NAMESPACE_BEGIN
#define MPT_DEPRECATED_PATH
//#define MPT_DEPRECATED_PATH MPT_DEPRECATED
namespace mpt
{
#if MPT_OS_WINDOWS
typedef std::wstring RawPathString;
#else // !MPT_OS_WINDOWS
typedef std::string RawPathString;
#endif // if MPT_OS_WINDOWS
class PathString
{
private:
RawPathString path;
private:
PathString(const RawPathString & path)
: path(path)
{
return;
}
public:
PathString()
{
return;
}
PathString(const PathString & other)
: path(other.path)
{
return;
}
PathString & assign(const PathString & other)
{
path = other.path;
return *this;
}
PathString & operator = (const PathString & other)
{
return assign(other);
}
PathString & append(const PathString & other)
{
path.append(other.path);
return *this;
}
PathString & operator += (const PathString & other)
{
return append(other);
}
friend PathString operator + (const PathString & a, const PathString & b)
{
return PathString(a).append(b);
}
friend bool operator < (const PathString & a, const PathString & b)
{
return a.AsNative() < b.AsNative();
}
friend bool operator == (const PathString & a, const PathString & b)
{
return a.AsNative() == b.AsNative();
}
friend bool operator != (const PathString & a, const PathString & b)
{
return a.AsNative() != b.AsNative();
}
bool empty() const { return path.empty(); }
std::size_t Length() const { return path.size(); }
public:
#if MPT_OS_WINDOWS
#if !MPT_OS_WINDOWS_WINRT
static int CompareNoCase(const PathString & a, const PathString & b);
#endif // !MPT_OS_WINDOWS_WINRT
#endif
#if MPT_OS_WINDOWS && (defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE))
void SplitPath(PathString *drive, PathString *dir, PathString *fname, PathString *ext) const;
// \\?\ prefixes will be removed and \\?\\UNC prefixes converted to canonical \\ form.
PathString GetDrive() const; // Drive letter + colon, e.g. "C:" or \\server\\share
PathString GetDir() const; // Directory, e.g. "\OpenMPT\"
PathString GetPath() const; // Drive + Dir, e.g. "C:\OpenMPT\"
PathString GetFileName() const; // File name without extension, e.g. "mptrack"
PathString GetFileExt() const; // Extension including dot, e.g. ".exe"
PathString GetFullFileName() const; // File name + extension, e.g. "mptrack.exe"
// Verify if this path represents a valid directory on the file system.
bool IsDirectory() const;
// Verify if this path exists and is a file on the file system.
bool IsFile() const;
#endif // MPT_OS_WINDOWS && (MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE)
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
bool FileOrDirectoryExists() const;
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
// Return the same path string with a different (or appended) extension (including "."), e.g. "foo.bar",".txt" -> "foo.txt" or "C:\OpenMPT\foo",".txt" -> "C:\OpenMPT\foo.txt"
PathString ReplaceExt(const mpt::PathString &newExt) const;
// Removes special characters from a filename component and replaces them with a safe replacement character ("_" on windows).
// Returns the result.
// Note that this also removes path component separators, so this should only be used on single-component PathString objects.
PathString SanitizeComponent() const;
bool HasTrailingSlash() const
{
if(empty())
return false;
RawPathString::value_type c = path[path.length() - 1];
#if MPT_OS_WINDOWS
return (c == L'\\' || c == L'/');
#else
return (c == '/');
#endif
}
mpt::PathString &EnsureTrailingSlash()
{
if(!path.empty() && !HasTrailingSlash())
{
#if MPT_OS_WINDOWS
path += L'\\';
#else
path += '/';
#endif
}
return *this;
}
mpt::PathString WithoutTrailingSlash() const
{
mpt::PathString result = *this;
while(result.HasTrailingSlash())
{
if(result.Length() == 1)
{
return result;
}
result = result.AsNative().substr(0, result.AsNative().length() - 1);
}
return result;
}
mpt::PathString WithTrailingSlash() const
{
mpt::PathString result = *this;
result.EnsureTrailingSlash();
return result;
}
// Relative / absolute paths conversion
mpt::PathString AbsolutePathToRelative(const mpt::PathString &relativeTo) const;
mpt::PathString RelativePathToAbsolute(const mpt::PathString &relativeTo) const;
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
public:
#if MPT_OS_WINDOWS
#if !(MPT_WSTRING_CONVERT)
#error "mpt::PathString on Windows depends on MPT_WSTRING_CONVERT)"
#endif
// conversions
#if defined(MPT_ENABLE_CHARSET_LOCALE)
MPT_DEPRECATED_PATH std::string ToLocale() const { return mpt::ToCharset(mpt::CharsetLocale, path); }
#endif
std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, path); }
std::wstring ToWide() const { return path; }
mpt::ustring ToUnicode() const { return mpt::ToUnicode(path); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
MPT_DEPRECATED_PATH static PathString FromLocale(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); }
static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetLocale, path)); }
#endif
static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWide(mpt::CharsetUTF8, path)); }
static PathString FromWide(const std::wstring &path) { return PathString(path); }
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWide(path)); }
RawPathString AsNative() const { return path; }
// Return native string, with possible \\?\ prefix if it exceeds MAX_PATH characters.
RawPathString AsNativePrefixed() const;
static PathString FromNative(const RawPathString &path) { return PathString(path); }
#if defined(_MFC_VER)
// CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE
MPT_DEPRECATED_PATH CString ToCString() const { return mpt::ToCString(path); }
MPT_DEPRECATED_PATH static PathString FromCString(const CString &path) { return PathString(mpt::ToWide(path)); }
// Non-warning-generating versions of the above. Use with extra care.
CString ToCStringSilent() const { return mpt::ToCString(path); }
static PathString FromCStringSilent(const CString &path) { return PathString(mpt::ToWide(path)); }
// really special purpose, if !UNICODE, encode unicode in CString as UTF8:
static mpt::PathString TunnelOutofCString(const CString &path);
static CString TunnelIntoCString(const mpt::PathString &path);
// CStringW
#ifdef UNICODE
MPT_DEPRECATED_PATH CString ToCStringW() const { return mpt::ToCString(path); }
MPT_DEPRECATED_PATH static PathString FromCStringW(const CString &path) { return PathString(mpt::ToWide(path)); }
#else
CStringW ToCStringW() const { return mpt::ToCStringW(path); }
static PathString FromCStringW(const CStringW &path) { return PathString(mpt::ToWide(path)); }
#endif
#endif
// Convert a path to its simplified form, i.e. remove ".\" and "..\" entries
mpt::PathString Simplify() const;
#else // !MPT_OS_WINDOWS
// conversions
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::string ToLocale() const { return path; }
std::string ToUTF8() const { return mpt::ToCharset(mpt::CharsetUTF8, mpt::CharsetLocale, path); }
#if MPT_WSTRING_CONVERT
std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetLocale, path); }
#endif
mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetLocale, path); }
static PathString FromLocale(const std::string &path) { return PathString(path); }
static PathString FromLocaleSilent(const std::string &path) { return PathString(path); }
static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, mpt::CharsetUTF8, path)); }
#if MPT_WSTRING_CONVERT
static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); }
#endif
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetLocale, path)); }
RawPathString AsNative() const { return path; }
RawPathString AsNativePrefixed() const { return path; }
static PathString FromNative(const RawPathString &path) { return PathString(path); }
#else // !MPT_ENABLE_CHARSET_LOCALE
std::string ToUTF8() const { return path; }
#if MPT_WSTRING_CONVERT
std::wstring ToWide() const { return mpt::ToWide(mpt::CharsetUTF8, path); }
#endif
mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::CharsetUTF8, path); }
static PathString FromUTF8(const std::string &path) { return path; }
#if MPT_WSTRING_CONVERT
static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); }
#endif
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::CharsetUTF8, path)); }
RawPathString AsNative() const { return path; }
RawPathString AsNativePrefixed() const { return path; }
static PathString FromNative(const RawPathString &path) { return PathString(path); }
#endif // MPT_ENABLE_CHARSET_LOCALE
// Convert a path to its simplified form (currently only implemented on Windows)
MPT_DEPRECATED mpt::PathString Simplify() const { return path; }
#endif // MPT_OS_WINDOWS
};
#if defined(MPT_ENABLE_CHARSET_LOCALE)
MPT_DEPRECATED_PATH static inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::CharsetLocale, x.ToUnicode()); }
#endif
static inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); }
#if MPT_WSTRING_FORMAT
static inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); }
#endif
} // namespace mpt
#if MPT_OS_WINDOWS
#define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x )
#else // !MPT_OS_WINDOWS
#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x )
#endif // MPT_OS_WINDOWS
namespace mpt
{
bool IsPathSeparator(mpt::RawPathString::value_type c);
bool PathIsAbsolute(const mpt::PathString &path);
#if MPT_OS_WINDOWS
// Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW)
mpt::PathString GetAbsolutePath(const mpt::PathString &path);
#ifdef MODPLUG_TRACKER
// Deletes a complete directory tree. Handle with EXTREME care.
// Returns false if any file could not be removed and aborts as soon as it
// encounters any error. path must be absolute.
bool DeleteWholeDirectoryTree(mpt::PathString path);
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
#if MPT_OS_WINDOWS
#if defined(MPT_ENABLE_DYNBIND) || defined(MPT_ENABLE_TEMPFILE)
// Returns the application path or an empty string (if unknown), e.g. "C:\mptrack\"
mpt::PathString GetAppPath();
#endif // MPT_ENABLE_DYNBIND || MPT_ENABLE_TEMPFILE
#if defined(MPT_ENABLE_DYNBIND)
#if !MPT_OS_WINDOWS_WINRT
// Returns the system directory path, e.g. "C:\Windows\System32\"
mpt::PathString GetSystemPath();
#endif // !MPT_OS_WINDOWS_WINRT
#endif // MPT_ENABLE_DYNBIND
#endif // MPT_OS_WINDOWS
#if defined(MPT_ENABLE_TEMPFILE)
#if MPT_OS_WINDOWS
// Returns temporary directory (with trailing backslash added) (e.g. "C:\TEMP\")
mpt::PathString GetTempDirectory();
// Returns a new unique absolute path.
mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix = mpt::PathString(), const mpt::PathString &fileNameExtension = MPT_PATHSTRING("tmp"));
// Scoped temporary file guard. Deletes the file when going out of scope.
// The file itself is not created automatically.
class TempFileGuard
{
private:
const mpt::PathString filename;
public:
TempFileGuard(const mpt::PathString &filename = CreateTempFileName());
mpt::PathString GetFilename() const;
~TempFileGuard();
};
#ifdef MODPLUG_TRACKER
// Scoped temporary directory guard. Deletes the directory when going out of scope.
// The directory itself is created automatically.
class TempDirGuard
{
private:
mpt::PathString dirname;
public:
TempDirGuard(const mpt::PathString &dirname_ = CreateTempFileName());
mpt::PathString GetDirname() const;
~TempDirGuard();
};
#endif // MODPLUG_TRACKER
#endif // MPT_OS_WINDOWS
#endif // MPT_ENABLE_TEMPFILE
} // namespace mpt
#if defined(MODPLUG_TRACKER)
// Sanitize a filename (remove special chars)
void SanitizeFilename(mpt::PathString &filename);
void SanitizeFilename(char *beg, char *end);
void SanitizeFilename(wchar_t *beg, wchar_t *end);
void SanitizeFilename(std::string &str);
void SanitizeFilename(std::wstring &str);
#if MPT_USTRING_MODE_UTF8
void SanitizeFilename(mpt::u8string &str);
#endif // MPT_USTRING_MODE_UTF8
template <std::size_t size>
void SanitizeFilename(char (&buffer)[size])
{
STATIC_ASSERT(size > 0);
SanitizeFilename(buffer, buffer + size);
}
template <std::size_t size>
void SanitizeFilename(wchar_t (&buffer)[size])
{
STATIC_ASSERT(size > 0);
SanitizeFilename(buffer, buffer + size);
}
#if defined(_MFC_VER)
void SanitizeFilename(CString &str);
#endif
#endif // MODPLUG_TRACKER
#if defined(MODPLUG_TRACKER)
enum FileTypeFormat
{
FileTypeFormatNone = 0 , // do not show extensions after description, i.e. "Foo Files"
FileTypeFormatShowExtensions = 1<<0, // show extensions after descripten, i.e. "Foo Files (*.foo,*.bar)"
};
MPT_DECLARE_ENUM(FileTypeFormat)
class FileType
{
private:
mpt::ustring m_ShortName; // "flac", "mod" (lowercase)
mpt::ustring m_Description; // "FastTracker 2 Module"
std::vector<std::string> m_MimeTypes; // "audio/ogg" (in ASCII)
std::vector<mpt::PathString> m_Extensions; // "mod", "xm" (lowercase)
std::vector<mpt::PathString> m_Prefixes; // "mod" for "mod.*"
public:
FileType() { }
FileType(const std::vector<FileType> &group)
{
for(const auto &type : group)
{
m_MimeTypes.insert(m_MimeTypes.end(), type.m_MimeTypes.begin(), type.m_MimeTypes.end());
m_Extensions.insert(m_Extensions.end(), type.m_Extensions.begin(), type.m_Extensions.end());
m_Prefixes.insert(m_Prefixes.end(), type.m_Prefixes.begin(), type.m_Prefixes.end());
}
}
static FileType Any()
{
return FileType().ShortName(MPT_USTRING("*")).Description(MPT_USTRING("All Files")).AddExtension(MPT_PATHSTRING("*"));
}
public:
FileType& ShortName(const mpt::ustring &shortName) { m_ShortName = shortName; return *this; }
FileType& Description(const mpt::ustring &description) { m_Description = description; return *this; }
FileType& MimeTypes(const std::vector<std::string> &mimeTypes) { m_MimeTypes = mimeTypes; return *this; }
FileType& Extensions(const std::vector<mpt::PathString> &extensions) { m_Extensions = extensions; return *this; }
FileType& Prefixes(const std::vector<mpt::PathString> &prefixes) { m_Prefixes = prefixes; return *this; }
FileType& AddMimeType(const std::string &mimeType) { m_MimeTypes.push_back(mimeType); return *this; }
FileType& AddExtension(const mpt::PathString &extension) { m_Extensions.push_back(extension); return *this; }
FileType& AddPrefix(const mpt::PathString &prefix) { m_Prefixes.push_back(prefix); return *this; }
public:
mpt::ustring GetShortName() const { return m_ShortName; }
mpt::ustring GetDescription() const { return m_Description; }
std::vector<std::string> GetMimeTypes() const { return m_MimeTypes; }
std::vector<mpt::PathString> GetExtensions() const { return m_Extensions; }
std::vector<mpt::PathString> GetPrefixes() const { return m_Prefixes; }
public:
mpt::PathString AsFilterString(FlagSet<FileTypeFormat> format = FileTypeFormatNone) const;
mpt::PathString AsFilterOnlyString() const;
}; // class FileType
// "Ogg Vorbis|*.ogg;*.oga|" // FileTypeFormatNone
// "Ogg Vorbis (*.ogg,*.oga)|*.ogg;*.oga|" // FileTypeFormatShowExtensions
mpt::PathString ToFilterString(const FileType &fileType, FlagSet<FileTypeFormat> format = FileTypeFormatNone);
mpt::PathString ToFilterString(const std::vector<FileType> &fileTypes, FlagSet<FileTypeFormat> format = FileTypeFormatNone);
// "*.ogg;*.oga" / ";*.ogg;*.oga"
mpt::PathString ToFilterOnlyString(const FileType &fileType, bool prependSemicolonWhenNotEmpty = false);
mpt::PathString ToFilterOnlyString(const std::vector<FileType> &fileTypes, bool prependSemicolonWhenNotEmpty = false);
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,318 @@
/*
* mptRandom.cpp
* -------------
* Purpose: PRNG
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptRandom.h"
#include "Endianness.h"
#include "mptCRC.h"
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdlib>
#if MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
template <typename T>
static T log2(T x)
{
return std::log(x) / std::log(static_cast<T>(2));
}
static MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x)
{
return detail::lower_bound_entropy_bits(x);
}
template <typename T>
static inline bool is_mask(T x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
typedef typename std::make_unsigned<T>::type unsigned_T;
unsigned_T ux = static_cast<unsigned_T>(x);
unsigned_T mask = 0;
for(std::size_t bits = 0; bits <= (sizeof(unsigned_T) * 8); ++bits)
{
mask = (mask << 1) | 1u;
if(ux == mask)
{
return true;
}
}
return false;
}
namespace {
template <typename T> struct default_hash { };
template <> struct default_hash<uint8> { typedef mpt::checksum::crc16 type; };
template <> struct default_hash<uint16> { typedef mpt::checksum::crc16 type; };
template <> struct default_hash<uint32> { typedef mpt::checksum::crc32c type; };
template <> struct default_hash<uint64> { typedef mpt::checksum::crc64_jones type; };
}
template <typename T>
static T generate_timeseed()
{
// Note: CRC is actually not that good a choice here, but it is simple and we
// already have an implementaion available. Better choices for mixing entropy
// would be a hash function with proper avalanche characteristics or a block
// or stream cipher with any pre-choosen random key and IV. The only aspect we
// really need here is whitening of the bits.
typename mpt::default_hash<T>::type hash;
#ifdef MPT_BUILD_FUZZER
return static_cast<T>(mpt::FUZZER_RNG_SEED);
#else // !MPT_BUILD_FUZZER
{
#if MPT_OS_WINDOWS
FILETIME t;
MemsetZero(t);
GetSystemTimeAsFileTime(&t);
#else // !MPT_OS_WINDOWS
std::time_t t = std::time(nullptr);
#endif // MPT_OS_WINDOWS
mpt::byte bytes[sizeof(t)];
std::memcpy(bytes, &t, sizeof(t));
MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little())
{
std::reverse(std::begin(bytes), std::end(bytes));
}
hash(std::begin(bytes), std::end(bytes));
}
{
std::clock_t c = std::clock();
mpt::byte bytes[sizeof(c)];
std::memcpy(bytes, &c, sizeof(c));
MPT_MAYBE_CONSTANT_IF(mpt::endian_is_little())
{
std::reverse(std::begin(bytes), std::end(bytes));
}
hash(std::begin(bytes), std::end(bytes));
}
return static_cast<T>(hash.result());
#endif // MPT_BUILD_FUZZER
}
#ifdef MODPLUG_TRACKER
namespace rng
{
void crand::reseed(uint32 seed)
{
std::srand(seed);
}
crand::result_type crand::operator()()
{
return std::rand();
}
} // namespace rng
#endif // MODPLUG_TRACKER
sane_random_device::sane_random_device()
: rd_reliable(rd.entropy() > 0.0)
{
if(!rd_reliable)
{
init_fallback();
}
}
sane_random_device::sane_random_device(const std::string & token_)
: token(token_)
, rd(token)
, rd_reliable(rd.entropy() > 0.0)
{
if(!rd_reliable)
{
init_fallback();
}
}
void sane_random_device::init_fallback()
{
if(!rd_fallback)
{
if(token.length() > 0)
{
uint64 seed_val = mpt::generate_timeseed<uint64>();
std::vector<unsigned int> seeds;
seeds.push_back(static_cast<uint32>(seed_val >> 32));
seeds.push_back(static_cast<uint32>(seed_val >> 0));
for(std::size_t i = 0; i < token.length(); ++i)
{
seeds.push_back(static_cast<unsigned int>(static_cast<unsigned char>(token[i])));
}
std::seed_seq seed(seeds.begin(), seeds.end());
rd_fallback = mpt::make_unique<std::mt19937>(seed);
} else
{
uint64 seed_val = mpt::generate_timeseed<uint64>();
unsigned int seeds[2];
seeds[0] = static_cast<uint32>(seed_val >> 32);
seeds[1] = static_cast<uint32>(seed_val >> 0);
std::seed_seq seed(seeds + 0, seeds + 2);
rd_fallback = mpt::make_unique<std::mt19937>(seed);
}
}
}
sane_random_device::result_type sane_random_device::operator()()
{
MPT_LOCK_GUARD<mpt::mutex> l(m);
result_type result = 0;
try
{
if(rd.min() != 0 || !mpt::is_mask(rd.max()))
{ // insane std::random_device
// This implementation is not exactly uniformly distributed but good enough
// for OpenMPT.
double rd_min = static_cast<double>(rd.min());
double rd_max = static_cast<double>(rd.max());
double rd_range = rd_max - rd_min;
double rd_size = rd_range + 1.0;
double rd_entropy = mpt::log2(rd_size);
int iterations = static_cast<int>(std::ceil(result_bits() / rd_entropy));
double tmp = 0.0;
for(int i = 0; i < iterations; ++i)
{
tmp = (tmp * rd_size) + (static_cast<double>(rd()) - rd_min);
}
double result_01 = std::floor(tmp / std::pow(rd_size, iterations));
result = static_cast<result_type>(std::floor(result_01 * (static_cast<double>(max() - min()) + 1.0))) + min();
} else
{ // sane std::random_device
result = 0;
std::size_t rd_bits = mpt::lower_bound_entropy_bits(rd.max());
for(std::size_t entropy = 0; entropy < (sizeof(result_type) * 8); entropy += rd_bits)
{
if(rd_bits < (sizeof(result_type) * 8))
{
result = (result << rd_bits) | static_cast<result_type>(rd());
} else
{
result = result | static_cast<result_type>(rd());
}
}
}
} catch(const std::exception &)
{
rd_reliable = false;
init_fallback();
}
if(!rd_reliable)
{ // std::random_device is unreliable
// XOR the generated random number with more entropy from the time-seeded
// PRNG.
// Note: This is safe even if the std::random_device itself is implemented
// as a std::mt19937 PRNG because we are very likely using a different
// seed.
result ^= mpt::random<result_type>(*rd_fallback);
}
return result;
}
prng_random_device_seeder::prng_random_device_seeder()
{
return;
}
uint8 prng_random_device_seeder::generate_seed8()
{
return mpt::generate_timeseed<uint8>();
}
uint16 prng_random_device_seeder::generate_seed16()
{
return mpt::generate_timeseed<uint16>();
}
uint32 prng_random_device_seeder::generate_seed32()
{
return mpt::generate_timeseed<uint32>();
}
uint64 prng_random_device_seeder::generate_seed64()
{
return mpt::generate_timeseed<uint64>();
}
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
static mpt::random_device *g_rd = nullptr;
static mpt::thread_safe_prng<mpt::best_prng> *g_best_prng = nullptr;
void set_global_random_device(mpt::random_device *rd)
{
g_rd = rd;
}
void set_global_prng(mpt::thread_safe_prng<mpt::best_prng> *prng)
{
g_best_prng = prng;
}
mpt::random_device & global_random_device()
{
return *g_rd;
}
mpt::thread_safe_prng<mpt::best_prng> & global_prng()
{
return *g_best_prng;
}
#else
mpt::random_device & global_random_device()
{
static mpt::random_device g_rd;
return g_rd;
}
mpt::thread_safe_prng<mpt::best_prng> & global_prng()
{
static mpt::thread_safe_prng<mpt::best_prng> g_best_prng(global_random_device());
return g_best_prng;
}
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,643 @@
/*
* mptRandom.h
* -----------
* Purpose: PRNG
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "mptMutex.h"
#include <limits>
#include <random>
#ifdef MODPLUG_TRACKER
#include <cstdlib>
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_BEGIN
// NOTE:
// We implement our own PRNG and distribution functions as the implementations
// of std::uniform_int_distribution is either wrong (not uniform in MSVC2010) or
// not guaranteed to be livelock-free for bad PRNGs (in GCC, Clang, boost).
// We resort to a simpler implementation with only power-of-2 result ranges for
// both the underlying PRNG and our interface function. This saves us from
// complicated code having to deal with partial bits of entropy.
// Our interface still somewhat follows the mindset of C++11 <random> (with the
// addition of a simple wrapper function mpt::random which saves the caller from
// instantiating distribution objects for the common uniform distribution case.
// We are still using std::random_device for initial seeding when avalable and
// after working around its set of problems.
namespace mpt
{
#ifdef MPT_BUILD_FUZZER
static const uint32 FUZZER_RNG_SEED = 3141592653u; // pi
#endif // MPT_BUILD_FUZZER
namespace detail
{
MPT_CONSTEXPR11_FUN int lower_bound_entropy_bits(unsigned int x)
{
// easy to compile-time evaluate even for stupid compilers
return
x >= 0xffffffffu ? 32 :
x >= 0x7fffffffu ? 31 :
x >= 0x3fffffffu ? 30 :
x >= 0x1fffffffu ? 29 :
x >= 0x0fffffffu ? 28 :
x >= 0x07ffffffu ? 27 :
x >= 0x03ffffffu ? 26 :
x >= 0x01ffffffu ? 25 :
x >= 0x00ffffffu ? 24 :
x >= 0x007fffffu ? 23 :
x >= 0x003fffffu ? 22 :
x >= 0x001fffffu ? 21 :
x >= 0x000fffffu ? 20 :
x >= 0x0007ffffu ? 19 :
x >= 0x0003ffffu ? 18 :
x >= 0x0001ffffu ? 17 :
x >= 0x0000ffffu ? 16 :
x >= 0x00007fffu ? 15 :
x >= 0x00003fffu ? 14 :
x >= 0x00001fffu ? 13 :
x >= 0x00000fffu ? 12 :
x >= 0x000007ffu ? 11 :
x >= 0x000003ffu ? 10 :
x >= 0x000001ffu ? 9 :
x >= 0x000000ffu ? 8 :
x >= 0x0000007fu ? 7 :
x >= 0x0000003fu ? 6 :
x >= 0x0000001fu ? 5 :
x >= 0x0000000fu ? 4 :
x >= 0x00000007u ? 3 :
x >= 0x00000003u ? 2 :
x >= 0x00000001u ? 1 :
0;
}
}
template <typename Trng> struct engine_traits
{
typedef typename Trng::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits()
{
return Trng::result_bits();
}
template<typename Trd>
static inline Trng make(Trd & rd)
{
return Trng(rd);
}
};
template <typename T, typename Trng>
inline T random(Trng & rng)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
typedef typename std::make_unsigned<T>::type unsigned_T;
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
unsigned_T result = 0;
for(std::size_t entropy = 0; entropy < (sizeof(T) * 8); entropy += rng_bits)
{
MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8))
{
MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
} else
{
result = static_cast<unsigned_T>(rng());
}
}
return static_cast<T>(result);
}
template <typename T, std::size_t required_entropy_bits, typename Trng>
inline T random(Trng & rng)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
typedef typename std::make_unsigned<T>::type unsigned_T;
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
unsigned_T result = 0;
for(std::size_t entropy = 0; entropy < std::min<std::size_t>(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
{
MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8))
{
MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
} else
{
result = static_cast<unsigned_T>(rng());
}
}
MPT_CONSTANT_IF(required_entropy_bits >= (sizeof(T) * 8))
{
return static_cast<T>(result);
} else
{
return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
}
}
template <typename T, typename Trng>
inline T random(Trng & rng, std::size_t required_entropy_bits)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
typedef typename std::make_unsigned<T>::type unsigned_T;
const unsigned int rng_bits = mpt::engine_traits<Trng>::result_bits();
unsigned_T result = 0;
for(std::size_t entropy = 0; entropy < std::min<std::size_t>(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
{
MPT_CONSTANT_IF(rng_bits < (sizeof(T) * 8))
{
MPT_CONSTEXPR11_VAR unsigned int shift_bits = rng_bits % (sizeof(T) * 8); // silence utterly stupid MSVC and GCC warnings about shifting by too big amount (in which case this branch is not even taken however)
result = (result << shift_bits) ^ static_cast<unsigned_T>(rng());
} else
{
result = static_cast<unsigned_T>(rng());
}
}
if(required_entropy_bits >= (sizeof(T) * 8))
{
return static_cast<T>(result);
} else
{
return static_cast<T>(result & ((static_cast<unsigned_T>(1) << required_entropy_bits) - static_cast<unsigned_T>(1)));
}
}
template <typename Tf> struct float_traits { };
template <> struct float_traits<float> {
typedef uint32 mantissa_uint_type;
static const int mantissa_bits = 24;
};
template <> struct float_traits<double> {
typedef uint64 mantissa_uint_type;
static const int mantissa_bits = 53;
};
template <> struct float_traits<long double> {
typedef uint64 mantissa_uint_type;
static const int mantissa_bits = 63;
};
template <typename T>
struct uniform_real_distribution
{
private:
T a;
T b;
public:
inline uniform_real_distribution(T a, T b)
: a(a)
, b(b)
{
return;
}
template <typename Trng>
inline T operator()(Trng & rng) const
{
typedef typename float_traits<T>::mantissa_uint_type uint_type;
const int bits = float_traits<T>::mantissa_bits;
return ((b - a) * static_cast<T>(mpt::random<uint_type, bits>(rng)) / static_cast<T>((static_cast<uint_type>(1u) << bits))) + a;
}
};
template <typename T, typename Trng>
inline T random(Trng & rng, T min, T max)
{
STATIC_ASSERT(!std::numeric_limits<T>::is_integer);
typedef mpt::uniform_real_distribution<T> dis_type;
dis_type dis(min, max);
return static_cast<T>(dis(rng));
}
namespace rng
{
#if MPT_COMPILER_MSVC
#pragma warning(push)
#pragma warning(disable:4724) // potential mod by 0
#endif // MPT_COMPILER_MSVC
template <typename Tstate, typename Tvalue, Tstate m, Tstate a, Tstate c, Tstate result_mask, int result_shift, int result_bits_>
class lcg
{
public:
typedef Tstate state_type;
typedef Tvalue result_type;
private:
state_type state;
public:
template <typename Trng>
explicit inline lcg(Trng & rd)
: state(mpt::random<state_type>(rd))
{
operator()(); // we return results from the current state and update state after returning. results in better pipelining.
}
explicit inline lcg(state_type seed)
: state(seed)
{
operator()(); // we return results from the current state and update state after returning. results in better pipelining.
}
public:
static MPT_CONSTEXPR11_FUN result_type min()
{
return static_cast<result_type>(0);
}
static MPT_CONSTEXPR11_FUN result_type max()
{
STATIC_ASSERT(((result_mask >> result_shift) << result_shift) == result_mask);
return static_cast<result_type>(result_mask >> result_shift);
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
STATIC_ASSERT(((static_cast<Tstate>(1) << result_bits_) - 1) == (result_mask >> result_shift));
return result_bits_;
}
inline result_type operator()()
{
// we return results from the current state and update state after returning. results in better pipelining.
state_type s = state;
result_type result = static_cast<result_type>((s & result_mask) >> result_shift);
s = Util::ModIfNotZero<state_type, m>((a * s) + c);
state = s;
return result;
}
};
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
typedef lcg<uint32, uint16, 0u, 214013u, 2531011u, 0x7fff0000u, 16, 15> lcg_msvc;
typedef lcg<uint32, uint16, 0x80000000u, 1103515245u, 12345u, 0x7fff0000u, 16, 15> lcg_c99;
typedef lcg<uint64, uint32, 0ull, 6364136223846793005ull, 1ull, 0xffffffff00000000ull, 32, 32> lcg_musl;
} // namespace rng
#ifdef MODPLUG_TRACKER
namespace rng
{
class crand
{
public:
typedef void state_type;
typedef int result_type;
private:
static void reseed(uint32 seed);
public:
template <typename Trd>
static void reseed(Trd & rd)
{
reseed(mpt::random<uint32>(rd));
}
public:
crand() { }
explicit crand(const std::string &) { }
public:
static MPT_CONSTEXPR11_FUN result_type min()
{
return 0;
}
static MPT_CONSTEXPR11_FUN result_type max()
{
return RAND_MAX;
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
return detail::lower_bound_entropy_bits(RAND_MAX);
}
result_type operator()();
};
} // namespace rng
#endif // MODPLUG_TRACKER
// C++11 std::random_device may be implemented as a deterministic PRNG.
// There is no way to seed this PRNG and it is allowed to be seeded with the
// same value on each program invocation. This makes std::random_device
// completely useless even as a non-cryptographic entropy pool.
// We fallback to time-seeded std::mt19937 if std::random_device::entropy() is
// 0 or less.
class sane_random_device
{
private:
mpt::mutex m;
std::string token;
std::random_device rd;
bool rd_reliable;
std::unique_ptr<std::mt19937> rd_fallback;
public:
typedef unsigned int result_type;
private:
void init_fallback();
public:
sane_random_device();
sane_random_device(const std::string & token);
static MPT_CONSTEXPR11_FUN result_type min()
{
return std::numeric_limits<result_type>::min();
}
static MPT_CONSTEXPR11_FUN result_type max()
{
return std::numeric_limits<result_type>::max();
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
return sizeof(result_type) * 8;
}
result_type operator()();
};
template <std::size_t N>
class seed_seq_values
{
private:
unsigned int seeds[N];
public:
template <typename Trd>
explicit seed_seq_values(Trd & rd)
{
for(std::size_t i = 0; i < N; ++i)
{
seeds[i] = rd();
}
}
const unsigned int * begin() const
{
return seeds + 0;
}
const unsigned int * end() const
{
return seeds + N;
}
};
// C++11 random does not provide any sane way to determine the amount of entropy
// required to seed a particular engine. VERY STUPID.
// List the ones we are likely to use.
template <> struct engine_traits<std::mt19937> {
static const std::size_t seed_bits = sizeof(std::mt19937::result_type) * 8 * std::mt19937::state_size;
typedef std::mt19937 rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::mt19937_64> {
static const std::size_t seed_bits = sizeof(std::mt19937_64::result_type) * 8 * std::mt19937_64::state_size;
typedef std::mt19937_64 rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::ranlux24_base> {
static const std::size_t seed_bits = std::ranlux24_base::word_size;
typedef std::ranlux24_base rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::ranlux48_base> {
static const std::size_t seed_bits = std::ranlux48_base::word_size;
typedef std::ranlux48_base rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return rng_type::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::ranlux24> {
static const std::size_t seed_bits = std::ranlux24_base::word_size;
typedef std::ranlux24 rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux24_base::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::ranlux48> {
static const std::size_t seed_bits = std::ranlux48_base::word_size;
typedef std::ranlux48 rng_type;
typedef rng_type::result_type result_type;
static MPT_CONSTEXPR11_FUN int result_bits() { return std::ranlux48_base::word_size; }
template<typename Trd> static inline rng_type make(Trd & rd)
{
mpt::seed_seq_values<seed_bits / sizeof(unsigned int)> values(rd);
std::seed_seq seed(values.begin(), values.end());
return rng_type(seed);
}
};
class prng_random_device_seeder
{
private:
uint8 generate_seed8();
uint16 generate_seed16();
uint32 generate_seed32();
uint64 generate_seed64();
protected:
template <typename T> inline T generate_seed();
protected:
prng_random_device_seeder();
};
template <> inline uint8 prng_random_device_seeder::generate_seed() { return generate_seed8(); }
template <> inline uint16 prng_random_device_seeder::generate_seed() { return generate_seed16(); }
template <> inline uint32 prng_random_device_seeder::generate_seed() { return generate_seed32(); }
template <> inline uint64 prng_random_device_seeder::generate_seed() { return generate_seed64(); }
template <typename Trng = mpt::rng::lcg_musl>
class prng_random_device
: public prng_random_device_seeder
{
public:
typedef unsigned int result_type;
private:
mpt::mutex m;
Trng rng;
public:
prng_random_device()
: rng(generate_seed<typename Trng::state_type>())
{
return;
}
prng_random_device(const std::string &)
: rng(generate_seed<typename Trng::state_type>())
{
return;
}
static MPT_CONSTEXPR11_FUN result_type min()
{
return std::numeric_limits<unsigned int>::min();
}
static MPT_CONSTEXPR11_FUN result_type max()
{
return std::numeric_limits<unsigned int>::max();
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
return sizeof(unsigned int) * 8;
}
result_type operator()()
{
MPT_LOCK_GUARD<mpt::mutex> l(m);
return mpt::random<unsigned int>(rng);
}
};
#ifdef MPT_BUILD_FUZZER
// 1. Use deterministic seeding
typedef mpt::prng_random_device<mpt::rng::lcg_musl> random_device;
// 2. Use fast PRNGs in order to not waste time fuzzing more complex PRNG
// implementations.
typedef mpt::rng::lcg_msvc fast_prng;
typedef mpt::rng::lcg_c99 main_prng;
typedef mpt::rng::lcg_musl best_prng;
#else // !MPT_BUILD_FUZZER
// mpt::random_device always generates 32 bits of entropy
typedef mpt::sane_random_device random_device;
// We cannot use std::minstd_rand here because it has not a power-of-2 sized
// output domain which we rely upon.
typedef mpt::rng::lcg_msvc fast_prng; // about 3 ALU operations, ~32bit of state, suited for inner loops
typedef std::mt19937 main_prng;
#if MPT_MSVC_AT_LEAST(2017,5) && defined(_MSC_FULL_VER)
#if (_MSC_FULL_VER < 191225831)
// work-around compiler crash
// c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.12.25827\include\random(978): fatal error C1001: An internal error has occurred in the compiler.
// (compiler file 'f:\dd\vctools\compiler\utc\src\p2\main.c', line 258)
// reported at: https://developercommunity.visualstudio.com/content/problem/162089/random-engines-crashing-vs-155-optimizer.html?childToView=164098#comment-164098
typedef std::mt19937_64 best_prng;
#else
typedef std::ranlux48 best_prng;
#endif
#else
typedef std::ranlux48 best_prng;
#endif
#endif // MPT_BUILD_FUZZER
typedef mpt::main_prng default_prng;
typedef mpt::main_prng prng;
template <typename Trng, typename Trd>
inline Trng make_prng(Trd & rd)
{
return mpt::engine_traits<Trng>::make(rd);
}
template <typename Trng>
class thread_safe_prng
: private Trng
{
private:
mpt::mutex m;
public:
typedef typename Trng::result_type result_type;
public:
template <typename Trd>
explicit thread_safe_prng(Trd & rd)
: Trng(mpt::make_prng<Trng>(rd))
{
return;
}
thread_safe_prng(Trng rng)
: Trng(rng)
{
return;
}
public:
static MPT_CONSTEXPR11_FUN typename engine_traits<Trng>::result_type min()
{
return Trng::min();
}
static MPT_CONSTEXPR11_FUN typename engine_traits<Trng>::result_type max()
{
return Trng::max();
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
return engine_traits<Trng>::result_bits();
}
public:
typename engine_traits<Trng>::result_type operator()()
{
MPT_LOCK_GUARD<mpt::mutex> l(m);
return Trng::operator()();
}
};
mpt::random_device & global_random_device();
mpt::thread_safe_prng<mpt::best_prng> & global_prng();
#if defined(MODPLUG_TRACKER) && !defined(MPT_BUILD_WINESUPPORT)
void set_global_random_device(mpt::random_device *rd);
void set_global_prng(mpt::thread_safe_prng<mpt::best_prng> *rng);
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
} // namespace mpt
OPENMPT_NAMESPACE_END

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,668 @@
/*
* mptString.h
* ----------
* Purpose: Small string-related utilities, number and message formatting.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "mptTypeTraits.h"
#include <algorithm>
#include <limits>
#include <string>
#include <cstring>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace String
{
template <typename Tstring> struct Traits {
static const char * GetDefaultWhitespace() { return " \n\r\t"; }
};
template <> struct Traits<std::string> {
static const char * GetDefaultWhitespace() { return " \n\r\t"; }
};
template <> struct Traits<std::wstring> {
static const wchar_t * GetDefaultWhitespace() { return L" \n\r\t"; }
};
// Remove whitespace at start of string
template <typename Tstring>
inline Tstring LTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits<Tstring>::GetDefaultWhitespace()))
{
typename Tstring::size_type pos = str.find_first_not_of(whitespace);
if(pos != Tstring::npos)
{
str.erase(str.begin(), str.begin() + pos);
} else if(pos == Tstring::npos && str.length() > 0 && str.find_last_of(whitespace) == str.length() - 1)
{
return Tstring();
}
return str;
}
// Remove whitespace at end of string
template <typename Tstring>
inline Tstring RTrim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits<Tstring>::GetDefaultWhitespace()))
{
typename Tstring::size_type pos = str.find_last_not_of(whitespace);
if(pos != Tstring::npos)
{
str.erase(str.begin() + pos + 1, str.end());
} else if(pos == Tstring::npos && str.length() > 0 && str.find_first_of(whitespace) == 0)
{
return Tstring();
}
return str;
}
// Remove whitespace at start and end of string
template <typename Tstring>
inline Tstring Trim(Tstring str, const Tstring &whitespace = Tstring(mpt::String::Traits<Tstring>::GetDefaultWhitespace()))
{
return RTrim(LTrim(str, whitespace), whitespace);
}
template <typename Tstring, typename Tstring2, typename Tstring3>
inline Tstring Replace(Tstring str, const Tstring2 &oldStr_, const Tstring3 &newStr_)
{
std::size_t pos = 0;
const Tstring oldStr = oldStr_;
const Tstring newStr = newStr_;
while((pos = str.find(oldStr, pos)) != Tstring::npos)
{
str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
return str;
}
} // namespace String
static inline std::size_t strnlen(const char *str, std::size_t n)
{
#if MPT_COMPILER_MSVC
return ::strnlen(str, n);
#else
if(n >= std::numeric_limits<std::size_t>::max())
{
return std::strlen(str);
}
for(std::size_t i = 0; i < n; ++i)
{
if(str[i] == '\0')
{
return i;
}
}
return n;
#endif
}
enum Charset {
#if defined(MPT_ENABLE_CHARSET_LOCALE)
CharsetLocale, // CP_ACP on windows, current C locale otherwise
#endif
CharsetUTF8,
CharsetASCII, // strictly 7-bit ASCII
CharsetISO8859_1,
CharsetISO8859_15,
CharsetCP437,
CharsetCP437AMS,
CharsetCP437AMS2,
CharsetWindows1252,
};
// Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
const Charset CharsetLocaleOrUTF8 = CharsetLocale;
#else
const Charset CharsetLocaleOrUTF8 = CharsetUTF8;
#endif
// Checks if the std::string represents an UTF8 string.
// This is currently implemented as converting to std::wstring and back assuming UTF8 both ways,
// and comparing the result to the original string.
// Caveats:
// - can give false negatives because of possible unicode normalization during conversion
// - can give false positives if the 8bit encoding contains high-ascii only in valid utf8 groups
// - slow because of double conversion
bool IsUTF8(const std::string &str);
#define MPT_CHAR_TYPE char
#define MPT_CHAR(x) x
#define MPT_LITERAL(x) x
#define MPT_STRING(x) std::string( x )
#define MPT_WCHAR_TYPE wchar_t
#define MPT_WCHAR(x) L ## x
#define MPT_WLITERAL(x) L ## x
#define MPT_WSTRING(x) std::wstring( L ## x )
#if MPT_ENABLE_U8STRING
template <mpt::Charset charset_tag>
struct charset_char_traits : std::char_traits<char> {
static mpt::Charset charset() { return charset_tag; }
};
#define MPT_ENCODED_STRING_TYPE(charset) std::basic_string< char, mpt::charset_char_traits< charset > >
typedef MPT_ENCODED_STRING_TYPE(mpt::CharsetUTF8) u8string;
#define MPT_U8CHAR_TYPE char
#define MPT_U8CHAR(x) x
#define MPT_U8LITERAL(x) x
#define MPT_U8STRING(x) mpt::u8string( x )
// mpt::u8string is a moderately type-safe string that is meant to contain
// UTF-8 encoded char bytes.
//
// mpt::u8string is not implicitely convertible to/from std::string, but
// it is convertible to/from C strings the same way as std::string is.
//
// The implementation of mpt::u8string is a compromise of compatibilty
// with implementation-defined STL details, efficiency, source code size,
// executable bloat, type-safety and simplicity.
//
// mpt::u8string is not meant to be used directly though.
// mpt::u8string is meant as an alternative implementaion to std::wstring
// for implementing the unicode string type mpt::ustring.
#endif // MPT_ENABLE_U8STRING
#if MPT_WSTRING_CONVERT
// Convert to a wide character string.
// The wide encoding is UTF-16 or UTF-32, based on sizeof(wchar_t).
// If str does not contain any invalid characters, this conversion is lossless.
// Invalid source bytes will be replaced by some replacement character or string.
static inline std::wstring ToWide(const std::wstring &str) { return str; }
static inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
std::wstring ToWide(Charset from, const std::string &str);
static inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); }
#endif
// Convert to a string encoded in the 'to'-specified character set.
// If str does not contain any invalid characters,
// this conversion will be lossless iff, and only iff,
// 'to' is UTF8.
// Invalid source bytes or characters that are not representable in the
// destination charset will be replaced by some replacement character or string.
#if MPT_WSTRING_CONVERT
std::string ToCharset(Charset to, const std::wstring &str);
static inline std::string ToCharset(Charset to, const wchar_t * str) { return ToCharset(to, str ? std::wstring(str) : std::wstring()); }
#endif
std::string ToCharset(Charset to, Charset from, const std::string &str);
static inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); }
#if defined(_MFC_VER)
#if !(MPT_WSTRING_CONVERT)
#error "MFC depends on MPT_WSTRING_CONVERT)"
#endif
// Convert to a MFC CString. The CString encoding depends on UNICODE.
// This should also be used when converting to TCHAR strings.
// If UNICODE is defined, this is a completely lossless operation.
static inline CString ToCString(const CString &str) { return str; }
CString ToCString(const std::wstring &str);
static inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); }
CString ToCString(Charset from, const std::string &str);
static inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); }
// Convert from a MFC CString. The CString encoding depends on UNICODE.
// This should also be used when converting from TCHAR strings.
// If UNICODE is defined, this is a completely lossless operation.
std::wstring ToWide(const CString &str);
std::string ToCharset(Charset to, const CString &str);
#ifdef UNICODE
MPT_DEPRECATED static inline CString ToCStringW(const CString &str) { return ToCString(str); }
MPT_DEPRECATED static inline CString ToCStringW(const std::wstring &str) { return ToCString(str); }
MPT_DEPRECATED static inline CString ToCStringW(Charset from, const std::string &str) { return ToCString(from, str); }
MPT_DEPRECATED static inline CString ToCStringW(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); }
#else // !UNICODE
CStringW ToCStringW(const CString &str);
CStringW ToCStringW(const std::wstring &str);
CStringW ToCStringW(Charset from, const std::string &str);
static inline CStringW ToCStringW(Charset from, const char * str) { return ToCStringW(from, str ? std::string(str) : std::string()); }
CStringW ToCStringW(const CStringW &str);
std::wstring ToWide(const CStringW &str);
std::string ToCharset(Charset to, const CStringW &str);
CString ToCString(const CStringW &str);
#endif // UNICODE
#endif // MFC
// mpt::ustring
//
// mpt::ustring is a string type that can hold unicode strings.
// It is implemented as a std::basic_string either based on wchar_t (i.e. the
// same as std::wstring) or a custom-defined char_traits class that is derived
// from std::char_traits<char>.
// The selection of the underlying implementation is done at compile-time.
// MPT_UCHAR, MPT_ULITERAL and MPT_USTRING are macros that ease construction
// of ustring char literals, ustring char array literals and ustring objects
// from ustring char literals that work consistently in both modes.
// Note that these are not supported for non-ASCII characters appearing in
// the macro argument.
// Also note that, as both UTF8 and UTF16 (it is less of an issue for UTF32)
// are variable-length encodings and mpt::ustring is implemented as a
// std::basic_string, all member functions that require individual character
// access will not work consistently or even at all in a meaningful way.
// This in particular affects operator[], at(), find() and substr().
// The code makes no effort in preventing these or generating warnings when
// these are used on mpt::ustring objects. However, compiling in the
// respectively other mpt::ustring mode will catch most of these anyway.
#if MPT_USTRING_MODE_WIDE
#if MPT_USTRING_MODE_UTF8
#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive."
#endif
typedef std::wstring ustring;
#define MPT_UCHAR_TYPE wchar_t
#define MPT_UCHAR(x) L ## x
#define MPT_ULITERAL(x) L ## x
#define MPT_USTRING(x) std::wstring( L ## x )
#endif // MPT_USTRING_MODE_WIDE
#if MPT_USTRING_MODE_UTF8
#if MPT_USTRING_MODE_WIDE
#error "MPT_USTRING_MODE_WIDE and MPT_USTRING_MODE_UTF8 are mutually exclusive."
#endif
typedef mpt::u8string ustring;
#define MPT_UCHAR_TYPE char
#define MPT_UCHAR(x) x
#define MPT_ULITERAL(x) x
#define MPT_USTRING(x) mpt::ustring( x )
#endif // MPT_USTRING_MODE_UTF8
#if MPT_USTRING_MODE_WIDE
#if !(MPT_WSTRING_CONVERT)
#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)"
#endif
static inline mpt::ustring ToUnicode(const std::wstring &str) { return str; }
static inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
static inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); }
static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(_MFC_VER)
static inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); }
#ifndef UNICODE
static inline mpt::ustring ToUnicode(const CStringW &str) { return ToWide(str); }
#endif // !UNICODE
#endif // MFC
#else // !MPT_USTRING_MODE_WIDE
static inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; }
#if MPT_WSTRING_CONVERT
mpt::ustring ToUnicode(const std::wstring &str);
static inline mpt::ustring ToUnicode(const wchar_t * str) { return ToUnicode(str ? std::wstring(str) : std::wstring()); }
#endif
mpt::ustring ToUnicode(Charset from, const std::string &str);
static inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(_MFC_VER)
mpt::ustring ToUnicode(const CString &str);
#ifndef UNICODE
mpt::ustring ToUnicode(const CStringW &str);
#endif // !UNICODE
#endif // MFC
#endif // MPT_USTRING_MODE_WIDE
#if MPT_USTRING_MODE_WIDE
#if !(MPT_WSTRING_CONVERT)
#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)"
#endif
// nothing, std::wstring overloads will catch all stuff
#else // !MPT_USTRING_MODE_WIDE
#if MPT_WSTRING_CONVERT
std::wstring ToWide(const mpt::ustring &str);
#endif
std::string ToCharset(Charset to, const mpt::ustring &str);
#if defined(_MFC_VER)
CString ToCString(const mpt::ustring &str);
#ifdef UNICODE
MPT_DEPRECATED static inline CString ToCStringW(const mpt::ustring &str) { return ToCString(str); }
#else // !UNICODE
static inline CStringW ToCStringW(const mpt::ustring &str) { return ToCStringW(ToWide(str)); }
#endif // UNICODE
#endif // MFC
#endif // MPT_USTRING_MODE_WIDE
// The MPT_UTF8 allows specifying UTF8 char arrays.
// The resulting type is mpt::ustring and the construction might require runtime translation,
// i.e. it is NOT generally available at compile time.
// Use explicit UTF8 encoding,
// i.e. U+00FC (LATIN SMALL LETTER U WITH DIAERESIS) would be written as "\xC3\xBC".
#define MPT_UTF8(x) mpt::ToUnicode(mpt::CharsetUTF8, x )
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
namespace String { namespace detail
{
template <mpt::Charset charset, std::size_t size>
inline mpt::ustring StringFromBuffer(const char (&buf)[size])
{
STATIC_ASSERT(size > 0);
std::size_t len = std::find(buf, buf + size, '\0') - buf; // terminate at \0
return mpt::ToUnicode(charset, std::string(buf, buf + len));
}
template <mpt::Charset charset, std::size_t size>
inline mpt::ustring StringFromBuffer(const wchar_t (&buf)[size])
{
STATIC_ASSERT(size > 0);
std::size_t len = std::find(buf, buf + size, L'\0') - buf; // terminate at \0
return mpt::ToUnicode(std::wstring(buf, buf + len));
}
template <mpt::Charset charset, std::size_t size>
inline bool StringToBuffer(char (&buf)[size], const mpt::ustring &str)
{
STATIC_ASSERT(size > 0);
MemsetZero(buf);
std::string encoded = mpt::ToCharset(charset, str);
std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf);
buf[size - 1] = '\0';
return (encoded.length() <= size - 1);
}
template <mpt::Charset charset, std::size_t size>
inline bool StringToBuffer(wchar_t (&buf)[size], const mpt::ustring &str)
{
STATIC_ASSERT(size > 0);
MemsetZero(buf);
std::wstring encoded = mpt::ToWide(str);
std::copy(encoded.data(), encoded.data() + std::min(encoded.length(), size - 1), buf);
buf[size - 1] = L'\0';
return (encoded.length() <= size - 1);
}
} } // namespace String::detail
// mpt::FromTcharBuf
// A lot of expecially older WinAPI functions return strings by filling in
// fixed-width TCHAR arrays inside some struct.
// mpt::FromTcharBuf converts these string to mpt::ustring regardless of whether
// in ANSI or UNICODE build and properly handles potentially missing NULL
// termination.
template <typename Tchar, std::size_t size>
inline mpt::ustring FromTcharBuf(const Tchar (&buf)[size])
{
return mpt::String::detail::StringFromBuffer<mpt::CharsetLocale>(buf);
}
// mpt::FromTcharStr
// Converts TCHAR strings to mpt::ustring in both ANSI and UNICODE builds.
// Useful when going through CString is not appropriate.
template <typename Tchar> mpt::ustring FromTcharStr(const Tchar *str);
template <> inline mpt::ustring FromTcharStr<char>(const char *str)
{
if(!str)
{
return mpt::ustring();
}
return mpt::ToUnicode(mpt::CharsetLocale, std::string(str));
}
template <> inline mpt::ustring FromTcharStr<wchar_t>(const wchar_t *str)
{
if(!str)
{
return mpt::ustring();
}
return mpt::ToUnicode(std::wstring(str));
}
// mpt::ToTcharBuf
// The inverse of mpt::FromTcharBuf.
// Always NULL-terminates the buffer.
// Return false if the string has been truncated to fit.
template <typename Tchar, std::size_t size>
inline bool ToTcharBuf(Tchar (&buf)[size], const mpt::ustring &str)
{
return mpt::String::detail::StringToBuffer<mpt::CharsetLocale>(buf, str);
}
// mpt::ToTcharStr
// Converts mpt::ustring to std::basic_stringy<TCHAR>,
// which is usable in both ANSI and UNICODE builds.
// Useful when going through CString is not appropriate.
template <typename Tchar> std::basic_string<Tchar> ToTcharStrImpl(const mpt::ustring &str);
template <> inline std::string ToTcharStrImpl<char>(const mpt::ustring &str)
{
return mpt::ToCharset(mpt::CharsetLocale, str);
}
template <> inline std::wstring ToTcharStrImpl<wchar_t>(const mpt::ustring &str)
{
return mpt::ToWide(str);
}
inline std::basic_string<TCHAR> ToTcharStr(const mpt::ustring &str)
{
return ToTcharStrImpl<TCHAR>(str);
}
#if defined(_MFC_VER)
template <std::size_t size>
inline CString CStringFromBuffer(const TCHAR (&buf)[size])
{
MPT_STATIC_ASSERT(size > 0);
std::size_t len = std::find(buf, buf + size, _T('\0')) - buf; // terminate at \0
return CString(buf, len);
}
template <std::size_t size>
inline void CopyCStringToBuffer(TCHAR (&buf)[size], const CString &str)
{
MPT_STATIC_ASSERT(size > 0);
MemsetZero(buf);
std::copy(str.GetString(), str.GetString() + std::min(static_cast<std::size_t>(str.GetLength()), size - 1), buf);
buf[size - 1] = _T('\0');
}
#endif // _MFC_VER
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
char ToLowerCaseAscii(char c);
char ToUpperCaseAscii(char c);
std::string ToLowerCaseAscii(std::string s);
std::string ToUpperCaseAscii(std::string s);
int CompareNoCaseAscii(const char *a, const char *b, std::size_t n);
int CompareNoCaseAscii(const std::string &a, const std::string &b);
#if defined(MODPLUG_TRACKER)
mpt::ustring ToLowerCase(const mpt::ustring &s);
mpt::ustring ToUpperCase(const mpt::ustring &s);
#endif // MODPLUG_TRACKER
} // namespace mpt
// The AnyString types are meant to be used as function argument types only,
// and only during the transition phase to all-unicode strings in the whole codebase.
// Using an AnyString type as function argument avoids the need to overload a function for all the
// different string types that we currently have.
// Warning: These types will silently do charset conversions. Only use them when this can be tolerated.
// BasicAnyString is convertable to mpt::ustring and constructable from any string at all.
template <mpt::Charset charset = mpt::CharsetUTF8, bool tryUTF8 = true>
class BasicAnyString : public mpt::ustring
{
private:
static mpt::ustring From8bit(const std::string &str)
{
if(charset == mpt::CharsetUTF8)
{
return mpt::ToUnicode(mpt::CharsetUTF8, str);
}
// auto utf8 detection
if(tryUTF8 && mpt::IsUTF8(str))
{
return mpt::ToUnicode(mpt::CharsetUTF8, str);
} else
{
return mpt::ToUnicode(charset, str);
}
}
public:
// 8 bit
BasicAnyString(const char *str) : mpt::ustring(str ? mpt::ToUnicode(charset, str) : mpt::ustring()) { }
BasicAnyString(const std::string str) : mpt::ustring(mpt::ToUnicode(charset, str)) { }
// unicode
BasicAnyString(const mpt::ustring &str) : mpt::ustring(str) { }
BasicAnyString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { }
#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT
BasicAnyString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif
#if MPT_WSTRING_CONVERT
BasicAnyString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { }
#endif
// mfc
#if defined(_MFC_VER)
BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#ifndef UNICODE
BasicAnyString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif
#endif
// fallback for custom string types
template <typename Tstring> BasicAnyString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { }
template <typename Tstring> BasicAnyString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward<Tstring>(str))) { }
};
// AnyUnicodeString is convertable to mpt::ustring and constructable from any unicode string,
class AnyUnicodeString : public mpt::ustring
{
public:
// unicode
AnyUnicodeString(const mpt::ustring &str) : mpt::ustring(str) { }
AnyUnicodeString(mpt::ustring &&str) : mpt::ustring(std::move(str)) { }
#if MPT_USTRING_MODE_UTF8 && MPT_WSTRING_CONVERT
AnyUnicodeString(const std::wstring &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif
#if MPT_WSTRING_CONVERT
AnyUnicodeString(const wchar_t *str) : mpt::ustring(str ? mpt::ToUnicode(str) : mpt::ustring()) { }
#endif
// mfc
#if defined(_MFC_VER)
AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#ifndef UNICODE
AnyUnicodeString(const CStringW &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif
#endif
// fallback for custom string types
template <typename Tstring> AnyUnicodeString(const Tstring &str) : mpt::ustring(mpt::ToUnicode(str)) { }
template <typename Tstring> AnyUnicodeString(Tstring &&str) : mpt::ustring(mpt::ToUnicode(std::forward<Tstring>(str))) { }
};
// AnyString
// Try to do the smartest auto-magic we can do.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
typedef BasicAnyString<mpt::CharsetLocale, true> AnyString;
#elif MPT_OS_WINDOWS
typedef BasicAnyString<mpt::CharsetWindows1252, true> AnyString;
#else
typedef BasicAnyString<mpt::CharsetISO8859_1, true> AnyString;
#endif
// AnyStringLocale
// char-based strings are assumed to be in locale encoding.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
typedef BasicAnyString<mpt::CharsetLocale, false> AnyStringLocale;
#else
typedef BasicAnyString<mpt::CharsetUTF8, false> AnyStringLocale;
#endif
// AnyStringUTF8orLocale
// char-based strings are tried in UTF8 first, if this fails, locale is used.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
typedef BasicAnyString<mpt::CharsetLocale, true> AnyStringUTF8orLocale;
#else
typedef BasicAnyString<mpt::CharsetUTF8, false> AnyStringUTF8orLocale;
#endif
// AnyStringUTF8
// char-based strings are assumed to be in UTF8.
typedef BasicAnyString<mpt::CharsetUTF8, false> AnyStringUTF8;
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,498 @@
/*
* mptStringFormat.cpp
* -------------------
* Purpose: Convert other types to strings.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptStringFormat.h"
#include <iomanip>
#include <locale>
#include <sstream>
#include <string>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
template<typename Tstream, typename T> inline void SaneInsert(Tstream & s, const T & x) { s << x; }
// do the right thing for signed/unsigned char and bool
template<typename Tstream> void SaneInsert(Tstream & s, const bool & x) { s << static_cast<int>(x); }
template<typename Tstream> void SaneInsert(Tstream & s, const signed char & x) { s << static_cast<signed int>(x); }
template<typename Tstream> void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast<unsigned int>(x); }
template<typename T>
inline std::string ToStringHelper(const T & x)
{
std::ostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#if MPT_WSTRING_FORMAT
template<typename T>
inline std::wstring ToWStringHelper(const T & x)
{
std::wostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#endif
#if MPT_WSTRING_CONVERT
std::string ToString(const std::wstring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); }
std::string ToString(const wchar_t * const & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); }
std::string ToString(const wchar_t & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, std::wstring(1, x)); }
#endif
#if MPT_USTRING_MODE_UTF8
std::string ToString(const mpt::ustring & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); }
#endif
#if defined(_MFC_VER)
std::string ToString(const CString & x) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x); }
#endif
std::string ToString(const bool & x) { return ToStringHelper(x); }
std::string ToString(const signed char & x) { return ToStringHelper(x); }
std::string ToString(const unsigned char & x) { return ToStringHelper(x); }
std::string ToString(const signed short & x) { return ToStringHelper(x); }
std::string ToString(const unsigned short & x) { return ToStringHelper(x); }
std::string ToString(const signed int & x) { return ToStringHelper(x); }
std::string ToString(const unsigned int & x) { return ToStringHelper(x); }
std::string ToString(const signed long & x) { return ToStringHelper(x); }
std::string ToString(const unsigned long & x) { return ToStringHelper(x); }
std::string ToString(const signed long long & x) { return ToStringHelper(x); }
std::string ToString(const unsigned long long & x) { return ToStringHelper(x); }
std::string ToString(const float & x) { return ToStringHelper(x); }
std::string ToString(const double & x) { return ToStringHelper(x); }
std::string ToString(const long double & x) { return ToStringHelper(x); }
mpt::ustring ToUString(const std::string & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); }
mpt::ustring ToUString(const char * const & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, x); }
mpt::ustring ToUString(const char & x) { return mpt::ToUnicode(mpt::CharsetLocaleOrUTF8, std::string(1, x)); }
#if MPT_WSTRING_FORMAT
#if MPT_USTRING_MODE_UTF8
mpt::ustring ToUString(const std::wstring & x) { return mpt::ToUnicode(x); }
#endif
mpt::ustring ToUString(const wchar_t * const & x) { return mpt::ToUnicode(x); }
mpt::ustring ToUString(const wchar_t & x) { return mpt::ToUnicode(std::wstring(1, x)); }
#endif
#if defined(_MFC_VER)
mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); }
#endif
#if MPT_USTRING_MODE_WIDE
mpt::ustring ToUString(const bool & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const signed char & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const signed short & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const signed int & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const signed long & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const signed long long & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const float & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const double & x) { return ToWStringHelper(x); }
mpt::ustring ToUString(const long double & x) { return ToWStringHelper(x); }
#endif
#if MPT_USTRING_MODE_UTF8
mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::CharsetUTF8, ToStringHelper(x)); }
#endif
#if MPT_WSTRING_FORMAT
std::wstring ToWString(const std::string & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); }
std::wstring ToWString(const char * const & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, x); }
std::wstring ToWString(const char & x) { return mpt::ToWide(mpt::CharsetLocaleOrUTF8, std::string(1, x)); }
#if MPT_USTRING_MODE_UTF8
std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); }
#endif
#if defined(_MFC_VER)
std::wstring ToWString(const CString & x) { return mpt::ToWide(x); }
#endif
std::wstring ToWString(const bool & x) { return ToWStringHelper(x); }
std::wstring ToWString(const signed char & x) { return ToWStringHelper(x); }
std::wstring ToWString(const unsigned char & x) { return ToWStringHelper(x); }
std::wstring ToWString(const signed short & x) { return ToWStringHelper(x); }
std::wstring ToWString(const unsigned short & x) { return ToWStringHelper(x); }
std::wstring ToWString(const signed int & x) { return ToWStringHelper(x); }
std::wstring ToWString(const unsigned int & x) { return ToWStringHelper(x); }
std::wstring ToWString(const signed long & x) { return ToWStringHelper(x); }
std::wstring ToWString(const unsigned long & x) { return ToWStringHelper(x); }
std::wstring ToWString(const signed long long & x) { return ToWStringHelper(x); }
std::wstring ToWString(const unsigned long long & x) { return ToWStringHelper(x); }
std::wstring ToWString(const float & x) { return ToWStringHelper(x); }
std::wstring ToWString(const double & x) { return ToWStringHelper(x); }
std::wstring ToWString(const long double & x) { return ToWStringHelper(x); }
#endif
template<typename Tostream>
inline void ApplyFormat(Tostream & o, const FormatSpec & format)
{
FormatFlags f = format.GetFlags();
std::size_t width = format.GetWidth();
int precision = format.GetPrecision();
if(precision != -1 && width != 0 && !(f & fmt_base::NotaFix) && !(f & fmt_base::NotaSci))
{
// fixup:
// precision behaves differently from .#
// avoid default format when precision and width are set
f &= ~fmt_base::NotaNrm;
f |= fmt_base::NotaFix;
}
if(f & fmt_base::BaseDec) { o << std::dec; }
else if(f & fmt_base::BaseHex) { o << std::hex; }
if(f & fmt_base::NotaNrm ) { /*nothing*/ }
else if(f & fmt_base::NotaFix ) { o << std::setiosflags(std::ios::fixed); }
else if(f & fmt_base::NotaSci ) { o << std::setiosflags(std::ios::scientific); }
if(f & fmt_base::CaseLow) { o << std::nouppercase; }
else if(f & fmt_base::CaseUpp) { o << std::uppercase; }
if(f & fmt_base::FillOff) { /* nothing */ }
else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); }
else if(f & fmt_base::FillSpc) { o << std::setw(width) << std::setfill(typename Tostream::char_type(' ')); }
if(precision != -1) { o << std::setprecision(precision); }
}
template<typename T>
inline std::string FormatValHelper(const T & x, const FormatSpec & f)
{
std::ostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f);
SaneInsert(o, x);
return o.str();
}
#if MPT_WSTRING_FORMAT
template<typename T>
inline std::wstring FormatValWHelper(const T & x, const FormatSpec & f)
{
std::wostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f);
SaneInsert(o, x);
return o.str();
}
#endif
// Parses a useful subset of standard sprintf syntax for specifying floating point formatting.
template<typename Tchar>
static inline FormatSpec ParseFormatStringFloat(const Tchar * str)
{
MPT_ASSERT(str);
FormatFlags f = FormatFlags();
std::size_t width = 0;
int precision = -1;
if(!str)
{
return FormatSpec();
}
const Tchar * p = str;
while(*p && *p != Tchar('%'))
{
++p;
}
++p;
while(*p && (*p == Tchar(' ') || *p == Tchar('0')))
{
if(*p == Tchar(' ')) f |= mpt::fmt_base::FillSpc;
if(*p == Tchar('0')) f |= mpt::fmt_base::FillNul;
++p;
}
if(!(f & mpt::fmt_base::FillSpc) && !(f & mpt::fmt_base::FillNul))
{
f |= mpt::fmt_base::FillOff;
}
while(*p && (Tchar('0') <= *p && *p <= Tchar('9')))
{
if(f & mpt::fmt_base::FillOff)
{
f &= ~mpt::fmt_base::FillOff;
f |= mpt::fmt_base::FillSpc;
}
width *= 10;
width += *p - Tchar('0');
++p;
}
if(*p && *p == Tchar('.'))
{
++p;
precision = 0;
while(*p && (Tchar('0') <= *p && *p <= Tchar('9')))
{
precision *= 10;
precision += *p - Tchar('0');
++p;
}
}
if(*p && (*p == Tchar('g') || *p == Tchar('G') || *p == Tchar('f') || *p == Tchar('F') || *p == Tchar('e') || *p == Tchar('E')))
{
if(*p == Tchar('g')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseLow;
if(*p == Tchar('G')) f |= mpt::fmt_base::NotaNrm | mpt::fmt_base::CaseUpp;
if(*p == Tchar('f')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseLow;
if(*p == Tchar('F')) f |= mpt::fmt_base::NotaFix | mpt::fmt_base::CaseUpp;
if(*p == Tchar('e')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseLow;
if(*p == Tchar('E')) f |= mpt::fmt_base::NotaSci | mpt::fmt_base::CaseUpp;
++p;
}
return FormatSpec().SetFlags(f).SetWidth(width).SetPrecision(precision);
}
FormatSpec & FormatSpec::ParsePrintf(const char * format)
{
*this = ParseFormatStringFloat(format);
return *this;
}
FormatSpec & FormatSpec::ParsePrintf(const wchar_t * format)
{
*this = ParseFormatStringFloat(format);
return *this;
}
FormatSpec & FormatSpec::ParsePrintf(const std::string & format)
{
*this = ParseFormatStringFloat(format.c_str());
return *this;
}
FormatSpec & FormatSpec::ParsePrintf(const std::wstring & format)
{
*this = ParseFormatStringFloat(format.c_str());
return *this;
}
std::string FormatVal(const char & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const wchar_t & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelper(x, f); }
std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelper(x, f); }
#if MPT_WSTRING_FORMAT
std::wstring FormatValW(const char & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelper(x, f); }
#endif
namespace String
{
namespace detail
{
template<typename Tstring>
Tstring PrintImplTemplate(const Tstring & format
, const Tstring & x1
, const Tstring & x2
, const Tstring & x3
, const Tstring & x4
, const Tstring & x5
, const Tstring & x6
, const Tstring & x7
, const Tstring & x8
)
{
Tstring result;
const std::size_t len = format.length();
result.reserve(len);
for(std::size_t pos = 0; pos != len; ++pos)
{
typename Tstring::value_type c = format[pos];
if(pos + 1 != len && c == '%')
{
pos++;
c = format[pos];
if('1' <= c && c <= '9')
{
const std::size_t n = c - '0';
switch(n)
{
case 1: result.append(x1); break;
case 2: result.append(x2); break;
case 3: result.append(x3); break;
case 4: result.append(x4); break;
case 5: result.append(x5); break;
case 6: result.append(x6); break;
case 7: result.append(x7); break;
case 8: result.append(x8); break;
}
continue;
} else if(c != '%')
{
result.append(1, '%');
}
}
result.append(1, c);
}
return result;
}
#if defined(_MFC_VER)
template<>
CString PrintImplTemplate<CString>(const CString & format
, const CString & x1
, const CString & x2
, const CString & x3
, const CString & x4
, const CString & x5
, const CString & x6
, const CString & x7
, const CString & x8
)
{
CString result;
const int len = format.GetLength();
result.Preallocate(len);
for(int pos = 0; pos != len; ++pos)
{
CString::XCHAR c = format[pos];
if(pos + 1 != len && c == _T('%'))
{
pos++;
c = format[pos];
if(_T('1') <= c && c <= _T('9'))
{
const std::size_t n = c - _T('0');
switch(n)
{
case 1: result += x1; break;
case 2: result += x2; break;
case 3: result += x3; break;
case 4: result += x4; break;
case 5: result += x5; break;
case 6: result += x6; break;
case 7: result += x7; break;
case 8: result += x8; break;
}
continue;
} else if(c != _T('%'))
{
result.AppendChar(_T('%'));
}
}
result.AppendChar(c);
}
return result;
}
#endif
std::string PrintImpl(const std::string & format
, const std::string & x1
, const std::string & x2
, const std::string & x3
, const std::string & x4
, const std::string & x5
, const std::string & x6
, const std::string & x7
, const std::string & x8
)
{
return PrintImplTemplate<std::string>(format, x1,x2,x3,x4,x5,x6,x7,x8);
}
#if MPT_WSTRING_FORMAT
std::wstring PrintImpl(const std::wstring & format
, const std::wstring & x1
, const std::wstring & x2
, const std::wstring & x3
, const std::wstring & x4
, const std::wstring & x5
, const std::wstring & x6
, const std::wstring & x7
, const std::wstring & x8
)
{
return PrintImplTemplate<std::wstring>(format, x1,x2,x3,x4,x5,x6,x7,x8);
}
#endif
#if MPT_USTRING_MODE_UTF8
mpt::ustring PrintImpl(const mpt::ustring & format
, const mpt::ustring & x1
, const mpt::ustring & x2
, const mpt::ustring & x3
, const mpt::ustring & x4
, const mpt::ustring & x5
, const mpt::ustring & x6
, const mpt::ustring & x7
, const mpt::ustring & x8
)
{
return PrintImplTemplate<mpt::ustring>(format, x1,x2,x3,x4,x5,x6,x7,x8);
}
#endif
#if defined(_MFC_VER)
CString PrintImpl(const CString & format
, const CString & x1
, const CString & x2
, const CString & x3
, const CString & x4
, const CString & x5
, const CString & x6
, const CString & x7
, const CString & x8
)
{
return PrintImplTemplate<CString>(format, x1,x2,x3,x4,x5,x6,x7,x8);
}
#endif
} // namespace detail
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,782 @@
/*
* mptStringFormat.h
* -----------------
* Purpose: Convert other types to strings.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "mptString.h"
OPENMPT_NAMESPACE_BEGIN
// The following section demands a rationale.
// 1. mpt::fmt::val(), mpt::wfmt::val() and mpt::ufmt::val() mimic the semantics of c++11 std::to_string() and std::to_wstring().
// There is an important difference though. The c++11 versions are specified in terms of sprintf formatting which in turn
// depends on the current C locale. This renders these functions unusable in a library context because the current
// C locale is set by the library-using application and could be anything. There is no way a library can get reliable semantics
// out of these functions. It is thus better to just avoid them.
// ToString() and ToWString() are based on iostream internally, but the the locale of the stream is forced to std::locale::classic(),
// which results in "C" ASCII locale behavior.
// 2. The full suite of printf-like or iostream like number formatting is generally not required. Instead, a sane subset functionality
// is provided here.
// For convenience, mpt::fmt::f(const char *, float) allows formatting a single floating point value with a
// standard printf-like format string. This itself relies on iostream with classic() locale internally and is thus current locale
// agnostic.
// When formatting integers, it is recommended to use mpt::fmt::dec or mpt::fmt::hex. Appending a template argument '<n>' sets the width,
// the same way as '%nd' would do. Appending a '0' to the function name causes zero-filling as print-like '%0nd' would do. Spelling 'HEX'
// in upper-case generates upper-case hex digits. If these are not known at compile-time, a more verbose FormatVal(int, format) can be
// used.
// 3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting.
// The only specifier allowed is '%' followed by a single digit n. It references to n-th parameter after the format string (1-based).
// This mimics the behaviour of QString::arg() in QT4/5 or MFC AfxFormatString2(). C printf-like functions offer similar functionality
// with a '%n$TYPE' syntax. In .NET, the syntax is '{n}'. This is useful to support localization strings that can change the parameter
// ordering.
// There are macro verions (MPT_FORMAT and variants) which properly use wide string literals for the format parameter.
// 4. Every function is available for std::string, std::wstring and mpt::ustring. std::string makes no assumption about the encoding, which
// basically means, it should work for any 7-bit or 8-bit encoding, including for example ASCII, UTF8 or the current locale encoding.
// std::string std::wstring mpt::ustring CString
// mpt::fmt::val mpt::wfmt::val mpt::ufmt::val mpt::tfmt::val
// mpt::FormatVal mpt::FormatValW mpt::FormatValTFunctor<mpt::ustring>() mpt::FormatValTFunctor<Cstring>()
// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt
// mpt::format mpt::format mpt::format mpt::format
// 5. All functionality here delegates real work outside of the header file so that <sstream> and <locale> do not need to be included when
// using this functionality.
// Advantages:
// - Avoids binary code bloat when too much of iostream operator << gets inlined at every usage site.
// - Faster compile times because <sstream> and <locale> (2 very complex headers) are not included everywhere.
// Disadvantages:
// - Slightly more c++ code is required for delegating work.
// - As the header does not use iostreams, custom types need to overload mpt::String, mpt::ToWstring and mpt::UString instead of
// iostream operator << to allow for custom type formatting.
// - std::string, std::wstring and mpt::ustring are returned from somewhat deep cascades of helper functions. Where possible, code is
// written in such a way that return-value-optimization (RVO) or named-return-value-optimization (NRVO) should be able to eliminate
// almost all these copies. This should not be a problem for any decent modern compiler (and even less so for a c++11 compiler where
// move-semantics will kick in if RVO/NRVO fails).
namespace mpt
{
// ToString() converts various built-in types to a well-defined, locale-independent string representation.
// This is also used as a type-tunnel pattern for mpt::format.
// Custom types that need to be converted to strings are encouraged to overload ToString() and ToWString().
// fallback to member function ToString()
template <typename T> auto ToString(const T & x) -> decltype(x.ToString()) { return x.ToString(); }
static inline std::string ToString(const std::string & x) { return x; }
static inline std::string ToString(const char * const & x) { return x; }
MPT_DEPRECATED static inline std::string ToString(const char & x) { return std::string(1, x); } // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if MPT_WSTRING_FORMAT
MPT_DEPRECATED std::string ToString(const std::wstring & x); // Unknown encoding.
MPT_DEPRECATED std::string ToString(const wchar_t * const & x); // Unknown encoding.
MPT_DEPRECATED std::string ToString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif
#if MPT_USTRING_MODE_UTF8
MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding.
#endif
#if defined(_MFC_VER)
MPT_DEPRECATED std::string ToString(const mpt::ustring & x); // Unknown encoding.
#endif
#if defined(_MFC_VER)
MPT_DEPRECATED std::string ToString(const CString & x);
#endif
std::string ToString(const bool & x);
std::string ToString(const signed char & x);
std::string ToString(const unsigned char & x);
std::string ToString(const signed short & x);
std::string ToString(const unsigned short & x);
std::string ToString(const signed int & x);
std::string ToString(const unsigned int & x);
std::string ToString(const signed long & x);
std::string ToString(const unsigned long & x);
std::string ToString(const signed long long & x);
std::string ToString(const unsigned long long & x);
std::string ToString(const float & x);
std::string ToString(const double & x);
std::string ToString(const long double & x);
// fallback to member function ToUString()
template <typename T> auto ToUString(const T & x) -> decltype(x.ToUString()) { return x.ToUString(); }
static inline mpt::ustring ToUString(const mpt::ustring & x) { return x; }
MPT_DEPRECATED mpt::ustring ToUString(const std::string & x); // Unknown encoding.
MPT_DEPRECATED mpt::ustring ToUString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
MPT_DEPRECATED mpt::ustring ToUString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if MPT_WSTRING_FORMAT
#if MPT_USTRING_MODE_UTF8
mpt::ustring ToUString(const std::wstring & x);
#endif
mpt::ustring ToUString(const wchar_t * const & x);
MPT_DEPRECATED mpt::ustring ToUString(const wchar_t & x); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif
#if defined(_MFC_VER)
mpt::ustring ToUString(const CString & x);
#endif
mpt::ustring ToUString(const bool & x);
mpt::ustring ToUString(const signed char & x);
mpt::ustring ToUString(const unsigned char & x);
mpt::ustring ToUString(const signed short & x);
mpt::ustring ToUString(const unsigned short & x);
mpt::ustring ToUString(const signed int & x);
mpt::ustring ToUString(const unsigned int & x);
mpt::ustring ToUString(const signed long & x);
mpt::ustring ToUString(const unsigned long & x);
mpt::ustring ToUString(const signed long long & x);
mpt::ustring ToUString(const unsigned long long & x);
mpt::ustring ToUString(const float & x);
mpt::ustring ToUString(const double & x);
mpt::ustring ToUString(const long double & x);
#if MPT_WSTRING_FORMAT
MPT_DEPRECATED std::wstring ToWString(const std::string & x); // Unknown encoding.
MPT_DEPRECATED std::wstring ToWString(const char * const & x); // Unknown encoding. Note that this also applies to TCHAR in !UNICODE builds as the type is indistinguishable from char. Wrap with CString or FromTcharStr in this case.
MPT_DEPRECATED std::wstring ToWString(const char & x); // deprecated to catch potential API mis-use, use std::string(1, x) instead
static inline std::wstring ToWString(const std::wstring & x) { return x; }
static inline std::wstring ToWString(const wchar_t * const & x) { return x; }
MPT_DEPRECATED static inline std::wstring ToWString(const wchar_t & x) { return std::wstring(1, x); } // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#if MPT_USTRING_MODE_UTF8
std::wstring ToWString(const mpt::ustring & x);
#endif
#if defined(_MFC_VER)
std::wstring ToWString(const CString & x);
#endif
std::wstring ToWString(const bool & x);
std::wstring ToWString(const signed char & x);
std::wstring ToWString(const unsigned char & x);
std::wstring ToWString(const signed short & x);
std::wstring ToWString(const unsigned short & x);
std::wstring ToWString(const signed int & x);
std::wstring ToWString(const unsigned int & x);
std::wstring ToWString(const signed long & x);
std::wstring ToWString(const unsigned long & x);
std::wstring ToWString(const signed long long & x);
std::wstring ToWString(const unsigned long long & x);
std::wstring ToWString(const float & x);
std::wstring ToWString(const double & x);
std::wstring ToWString(const long double & x);
#endif
#if defined(_MFC_VER)
#ifdef UNICODE
#if MPT_WSTRING_FORMAT
template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToWString(x)); }
#else
template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::ToCString(ToUString(x)); }
#endif
#else
namespace detail {
template <typename T> struct CstringToStdStringImpl { CString operator () (const T & v) { return mpt::ToCString(mpt::CharsetLocale, ToString(v)); } };
template <> struct CstringToStdStringImpl<CString> { CString operator () (const CString & v) { return v; } };
}
template <typename T> static inline CString ToCStringHelper(const T & x) { return mpt::detail::CstringToStdStringImpl<T>()(x); }
#endif
#endif
template <typename Tstring> struct ToStringTFunctor {};
template <> struct ToStringTFunctor<std::string> { template <typename T> inline std::string operator() (const T & x) { return ToString(x); } };
template <> struct ToStringTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x) { return ToUString(x); } };
#if MPT_WSTRING_FORMAT && MPT_USTRING_MODE_UTF8
template <> struct ToStringTFunctor<std::wstring> { template <typename T> inline std::wstring operator() (const T & x) { return ToWString(x); } };
#endif
#if defined(_MFC_VER)
template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCStringHelper(x); } };
#endif
template<typename Tstring, typename T> inline Tstring ToStringT(const T & x) { return ToStringTFunctor<Tstring>()(x); }
struct fmt_base
{
enum FormatFlagsEnum
{
BaseDec = 0x0001, // base 10 (integers only)
BaseHex = 0x0002, // base 16 (integers only)
CaseLow = 0x0010, // lower case hex digits
CaseUpp = 0x0020, // upper case hex digits
FillOff = 0x0100, // do not fill up width
FillSpc = 0x0200, // fill up width with spaces
FillNul = 0x0400, // fill up width with zeros
NotaNrm = 0x1000, // float: normal/default notation
NotaFix = 0x2000, // float: fixed point notation
NotaSci = 0x4000, // float: scientific notation
};
}; // struct fmt_base
typedef unsigned int FormatFlags;
STATIC_ASSERT(sizeof(FormatFlags) >= sizeof(fmt_base::FormatFlagsEnum));
class FormatSpec;
MPT_DEPRECATED std::string FormatVal(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead
MPT_DEPRECATED std::string FormatVal(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
std::string FormatVal(const bool & x, const FormatSpec & f);
std::string FormatVal(const signed char & x, const FormatSpec & f);
std::string FormatVal(const unsigned char & x, const FormatSpec & f);
std::string FormatVal(const signed short & x, const FormatSpec & f);
std::string FormatVal(const unsigned short & x, const FormatSpec & f);
std::string FormatVal(const signed int & x, const FormatSpec & f);
std::string FormatVal(const unsigned int & x, const FormatSpec & f);
std::string FormatVal(const signed long & x, const FormatSpec & f);
std::string FormatVal(const unsigned long & x, const FormatSpec & f);
std::string FormatVal(const signed long long & x, const FormatSpec & f);
std::string FormatVal(const unsigned long long & x, const FormatSpec & f);
std::string FormatVal(const float & x, const FormatSpec & f);
std::string FormatVal(const double & x, const FormatSpec & f);
std::string FormatVal(const long double & x, const FormatSpec & f);
#if MPT_WSTRING_FORMAT
MPT_DEPRECATED std::wstring FormatValW(const char & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::string(1, x) instead
MPT_DEPRECATED std::wstring FormatValW(const wchar_t & x, const FormatSpec & f); // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
std::wstring FormatValW(const bool & x, const FormatSpec & f);
std::wstring FormatValW(const signed char & x, const FormatSpec & f);
std::wstring FormatValW(const unsigned char & x, const FormatSpec & f);
std::wstring FormatValW(const signed short & x, const FormatSpec & f);
std::wstring FormatValW(const unsigned short & x, const FormatSpec & f);
std::wstring FormatValW(const signed int & x, const FormatSpec & f);
std::wstring FormatValW(const unsigned int & x, const FormatSpec & f);
std::wstring FormatValW(const signed long & x, const FormatSpec & f);
std::wstring FormatValW(const unsigned long & x, const FormatSpec & f);
std::wstring FormatValW(const signed long long & x, const FormatSpec & f);
std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f);
std::wstring FormatValW(const float & x, const FormatSpec & f);
std::wstring FormatValW(const double & x, const FormatSpec & f);
std::wstring FormatValW(const long double & x, const FormatSpec & f);
#endif
template <typename Tstring> struct FormatValTFunctor {};
template <> struct FormatValTFunctor<std::string> { template <typename T> inline std::string operator() (const T & x, const FormatSpec & f) { return FormatVal(x, f); } };
#if MPT_WSTRING_FORMAT
template <> struct FormatValTFunctor<std::wstring> { template <typename T> inline std::wstring operator() (const T & x, const FormatSpec & f) { return FormatValW(x, f); } };
#endif
#if MPT_USTRING_MODE_UTF8
template <> struct FormatValTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::CharsetUTF8, FormatVal(x, f)); } };
#endif
#if defined(_MFC_VER)
#ifdef UNICODE
template <> struct FormatValTFunctor<CString> { template <typename T> inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(FormatValW(x, f)); } };
#else
template <> struct FormatValTFunctor<CString> { template <typename T> inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::CharsetLocale, FormatVal(x, f)); } };
#endif
#endif
class FormatSpec
{
private:
FormatFlags flags;
std::size_t width;
int precision;
public:
FormatSpec() : flags(0), width(0), precision(-1) {}
FormatFlags GetFlags() const { return flags; }
std::size_t GetWidth() const { return width; }
int GetPrecision() const { return precision; }
FormatSpec & SetFlags(FormatFlags f) { flags = f; return *this; }
FormatSpec & SetWidth(std::size_t w) { width = w; return *this; }
FormatSpec & SetPrecision(int p) { precision = p; return *this; }
public:
// short-hand construction
explicit FormatSpec(FormatFlags f, std::size_t w = 0, int p = -1) : flags(f), width(w), precision(p) {}
explicit FormatSpec(const char * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); }
explicit FormatSpec(const wchar_t * format) : flags(0), width(0), precision(-1) { ParsePrintf(format); }
explicit FormatSpec(const std::string & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); }
explicit FormatSpec(const std::wstring & format) : flags(0), width(0), precision(-1) { ParsePrintf(format); }
public:
// only for floating point formats
FormatSpec & ParsePrintf(const char * format);
FormatSpec & ParsePrintf(const wchar_t * format);
FormatSpec & ParsePrintf(const std::string & format);
FormatSpec & ParsePrintf(const std::wstring & format);
public:
FormatSpec & BaseDec() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; }
FormatSpec & BaseHex() { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; }
FormatSpec & CaseLow() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; }
FormatSpec & CaseUpp() { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; }
FormatSpec & FillOff() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; }
FormatSpec & FillSpc() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillSpc; return *this; }
FormatSpec & FillNul() { flags &= ~(fmt_base::FillOff|fmt_base::FillSpc|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; }
FormatSpec & NotaNrm() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; }
FormatSpec & NotaFix() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; }
FormatSpec & NotaSci() { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; }
FormatSpec & Width(std::size_t w) { width = w; return *this; }
FormatSpec & Prec(int p) { precision = p; return *this; }
public:
FormatSpec & Dec() { return BaseDec(); }
FormatSpec & Hex() { return BaseHex(); }
FormatSpec & Low() { return CaseLow(); }
FormatSpec & Upp() { return CaseUpp(); }
FormatSpec & Off() { return FillOff(); }
FormatSpec & Spc() { return FillSpc(); }
FormatSpec & Nul() { return FillNul(); }
FormatSpec & Nrm() { return NotaNrm(); }
FormatSpec & Fix() { return NotaFix(); }
FormatSpec & Sci() { return NotaSci(); }
public:
FormatSpec & Decimal() { return BaseDec(); }
FormatSpec & Hexadecimal() { return BaseHex(); }
FormatSpec & Lower() { return CaseLow(); }
FormatSpec & Upper() { return CaseUpp(); }
FormatSpec & FillNone() { return FillOff(); }
FormatSpec & FillSpace() { return FillSpc(); }
FormatSpec & FillZero() { return FillNul(); }
FormatSpec & FloatNormal() { return NotaNrm(); }
FormatSpec & FloatFixed() { return NotaFix(); }
FormatSpec & FloatScientific() { return NotaSci(); }
FormatSpec & Precision(int p) { return Prec(p); }
template<typename Tstring, typename T>
inline Tstring ToStringT(const T & x) const
{
return FormatValTFunctor<Tstring>()(x, *this);
}
template<typename T>
inline std::string ToString(const T & x) const
{
return FormatVal(x, *this);
}
#if MPT_WSTRING_FORMAT
template<typename T>
inline std::wstring ToWString(const T & x) const
{
return FormatValW(x, *this);
}
#endif
};
template <typename Tstring>
struct fmtT : fmt_base
{
template<typename T>
static inline Tstring val(const T& x)
{
return ToStringTFunctor<Tstring>()(x);
}
template<typename T>
static inline Tstring dec(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillOff());
}
template<int width, typename T>
static inline Tstring dec(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillSpc().Width(width));
}
template<int width, typename T>
static inline Tstring dec0(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillNul().Width(width));
}
template<typename T>
static inline Tstring hex(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillOff());
}
template<typename T>
static inline Tstring HEX(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillOff());
}
template<int width, typename T>
static inline Tstring hex(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillSpc().Width(width));
}
template<int width, typename T>
static inline Tstring HEX(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillSpc().Width(width));
}
template<int width, typename T>
static inline Tstring hex0(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width));
}
template<int width, typename T>
static inline Tstring HEX0(const T& x)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width));
}
template<typename T>
static inline Tstring flt(const T& x, std::size_t width = 0, int precision = -1)
{
STATIC_ASSERT(std::is_floating_point<T>::value);
if(width == 0)
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaNrm().FillOff().Precision(precision));
} else
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaNrm().FillSpc().Width(width).Precision(precision));
}
}
template<typename T>
static inline Tstring fix(const T& x, std::size_t width = 0, int precision = -1)
{
STATIC_ASSERT(std::is_floating_point<T>::value);
if(width == 0)
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaFix().FillOff().Precision(precision));
} else
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaFix().FillSpc().Width(width).Precision(precision));
}
}
template<typename T>
static inline Tstring sci(const T& x, std::size_t width = 0, int precision = -1)
{
STATIC_ASSERT(std::is_floating_point<T>::value);
if(width == 0)
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaSci().FillOff().Precision(precision));
} else
{
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaSci().FillSpc().Width(width).Precision(precision));
}
}
template <typename T, typename Tformat>
static inline Tstring f(const Tformat & format, const T& x)
{
STATIC_ASSERT(std::is_floating_point<T>::value);
return FormatValTFunctor<Tstring>()(x, FormatSpec().ParsePrintf(format));
}
}; // struct fmtT
typedef fmtT<std::string> fmt;
#if MPT_WSTRING_FORMAT
typedef fmtT<std::wstring> wfmt;
#endif
#if MPT_USTRING_MODE_WIDE
typedef fmtT<std::wstring> ufmt;
#else
typedef fmtT<mpt::ustring> ufmt;
#endif
#if defined(_MFC_VER)
typedef fmtT<CString> tfmt;
#endif
} // namespace mpt
namespace mpt {
namespace String {
namespace detail
{
template <typename T> struct to_string_type { };
template <> struct to_string_type<std::string > { typedef std::string type; };
template <> struct to_string_type<char > { typedef std::string type; };
template <> struct to_string_type<char * > { typedef std::string type; };
template <> struct to_string_type<const char > { typedef std::string type; };
template <> struct to_string_type<const char * > { typedef std::string type; };
template <> struct to_string_type<std::wstring > { typedef std::wstring type; };
template <> struct to_string_type<wchar_t > { typedef std::wstring type; };
template <> struct to_string_type<wchar_t * > { typedef std::wstring type; };
template <> struct to_string_type<const wchar_t > { typedef std::wstring type; };
template <> struct to_string_type<const wchar_t *> { typedef std::wstring type; };
#if MPT_USTRING_MODE_UTF8
template <> struct to_string_type<mpt::ustring > { typedef mpt::ustring type; };
#endif
#if defined(_MFC_VER)
template <> struct to_string_type<CString > { typedef CString type; };
#endif
template <typename T, std::size_t N> struct to_string_type<T [N]> { typedef typename to_string_type<T>::type type; };
std::string PrintImpl(const std::string & format
, const std::string & x1 = std::string()
, const std::string & x2 = std::string()
, const std::string & x3 = std::string()
, const std::string & x4 = std::string()
, const std::string & x5 = std::string()
, const std::string & x6 = std::string()
, const std::string & x7 = std::string()
, const std::string & x8 = std::string()
);
#if MPT_WSTRING_FORMAT
std::wstring PrintImpl(const std::wstring & format
, const std::wstring & x1 = std::wstring()
, const std::wstring & x2 = std::wstring()
, const std::wstring & x3 = std::wstring()
, const std::wstring & x4 = std::wstring()
, const std::wstring & x5 = std::wstring()
, const std::wstring & x6 = std::wstring()
, const std::wstring & x7 = std::wstring()
, const std::wstring & x8 = std::wstring()
);
#endif
#if MPT_USTRING_MODE_UTF8
mpt::ustring PrintImpl(const mpt::ustring & format
, const mpt::ustring & x1 = mpt::ustring()
, const mpt::ustring & x2 = mpt::ustring()
, const mpt::ustring & x3 = mpt::ustring()
, const mpt::ustring & x4 = mpt::ustring()
, const mpt::ustring & x5 = mpt::ustring()
, const mpt::ustring & x6 = mpt::ustring()
, const mpt::ustring & x7 = mpt::ustring()
, const mpt::ustring & x8 = mpt::ustring()
);
#endif
#if defined(_MFC_VER)
CString PrintImpl(const CString & format
, const CString & x1 = CString()
, const CString & x2 = CString()
, const CString & x3 = CString()
, const CString & x4 = CString()
, const CString & x5 = CString()
, const CString & x6 = CString()
, const CString & x7 = CString()
, const CString & x8 = CString()
);
#endif
} // namespace detail
} // namespace String
template<typename Tformat>
struct message_formatter
{
typedef typename mpt::String::detail::to_string_type<Tformat>::type Tstring;
Tstring format;
message_formatter(const Tstring & format) : format(format) {}
Tstring operator() (
) const
{
return mpt::String::detail::PrintImpl(format
);
}
template<
typename T1
>
Tstring operator() (
const T1& x1
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
);
}
template<
typename T1
, typename T2
>
Tstring operator() (
const T1& x1
, const T2& x2
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
);
}
template<
typename T1
, typename T2
, typename T3
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
);
}
template<
typename T1
, typename T2
, typename T3
, typename T4
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
, const T4& x4
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
, ToStringTFunctor<Tstring>()(x4)
);
}
template<
typename T1
, typename T2
, typename T3
, typename T4
, typename T5
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
, const T4& x4
, const T5& x5
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
, ToStringTFunctor<Tstring>()(x4)
, ToStringTFunctor<Tstring>()(x5)
);
}
template<
typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
, const T4& x4
, const T5& x5
, const T6& x6
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
, ToStringTFunctor<Tstring>()(x4)
, ToStringTFunctor<Tstring>()(x5)
, ToStringTFunctor<Tstring>()(x6)
);
}
template<
typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
, const T4& x4
, const T5& x5
, const T6& x6
, const T7& x7
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
, ToStringTFunctor<Tstring>()(x4)
, ToStringTFunctor<Tstring>()(x5)
, ToStringTFunctor<Tstring>()(x6)
, ToStringTFunctor<Tstring>()(x7)
);
}
template<
typename T1
, typename T2
, typename T3
, typename T4
, typename T5
, typename T6
, typename T7
, typename T8
>
Tstring operator() (
const T1& x1
, const T2& x2
, const T3& x3
, const T4& x4
, const T5& x5
, const T6& x6
, const T7& x7
, const T8& x8
) const {
return mpt::String::detail::PrintImpl(format
, ToStringTFunctor<Tstring>()(x1)
, ToStringTFunctor<Tstring>()(x2)
, ToStringTFunctor<Tstring>()(x3)
, ToStringTFunctor<Tstring>()(x4)
, ToStringTFunctor<Tstring>()(x5)
, ToStringTFunctor<Tstring>()(x6)
, ToStringTFunctor<Tstring>()(x7)
, ToStringTFunctor<Tstring>()(x8)
);
}
}; // struct message_formatter<Tformat>
template<typename Tformat>
message_formatter<typename mpt::String::detail::to_string_type<Tformat>::type> format(const Tformat &format)
{
typedef typename mpt::String::detail::to_string_type<Tformat>::type Tstring;
return message_formatter<Tstring>(Tstring(format));
}
#if MPT_WSTRING_FORMAT
static inline message_formatter<std::wstring> wformat(const std::wstring &format)
{
return message_formatter<std::wstring>(format);
}
#endif
static inline message_formatter<mpt::ustring> uformat(const mpt::ustring &format)
{
return message_formatter<mpt::ustring>(format);
}
#if defined(_MFC_VER)
static inline message_formatter<CString> tformat(const CString &format)
{
return message_formatter<CString>(format);
}
#endif
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,120 @@
/*
* mptStringParse.cpp
* ------------------
* Purpose: Convert strings to other types.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptStringParse.h"
#include <locale>
#include <sstream>
OPENMPT_NAMESPACE_BEGIN
template<typename T>
inline T ConvertStrToHelper(const std::string &str)
{
std::istringstream i(str);
i.imbue(std::locale::classic());
T x;
if(!(i >> x))
{
return T();
}
return x;
}
template<> inline bool ConvertStrToHelper(const std::string &str) { return ConvertStrToHelper<int>(str)?true:false; }
template<> inline signed char ConvertStrToHelper(const std::string &str) { return static_cast<signed char>(ConvertStrToHelper<signed int>(str)); }
template<> inline unsigned char ConvertStrToHelper(const std::string &str) { return static_cast<unsigned char>(ConvertStrToHelper<unsigned int>(str)); }
#if MPT_WSTRING_FORMAT
template<typename T>
inline T ConvertStrToHelper(const std::wstring &str)
{
std::wistringstream i(str);
i.imbue(std::locale::classic());
T x;
if(!(i >> x))
{
return T();
}
return x;
}
template<> inline bool ConvertStrToHelper(const std::wstring &str) { return ConvertStrToHelper<int>(str)?true:false; }
template<> inline signed char ConvertStrToHelper(const std::wstring &str) { return static_cast<signed char>(ConvertStrToHelper<signed int>(str)); }
template<> inline unsigned char ConvertStrToHelper(const std::wstring &str) { return static_cast<unsigned char>(ConvertStrToHelper<unsigned int>(str)); }
#endif
bool ConvertStrToBool(const std::string &str) { return ConvertStrToHelper<bool>(str); }
signed char ConvertStrToSignedChar(const std::string &str) { return ConvertStrToHelper<signed char>(str); }
unsigned char ConvertStrToUnsignedChar(const std::string &str) { return ConvertStrToHelper<unsigned char>(str); }
signed short ConvertStrToSignedShort(const std::string &str) { return ConvertStrToHelper<signed short>(str); }
unsigned short ConvertStrToUnsignedShort(const std::string &str) { return ConvertStrToHelper<unsigned short>(str); }
signed int ConvertStrToSignedInt(const std::string &str) { return ConvertStrToHelper<signed int>(str); }
unsigned int ConvertStrToUnsignedInt(const std::string &str) { return ConvertStrToHelper<unsigned int>(str); }
signed long ConvertStrToSignedLong(const std::string &str) { return ConvertStrToHelper<signed long>(str); }
unsigned long ConvertStrToUnsignedLong(const std::string &str) { return ConvertStrToHelper<unsigned long>(str); }
signed long long ConvertStrToSignedLongLong(const std::string &str) { return ConvertStrToHelper<signed long long>(str); }
unsigned long long ConvertStrToUnsignedLongLong(const std::string &str) { return ConvertStrToHelper<unsigned long long>(str); }
float ConvertStrToFloat(const std::string &str) { return ConvertStrToHelper<float>(str); }
double ConvertStrToDouble(const std::string &str) { return ConvertStrToHelper<double>(str); }
long double ConvertStrToLongDouble(const std::string &str) { return ConvertStrToHelper<long double>(str); }
#if MPT_WSTRING_FORMAT
bool ConvertStrToBool(const std::wstring &str) { return ConvertStrToHelper<bool>(str); }
signed char ConvertStrToSignedChar(const std::wstring &str) { return ConvertStrToHelper<signed char>(str); }
unsigned char ConvertStrToUnsignedChar(const std::wstring &str) { return ConvertStrToHelper<unsigned char>(str); }
signed short ConvertStrToSignedShort(const std::wstring &str) { return ConvertStrToHelper<signed short>(str); }
unsigned short ConvertStrToUnsignedShort(const std::wstring &str) { return ConvertStrToHelper<unsigned short>(str); }
signed int ConvertStrToSignedInt(const std::wstring &str) { return ConvertStrToHelper<signed int>(str); }
unsigned int ConvertStrToUnsignedInt(const std::wstring &str) { return ConvertStrToHelper<unsigned int>(str); }
signed long ConvertStrToSignedLong(const std::wstring &str) { return ConvertStrToHelper<signed long>(str); }
unsigned long ConvertStrToUnsignedLong(const std::wstring &str) { return ConvertStrToHelper<unsigned long>(str); }
signed long long ConvertStrToSignedLongLong(const std::wstring &str) { return ConvertStrToHelper<signed long long>(str); }
unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str) { return ConvertStrToHelper<unsigned long long>(str); }
float ConvertStrToFloat(const std::wstring &str) { return ConvertStrToHelper<float>(str); }
double ConvertStrToDouble(const std::wstring &str) { return ConvertStrToHelper<double>(str); }
long double ConvertStrToLongDouble(const std::wstring &str) { return ConvertStrToHelper<long double>(str); }
#endif
namespace mpt
{
namespace String
{
namespace Parse
{
template<typename T>
T HexToHelper(const std::string &str)
{
std::istringstream i(str);
i.imbue(std::locale::classic());
T x;
if(!(i >> std::hex >> x))
{
return T();
}
return x;
}
template<> unsigned char HexToHelper(const std::string &str) { return static_cast<unsigned char>(HexToHelper<unsigned int>(str)); }
unsigned char HexToUnsignedChar(const std::string &str) { return HexToHelper<unsigned char>(str); }
unsigned short HexToUnsignedShort(const std::string &str) { return HexToHelper<unsigned short>(str); }
unsigned int HexToUnsignedInt(const std::string &str) { return HexToHelper<unsigned int>(str); }
unsigned long HexToUnsignedLong(const std::string &str) { return HexToHelper<unsigned long>(str); }
unsigned long long HexToUnsignedLongLong(const std::string &str) { return HexToHelper<unsigned long long>(str); }
} // namespace Parse
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,196 @@
/*
* mptStringParse.h
* ----------------
* Purpose: Convert strings to other types.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
bool ConvertStrToBool(const std::string &str);
signed char ConvertStrToSignedChar(const std::string &str);
unsigned char ConvertStrToUnsignedChar(const std::string &str);
signed short ConvertStrToSignedShort(const std::string &str);
unsigned short ConvertStrToUnsignedShort(const std::string &str);
signed int ConvertStrToSignedInt(const std::string &str);
unsigned int ConvertStrToUnsignedInt(const std::string &str);
signed long ConvertStrToSignedLong(const std::string &str);
unsigned long ConvertStrToUnsignedLong(const std::string &str);
signed long long ConvertStrToSignedLongLong(const std::string &str);
unsigned long long ConvertStrToUnsignedLongLong(const std::string &str);
float ConvertStrToFloat(const std::string &str);
double ConvertStrToDouble(const std::string &str);
long double ConvertStrToLongDouble(const std::string &str);
template<typename T> inline T ConvertStrTo(const std::string &str); // not defined, generates compiler error for non-specialized types
template<> inline std::string ConvertStrTo(const std::string &str) { return str; }
template<> inline bool ConvertStrTo(const std::string &str) { return ConvertStrToBool(str); }
template<> inline signed char ConvertStrTo(const std::string &str) { return ConvertStrToSignedChar(str); }
template<> inline unsigned char ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedChar(str); }
template<> inline signed short ConvertStrTo(const std::string &str) { return ConvertStrToSignedShort(str); }
template<> inline unsigned short ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedShort(str); }
template<> inline signed int ConvertStrTo(const std::string &str) { return ConvertStrToSignedInt(str); }
template<> inline unsigned int ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedInt(str); }
template<> inline signed long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLong(str); }
template<> inline unsigned long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLong(str); }
template<> inline signed long long ConvertStrTo(const std::string &str) { return ConvertStrToSignedLongLong(str); }
template<> inline unsigned long long ConvertStrTo(const std::string &str) { return ConvertStrToUnsignedLongLong(str); }
template<> inline float ConvertStrTo(const std::string &str) { return ConvertStrToFloat(str); }
template<> inline double ConvertStrTo(const std::string &str) { return ConvertStrToDouble(str); }
template<> inline long double ConvertStrTo(const std::string &str) { return ConvertStrToLongDouble(str); }
#if MPT_WSTRING_FORMAT
bool ConvertStrToBool(const std::wstring &str);
signed char ConvertStrToSignedChar(const std::wstring &str);
unsigned char ConvertStrToUnsignedChar(const std::wstring &str);
signed short ConvertStrToSignedShort(const std::wstring &str);
unsigned short ConvertStrToUnsignedShort(const std::wstring &str);
signed int ConvertStrToSignedInt(const std::wstring &str);
unsigned int ConvertStrToUnsignedInt(const std::wstring &str);
signed long ConvertStrToSignedLong(const std::wstring &str);
unsigned long ConvertStrToUnsignedLong(const std::wstring &str);
signed long long ConvertStrToSignedLongLong(const std::wstring &str);
unsigned long long ConvertStrToUnsignedLongLong(const std::wstring &str);
float ConvertStrToFloat(const std::wstring &str);
double ConvertStrToDouble(const std::wstring &str);
long double ConvertStrToLongDouble(const std::wstring &str);
template<typename T> inline T ConvertStrTo(const std::wstring &str); // not defined, generates compiler error for non-specialized types
template<> inline std::wstring ConvertStrTo(const std::wstring &str) { return str; }
template<> inline bool ConvertStrTo(const std::wstring &str) { return ConvertStrToBool(str); }
template<> inline signed char ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedChar(str); }
template<> inline unsigned char ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedChar(str); }
template<> inline signed short ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedShort(str); }
template<> inline unsigned short ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedShort(str); }
template<> inline signed int ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedInt(str); }
template<> inline unsigned int ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedInt(str); }
template<> inline signed long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLong(str); }
template<> inline unsigned long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLong(str); }
template<> inline signed long long ConvertStrTo(const std::wstring &str) { return ConvertStrToSignedLongLong(str); }
template<> inline unsigned long long ConvertStrTo(const std::wstring &str) { return ConvertStrToUnsignedLongLong(str); }
template<> inline float ConvertStrTo(const std::wstring &str) { return ConvertStrToFloat(str); }
template<> inline double ConvertStrTo(const std::wstring &str) { return ConvertStrToDouble(str); }
template<> inline long double ConvertStrTo(const std::wstring &str) { return ConvertStrToLongDouble(str); }
#endif
#if defined(_MFC_VER)
template<typename T>
inline T ConvertStrTo(const CString &str)
{
#if defined(UNICODE) && MPT_WSTRING_FORMAT
return ConvertStrTo<T>(mpt::ToWide(str));
#elif defined(UNICODE)
return ConvertStrTo<T>(mpt::ToCharset(mpt::CharsetUTF8, str));
#else // !UNICODE
return ConvertStrTo<T>(mpt::ToCharset(mpt::CharsetLocale, str));
#endif // UNICODE
}
#endif // _MFC_VER
template<typename T>
inline T ConvertStrTo(const char *str)
{
if(!str)
{
return T();
}
return ConvertStrTo<T>(std::string(str));
}
#if MPT_WSTRING_FORMAT
#if MPT_USTRING_MODE_UTF8
template<> inline mpt::ustring ConvertStrTo(const std::wstring &str) { return mpt::ToUnicode(str); }
#endif
template<typename T>
inline T ConvertStrTo(const wchar_t *str)
{
if(!str)
{
return T();
}
return ConvertStrTo<T>(std::wstring(str));
}
#endif
#if MPT_USTRING_MODE_UTF8
template<typename T>
inline T ConvertStrTo(const mpt::ustring &str)
{
return ConvertStrTo<T>(mpt::ToCharset(mpt::CharsetUTF8, str));
}
template<> inline mpt::ustring ConvertStrTo(const mpt::ustring &str) { return str; }
#if MPT_WSTRING_CONVERT
template<> inline std::wstring ConvertStrTo(const mpt::ustring &str) { return mpt::ToWide(str); }
#endif
#endif
namespace mpt
{
namespace String
{
namespace Parse
{
unsigned char HexToUnsignedChar(const std::string &str);
unsigned short HexToUnsignedShort(const std::string &str);
unsigned int HexToUnsignedInt(const std::string &str);
unsigned long HexToUnsignedLong(const std::string &str);
unsigned long long HexToUnsignedLongLong(const std::string &str);
template<typename T> inline T Hex(const std::string &str); // not defined, generates compiler error for non-specialized types
template<> inline unsigned char Hex(const std::string &str) { return HexToUnsignedChar(str); }
template<> inline unsigned short Hex(const std::string &str) { return HexToUnsignedShort(str); }
template<> inline unsigned int Hex(const std::string &str) { return HexToUnsignedInt(str); }
template<> inline unsigned long Hex(const std::string &str) { return HexToUnsignedLong(str); }
template<> inline unsigned long long Hex(const std::string &str) { return HexToUnsignedLongLong(str); }
template<typename T>
inline T Hex(const char *str)
{
if(!str)
{
return T();
}
return Hex<T>(std::string(str));
}
#if MPT_WSTRING_FORMAT
template<typename T>
inline T Hex(const std::wstring &str)
{
return Hex<T>(mpt::ToCharset(mpt::CharsetUTF8, str));
}
template<typename T>
inline T Hex(const wchar_t *str)
{
if(!str)
{
return T();
}
return Hex<T>(std::wstring(str));
}
#endif
#if MPT_USTRING_MODE_UTF8
template<typename T>
inline T Hex(const mpt::ustring &str)
{
return Hex<T>(mpt::ToCharset(mpt::CharsetUTF8, str));
}
#endif
} // namespace Parse
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,460 @@
/*
* mptThread.h
* -----------
* Purpose: Helper class for running threads, with a more or less platform-independent interface.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#if defined(MPT_ENABLE_THREAD)
#include <vector> // some C++ header in order to have the C++ standard library version information available
#if defined(MPT_QUIRK_NO_CPP_THREAD)
#define MPT_STD_THREAD 0
#elif MPT_COMPILER_GENERIC
#define MPT_STD_THREAD 1
#elif MPT_COMPILER_MSVC
#define MPT_STD_THREAD 1
#elif MPT_COMPILER_GCC && !MPT_OS_WINDOWS
#define MPT_STD_THREAD 1
#elif MPT_COMPILER_CLANG && defined(__GLIBCXX__)
#define MPT_STD_THREAD 1
#elif (MPT_OS_MACOSX_OR_IOS || MPT_OS_FREEBSD) && MPT_COMPILER_CLANG
#define MPT_STD_THREAD 1
#elif MPT_CLANG_AT_LEAST(3,6,0) && defined(_LIBCPP_VERSION)
#define MPT_STD_THREAD 1
#else
#define MPT_STD_THREAD 0
#endif
#if MPT_STD_THREAD
#include <thread>
#else // !MPT_STD_THREAD
#if MPT_OS_WINDOWS
#include <windows.h>
#else // !MPT_OS_WINDOWS
#include <pthread.h>
#endif // MPT_OS_WINDOWS
#endif // MPT_STD_THREAD
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#endif // MPT_ENABLE_THREAD
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_THREAD)
namespace mpt
{
#if MPT_STD_THREAD
typedef std::thread::native_handle_type native_handle_type;
typedef std::thread thread;
#else // !MPT_STD_THREAD
#if MPT_OS_WINDOWS
typedef HANDLE native_handle_type;
// std::thread
// NOTE: This implementation is not movable and prevents copying.
// Therefore, it is not as versatile as a full C++11 std::thread implementation.
// It is only a strict subset.
class thread
{
private:
thread(const thread &) = delete;
thread & operator = (const thread &) = delete;
private:
class functor_helper_base {
protected:
functor_helper_base() {}
public:
virtual ~functor_helper_base() {}
public:
virtual void operator () () = 0;
};
template<typename Tfunc>
class functor_helper : public functor_helper_base {
private:
Tfunc func;
public:
functor_helper(Tfunc func_) : func(func_) { return; }
virtual ~functor_helper() { return; }
virtual void operator () () { func(); }
};
enum FunctionMode
{
FunctionModeNone = 0,
FunctionModeParamNone = 1,
FunctionModeParamPointer = 2,
FunctionModeFunctor = 3,
};
native_handle_type threadHandle;
// Thread startup accesses members of mpt::thread.
// If the mpt::thread instanced gets detached and destroyed directly after initialization,
// there is a race between thread startup and detach/destroy.
// startupDoneEvent protects against this race.
HANDLE startupDoneEvent;
FunctionMode functionMode;
union {
struct {
void (*function)(void);
} ModeParamNone;
struct {
void (*function)(void*);
void * userdata;
} ModeParamPointer;
struct {
functor_helper_base * pfunctor;
} ModeFunctor;
} modeState;
private:
uintptr_t ThreadFuntion()
{
switch(functionMode)
{
case FunctionModeNone:
SetEvent(startupDoneEvent);
return 0;
break;
case FunctionModeParamNone:
{
void (*f)(void) = modeState.ModeParamNone.function;
SetEvent(startupDoneEvent);
f();
}
return 0;
break;
case FunctionModeParamPointer:
{
void (*f)(void*) = modeState.ModeParamPointer.function;
void * d = modeState.ModeParamPointer.userdata;
SetEvent(startupDoneEvent);
f(d);
}
return 0;
break;
case FunctionModeFunctor:
{
functor_helper_base * pf = modeState.ModeFunctor.pfunctor;
SetEvent(startupDoneEvent);
(*pf)();
delete pf;
}
return 0;
break;
default:
SetEvent(startupDoneEvent);
return 0;
break;
}
SetEvent(startupDoneEvent);
return 0;
}
static DWORD WINAPI ThreadFunctionWrapper(LPVOID param)
{
reinterpret_cast<mpt::thread*>(param)->ThreadFuntion();
return 0;
}
public:
mpt::native_handle_type native_handle()
{
return threadHandle;
}
bool joinable() const
{
return (threadHandle != nullptr);
}
void join()
{
if(!joinable())
{
throw std::invalid_argument("thread::joinable() == false");
}
WaitForSingleObject(threadHandle, INFINITE);
CloseHandle(threadHandle);
threadHandle = nullptr;
}
void detach()
{
if(!joinable())
{
throw std::invalid_argument("thread::joinable() == false");
}
CloseHandle(threadHandle);
threadHandle = nullptr;
}
void swap(thread & other) noexcept
{
using std::swap;
swap(threadHandle, other.threadHandle);
swap(startupDoneEvent, other.startupDoneEvent);
swap(functionMode, other.functionMode);
}
friend void swap(thread & a, thread & b) noexcept
{
a.swap(b);
}
thread(thread && other) noexcept
: threadHandle(nullptr)
, startupDoneEvent(nullptr)
, functionMode(FunctionModeNone)
{
swap(other);
}
thread & operator=(thread && other) noexcept
{
if(joinable())
{
std::terminate();
}
swap(other);
return *this;
}
thread()
: threadHandle(nullptr)
, startupDoneEvent(nullptr)
, functionMode(FunctionModeNone)
{
std::memset(&modeState, 0, sizeof(modeState));
}
thread(void (*function)(void))
: threadHandle(nullptr)
, startupDoneEvent(nullptr)
, functionMode(FunctionModeParamNone)
{
std::memset(&modeState, 0, sizeof(modeState));
modeState.ModeParamNone.function = function;
startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); }
DWORD dummy = 0; // For Win9x
threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy);
if(threadHandle)
{
WaitForSingleObject(startupDoneEvent, INFINITE);
}
CloseHandle(startupDoneEvent);
startupDoneEvent = nullptr;
if(!threadHandle) { throw std::runtime_error("unable to start thread"); }
}
thread(void (*function)(void*), void * userdata)
: threadHandle(nullptr)
, startupDoneEvent(nullptr)
, functionMode(FunctionModeParamPointer)
{
std::memset(&modeState, 0, sizeof(modeState));
modeState.ModeParamPointer.function = function;
modeState.ModeParamPointer.userdata = userdata;
startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); }
DWORD dummy = 0; // For Win9x
threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy);
if(threadHandle)
{
WaitForSingleObject(startupDoneEvent, INFINITE);
}
CloseHandle(startupDoneEvent);
startupDoneEvent = nullptr;
if(!threadHandle) { throw std::runtime_error("unable to start thread"); }
}
template<typename Tfunctor>
thread(Tfunctor functor)
: threadHandle(nullptr)
, startupDoneEvent(nullptr)
, functionMode(FunctionModeFunctor)
{
std::memset(&modeState, 0, sizeof(modeState));
modeState.ModeFunctor.pfunctor = new functor_helper<Tfunctor>(functor);
startupDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if(!startupDoneEvent) { throw std::runtime_error("unable to start thread"); }
DWORD dummy = 0; // For Win9x
threadHandle = CreateThread(NULL, 0, ThreadFunctionWrapper, this, 0, &dummy);
if(threadHandle)
{
WaitForSingleObject(startupDoneEvent, INFINITE);
}
CloseHandle(startupDoneEvent);
startupDoneEvent = nullptr;
if(!threadHandle) { throw std::runtime_error("unable to start thread"); }
}
~thread()
{
MPT_ASSERT(!joinable());
}
public:
static unsigned int hardware_concurrency()
{
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
return std::max<unsigned int>(sysInfo.dwNumberOfProcessors, 1);
}
};
#endif // MPT_OS_WINDOWS
#endif // MPT_STD_THREAD
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
enum ThreadPriority
{
ThreadPriorityLowest = THREAD_PRIORITY_LOWEST,
ThreadPriorityLower = THREAD_PRIORITY_BELOW_NORMAL,
ThreadPriorityNormal = THREAD_PRIORITY_NORMAL,
ThreadPriorityHigh = THREAD_PRIORITY_ABOVE_NORMAL,
ThreadPriorityHighest = THREAD_PRIORITY_HIGHEST
};
inline void SetThreadPriority(mpt::thread &t, mpt::ThreadPriority priority)
{
::SetThreadPriority(t.native_handle(), priority);
}
inline void SetCurrentThreadPriority(mpt::ThreadPriority priority)
{
::SetThreadPriority(GetCurrentThread(), priority);
}
#else // !MPT_OS_WINDOWS
enum ThreadPriority
{
ThreadPriorityLowest = -2,
ThreadPriorityLower = -1,
ThreadPriorityNormal = 0,
ThreadPriorityHigh = 1,
ThreadPriorityHighest = 2
};
inline void SetThreadPriority(mpt::thread & /*t*/ , mpt::ThreadPriority /*priority*/ )
{
// nothing
}
inline void SetCurrentThreadPriority(mpt::ThreadPriority /*priority*/ )
{
// nothing
}
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
// Default WinAPI thread
class UnmanagedThread
{
protected:
HANDLE threadHandle;
public:
operator HANDLE& () { return threadHandle; }
operator bool () const { return threadHandle != nullptr; }
UnmanagedThread() : threadHandle(nullptr) { }
UnmanagedThread(LPTHREAD_START_ROUTINE function, void *userData = nullptr)
{
DWORD dummy = 0; // For Win9x
threadHandle = CreateThread(NULL, 0, function, userData, 0, &dummy);
}
};
// Thread that operates on a member function
template<typename T, void (T::*Fun)()>
class UnmanagedThreadMember : public mpt::UnmanagedThread
{
protected:
static DWORD WINAPI wrapperFunc(LPVOID param)
{
(static_cast<T *>(param)->*Fun)();
return 0;
}
public:
UnmanagedThreadMember(T *instance) : mpt::UnmanagedThread(wrapperFunc, instance) { }
};
inline void SetThreadPriority(mpt::UnmanagedThread &t, mpt::ThreadPriority priority)
{
::SetThreadPriority(t, priority);
}
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
} // namespace mpt
#endif // MPT_ENABLE_THREAD
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,307 @@
/*
* mptTime.cpp
* -----------
* Purpose: Various time utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptTime.h"
#include <time.h>
#if MPT_OS_WINDOWS
#include <windows.h>
#if defined(MODPLUG_TRACKER)
#include <mmsystem.h>
#endif
#endif
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace Date
{
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
namespace ANSI
{
uint64 Now()
{
FILETIME filetime;
GetSystemTimeAsFileTime(&filetime);
return ((uint64)filetime.dwHighDateTime << 32 | filetime.dwLowDateTime);
}
mpt::ustring ToString(uint64 time100ns)
{
static const std::size_t bufsize = 256;
mpt::ustring result;
FILETIME filetime;
SYSTEMTIME systime;
filetime.dwHighDateTime = (DWORD)(((uint64)time100ns) >> 32);
filetime.dwLowDateTime = (DWORD)((uint64)time100ns);
FileTimeToSystemTime(&filetime, &systime);
WCHAR buf[bufsize];
GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &systime, L"yyyy-MM-dd", buf, bufsize);
result.append(mpt::ToUnicode(buf));
result.append(MPT_USTRING(" "));
GetTimeFormatW(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, L"HH:mm:ss", buf, bufsize);
result.append(mpt::ToUnicode(buf));
result.append(MPT_USTRING("."));
result.append(mpt::ufmt::dec0<3>((unsigned)systime.wMilliseconds));
return result;
}
} // namespace ANSI
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
Unix::Unix()
: Value(0)
{
return;
}
Unix::Unix(int64 unixtime)
: Value(unixtime)
{
return;
}
Unix::operator int64 () const
{
return Value;
}
static int32 ToDaynum(int32 year, int32 month, int32 day)
{
month = (month + 9) % 12;
year = year - (month / 10);
int32 daynum = year*365 + year/4 - year/100 + year/400 + (month*306 + 5)/10 + (day - 1);
return daynum;
}
static void FromDaynum(int32 d, int32 & year, int32 & month, int32 & day)
{
int64 g = d;
int64 y,ddd,mi,mm,dd;
y = (10000*g + 14780)/3652425;
ddd = g - (365*y + y/4 - y/100 + y/400);
if(ddd < 0)
{
y = y - 1;
ddd = g - (365*y + y/4 - y/100 + y/400);
}
mi = (100*ddd + 52)/3060;
mm = (mi + 2)%12 + 1;
y = y + (mi + 2)/12;
dd = ddd - (mi*306 + 5)/10 + 1;
year = static_cast<int32>(y);
month = static_cast<int32>(mm);
day = static_cast<int32>(dd);
}
mpt::Date::Unix Unix::FromUTC(tm timeUtc)
{
int32 daynum = ToDaynum(timeUtc.tm_year+1900, timeUtc.tm_mon+1, timeUtc.tm_mday);
int64 seconds = static_cast<int64>(daynum - ToDaynum(1970,1,1))*24*60*60 + timeUtc.tm_hour*60*60 + timeUtc.tm_min*60 + timeUtc.tm_sec;
return mpt::Date::Unix(seconds);
}
tm Unix::AsUTC() const
{
int64 tmp = Value;
int64 seconds = tmp % 60; tmp /= 60;
int64 minutes = tmp % 60; tmp /= 60;
int64 hours = tmp % 24; tmp /= 24;
int32 year = 0, month = 0, day = 0;
FromDaynum(static_cast<int32>(tmp) + ToDaynum(1970,1,1), year, month, day);
tm result;
MemsetZero(result);
result.tm_year = year - 1900;
result.tm_mon = month - 1;
result.tm_mday = day;
result.tm_hour = static_cast<int32>(hours);
result.tm_min = static_cast<int32>(minutes);
result.tm_sec = static_cast<int32>(seconds);
return result;
}
mpt::ustring ToShortenedISO8601(tm date)
{
// We assume date in UTC here.
// There are too many differences in supported format specifiers in strftime()
// and strftime does not support reduced precision ISO8601 at all.
// Just do the formatting ourselves.
mpt::ustring result;
mpt::ustring tz = MPT_USTRING("Z");
if(date.tm_year == 0)
{
return result;
}
result += mpt::ufmt::dec0<4>(date.tm_year + 1900);
if(date.tm_mon < 0 || date.tm_mon > 11)
{
return result;
}
result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1);
if(date.tm_mday < 1 || date.tm_mday > 31)
{
return result;
}
result += MPT_USTRING("-") + mpt::ufmt::dec0<2>(date.tm_mday);
if(date.tm_hour == 0 && date.tm_min == 0 && date.tm_sec == 0)
{
return result;
}
if(date.tm_hour < 0 || date.tm_hour > 23)
{
return result;
}
if(date.tm_min < 0 || date.tm_min > 59)
{
return result;
}
result += MPT_USTRING("T");
if(date.tm_isdst > 0)
{
tz = MPT_USTRING("+01:00");
}
result += mpt::ufmt::dec0<2>(date.tm_hour) + MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_min);
if(date.tm_sec < 0 || date.tm_sec > 61)
{
return result + tz;
}
result += MPT_USTRING(":") + mpt::ufmt::dec0<2>(date.tm_sec);
result += tz;
return result;
}
} // namespace Date
} // namespace mpt
#ifdef MODPLUG_TRACKER
namespace Util
{
#if MPT_OS_WINDOWS
void MultimediaClock::Init()
{
m_CurrentPeriod = 0;
}
void MultimediaClock::SetPeriod(uint32 ms)
{
TIMECAPS caps;
MemsetZero(caps);
if(timeGetDevCaps(&caps, sizeof(caps)) != MMSYSERR_NOERROR)
{
return;
}
if((caps.wPeriodMax == 0) || (caps.wPeriodMin > caps.wPeriodMax))
{
return;
}
ms = mpt::clamp<uint32>(ms, caps.wPeriodMin, caps.wPeriodMax);
if(timeBeginPeriod(ms) != MMSYSERR_NOERROR)
{
return;
}
m_CurrentPeriod = ms;
}
void MultimediaClock::Cleanup()
{
if(m_CurrentPeriod > 0)
{
if(timeEndPeriod(m_CurrentPeriod) != MMSYSERR_NOERROR)
{
// should not happen
MPT_ASSERT_NOTREACHED();
}
m_CurrentPeriod = 0;
}
}
MultimediaClock::MultimediaClock()
{
Init();
}
MultimediaClock::MultimediaClock(uint32 ms)
{
Init();
SetResolution(ms);
}
MultimediaClock::~MultimediaClock()
{
Cleanup();
}
uint32 MultimediaClock::SetResolution(uint32 ms)
{
if(m_CurrentPeriod == ms)
{
return m_CurrentPeriod;
}
Cleanup();
if(ms != 0)
{
SetPeriod(ms);
}
return GetResolution();
}
uint32 MultimediaClock::GetResolution() const
{
return m_CurrentPeriod;
}
uint32 MultimediaClock::Now() const
{
return timeGetTime();
}
uint64 MultimediaClock::NowNanoseconds() const
{
return (uint64)timeGetTime() * (uint64)1000000;
}
#endif // MPT_OS_WINDOWS
} // namespace Util
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,114 @@
/*
* mptTime.h
* ---------
* Purpose: Various time utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <string>
#include <time.h>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace Date
{
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
namespace ANSI
{
// uint64 counts 100ns since 1601-01-01T00:00Z
uint64 Now();
mpt::ustring ToString(uint64 time100ns); // i.e. 2015-01-15 18:32:01.718
} // namespacee ANSI
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
class Unix
{
// int64 counts 1s since 1970-01-01T00:00Z
private:
int64 Value;
public:
Unix();
explicit Unix(int64 unixtime);
operator int64 () const;
public:
static mpt::Date::Unix FromUTC(tm timeUtc);
tm AsUTC() const;
};
mpt::ustring ToShortenedISO8601(tm date); // i.e. 2015-01-15T18:32:01Z
} // namespace Date
} // namespace mpt
#ifdef MODPLUG_TRACKER
namespace Util
{
#if MPT_OS_WINDOWS
// RAII wrapper around timeBeginPeriod/timeEndPeriod/timeGetTime (on Windows).
// This clock is monotonic, even across changing its resolution.
// This is needed to synchronize time in Steinberg APIs (ASIO and VST).
class MultimediaClock
{
private:
uint32 m_CurrentPeriod;
private:
void Init();
void SetPeriod(uint32 ms);
void Cleanup();
public:
MultimediaClock();
MultimediaClock(uint32 ms);
~MultimediaClock();
public:
// Sets the desired resolution in milliseconds, returns the obtained resolution in milliseconds.
// A parameter of 0 causes the resolution to be reset to system defaults.
// A return value of 0 means the resolution is unknown, but timestamps will still be valid.
uint32 SetResolution(uint32 ms);
// Returns obtained resolution in milliseconds.
// A return value of 0 means the resolution is unknown, but timestamps will still be valid.
uint32 GetResolution() const;
// Returns current instantaneous timestamp in milliseconds.
// The epoch (offset) of the timestamps is undefined but constant until the next system reboot.
// The resolution is the value returned from GetResolution().
uint32 Now() const;
// Returns current instantaneous timestamp in nanoseconds.
// The epoch (offset) of the timestamps is undefined but constant until the next system reboot.
// The resolution is the value returned from GetResolution() in milliseconds.
uint64 NowNanoseconds() const;
};
#endif // MPT_OS_WINDOWS
} // namespace Util
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,142 @@
/*
* mptTypeTraits.h
* ---------------
* Purpose: C++11 similar type_traits header plus some OpenMPT specific traits.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include <type_traits>
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
template <std::size_t size> struct int_of_size { };
template <> struct int_of_size<1> { typedef int8 type; };
template <> struct int_of_size<2> { typedef int16 type; };
template <> struct int_of_size<3> { typedef int32 type; };
template <> struct int_of_size<4> { typedef int32 type; };
template <> struct int_of_size<5> { typedef int64 type; };
template <> struct int_of_size<6> { typedef int64 type; };
template <> struct int_of_size<7> { typedef int64 type; };
template <> struct int_of_size<8> { typedef int64 type; };
template <std::size_t size> struct uint_of_size { };
template <> struct uint_of_size<1> { typedef uint8 type; };
template <> struct uint_of_size<2> { typedef uint16 type; };
template <> struct uint_of_size<3> { typedef uint32 type; };
template <> struct uint_of_size<4> { typedef uint32 type; };
template <> struct uint_of_size<5> { typedef uint64 type; };
template <> struct uint_of_size<6> { typedef uint64 type; };
template <> struct uint_of_size<7> { typedef uint64 type; };
template <> struct uint_of_size<8> { typedef uint64 type; };
// Tell which types are safe for mpt::byte_cast.
// signed char is actually not allowed to alias into an object representation,
// which means that, if the actual type is not itself signed char but char or
// unsigned char instead, dereferencing the signed char pointer is undefined
// behaviour.
template <typename T> struct is_byte_castable : public std::false_type { };
template <> struct is_byte_castable<char> : public std::true_type { };
template <> struct is_byte_castable<unsigned char> : public std::true_type { };
template <> struct is_byte_castable<const char> : public std::true_type { };
template <> struct is_byte_castable<const unsigned char> : public std::true_type { };
// Tell which types are safe to binary write into files.
// By default, no types are safe.
// When a safe type gets defined,
// also specialize this template so that IO functions will work.
template <typename T> struct is_binary_safe : public std::false_type { };
// Specialization for byte types.
template <> struct is_binary_safe<char> : public std::true_type { };
template <> struct is_binary_safe<uint8> : public std::true_type { };
template <> struct is_binary_safe<int8> : public std::true_type { };
// Generic Specialization for arrays.
template <typename T, std::size_t N> struct is_binary_safe<T[N]> : public is_binary_safe<T> { };
template <typename T, std::size_t N> struct is_binary_safe<const T[N]> : public is_binary_safe<T> { };
template <typename T>
struct GetRawBytesFunctor
{
inline const mpt::byte * operator () (const T & v) const
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return reinterpret_cast<const mpt::byte *>(&v);
}
inline mpt::byte * operator () (T & v) const
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return reinterpret_cast<mpt::byte *>(&v);
}
};
template <typename T, std::size_t N>
struct GetRawBytesFunctor<T[N]>
{
inline const mpt::byte * operator () (const T (&v)[N]) const
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return reinterpret_cast<const mpt::byte *>(v);
}
inline mpt::byte * operator () (T (&v)[N]) const
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return reinterpret_cast<mpt::byte *>(v);
}
};
template <typename T, std::size_t N>
struct GetRawBytesFunctor<const T[N]>
{
inline const mpt::byte * operator () (const T (&v)[N]) const
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return reinterpret_cast<const mpt::byte *>(v);
}
};
// In order to be able to partially specialize it,
// as_raw_memory is implemented via a class template.
// Do not overload or specialize as_raw_memory directly.
// Using a wrapper (by default just around a cast to const mpt::byte *),
// allows for implementing raw memory access
// via on-demand generating a cached serialized representation.
template <typename T> inline const mpt::byte * as_raw_memory(const T & v)
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::GetRawBytesFunctor<T>()(v);
}
template <typename T> inline mpt::byte * as_raw_memory(T & v)
{
STATIC_ASSERT(mpt::is_binary_safe<typename std::remove_const<T>::type>::value);
return mpt::GetRawBytesFunctor<T>()(v);
}
} // namespace mpt
#define MPT_BINARY_STRUCT(type, size) \
MPT_STATIC_ASSERT(sizeof( type ) == (size) ); \
MPT_STATIC_ASSERT(alignof( type ) == 1); \
MPT_STATIC_ASSERT(std::is_standard_layout< type >::value); \
namespace mpt { \
template <> struct is_binary_safe< type > : public std::true_type { }; \
} \
/**/
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,756 @@
/*
* mptUUID.cpp
* -----------
* Purpose: UUID utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptUUID.h"
#include "mptRandom.h"
#include "mptStringFormat.h"
#include "Endianness.h"
#include <cstdlib>
#if MPT_OS_WINDOWS
#include <windows.h>
#include <rpc.h>
#if defined(MODPLUG_TRACKER) || !defined(NO_DMO) || MPT_OS_WINDOWS_WINRT
#include <objbase.h>
#endif // MODPLUG_TRACKER || !NO_DMO || MPT_OS_WINDOWS_WINRT
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_BEGIN
#if MPT_OS_WINDOWS
namespace Util
{
#if defined(MODPLUG_TRACKER) || !defined(NO_DMO)
std::wstring CLSIDToString(CLSID clsid)
{
std::wstring str;
LPOLESTR tmp = nullptr;
switch(::StringFromCLSID(clsid, &tmp))
{
case S_OK:
break;
case E_OUTOFMEMORY:
if(tmp)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
}
MPT_EXCEPTION_THROW_OUT_OF_MEMORY();
break;
default:
if(tmp)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
}
throw std::logic_error("StringFromCLSID() failed.");
break;
}
if(!tmp)
{
throw std::logic_error("StringFromCLSID() failed.");
}
try
{
str = tmp;
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
MPT_UNUSED_VARIABLE(e);
MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY();
}
::CoTaskMemFree(tmp);
tmp = nullptr;
return str;
}
CLSID StringToCLSID(const std::wstring &str)
{
CLSID clsid = CLSID();
std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
switch(::CLSIDFromString(tmp.data(), &clsid))
{
case NOERROR:
// nothing
break;
case E_INVALIDARG:
clsid = CLSID();
break;
case CO_E_CLASSSTRING:
clsid = CLSID();
break;
case REGDB_E_CLASSNOTREG:
clsid = CLSID();
break;
case REGDB_E_READREGDB:
clsid = CLSID();
throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
break;
default:
clsid = CLSID();
throw std::logic_error("CLSIDFromString() failed.");
break;
}
return clsid;
}
bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid)
{
bool result = false;
clsid = CLSID();
std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
switch(::CLSIDFromString(tmp.data(), &clsid))
{
case NOERROR:
result = true;
break;
case E_INVALIDARG:
result = false;
break;
case CO_E_CLASSSTRING:
result = false;
break;
case REGDB_E_CLASSNOTREG:
result = false;
break;
case REGDB_E_READREGDB:
throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
break;
default:
throw std::logic_error("CLSIDFromString() failed.");
break;
}
return result;
}
bool IsCLSID(const std::wstring &str)
{
bool result = false;
CLSID clsid = CLSID();
std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
switch(::CLSIDFromString(tmp.data(), &clsid))
{
case NOERROR:
result = true;
break;
case E_INVALIDARG:
result = false;
break;
case CO_E_CLASSSTRING:
result = false;
break;
case REGDB_E_CLASSNOTREG:
result = false;
break;
case REGDB_E_READREGDB:
result = false;
throw std::runtime_error("CLSIDFromString() failed: REGDB_E_READREGDB.");
break;
default:
result = false;
throw std::logic_error("CLSIDFromString() failed.");
break;
}
return result;
}
std::wstring IIDToString(IID iid)
{
std::wstring str;
LPOLESTR tmp = nullptr;
switch(::StringFromIID(iid, &tmp))
{
case S_OK:
break;
case E_OUTOFMEMORY:
if(tmp)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
}
MPT_EXCEPTION_THROW_OUT_OF_MEMORY();
break;
default:
if(tmp)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
}
throw std::logic_error("StringFromIID() failed.");
break;
}
if(!tmp)
{
throw std::logic_error("StringFromIID() failed.");
}
try
{
str = tmp;
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
::CoTaskMemFree(tmp);
tmp = nullptr;
MPT_UNUSED_VARIABLE(e);
MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY();
}
return str;
}
IID StringToIID(const std::wstring &str)
{
IID iid = IID();
std::vector<OLECHAR> tmp(str.c_str(), str.c_str() + str.length() + 1);
switch(::IIDFromString(tmp.data(), &iid))
{
case S_OK:
// nothing
break;
case E_OUTOFMEMORY:
iid = IID();
MPT_EXCEPTION_THROW_OUT_OF_MEMORY();
break;
case E_INVALIDARG:
iid = IID();
break;
default:
iid = IID();
throw std::logic_error("IIDFromString() failed.");
break;
}
return iid;
}
std::wstring GUIDToString(GUID guid)
{
std::vector<OLECHAR> tmp(256);
if(::StringFromGUID2(guid, tmp.data(), static_cast<int>(tmp.size())) <= 0)
{
throw std::logic_error("StringFromGUID2() failed.");
}
return tmp.data();
}
GUID StringToGUID(const std::wstring &str)
{
return StringToIID(str);
}
GUID CreateGUID()
{
GUID guid = GUID();
switch(::CoCreateGuid(&guid))
{
case S_OK:
// nothing
break;
default:
guid = GUID();
throw std::runtime_error("CoCreateGuid() failed.");
}
return guid;
}
#if !MPT_OS_WINDOWS_WINRT
UUID StringToUUID(const mpt::ustring &str)
{
UUID uuid = UUID();
std::wstring wstr = mpt::ToWide(str);
std::vector<wchar_t> tmp(wstr.c_str(), wstr.c_str() + wstr.length() + 1);
switch(::UuidFromStringW((RPC_WSTR)(&(tmp[0])), &uuid))
{
case RPC_S_OK:
// nothing
break;
case RPC_S_INVALID_STRING_UUID:
uuid = UUID();
break;
default:
throw std::logic_error("UuidFromStringW() failed.");
break;
}
return uuid;
}
mpt::ustring UUIDToString(UUID uuid)
{
std::wstring wstr;
RPC_WSTR tmp = nullptr;
switch(::UuidToStringW(&uuid, &tmp))
{
case RPC_S_OK:
// nothing
break;
case RPC_S_OUT_OF_MEMORY:
if(tmp)
{
::RpcStringFreeW(&tmp);
tmp = nullptr;
}
MPT_EXCEPTION_THROW_OUT_OF_MEMORY();
break;
default:
throw std::logic_error("UuidToStringW() failed.");
break;
}
try
{
std::size_t len = 0;
for(len = 0; tmp[len] != 0; ++len)
{
// nothing
}
wstr = std::wstring(tmp, tmp + len);
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
::RpcStringFreeW(&tmp);
tmp = nullptr;
MPT_UNUSED_VARIABLE(e);
MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY();
}
return mpt::ToUnicode(wstr);
}
#endif // !MPT_OS_WINDOWS_WINRT
bool IsValid(UUID uuid)
{
return false
|| uuid.Data1 != 0
|| uuid.Data2 != 0
|| uuid.Data3 != 0
|| uuid.Data4[0] != 0
|| uuid.Data4[1] != 0
|| uuid.Data4[2] != 0
|| uuid.Data4[3] != 0
|| uuid.Data4[4] != 0
|| uuid.Data4[5] != 0
|| uuid.Data4[6] != 0
|| uuid.Data4[7] != 0
;
}
#endif // MODPLUG_TRACKER || !NO_DMO
} // namespace Util
#endif // MPT_OS_WINDOWS
namespace mpt
{
#if MPT_OS_WINDOWS
mpt::UUID UUIDFromWin32(::UUID uuid)
{
return mpt::UUID
( uuid.Data1
, uuid.Data2
, uuid.Data3
, (static_cast<uint64>(0)
| (static_cast<uint64>(uuid.Data4[0]) << 56)
| (static_cast<uint64>(uuid.Data4[1]) << 48)
| (static_cast<uint64>(uuid.Data4[2]) << 40)
| (static_cast<uint64>(uuid.Data4[3]) << 32)
| (static_cast<uint64>(uuid.Data4[4]) << 24)
| (static_cast<uint64>(uuid.Data4[5]) << 16)
| (static_cast<uint64>(uuid.Data4[6]) << 8)
| (static_cast<uint64>(uuid.Data4[7]) << 0)
)
);
}
::UUID UUIDToWin32(mpt::UUID uuid)
{
::UUID result = ::UUID();
result.Data1 = uuid.GetData1();
result.Data2 = uuid.GetData2();
result.Data3 = uuid.GetData3();
result.Data4[0] = static_cast<uint8>(uuid.GetData4() >> 56);
result.Data4[1] = static_cast<uint8>(uuid.GetData4() >> 48);
result.Data4[2] = static_cast<uint8>(uuid.GetData4() >> 40);
result.Data4[3] = static_cast<uint8>(uuid.GetData4() >> 32);
result.Data4[4] = static_cast<uint8>(uuid.GetData4() >> 24);
result.Data4[5] = static_cast<uint8>(uuid.GetData4() >> 16);
result.Data4[6] = static_cast<uint8>(uuid.GetData4() >> 8);
result.Data4[7] = static_cast<uint8>(uuid.GetData4() >> 0);
return result;
}
#if defined(MODPLUG_TRACKER) || !defined(NO_DMO)
UUID::UUID(::UUID uuid)
{
*this = UUIDFromWin32(uuid);
}
UUID::operator ::UUID () const
{
return UUIDToWin32(*this);
}
mpt::UUID UUID::FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5)
{
MPT_ASSERT((group5 & 0xffff000000000000ull) == 0ull);
return mpt::UUID
( group1
, group2
, group3
, (static_cast<uint64>(group4) << 48) | group5
);
}
#endif // MODPLUG_TRACKER || !NO_DMO
#endif // MPT_OS_WINDOWS
UUID UUID::Generate()
{
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT >= 0x0602)
::GUID guid = ::GUID();
HRESULT result = CoCreateGuid(&guid);
if(result != S_OK)
{
return mpt::UUID::RFC4122Random();
}
return mpt::UUIDFromWin32(guid);
#else
return mpt::UUID::RFC4122Random();
#endif
#elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
::UUID uuid = ::UUID();
RPC_STATUS status = ::UuidCreate(&uuid);
if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
{
return mpt::UUID::RFC4122Random();
}
status = RPC_S_OK;
if(UuidIsNil(&uuid, &status) != FALSE)
{
return mpt::UUID::RFC4122Random();
}
if(status != RPC_S_OK)
{
return mpt::UUID::RFC4122Random();
}
return mpt::UUIDFromWin32(uuid);
#else
return RFC4122Random();
#endif
}
UUID UUID::GenerateLocalUseOnly()
{
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT >= 0x0602)
::GUID guid = ::GUID();
HRESULT result = CoCreateGuid(&guid);
if(result != S_OK)
{
return mpt::UUID::RFC4122Random();
}
return mpt::UUIDFromWin32(guid);
#else
return mpt::UUID::RFC4122Random();
#endif
#elif MPT_OS_WINDOWS && !MPT_OS_WINDOWS_WINRT
#if _WIN32_WINNT >= 0x0501
// Available since Win2000, but we check for WinXP in order to not use this
// function in Win32old builds. It is not available on some non-fully
// patched Win98SE installs in the wild.
::UUID uuid = ::UUID();
RPC_STATUS status = ::UuidCreateSequential(&uuid);
if(status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY)
{
return Generate();
}
status = RPC_S_OK;
if(UuidIsNil(&uuid, &status) != FALSE)
{
return mpt::UUID::RFC4122Random();
}
if(status != RPC_S_OK)
{
return mpt::UUID::RFC4122Random();
}
return mpt::UUIDFromWin32(uuid);
#else
// Fallback to ::UuidCreate is safe as ::UuidCreateSequential is only a
// tiny performance optimization.
return Generate();
#endif
#else
return RFC4122Random();
#endif
}
UUID UUID::RFC4122Random()
{
UUID result;
mpt::thread_safe_prng<mpt::best_prng> & prng = mpt::global_prng();
result.Data1 = mpt::random<uint32>(prng);
result.Data2 = mpt::random<uint16>(prng);
result.Data3 = mpt::random<uint16>(prng);
result.Data4 = mpt::random<uint64>(prng);
result.MakeRFC4122(4);
return result;
}
uint32 UUID::GetData1() const
{
return Data1;
}
uint16 UUID::GetData2() const
{
return Data2;
}
uint16 UUID::GetData3() const
{
return Data3;
}
uint64 UUID::GetData4() const
{
return Data4;
}
bool UUID::IsNil() const
{
return (Data1 == 0) && (Data2 == 0) && (Data3 == 0) && (Data4 == 0);
}
bool UUID::IsValid() const
{
return (Data1 != 0) || (Data2 != 0) || (Data3 != 0) || (Data4 != 0);
}
uint8 UUID::Mm() const
{
return static_cast<uint8>((Data3 >> 8) & 0xffu);
}
uint8 UUID::Nn() const
{
return static_cast<uint8>((Data4 >> 56) & 0xffu);
}
uint8 UUID::Variant() const
{
return Nn() >> 4u;
}
uint8 UUID::Version() const
{
return Mm() >> 4u;
}
bool UUID::IsRFC4122() const
{
return (Variant() & 0xcu) == 0x8u;
}
void UUID::MakeRFC4122(uint8 version)
{
// variant
uint8 Nn = static_cast<uint8>((Data4 >> 56) & 0xffu);
Data4 &= 0x00ffffffffffffffull;
Nn &= ~(0xc0u);
Nn |= 0x80u;
Data4 |= static_cast<uint64>(Nn) << 56;
// version
version &= 0x0fu;
uint8 Mm = static_cast<uint8>((Data3 >> 8) & 0xffu);
Data3 &= 0x00ffu;
Mm &= ~(0xf0u);
Mm |= (version << 4u);
Data3 |= static_cast<uint16>(Mm) << 8;
}
UUID::UUID()
{
Data1 = 0;
Data2 = 0;
Data3 = 0;
Data4 = 0;
}
UUID::UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4)
{
this->Data1 = Data1;
this->Data2 = Data2;
this->Data3 = Data3;
this->Data4 = Data4;
}
bool operator==(const mpt::UUID & a, const mpt::UUID & b)
{
return (a.Data1 == b.Data1) && (a.Data2 == b.Data2) && (a.Data3 == b.Data3) && (a.Data4 == b.Data4);
}
bool operator!=(const mpt::UUID & a, const mpt::UUID & b)
{
return (a.Data1 != b.Data1) || (a.Data2 != b.Data2) || (a.Data3 != b.Data3) || (a.Data4 != b.Data4);
}
UUID UUID::FromString(const std::string &str)
{
std::vector<std::string> segments = mpt::String::Split<std::string>(str, std::string("-"));
if(segments.size() != 5)
{
return UUID();
}
if(segments[0].length() != 8)
{
return UUID();
}
if(segments[1].length() != 4)
{
return UUID();
}
if(segments[2].length() != 4)
{
return UUID();
}
if(segments[3].length() != 4)
{
return UUID();
}
if(segments[4].length() != 12)
{
return UUID();
}
UUID result;
result.Data1 = mpt::String::Parse::Hex<uint32>(segments[0]);
result.Data2 = mpt::String::Parse::Hex<uint16>(segments[1]);
result.Data3 = mpt::String::Parse::Hex<uint16>(segments[2]);
result.Data4 = mpt::String::Parse::Hex<uint64>(segments[3] + segments[4]);
return result;
}
UUID UUID::FromString(const mpt::ustring &str)
{
std::vector<mpt::ustring> segments = mpt::String::Split<mpt::ustring>(str, MPT_USTRING("-"));
if(segments.size() != 5)
{
return UUID();
}
if(segments[0].length() != 8)
{
return UUID();
}
if(segments[1].length() != 4)
{
return UUID();
}
if(segments[2].length() != 4)
{
return UUID();
}
if(segments[3].length() != 4)
{
return UUID();
}
if(segments[4].length() != 12)
{
return UUID();
}
UUID result;
result.Data1 = mpt::String::Parse::Hex<uint32>(segments[0]);
result.Data2 = mpt::String::Parse::Hex<uint16>(segments[1]);
result.Data3 = mpt::String::Parse::Hex<uint16>(segments[2]);
result.Data4 = mpt::String::Parse::Hex<uint64>(segments[3] + segments[4]);
return result;
}
std::string UUID::ToString() const
{
return std::string()
+ mpt::fmt::hex0<8>(GetData1())
+ std::string("-")
+ mpt::fmt::hex0<4>(GetData2())
+ std::string("-")
+ mpt::fmt::hex0<4>(GetData3())
+ std::string("-")
+ mpt::fmt::hex0<4>(static_cast<uint16>(GetData4() >> 48))
+ std::string("-")
+ mpt::fmt::hex0<4>(static_cast<uint16>(GetData4() >> 32))
+ mpt::fmt::hex0<8>(static_cast<uint32>(GetData4() >> 0))
;
}
mpt::ustring UUID::ToUString() const
{
return mpt::ustring()
+ mpt::ufmt::hex0<8>(GetData1())
+ MPT_USTRING("-")
+ mpt::ufmt::hex0<4>(GetData2())
+ MPT_USTRING("-")
+ mpt::ufmt::hex0<4>(GetData3())
+ MPT_USTRING("-")
+ mpt::ufmt::hex0<4>(static_cast<uint16>(GetData4() >> 48))
+ MPT_USTRING("-")
+ mpt::ufmt::hex0<4>(static_cast<uint16>(GetData4() >> 32))
+ mpt::ufmt::hex0<8>(static_cast<uint32>(GetData4() >> 0))
;
}
UUID::UUID(GUIDms guid)
{
Data1 = guid.Data1.get();
Data2 = guid.Data2.get();
Data3 = guid.Data3.get();
Data4 = guid.Data4.get();
}
UUID::operator GUIDms() const
{
GUIDms result;
result.Data1 = GetData1();
result.Data2 = GetData2();
result.Data3 = GetData3();
result.Data4 = GetData4();
return result;
}
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,146 @@
/*
* mptUUID.h
* ---------
* Purpose: UUID utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "Endianness.h"
#if MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) || !defined(NO_DMO)
#include <guiddef.h>
#include <rpc.h>
#endif // MODPLUG_TRACKER || !NO_DMO
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_BEGIN
#if MPT_OS_WINDOWS
namespace Util
{
#if defined(MODPLUG_TRACKER) || !defined(NO_DMO)
// COM CLSID<->string conversion
// A CLSID string is not necessarily a standard UUID string,
// it might also be a symbolic name for the interface.
// (see CLSIDFromString ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms680589%28v=vs.85%29.aspx ))
std::wstring CLSIDToString(CLSID clsid);
CLSID StringToCLSID(const std::wstring &str);
bool VerifyStringToCLSID(const std::wstring &str, CLSID &clsid);
bool IsCLSID(const std::wstring &str);
// COM IID<->string conversion
IID StringToIID(const std::wstring &str);
std::wstring IIDToString(IID iid);
// General GUID<->string conversion.
// The string must/will be in standard GUID format: {4F9A455D-E7EF-4367-B2F0-0C83A38A5C72}
GUID StringToGUID(const std::wstring &str);
std::wstring GUIDToString(GUID guid);
// Create a COM GUID
GUID CreateGUID();
#if !MPT_OS_WINDOWS_WINRT
// General UUID<->string conversion.
// The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72
UUID StringToUUID(const mpt::ustring &str);
mpt::ustring UUIDToString(UUID uuid);
#endif // !MPT_OS_WINDOWS_WINRT
// Checks the UUID against the NULL UUID. Returns false if it is NULL, true otherwise.
bool IsValid(UUID uuid);
#endif // MODPLUG_TRACKER || !NO_DMO
} // namespace Util
#endif // MPT_OS_WINDOWS
// Microsoft on-disk layout
struct GUIDms
{
uint32le Data1;
uint16le Data2;
uint16le Data3;
uint64be Data4; // yes, big endian here
};
STATIC_ASSERT(sizeof(GUIDms) == 16);
namespace mpt {
struct UUID
{
private:
uint32be Data1;
uint16be Data2;
uint16be Data3;
uint64be Data4;
public:
uint32 GetData1() const;
uint16 GetData2() const;
uint16 GetData3() const;
uint64 GetData4() const;
public:
// xxxxxxxx-xxxx-Mmxx-Nnxx-xxxxxxxxxxxx
// <--32-->-<16>-<16>-<-------64------>
bool IsNil() const;
bool IsValid() const;
uint8 Variant() const;
uint8 Version() const;
bool IsRFC4122() const;
private:
uint8 Mm() const;
uint8 Nn() const;
void MakeRFC4122(uint8 version);
public:
#if MPT_OS_WINDOWS && (defined(MODPLUG_TRACKER) || !defined(NO_DMO))
explicit UUID(::UUID uuid);
operator ::UUID () const;
static UUID FromGroups(uint32 group1, uint16 group2, uint16 group3, uint16 group4, uint64 group5);
#define MPT_UUID_HELPER( prefix , value , suffix ) ( prefix ## value ## suffix )
#define MPT_UUID(group1, group2, group3, group4, group5) mpt::UUID::FromGroups(MPT_UUID_HELPER(0x,group1,u), MPT_UUID_HELPER(0x,group2,u), MPT_UUID_HELPER(0x,group3,u), MPT_UUID_HELPER(0x,group4,u), MPT_UUID_HELPER(0x,group5,ull))
#endif // MPT_OS_WINDOWS && (MODPLUG_TRACKER || !NO_DMO)
public:
UUID();
explicit UUID(uint32 Data1, uint16 Data2, uint16 Data3, uint64 Data4);
explicit UUID(GUIDms guid);
operator GUIDms () const;
friend bool operator==(const mpt::UUID & a, const mpt::UUID & b);
friend bool operator!=(const mpt::UUID & a, const mpt::UUID & b);
public:
// Create a UUID
static UUID Generate();
// Create a UUID that contains local, traceable information.
// Safe for local use. May be faster.
static UUID GenerateLocalUseOnly();
// Create a RFC4122 Random UUID.
static UUID RFC4122Random();
public:
// General UUID<->string conversion.
// The string must/will be in standard UUID format: 4f9a455d-e7ef-4367-b2f0-0c83a38a5c72
static UUID FromString(const std::string &str);
static UUID FromString(const mpt::ustring &str);
std::string ToString() const;
mpt::ustring ToUString() const;
};
STATIC_ASSERT(sizeof(mpt::UUID) == 16);
bool operator==(const mpt::UUID & a, const mpt::UUID & b);
bool operator!=(const mpt::UUID & a, const mpt::UUID & b);
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,766 @@
/*
* mptWine.cpp
* -----------
* Purpose: Wine stuff.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptWine.h"
#include "mptOS.h"
#include "mptFileIO.h"
#include <deque>
#include <map>
#if MPT_OS_WINDOWS
#include <windows.h>
#endif
OPENMPT_NAMESPACE_BEGIN
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
namespace mpt
{
namespace Wine
{
Context::Context(mpt::Wine::VersionContext versionContext)
: m_VersionContext(versionContext)
, wine_get_dos_file_name(nullptr)
, wine_get_unix_file_name(nullptr)
{
if(!mpt::Windows::IsWine())
{
throw mpt::Wine::Exception("Wine not detected.");
}
if(!m_VersionContext.Version().IsValid())
{
throw mpt::Wine::Exception("Unknown Wine version detected.");
}
m_Kernel32 = mpt::Library(mpt::LibraryPath::FullPath(MPT_PATHSTRING("kernel32.dll")));
if(!m_Kernel32.IsValid())
{
throw mpt::Wine::Exception("Could not load Wine kernel32.dll.");
}
if(!m_Kernel32.Bind(wine_get_unix_file_name, "wine_get_unix_file_name"))
{
throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_unix_file_name.");
}
if(!m_Kernel32.Bind(wine_get_dos_file_name, "wine_get_dos_file_name"))
{
throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_dos_file_name.");
}
{
std::string out;
std::string err;
try
{
if(ExecutePosixShellCommand("uname -m", out, err) != 0)
{
throw mpt::Wine::Exception("Wine 'uname -m' failed.");
}
if(!err.empty())
{
throw mpt::Wine::Exception("Wine 'uname -m' failed.");
}
out = mpt::String::Trim(out, std::string("\r\n"));
m_Uname_m = out;
} catch(const std::exception &)
{
m_Uname_m = std::string();
}
}
try
{
m_HOME = GetPosixEnvVar("HOME");
} catch(const std::exception &)
{
m_HOME = std::string();
}
try
{
m_XDG_DATA_HOME = GetPosixEnvVar("XDG_DATA_HOME");
if(m_XDG_DATA_HOME.empty())
{
m_XDG_DATA_HOME = m_HOME + "/.local/share";
}
} catch(const std::exception &)
{
m_XDG_DATA_HOME = std::string();
}
try
{
m_XDG_CACHE_HOME = GetPosixEnvVar("XDG_CACHE_HOME");
if(m_XDG_CACHE_HOME.empty())
{
m_XDG_CACHE_HOME = m_HOME + "/.cache";
}
} catch(const std::exception &)
{
m_XDG_CACHE_HOME = std::string();
}
try
{
m_XDG_CONFIG_HOME = GetPosixEnvVar("XDG_CONFIG_HOME");
if(m_XDG_CONFIG_HOME.empty())
{
m_XDG_CONFIG_HOME = m_HOME + "/.config";
}
} catch(const std::exception &)
{
m_XDG_CONFIG_HOME = std::string();
}
}
std::string Context::PathToPosix(mpt::PathString windowsPath)
{
std::string result;
if(windowsPath.empty())
{
return result;
}
if(windowsPath.Length() >= 32000)
{
throw mpt::Wine::Exception("Path too long.");
}
LPSTR tmp = nullptr;
tmp = wine_get_unix_file_name(windowsPath.AsNative().c_str());
if(!tmp)
{
throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_unix_file_name failed.");
}
result = tmp;
HeapFree(GetProcessHeap(), 0, tmp);
tmp = nullptr;
return result;
}
mpt::PathString Context::PathToWindows(std::string hostPath)
{
mpt::PathString result;
if(hostPath.empty())
{
return result;
}
if(hostPath.length() >= 32000)
{
throw mpt::Wine::Exception("Path too long.");
}
LPWSTR tmp = nullptr;
tmp = wine_get_dos_file_name(hostPath.c_str());
if(!tmp)
{
throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_dos_file_name failed.");
}
result = mpt::PathString::FromNative(tmp);
HeapFree(GetProcessHeap(), 0, tmp);
tmp = nullptr;
return result;
}
std::string Context::PathToPosixCanonical(mpt::PathString windowsPath)
{
std::string result;
std::string hostPath = PathToPosix(windowsPath);
if(hostPath.empty())
{
return result;
}
std::string output;
std::string error;
int exitcode = ExecutePosixShellCommand(std::string() + "readlink -f " + EscapePosixShell(hostPath), output, error);
if(!error.empty())
{
throw mpt::Wine::Exception("Wine readlink failed: " + error);
}
if(exitcode != 0 && exitcode != 1)
{
throw mpt::Wine::Exception("Wine readlink failed.");
}
std::string trimmedOutput = mpt::String::Trim(output, std::string("\r\n"));
result = trimmedOutput;
return result;
}
static void ExecutePosixCommandProgressDefault(void * /*userdata*/ )
{
::Sleep(10);
return;
}
static ExecuteProgressResult ExecutePosixShellScriptProgressDefault(void * /*userdata*/ )
{
::Sleep(10);
return ExecuteProgressContinueWaiting;
}
std::string Context::EscapePosixShell(std::string line)
{
const char escape_chars [] = { '|', '&', ';', '<', '>', '(', ')', '$', '`', '"', '\'', ' ', '\t' };
const char maybe_escape_chars [] = { '*', '?', '[', '#', '~', '=', '%' };
line = mpt::String::Replace(line, "\\", "\\\\");
for(std::size_t i = 0; i < mpt::size(escape_chars); ++i)
{
line = mpt::String::Replace(line, std::string(1, escape_chars[i]), "\\" + std::string(1, escape_chars[i]));
}
for(std::size_t i = 0; i < mpt::size(maybe_escape_chars); ++i)
{
line = mpt::String::Replace(line, std::string(1, maybe_escape_chars[i]), "\\" + std::string(1, maybe_escape_chars[i]));
}
return line;
}
ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet<ExecFlags> flags, std::map<std::string, std::vector<char> > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata)
{
// Relevant documentation:
// https://stackoverflow.com/questions/6004070/execute-shell-commands-from-program-running-in-wine
// https://www.winehq.org/pipermail/wine-bugs/2014-January/374918.html
// https://bugs.winehq.org/show_bug.cgi?id=34730
if(!progress) progress = &ExecutePosixCommandProgressDefault;
if(!progressCancel) progressCancel = &ExecutePosixShellScriptProgressDefault;
if(flags[ExecFlagInteractive]) flags.reset(ExecFlagSilent);
if(flags[ExecFlagSplitOutput]) flags.set(ExecFlagSilent);
std::vector<mpt::PathString> tempfiles;
progress(userdata);
mpt::TempDirGuard dirWindowsTemp(mpt::CreateTempFileName());
if(dirWindowsTemp.GetDirname().empty())
{
throw mpt::Wine::Exception("Creating temporary directoy failed.");
}
std::string dirPosix = PathToPosix(dirWindowsTemp.GetDirname());
if(dirPosix.empty())
{
throw mpt::Wine::Exception("mpt::Wine::ConvertWindowsPathToHost returned empty path.");
}
const mpt::PathString dirWindows = dirWindowsTemp.GetDirname();
progress(userdata);
// write the script to disk
mpt::PathString scriptFilenameWindows = dirWindows + MPT_PATHSTRING("script.sh");
{
mpt::ofstream tempfile(scriptFilenameWindows, std::ios::binary);
tempfile << script;
tempfile.flush();
if(!tempfile)
{
throw mpt::Wine::Exception("Error writing script.sh.");
}
}
std::string scriptFilenamePosix = PathToPosix(scriptFilenameWindows);
if(scriptFilenamePosix.empty())
{
throw mpt::Wine::Exception("Error converting script.sh path.");
}
progress(userdata);
// create a wrapper that will call the script and gather result.
mpt::PathString wrapperstarterFilenameWindows = dirWindows + MPT_PATHSTRING("wrapperstarter.sh");
{
mpt::ofstream tempfile(wrapperstarterFilenameWindows, std::ios::binary);
std::string wrapperstarterscript;
wrapperstarterscript += std::string() + "#!/usr/bin/env sh" + "\n";
wrapperstarterscript += std::string() + "exec /usr/bin/env sh " + EscapePosixShell(dirPosix) + "wrapper.sh" + "\n";
tempfile << wrapperstarterscript;
tempfile.flush();
if(!tempfile)
{
throw mpt::Wine::Exception("Error writing wrapper.sh.");
}
}
mpt::PathString wrapperFilenameWindows = dirWindows + MPT_PATHSTRING("wrapper.sh");
std::string cleanupscript;
{
mpt::ofstream tempfile(wrapperFilenameWindows, std::ios::binary);
std::string wrapperscript;
if(!flags[ExecFlagSilent])
{
wrapperscript += std::string() + "printf \"\\033]0;" + title + "\\a\"" + "\n";
}
wrapperscript += std::string() + "chmod u+x " + EscapePosixShell(scriptFilenamePosix) + "\n";
wrapperscript += std::string() + "cd " + EscapePosixShell(dirPosix) + "filetree" + "\n";
if(flags[ExecFlagInteractive])
{ // no stdout/stderr capturing for interactive scripts
wrapperscript += std::string() + EscapePosixShell(scriptFilenamePosix) + "\n";
wrapperscript += std::string() + "MPT_RESULT=$?" + "\n";
wrapperscript += std::string() + "echo ${MPT_RESULT} > " + EscapePosixShell(dirPosix) + "exit" + "\n";
} else if(flags[ExecFlagSplitOutput])
{
wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 4>" + EscapePosixShell(dirPosix) + "exit 1>" + EscapePosixShell(dirPosix) + "out 2>" + EscapePosixShell(dirPosix) + "err" + "\n";
} else
{
wrapperscript += std::string() + "(" + EscapePosixShell(scriptFilenamePosix) + "; echo $? >&4) 2>&1 4>" + EscapePosixShell(dirPosix) + "exit | tee " + EscapePosixShell(dirPosix) + "out" + "\n";
}
wrapperscript += std::string() + "echo done > " + EscapePosixShell(dirPosix) + "done" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "done" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "exit" + "\n";
if(flags[ExecFlagInteractive])
{
// nothing
} else if(flags[ExecFlagSplitOutput])
{
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "err" + "\n";
} else
{
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "out" + "\n";
}
cleanupscript += std::string() + "rm -r " + EscapePosixShell(dirPosix) + "filetree" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "script.sh" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapper.sh" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n";
cleanupscript += std::string() + "rm " + EscapePosixShell(dirPosix) + "terminal.sh" + "\n";
if(flags[ExecFlagAsync])
{
wrapperscript += cleanupscript;
cleanupscript.clear();
}
tempfile << wrapperscript;
tempfile.flush();
if(!tempfile)
{
throw mpt::Wine::Exception("Error writing wrapper.sh.");
}
}
progress(userdata);
::CreateDirectoryW((dirWindows + MPT_PATHSTRING("filetree")).AsNative().c_str(), NULL);
for(const auto &file : filetree)
{
std::vector<mpt::ustring> path = mpt::String::Split<mpt::ustring>(mpt::ToUnicode(mpt::CharsetUTF8, file.first), MPT_USTRING("/"));
mpt::PathString combinedPath = dirWindows + MPT_PATHSTRING("filetree") + MPT_PATHSTRING("\\");
if(path.size() > 1)
{
for(std::size_t singlepath = 0; singlepath < path.size() - 1; ++singlepath)
{
if(path[singlepath].empty())
{
continue;
}
combinedPath += mpt::PathString::FromUnicode(path[singlepath]);
if(!combinedPath.IsDirectory())
{
if(::CreateDirectoryW(combinedPath.AsNative().c_str(), NULL) == 0)
{
throw mpt::Wine::Exception("Error writing filetree.");
}
}
combinedPath += MPT_PATHSTRING("\\");
}
}
try
{
mpt::LazyFileRef out(dirWindows + MPT_PATHSTRING("filetree") + MPT_PATHSTRING("\\") + mpt::PathString::FromUTF8(mpt::String::Replace(file.first, "/", "\\")));
out = file.second;
} catch(std::exception &)
{
throw mpt::Wine::Exception("Error writing filetree.");
}
}
progress(userdata);
// create a wrapper that will find a suitable terminal and run the wrapper script in the terminal window.
mpt::PathString terminalWrapperFilenameWindows = dirWindows + MPT_PATHSTRING("terminal.sh");
{
mpt::ofstream tempfile(terminalWrapperFilenameWindows, std::ios::binary);
// NOTE:
// Modern terminals detach themselves from the invoking shell if another instance is already present.
// This means we cannot rely on terminal invocation being syncronous.
std::vector<std::string> terminals;
terminals.push_back("x-terminal-emulator");
terminals.push_back("konsole");
terminals.push_back("mate-terminal");
terminals.push_back("xfce4-terminal");
terminals.push_back("gnome-terminal");
terminals.push_back("uxterm");
terminals.push_back("xterm");
terminals.push_back("rxvt");
std::map<std::string, std::string> terminalLanchers;
for(std::size_t i = 0; i < terminals.size(); ++i)
{
// mate-terminal on Debian 8 cannot execute commands with arguments,
// thus we use a separate script that requires no arguments to execute.
terminalLanchers[terminals[i]] += std::string() + "if command -v " + terminals[i] + " 2>/dev/null 1>/dev/null ; then" + "\n";
terminalLanchers[terminals[i]] += std::string() + " chmod u+x " + EscapePosixShell(dirPosix) + "wrapperstarter.sh" + "\n";
terminalLanchers[terminals[i]] += std::string() + " exec `command -v " + terminals[i] + "` -e \"" + EscapePosixShell(dirPosix) + "wrapperstarter.sh\"" + "\n";
terminalLanchers[terminals[i]] += std::string() + "fi" + "\n";
}
std::string terminalscript;
terminalscript += std::string() + "\n";
for(std::size_t i = 0; i < terminals.size(); ++i)
{
terminalscript += terminalLanchers[terminals[i]];
}
tempfile << terminalscript;
tempfile.flush();
if(!tempfile)
{
return ExecResult::Error();
}
}
progress(userdata);
// build unix command line
std::string unixcommand;
bool createProcessSuccess = false;
if(!createProcessSuccess)
{
if(flags[ExecFlagSilent])
{
unixcommand = "/usr/bin/env sh \"" + EscapePosixShell(dirPosix) + "wrapper.sh\"";
} else
{
unixcommand = "/usr/bin/env sh \"" + EscapePosixShell(dirPosix) + "terminal.sh\"";
}
progress(userdata);
std::wstring unixcommandW = mpt::ToWide(mpt::CharsetUTF8, unixcommand);
std::vector<WCHAR> commandline = std::vector<WCHAR>(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1);
std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title);
std::vector<WCHAR> titleWv = std::vector<WCHAR>(titleW.data(), titleW.data() + titleW.length() + 1);
STARTUPINFOW startupInfo;
MemsetZero(startupInfo);
startupInfo.lpTitle = &titleWv[0];
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInformation;
MemsetZero(processInformation);
progress(userdata);
BOOL success = FALSE;
if(flags[ExecFlagSilent])
{
success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
} else
{
success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
}
progress(userdata);
createProcessSuccess = (success ? true : false);
progress(userdata);
if(success)
{
if(!flags[ExecFlagAsync])
{
// note: execution is not syncronous with all Wine versions,
// we additionally explicitly wait for "done" later
while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
{ // wait
if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
{
CloseHandle(processInformation.hThread);
CloseHandle(processInformation.hProcess);
throw mpt::Wine::Exception("Canceled.");
}
}
}
progress(userdata);
CloseHandle(processInformation.hThread);
CloseHandle(processInformation.hProcess);
}
}
progress(userdata);
// Work around Wine being able to execute PIE binaries on Debian 9.
// Luckily, /bin/bash is still non-PIE on Debian 9.
if(!createProcessSuccess)
{
if(flags[ExecFlagSilent]) {
unixcommand = "/bin/bash \"" + EscapePosixShell(dirPosix) + "wrapper.sh\"";
} else
{
unixcommand = "/bin/bash \"" + EscapePosixShell(dirPosix) + "terminal.sh\"";
}
progress(userdata);
std::wstring unixcommandW = mpt::ToWide(mpt::CharsetUTF8, unixcommand);
std::vector<WCHAR> commandline = std::vector<WCHAR>(unixcommandW.data(), unixcommandW.data() + unixcommandW.length() + 1);
std::wstring titleW = mpt::ToWide(mpt::CharsetUTF8, title);
std::vector<WCHAR> titleWv = std::vector<WCHAR>(titleW.data(), titleW.data() + titleW.length() + 1);
STARTUPINFOW startupInfo;
MemsetZero(startupInfo);
startupInfo.lpTitle = &titleWv[0];
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInformation;
MemsetZero(processInformation);
progress(userdata);
BOOL success = FALSE;
if(flags[ExecFlagSilent])
{
success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
} else
{
success = CreateProcessW(NULL, &commandline[0], NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
}
progress(userdata);
createProcessSuccess = (success ? true : false);
progress(userdata);
if(success)
{
if(!flags[ExecFlagAsync])
{
// note: execution is not syncronous with all Wine versions,
// we additionally explicitly wait for "done" later
while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
{ // wait
if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
{
CloseHandle(processInformation.hThread);
CloseHandle(processInformation.hProcess);
throw mpt::Wine::Exception("Canceled.");
}
}
}
progress(userdata);
CloseHandle(processInformation.hThread);
CloseHandle(processInformation.hProcess);
}
}
progress(userdata);
if(!createProcessSuccess)
{
throw mpt::Wine::Exception("CreateProcess failed.");
}
progress(userdata);
if(flags[ExecFlagAsync])
{
ExecResult result;
result.exitcode = 0;
return result;
}
while(!(dirWindows + MPT_PATHSTRING("done")).IsFile())
{ // wait
if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
{
throw mpt::Wine::Exception("Canceled.");
}
}
progress(userdata);
int exitCode = 0;
{
mpt::ifstream exitFile(dirWindows + MPT_PATHSTRING("exit"), std::ios::binary);
if(!exitFile)
{
throw mpt::Wine::Exception("Script .exit file not found.");
}
std::string exitString;
exitFile >> exitString;
if(exitString.empty())
{
throw mpt::Wine::Exception("Script .exit file empty.");
}
exitCode = ConvertStrTo<int>(exitString);
}
progress(userdata);
std::string outputString;
if(!flags[ExecFlagInteractive])
{
mpt::ifstream outputFile(dirWindows + MPT_PATHSTRING("out"), std::ios::binary);
if(outputFile)
{
outputFile.seekg(0, std::ios::end);
std::streampos outputFileSize = outputFile.tellg();
outputFile.seekg(0, std::ios::beg);
std::vector<char> outputFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(outputFileSize)));
outputFile.read(&outputFileBuf[0], outputFileBuf.size());
outputString = std::string(outputFileBuf.begin(), outputFileBuf.end());
}
}
progress(userdata);
std::string errorString;
if(flags[ExecFlagSplitOutput])
{
mpt::ifstream errorFile(dirWindows + MPT_PATHSTRING("err"), std::ios::binary);
if(errorFile)
{
errorFile.seekg(0, std::ios::end);
std::streampos errorFileSize = errorFile.tellg();
errorFile.seekg(0, std::ios::beg);
std::vector<char> errorFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(errorFileSize)));
errorFile.read(&errorFileBuf[0], errorFileBuf.size());
errorString = std::string(errorFileBuf.begin(), errorFileBuf.end());
}
}
progress(userdata);
ExecResult result;
result.exitcode = exitCode;
result.output = outputString;
result.error = errorString;
std::deque<mpt::PathString> paths;
paths.push_back(dirWindows + MPT_PATHSTRING("filetree"));
mpt::PathString basePath = (dirWindows + MPT_PATHSTRING("filetree")).EnsureTrailingSlash();
while(!paths.empty())
{
mpt::PathString path = paths.front();
paths.pop_front();
path.EnsureTrailingSlash();
HANDLE hFind = NULL;
WIN32_FIND_DATAW wfd;
MemsetZero(wfd);
hFind = FindFirstFileW((path + MPT_PATHSTRING("*.*")).AsNative().c_str(), &wfd);
if(hFind != NULL && hFind != INVALID_HANDLE_VALUE)
{
do
{
mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName);
if(filename != MPT_PATHSTRING(".") && filename != MPT_PATHSTRING(".."))
{
filename = path + filename;
filetree[filename.ToUTF8()] = std::vector<char>();
if(filename.IsDirectory())
{
paths.push_back(filename);
} else if(filename.IsFile())
{
try
{
mpt::LazyFileRef f(filename);
std::vector<char> buf = f;
mpt::PathString treeFilename = mpt::PathString::FromNative(filename.AsNative().substr(basePath.AsNative().length()));
result.filetree[treeFilename.ToUTF8()] = buf;
} catch (std::exception &)
{
// nothing?!
}
}
}
} while(FindNextFileW(hFind, &wfd));
FindClose(hFind);
}
}
mpt::DeleteWholeDirectoryTree(dirWindows);
return result;
}
int Context::ExecutePosixShellCommand(std::string command, std::string & output, std::string & error)
{
std::string script;
script += std::string() + "#!/usr/bin/env sh" + "\n";
script += std::string() + "exec " + command + "\n";
mpt::Wine::ExecResult execResult = ExecutePosixShellScript
( script
, mpt::Wine::ExecFlagSilent | mpt::Wine::ExecFlagSplitOutput, std::map<std::string, std::vector<char> >()
, std::string()
, nullptr
, nullptr
, nullptr
);
output = execResult.output;
error = execResult.error;
return execResult.exitcode;
}
std::string Context::GetPosixEnvVar(std::string var, std::string def)
{
// We cannot use std::getenv here because Wine overrides SOME env vars,
// in particular, HOME is unset in the Wine environment.
// Instead, we just spawn a shell that will catch up a sane environment on
// its own.
std::string output;
std::string error;
int exitcode = ExecutePosixShellCommand(std::string() + "echo $" + var, output, error);
if(!error.empty())
{
throw mpt::Wine::Exception("Wine echo $var failed: " + error);
}
if(exitcode != 0)
{
throw mpt::Wine::Exception("Wine echo $var failed.");
}
std::string result = mpt::String::RTrim(output, std::string("\r\n"));
if(result.empty())
{
result = def;
}
return result;
}
} // namespace Wine
} // namespace mpt
#else // !(MODPLUG_TRACKER && MPT_OS_WINDOWS)
MPT_MSVC_WORKAROUND_LNK4221(mptWine)
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,127 @@
/*
* mptWine.h
* ---------
* Purpose: Wine stuff.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "mptOS.h"
#include "FlagSet.h"
#include <map>
#include <string>
#include <vector>
OPENMPT_NAMESPACE_BEGIN
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
namespace mpt
{
namespace Wine
{
class Exception
: public std::runtime_error
{
public:
Exception(const std::string &text)
: std::runtime_error(text)
{
return;
}
};
typedef void (*ExecutePosixCommandProgress)(void *userdata);
enum ExecuteProgressResult
{
ExecuteProgressContinueWaiting = 0,
ExecuteProgressAsyncCancel = -1,
};
typedef ExecuteProgressResult (*ExecutePosixShellScriptProgress)(void *userdata);
enum ExecFlags
{
ExecFlagNone = 0,
ExecFlagSilent = 1<<0, // do not show a terminal window
ExecFlagInteractive = 1<<1, // allow interaction (prevents stdout and stderr capturing and implies !silent)
ExecFlagAsync = 1<<2, // do not wait for the script to finish
ExecFlagProgressWindow = 1<<3, // not implemented by mptOS
ExecFlagSplitOutput = 1<<4, // split stdout and stderr (implies silent)
ExecFlagsDefault = ExecFlagNone
};
MPT_DECLARE_ENUM(ExecFlags)
struct ExecResult
{
int exitcode;
std::string output;
std::string error;
std::map<std::string, std::vector<char> > filetree;
static ExecResult Error()
{
ExecResult result;
result.exitcode = -1;
return result;
}
};
class Context
{
protected:
mpt::Wine::VersionContext m_VersionContext;
mpt::Library m_Kernel32;
private:
LPWSTR (*CDECL wine_get_dos_file_name)(LPCSTR str);
LPSTR (*CDECL wine_get_unix_file_name)(LPCWSTR str);
protected:
std::string m_Uname_m;
std::string m_HOME;
std::string m_XDG_DATA_HOME;
std::string m_XDG_CACHE_HOME;
std::string m_XDG_CONFIG_HOME;
public:
Context(mpt::Wine::VersionContext versionContext);
public:
std::string EscapePosixShell(std::string line);
std::string PathToPosix(mpt::PathString windowsPath);
mpt::PathString PathToWindows(std::string hostPath);
ExecResult ExecutePosixShellScript(std::string script, FlagSet<ExecFlags> flags, std::map<std::string, std::vector<char> > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata);
int ExecutePosixShellCommand(std::string command, std::string & output, std::string & error);
std::string PathToPosixCanonical(mpt::PathString windowsPath);
std::string GetPosixEnvVar(std::string var, std::string def = std::string());
public:
mpt::Wine::VersionContext VersionContext() const { return m_VersionContext; }
mpt::Library Kernel32() const { return m_Kernel32; }
std::string Uname_m() const { return m_Uname_m; }
std::string HOME() const { return m_HOME; }
std::string XDG_DATA_HOME() const { return m_XDG_DATA_HOME; }
std::string XDG_CACHE_HOME() const { return m_XDG_CACHE_HOME; }
std::string XDG_CONFIG_HOME() const { return m_XDG_CONFIG_HOME; }
};
} // namespace Wine
} // namespace mpt
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,735 @@
/*
* serialization_utils.cpp
* -----------------------
* Purpose: Serializing data to and from MPTM files.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "serialization_utils.h"
#include <istream>
#include <ostream>
#include "misc_util.h"
#include "mptBufferIO.h"
OPENMPT_NAMESPACE_BEGIN
namespace srlztn
{
//#define SSB_LOGGING
#ifdef SSB_LOGGING
#define SSB_LOG(x) Log(x)
#else
#define SSB_LOG(x) MPT_DO { } MPT_WHILE_0
#endif
static const uint8 HeaderId_FlagByte = 0;
// Indexing starts from 0.
static inline bool Testbit(uint8 val, uint8 bitindex) {return ((val & (1 << bitindex)) != 0);}
static inline void Setbit(uint8& val, uint8 bitindex, bool newval)
{
if(newval) val |= (1 << bitindex);
else val &= ~(1 << bitindex);
}
bool ID::IsPrintable() const
{
for(std::size_t i = 0; i < m_ID.length(); ++i)
{
if(m_ID[i] <= 0 || isprint(static_cast<unsigned char>(m_ID[i])) == 0)
{
return false;
}
}
return true;
}
//Format: First bit tells whether the size indicator is 1 or 2 bytes.
static void WriteAdaptive12String(std::ostream& oStrm, const std::string& str)
{
uint16 s = static_cast<uint16>(str.size());
LimitMax(s, uint16(uint16_max / 2));
mpt::IO::WriteAdaptiveInt16LE(oStrm, s);
oStrm.write(str.c_str(), s);
}
void WriteItemString(std::ostream& oStrm, const std::string &str)
{
uint32 id = static_cast<uint32>(std::min<std::size_t>(str.size(), (uint32_max >> 4))) << 4;
id |= 12; // 12 == 1100b
Binarywrite<uint32>(oStrm, id);
id >>= 4;
if(id > 0)
oStrm.write(str.data(), id);
}
void ReadItemString(std::istream& iStrm, std::string& str, const DataSize)
{
// bits 0,1: Bytes per char type: 1,2,3,4.
// bits 2,3: Bytes in size indicator, 1,2,3,4
uint32 id = 0;
Binaryread(iStrm, id, 1);
const uint8 nSizeBytes = (id & 12) >> 2; // 12 == 1100b
if (nSizeBytes > 0)
{
uint8 bytes = std::min<uint8>(3, nSizeBytes);
uint8 v2 = 0;
uint8 v3 = 0;
uint8 v4 = 0;
if(bytes >= 1) Binaryread(iStrm, v2);
if(bytes >= 2) Binaryread(iStrm, v3);
if(bytes >= 3) Binaryread(iStrm, v4);
id &= 0xff;
id |= (v2 << 8) | (v3 << 16) | (v4 << 24);
}
// Limit to 1 MB.
str.resize(std::min<std::size_t>(id >> 4, 1000000));
for(size_t i = 0; i < str.size(); i++)
iStrm.read(&str[i], 1);
id = (id >> 4) - static_cast<uint32>(str.size());
if(id > 0)
iStrm.ignore(id);
}
mpt::ustring ID::AsString() const
{
if(IsPrintable())
{
return mpt::ToUnicode(mpt::CharsetISO8859_1, m_ID);
}
if(m_ID.length() > 8)
{
return mpt::ustring();
}
uint64le val;
val.set(0);
std::memcpy(&val, m_ID.data(), m_ID.length());
return mpt::ufmt::val(val);
}
const char Ssb::s_EntryID[3] = {'2','2','8'};
#ifdef SSB_LOGGING
static const MPT_UCHAR_TYPE tstrWriteHeader[] = MPT_ULITERAL("Write header with ID = %1\n");
static const MPT_UCHAR_TYPE tstrWriteProgress[] = MPT_ULITERAL("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n");
static const MPT_UCHAR_TYPE tstrWritingMap[] = MPT_ULITERAL("Writing map to rpos: %1\n");
static const MPT_UCHAR_TYPE tstrMapEntryWrite[] = MPT_ULITERAL("Writing map entry: id=%1, rpos=%2, size=%3\n");
static const MPT_UCHAR_TYPE strWriteNote[] = MPT_ULITERAL("Write note: ");
static const MPT_UCHAR_TYPE tstrEndOfStream[] = MPT_ULITERAL("End of stream(rpos): %1\n");
static const MPT_UCHAR_TYPE tstrReadingHeader[] = MPT_ULITERAL("Read header with expected ID = %1\n");
static const MPT_UCHAR_TYPE strNoMapInFile[] = MPT_ULITERAL("No map in the file.\n");
static const MPT_UCHAR_TYPE strIdMismatch[] = MPT_ULITERAL("ID mismatch, terminating read.\n");
static const MPT_UCHAR_TYPE strIdMatch[] = MPT_ULITERAL("ID match, continuing reading.\n");
static const MPT_UCHAR_TYPE tstrReadingMap[] = MPT_ULITERAL("Reading map from rpos: %1\n");
static const MPT_UCHAR_TYPE tstrEndOfMap[] = MPT_ULITERAL("End of map(rpos): %1\n");
static const MPT_UCHAR_TYPE tstrReadProgress[] = MPT_ULITERAL("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n");
static const MPT_UCHAR_TYPE tstrNoEntryFound[] = MPT_ULITERAL("No entry with id %1 found.\n");
static const MPT_UCHAR_TYPE strReadNote[] = MPT_ULITERAL("Read note: ");
#endif
Ssb::Ssb()
: m_Status(SNT_NONE)
, m_nFixedEntrySize(0)
, m_posStart(0)
, m_nIdbytes(IdSizeVariable)
, m_nCounter(0)
, m_Flags((1 << RwfWMapStartPosEntry) + (1 << RwfWMapSizeEntry) + (1 << RwfWVersionNum))
{
return;
}
SsbWrite::SsbWrite(std::ostream& oStrm)
: m_pOstrm(&oStrm)
, m_posEntrycount(0)
, m_posMapPosField(0)
{
return;
}
SsbRead::SsbRead(std::istream& iStrm)
: m_pIstrm(&iStrm)
, m_nReadVersion(0)
, m_rposMapBegin(0)
, m_posMapEnd(0)
, m_posDataBegin(0)
, m_rposEndofHdrData(0)
, m_nReadEntrycount(0)
, m_nNextReadHint(0)
{
return;
}
void SsbWrite::AddWriteNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(mpt::format(MPT_USTRING("%1: 0x%2\n"))(strWriteNote, mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(mpt::format(MPT_USTRING("%1: 0x%2\n"))(strReadNote, mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum)
{
m_Status |= SNT_PROGRESS;
SSB_LOG(mpt::format(mpt::ustring(tstrReadProgress))(
nNum,
(pRe && pRe->nIdLength < 30 && m_Idarray.size() > 0) ? ID(&m_Idarray[pRe->nIdpos], pRe->nIdLength).AsString() : MPT_USTRING(""),
(pRe) ? pRe->rposStart : 0,
(pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : MPT_USTRING(""),
MPT_USTRING("")));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(pRe);
MPT_UNREFERENCED_PARAMETER(nNum);
#endif
}
// Called after writing an entry.
void SsbWrite::AddWriteNote(const ID &id, const NumType nEntryNum, const DataSize nBytecount, const RposType rposStart)
{
m_Status |= SNT_PROGRESS;
SSB_LOG(mpt::format(mpt::ustring(tstrWriteProgress))(nEntryNum, id.AsString(), rposStart, nBytecount));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(id);
MPT_UNREFERENCED_PARAMETER(nEntryNum);
MPT_UNREFERENCED_PARAMETER(nBytecount);
MPT_UNREFERENCED_PARAMETER(rposStart);
#endif
}
void SsbRead::ResetReadstatus()
{
m_Status = SNT_NONE;
m_Idarray.reserve(32);
m_Idarray.push_back(0);
}
void SsbWrite::WriteMapItem(const ID &id,
const RposType& rposDataStart,
const DataSize& nDatasize,
const char* pszDesc)
{
SSB_LOG(mpt::format(mpt::ustring(tstrMapEntryWrite))(
(id.GetSize() > 0) ? id.AsString() : MPT_USTRING(""),
rposDataStart,
nDatasize));
mpt::ostringstream mapStream;
if(m_nIdbytes > 0)
{
if (m_nIdbytes != IdSizeVariable && id.GetSize() != m_nIdbytes)
{ AddWriteNote(SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING); return; }
if (m_nIdbytes == IdSizeVariable) //Variablesize ID?
mpt::IO::WriteAdaptiveInt16LE(mapStream, static_cast<uint16>(id.GetSize()));
if(id.GetSize() > 0)
mapStream.write(id.GetBytes(), id.GetSize());
}
if (GetFlag(RwfWMapStartPosEntry)) //Startpos
mpt::IO::WriteAdaptiveInt64LE(mapStream, rposDataStart);
if (GetFlag(RwfWMapSizeEntry)) //Entrysize
mpt::IO::WriteAdaptiveInt64LE(mapStream, nDatasize);
if (GetFlag(RwfWMapDescEntry)) //Entry descriptions
WriteAdaptive12String(mapStream, std::string(pszDesc));
m_MapStreamString.append(mapStream.str());
}
void SsbWrite::IncrementWriteCounter()
{
m_nCounter++;
if (m_nCounter >= (uint16_max >> 2))
{
FinishWrite();
AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED);
}
}
void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion)
{
std::ostream& oStrm = *m_pOstrm;
SSB_LOG(mpt::format(mpt::ustring(tstrWriteHeader))(id.AsString()));
ResetWritestatus();
if(!oStrm.good())
{ AddWriteNote(SNRW_BADGIVEN_STREAM); return; }
// Start bytes.
oStrm.write(s_EntryID, sizeof(s_EntryID));
m_posStart = oStrm.tellp() - Offtype(sizeof(s_EntryID));
// Object ID.
{
uint8 idsize = static_cast<uint8>(id.GetSize());
Binarywrite<uint8>(oStrm, idsize);
if(idsize > 0) oStrm.write(id.GetBytes(), id.GetSize());
}
// Form header.
uint8 header = 0;
SetFlag(RwfWMapStartPosEntry, GetFlag(RwfWMapStartPosEntry) && m_nFixedEntrySize == 0);
SetFlag(RwfWMapSizeEntry, GetFlag(RwfWMapSizeEntry) && m_nFixedEntrySize == 0);
header = (m_nIdbytes != 4) ? (m_nIdbytes & 3) : 3; //0,1 : Bytes per IDtype, 0,1,2,4
Setbit(header, 2, GetFlag(RwfWMapStartPosEntry)); //2 : Startpos in map?
Setbit(header, 3, GetFlag(RwfWMapSizeEntry)); //3 : Datasize in map?
Setbit(header, 4, GetFlag(RwfWVersionNum)); //4 : Version numeric field?
Setbit(header, 7, GetFlag(RwfWMapDescEntry)); //7 : Entrydescriptions in map?
// Write header
Binarywrite<uint8>(oStrm, header);
// Additional options.
uint8 tempU8 = 0;
Setbit(tempU8, 0, (m_nIdbytes == IdSizeVariable) || (m_nIdbytes == 3) || (m_nIdbytes > 4));
Setbit(tempU8, 1, m_nFixedEntrySize != 0);
const uint8 flags = tempU8;
if(flags != s_DefaultFlagbyte)
{
mpt::IO::WriteAdaptiveInt32LE(oStrm, 2); //Headersize - now it is 2.
Binarywrite<uint8>(oStrm, HeaderId_FlagByte);
Binarywrite<uint8>(oStrm, flags);
}
else
mpt::IO::WriteAdaptiveInt32LE(oStrm, 0);
if(Testbit(header, 4)) // Version(numeric)?
mpt::IO::WriteAdaptiveInt64LE(oStrm, nVersion);
if(Testbit(flags, 0)) // Custom IDbytecount?
{
uint8 n = (m_nIdbytes == IdSizeVariable) ? 1 : static_cast<uint8>((m_nIdbytes << 1));
Binarywrite<uint8>(oStrm, n);
}
if(Testbit(flags, 1)) // Fixedsize entries?
mpt::IO::WriteAdaptiveInt32LE(oStrm, m_nFixedEntrySize);
//Entrycount. Reserve two bytes(max uint16_max / 4 entries), actual value is written after writing data.
m_posEntrycount = oStrm.tellp();
Binarywrite<uint16>(oStrm, 0);
SetFlag(RwfRwHasMap, (m_nIdbytes != 0 || GetFlag(RwfWMapStartPosEntry) || GetFlag(RwfWMapSizeEntry) || GetFlag(RwfWMapDescEntry)));
m_posMapPosField = oStrm.tellp();
if (GetFlag(RwfRwHasMap)) //Mapping begin pos(reserve space - actual value is written after writing data)
Binarywrite<uint64>(oStrm, 0);
}
SsbRead::ReadRv SsbRead::OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin)
{
if (pE != nullptr)
AddReadNote(pE, m_nCounter);
else if (GetFlag(RwfRMapHasId) == false) // Not ID's in map.
{
ReadEntry e;
e.rposStart = static_cast<RposType>(posReadBegin - m_posStart);
e.nSize = static_cast<DataSize>(m_pIstrm->tellg() - posReadBegin);
AddReadNote(&e, m_nCounter);
}
else // Entry not found.
{
SSB_LOG(mpt::format(mpt::ustring(tstrNoEntryFound))(id.AsString()));
#ifndef SSB_LOGGING
MPT_UNREFERENCED_PARAMETER(id);
#endif
return EntryNotFound;
}
m_nCounter++;
return EntryRead;
}
void SsbWrite::OnWroteItem(const ID &id, const Postype& posBeforeWrite)
{
const Offtype nRawEntrySize = m_pOstrm->tellp() - posBeforeWrite;
if (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > std::numeric_limits<DataSize>::max())
{ AddWriteNote(SNW_INSUFFICIENT_DATASIZETYPE); return; }
if(GetFlag(RwfRMapHasSize) && (nRawEntrySize < 0 || static_cast<uint64>(nRawEntrySize) > (std::numeric_limits<DataSize>::max() >> 2)))
{ AddWriteNote(SNW_DATASIZETYPE_OVERFLOW); return; }
DataSize nEntrySize = static_cast<DataSize>(nRawEntrySize);
// Handle fixed size entries:
if (m_nFixedEntrySize > 0)
{
if(nEntrySize <= m_nFixedEntrySize)
{
for(uint32 i = 0; i<m_nFixedEntrySize-nEntrySize; i++)
m_pOstrm->put(0);
nEntrySize = m_nFixedEntrySize;
}
else
{ AddWriteNote(SNW_INSUFFICIENT_FIXEDSIZE); return; }
}
if (GetFlag(RwfRwHasMap))
WriteMapItem(id, static_cast<RposType>(posBeforeWrite - m_posStart), nEntrySize, "");
AddWriteNote(id, m_nCounter, nEntrySize, static_cast<RposType>(posBeforeWrite - m_posStart));
IncrementWriteCounter();
}
void SsbRead::BeginRead(const ID &id, const uint64& nVersion)
{
std::istream& iStrm = *m_pIstrm;
SSB_LOG(mpt::format(mpt::ustring(tstrReadingHeader))(id.AsString()));
ResetReadstatus();
if (!iStrm.good())
{ AddReadNote(SNRW_BADGIVEN_STREAM); return; }
m_posStart = iStrm.tellg();
// Start bytes.
{
char temp[sizeof(s_EntryID)];
ArrayReader<char>(sizeof(s_EntryID))(iStrm, temp, sizeof(s_EntryID));
if(std::memcmp(temp, s_EntryID, sizeof(s_EntryID)))
{
AddReadNote(SNR_STARTBYTE_MISMATCH);
return;
}
}
// Compare IDs.
uint8 storedIdLen = 0;
Binaryread<uint8>(iStrm, storedIdLen);
char storedIdBuf[256];
MemsetZero(storedIdBuf);
if(storedIdLen > 0)
{
iStrm.read(storedIdBuf, storedIdLen);
}
if(!(id == ID(storedIdBuf, storedIdLen)))
{
AddReadNote(SNR_OBJECTCLASS_IDMISMATCH);
}
if ((m_Status & SNT_FAILURE) != 0)
{
SSB_LOG(mpt::ustring(strIdMismatch));
return;
}
SSB_LOG(mpt::ustring(strIdMatch));
// Header
uint8 tempU8;
Binaryread<uint8>(iStrm, tempU8);
const uint8 header = tempU8;
m_nIdbytes = ((header & 3) == 3) ? 4 : (header & 3);
if (Testbit(header, 6))
SetFlag(RwfRTwoBytesDescChar, true);
// Read headerdata size
uint32 tempU32 = 0;
mpt::IO::ReadAdaptiveInt32LE(iStrm, tempU32);
const uint32 headerdatasize = tempU32;
// If headerdatasize != 0, read known headerdata and ignore rest.
uint8 flagbyte = s_DefaultFlagbyte;
if(headerdatasize >= 2)
{
Binaryread<uint8>(iStrm, tempU8);
if(tempU8 == HeaderId_FlagByte)
Binaryread<uint8>(iStrm, flagbyte);
iStrm.ignore( (tempU8 == HeaderId_FlagByte) ? headerdatasize - 2 : headerdatasize - 1);
}
uint64 tempU64 = 0;
// Read version numeric if available.
if (Testbit(header, 4))
{
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
m_nReadVersion = tempU64;
if(tempU64 > nVersion)
AddReadNote(SNR_LOADING_OBJECT_WITH_LARGER_VERSION);
}
if (Testbit(header, 5))
{
Binaryread<uint8>(iStrm, tempU8);
iStrm.ignore(tempU8);
}
if(Testbit(flagbyte, 0)) // Custom ID?
{
Binaryread<uint8>(iStrm, tempU8);
if ((tempU8 & 1) != 0)
m_nIdbytes = IdSizeVariable;
else
m_nIdbytes = (tempU8 >> 1);
if(m_nIdbytes == 0)
AddReadNote(SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED);
}
m_nFixedEntrySize = 0;
if(Testbit(flagbyte, 1)) // Fixedsize entries?
mpt::IO::ReadAdaptiveInt32LE(iStrm, m_nFixedEntrySize);
SetFlag(RwfRMapHasStartpos, Testbit(header, 2));
SetFlag(RwfRMapHasSize, Testbit(header, 3));
SetFlag(RwfRMapHasId, (m_nIdbytes > 0));
SetFlag(RwfRMapHasDesc, Testbit(header, 7));
SetFlag(RwfRwHasMap, GetFlag(RwfRMapHasId) || GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || GetFlag(RwfRMapHasDesc));
if (GetFlag(RwfRwHasMap) == false)
{
SSB_LOG(mpt::ustring(strNoMapInFile));
}
if (Testbit(flagbyte, 2)) // Object description?
{
uint16 size = 0;
mpt::IO::ReadAdaptiveInt16LE(iStrm, size);
iStrm.ignore(size * (GetFlag(RwfRTwoBytesDescChar) ? 2 : 1));
}
if(Testbit(flagbyte, 3))
iStrm.ignore(5);
// Read entrycount
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > 16000)
// The current code can only write 16383 entries because it uses a Adaptive64LE with a fixed size=2
// Additionally, 16000 is an arbitrary limit to avoid an out-of-memory DoS when caching the map.
{ AddReadNote(SNR_TOO_MANY_ENTRIES_TO_READ); return; }
m_nReadEntrycount = static_cast<NumType>(tempU64);
if(m_nReadEntrycount == 0)
AddReadNote(SNR_ZEROENTRYCOUNT);
// Read map rpos if map exists.
if (GetFlag(RwfRwHasMap))
{
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
}
const Offtype rawEndOfHdrData = iStrm.tellg() - m_posStart;
if (rawEndOfHdrData < 0 || static_cast<uint64>(rawEndOfHdrData) > std::numeric_limits<RposType>::max())
{ AddReadNote(SNR_INSUFFICIENT_RPOSTYPE); return; }
m_rposEndofHdrData = static_cast<RposType>(rawEndOfHdrData);
m_rposMapBegin = (GetFlag(RwfRwHasMap)) ? static_cast<RposType>(tempU64) : m_rposEndofHdrData;
if (GetFlag(RwfRwHasMap) == false)
m_posMapEnd = m_posStart + m_rposEndofHdrData;
SetFlag(RwfRHeaderIsRead, true);
}
void SsbRead::CacheMap()
{
std::istream& iStrm = *m_pIstrm;
if(GetFlag(RwfRwHasMap) || m_nFixedEntrySize > 0)
{
iStrm.seekg(m_posStart + m_rposMapBegin);
if(iStrm.fail())
{ AddReadNote(SNR_BADSTREAM_AFTER_MAPHEADERSEEK); return; }
SSB_LOG(mpt::format(mpt::ustring(tstrReadingMap))(m_rposMapBegin));
mapData.resize(m_nReadEntrycount);
m_Idarray.reserve(m_nReadEntrycount * 4);
//Read map
for(NumType i = 0; i<m_nReadEntrycount; i++)
{
if(iStrm.fail())
{ AddReadNote(SNR_BADSTREAM_AT_MAP_READ); return; }
// Read ID.
uint16 nIdsize = m_nIdbytes;
if(nIdsize == IdSizeVariable) //Variablesize ID
mpt::IO::ReadAdaptiveInt16LE(iStrm, nIdsize);
const size_t nOldEnd = m_Idarray.size();
if (nIdsize > 0 && (Util::MaxValueOfType(nOldEnd) - nOldEnd >= nIdsize))
{
m_Idarray.resize(nOldEnd + nIdsize);
iStrm.read(&m_Idarray[nOldEnd], nIdsize);
}
mapData[i].nIdLength = nIdsize;
mapData[i].nIdpos = nOldEnd;
// Read position.
if(GetFlag(RwfRMapHasStartpos))
{
uint64 tempU64;
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
mapData[i].rposStart = static_cast<RposType>(tempU64);
}
// Read entry size.
if (m_nFixedEntrySize > 0)
mapData[i].nSize = m_nFixedEntrySize;
else if(GetFlag(RwfRMapHasSize)) // Map has datasize field.
{
uint64 tempU64;
mpt::IO::ReadAdaptiveInt64LE(iStrm, tempU64);
if(tempU64 > static_cast<uint64>(std::numeric_limits<Offtype>::max()))
{ AddReadNote(SNR_INSUFFICIENT_STREAM_OFFTYPE); return; }
mapData[i].nSize = static_cast<DataSize>(tempU64);
}
// If there's no entry startpos in map, count start pos from datasizes.
// Here readentry.rposStart is set to relative position from databegin.
if (mapData[i].nSize != invalidDatasize && GetFlag(RwfRMapHasStartpos) == false)
mapData[i].rposStart = (i > 0) ? mapData[i-1].rposStart + mapData[i-1].nSize : 0;
if(GetFlag(RwfRMapHasDesc)) //Map has entrydescriptions?
{
uint16 size = 0;
mpt::IO::ReadAdaptiveInt16LE(iStrm, size);
if(GetFlag(RwfRTwoBytesDescChar))
iStrm.ignore(size * 2);
else
iStrm.ignore(size);
}
}
m_posMapEnd = iStrm.tellg();
SSB_LOG(mpt::format(mpt::ustring(tstrEndOfMap))(m_posMapEnd - m_posStart));
}
SetFlag(RwfRMapCached, true);
m_posDataBegin = (m_rposMapBegin == m_rposEndofHdrData) ? m_posMapEnd : m_posStart + Postype(m_rposEndofHdrData);
m_pIstrm->seekg(m_posDataBegin);
// If there are no positions in the map but there are entry sizes, rposStart will
// be relative to data start. Now that posDataBegin is known, make them relative to
// startpos.
if (GetFlag(RwfRMapHasStartpos) == false && (GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0))
{
const RposType offset = static_cast<RposType>(m_posDataBegin - m_posStart);
for(size_t i = 0; i < m_nReadEntrycount; i++)
mapData[i].rposStart += offset;
}
}
const ReadEntry* SsbRead::Find(const ID &id)
{
m_pIstrm->clear();
if (GetFlag(RwfRMapCached) == false)
CacheMap();
if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false)
m_pIstrm->seekg(m_posDataBegin + Postype(m_nFixedEntrySize * m_nCounter));
if (GetFlag(RwfRMapHasId) == true)
{
const size_t nEntries = mapData.size();
for(size_t i0 = 0; i0 < nEntries; i0++)
{
const size_t i = (i0 + m_nNextReadHint) % nEntries;
if(mapData[i].nIdpos < m_Idarray.size() && id == ID(&m_Idarray[mapData[i].nIdpos], mapData[i].nIdLength))
{
m_nNextReadHint = (i + 1) % nEntries;
if (mapData[i].rposStart != 0)
m_pIstrm->seekg(m_posStart + Postype(mapData[i].rposStart));
return &mapData[i];
}
}
}
return nullptr;
}
void SsbWrite::FinishWrite()
{
std::ostream& oStrm = *m_pOstrm;
const Postype posDataEnd = oStrm.tellp();
Postype posMapStart = oStrm.tellp();
SSB_LOG(mpt::format(mpt::ustring(tstrWritingMap))(posMapStart - m_posStart));
if (GetFlag(RwfRwHasMap)) //Write map
{
oStrm.write(m_MapStreamString.c_str(), m_MapStreamString.length());
}
const Postype posMapEnd = oStrm.tellp();
// Write entry count.
oStrm.seekp(m_posEntrycount);
// Write a fixed size=2 Adaptive64LE because space for this value has already been reserved berforehand.
mpt::IO::WriteAdaptiveInt64LE(oStrm, m_nCounter, 2, 2);
if (GetFlag(RwfRwHasMap))
{ // Write map start position.
oStrm.seekp(m_posMapPosField);
const uint64 rposMap = posMapStart - m_posStart;
// Write a fixed size=8 Adaptive64LE because space for this value has already been reserved berforehand.
mpt::IO::WriteAdaptiveInt64LE(oStrm, rposMap, 8, 8);
}
// Seek to end.
oStrm.seekp(std::max<Postype>(posMapEnd, posDataEnd));
SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart));
}
} // namespace srlztn
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,552 @@
/*
* serialization_utils.h
* ---------------------
* Purpose: Serializing data to and from MPTM files.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "../common/typedefs.h"
#include "../common/mptTypeTraits.h"
#include "../common/mptIO.h"
#include "../common/Endianness.h"
#include <algorithm>
#include <bitset>
#include <ios>
#include <iosfwd>
#include <limits>
#include <string>
#include <vector>
#include <istream>
#include <ostream>
#include <cstring>
OPENMPT_NAMESPACE_BEGIN
namespace srlztn //SeRiaLiZaTioN
{
typedef std::ios::off_type Offtype;
typedef Offtype Postype;
typedef uintptr_t DataSize; // Data size type.
typedef uintptr_t RposType; // Relative position type.
typedef uintptr_t NumType; // Entry count type.
const DataSize invalidDatasize = DataSize(-1);
enum
{
SNT_PROGRESS = 0x08000000, // = 1 << 27
SNT_FAILURE = 0x40000000, // = 1 << 30
SNT_NOTE = 0x20000000, // = 1 << 29
SNT_WARNING = 0x10000000, // = 1 << 28
SNT_NONE = 0,
SNRW_BADGIVEN_STREAM = 1 | SNT_FAILURE,
// Read failures.
SNR_BADSTREAM_AFTER_MAPHEADERSEEK = 2 | SNT_FAILURE,
SNR_STARTBYTE_MISMATCH = 3 | SNT_FAILURE,
SNR_BADSTREAM_AT_MAP_READ = 4 | SNT_FAILURE,
SNR_INSUFFICIENT_STREAM_OFFTYPE = 5 | SNT_FAILURE,
SNR_OBJECTCLASS_IDMISMATCH = 6 | SNT_FAILURE,
SNR_TOO_MANY_ENTRIES_TO_READ = 7 | SNT_FAILURE,
SNR_INSUFFICIENT_RPOSTYPE = 8 | SNT_FAILURE,
// Read notes and warnings.
SNR_ZEROENTRYCOUNT = 0x80 | SNT_NOTE, // 0x80 == 1 << 7
SNR_NO_ENTRYIDS_WITH_CUSTOMID_DEFINED = 0x100 | SNT_NOTE,
SNR_LOADING_OBJECT_WITH_LARGER_VERSION = 0x200 | SNT_NOTE,
// Write failures.
SNW_INSUFFICIENT_FIXEDSIZE = (0x10) | SNT_FAILURE,
SNW_CHANGING_IDSIZE_WITH_FIXED_IDSIZESETTING = (0x11) | SNT_FAILURE,
SNW_DATASIZETYPE_OVERFLOW = (0x13) | SNT_FAILURE,
SNW_MAX_WRITE_COUNT_REACHED = (0x14) | SNT_FAILURE,
SNW_INSUFFICIENT_DATASIZETYPE = (0x16) | SNT_FAILURE
};
enum
{
IdSizeVariable = uint16_max,
IdSizeMaxFixedSize = (uint8_max >> 1)
};
typedef int32 SsbStatus;
struct ReadEntry
{
ReadEntry() : nIdpos(0), rposStart(0), nSize(invalidDatasize), nIdLength(0) {}
uintptr_t nIdpos; // Index of id start in ID array.
RposType rposStart; // Entry start position.
DataSize nSize; // Entry size.
uint16 nIdLength; // Length of id.
};
enum Rwf
{
RwfWMapStartPosEntry, // Write. True to include data start pos entry to map.
RwfWMapSizeEntry, // Write. True to include data size entry to map.
RwfWMapDescEntry, // Write. True to include description entry to map.
RwfWVersionNum, // Write. True to include version numeric.
RwfRMapCached, // Read. True if map has been cached.
RwfRMapHasId, // Read. True if map has IDs
RwfRMapHasStartpos, // Read. True if map data start pos.
RwfRMapHasSize, // Read. True if map has entry size.
RwfRMapHasDesc, // Read. True if map has entry description.
RwfRTwoBytesDescChar, // Read. True if map description characters are two bytes.
RwfRHeaderIsRead, // Read. True when header is read.
RwfRwHasMap, // Read/write. True if map exists.
RwfNumFlags
};
template<class T>
inline void Binarywrite(std::ostream& oStrm, const T& data)
{
mpt::IO::WriteIntLE(oStrm, data);
}
template<>
inline void Binarywrite(std::ostream& oStrm, const float& data)
{
IEEE754binary32LE tmp = IEEE754binary32LE(data);
mpt::IO::Write(oStrm, tmp);
}
template<>
inline void Binarywrite(std::ostream& oStrm, const double& data)
{
IEEE754binary64LE tmp = IEEE754binary64LE(data);
mpt::IO::Write(oStrm, tmp);
}
template <class T>
inline void WriteItem(std::ostream& oStrm, const T& data)
{
static_assert(std::is_trivial<T>::value == true, "");
Binarywrite(oStrm, data);
}
void WriteItemString(std::ostream& oStrm, const std::string &str);
template <>
inline void WriteItem<std::string>(std::ostream& oStrm, const std::string& str) {WriteItemString(oStrm, str);}
template<class T>
inline void Binaryread(std::istream& iStrm, T& data)
{
mpt::IO::ReadIntLE(iStrm, data);
}
template<>
inline void Binaryread(std::istream& iStrm, float& data)
{
IEEE754binary32LE tmp = IEEE754binary32LE(0.0f);
mpt::IO::Read(iStrm, tmp);
data = tmp;
}
template<>
inline void Binaryread(std::istream& iStrm, double& data)
{
IEEE754binary64LE tmp = IEEE754binary64LE(0.0);
mpt::IO::Read(iStrm, tmp);
data = tmp;
}
//Read only given number of bytes to the beginning of data; data bytes are memset to 0 before reading.
template <class T>
inline void Binaryread(std::istream& iStrm, T& data, const Offtype bytecount)
{
mpt::IO::ReadBinaryTruncatedLE(iStrm, data, static_cast<std::size_t>(bytecount));
}
template <>
inline void Binaryread<float>(std::istream& iStrm, float& data, const Offtype bytecount)
{
typedef IEEE754binary32LE T;
mpt::byte bytes[sizeof(T)];
std::memset(bytes, 0, sizeof(T));
mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast<std::size_t>(bytecount), sizeof(T)));
// There is not much we can sanely do for truncated floats,
// thus we ignore what we just read and return 0.
data = 0.0f;
}
template <>
inline void Binaryread<double>(std::istream& iStrm, double& data, const Offtype bytecount)
{
typedef IEEE754binary64LE T;
mpt::byte bytes[sizeof(T)];
std::memset(bytes, 0, sizeof(T));
mpt::IO::ReadRaw(iStrm, bytes, std::min(static_cast<std::size_t>(bytecount), sizeof(T)));
// There is not much we can sanely do for truncated floats,
// thus we ignore what we just read and return 0.
data = 0.0;
}
template <class T>
inline void ReadItem(std::istream& iStrm, T& data, const DataSize nSize)
{
static_assert(std::is_trivial<T>::value == true, "");
if (nSize == sizeof(T) || nSize == invalidDatasize)
Binaryread(iStrm, data);
else
Binaryread(iStrm, data, nSize);
}
void ReadItemString(std::istream& iStrm, std::string& str, const DataSize);
template <>
inline void ReadItem<std::string>(std::istream& iStrm, std::string& str, const DataSize nSize)
{
ReadItemString(iStrm, str, nSize);
}
class ID
{
private:
std::string m_ID; // NOTE: can contain null characters ('\0')
public:
ID() { }
ID(const std::string &id) : m_ID(id) { }
ID(const char *beg, const char *end) : m_ID(beg, end) { }
ID(const char *id) : m_ID(id?id:"") { }
ID(const char * str, std::size_t len) : m_ID(str, str + len) { }
template <typename T>
static ID FromInt(const T &val)
{
STATIC_ASSERT(std::numeric_limits<T>::is_integer);
typename mpt::make_le<T>::type valle;
valle = val;
return ID(std::string(mpt::as_raw_memory(valle), mpt::as_raw_memory(valle) + sizeof(valle)));
}
bool IsPrintable() const;
mpt::ustring AsString() const;
const char *GetBytes() const { return m_ID.c_str(); }
std::size_t GetSize() const { return m_ID.length(); }
bool operator == (const ID &other) const { return m_ID == other.m_ID; }
bool operator != (const ID &other) const { return m_ID != other.m_ID; }
};
class Ssb
{
protected:
Ssb();
public:
SsbStatus GetStatus() const
{
return m_Status;
}
protected:
// When writing, returns the number of entries written.
// When reading, returns the number of entries read not including unrecognized entries.
NumType GetCounter() const {return m_nCounter;}
void SetFlag(Rwf flag, bool val) {m_Flags.set(flag, val);}
bool GetFlag(Rwf flag) const {return m_Flags[flag];}
protected:
SsbStatus m_Status;
uint32 m_nFixedEntrySize; // Read/write: If > 0, data entries have given fixed size.
Postype m_posStart; // Read/write: Stream position at the beginning of object.
uint16 m_nIdbytes; // Read/Write: Tells map ID entry size in bytes. If size is variable, value is IdSizeVariable.
NumType m_nCounter; // Read/write: Keeps count of entries written/read.
std::bitset<RwfNumFlags> m_Flags; // Read/write: Various flags.
protected:
static const uint8 s_DefaultFlagbyte = 0;
static const char s_EntryID[3];
};
class SsbRead
: public Ssb
{
public:
enum ReadRv // Read return value.
{
EntryRead,
EntryNotFound
};
enum IdMatchStatus
{
IdMatch, IdMismatch
};
typedef std::vector<ReadEntry>::const_iterator ReadIterator;
SsbRead(std::istream& iStrm);
// Call this to begin reading: must be called before other read functions.
void BeginRead(const ID &id, const uint64& nVersion);
// After calling BeginRead(), this returns number of entries in the file.
NumType GetNumEntries() const {return m_nReadEntrycount;}
// Returns read iterator to the beginning of entries.
// The behaviour of read iterators is undefined if map doesn't
// contain entry ids or data begin positions.
ReadIterator GetReadBegin();
// Returns read iterator to the end(one past last) of entries.
ReadIterator GetReadEnd();
// Compares given id with read entry id
IdMatchStatus CompareId(const ReadIterator& iter, const ID &id);
uint64 GetReadVersion() {return m_nReadVersion;}
// Read item using default read implementation.
template <class T>
ReadRv ReadItem(T& obj, const ID &id) {return ReadItem(obj, id, srlztn::ReadItem<T>);}
// Read item using given function.
template <class T, class FuncObj>
ReadRv ReadItem(T& obj, const ID &id, FuncObj);
// Read item using read iterator.
template <class T>
ReadRv ReadIterItem(const ReadIterator& iter, T& obj) {return ReadIterItem(iter, obj, srlztn::ReadItem<T>);}
template <class T, class FuncObj>
ReadRv ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func);
private:
// Reads map to cache.
void CacheMap();
// Searches for entry with given ID. If found, returns pointer to corresponding entry, else
// returns nullptr.
const ReadEntry* Find(const ID &id);
// Called after reading an object.
ReadRv OnReadEntry(const ReadEntry* pE, const ID &id, const Postype& posReadBegin);
void AddReadNote(const SsbStatus s);
// Called after reading entry. pRe is a pointer to associated map entry if exists.
void AddReadNote(const ReadEntry* const pRe, const NumType nNum);
void ResetReadstatus();
private:
// mapData is a cache that facilitates faster access to the stored data
// without having to reparse on every access.
// Iterator invalidation in CacheMap() is not a problem because every code
// path that ever returns an iterator into mapData does CacheMap exactly once
// beforehand. Following calls use this already cached map. As the data is
// immutable when reading, there is no need to ever invalidate the cache and
// redo CacheMap().
std::istream* m_pIstrm; // Read: Pointer to read stream.
std::vector<char> m_Idarray; // Read: Holds entry ids.
std::vector<ReadEntry> mapData; // Read: Contains map information.
uint64 m_nReadVersion; // Read: Version is placed here when reading.
RposType m_rposMapBegin; // Read: If map exists, rpos of map begin, else m_rposEndofHdrData.
Postype m_posMapEnd; // Read: If map exists, map end position, else pos of end of hdrData.
Postype m_posDataBegin; // Read: Data begin position.
RposType m_rposEndofHdrData; // Read: rpos of end of header data.
NumType m_nReadEntrycount; // Read: Number of entries.
NumType m_nNextReadHint; // Read: Hint where to start looking for the next read entry.
};
class SsbWrite
: public Ssb
{
public:
SsbWrite(std::ostream& oStrm);
// Write header
void BeginWrite(const ID &id, const uint64& nVersion);
// Write item using default write implementation.
template <class T>
void WriteItem(const T& obj, const ID &id) {WriteItem(obj, id, &srlztn::WriteItem<T>);}
// Write item using given function.
template <class T, class FuncObj>
void WriteItem(const T& obj, const ID &id, FuncObj);
// Writes mapping.
void FinishWrite();
private:
// Called after writing an item.
void OnWroteItem(const ID &id, const Postype& posBeforeWrite);
void AddWriteNote(const SsbStatus s);
void AddWriteNote(const ID &id,
const NumType nEntryNum,
const DataSize nBytecount,
const RposType rposStart);
// Writes mapping item to mapstream.
void WriteMapItem(const ID &id,
const RposType& rposDataStart,
const DataSize& nDatasize,
const char* pszDesc);
void ResetWritestatus() {m_Status = SNT_NONE;}
void IncrementWriteCounter();
private:
std::ostream* m_pOstrm; // Write: Pointer to write stream.
Postype m_posEntrycount; // Write: Pos of entrycount field.
Postype m_posMapPosField; // Write: Pos of map position field.
std::string m_MapStreamString; // Write: Map stream string.
};
template <class T, class FuncObj>
void SsbWrite::WriteItem(const T& obj, const ID &id, FuncObj Func)
{
const Postype pos = m_pOstrm->tellp();
Func(*m_pOstrm, obj);
OnWroteItem(id, pos);
}
template <class T, class FuncObj>
SsbRead::ReadRv SsbRead::ReadItem(T& obj, const ID &id, FuncObj Func)
{
const ReadEntry* pE = Find(id);
const Postype pos = m_pIstrm->tellg();
if (pE != nullptr || GetFlag(RwfRMapHasId) == false)
Func(*m_pIstrm, obj, (pE) ? (pE->nSize) : invalidDatasize);
return OnReadEntry(pE, id, pos);
}
template <class T, class FuncObj>
SsbRead::ReadRv SsbRead::ReadIterItem(const ReadIterator& iter, T& obj, FuncObj func)
{
m_pIstrm->clear();
if (iter->rposStart != 0)
m_pIstrm->seekg(m_posStart + Postype(iter->rposStart));
const Postype pos = m_pIstrm->tellg();
func(*m_pIstrm, obj, iter->nSize);
return OnReadEntry(&(*iter), ID(&m_Idarray[iter->nIdpos], iter->nIdLength), pos);
}
inline SsbRead::IdMatchStatus SsbRead::CompareId(const ReadIterator& iter, const ID &id)
{
if(iter->nIdpos >= m_Idarray.size()) return IdMismatch;
return (id == ID(&m_Idarray[iter->nIdpos], iter->nIdLength)) ? IdMatch : IdMismatch;
}
inline SsbRead::ReadIterator SsbRead::GetReadBegin()
{
MPT_ASSERT(GetFlag(RwfRMapHasId) && (GetFlag(RwfRMapHasStartpos) || GetFlag(RwfRMapHasSize) || m_nFixedEntrySize > 0));
if (GetFlag(RwfRMapCached) == false)
CacheMap();
return mapData.begin();
}
inline SsbRead::ReadIterator SsbRead::GetReadEnd()
{
if (GetFlag(RwfRMapCached) == false)
CacheMap();
return mapData.end();
}
template <class T>
struct VectorWriter
{
VectorWriter(size_t nCount) : m_nCount(nCount) {}
void operator()(std::ostream &oStrm, const std::vector<T> &vec)
{
for(size_t i = 0; i < m_nCount; i++)
{
Binarywrite(oStrm, vec[i]);
}
}
size_t m_nCount;
};
template <class T>
struct VectorReader
{
VectorReader(size_t nCount) : m_nCount(nCount) {}
void operator()(std::istream& iStrm, std::vector<T> &vec, const size_t)
{
vec.resize(m_nCount);
for(std::size_t i = 0; i < m_nCount; ++i)
{
Binaryread(iStrm, vec[i]);
}
}
size_t m_nCount;
};
template <class T>
struct ArrayReader
{
ArrayReader(size_t nCount) : m_nCount(nCount) {}
void operator()(std::istream& iStrm, T* pData, const size_t)
{
for(std::size_t i=0; i<m_nCount; ++i)
{
Binaryread(iStrm, pData[i]);
}
}
size_t m_nCount;
};
} //namespace srlztn.
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,20 @@
/*
* StdAfx.cpp
* ----------
* Purpose: Source file that includes just the standard includes
* Notes : mptrack.pch will be the pre-compiled header
* stdafx.obj will contain the pre-compiled type information
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
OPENMPT_NAMESPACE_BEGIN
MPT_MSVC_WORKAROUND_LNK4221(stdafx)
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,124 @@
/*
* StdAfx.h
* --------
* Purpose: Include file for standard system include files, or project specific include files that are used frequently, but are changed infrequently. Also includes the global build settings from BuildSettings.h.
* Notes : (currently none)
* Authors: Olivier Lapicque
* OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
// has to be first
#include "BuildSettings.h"
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
#if !defined(MPT_BUILD_WINESUPPORT)
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS // Do not include support for MFC controls in dialogs (reduces binary bloat; remove this #define if you want to use MFC controls)
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxcmn.h> // MFC support for Windows Common Controls
#include <afxcview.h>
#include <afxdlgs.h>
#include <afxole.h>
#endif // !MPT_BUILD_WINESUPPORT
#include <windows.h>
#include <windowsx.h>
#include <shlwapi.h>
#include <mmsystem.h>
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#if MPT_COMPILER_MSVC
#include <intrin.h>
#endif
#if MPT_OS_WINDOWS
#if MPT_COMPILER_MSVCCLANGC2
// windows.h references IUnknown in a template function without having it even forward-declared.
// Clang does not like that. Forward-declaration fixes it.
struct IUnknown;
#endif
#endif
// this will be available everywhere
#include "../common/typedefs.h"
// <memory>
// <new>
// <climits>
// <cstddef>
// <cstdint>
// <stdint.h>
#include "../common/mptTypeTraits.h"
// <type_traits>
#include "../common/mptString.h"
// <algorithm>
// <limits>
// <string>
// <type_traits>
// <cstring>
#include "../common/mptStringFormat.h"
#include "../common/mptPathString.h"
#include "../common/Logging.h"
#include "../common/misc_util.h"
// <algorithm>
// <limits>
// <string>
// <type_traits>
// <vector>
// <cmath>
// <cstdlib>
// <cstring>
// <time.h>
// for std::abs
#include <cstdlib>
#include <stdlib.h>
#include <cmath>
#include <math.h>
#if defined(MPT_ENABLE_FILEIO_STDIO)
// for FILE* definition (which cannot be forward-declared in a portable way)
#include <cstdio>
#include <stdio.h>
#endif
#ifndef NO_VST
// VST SDK includes these headers after messing with default compiler structure
// packing. No problem in practice as VST SDK sets packing matching the default
// packing and we are compiling with default packing and standard headers should
// be careful about structure packing anyway, but it is very much unclean
// nonetheless. Pre-include the affected headers here as a future-proof
// safe-guard and let their own include guards handle the further including by
// VST SDK.
#include <cstdint>
#include <stdint.h>
#include <cstring>
#include <string.h>
#endif
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

View file

@ -0,0 +1,49 @@
/*
* typedefs.cpp
* ------------
* Purpose: Basic data type definitions and assorted compiler-related helpers.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "typedefs.h"
#include "Endianness.h"
OPENMPT_NAMESPACE_BEGIN
#if MPT_PLATFORM_ENDIAN_KNOWN
MPT_MSVC_WORKAROUND_LNK4221(typedefs)
#else
int24::int24(int other)
{
MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) {
bytes[0] = (static_cast<unsigned int>(other)>>16)&0xff;
bytes[1] = (static_cast<unsigned int>(other)>> 8)&0xff;
bytes[2] = (static_cast<unsigned int>(other)>> 0)&0xff;
} else {
bytes[0] = (static_cast<unsigned int>(other)>> 0)&0xff;
bytes[1] = (static_cast<unsigned int>(other)>> 8)&0xff;
bytes[2] = (static_cast<unsigned int>(other)>>16)&0xff;
}
}
int24::operator int() const
{
MPT_MAYBE_CONSTANT_IF(mpt::endian_is_big()) {
return (static_cast<int8>(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2];
} else {
return (static_cast<int8>(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0];
}
}
#endif
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,544 @@
/*
* typedefs.h
* ----------
* Purpose: Basic data type definitions and assorted compiler-related helpers.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
// Advanced inline attributes
#if MPT_COMPILER_MSVC
#define MPT_FORCEINLINE __forceinline
#define MPT_NOINLINE __declspec(noinline)
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_FORCEINLINE __attribute__((always_inline)) inline
#define MPT_NOINLINE __attribute__((noinline))
#else
#define MPT_FORCEINLINE inline
#define MPT_NOINLINE
#endif
// constexpr
#define MPT_CONSTEXPR11_FUN constexpr MPT_FORCEINLINE
#define MPT_CONSTEXPR11_VAR constexpr
#if MPT_CXX_AT_LEAST(14)
#define MPT_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE
#define MPT_CONSTEXPR14_VAR constexpr
#else
#define MPT_CONSTEXPR14_FUN MPT_FORCEINLINE
#define MPT_CONSTEXPR14_VAR const
#endif
// C++17 std::size
OPENMPT_NAMESPACE_END
#include <cstddef>
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
template <typename T>
MPT_CONSTEXPR11_FUN auto size(const T & v) -> decltype(v.size())
{
return v.size();
}
template <typename T, std::size_t N>
MPT_CONSTEXPR11_FUN std::size_t size(const T(&)[N]) noexcept
{
return N;
}
} // namespace mpt
// MPT_ARRAY_COUNT macro computes the number of elements in a statically-allocated array.
#if MPT_COMPILER_MSVC
OPENMPT_NAMESPACE_END
#include <cstdlib>
OPENMPT_NAMESPACE_BEGIN
#define MPT_ARRAY_COUNT(x) _countof(x)
#else
#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0]))
#endif
// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased.
#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_RESTRICT __restrict
#else
#define MPT_RESTRICT
#endif
// Some functions might be deprecated although they are still in use.
// Tag them with "MPT_DEPRECATED".
#if MPT_COMPILER_MSVC
#define MPT_DEPRECATED __declspec(deprecated)
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_DEPRECATED __attribute__((deprecated))
#else
#define MPT_DEPRECATED
#endif
#if defined(MODPLUG_TRACKER)
#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED
#define MPT_DEPRECATED_LIBOPENMPT
#elif defined(LIBOPENMPT_BUILD)
#define MPT_DEPRECATED_TRACKER
#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED
#else
#define MPT_DEPRECATED_TRACKER MPT_DEPRECATED
#define MPT_DEPRECATED_LIBOPENMPT MPT_DEPRECATED
#endif
OPENMPT_NAMESPACE_END
#include <memory>
#include <utility>
OPENMPT_NAMESPACE_BEGIN
#if MPT_CXX_AT_LEAST(14)
namespace mpt {
using std::make_unique;
} // namespace mpt
#else
namespace mpt {
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace mpt
#endif
#if MPT_COMPILER_MSVC
#define MPT_CONSTANT_IF(x) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
if(x) \
__pragma(warning(pop)) \
/**/
#define MPT_MAYBE_CONSTANT_IF(x) \
__pragma(warning(push)) \
__pragma(warning(disable:4127)) \
if(x) \
__pragma(warning(pop)) \
/**/
#endif
#if MPT_COMPILER_GCC
#define MPT_MAYBE_CONSTANT_IF(x) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wtype-limits\"") \
if(x) \
_Pragma("GCC diagnostic pop") \
/**/
#endif
#if MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_MAYBE_CONSTANT_IF(x) \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \
_Pragma("clang diagnostic ignored \"-Wtype-limits\"") \
_Pragma("clang diagnostic ignored \"-Wtautological-constant-out-of-range-compare\"") \
if(x) \
_Pragma("clang diagnostic pop") \
/**/
#endif
#if !defined(MPT_CONSTANT_IF)
// MPT_CONSTANT_IF disables compiler warnings for conditions that are either always true or always false for some reason (dependent on template arguments for example)
#define MPT_CONSTANT_IF(x) if(x)
#endif
#if !defined(MPT_MAYBE_CONSTANT_IF)
// MPT_MAYBE_CONSTANT_IF disables compiler warnings for conditions that may in some case be either always false or always true (this may turn out to be useful in ASSERTions in some cases).
#define MPT_MAYBE_CONSTANT_IF(x) if(x)
#endif
#if MPT_COMPILER_MSVC
// MSVC warns for the well-known and widespread "do { } while(0)" idiom with warning level 4 ("conditional expression is constant").
// It does not warn with "while(0,0)". However this again causes warnings with other compilers.
// Solve it with a macro.
#define MPT_DO do
#define MPT_WHILE_0 while(0,0)
#endif
#ifndef MPT_DO
#define MPT_DO do
#endif
#ifndef MPT_WHILE_0
#define MPT_WHILE_0 while(0)
#endif
#if MPT_COMPILER_MSVC && defined(UNREFERENCED_PARAMETER)
#define MPT_UNREFERENCED_PARAMETER(x) UNREFERENCED_PARAMETER(x)
#else
#define MPT_UNREFERENCED_PARAMETER(x) (void)(x)
#endif
#define MPT_UNUSED_VARIABLE(x) MPT_UNREFERENCED_PARAMETER(x)
// Exception handling helpers, because MFC requires explicit deletion of the exception object,
// Thus, always call exactly one of MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() or MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e).
#if defined(_MFC_VER)
#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { AfxThrowMemoryException(); } MPT_WHILE_0
#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( CMemoryException * e )
#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0
#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { if(e) { e->Delete(); e = nullptr; } } MPT_WHILE_0
#else // !_MFC_VER
OPENMPT_NAMESPACE_END
#include <new>
OPENMPT_NAMESPACE_BEGIN
#define MPT_EXCEPTION_THROW_OUT_OF_MEMORY() MPT_DO { throw std::bad_alloc(); } MPT_WHILE_0
#define MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e) catch ( const std::bad_alloc & e )
#define MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY() MPT_DO { throw; } MPT_WHILE_0
#define MPT_EXCEPTION_DELETE_OUT_OF_MEMORY(e) MPT_DO { MPT_UNUSED_VARIABLE(e); } MPT_WHILE_0
#endif // _MFC_VER
// Static code checkers might need to get the knowledge of our assertions transferred to them.
#define MPT_CHECKER_ASSUME_ASSERTIONS 1
//#define MPT_CHECKER_ASSUME_ASSERTIONS 0
#ifdef MPT_BUILD_ANALYZED
#if MPT_COMPILER_MSVC
#if MPT_CHECKER_ASSUME_ASSERTIONS
#define MPT_CHECKER_ASSUME(x) __analysis_assume(!!(x))
#endif
#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#if MPT_CHECKER_ASSUME_ASSERTIONS
#ifdef NDEBUG
#error "Builds for static analyzers depend on std::assert being enabled, but the current build has #define NDEBUG. This makes no sense."
#endif
OPENMPT_NAMESPACE_END
#include <cassert>
OPENMPT_NAMESPACE_BEGIN
#define MPT_CHECKER_ASSUME(x) assert(!!(x))
#endif
#endif // MPT_COMPILER
#endif // MPT_BUILD_ANALYZED
#ifndef MPT_CHECKER_ASSUME
#define MPT_CHECKER_ASSUME(x) MPT_DO { } MPT_WHILE_0
#endif
#if defined(_MFC_VER)
#if !defined(ASSERT)
#error "MFC is expected to #define ASSERT"
#endif // !defined(ASERRT)
#define MPT_FRAMEWORK_ASSERT_IS_DEFINED
#if defined(_DEBUG)
#define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1
#else // !_DEBUG
#define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0
#endif // _DEBUG
// let MFC handle our asserts
#define MPT_ASSERT_USE_FRAMEWORK 1
#else // !_MFC_VER
#if defined(ASSERT)
#define MPT_FRAMEWORK_ASSERT_IS_DEFINED
#if defined(_DEBUG)
#define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 1
#else // !_DEBUG
#define MPT_FRAMEWORK_ASSERT_IS_ACTIVE 0
#endif // _DEBUG
#endif // !defined(ASERRT)
// handle assert in our own way without relying on some platform-/framework-specific assert implementation
#define MPT_ASSERT_USE_FRAMEWORK 0
#endif // _MFC_VER
#if defined(MPT_FRAMEWORK_ASSERT_IS_DEFINED) && (MPT_ASSERT_USE_FRAMEWORK == 1)
#define MPT_ASSERT_NOTREACHED() ASSERT(0)
#define MPT_ASSERT(expr) ASSERT((expr))
#define MPT_ASSERT_MSG(expr, msg) ASSERT((expr) && (msg))
#if (MPT_FRAMEWORK_ASSERT_IS_ACTIVE == 1)
#define MPT_ASSERT_ALWAYS(expr) ASSERT((expr))
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) ASSERT((expr) && (msg))
#else
#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#ifndef MPT_ASSERT_HANDLER_NEEDED
#define MPT_ASSERT_HANDLER_NEEDED
#endif
#endif
#elif defined(NO_ASSERTS)
#define MPT_ASSERT_NOTREACHED() MPT_CHECKER_ASSUME(0)
#define MPT_ASSERT(expr) MPT_CHECKER_ASSUME(expr)
#define MPT_ASSERT_MSG(expr, msg) MPT_CHECKER_ASSUME(expr)
#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#ifndef MPT_ASSERT_HANDLER_NEEDED
#define MPT_ASSERT_HANDLER_NEEDED
#endif
#else // !NO_ASSERTS
#define MPT_ASSERT_NOTREACHED() MPT_DO { MPT_CONSTANT_IF(!(0)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0
#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(__FILE__, __LINE__, __FUNCTION__, #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#ifndef MPT_ASSERT_HANDLER_NEEDED
#define MPT_ASSERT_HANDLER_NEEDED
#endif
#endif // NO_ASSERTS
#if defined(MPT_ASSERT_HANDLER_NEEDED)
// custom assert handler needed
MPT_NOINLINE void AssertHandler(const char *file, int line, const char *function, const char *expr, const char *msg=nullptr);
#endif // MPT_ASSERT_HANDLER_NEEDED
// Compile time assert.
#define MPT_STATIC_ASSERT(expr) static_assert((expr), "compile time assertion failed: " #expr)
// Macro for marking intentional fall-throughs in switch statements - can be used for static analysis if supported.
#if (MPT_CXX >= 17)
#define MPT_FALLTHROUGH [[fallthrough]]
#elif MPT_COMPILER_MSVC
#define MPT_FALLTHROUGH __fallthrough
#elif MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_FALLTHROUGH [[clang::fallthrough]]
#elif MPT_COMPILER_GCC && MPT_GCC_AT_LEAST(7,1,0)
#define MPT_FALLTHROUGH __attribute__((fallthrough))
#elif defined(__has_cpp_attribute)
#if __has_cpp_attribute(fallthrough)
#define MPT_FALLTHROUGH [[fallthrough]]
#else
#define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
#endif
#else
#define MPT_FALLTHROUGH MPT_DO { } MPT_WHILE_0
#endif
OPENMPT_NAMESPACE_END
#include <climits>
#include <cstdint>
#include <stdint.h>
OPENMPT_NAMESPACE_BEGIN
typedef std::int8_t int8;
typedef std::int16_t int16;
typedef std::int32_t int32;
typedef std::int64_t int64;
typedef std::uint8_t uint8;
typedef std::uint16_t uint16;
typedef std::uint32_t uint32;
typedef std::uint64_t uint64;
const int8 int8_min = INT8_MIN;
const int16 int16_min = INT16_MIN;
const int32 int32_min = INT32_MIN;
const int64 int64_min = INT64_MIN;
const int8 int8_max = INT8_MAX;
const int16 int16_max = INT16_MAX;
const int32 int32_max = INT32_MAX;
const int64 int64_max = INT64_MAX;
const uint8 uint8_max = UINT8_MAX;
const uint16 uint16_max = UINT16_MAX;
const uint32 uint32_max = UINT32_MAX;
const uint64 uint64_max = UINT64_MAX;
// 24-bit integer wrapper (for 24-bit PCM)
struct int24
{
uint8 bytes[3];
int24() { bytes[0] = bytes[1] = bytes[2] = 0; }
#if MPT_PLATFORM_ENDIAN_KNOWN
explicit int24(int other)
{
#ifdef MPT_PLATFORM_BIG_ENDIAN
bytes[0] = (static_cast<unsigned int>(other)>>16)&0xff;
bytes[1] = (static_cast<unsigned int>(other)>> 8)&0xff;
bytes[2] = (static_cast<unsigned int>(other)>> 0)&0xff;
#else
bytes[0] = (static_cast<unsigned int>(other)>> 0)&0xff;
bytes[1] = (static_cast<unsigned int>(other)>> 8)&0xff;
bytes[2] = (static_cast<unsigned int>(other)>>16)&0xff;
#endif
}
operator int() const
{
#ifdef MPT_PLATFORM_BIG_ENDIAN
return (static_cast<int8>(bytes[0]) * 65536) + (bytes[1] * 256) + bytes[2];
#else
return (static_cast<int8>(bytes[2]) * 65536) + (bytes[1] * 256) + bytes[0];
#endif
}
#else
explicit int24(int other);
operator int() const;
#endif
};
MPT_STATIC_ASSERT(sizeof(int24) == 3);
#define int24_min (0-0x00800000)
#define int24_max (0+0x007fffff)
typedef float float32;
MPT_STATIC_ASSERT(sizeof(float32) == 4);
typedef double float64;
MPT_STATIC_ASSERT(sizeof(float64) == 8);
MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*));
namespace mpt {
MPT_STATIC_ASSERT(CHAR_BIT == 8);
MPT_STATIC_ASSERT(sizeof(char) == 1);
typedef unsigned char byte;
MPT_STATIC_ASSERT(sizeof(mpt::byte) == 1);
} // namespace mpt
#if MPT_COMPILER_MSVC
#if defined(_M_X64)
#define MPT_ARCH_BITS 64
#define MPT_ARCH_BITS_32 0
#define MPT_ARCH_BITS_64 1
#elif defined(_M_IX86)
#define MPT_ARCH_BITS 32
#define MPT_ARCH_BITS_32 1
#define MPT_ARCH_BITS_64 0
#endif
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#if defined(__SIZEOF_POINTER__)
#if (__SIZEOF_POINTER__ == 8)
#define MPT_ARCH_BITS 64
#define MPT_ARCH_BITS_32 0
#define MPT_ARCH_BITS_64 1
#elif (__SIZEOF_POINTER__ == 4)
#define MPT_ARCH_BITS 32
#define MPT_ARCH_BITS_32 1
#define MPT_ARCH_BITS_64 0
#endif
#endif
#endif // MPT_COMPILER
// fallback
#if !defined(MPT_ARCH_BITS)
#include <cstdint>
#include <stdint.h>
MPT_STATIC_ASSERT(sizeof(std::uintptr_t) == sizeof(void*));
#if defined(UINTPTR_MAX)
#if (UINTPTR_MAX == 0xffffffffffffffffull)
#define MPT_ARCH_BITS 64
#define MPT_ARCH_BITS_32 0
#define MPT_ARCH_BITS_64 1
#elif (UINTPTR_MAX == 0xffffffffu)
#define MPT_ARCH_BITS 32
#define MPT_ARCH_BITS_32 1
#define MPT_ARCH_BITS_64 0
#endif
#endif // UINTPTR_MAX
#endif // MPT_ARCH_BITS
#if MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#define MPT_PRINTF_FUNC(formatstringindex,varargsindex) __attribute__((format(printf, formatstringindex, varargsindex)))
#else
#define MPT_PRINTF_FUNC(formatstringindex,varargsindex)
#endif
#if MPT_COMPILER_MSVC
// warning LNK4221: no public symbols found; archive member will be inaccessible
// There is no way to selectively disable linker warnings.
// #pragma warning does not apply and a command line option does not exist.
// Some options:
// 1. Macro which generates a variable with a unique name for each file (which means we have to pass the filename to the macro)
// 2. unnamed namespace containing any symbol (does not work for c++11 compilers because they actually have internal linkage now)
// 3. An unused trivial inline function.
// Option 3 does not actually solve the problem though, which leaves us with option 1.
// In any case, for optimized builds, the linker will just remove the useless symbol.
#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y) x##y
#define MPT_MSVC_WORKAROUND_LNK4221_CONCAT(x,y) MPT_MSVC_WORKAROUND_LNK4221_CONCAT_DETAIL(x,y)
#define MPT_MSVC_WORKAROUND_LNK4221(x) int MPT_MSVC_WORKAROUND_LNK4221_CONCAT(mpt_msvc_workaround_lnk4221_,x) = 0;
#endif
#ifndef MPT_MSVC_WORKAROUND_LNK4221
#define MPT_MSVC_WORKAROUND_LNK4221(x)
#endif
// legacy
#define CountOf(x) MPT_ARRAY_COUNT(x)
#define STATIC_ASSERT(x) MPT_STATIC_ASSERT(x)
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,794 @@
/*
* version.cpp
* -----------
* Purpose: OpenMPT version handling.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "version.h"
#include "mptString.h"
#include "mptStringFormat.h"
#include "mptStringParse.h"
#include "versionNumber.h"
#include "svn_version.h"
OPENMPT_NAMESPACE_BEGIN
namespace MptVersion {
static_assert((MPT_VERSION_NUMERIC & 0xffff) != 0x0000, "Version numbers ending in .00.00 shall never exist again, as they make interpreting the version number ambiguous for file formats which can only store the two major parts of the version number (e.g. IT and S3M).");
const VersionNum num = MPT_VERSION_NUMERIC;
const char * const str = MPT_VERSION_STR;
std::string GetOpenMPTVersionStr()
{
return std::string("OpenMPT " MPT_VERSION_STR);
}
VersionNum ToNum(const std::string &s)
{
VersionNum result = 0;
std::vector<std::string> numbers = mpt::String::Split<std::string>(s, std::string("."));
for(std::size_t i = 0; i < numbers.size() && i < 4; ++i)
{
result |= (mpt::String::Parse::Hex<unsigned int>(numbers[i]) & 0xff) << ((3-i)*8);
}
return result;
}
std::string ToStr(const VersionNum v)
{
if(v == 0)
{
// Unknown version
return "Unknown";
} else if((v & 0xFFFF) == 0)
{
// Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header)
return mpt::format("%1.%2")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF));
} else
{
// Full version info available
return mpt::format("%1.%2.%3.%4")(mpt::fmt::HEX((v >> 24) & 0xFF), mpt::fmt::HEX0<2>((v >> 16) & 0xFF), mpt::fmt::HEX0<2>((v >> 8) & 0xFF), mpt::fmt::HEX0<2>((v) & 0xFF));
}
}
mpt::ustring ToUString(const VersionNum v)
{
if(v == 0)
{
// Unknown version
return MPT_USTRING("Unknown");
} else if((v & 0xFFFF) == 0)
{
// Only parts of the version number are known (e.g. when reading the version from the IT or S3M file header)
return mpt::format(MPT_USTRING("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF));
} else
{
// Full version info available
return mpt::format(MPT_USTRING("%1.%2.%3.%4"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF), mpt::ufmt::HEX0<2>((v >> 8) & 0xFF), mpt::ufmt::HEX0<2>((v) & 0xFF));
}
}
VersionNum RemoveBuildNumber(const VersionNum num_)
{
return (num_ & 0xFFFFFF00);
}
bool IsTestBuild(const VersionNum num_)
{
return (
// Legacy
(num_ > MAKE_VERSION_NUMERIC(1,17,02,54) && num_ < MAKE_VERSION_NUMERIC(1,18,02,00) && num_ != MAKE_VERSION_NUMERIC(1,18,00,00))
||
// Test builds have non-zero VER_MINORMINOR
(num_ > MAKE_VERSION_NUMERIC(1,18,02,00) && RemoveBuildNumber(num_) != num_)
);
}
bool IsDebugBuild()
{
#ifdef _DEBUG
return true;
#else
return false;
#endif
}
static std::string GetUrl()
{
#ifdef OPENMPT_VERSION_URL
return OPENMPT_VERSION_URL;
#else
return "";
#endif
}
static int GetRevision()
{
#if defined(OPENMPT_VERSION_REVISION)
return OPENMPT_VERSION_REVISION;
#elif defined(OPENMPT_VERSION_SVNVERSION)
std::string svnversion = OPENMPT_VERSION_SVNVERSION;
if(svnversion.length() == 0)
{
return 0;
}
if(svnversion.find(":") != std::string::npos)
{
svnversion = svnversion.substr(svnversion.find(":") + 1);
}
if(svnversion.find("-") != std::string::npos)
{
svnversion = svnversion.substr(svnversion.find("-") + 1);
}
if(svnversion.find("M") != std::string::npos)
{
svnversion = svnversion.substr(0, svnversion.find("M"));
}
if(svnversion.find("S") != std::string::npos)
{
svnversion = svnversion.substr(0, svnversion.find("S"));
}
if(svnversion.find("P") != std::string::npos)
{
svnversion = svnversion.substr(0, svnversion.find("P"));
}
return ConvertStrTo<int>(svnversion);
#else
#if MPT_COMPILER_MSVC
#pragma message("SVN revision unknown. Please check your build system.")
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG || MPT_COMPILER_MSVCCLANGC2
#warning "SVN revision unknown. Please check your build system."
#else
// There is no portable way to display a warning.
// Try to provoke a warning with an unused variable.
int SVN_revision_unknown__Please_check_your_build_system;
#endif
return 0;
#endif
}
static bool IsDirty()
{
#if defined(OPENMPT_VERSION_DIRTY)
return OPENMPT_VERSION_DIRTY != 0;
#elif defined(OPENMPT_VERSION_SVNVERSION)
std::string svnversion = OPENMPT_VERSION_SVNVERSION;
if(svnversion.length() == 0)
{
return false;
}
if(svnversion.find("M") != std::string::npos)
{
return true;
}
return false;
#else
return false;
#endif
}
static bool HasMixedRevisions()
{
#if defined(OPENMPT_VERSION_MIXEDREVISIONS)
return OPENMPT_VERSION_MIXEDREVISIONS != 0;
#elif defined(OPENMPT_VERSION_SVNVERSION)
std::string svnversion = OPENMPT_VERSION_SVNVERSION;
if(svnversion.length() == 0)
{
return false;
}
if(svnversion.find(":") != std::string::npos)
{
return true;
}
if(svnversion.find("-") != std::string::npos)
{
return true;
}
if(svnversion.find("S") != std::string::npos)
{
return true;
}
if(svnversion.find("P") != std::string::npos)
{
return true;
}
return false;
#else
return false;
#endif
}
static bool IsPackage()
{
#if defined(OPENMPT_VERSION_IS_PACKAGE)
return OPENMPT_VERSION_IS_PACKAGE != 0;
#else
return false;
#endif
}
static std::string GetSourceDate()
{
#if defined(OPENMPT_VERSION_DATE)
return OPENMPT_VERSION_DATE;
#else
return "";
#endif
}
SourceInfo GetSourceInfo()
{
SourceInfo result;
result.Url = GetUrl();
result.Revision = GetRevision();
result.IsDirty = IsDirty();
result.HasMixedRevisions = HasMixedRevisions();
result.IsPackage = IsPackage();
result.Date = GetSourceDate();
return result;
}
std::string SourceInfo::GetStateString() const
{
std::string retval;
if(IsDirty)
{
retval += "+dirty";
}
if(HasMixedRevisions)
{
retval += "+mixed";
}
if(retval.empty())
{
retval += "clean";
}
if(IsPackage)
{
retval += "-pkg";
}
return retval;
}
std::string GetBuildDateString()
{
#ifdef MODPLUG_TRACKER
#if defined(OPENMPT_BUILD_DATE)
return OPENMPT_BUILD_DATE;
#else
return __DATE__ " " __TIME__ ;
#endif
#else // !MODPLUG_TRACKER
return GetSourceInfo().Date;
#endif // MODPLUG_TRACKER
}
static std::string GetBuildFlagsString()
{
std::string retval;
#ifdef MODPLUG_TRACKER
if(IsTestBuild())
{
retval += " TEST";
}
#endif // MODPLUG_TRACKER
if(IsDebugBuild())
{
retval += " DEBUG";
}
return retval;
}
std::string GetBuildFeaturesString()
{
std::string retval;
#ifdef LIBOPENMPT_BUILD
#if defined(MPT_CHARSET_WIN32)
retval += " +WINAPI";
#endif
#if defined(MPT_CHARSET_ICONV)
retval += " +ICONV";
#endif
#if defined(MPT_CHARSET_CODECVTUTF8)
retval += " +CODECVTUTF8";
#endif
#if defined(MPT_CHARSET_INTERNAL)
retval += " +INTERNALCHARSETS";
#endif
#if defined(MPT_WITH_ZLIB)
retval += " +ZLIB";
#endif
#if defined(MPT_WITH_MINIZ)
retval += " +MINIZ";
#endif
#if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ)
retval += " -INFLATE";
#endif
#if defined(MPT_WITH_MPG123)
retval += " +MPG123";
#endif
#if defined(MPT_WITH_MINIMP3)
retval += " +MINIMP3";
#endif
#if defined(MPT_WITH_MEDIAFOUNDATION)
retval += " +MF";
#endif
#if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION)
retval += " -MP3";
#endif
#if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)
retval += " +VORBIS";
#endif
#if defined(MPT_WITH_STBVORBIS)
retval += " +STBVORBIS";
#endif
#if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS)
retval += " -VORBIS";
#endif
#if !defined(NO_PLUGINS)
retval += " +PLUGINS";
#else
retval += " -PLUGINS";
#endif
#if !defined(NO_DMO)
retval += " +DMO";
#endif
#endif
#ifdef MODPLUG_TRACKER
#if (MPT_ARCH_BITS == 64)
if (true
&& (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP64)
&& (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP64)
) {
retval += " WIN64OLD";
}
#elif (MPT_ARCH_BITS == 32)
if (true
&& (mpt::Windows::Version::GetMinimumKernelLevel() <= mpt::Windows::Version::WinXP)
&& (mpt::Windows::Version::GetMinimumAPILevel() <= mpt::Windows::Version::WinXP)
) {
retval += " WIN32OLD";
}
#endif
#if defined(UNICODE)
retval += " UNICODE";
#else
retval += " ANSI";
#endif
#ifdef NO_VST
retval += " NO_VST";
#endif
#ifdef NO_DMO
retval += " NO_DMO";
#endif
#ifdef NO_PLUGINS
retval += " NO_PLUGINS";
#endif
#ifndef MPT_WITH_ASIO
retval += " NO_ASIO";
#endif
#ifndef MPT_WITH_DSOUND
retval += " NO_DSOUND";
#endif
#endif
return retval;
}
std::string GetBuildCompilerString()
{
std::string retval;
#if MPT_COMPILER_GENERIC
retval += "*Generic C++11 Compiler";
#elif MPT_COMPILER_MSVC
#if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0)
retval += mpt::format("Microsoft Compiler %1.%2.%3.%4")
( _MSC_FULL_VER / 10000000
, mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100)
, mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000)
, mpt::fmt::dec0<2>(_MSC_BUILD)
);
#elif defined(_MSC_FULL_VER)
retval += mpt::format("Microsoft Compiler %1.%2.%3")
( _MSC_FULL_VER / 10000000
, mpt::fmt::dec0<2>((_MSC_FULL_VER / 100000) % 100)
, mpt::fmt::dec0<5>(_MSC_FULL_VER % 100000)
);
#else
retval += mpt::format("Microsoft Compiler %1.%2")(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100);
#endif
#elif MPT_COMPILER_GCC
retval += mpt::format("GNU Compiler Collection %1.%2.%3")(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100);
#elif MPT_COMPILER_CLANG
retval += mpt::format("Clang %1.%2.%3")(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100);
#elif MPT_COMPILER_MSVCCLANGC2
retval += mpt::format("MSVC-Clang/C2 %1")(MPT_COMPILER_MSVCCLANGC2_VERSION);
#else
retval += "*unknown";
#endif
return retval;
}
static std::string GetRevisionString()
{
std::string result;
if(GetRevision() == 0)
{
return result;
}
result = std::string("-r") + mpt::fmt::val(GetRevision());
if(HasMixedRevisions())
{
result += "!";
}
if(IsDirty())
{
result += "+";
}
if(IsPackage())
{
result += "p";
}
return result;
}
mpt::ustring GetDownloadURL()
{
#ifdef MODPLUG_TRACKER
return (MptVersion::IsDebugBuild() || MptVersion::IsTestBuild() || MptVersion::IsDirty() || MptVersion::HasMixedRevisions())
?
MPT_USTRING("https://buildbot.openmpt.org/builds/")
:
MPT_USTRING("https://openmpt.org/download")
;
#else
return MPT_USTRING("https://lib.openmpt.org/");
#endif
}
std::string GetVersionString(FlagSet<MptVersion::Strings> strings)
{
std::vector<std::string> result;
if(strings[StringVersion])
{
result.push_back(MPT_VERSION_STR);
}
if(strings[StringRevision])
{
if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions())
{
result.push_back(GetRevisionString());
}
}
if(strings[StringBitness])
{
result.push_back(mpt::format(" %1 bit")(sizeof(void*)*8));
}
if(strings[StringSourceInfo])
{
const SourceInfo sourceInfo = GetSourceInfo();
if(!sourceInfo.GetUrlWithRevision().empty())
{
result.push_back(mpt::format(" %1")(sourceInfo.GetUrlWithRevision()));
}
if(!sourceInfo.Date.empty())
{
result.push_back(mpt::format(" (%1)")(sourceInfo.Date));
}
if(!sourceInfo.GetStateString().empty())
{
result.push_back(mpt::format(" %1")(sourceInfo.GetStateString()));
}
}
if(strings[StringBuildFlags])
{
if(IsDebugBuild() || IsTestBuild() || IsDirty() || HasMixedRevisions())
{
result.push_back(GetBuildFlagsString());
}
}
if(strings[StringBuildFeatures])
{
result.push_back(GetBuildFeaturesString());
}
return mpt::String::Trim(mpt::String::Combine<std::string>(result, std::string("")));
}
std::string GetVersionStringPure()
{
FlagSet<MptVersion::Strings> strings;
strings |= MptVersion::StringVersion;
strings |= MptVersion::StringRevision;
#ifdef MODPLUG_TRACKER
strings |= MptVersion::StringBitness;
#endif
return GetVersionString(strings);
}
std::string GetVersionStringSimple()
{
FlagSet<MptVersion::Strings> strings;
strings |= MptVersion::StringVersion;
strings |= MptVersion::StringRevision;
strings |= MptVersion::StringBuildFlags;
return GetVersionString(strings);
}
std::string GetVersionStringExtended()
{
FlagSet<MptVersion::Strings> strings;
strings |= MptVersion::StringVersion;
strings |= MptVersion::StringRevision;
#ifdef MODPLUG_TRACKER
strings |= MptVersion::StringBitness;
#endif
#ifndef MODPLUG_TRACKER
strings |= MptVersion::StringSourceInfo;
#endif
strings |= MptVersion::StringBuildFlags;
#ifdef MODPLUG_TRACKER
strings |= MptVersion::StringBuildFeatures;
#endif
return GetVersionString(strings);
}
std::string SourceInfo::GetUrlWithRevision() const
{
if(Url.empty() || (Revision == 0))
{
return std::string();
}
return Url + "@" + mpt::fmt::val(Revision);
}
mpt::ustring GetURL(std::string key)
{
mpt::ustring result;
if(key.empty())
{
result = mpt::ustring();
} else if(key == "website")
{
#ifdef LIBOPENMPT_BUILD
result = MPT_USTRING("https://lib.openmpt.org/");
#else
result = MPT_USTRING("https://openmpt.org/");
#endif
} else if(key == "forum")
{
result = MPT_USTRING("https://forum.openmpt.org/");
} else if(key == "bugtracker")
{
result = MPT_USTRING("https://bugs.openmpt.org/");
} else if(key == "updates")
{
result = MPT_USTRING("https://openmpt.org/download");
} else if(key == "top_picks")
{
result = MPT_USTRING("https://openmpt.org/top_picks");
}
return result;
}
mpt::ustring GetFullCreditsString()
{
return mpt::ToUnicode(mpt::CharsetUTF8,
#ifdef MODPLUG_TRACKER
"OpenMPT / ModPlug Tracker\n"
#else
"libopenmpt (based on OpenMPT / ModPlug Tracker)\n"
#endif
"\n"
"Copyright \xC2\xA9 2004-2018 Contributors\n"
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
"\n"
"Contributors:\n"
"Johannes Schultz (2008-2018)\n"
"J\xC3\xB6rn Heusipp (2012-2018)\n"
"Ahti Lepp\xC3\xA4nen (2005-2011)\n"
"Robin Fernandes (2004-2007)\n"
"Sergiy Pylypenko (2007)\n"
"Eric Chavanon (2004-2005)\n"
"Trevor Nunes (2004)\n"
"Olivier Lapicque (1997-2003)\n"
"\n"
"Additional patch submitters:\n"
"coda (http://coda.s3m.us/)\n"
"kode54 (https://kode54.net/)\n"
"Revenant (http://revenant1.net/)\n"
"xaimus (http://xaimus.com/)\n"
"\n"
"Thanks to:\n"
"\n"
"Konstanty for the XMMS-ModPlug resampling implementation\n"
"http://modplug-xmms.sourceforge.net/\n"
"\n"
#ifdef MODPLUG_TRACKER
"Stephan M. Bernsee for pitch shifting source code\n"
"http://www.dspdimension.com/\n"
"\n"
"Aleksey Vaneev of Voxengo for r8brain sample rate converter\n"
"https://github.com/avaneev/r8brain-free-src\n"
"\n"
"Olli Parviainen for SoundTouch Library (time stretching)\n"
"http://www.surina.net/soundtouch/\n"
"\n"
#endif
#ifndef NO_VST
"Hermann Seib for his example VST Host implementation\n"
"http://www.hermannseib.com/english/vsthost.htm\n"
"\n"
#endif
"Laurent Cl\xc3\xA9vy for unofficial MO3 documentation and decompression code\n"
"https://github.com/lclevy/unmo3\n"
"\n"
"Ben \"GreaseMonkey\" Russell for IT sample compression code\n"
"https://github.com/iamgreaser/it2everything/\n"
"\n"
"Antti S. Lankila for Amiga resampler implementation\n"
"https://bel.fi/alankila/modguide/interpolate.txt\n"
"\n"
#ifdef MPT_WITH_ZLIB
"Jean-loup Gailly and Mark Adler for zlib\n"
"http://zlib.net/\n"
"\n"
#endif
#ifdef MPT_WITH_MINIZ
"Rich Geldreich et al. for miniz\n"
"https://github.com/richgel999/miniz\n"
"\n"
#endif
#ifdef MPT_WITH_LHASA
"Simon Howard for lhasa\n"
"https://fragglet.github.io/lhasa/\n"
"\n"
#endif
#ifdef MPT_WITH_UNRAR
"Alexander L. Roshal for UnRAR\n"
"http://rarlab.com/\n"
"\n"
#endif
#ifdef MPT_WITH_PORTAUDIO
"PortAudio contributors\n"
"http://www.portaudio.com/\n"
"\n"
#endif
#ifdef MPT_WITH_FLAC
"Josh Coalson / Xiph.Org Foundation for libFLAC\n"
"https://xiph.org/flac/\n"
"\n"
#endif
#if defined(MPT_WITH_MPG123)
"The mpg123 project for libmpg123\n"
"http://mpg123.de/\n"
"\n"
#endif
#ifdef MPT_WITH_MINIMP3
"Fabrice Bellard, FFMPEG contributors\n"
"and Martin J. Fiedler (KeyJ/kakiarts) for minimp3\n"
"http://keyj.emphy.de/minimp3/\n"
"\n"
#endif
#ifdef MPT_WITH_STBVORBIS
"Sean Barrett for stb_vorbis\n"
"https://github.com/nothings/stb/\n"
"\n"
#endif
#ifdef MPT_WITH_OGG
"Xiph.Org Foundation for libogg\n"
"https://xiph.org/ogg/\n"
"\n"
#endif
#if defined(MPT_WITH_VORBIS) || defined(MPT_WITH_LIBVORBISFILE)
"Xiph.Org Foundation for libvorbis\n"
"https://xiph.org/vorbis/\n"
"\n"
#endif
#if defined(MPT_WITH_OPUS)
"Xiph.Org, Skype Limited, Octasic, Jean-Marc Valin, Timothy B. Terriberry,\n"
"CSIRO, Gregory Maxwell, Mark Borgerding, Erik de Castro Lopo,\n"
"Xiph.Org Foundation, Microsoft Corporation, Broadcom Corporation for libopus\n"
"https://opus-codec.org/\n"
"\n"
#endif
#if defined(MPT_WITH_OPUSFILE)
"Xiph.Org Foundation and contributors for libopusfile\n"
"https://opus-codec.org/\n"
"\n"
#endif
#if defined(MPT_WITH_OPUSENC)
"Xiph.Org Foundation, Jean-Marc Valin and contributors for libopusenc\n"
"https://git.xiph.org/?p=libopusenc.git;a=summary\n"
"\n"
#endif
#if defined(MPT_WITH_PICOJSON)
"Cybozu Labs Inc. and Kazuho Oku et. al. for picojson\n"
"https://github.com/kazuho/picojson\n"
"\n"
#endif
"Storlek for all the IT compatibility hints and testcases\n"
"as well as the IMF, MDL, OKT and ULT loaders\n"
"http://schismtracker.org/\n"
"\n"
#ifdef MODPLUG_TRACKER
"Lennart Poettering and David Henningsson for RealtimeKit\n"
"http://git.0pointer.net/rtkit.git/\n"
"\n"
"Gary P. Scavone for RtMidi\n"
"https://www.music.mcgill.ca/~gary/rtmidi/\n"
"\n"
"Alexander Uckun for decimal input field\n"
"http://www.codeproject.com/Articles/21257/_\n"
"\n"
"Nobuyuki for application and file icon\n"
"https://twitter.com/nobuyukinyuu\n"
"\n"
#endif
"Daniel Collin (emoon/TBL) for providing test infrastructure\n"
"https://twitter.com/daniel_collin\n"
"\n"
"The people at ModPlug forums for crucial contribution\n"
"in the form of ideas, testing and support;\n"
"thanks particularly to:\n"
"33, 8bitbubsy, Anboi, BooT-SectoR-ViruZ, Bvanoudtshoorn\n"
"christofori, cubaxd, Diamond, Ganja, Georg, Goor00,\n"
"Harbinger, jmkz, KrazyKatz, LPChip, Nofold, Rakib, Sam Zen\n"
"Skaven, Skilletaudio, Snu, Squirrel Havoc, Waxhead\n"
"\n"
#ifndef NO_VST
"VST PlugIn Technology by Steinberg Media Technologies GmbH\n"
"\n"
#endif
#ifdef MPT_WITH_ASIO
"ASIO Technology by Steinberg Media Technologies GmbH\n"
"\n"
#endif
);
}
mpt::ustring GetLicenseString()
{
return MPT_UTF8(
"The OpenMPT code is licensed under the BSD license." "\n"
"" "\n"
"Copyright (c) 2004-2018, OpenMPT contributors" "\n"
"Copyright (c) 1997-2003, Olivier Lapicque" "\n"
"All rights reserved." "\n"
"" "\n"
"Redistribution and use in source and binary forms, with or without" "\n"
"modification, are permitted provided that the following conditions are met:" "\n"
" * Redistributions of source code must retain the above copyright" "\n"
" notice, this list of conditions and the following disclaimer." "\n"
" * Redistributions in binary form must reproduce the above copyright" "\n"
" notice, this list of conditions and the following disclaimer in the" "\n"
" documentation and/or other materials provided with the distribution." "\n"
" * Neither the name of the OpenMPT project nor the" "\n"
" names of its contributors may be used to endorse or promote products" "\n"
" derived from this software without specific prior written permission." "\n"
"" "\n"
"THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY" "\n"
"EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED" "\n"
"WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n"
"DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY" "\n"
"DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES" "\n"
"(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;" "\n"
"LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND" "\n"
"ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT" "\n"
"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS" "\n"
"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n"
);
}
} // namespace MptVersion
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,120 @@
/*
* version.h
* ---------
* Purpose: OpenMPT version handling.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "FlagSet.h"
#include <string>
OPENMPT_NAMESPACE_BEGIN
//Creates version number from version parts that appears in version string.
//For example MAKE_VERSION_NUMERIC(1,17,02,28) gives version number of
//version 1.17.02.28.
#define MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) ((prefix##v0 << 24) | (prefix##v1<<16) | (prefix##v2<<8) | (prefix##v3))
#define MAKE_VERSION_NUMERIC(v0,v1,v2,v3) (MptVersion::VersionNum(MAKE_VERSION_NUMERIC_HELPER(0x,v0,v1,v2,v3)))
namespace MptVersion
{
typedef uint32 VersionNum;
extern const VersionNum num; // e.g. 0x01170208
extern const char * const str; // e.g "1.17.02.08"
// Return a OpenMPT version string suitable for file format tags
std::string GetOpenMPTVersionStr(); // e.g. "OpenMPT 1.17.02.08"
// Returns numerical version value from given version string.
VersionNum ToNum(const std::string &s);
// Returns version string from given numerical version value.
std::string ToStr(const VersionNum v);
mpt::ustring ToUString(const VersionNum v);
// Return a version without build number (the last number in the version).
// The current versioning scheme uses this number only for test builds, and it should be 00 for official builds,
// So sometimes it might be wanted to do comparisons without the build number.
VersionNum RemoveBuildNumber(const VersionNum num_);
// Returns true if a given version number is from a test build, false if it's a release build.
bool IsTestBuild(const VersionNum num_ = MptVersion::num);
// Return true if this is a debug build with no optimizations
bool IsDebugBuild();
struct SourceInfo
{
std::string Url; // svn repository url (or empty string)
int Revision; // svn revision (or 0)
bool IsDirty; // svn working copy is dirty (or false)
bool HasMixedRevisions; // svn working copy has mixed revisions (or false)
bool IsPackage; // source code originates from a packaged version of the source code
std::string Date; // svn date (or empty string)
SourceInfo() : Url(std::string()), Revision(0), IsDirty(false), HasMixedRevisions(false), IsPackage(false) { }
public:
std::string GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string
std::string GetStateString() const; // i.e. "+dirty" or "clean"
};
SourceInfo GetSourceInfo();
// Returns either the URL to download release builds or the URL to download test builds, depending on the current build.
mpt::ustring GetDownloadURL();
// Return a string decribing the time of the build process (if built from a svn working copy and tsvn was available during build, otherwise it returns the time version.cpp was last rebuild which could be unreliable as it does not get rebuild every time without tsvn)
std::string GetBuildDateString();
// Return a string decribing some of the build features
std::string GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND"
// Return a string describing the compiler version used for building.
std::string GetBuildCompilerString(); // e.g. "Microsoft Compiler 15.00.20706.01"
enum Strings
{
StringsNone = 0,
StringVersion = 1<<0, // "1.23.35.45"
StringRevision = 1<<2, // "-r1234+"
StringBitness = 1<<3, // "32 bit"
StringSourceInfo = 1<<4, // "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234 (2016-01-02) +dirty"
StringBuildFlags = 1<<5, // "TEST DEBUG"
StringBuildFeatures = 1<<6, // "NO_VST NO_DSOUND"
};
MPT_DECLARE_ENUM(Strings)
// Returns a versions string with the fields selected via @strings.
std::string GetVersionString(FlagSet<MptVersion::Strings> strings);
// Returns a pure version string
std::string GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit"
// Returns a simple version string
std::string GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST"
// Returns MptVersion::str if the build is a clean release build straight from the repository or an extended string otherwise (if built from a svn working copy and tsvn was available during build)
std::string GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG"
// Returns a URL for the respective keys. Supported keys: "website", "forum", "bugtracker", "updates", "top_picks"
mpt::ustring GetURL(std::string key);
// Returns a multi-line string containing the full credits for the code base
mpt::ustring GetFullCreditsString();
// Returns the OpenMPT license text
mpt::ustring GetLicenseString();
} //namespace MptVersion
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,30 @@
/*
* versionNumber.h
* ---------------
* Purpose: OpenMPT version handling.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
OPENMPT_NAMESPACE_BEGIN
#define VER_HELPER_STRINGIZE(x) #x
#define VER_STRINGIZE(x) VER_HELPER_STRINGIZE(x)
//Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 27
#define VER_MINOR 04
#define VER_MINORMINOR 02
//Version string. For example "1.17.02.28"
#define MPT_VERSION_STR VER_STRINGIZE(VER_MAJORMAJOR) "." VER_STRINGIZE(VER_MAJOR) "." VER_STRINGIZE(VER_MINOR) "." VER_STRINGIZE(VER_MINORMINOR)
//Numerical value of the version.
#define MPT_VERSION_NUMERIC MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR)
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,304 @@
669="if"
669="JN"
amf="ASYLUM Music Format V1.0\x00"
amf="AMF\x0A"
ams="Extreme"
ams="AMShdr\x1A\x02\x02"
#dbm="DBM0"
dbm="NAME"
dbm="INFO"
dbm="SONG"
dbm="INST"
dbm="VENV"
dbm="PENV"
dbm="PNAM"
dbm="SMPL"
dbm="DSPE"
#dbm="MPEG"
digi="DIGI Booster module\x00"
dmf="DDMF"
dmf="XTRACKER"
dmf="CMSG"
dmf="SEQU"
dmf="SMPI"
dmf="SMPD"
#dmf="SMPJ"
#dmf="ENDE"
#dmf="SETT"
dsm="RIFF"
dsm="DSMF"
dtm="D.T."
dtm="S.Q."
#dtm="PATT"
#dtm="INST"
dtm="DAPT"
dtm="DAIT"
far="FAR\xFE"
far="\x0D\x0A\x1A"
gdm="GDM\xFE"
gdm="GMFS"
imf="IM10"
imf="IS10"
it="IMPM"
it="IMPI"
it="IMPS"
#it="OMPT"
it="PNAM"
it="CNAM"
it="STPM"
it="XTPM"
it="CHBI"
it="FX00"
it="F255"
it="DWRT"
it="PROG"
it="CHFX"
it="..TD"
it="DTFR"
it=".BPR"
it=".MPR"
it="...C"
it="SnhC"
it="..MT"
it=".MMP"
it=".VWC"
it="VWSL"
it=".APS"
it="VTSV"
it=".VGD"
it="..PR"
it="RSMP"
it="CUES"
it="SWNG"
it=".FSM"
it="AUTH"
itp=".pti\x03\x01\x00\x00"
j2b="MUSE"
j2b="\xDE\xAD\xBE\xAF"
j2b="\xDE\xAD\xBA\xBE"
j2b="AMFF"
j2b="AM "
j2b="MAIN"
j2b="INIT"
j2b="ORDR"
j2b="AI "
j2b="AS "
MDL="DMDL"
# Most chunk IDs are commented out as they are substrings of other dictionary entries
#mdl="IN"
mdl="ME"
#mdl="PA"
#mdl="TR"
mdl="II"
#mdl="VE"
#mdl="PE"
#mdl="FE"
#mdl="IS"
#mdl="SA"
med="MMD1"
mo3="MO3\x05"
# A couple of magic bytes are commented out because they do not modify the loader's behaviour, apart from setting a "made with" string.
mod="M.K."
#mod="M!K!"
mod="M&K!"
mod="N.T."
#mod="FEST"
#mod="NSMS"
#mod="LARD"
mod="OKTA"
#mod="OCTA"
#mod="CD61"
mod="CD81"
#mod="FA08"
mod="FLT8"
mod="EXO8"
# Depending on the byte offset in the file, we generate either a "xCHN" or "xxCH" magic
mod="99CHN"
mod="TDZ8"
ice="MTN\x00"
ice="IT10"
pt36="CMNT"
pt36="PTDT"
sfx="SO31"
# External Startrekker instrument files.
stam="ST1.3 ModuleINFO"
stam="AudioSculpture10"
mptm="->MPT_ORIGINAL_IT<-"
mptm=".tpm"
mptm="mptm"
mptm="\x89\x08"
mptm="\x8D\x08"
# No structural changes in these format versions
#mptm="\x8E\x08"
#mptm="\x8F\x08"
#mptm="\x90\x08"
mptm="\x91\x08"
mptm="228\x04"
mt2="MT20"
#mt2="MadTracker 2.0"
mt2="BPM+"
mt2="TFXM"
mt2="TRKS"
mt2="TRKL"
mt2="PATN"
mt2="MSG\x00"
#mt2="PICT"
mt2="SUM\x00"
mt2="VST2"
mtm="MTM\x10"
okt="OKTASONG"
okt="CMOD"
okt="SAMP"
okt="SPEE"
okt="SLEN"
okt="PLEN"
okt="PATT"
okt="PBOD"
okt="SBOD"
plm="PLM\x1A"
plm="PLS\x1A"
psm="PSM "
psm="FILE"
psm="TITL"
psm="SDFT"
psm="DATE"
psm="OPLH"
psm="PPAN"
psm="DSAM"
psm="DSMP"
psm="MAINSONG"
psm="\x00\xFF\x00\x00\x01\x00"
psm16="PSM\xFE"
psm16="PORD"
psm16="PPAN"
psm16="PSAH"
psm16="PPAT"
ptm="PTMF"
ptm="\x1A\x03\x02"
s3m="SCRM"
s3m="SCRS"
stm="\x1A\x02\x15"
stp="STP3\x02"
ult="MAS_UTrack_V004"
umx="\xC1\x83\x2A\x9E"
umx="music"
umx="sound"
xm="Extended Module: "
xm="OpenMPT "
#xm="FastTracker v 2.00 "
xm="MilkyTracker "
xm="text"
xm="MIDI"
it="..OF"
it="LTTP"
it="PTTF"
it="..Fd"
it="..VG"
it="...P"
it="..EV"
it="..EP"
it=".EiP"
it=".SLV"
it=".ELV"
it=".BSV"
it=".ESV"
it=".SLP"
it=".ELP"
it=".BSP"
it=".ESP"
it="SLiP"
it="ELiP"
it="BSiP"
it="ESiP"
it=".ANN"
it=".TCD"
it=".AND"
it="..SP"
it="..SV"
it=".CFI"
it=".RFI"
it="..BM"
it="..PM"
it="..CM"
it=".SPP"
it=".CPP"
it=".[PV"
it=".[PP"
it="[PiP"
it=".[EV"
it=".[EP"
it="[EiP"
it="..[K"
it="..[n"
it=".[MN"
it=".[nf"
it=".PiM"
it="..RV"
it="...R"
it="..SC"
it="..SR"
it="..MF"
it="HEVP"
it="HOVP"
it="NREP"
it="NREA"
it="NREV"
it="GLFP"
it="GLFA"
it="GLFV"
it="DWPM"
mmcmp="ziRCONia\x0e\x00"
xpk="XPKF\x00\x10\x00\x00SQSH"
pp20="PP20"
plugin_chorus="OMXD\x9C\x62\xE6\xEF"
plugin_compressor="OMXD\x79\x1F\x01\xEF"
plugin_distortion="OMXD\x90\x4C\x11\xEF"
plugin_echo="OMXD\x2C\x93\x3E\xEF"
plugin_flanger="OMXD\x92\x3D\xCA\xEF"
plugin_gargle="OMXD\x10\x82\xFD\xDA"
plugin_i3dl2reverb="OMXD\x71\x5E\x98\xEF"
plugin_parameq="OMXD\x89\xED\x0C\x12"
plugin_wavesreverb="OMDX\x68\x02\xFC\x87"
plugin_lfo="OMPTLFO "
plugin_dbproecho="DBM0Echo"
midi="MThd\x00\x00\x00\x06\x00\x01\x00\x01\x01\xE0MTrk"
wave="WAVEfmt "
wave="data"

View file

@ -0,0 +1,4 @@
#!/usr/bin/env bash
cd "${0%/*}"
cd ../..
AFL_HARDEN=1 CONFIG=afl make clean all EXAMPLES=0 TEST=0 OPENMPT123=0 NO_VORBIS=1 NO_VORBISFILE=1 USE_MINIMP3=1

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
# Create tmpfs for storing temporary fuzzing data
mkdir $FUZZING_TEMPDIR
sudo mount -t tmpfs -o size=200M none $FUZZING_TEMPDIR
rm -rf $FUZZING_TEMPDIR/bin
mkdir $FUZZING_TEMPDIR/bin
cp ../../bin/* $FUZZING_TEMPDIR/bin/
rm $FUZZING_TEMPDIR/bin/libopenmpt.so
cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so
cp ../../bin/libopenmpt.so.1 $FUZZING_TEMPDIR/bin/libopenmpt.so.1
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Input data for fuzzer
# If you run the fuzzer for the first time, specify a directory with some input
# files for the fuzzer, e.g.
# FUZZING_INPUT="-i /home/foo/testcases/"
# If you want to continue fuzzing using the previous findings, use:
# FUZZING_INPUT=-i-
FUZZING_INPUT=-i-
# Directory to place temporary fuzzing data into
FUZZING_TEMPDIR=~/libopenmpt-fuzzing-temp
# Directory to store permanent fuzzing data (e.g. found crashes) into
FUZZING_FINDINGS_DIR=~/libopenmpt-fuzzing
# Fuzzer timeout in ms, + = don't abort on timeout
FUZZING_TIMEOUT=5000+
# Path to afl-fuzz binary
AFL_BIN=afl/afl-fuzz

View file

@ -0,0 +1,5 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_BIN -f $FUZZING_TEMPDIR/infile02 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer02 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile02

View file

@ -0,0 +1,54 @@
/*
* fuzz.c
* ------
* Purpose: Tiny libopenmpt user to be used by fuzzing tools
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <unistd.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#define BUFFERSIZE 512
#define SAMPLERATE 22050
static int16_t buffer[BUFFERSIZE];
int main( int argc, char * argv[] ) {
static FILE * file = NULL;
static openmpt_module * mod = NULL;
static size_t count = 0;
static int i = 0;
(void)argc;
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
file = fopen( argv[1], "rb" );
mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL );
fclose( file );
if ( mod == NULL ) return 1;
openmpt_module_ctl_set( mod, "render.resampler.emulate_amiga", (openmpt_module_get_num_orders( mod ) & 1) ? "0" : "1" );
/* render about a second of the module for fuzzing the actual mix routines */
for(; i < 50; i++) {
count = openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
if ( count == 0 ) {
break;
}
}
/* fuzz string-related stuff */
openmpt_free_string ( openmpt_module_get_metadata( mod, "date" ) );
openmpt_free_string ( openmpt_module_get_metadata( mod, "message" ) );
openmpt_module_destroy( mod );
return 0;
}

View file

@ -0,0 +1,16 @@
#!/usr/bin/env bash
cd "${0%/*}"
rm afl-latest.tgz
wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz || exit
tar -xzvf afl-latest.tgz
rm afl-latest.tgz
cd afl-*
make || exit
cd llvm_mode
# may need to prepend LLVM_CONFIG=/usr/bin/llvm-config-3.8 or similar, depending on the system
make || exit
cd ../libdislocator
make || exit
cd ../..
rm -rf afl
mv afl-* afl

View file

@ -0,0 +1,46 @@
libopenmpt fuzz suite
=====================
In this directory, you can find the necessary tools for fuzzing libopenmpt with
the American Fuzzy Lop fuzzer (afl-fuzz).
Contents:
* `all_formats.dict`: A dictionary containing magic bytes from all supported
module formats to make the life of the fuzzer a bit easier.
* `fuzz-master.sh`: Script to launch the main fuzzing process. If you want to
use just one fuzzer instance, run this one.
* `fuzz-slave.sh`: Script to launch the secondary fuzzing process. It is
recommended to run at least two fuzzer instances, as the deterministic and
random fuzz mode have been found to complement each other really well.
* `fuzz-settings.sh`: Set up your preferences and afl settings here before the
first run.
* `fuzz.c`: A tiny C program that is used by the fuzzer to test libopenmpt.
* `get-afl.sh`: A simple script to obtain the latest version of the fuzzer.
Prerequisites
=============
* afl from http://lcamtuf.coredump.cx/afl/ - the makefile expects this to be
installed in `contrib/fuzzing/afl`, as it is automatically done by the
`get-afl.sh` install script.
* Clang with LLVM dev headers (llvm-config needs to be installed).
afl also works with gcc, but our makefile has been set up to make use of afl's
faster LLVM mode.
How to use
==========
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use `make`
to build afl-fuzz, `cd llvm_mode`, `make` to build afl-clang-fast.
If building with either option fails because `llvm-config` cannot be found,
try prepending `LLVM_CONFIG=/usr/bin/llvm-config-3.8` or similar, and read the
afl manual.
* Build libopenmpt with the `build.sh` script in this directory.
* Set up `fuzz-settings.sh` to your taste. Most importantly, you will have to
specify the input directory for first use.
The default setup mounts a tmpfs folder for all temporary files. You may
change this behaviour if you do not have root privileges.
* Run `fuzz-master.sh` for the first (deterministic) instance of afl-fuzz.
* For a "slave" instance to run on another core, run `fuzz-slave.sh`.
* If you want to make use of even more cores, make a copy of `fuzz-slave.sh`
and adjust "infile02" / "fuzzer02" to "infile03" / "fuzzer03" (they need to be
unique)

View file

@ -0,0 +1,109 @@
/*
* libopenmpt_bass.c
* -----------------
* Purpose: Example of how to use libopenmpt with the BASS audio library.
* Notes : BASS from un4seen (http://www.un4seen.com/bass.html) is used for audio output.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_bass SOMEMODULE
*/
#include <stdio.h>
#include <string.h>
#if ( defined( _WIN32 ) || defined( WIN32 ) )
#include <Windows.h>
#define sleep(n) Sleep(n)
#else
#include <unistd.h>
#endif
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#include <bass.h>
#define SAMPLERATE 48000
DWORD CALLBACK StreamProc(HSTREAM handle, void *buffer, DWORD length, void *user)
{
// length is in bytes, but libopenmpt wants samples => divide by number of channels (2) and size of a sample (float = 4)
// same for return value.
size_t count = openmpt_module_read_interleaved_float_stereo( (openmpt_module *)user, SAMPLERATE, length / (2 * sizeof(float)), (float *)buffer );
count *= (2 * sizeof(float));
// Reached end of stream?
if(count < length) count |= BASS_STREAMPROC_END;
return (DWORD)count;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
FILE * file = 0;
openmpt_module * mod = 0;
HSTREAM stream = 0;
int result = 0;
if ( argc != 2 ) {
fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] );
return 1;
}
if ( HIWORD( BASS_GetVersion() ) !=BASSVERSION ) {
fprintf( stderr, "Error: Wrong BASS version\n" );
return 1;
}
if ( !BASS_Init( -1, SAMPLERATE, 0, NULL, NULL ) ) {
fprintf( stderr, "Error: Cannot initialize BASS (error %d)\n", BASS_ErrorGetCode() );
return 1;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
if ( wcslen( argv[1] ) == 0 ) {
fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] );
result = 1;
goto fail;
}
file = _wfopen( argv[1], L"rb" );
#else
if ( strlen( argv[1] ) == 0 ) {
fprintf( stderr, "Usage: %s SOMEMODULE\n", argv[0] );
result = 1;
goto fail;
}
file = fopen( argv[1], "rb" );
#endif
if ( !file ) {
fprintf( stderr, "Error: %s\n", "fopen() failed." );
result = 1;
goto fail;
}
mod = openmpt_module_create( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL );
if ( !mod ) {
fprintf( stderr, "Error: %s\n", "openmpt_module_create() failed." );
goto fail;
}
// Create a "pull" channel. BASS will call StreamProc whenever the channel needs new data to be decoded.
stream = BASS_StreamCreate(SAMPLERATE, 2, BASS_SAMPLE_FLOAT, StreamProc, mod);
BASS_ChannelPlay(stream, FALSE);
while ( BASS_ChannelIsActive( stream ) ) {
// Do something useful here
sleep(1);
}
BASS_StreamFree( stream );
openmpt_module_destroy( mod );
fail:
BASS_Free();
return result;
}

View file

@ -0,0 +1,63 @@
AccessModifierOffset: -2
AlignAfterOpenBracket: true
AlignConsecutiveAssignments: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: false
BreakConstructorInitializersBeforeComma: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IndentCaseLabels: false
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: true
Language: Cpp
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 3
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 60
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Middle
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: true
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 2
UseTab: ForIndentation

View file

@ -0,0 +1,203 @@
/*
* libopenmpt_example_c.c
* ----------------------
* Purpose: libopenmpt C API example
* Notes : PortAudio is used for sound output.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_c SOMEMODULE
*/
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#include <portaudio.h>
#define BUFFERSIZE 480
#define SAMPLERATE 48000
static int16_t left[BUFFERSIZE];
static int16_t right[BUFFERSIZE];
static int16_t * const buffers[2] = { left, right };
static void libopenmpt_example_logfunc( const char * message, void * userdata ) {
(void)userdata;
if ( message ) {
fprintf( stderr, "openmpt: %s\n", message );
}
}
static int libopenmpt_example_errfunc( int error, void * userdata ) {
(void)userdata;
(void)error;
return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG;
}
static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) {
if ( !func_name ) {
func_name = "unknown function";
}
if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" );
} else {
fprintf( stderr, "Error: %s\n", mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
} else {
if ( !mod_err_str ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s failed.\n", func_name );
} else {
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
int result = 0;
FILE * file = 0;
openmpt_module * mod = 0;
int mod_err = OPENMPT_ERROR_OK;
const char * mod_err_str = NULL;
size_t count = 0;
PaError pa_error = paNoError;
int pa_initialized = 0;
PaStream * stream = 0;
if ( argc != 2 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." );
goto fail;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
if ( wcslen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." );
goto fail;
}
file = _wfopen( argv[1], L"rb" );
#else
if ( strlen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c SOMEMODULE'." );
goto fail;
}
file = fopen( argv[1], "rb" );
#endif
if ( !file ) {
fprintf( stderr, "Error: %s\n", "fopen() failed." );
goto fail;
}
mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL );
if ( !mod ) {
libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
goto fail;
}
openmpt_module_set_error_func( mod, NULL, NULL );
pa_error = Pa_Initialize();
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." );
goto fail;
}
pa_initialized = 1;
pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL );
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." );
goto fail;
}
if ( !stream ) {
fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." );
goto fail;
}
pa_error = Pa_StartStream( stream );
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." );
goto fail;
}
while ( 1 ) {
openmpt_module_error_clear( mod );
count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right );
mod_err = openmpt_module_error_get_last( mod );
mod_err_str = openmpt_module_error_get_last_message( mod );
if ( mod_err != OPENMPT_ERROR_OK ) {
libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
if ( count == 0 ) {
break;
}
pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count );
if ( pa_error == paOutputUnderflowed ) {
pa_error = paNoError;
}
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." );
goto fail;
}
}
result = 0;
goto cleanup;
fail:
result = 1;
cleanup:
if ( stream ) {
if ( Pa_IsStreamActive( stream ) == 1 ) {
Pa_StopStream( stream );
}
Pa_CloseStream( stream );
stream = 0;
}
if ( pa_initialized ) {
Pa_Terminate();
pa_initialized = 0;
}
if ( mod ) {
openmpt_module_destroy( mod );
mod = 0;
}
if ( file ) {
fclose( file );
file = 0;
}
return result;
}

View file

@ -0,0 +1,292 @@
/*
* libopenmpt_example_c_mem.c
* --------------------------
* Purpose: libopenmpt C API example
* Notes : PortAudio is used for sound output.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_c_mem SOMEMODULE
*/
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libopenmpt/libopenmpt.h>
#include <portaudio.h>
#define BUFFERSIZE 480
#define SAMPLERATE 48000
static int16_t left[BUFFERSIZE];
static int16_t right[BUFFERSIZE];
static int16_t * const buffers[2] = { left, right };
static void libopenmpt_example_logfunc( const char * message, void * userdata ) {
(void)userdata;
if ( message ) {
fprintf( stderr, "openmpt: %s\n", message );
}
}
static int libopenmpt_example_errfunc( int error, void * userdata ) {
(void)userdata;
(void)error;
return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG;
}
static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) {
if ( !func_name ) {
func_name = "unknown function";
}
if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" );
} else {
fprintf( stderr, "Error: %s\n", mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
} else {
if ( !mod_err_str ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s failed.\n", func_name );
} else {
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
}
typedef struct blob_t {
size_t size;
void * data;
} blob_t;
static void free_blob( blob_t * blob ) {
if ( blob ) {
if ( blob->data ) {
free( blob->data );
blob->data = 0;
}
blob->size = 0;
free( blob );
}
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
static blob_t * load_file( const wchar_t * filename ) {
#else
static blob_t * load_file( const char * filename ) {
#endif
blob_t * result = 0;
blob_t * blob = 0;
FILE * file = 0;
long tell_result = 0;
blob = malloc( sizeof( blob_t ) );
if ( !blob ) {
goto fail;
}
memset( blob, 0, sizeof( blob_t ) );
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
file = _wfopen( filename, L"rb" );
#else
file = fopen( filename, "rb" );
#endif
if ( !file ) {
goto fail;
}
if ( fseek( file, 0, SEEK_END ) != 0 ) {
goto fail;
}
tell_result = ftell( file );
if ( tell_result < 0 ) {
goto fail;
}
if ( (unsigned long)tell_result > SIZE_MAX ) {
goto fail;
}
blob->size = (size_t)tell_result;
if ( fseek( file, 0, SEEK_SET ) != 0 ) {
goto fail;
}
blob->data = malloc( blob->size );
if ( !blob->data ) {
goto fail;
}
memset( blob->data, 0, blob->size );
if ( fread( blob->data, 1, blob->size, file ) != blob->size ) {
goto fail;
}
result = blob;
blob = 0;
goto cleanup;
fail:
result = 0;
cleanup:
if ( blob ) {
free_blob( blob );
blob = 0;
}
if ( file ) {
fclose( file );
file = 0;
}
return result;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
int result = 0;
blob_t * blob = 0;
openmpt_module * mod = 0;
int mod_err = OPENMPT_ERROR_OK;
const char * mod_err_str = NULL;
size_t count = 0;
PaError pa_error = paNoError;
int pa_initialized = 0;
PaStream * stream = 0;
if ( argc != 2 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." );
goto fail;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
if ( wcslen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." );
goto fail;
}
#else
if ( strlen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_mem SOMEMODULE'." );
goto fail;
}
#endif
blob = load_file( argv[1] );
if ( !blob ) {
fprintf( stderr, "Error: %s\n", "load_file() failed." );
goto fail;
}
mod = openmpt_module_create_from_memory2( blob->data, blob->size, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL );
if ( !mod ) {
libopenmpt_example_print_error( "openmpt_module_create_from_memory2()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
goto fail;
}
pa_error = Pa_Initialize();
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_Initialize() failed." );
goto fail;
}
pa_initialized = 1;
pa_error = Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL );
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." );
goto fail;
}
if ( !stream ) {
fprintf( stderr, "Error: %s\n", "Pa_OpenStream() failed." );
goto fail;
}
pa_error = Pa_StartStream( stream );
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_StartStream() failed." );
goto fail;
}
while ( 1 ) {
openmpt_module_error_clear( mod );
count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right );
mod_err = openmpt_module_error_get_last( mod );
mod_err_str = openmpt_module_error_get_last_message( mod );
if ( mod_err != OPENMPT_ERROR_OK ) {
libopenmpt_example_print_error( "openmpt_module_read_stereo()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
if ( count == 0 ) {
break;
}
pa_error = Pa_WriteStream( stream, buffers, (unsigned long)count );
if ( pa_error == paOutputUnderflowed ) {
pa_error = paNoError;
}
if ( pa_error != paNoError ) {
fprintf( stderr, "Error: %s\n", "Pa_WriteStream() failed." );
goto fail;
}
}
result = 0;
goto cleanup;
fail:
result = 1;
cleanup:
if ( stream ) {
if ( Pa_IsStreamActive( stream ) == 1 ) {
Pa_StopStream( stream );
}
Pa_CloseStream( stream );
stream = 0;
}
if ( pa_initialized ) {
Pa_Terminate();
pa_initialized = 0;
}
if ( mod ) {
openmpt_module_destroy( mod );
mod = 0;
}
if ( blob ) {
free_blob( blob );
blob = 0;
}
return result;
}

View file

@ -0,0 +1,152 @@
/*
* libopenmpt_example_c_pipe.c
* ---------------------------
* Purpose: libopenmpt C API simple pipe example
* Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: cat SOMEMODULE | libopenmpt_example_c_pipe | aplay --file-type raw --format=dat
*/
#include <memory.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_fd.h>
#define BUFFERSIZE 480
#define SAMPLERATE 48000
static void libopenmpt_example_logfunc( const char * message, void * userdata ) {
(void)userdata;
if ( message ) {
fprintf( stderr, "openmpt: %s\n", message );
}
}
static int libopenmpt_example_errfunc( int error, void * userdata ) {
(void)userdata;
(void)error;
return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG;
}
static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) {
if ( !func_name ) {
func_name = "unknown function";
}
if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" );
} else {
fprintf( stderr, "Error: %s\n", mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
} else {
if ( !mod_err_str ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s failed.\n", func_name );
} else {
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
}
static ssize_t xwrite( int fd, const void * buffer, size_t size ) {
size_t written = 0;
ssize_t retval = 0;
while ( written < size ) {
retval = write( fd, (const char *)buffer + written, size - written );
if ( retval < 0 ) {
if ( errno != EINTR ) {
break;
}
retval = 0;
}
written += retval;
}
return written;
}
static int16_t buffer[BUFFERSIZE * 2];
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
int result = 0;
openmpt_module * mod = 0;
int mod_err = OPENMPT_ERROR_OK;
const char * mod_err_str = NULL;
size_t count = 0;
size_t written = 0;
(void)argv;
if ( argc != 1 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_pipe' and connect stdin and stdout." );
goto fail;
}
mod = openmpt_module_create2( openmpt_stream_get_fd_callbacks(), (void*)(uintptr_t)STDIN_FILENO, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL );
if ( !mod ) {
libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
goto fail;
}
while ( 1 ) {
openmpt_module_error_clear( mod );
count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer );
mod_err = openmpt_module_error_get_last( mod );
mod_err_str = openmpt_module_error_get_last_message( mod );
if ( mod_err != OPENMPT_ERROR_OK ) {
libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
if ( count == 0 ) {
break;
}
written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) );
if ( written == 0 ) {
fprintf( stderr, "Error: %s\n", "write() failed." );
goto fail;
}
}
result = 0;
goto cleanup;
fail:
result = 1;
cleanup:
if ( mod ) {
openmpt_module_destroy( mod );
mod = 0;
}
return result;
}

View file

@ -0,0 +1,181 @@
/*
* libopenmpt_example_c_probe.c
* ----------------------------
* Purpose: libopenmpt C API probing example
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_c_probe SOMEMODULE ...
* Returns 0 on successful probing for all files.
* Returns 1 on failed probing for 1 or more files.
* Returns 2 on error.
*/
#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY 1
#define LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT 2
#define LIBOPENMPT_EXAMPLE_PROBE_RESULT LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
static void libopenmpt_example_logfunc( const char * message, void * userdata ) {
(void)userdata;
if ( message ) {
fprintf( stderr, "%s\n", message );
}
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
static int probe_file( const wchar_t * filename ) {
#else
static int probe_file( const char * filename ) {
#endif
int result = 0;
int mod_err = OPENMPT_ERROR_OK;
FILE * file = NULL;
#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY )
int result_binary = 0;
int probe_file_header_result = OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE;
const char * probe_file_header_result_str = NULL;
#endif
#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT )
double probability = 0.0;
#endif
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
if ( wcslen( filename ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." );
goto fail;
}
#else
if ( strlen( filename ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE'." );
goto fail;
}
#endif
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
file = _wfopen( filename, L"rb" );
#else
file = fopen( filename, "rb" );
#endif
if ( !file ) {
fprintf( stderr, "Error: %s\n", "fopen() failed." );
goto fail;
}
#if ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_BINARY )
probe_file_header_result = openmpt_probe_file_header_from_stream( OPENMPT_PROBE_FILE_HEADER_FLAGS_DEFAULT, openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL );
probe_file_header_result_str = NULL;
result_binary = 0;
switch ( probe_file_header_result ) {
case OPENMPT_PROBE_FILE_HEADER_RESULT_SUCCESS:
probe_file_header_result_str = "Success ";
result_binary = 1;
break;
case OPENMPT_PROBE_FILE_HEADER_RESULT_FAILURE:
probe_file_header_result_str = "Failure ";
result_binary = 0;
break;
case OPENMPT_PROBE_FILE_HEADER_RESULT_WANTMOREDATA:
probe_file_header_result_str = "WantMoreData";
result_binary = 0;
break;
case OPENMPT_PROBE_FILE_HEADER_RESULT_ERROR:
result_binary = 0;
fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." );
goto fail;
break;
default:
result_binary = 0;
fprintf( stderr, "Error: %s\n", "openmpt_probe_file_header() failed." );
goto fail;
break;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
fprintf( stdout, "%s - %ls\n", probe_file_header_result_str, filename );
#else
fprintf( stdout, "%s - %s\n", probe_file_header_result_str, filename );
#endif
if ( result_binary ) {
result = 0;
} else {
result = 1;
}
#elif ( LIBOPENMPT_EXAMPLE_PROBE_RESULT == LIBOPENMPT_EXAMPLE_PROBE_RESULT_FLOAT )
probability = openmpt_could_open_probability2( openmpt_stream_get_file_callbacks(), file, 0.25, &libopenmpt_example_logfunc, NULL, &openmpt_error_func_default, NULL, &mod_err, NULL );
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
fprintf( stdout, "%s: %f - %ls\n", "Result", probability, filename );
#else
fprintf( stdout, "%s: %f - %s\n", "Result", probability, filename );
#endif
if ( probability >= 0.5 ) {
result = 0;
} else {
result = 1;
}
#else
#error "LIBOPENMPT_EXAMPLE_PROBE_RESULT is wrong"
#endif
goto cleanup;
fail:
result = 2;
cleanup:
if ( file ) {
fclose( file );
file = 0;
}
return result;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
int global_result = 0;
if ( argc <= 1 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_probe SOMEMODULE ...'." );
goto fail;
}
for ( int i = 1; i < argc; ++i ) {
int result = probe_file( argv[i] );
if ( result > global_result ) {
global_result = result;
}
}
goto cleanup;
fail:
global_result = 2;
cleanup:
return global_result;
}

View file

@ -0,0 +1,176 @@
/*
* libopenmpt_example_c_stdout.c
* -----------------------------
* Purpose: libopenmpt C API simple example
* Notes : This example writes raw 48000Hz / stereo / 16bit native endian PCM data to stdout.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_c_stdout SOMEMODULE | aplay --file-type raw --format=dat
*/
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#define BUFFERSIZE 480
#define SAMPLERATE 48000
static void libopenmpt_example_logfunc( const char * message, void * userdata ) {
(void)userdata;
if ( message ) {
fprintf( stderr, "openmpt: %s\n", message );
}
}
static int libopenmpt_example_errfunc( int error, void * userdata ) {
(void)userdata;
(void)error;
return OPENMPT_ERROR_FUNC_RESULT_DEFAULT & ~OPENMPT_ERROR_FUNC_RESULT_LOG;
}
static void libopenmpt_example_print_error( const char * func_name, int mod_err, const char * mod_err_str ) {
if ( !func_name ) {
func_name = "unknown function";
}
if ( mod_err == OPENMPT_ERROR_OUT_OF_MEMORY ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s\n", "OPENMPT_ERROR_OUT_OF_MEMORY" );
} else {
fprintf( stderr, "Error: %s\n", mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
} else {
if ( !mod_err_str ) {
mod_err_str = openmpt_error_string( mod_err );
if ( !mod_err_str ) {
fprintf( stderr, "Error: %s failed.\n", func_name );
} else {
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
fprintf( stderr, "Error: %s failed: %s\n", func_name, mod_err_str );
}
}
static ssize_t xwrite( int fd, const void * buffer, size_t size ) {
size_t written = 0;
ssize_t retval = 0;
while ( written < size ) {
retval = write( fd, (const char *)buffer + written, size - written );
if ( retval < 0 ) {
if ( errno != EINTR ) {
break;
}
retval = 0;
}
written += retval;
}
return written;
}
static int16_t buffer[BUFFERSIZE * 2];
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
int result = 0;
FILE * file = 0;
openmpt_module * mod = 0;
int mod_err = OPENMPT_ERROR_OK;
const char * mod_err_str = NULL;
size_t count = 0;
size_t written = 0;
if ( argc != 2 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." );
goto fail;
}
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
if ( wcslen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." );
goto fail;
}
file = _wfopen( argv[1], L"rb" );
#else
if ( strlen( argv[1] ) == 0 ) {
fprintf( stderr, "Error: %s\n", "Wrong invocation. Use 'libopenmpt_example_c_stdout SOMEMODULE'." );
goto fail;
}
file = fopen( argv[1], "rb" );
#endif
if ( !file ) {
fprintf( stderr, "Error: %s\n", "fopen() failed." );
goto fail;
}
mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, &libopenmpt_example_logfunc, NULL, &libopenmpt_example_errfunc, NULL, &mod_err, &mod_err_str, NULL );
if ( !mod ) {
libopenmpt_example_print_error( "openmpt_module_create2()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
goto fail;
}
while ( 1 ) {
openmpt_module_error_clear( mod );
count = openmpt_module_read_interleaved_stereo( mod, SAMPLERATE, BUFFERSIZE, buffer );
mod_err = openmpt_module_error_get_last( mod );
mod_err_str = openmpt_module_error_get_last_message( mod );
if ( mod_err != OPENMPT_ERROR_OK ) {
libopenmpt_example_print_error( "openmpt_module_read_interleaved_stereo()", mod_err, mod_err_str );
openmpt_free_string( mod_err_str );
mod_err_str = NULL;
}
if ( count == 0 ) {
break;
}
written = xwrite( STDOUT_FILENO, buffer, count * 2 * sizeof( int16_t ) );
if ( written == 0 ) {
fprintf( stderr, "Error: %s\n", "write() failed." );
goto fail;
}
}
result = 0;
goto cleanup;
fail:
result = 1;
cleanup:
if ( mod ) {
openmpt_module_destroy( mod );
mod = 0;
}
if ( file ) {
fclose( file );
file = 0;
}
return result;
}

View file

@ -0,0 +1,65 @@
/*
* libopenmpt_example_c_unsafe.c
* -----------------------------
* Purpose: libopenmpt C API simplified example
* Notes : PortAudio is used for sound output.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_c_unsafe SOMEMODULE
* CAUTION: This simple example does no error cheking at all.
*/
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libopenmpt/libopenmpt.h>
#include <libopenmpt/libopenmpt_stream_callbacks_file.h>
#include <portaudio.h>
#define BUFFERSIZE 480
#define SAMPLERATE 48000
static int16_t left[BUFFERSIZE];
static int16_t right[BUFFERSIZE];
static int16_t * const buffers[2] = { left, right };
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
int wmain( int argc, wchar_t * argv[] ) {
#else
int main( int argc, char * argv[] ) {
#endif
FILE * file = 0;
openmpt_module * mod = 0;
size_t count = 0;
PaStream * stream = 0;
(void)argc;
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
file = _wfopen( argv[1], L"rb" );
#else
file = fopen( argv[1], "rb" );
#endif
mod = openmpt_module_create2( openmpt_stream_get_file_callbacks(), file, NULL, NULL, NULL, NULL, NULL, NULL, NULL );
fclose( file );
Pa_Initialize();
Pa_OpenDefaultStream( &stream, 0, 2, paInt16 | paNonInterleaved, SAMPLERATE, paFramesPerBufferUnspecified, NULL, NULL );
Pa_StartStream( stream );
while ( 1 ) {
count = openmpt_module_read_stereo( mod, SAMPLERATE, BUFFERSIZE, left, right );
if ( count == 0 ) {
break;
}
Pa_WriteStream( stream, buffers, (unsigned long)count );
}
Pa_StopStream( stream );
Pa_CloseStream( stream );
Pa_Terminate();
openmpt_module_destroy( mod );
return 0;
}

View file

@ -0,0 +1,78 @@
/*
* libopenmpt_example_cxx.cpp
* --------------------------
* Purpose: libopenmpt C++ API example
* Notes : PortAudio C++ is used for sound output.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
/*
* Usage: libopenmpt_example_cxx SOMEMODULE
*/
#include <exception>
#include <fstream>
#include <iostream>
#include <new>
#include <stdexcept>
#include <vector>
#include <libopenmpt/libopenmpt.hpp>
#include <portaudiocpp/PortAudioCpp.hxx>
#if ( defined( _WIN32 ) || defined( WIN32 ) ) && ( defined( _UNICODE ) || defined( UNICODE ) )
#if defined( __GNUC__ )
// mingw-w64 g++ does only default to special C linkage for "main", but not for "wmain" (see <https://sourceforge.net/p/mingw-w64/wiki2/Unicode%20apps/>).
extern "C" int wmain( int argc, wchar_t * argv[] ) {
#else
int wmain( int argc, wchar_t * argv[] ) {
#endif
#else
int main( int argc, char * argv[] ) {
#endif
try {
if ( argc != 2 ) {
throw std::runtime_error( "Usage: libopenmpt_example_cxx SOMEMODULE" );
}
const std::size_t buffersize = 480;
#if defined( LIBOPENMPT_QUIRK_NO_CSTDINT )
const openmpt::std::int32_t samplerate = 48000;
#else
const std::int32_t samplerate = 48000;
#endif
std::vector<float> left( buffersize );
std::vector<float> right( buffersize );
const float * const buffers[2] = { left.data(), right.data() };
std::ifstream file( argv[1], std::ios::binary );
openmpt::module mod( file );
portaudio::AutoSystem portaudio_initializer;
portaudio::System & portaudio = portaudio::System::instance();
portaudio::DirectionSpecificStreamParameters outputstream_parameters( portaudio.defaultOutputDevice(), 2, portaudio::FLOAT32, false, portaudio.defaultOutputDevice().defaultHighOutputLatency(), 0 );
portaudio::StreamParameters stream_parameters( portaudio::DirectionSpecificStreamParameters::null(), outputstream_parameters, samplerate, paFramesPerBufferUnspecified, paNoFlag );
portaudio::BlockingStream stream( stream_parameters );
stream.start();
while ( true ) {
std::size_t count = mod.read( samplerate, buffersize, left.data(), right.data() );
if ( count == 0 ) {
break;
}
try {
stream.write( buffers, static_cast<unsigned long>( count ) );
} catch ( const portaudio::PaException & pa_exception ) {
if ( pa_exception.paError() != paOutputUnderflowed ) {
throw;
}
}
}
stream.stop();
} catch ( const std::bad_alloc & ) {
std::cerr << "Error: " << std::string( "out of memory" ) << std::endl;
return 1;
} catch ( const std::exception & e ) {
std::cerr << "Error: " << std::string( e.what() ? e.what() : "unknown error" ) << std::endl;
return 1;
}
return 0;
}

View file

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View file

@ -0,0 +1,10 @@
minimp3 version as of 2017-04-25 from
http://keyj.emphy.de/files/projects/minimp3.tar.gz .
The following changes have been made:
- mp3_create() declaration has been fixed.
- GET_DATA() has been rewritten to avoid unaligned access warnings.
- Signed/unsigned comparison warnings have been fixed.
- Modifications have been marked with // OpenMPT
- Obviously, unnecessary folders and files have been removed.
- For building, premake is used to generate Visual Studio project files.
See ../build/premake/ for details.

View file

@ -0,0 +1,184 @@
// a libc replacement (more or less) for the Microsoft Visual C compiler
// this file is public domain -- do with it whatever you want!
#ifndef __LIBC_H_INCLUDED__
#define __LIBC_H_INCLUDED__
// check if minilibc is required
#ifndef NEED_MINILIBC
#ifndef NOLIBC
#define NEED_MINILIBC 0
#else
#define NEED_MINILIBC 1
#endif
#endif
#ifdef _MSC_VER
#define INLINE __forceinline
#define FASTCALL __fastcall
#ifdef NOLIBC
#ifdef MAIN_PROGRAM
int _fltused=0;
#endif
#endif
#else
#define INLINE inline
#define FASTCALL __attribute__((fastcall))
#include <stdint.h>
#endif
#ifdef _WIN32
#ifndef WIN32
#define WIN32
#endif
#endif
#ifdef WIN32
#include <windows.h>
#endif
#if !NEED_MINILIBC
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#endif
#include <math.h>
#ifndef __int8_t_defined
#define __int8_t_defined
typedef unsigned char uint8_t;
typedef signed char int8_t;
typedef unsigned short uint16_t;
typedef signed short int16_t;
typedef unsigned int uint32_t;
typedef signed int int32_t;
#ifdef _MSC_VER
typedef unsigned __int64 uint64_t;
typedef signed __int64 int64_t;
#else
typedef unsigned long long uint64_t;
typedef signed long long int64_t;
#endif
#endif
#ifndef NULL
#define NULL 0
#endif
#ifndef M_PI
#define M_PI 3.14159265358979
#endif
///////////////////////////////////////////////////////////////////////////////
#if NEED_MINILIBC
static INLINE void libc_memset(void *dest, int value, int count) {
if (!count) return;
__asm {
cld
mov edi, dest
mov eax, value
mov ecx, count
rep stosb
}
}
static INLINE void libc_memcpy(void *dest, const void *src, int count) {
if (!count) return;
__asm {
cld
mov esi, src
mov edi, dest
mov ecx, count
rep movsb
}
}
#define libc_memmove libc_memcpy
static INLINE void* libc_malloc(int size) {
return (void*) LocalAlloc(LMEM_FIXED, size);
}
static INLINE void* libc_calloc(int size, int nmemb) {
return (void*) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, size * nmemb);
}
static INLINE void* libc_realloc(void* old, int size) {
int oldsize = (int) LocalSize((HLOCAL) old);
void *mem;
if (size <= oldsize) return old;
mem = LocalAlloc(LMEM_FIXED, size);
libc_memcpy(mem, old, oldsize);
LocalFree((HLOCAL) old);
return mem;
}
static INLINE void libc_free(void *mem) {
LocalFree((HLOCAL) mem);
}
static INLINE double libc_frexp(double x, int *e) {
double res = -9999.999;
unsigned __int64 i = *(unsigned __int64*)(&x);
if (!(i & 0x7F00000000000000UL)) {
*e = 0;
return x;
}
*e = ((i << 1) >> 53) - 1022;
i &= 0x800FFFFFFFFFFFFFUL;
i |= 0x3FF0000000000000UL;
return *(double*)(&i) * 0.5;
}
static INLINE double __declspec(naked) libc_exp(double x) { __asm {
fldl2e
fld qword ptr [esp+4]
fmul
fst st(1)
frndint
fxch
fsub st(0), st(1)
f2xm1
fld1
fadd
fscale
ret
} }
static INLINE double __declspec(naked) libc_pow(double b, double e) { __asm {
fld qword ptr [esp+12]
fld qword ptr [esp+4]
fyl2x
// following is a copy of libc_exp:
fst st(1)
frndint
fxch
fsub st(0), st(1)
f2xm1
fld1
fadd
fscale
ret
} }
#else // NEED_MINILIBC == 0
#define libc_malloc malloc
#define libc_calloc calloc
#define libc_realloc realloc
#define libc_free free
#define libc_memset memset
#define libc_memcpy memcpy
#define libc_memmove memmove
#define libc_frexp frexp
#define libc_exp exp
#define libc_pow pow
#endif // NEED_MINILIBC
#endif//__LIBC_H_INCLUDED__

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,23 @@
#ifndef __MINIMP3_H_INCLUDED__
#define __MINIMP3_H_INCLUDED__
#define MP3_MAX_SAMPLES_PER_FRAME (1152*2)
typedef struct _mp3_info {
int sample_rate;
int channels;
int audio_bytes; // generated amount of audio per frame
} mp3_info_t;
typedef void* mp3_decoder_t;
#if 1 // OpenMPT
extern mp3_decoder_t *mp3_create(void); // OpenMPT
#else // OpenMPT
extern mp3_decoder_t mp3_create(void);
#endif // OpenMPT
extern int mp3_decode(mp3_decoder_t *dec, void *buf, int bytes, signed short *out, mp3_info_t *info);
extern void mp3_done(mp3_decoder_t *dec);
#define mp3_free(dec) do { mp3_done(dec); dec = NULL; } while(0)
#endif//__MINIMP3_H_INCLUDED__

View file

@ -0,0 +1,17 @@
miniz DEFLATE implementation.
https://github.com/richgel999/miniz
2.0.6 beta
Modifications for OpenMPT:
* #define MINIZ_NO_STDIO has been set because OpenMPT does not need stdio
functionality and miniz relies on secure-CRT file i/o functions in windows
builds which are not available on all mingw64 versions.
* #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 is used unconditionally,
because unaligned access is undefined behaviour.
* Various warnings in platform detection logic using undefined macros have
been fixed.
* Warning `warning: cast from 'const mz_uint8 *' (aka 'const unsigned char *')
to 'const mz_uint32 *' (aka 'const unsigned int *') increases required
alignment from 1 to 4 [-Wcast-align]` has been fixed.
* Prototypes of `tdefl_compressor_alloc` and `tinfl_decompressor_alloc`
have beeen fixed
No further changes have been made.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=${prefix}/include
Name: libmodplug
Description: The ModPlug mod file playing library (emulated via libopenmpt).
Version: 0.8.8.5
Requires.private: libopenmpt
Libs: -L${libdir} -lmodplug
Libs.private:
Cflags: -I${includedir}

View file

@ -0,0 +1,171 @@
/*
* This source code is public domain.
*
* Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
*/
#ifndef MODPLUG_H__INCLUDED
#define MODPLUG_H__INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
struct _ModPlugFile;
typedef struct _ModPlugFile ModPlugFile;
struct _ModPlugNote {
unsigned char Note;
unsigned char Instrument;
unsigned char VolumeEffect;
unsigned char Effect;
unsigned char Volume;
unsigned char Parameter;
};
typedef struct _ModPlugNote ModPlugNote;
typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long);
/* Load a mod file. [data] should point to a block of memory containing the complete
* file, and [size] should be the size of that block.
* Return the loaded mod file on success, or NULL on failure. */
ModPlugFile* ModPlug_Load(const void* data, int size);
/* Unload a mod file. */
void ModPlug_Unload(ModPlugFile* file);
/* Read sample data into the buffer. Returns the number of bytes read. If the end
* of the mod has been reached, zero is returned. */
int ModPlug_Read(ModPlugFile* file, void* buffer, int size);
/* Get the name of the mod. The returned buffer is stored within the ModPlugFile
* structure and will remain valid until you unload the file. */
const char* ModPlug_GetName(ModPlugFile* file);
/* Get the length of the mod, in milliseconds. Note that this result is not always
* accurate, especially in the case of mods with loops. */
int ModPlug_GetLength(ModPlugFile* file);
/* Seek to a particular position in the song. Note that seeking and MODs don't mix very
* well. Some mods will be missing instruments for a short time after a seek, as ModPlug
* does not scan the sequence backwards to find out which instruments were supposed to be
* playing at that time. (Doing so would be difficult and not very reliable.) Also,
* note that seeking is not very exact in some mods -- especially those for which
* ModPlug_GetLength() does not report the full length. */
void ModPlug_Seek(ModPlugFile* file, int millisecond);
enum _ModPlug_Flags
{
MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */
MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */
MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */
MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */
MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */
};
enum _ModPlug_ResamplingMode
{
MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */
MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */
MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */
MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */
};
typedef struct _ModPlug_Settings
{
int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
/* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then
* down-mixes to the settings you choose. */
int mChannels; /* Number of channels - 1 for mono or 2 for stereo */
int mBits; /* Bits per sample - 8, 16, or 32 */
int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */
int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */
int mStereoSeparation; /* Stereo separation, 1 - 256 */
int mMaxMixChannels; /* Maximum number of mixing channels (polyphony), 32 - 256 */
int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */
int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */
int mBassAmount; /* XBass level 0(quiet)-100(loud) */
int mBassRange; /* XBass cutoff in Hz 10-100 */
int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */
int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */
int mLoopCount; /* Number of times to loop. Zero prevents looping.
-1 loops forever. */
} ModPlug_Settings;
/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample,
* sampling rate, and loop count, will take effect immediately. Those options which don't
* take effect immediately will take effect the next time you load a mod. */
void ModPlug_GetSettings(ModPlug_Settings* settings);
void ModPlug_SetSettings(const ModPlug_Settings* settings);
/* New ModPlug API Functions */
/* NOTE: Master Volume (1-512) */
unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ;
void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ;
int ModPlug_GetCurrentSpeed(ModPlugFile* file);
int ModPlug_GetCurrentTempo(ModPlugFile* file);
int ModPlug_GetCurrentOrder(ModPlugFile* file);
int ModPlug_GetCurrentPattern(ModPlugFile* file);
int ModPlug_GetCurrentRow(ModPlugFile* file);
int ModPlug_GetPlayingChannels(ModPlugFile* file);
void ModPlug_SeekOrder(ModPlugFile* file,int order);
int ModPlug_GetModuleType(ModPlugFile* file);
char* ModPlug_GetMessage(ModPlugFile* file);
#ifndef MODPLUG_NO_FILESAVE
/*
* EXPERIMENTAL Export Functions
*/
/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath);
/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportXM(ModPlugFile* file, const char* filepath);
/*Export to a Amiga MOD file. EXPERIMENTAL.*/
char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath);
/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */
char ModPlug_ExportIT(ModPlugFile* file, const char* filepath);
#endif // MODPLUG_NO_FILESAVE
unsigned int ModPlug_NumInstruments(ModPlugFile* file);
unsigned int ModPlug_NumSamples(ModPlugFile* file);
unsigned int ModPlug_NumPatterns(ModPlugFile* file);
unsigned int ModPlug_NumChannels(ModPlugFile* file);
unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff);
unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff);
/*
* Retrieve pattern note-data
*/
ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows);
/*
* =================
* Mixer callback
* =================
*
* Use this callback if you want to 'modify' the mixed data of LibModPlug.
*
* void proc(int* buffer,unsigned long channels,unsigned long nsamples) ;
*
* 'buffer': A buffer of mixed samples
* 'channels': N. of channels in the buffer
* 'nsamples': N. of samples in the buffeer (without taking care of n.channels)
*
* (Samples are signed 32-bit integers)
*/
void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ;
void ModPlug_UnloadMixerCallback(ModPlugFile* file) ;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View file

@ -0,0 +1,168 @@
/*
* This source code is public domain.
*
* Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
*/
#ifndef MODPLUG_H__INCLUDED
#define MODPLUG_H__INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
struct _ModPlugFile;
typedef struct _ModPlugFile ModPlugFile;
struct _ModPlugNote {
unsigned char Note;
unsigned char Instrument;
unsigned char VolumeEffect;
unsigned char Effect;
unsigned char Volume;
unsigned char Parameter;
};
typedef struct _ModPlugNote ModPlugNote;
typedef void (*ModPlugMixerProc)(int*, unsigned long, unsigned long);
/* Load a mod file. [data] should point to a block of memory containing the complete
* file, and [size] should be the size of that block.
* Return the loaded mod file on success, or NULL on failure. */
ModPlugFile* ModPlug_Load(const void* data, int size);
/* Unload a mod file. */
void ModPlug_Unload(ModPlugFile* file);
/* Read sample data into the buffer. Returns the number of bytes read. If the end
* of the mod has been reached, zero is returned. */
int ModPlug_Read(ModPlugFile* file, void* buffer, int size);
/* Get the name of the mod. The returned buffer is stored within the ModPlugFile
* structure and will remain valid until you unload the file. */
const char* ModPlug_GetName(ModPlugFile* file);
/* Get the length of the mod, in milliseconds. Note that this result is not always
* accurate, especially in the case of mods with loops. */
int ModPlug_GetLength(ModPlugFile* file);
/* Seek to a particular position in the song. Note that seeking and MODs don't mix very
* well. Some mods will be missing instruments for a short time after a seek, as ModPlug
* does not scan the sequence backwards to find out which instruments were supposed to be
* playing at that time. (Doing so would be difficult and not very reliable.) Also,
* note that seeking is not very exact in some mods -- especially those for which
* ModPlug_GetLength() does not report the full length. */
void ModPlug_Seek(ModPlugFile* file, int millisecond);
enum _ModPlug_Flags
{
MODPLUG_ENABLE_OVERSAMPLING = 1 << 0, /* Enable oversampling (*highly* recommended) */
MODPLUG_ENABLE_NOISE_REDUCTION = 1 << 1, /* Enable noise reduction */
MODPLUG_ENABLE_REVERB = 1 << 2, /* Enable reverb */
MODPLUG_ENABLE_MEGABASS = 1 << 3, /* Enable megabass */
MODPLUG_ENABLE_SURROUND = 1 << 4 /* Enable surround sound. */
};
enum _ModPlug_ResamplingMode
{
MODPLUG_RESAMPLE_NEAREST = 0, /* No interpolation (very fast, extremely bad sound quality) */
MODPLUG_RESAMPLE_LINEAR = 1, /* Linear interpolation (fast, good quality) */
MODPLUG_RESAMPLE_SPLINE = 2, /* Cubic spline interpolation (high quality) */
MODPLUG_RESAMPLE_FIR = 3 /* 8-tap fir filter (extremely high quality) */
};
typedef struct _ModPlug_Settings
{
int mFlags; /* One or more of the MODPLUG_ENABLE_* flags above, bitwise-OR'ed */
/* Note that ModPlug always decodes sound at 44100kHz, 32 bit, stereo and then
* down-mixes to the settings you choose. */
int mChannels; /* Number of channels - 1 for mono or 2 for stereo */
int mBits; /* Bits per sample - 8, 16, or 32 */
int mFrequency; /* Sampling rate - 11025, 22050, or 44100 */
int mResamplingMode; /* One of MODPLUG_RESAMPLE_*, above */
int mReverbDepth; /* Reverb level 0(quiet)-100(loud) */
int mReverbDelay; /* Reverb delay in ms, usually 40-200ms */
int mBassAmount; /* XBass level 0(quiet)-100(loud) */
int mBassRange; /* XBass cutoff in Hz 10-100 */
int mSurroundDepth; /* Surround level 0(quiet)-100(heavy) */
int mSurroundDelay; /* Surround delay in ms, usually 5-40ms */
int mLoopCount; /* Number of times to loop. Zero prevents looping.
-1 loops forever. */
} ModPlug_Settings;
/* Get and set the mod decoder settings. All options, except for channels, bits-per-sample,
* sampling rate, and loop count, will take effect immediately. Those options which don't
* take effect immediately will take effect the next time you load a mod. */
void ModPlug_GetSettings(ModPlug_Settings* settings);
void ModPlug_SetSettings(const ModPlug_Settings* settings);
/* New ModPlug API Functions */
/* NOTE: Master Volume (1-512) */
unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ;
void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ;
int ModPlug_GetCurrentSpeed(ModPlugFile* file);
int ModPlug_GetCurrentTempo(ModPlugFile* file);
int ModPlug_GetCurrentOrder(ModPlugFile* file);
int ModPlug_GetCurrentPattern(ModPlugFile* file);
int ModPlug_GetCurrentRow(ModPlugFile* file);
int ModPlug_GetPlayingChannels(ModPlugFile* file);
void ModPlug_SeekOrder(ModPlugFile* file,int order);
int ModPlug_GetModuleType(ModPlugFile* file);
char* ModPlug_GetMessage(ModPlugFile* file);
#ifndef MODPLUG_NO_FILESAVE
/*
* EXPERIMENTAL Export Functions
*/
/*Export to a Scream Tracker 3 S3M module. EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath);
/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/
char ModPlug_ExportXM(ModPlugFile* file, const char* filepath);
/*Export to a Amiga MOD file. EXPERIMENTAL.*/
char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath);
/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */
char ModPlug_ExportIT(ModPlugFile* file, const char* filepath);
#endif // MODPLUG_NO_FILESAVE
unsigned int ModPlug_NumInstruments(ModPlugFile* file);
unsigned int ModPlug_NumSamples(ModPlugFile* file);
unsigned int ModPlug_NumPatterns(ModPlugFile* file);
unsigned int ModPlug_NumChannels(ModPlugFile* file);
unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff);
unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff);
/*
* Retrieve pattern note-data
*/
ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows);
/*
* =================
* Mixer callback
* =================
*
* Use this callback if you want to 'modify' the mixed data of LibModPlug.
*
* void proc(int* buffer,unsigned long channels,unsigned long nsamples) ;
*
* 'buffer': A buffer of mixed samples
* 'channels': N. of channels in the buffer
* 'nsamples': N. of samples in the buffeer (without taking care of n.channels)
*
* (Samples are signed 32-bit integers)
*/
void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ;
void ModPlug_UnloadMixerCallback(ModPlugFile* file) ;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
/*
* This source code is public domain.
*
* Authors: Rani Assaf <rani@magic.metawire.com>,
* Olivier Lapicque <olivierl@jps.net>,
* Adam Goode <adam@evdebs.org> (endian and char fixes for PPC)
*/
#ifndef _STDAFX_H_
#define _STDAFX_H_
/* Autoconf detection of stdint/inttypes */
#if defined(HAVE_CONFIG_H) && !defined(CONFIG_H_INCLUDED)
# include "config.h"
# define CONFIG_H_INCLUDED 1
#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef _WIN32
#ifdef MSC_VER
#pragma warning (disable:4201)
#pragma warning (disable:4514)
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <stdio.h>
#include <malloc.h>
#include <stdint.h>
#define srandom(_seed) srand(_seed)
#define random() rand()
#define sleep(_ms) Sleep(_ms)
inline void ProcessPlugins(int n) {}
#define strncasecmp(a,b,c) strncmp(a,b,c)
#define strcasecmp(a,b) strcmp(a,b)
#define strnicmp(a,b,c) strncasecmp(a,b,c)
#define HAVE_SINF 1
#else
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
typedef int8_t CHAR;
typedef uint8_t UCHAR;
typedef uint8_t* PUCHAR;
typedef uint16_t USHORT;
typedef uint32_t ULONG;
typedef uint32_t UINT;
typedef uint32_t DWORD;
typedef int32_t LONG;
typedef int64_t LONGLONG;
typedef int32_t* LPLONG;
typedef uint32_t* LPDWORD;
typedef uint16_t WORD;
typedef uint8_t BYTE;
typedef uint8_t* LPBYTE;
typedef bool BOOL;
typedef char* LPSTR;
typedef void* LPVOID;
typedef uint16_t* LPWORD;
typedef const char* LPCSTR;
typedef void* PVOID;
typedef void VOID;
inline LONG MulDiv (long a, long b, long c)
{
// if (!c) return 0;
return ((uint64_t) a * (uint64_t) b ) / c;
}
#define MODPLUG_NO_FILESAVE
#define NO_AGC
#define LPCTSTR LPCSTR
#define lstrcpyn strncpy
#define lstrcpy strcpy
#define lstrcmp strcmp
#define WAVE_FORMAT_PCM 1
//#define ENABLE_EQ
#define GHND 0
inline int8_t * GlobalAllocPtr(unsigned int, size_t size)
{
int8_t * p = (int8_t *) malloc(size);
if (p != NULL) memset(p, 0, size);
return p;
}
inline void ProcessPlugins(int /* n */ ) {}
#define GlobalFreePtr(p) free((void *)(p))
#define strnicmp(a,b,c) strncasecmp(a,b,c)
#define wsprintf sprintf
#ifndef FALSE
#define FALSE false
#endif
#ifndef TRUE
#define TRUE true
#endif
#endif // _WIN32
#endif

View file

@ -0,0 +1,17 @@
This folder contains the stb_vorbis library from
https://github.com/nothings/stb/blob/master/stb_vorbis.c
(commit 9d9f75eb682dd98b34de08bb5c489c6c561c9fa6)
Modifications:
* Use of alloca has been replaced with malloc, as alloca is not in C99 and
fails to compile.
* Macro redefinition of alloca with mingw-w64 has been fixed.
* Macro redefinition of STB_VORBIS_NO_STDIO has been fixed.
* The following warnings have been silenced:
include/stb_vorbis/stb_vorbis.c:3928:32: warning: hi may be used uninitialized in this function [-Wmaybe-uninitialized]
include/stb_vorbis/stb_vorbis.c:3927:32: warning: low may be used uninitialized in this function [-Wmaybe-uninitialized]
Modifications are always additions and have been marked with // OpenMPT.
For building, premake is used to generate Visual Studio project files.
See ../build/premake/ for details.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,332 @@
/'
' libopenmpt_ext.bi
' -----------------
' Purpose: libopenmpt public FreeBASIC interface for libopenmpt extensions
' Notes : (currently none)
' Authors: Johannes Schultz
' OpenMPT Devs
' The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
'/
#Pragma Once
#Include Once "libopenmpt.bi"
Extern "C"
'* \brief Opaque type representing a libopenmpt extension module
Type openmpt_module_ext
opaque As Any Ptr
End Type
/'* \brief Construct an openmpt_module_ext
\param stream_callbacks Input stream callback operations.
\param stream Input stream to load the module from.
\param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module_ext. May be NULL.
\param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
\param errfunc Error function to define error behaviour. May be NULL.
\param erruser Error function user context.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
\param ctls A map of initial ctl values, see openmpt_module_get_ctls.
\return A pointer to the constructed openmpt_module_ext, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module_ext has been constructed successfully.
\sa openmpt_stream_callbacks
\since 0.3.0
'/
Declare Function openmpt_module_ext_create(ByVal stream_callbacks As openmpt_stream_callbacks, ByVal stream As Any Ptr, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal Error As Long Ptr, ByVal error_message As Const ZString Ptr Ptr, ByVal ctls As Const openmpt_module_initial_ctl Ptr) As openmpt_module_ext Ptr
/'* \brief Construct an openmpt_module_ext
\param filedata Data to load the module from.
\param filesize Amount of data available.
\param logfunc Logging function where warning and errors are written. The logging function may be called throughout the lifetime of openmpt_module_ext.
\param loguser User-defined data associated with this module. This value will be passed to the logging callback function (logfunc)
\param errfunc Error function to define error behaviour. May be NULL.
\param erruser Error function user context.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
\param ctls A map of initial ctl values, see openmpt_module_get_ctls.
\return A pointer to the constructed openmpt_module_ext, or NULL on failure.
\remarks The input data can be discarded after an openmpt_module_ext has been constructed successfully.
\since 0.3.0
'/
Declare Function openmpt_module_ext_create_from_memory(ByVal filedata As Const Any Ptr, ByVal filesize As UInteger, ByVal logfunc As openmpt_log_func, ByVal loguser As Any Ptr, ByVal errfunc As openmpt_error_func, ByVal erruser As Any Ptr, ByVal Error As Long Ptr, ByVal error_message As Const ZString Ptr Ptr, ByVal ctls As Const openmpt_module_initial_ctl Ptr) As openmpt_module_ext Ptr
/'* \brief Unload a previously created openmpt_module_ext from memory.
\param mod_ext The module to unload.
\since 0.3.0
'/
Declare Sub openmpt_module_ext_destroy(ByVal mod_ext As openmpt_module_ext Ptr)
/'* \brief Retrieve the openmpt_module handle from an openmpt_module_ext handle.
\param mod_ext The extension module handle to convert
\return An equivalent openmpt_module handle to pass to standard libopenmpt functions
\since 0.3.0
'/
Declare Function openmpt_module_ext_get_module(ByVal mod_ext As openmpt_module_ext Ptr) As openmpt_module Ptr
/'* Retrieve a libopenmpt extension.
\param mod_ext The module handle to work on.
\param interface_id The name of the extension interface to retrieve (e.g. LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS).
\param interface Appropriate structure of interface function pointers which is to be filled by this function (e.g. a pointer to a openmpt_module_ext_interface_pattern_vis structure).
\param interface_size Size of the interface's structure of function pointers (e.g. sizeof(openmpt_module_ext_interface_pattern_vis)).
\return 1 on success, 0 if the interface was not found.
\since 0.3.0
'/
Declare Function openmpt_module_ext_get_interface(ByVal mod_ext As openmpt_module_ext Ptr, ByVal interface_id As Const ZString Ptr, ByVal interface As Any Ptr, ByVal interface_size As UInteger) As Long
#define LIBOPENMPT_EXT_C_INTERFACE_PATTERN_VIS "pattern_vis"
'* Pattern command type
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_UNKNOWN = 0
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_GENERAL = 1
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_GLOBAL = 2
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_VOLUME = 3
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_PANNING = 4
Const OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_PITCH = 5
Type openmpt_module_ext_interface_pattern_vis
/'* Get pattern command type for pattern highlighting
\param mod_ext The module handle to work on.
\param pattern The pattern whose data should be retrieved.
\param row The row from which the data should be retrieved.
\param channel The channel from which the data should be retrieved.
\return The command type in the effect column at the given pattern position (see OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_*)
\sa get_pattern_row_channel_effect_type
'/
get_pattern_row_channel_volume_effect_type As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long) As Long
/'* Get pattern command type for pattern highlighting
\param mod_ext The module handle to work on.
\param pattern The pattern whose data should be retrieved.
\param row The row from which the data should be retrieved.
\param channel The channel from which the data should be retrieved.
\return The command type in the effect column at the given pattern position (see OPENMPT_MODULE_EXT_INTERFACE_PATTERN_VIS_EFFECT_TYPE_*)
\sa get_pattern_row_channel_volume_effect_type
'/
get_pattern_row_channel_effect_type As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal pattern As Long, ByVal row As Long, ByVal channel As Long) As Long
End Type
#define LIBOPENMPT_EXT_C_INTERFACE_INTERACTIVE "interactive"
Type openmpt_module_ext_interface_interactive
/'* Set the current ticks per row (speed)
\param mod_ext The module handle to work on.
\param speed The new tick count in range [1, 65535].
\throws openmpt::exception Throws an exception derived from openmpt::exception if the speed is outside the specified range.
\return 1 on success, 0 on failure.
\sa openmpt_module_get_current_speed
'/
set_current_speed As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal speed As Long) As Long
/'* Set the current module tempo
\param mod_ext The module handle to work on.
\param tempo The new tempo in range [32, 512]. The exact meaning of the value depends on the tempo mode used by the module.
\return 1 on success, 0 on failure.
\remarks The tempo may be reset by pattern commands at any time. Use set_tempo_factor to apply a tempo factor that is independent of pattern commands.
\sa openmpt_module_get_current_tempo
'/
set_current_tempo As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal tempo As Long) As Long
/'* Set the current module tempo factor without affecting playback pitch
\param mod_ext The module handle to work on.
\param factor The new tempo factor in range ]0.0, 4.0] - 1.0 means unmodified tempo.
\return 1 on success, 0 on failure.
\remarks Modifying the tempo without applying the same pitch factor using set_pitch_factor may cause rhythmic samples (e.g. drum loops) to go out of sync.
\sa get_tempo_factor
'/
set_tempo_factor As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal factor As Double) As Long
/'* Gets the current module tempo factor
\param mod_ext The module handle to work on.
\return The current tempo factor.
\sa set_tempo_factor
'/
get_tempo_factor As Function(ByVal mod_ext As openmpt_module_ext Ptr) As Double
/'* Set the current module pitch factor without affecting playback speed
\param mod_ext The module handle to work on.
\param factor The new pitch factor in range ]0.0, 4.0] - 1.0 means unmodified pitch.
\return 1 on success, 0 on failure.
\remarks Modifying the pitch without applying the the same tempo factor using set_tempo_factor may cause rhythmic samples (e.g. drum loops) to go out of sync.
\remarks To shift the pich by `n` semitones, the parameter can be calculated as follows: `2.0 ^ ( n / 12.0 )`
\sa get_pitch_factor
'/
set_pitch_factor As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal factor As Double) As Long
/'* Gets the current module pitch factor
\param mod_ext The module handle to work on.
\return The current pitch factor.
\sa set_pitch_factor
'/
get_pitch_factor As Function(ByVal mod_ext As openmpt_module_ext Ptr) As Double
/'* Set the current global volume
\param mod_ext The module handle to work on.
\param volume The new global volume in range [0.0, 1.0]
\return 1 on success, 0 on failure.
\remarks The global volume may be reset by pattern commands at any time. Use openmpt_module_set_render_param to apply a global overall volume factor that is independent of pattern commands.
\sa get_global_volume
'/
set_global_volume As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal volume As Double) As Long
/'* Get the current global volume
\param mod_ext The module handle to work on.
\return The current global volume in range [0.0, 1.0]
\sa set_global_volume
'/
get_global_volume As Function(ByVal mod_ext As openmpt_module_ext Ptr) As Double
/'* Set the current channel volume for a channel
\param mod_ext The module handle to work on.
\param channel The channel whose volume should be set, in range [0, openmpt_module_get_num_channels()[
\param volume The new channel volume in range [0.0, 1.0]
\return 1 on success, 0 on failure (channel out of range).
\remarks The channel volume may be reset by pattern commands at any time.
\sa get_channel_volume
'/
set_channel_volume As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal channel As Long, ByVal volume As Double) As Long
/'* Get the current channel volume for a channel
\param mod_ext The module handle to work on.
\param channel The channel whose volume should be retrieved, in range [0, openmpt_module_get_num_channels()[
\return The current channel volume in range [0.0, 1.0]
\sa set_channel_volume
'/
get_channel_volume As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal channel As Long) As Double
/'* Set the current mute status for a channel
\param mod_ext The module handle to work on.
\param channel The channel whose mute status should be set, in range [0, openmpt_module_get_num_channels()[
\param mute The new mute status. true is muted, false is unmuted.
\return 1 on success, 0 on failure (channel out of range).
\sa get_channel_mute_status
'/
set_channel_mute_status As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal channel As Long, ByVal mute As Long) As Long
/'* Get the current mute status for a channel
\param mod_ext The module handle to work on.
\param channel The channel whose mute status should be retrieved, in range [0, openmpt_module_get_num_channels()[
\return The current channel mute status. true is muted, false is unmuted.
\sa set_channel_mute_status
'/
get_channel_mute_status As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal channel As Long) As Long
/'* Set the current mute status for an instrument
\param mod_ext The module handle to work on.
\param instrument The instrument whose mute status should be set, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[
\param mute The new mute status. true is muted, false is unmuted.
\return 1 on success, 0 on failure (instrument out of range).
\sa get_instrument_mute_status
'/
set_instrument_mute_status As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal instrument As Long, ByVal mute As Long) As Long
/'* Get the current mute status for an instrument
\param mod_ext The module handle to work on.
\param instrument The instrument whose mute status should be retrieved, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[
\return The current instrument mute status. 1 is muted, 0 is unmuted, -1 means the instrument was out of range.
\sa set_instrument_mute_status
'/
get_instrument_mute_status As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal instrument As Long) As Long
/'* Play a note using the specified instrument
\param mod_ext The module handle to work on.
\param instrument The instrument that should be played, in range [0, openmpt_module_get_num_instruments()[ if openmpt_module_get_num_instruments is not 0, otherwise in [0, openmpt_module_get_num_samples()[
\param note The note to play, in rage [0, 119]. 60 is the middle C.
\param volume The volume at which the note should be triggered, in range [0.0, 1.0]
\param panning The panning position at which the note should be triggered, in range [-1.0, 1.0], 0.0 is center.
\return The channel on which the note is played. This can pe be passed to stop_note to stop the note. -1 means that no channel could be allocated and the note is not played.
\sa stop_note
'/
play_note As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal instrument As Long, ByVal note As Long, ByVal volume As Double, ByVal panning As Double) As Long
/'* Stop the note playing on the specified channel
\param mod_ext The module handle to work on.
\param channel The channel on which the note should be stopped.
\return 1 on success, 0 on failure (channel out of range).
\sa play_note
'/
stop_note As Function(ByVal mod_ext As openmpt_module_ext Ptr, ByVal channel As Long) As Long
End Type
End Extern
/'* \brief Construct an openmpt_module_ext
\param file The FreeBASIC file handle to load from.
\param logfunc Logging function where warning and errors are written. May be NULL.
\param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function.
\param errfunc Error function to define error behaviour. May be NULL.
\param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
\param ctls A map of initial ctl values, see openmpt_module_get_ctls.
\return A pointer to the constructed openmpt_module, or NULL on failure.
\remarks The file handle can be closed after an openmpt_module has been constructed successfully.
\sa openmpt_module_ext_create
'/
Function openmpt_module_ext_create_from_fbhandle(_
ByVal file As Integer,_
ByVal logfunc As openmpt_log_func = 0,_
ByVal loguser As Any Ptr = 0,_
ByVal errfunc As openmpt_error_func = 0,_
ByVal erruser As Any Ptr = 0,_
ByVal errorcode As Long Ptr = 0,_
ByVal error_message As Const ZString Ptr Ptr = 0,_
ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module_ext Ptr
Return openmpt_module_ext_create(openmpt_stream_get_file_callbacks(), Cast(FILE Ptr, FileAttr(file, fbFileAttrHandle)), logfunc, loguser, errfunc, erruser, errorcode, error_message, ctls)
End Function
/'* \brief Construct an openmpt_module_ext
\param filename The file to load from.
\param logfunc Logging function where warning and errors are written. May be NULL.
\param loguser Logging function user context. Used to pass any user-defined data associated with this module to the logging function.
\param errfunc Error function to define error behaviour. May be NULL.
\param erruser Error function user context. Used to pass any user-defined data associated with this module to the error function.
\param errorcode Pointer to an integer where an error may get stored. May be NULL.
\param error_message Pointer to a string pointer where an error message may get stored. May be NULL.
\param ctls A map of initial ctl values, see openmpt_module_get_ctls.
\return A pointer to the constructed openmpt_module, or NULL on failure.
\sa openmpt_module_ext_create
'/
Function openmpt_module_ext_create_from_filename(_
ByRef filename As String,_
ByVal logfunc As openmpt_log_func = 0,_
ByVal loguser As Any Ptr = 0,_
ByVal errfunc As openmpt_error_func = 0,_
ByVal erruser As Any Ptr = 0,_
ByVal errorcode As Long Ptr = 0,_
ByVal error_message As Const ZString Ptr Ptr = 0,_
ByVal ctls As Const openmpt_module_initial_ctl Ptr = 0) As openmpt_module_ext Ptr
Var file = fopen(filename, "rb")
Var retval = CPtr(openmpt_module Ptr, 0)
If(file <> 0) Then
retval = openmpt_module_ext_create(openmpt_stream_get_file_callbacks(), file, logfunc, loguser, errfunc, erruser, errorcode, error_message, ctls)
fclose(file)
EndIf
Return retval
End Function

View file

@ -0,0 +1 @@
See https://lib.openmpt.org/ for documentation, FAQ and other details.

View file

@ -0,0 +1 @@
See https://lib.openmpt.org/ for documentation, FAQ and other details.

View file

@ -0,0 +1 @@
See https://lib.openmpt.org/ for documentation, FAQ and other details.

Some files were not shown because too many files have changed in this diff Show more