Updated libopenmpt to version 0.6.0, with major new changes. This new version requires macOS 10.15 to work, due to libc++ features required. A compatibility plugin has been duplicated from the existing plugin, which will now load libopenmpt 0.5.14, or whatever newer version may come out that still supports as old as macOS 10.12.

CQTexperiment
Christopher Snowhill 2021-12-26 03:29:43 -08:00
parent 8d7cd6cc93
commit 106eb587b4
846 changed files with 202461 additions and 15604 deletions

View File

@ -97,6 +97,7 @@
56C63D910D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m in Sources */ = {isa = PBXBuildFile; fileRef = 56C63D900D647DF300EAE25A /* NSComparisonPredicate+CogPredicate.m */; };
56DB084C0D6717DC00453B6A /* NSNumber+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB084B0D6717DC00453B6A /* NSNumber+CogSort.m */; };
56DB08550D67185300453B6A /* NSArray+CogSort.m in Sources */ = {isa = PBXBuildFile; fileRef = 56DB08540D67185300453B6A /* NSArray+CogSort.m */; };
83293070277886250010C07E /* OpenMPTOld.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8329306D277885790010C07E /* OpenMPTOld.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
832C1253180BD1E2005507C1 /* Cog.help in Resources */ = {isa = PBXBuildFile; fileRef = 832C1252180BD1E2005507C1 /* Cog.help */; };
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 830B62B320E4EF89004A74B2 /* OpusPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8301F94520E4EEF70017B2DC /* VorbisPlugin.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
@ -352,6 +353,20 @@
remoteGlobalIDString = 8314D6311A354DFE00EEE8E6;
remoteInfo = sidplay;
};
8329306C277885790010C07E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 83E5EFA31FFEF78100659F0F;
remoteInfo = OpenMPTOld;
};
8329306E2778860C0010C07E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 83E5EFA21FFEF78100659F0F;
remoteInfo = OpenMPTOld;
};
834068BC20E4E40200A01561 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 17C808C00C3BD1DD005707C4 /* WavPack.xcodeproj */;
@ -623,6 +638,7 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
83293070277886250010C07E /* OpenMPTOld.bundle in CopyFiles */,
834D794020E4EFEF00C4A5CC /* VorbisPlugin.bundle in CopyFiles */,
834D793F20E4EFEA00C4A5CC /* OpusPlugin.bundle in CopyFiles */,
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */,
@ -829,6 +845,7 @@
56DB08530D67185300453B6A /* NSArray+CogSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+CogSort.h"; path = "Spotlight/NSArray+CogSort.h"; sourceTree = "<group>"; };
56DB08540D67185300453B6A /* NSArray+CogSort.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+CogSort.m"; path = "Spotlight/NSArray+CogSort.m"; sourceTree = "<group>"; };
8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = "<group>"; };
83293065277885790010C07E /* OpenMPTOld.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPTOld.xcodeproj; path = Plugins/OpenMPT.old/OpenMPTOld.xcodeproj; sourceTree = "<group>"; };
832C1252180BD1E2005507C1 /* Cog.help */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Cog.help; sourceTree = "<group>"; };
833F681E1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
833F681F1CDBCAA800AFB9F0 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -1155,6 +1172,7 @@
83B0669C180D5668008E3612 /* MIDI.xcodeproj */,
17C8089E0C3BD1AB005707C4 /* Musepack.xcodeproj */,
83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */,
83293065277885790010C07E /* OpenMPTOld.xcodeproj */,
8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */,
8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */,
17C808A70C3BD1BA005707C4 /* Shorten.xcodeproj */,
@ -1492,6 +1510,14 @@
name = Products;
sourceTree = "<group>";
};
83293066277885790010C07E /* Products */ = {
isa = PBXGroup;
children = (
8329306D277885790010C07E /* OpenMPTOld.bundle */,
);
name = Products;
sourceTree = "<group>";
};
834068A720E4E40200A01561 /* Products */ = {
isa = PBXGroup;
children = (
@ -1701,6 +1727,7 @@
buildRules = (
);
dependencies = (
8329306F2778860C0010C07E /* PBXTargetDependency */,
ED69CBC625BE32B40090B90D /* PBXTargetDependency */,
834D793E20E4EFD200C4A5CC /* PBXTargetDependency */,
834D793020E4EFCC00C4A5CC /* PBXTargetDependency */,
@ -1843,6 +1870,10 @@
ProductGroup = 83CA5AF820E4E394003E463A /* Products */;
ProjectRef = 83E5EFAC1FFEF78100659F0F /* OpenMPT.xcodeproj */;
},
{
ProductGroup = 83293066277885790010C07E /* Products */;
ProjectRef = 83293065277885790010C07E /* OpenMPTOld.xcodeproj */;
},
{
ProductGroup = 830B62AF20E4EF89004A74B2 /* Products */;
ProjectRef = 8375B05117FFEA400092A79F /* OpusPlugin.xcodeproj */;
@ -2001,6 +2032,13 @@
remoteRef = 8314D6401A354DFF00EEE8E6 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
8329306D277885790010C07E /* OpenMPTOld.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = OpenMPTOld.bundle;
remoteRef = 8329306C277885790010C07E /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
834068BD20E4E40200A01561 /* WavPack.bundle */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
@ -2330,6 +2368,11 @@
name = Preferences;
targetProxy = 17F5623A0C3BD9280019975C /* PBXContainerItemProxy */;
};
8329306F2778860C0010C07E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = OpenMPTOld;
targetProxy = 8329306E2778860C0010C07E /* PBXContainerItemProxy */;
};
834D793020E4EFCC00C4A5CC /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = "VorbisPlugin Plugin";

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2021 Christopher Snowhill. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,26 @@
Copyright (c) 2004-2021, 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,260 @@
README
======
OpenMPT and libopenmpt
======================
This repository contains OpenMPT, a free Windows/Wine-based
[tracker](https://en.wikipedia.org/wiki/Music_tracker) and libopenmpt,
a library to render tracker music (MOD, XM, S3M, IT MPTM and dozens of other
legacy formats) to a PCM audio stream. libopenmpt is directly based on OpenMPT,
offering the same playback quality and format support, and development of the
two happens in parallel.
License
-------
The OpenMPT/libopenmpt project is distributed under the *BSD-3-Clause* License.
See [LICENSE](LICENSE) for the full license text.
Files below the `include/` (external projects) and `contrib/` (related assets
not directly considered integral part of the project) folders may be subject to
other licenses. See the respective subfolders for license information. These
folders are not distributed in all source packages, and in particular they are
not distributed in the Autotools packages.
How to compile
--------------
### OpenMPT
- Supported Visual Studio versions:
- Visual Studio 2017 and 2019 Community/Professional/Enterprise
To compile the project, open `build/vsVERSIONwin7/OpenMPT.sln` (VERSION
being 2017 or 2019) and hit the compile button. Other target systems can
be found in the `vs2017*` and `vs2019*` sibling folders.
- OpenMPT requires the compile host system to be 64bit x86-64.
- The Windows 8.1 SDK is required to build OpenMPT with Visual Studio 2017
(this is included with Visual Studio, however may need to be selected
explicitly during setup).
- Microsoft Foundation Classes (MFC) are required to build OpenMPT.
### 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
Cross-compilation is generally supported (although only tested for
targetting MinGW-w64).
Note that some MinGW-w64 distributions come with the `win32` threading model
enabled by default instead of the `posix` threading model. The `win32`
threading model lacks proper support for C++11 `<thread>` and `<mutex>` as
well as thread-safe magic statics. It is recommended to use the `posix`
threading model for libopenmpt for this reason. On Debian, the appropriate
configure command is
`./configure --host=x86_64-w64-mingw32 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix`
for 64bit, or
`./configure --host=i686-w64-mingw32 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix`
for 32bit. Other MinGW-w64 distributions may differ.
- Visual Studio:
- You will find solutions for Visual Studio 2017 and 2019 in the
corresponding `build/vsVERSIONwin7/` folder.
Projects that target Windows 10 Desktop (including ARM and ARM64) are
available in `build/vsVERSIONwin10/` (Visual Studio 2017 requires SDK
version 10.0.16299).
Minimal projects that target Windows 10 UWP are available in
`build/vsVERSIONuwp/`.
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.
- libopenmpt requires the compile host system to be 64bit x86-64 when
building with Visual Studio.
- 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 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](https://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 compiler version is at least GCC 7.
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,
and Haiku (32-bit Hybrid and 64-bit)):
The minimum required compiler versions are:
- gcc 7
- clang 5
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):
Run:
# generates WebAssembly with JavaScript fallback
make CONFIG=emscripten EMSCRIPTEN_TARGET=all
or
# generates WebAssembly
make CONFIG=emscripten EMSCRIPTEN_TARGET=wasm
or
# generates JavaScript with compatibility for older VMs
make CONFIG=emscripten EMSCRIPTEN_TARGET=js
Running the test suite on the command line is also supported by using
node.js. Depending on how your distribution calls the `node.js` binary,
you might have to edit `build/make/config-emscripten.mk`.
- DJGPP / DOS
Cross-compilation from Linux systems is supported with DJGPP GCC 7.2 or
later via
make CONFIG=djgpp
`openmpt123` can use liballegro 4.2 for sound output on DJGPP/DOS.
liballegro can either be installed system-wide in the DJGPP environment
or downloaded into the `libopenmpt` source tree.
make CONFIG=djgpp USE_ALLEGRO42=1 # use installed liballegro
or
./build/download_externals.sh # download liballegro binaries
make CONFIG=djgpp USE_ALLEGRO42=1 BUNDLED_ALLEGRO42=1
- 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 compile libopenmpt with other C++17 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`.
Contributing to OpenMPT/libopenmpt
----------------------------------
See [contributing](doc/contributing.md).

View File

@ -0,0 +1,744 @@
/*
* 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
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
#endif
#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)
#if MPT_OS_WINDOWS
#if !defined(MPT_BUILD_WINESUPPORT)
#define MPT_WITH_MFC
#endif // !MPT_BUILD_WINESUPPORT
#endif // MPT_OS_WINDOWS
// OpenMPT-only dependencies
#if !MPT_MSVC_BEFORE(2019,0)
// disabled for VS2017 because of multiple initialization of inline variables
// https://developercommunity.visualstudio.com/t/static-inline-variable-gets-destroyed-multiple-tim/297876
#define MPT_WITH_ASIO
#endif
#define MPT_WITH_DMO
#define MPT_WITH_LAME
#define MPT_WITH_LHASA
#define MPT_WITH_MINIZIP
#define MPT_WITH_NLOHMANNJSON
#define MPT_WITH_OPUS
#define MPT_WITH_OPUSENC
#define MPT_WITH_OPUSFILE
#define MPT_WITH_PORTAUDIO
//#define MPT_WITH_PULSEAUDIO
//#define MPT_WITH_PULSEAUDIOSIMPLE
#define MPT_WITH_RTAUDIO
#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_LTDL
#if MPT_OS_WINDOWS
#define MPT_WITH_MEDIAFOUNDATION
#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
#if MPT_OS_WINDOWS
#if (_WIN32_WINNT >= 0x0A00)
#define MPT_WITH_WINDOWS10
#endif
#endif
#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_SMALL)
//#define MPT_WITH_DL
//#define MPT_WITH_FLAC
//#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_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(MPT_BUILD_XCODE)
#if defined(MODPLUG_TRACKER)
// n/a
#endif // MODPLUG_TRACKER
#if defined(LIBOPENMPT_BUILD)
//#define MPT_WITH_DL
//#define MPT_WITH_FLAC
//#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
#endif // MPT_BUILD_XCODE
#if defined(MODPLUG_TRACKER)
// Enable built-in test suite.
#if defined(MPT_BUILD_DEBUG) || defined(MPT_BUILD_CHECKED)
#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(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT)
#define MPT_LOG_GLOBAL_LEVEL_STATIC
#define MPT_LOG_GLOBAL_LEVEL 0
#endif
// Enable all individual logging macros and MPT_LOG calls
//#define MPT_ALL_LOGGING
// Disable all runtime asserts
#if !defined(MPT_BUILD_DEBUG) && !defined(MPT_BUILD_CHECKED) && !defined(MPT_BUILD_WINESUPPORT)
#define NO_ASSERTS
#endif
// 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
// (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_DEBUG) || defined(MPT_BUILD_CHECKED) || defined(ENABLE_TESTS)
// enable asserts
#else
#define NO_ASSERTS
#endif
//#define NO_LOGGING
//#define MPT_ALL_LOGGING
#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
//#define NO_PLUGINS
//#define NO_LIBOPENMPT_C
//#define NO_LIBOPENMPT_CXX
#endif // LIBOPENMPT_BUILD
#if MPT_OS_WINDOWS
#ifndef MPT_ENABLE_CHARSET_LOCALE
#define MPT_ENABLE_CHARSET_LOCALE
#endif
#elif MPT_OS_LINUX
#elif MPT_OS_ANDROID
#elif MPT_OS_EMSCRIPTEN
#ifndef MPT_LOCALE_ASSUME_CHARSET
#define MPT_LOCALE_ASSUME_CHARSET Charset::UTF8
#endif
#elif MPT_OS_MACOSX_OR_IOS
#elif MPT_OS_DJGPP
#ifndef MPT_LOCALE_ASSUME_CHARSET
#define MPT_LOCALE_ASSUME_CHARSET Charset::CP437
#endif
#endif
#if MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE)
// 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_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 defined(ENABLE_ASM)
#if MPT_COMPILER_MSVC && defined(_M_IX86)
#define ENABLE_CPUID
// Generate general x86 inline assembly and intrinsics.
#define ENABLE_X86
// Generate MMX instructions (only used when the CPU supports it).
#define ENABLE_MMX
// Generate SSE instructions (only used when the CPU supports it).
#define ENABLE_SSE
// Generate SSE2 instructions (only used when the CPU supports it).
#define ENABLE_SSE2
// Generate SSE3 instructions (only used when the CPU supports it).
#define ENABLE_SSE3
// Generate SSE4 instructions (only used when the CPU supports it).
#define ENABLE_SSE4
// Generate AVX instructions (only used when the CPU supports it).
#define ENABLE_AVX
// Generate AVX2 instructions (only used when the CPU supports it).
#define ENABLE_AVX2
#elif MPT_COMPILER_MSVC && defined(_M_X64)
#define ENABLE_CPUID
// Generate general x64 intrinsics.
#define ENABLE_X64
// Generate SSE instructions (only used when the CPU supports it).
#define ENABLE_SSE
// Generate SSE2 instructions (only used when the CPU supports it).
#define ENABLE_SSE2
// Generate SSE3 instructions (only used when the CPU supports it).
#define ENABLE_SSE3
// Generate SSE4 instructions (only used when the CPU supports it).
#define ENABLE_SSE4
// Generate AVX instructions (only used when the CPU supports it).
#define ENABLE_AVX
// Generate AVX2 instructions (only used when the CPU supports it).
#define ENABLE_AVX2
#elif MPT_BUILD_XCODE && defined(__x86_64__)
// No CPUID enabled, only one code path supported anyway
// #define ENABLE_CPUID
// Enable the SSE2 intrinsic functions unconditionally
#define ENABLE_SSE2
#elif MPT_BUILD_XCODE && defined(__arm64__)
// No CPUID, it's kind of a pain on ARM anyway
// #define ENABLE_CPUID
// Enable the NEON intrinsic functions unconditionally
#define ENABLE_NEON
#else
#undef ENABLE_ASM
#endif // arch
#endif // ENABLE_ASM
#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 !MPT_COMPILER_MSVC && !MPT_COMPILER_CLANG && defined(MPT_WITH_MEDIAFOUNDATION)
#undef MPT_WITH_MEDIAFOUNDATION // MediaFoundation requires MSVC or Clang due to ATL (no MinGW support)
#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(MODPLUG_TRACKER) && !defined(MPT_ENABLE_DYNBIND)
#define MPT_ENABLE_DYNBIND // Tracker requires dynamic library loading for export codecs
#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 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(NO_PLUGINS)
// Any plugin type requires NO_PLUGINS to not be defined.
#define NO_VST
#endif
#if defined(ENABLE_ASM) || !defined(NO_VST)
#define MPT_ENABLE_ALIGNED_ALLOC
#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
#ifdef MPT_WITH_MFC
//#define MPT_MFC_FULL // use full MFC, including MFC controls
#endif // MPT_WITH_MFC
#if defined(MODPLUG_TRACKER)
#if MPT_OS_WINDOWS
#if !defined(MPT_BUILD_WINESUPPORT)
#ifndef MPT_MFC_FULL
#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)
#endif // !MPT_MFC_FULL
#endif // !MPT_BUILD_WINESUPPORT
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#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
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif
#define __STDC_CONSTANT_MACROS
#define __STDC_LIMIT_MACROS
#define _USE_MATH_DEFINES
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif
// 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.
#pragma warning(error:4463) // Treat overflow; assigning value to bit-field that can only hold values from low_value to high_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:6297) // 32-bit value is shifted, then cast to 64-bit value. Results might not be an expected value.
#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_CLANG
#if defined(MPT_BUILD_MSVC)
#pragma clang diagnostic warning "-Wimplicit-fallthrough"
#endif // MPT_BUILD_MSVC
#if defined(MODPLUG_TRACKER)
#pragma clang diagnostic ignored "-Wunused-local-typedef"
#endif // MODPLUG_TRACKER
#endif // MPT_COMPILER_CLANG
// standard library quirks
// third-party library configuration
#ifdef MPT_WITH_FLAC
#ifdef MPT_BUILD_MSVC_STATIC
#define FLAC__NO_DLL
#endif
#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,469 @@
/*
* 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(U_("Constructing Component %1"))
( mpt::ToUnicode(mpt::Charset::ASCII, 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", U_("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", U_("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", U_("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
{
if(settingsKey.empty())
{
return false;
}
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,519 @@
/*
* 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 "BuildSettings.h"
#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() = default;
public:
virtual ~IComponent() = default;
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:
ComponentType GetType() const override;
bool IsInitialized() const override;
bool IsAvailable() const override;
mpt::ustring GetVersion() const override;
public:
void Initialize() override;
protected:
virtual bool DoInitialize() = 0;
};
class ComponentBuiltin : public ComponentBase
{
public:
ComponentBuiltin()
: ComponentBase(ComponentTypeBuiltin)
{
return;
}
bool DoInitialize() override
{
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:
bool DoInitialize() override = 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 )
#if MPT_OS_WINDOWS
#ifdef UNICODE
#define MPT_COMPONENT_BINDWIN_SUFFIX "W"
#else
#define MPT_COMPONENT_BINDWIN_SUFFIX "A"
#endif
#define MPT_COMPONENT_BINDWIN(libName, func) MPT_DO { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0
#define MPT_COMPONENT_BINDWIN_OPTIONAL(libName, func) Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )
#define MPT_COMPONENT_BINDWIN_SYMBOL(libName, symbol, func) MPT_DO { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } MPT_WHILE_0
#define MPT_COMPONENT_BINDWIN_SYMBOL_OPTIONAL(libName, symbol, func) Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )
#endif
class ComponentSystemDLL : public ComponentLibrary
{
private:
mpt::PathString m_BaseName;
public:
ComponentSystemDLL(const mpt::PathString &baseName)
: ComponentLibrary(ComponentTypeSystem)
, m_BaseName(baseName)
{
return;
}
bool DoInitialize() override
{
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;
}
bool DoInitialize() override
{
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() = default;
public:
virtual ~IComponentFactory() = default;
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();
std::string GetID() const override;
std::string GetSettingsKey() const override;
std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const override = 0;
ComponentFactoryMethod GetStaticConstructor() const override = 0;
};
template <typename T>
class ComponentFactory
: public ComponentFactoryBase
{
public:
ComponentFactory()
: ComponentFactoryBase(T::g_ID, T::g_SettingsKey)
{
return;
}
public:
std::shared_ptr<IComponent> Construct(ComponentManager &componentManager) const override
{
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 override
{
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;
protected:
virtual ~IComponentManagerSettings() = default;
};
class ComponentManagerSettingsDefault
: public IComponentManagerSettings
{
public:
bool LoadOnStartup() const override { return false; }
bool KeepLoaded() const override { return true; }
bool IsBlocked(const std::string & /*key*/ ) const override { return false; }
mpt::PathString Path() const override { 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>()));
}
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;
}
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,35 @@
/*
* 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 "BuildSettings.h"
OPENMPT_NAMESPACE_BEGIN
class FileReaderTraitsMemory;
class FileReaderTraitsStdStream;
using FileReaderTraitsDefault = FileReaderTraitsStdStream;
namespace detail {
template <typename Ttraits>
class FileReader;
} // namespace detail
using FileReader = detail::FileReader<FileReaderTraitsDefault>;
using MemoryFileReader = detail::FileReader<FileReaderTraitsMemory>;
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,433 @@
/*
* 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 mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text)
{
#ifdef MPT_LOG_IS_DISABLED
MPT_UNREFERENCED_PARAMETER(loc);
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) + U_(": ") + mpt::String::RTrim(text, U_("\r\n"));
const mpt::ustring file = mpt::ToUnicode(mpt::CharsetSource, loc.file_name() ? loc.file_name() : "");
const mpt::ustring function = mpt::ToUnicode(mpt::CharsetSource, loc.function_name() ? loc.function_name() : "");
const mpt::ustring line = mpt::ufmt::dec(loc.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 mpt::ofstream s_logfile;
if(!s_logfile)
{
s_logfile.open(P_("mptrack.log"), std::ios::app);
}
if(s_logfile)
{
mpt::IO::WriteText(s_logfile, mpt::ToCharset(mpt::CharsetLogfile, mpt::format(U_("%1+%2 %3(%4): %5 [%6]\n"))
( mpt::Date::ANSI::ToUString(cur)
, mpt::ufmt::right(6, mpt::ufmt::dec(diff))
, file
, line
, message
, function
)));
mpt::IO::Flush(s_logfile);
}
}
if(mpt::log::DebuggerEnabled)
{
OutputDebugStringW(mpt::ToWide(mpt::format(U_("%1(%2): +%3 %4 [%5]\n"))
( file
, line
, mpt::ufmt::right(6, mpt::ufmt::dec(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(), mpt::saturate_cast<DWORD>(consoletext.length()), &dummy, NULL);
}
#elif defined(MODPLUG_TRACKER) && defined(MPT_BUILD_WINESUPPORT)
std::clog
<< "NativeSupport: "
<< mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": "
<< mpt::ToCharset(mpt::CharsetStdIO, message)
<< " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]"
<< std::endl;
#else // !MODPLUG_TRACKER
std::clog
<< "libopenmpt: "
<< mpt::ToCharset(mpt::CharsetStdIO, file) << "(" << mpt::ToCharset(mpt::CharsetStdIO, line) << ")" << ": "
<< mpt::ToCharset(mpt::CharsetStdIO, message)
<< " [" << mpt::ToCharset(mpt::CharsetStdIO, function) << "]"
<< std::endl;
#endif // MODPLUG_TRACKER
#endif // MPT_LOG_IS_DISABLED
}
#endif // !NO_LOGGING
#if defined(MODPLUG_TRACKER)
namespace Trace {
#if MPT_OS_WINDOWS
// Debugging functionality will use simple globals.
std::atomic<bool> g_Enabled = ATOMIC_VAR_INIT(false);
static bool g_Sealed = false;
struct Entry {
uint32 Index;
uint32 ThreadId;
uint64 Timestamp;
const char * Function;
const char * File;
int Line;
Direction Direction;
};
static MPT_FORCEINLINE bool operator < (const Entry &a, const Entry &b) noexcept
{
/*
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;
static uint32 ThreadIdWatchdir = 0;
void Enable(std::size_t numEntries)
{
if(g_Sealed)
{
return;
}
Entries.clear();
Entries.resize(numEntries);
NextIndex.store(0);
g_Enabled = (numEntries > 0);
}
void Disable()
{
if(g_Sealed)
{
return;
}
g_Enabled = false;
}
MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction) noexcept
{
// This will get called in realtime contexts and hot paths.
// No blocking allowed here.
const uint32 index = NextIndex.fetch_add(1);
const std::size_t numEntries = Entries.size();
#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 % numEntries];
entry.Index = index;
entry.ThreadId = threadid;
entry.Timestamp = timestamp;
entry.Function = loc.function_name();
entry.File = loc.file_name();
entry.Line = loc.line();
entry.Direction = direction;
}
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);
f << "Build: OpenMPT " << mpt::ToCharset(mpt::CharsetLogfile, Build::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::CharsetLogfile, mpt::Date::ANSI::ToUString(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(auto &entry : Entries)
{
if(!entry.Function) entry.Function = "";
if(!entry.File) entry.File = "";
std::string time;
if(qpcValid)
{
time = mpt::ToCharset(mpt::CharsetLogfile, mpt::Date::ANSI::ToUString( 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 if(entry.ThreadId == ThreadIdWatchdir)
{
f << " WatchDir ";
} else
{
f << " " << mpt::fmt::hex0<8>(entry.ThreadId) << " ";
}
f << (entry.Direction == mpt::log::Trace::Direction::Enter ? ">" : entry.Direction == mpt::log::Trace::Direction::Leave ? "<" : " ") << " ";
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;
case ThreadKindWatchdir:
ThreadIdWatchdir = id;
break;
}
}
uint32 GetThreadId(mpt::log::Trace::ThreadKind kind)
{
uint32 result = 0;
switch(kind)
{
case ThreadKindGUI:
result = ThreadIdGUI;
break;
case ThreadKindAudio:
result = ThreadIdAudio;
break;
case ThreadKindNotify:
result = ThreadIdNotify;
break;
case ThreadKindWatchdir:
result = ThreadIdWatchdir;
break;
}
return result;
}
#endif // MPT_OS_WINDOWS
} // namespace Trace
#endif // MODPLUG_TRACKER
} // namespace log
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,254 @@
/*
* 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
#include "BuildSettings.h"
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
#include <atomic>
#endif
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", U_("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(U_("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 U_("error"); break;
case LogWarning: return U_("warning"); break;
case LogNotification: return U_("notify"); break;
case LogInformation: return U_("info"); break;
case LogDebug: return U_("debug"); break;
}
return U_("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 constexpr 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
MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) { return true; }
#endif
#endif // !NO_LOGGING
#ifndef NO_LOGGING
class Logger
{
public:
// facility:ASCII
void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &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_SOURCE_LOCATION_CURRENT() , ( level ), ( facility ), ( text )); \
} \
} \
} MPT_WHILE_0 \
/**/
#else // !NO_LOGGING
#define MPT_LOG(level, facility, text) MPT_DO { } MPT_WHILE_0
#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 std::atomic<bool> g_Enabled;
inline bool IsEnabled() { return g_Enabled; }
enum class Direction : int8
{
Unknown = 0,
Enter = 1,
Leave = -1,
};
MPT_NOINLINE void Trace(const mpt::source_location & loc, Direction direction = Direction::Unknown) noexcept;
enum ThreadKind {
ThreadKindGUI,
ThreadKindAudio,
ThreadKindNotify,
ThreadKindWatchdir,
};
void Enable(std::size_t numEntries);
void Disable();
void SetThreadId(mpt::log::Trace::ThreadKind kind, uint32 id);
uint32 GetThreadId(mpt::log::Trace::ThreadKind kind);
void Seal();
bool Dump(const mpt::PathString &filename);
class Scope
{
private:
const mpt::source_location loc;
public:
MPT_FORCEINLINE Scope(mpt::source_location loc) noexcept
: loc(loc)
{
if(mpt::log::Trace::g_Enabled)
{
mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Enter);
}
}
MPT_FORCEINLINE ~Scope() noexcept
{
if(mpt::log::Trace::g_Enabled)
{
mpt::log::Trace::Trace(loc, mpt::log::Trace::Direction::Leave);
}
}
};
#define MPT_TRACE_CONCAT_HELPER(x, y) x ## y
#define MPT_TRACE_CONCAT(x, y) MPT_TRACE_CONCAT_HELPER(x, y)
#define MPT_TRACE_SCOPE() mpt::log::Trace::Scope MPT_TRACE_CONCAT(MPT_TRACE_VAR, __LINE__)(MPT_SOURCE_LOCATION_CURRENT())
#define MPT_TRACE() MPT_DO { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } MPT_WHILE_0
} // namespace Trace
#else // !MODPLUG_TRACKER
#define MPT_TRACE_SCOPE() MPT_DO { } MPT_WHILE_0
#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 constexpr 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::right(6, mpt::fmt::fix(stats.usage * 100.0, 3)) + "%\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,127 @@
/*
* 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 "BuildSettings.h"
#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, __func__)
#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,247 @@
/*
* misc_util.h
* -----------
* 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.
*/
#pragma once
#include "BuildSettings.h"
#include "mptAssert.h"
#include "mptBaseMacros.h"
#include "mptBaseTypes.h"
#include "mptBaseUtils.h"
#include "mptString.h"
// old
#include "mptBaseUtils.h"
#include "mptSpan.h"
#include "mptMemory.h"
#include "mptExceptionText.h"
#include "mptStringFormat.h"
#include "mptStringParse.h"
#include "mptCPU.h"
#include "mptOS.h"
#include "mptTime.h"
#include "mptLibrary.h"
#include <stdexcept>
#include <vector>
#include <cstdlib>
#include <stdlib.h>
OPENMPT_NAMESPACE_BEGIN
namespace Util
{
// Insert a range of items [insStart, insEnd], and possibly shift item fix to the left.
template<typename T>
void InsertItem(const T insStart, const T insEnd, T &fix)
{
MPT_ASSERT(insEnd >= insStart);
if(fix >= insStart)
{
fix += (insEnd - insStart + 1);
}
}
// Insert a range of items [insStart, insEnd], and possibly shift items in range [fixStart, fixEnd] to the right.
template<typename T>
void InsertRange(const T insStart, const T insEnd, T &fixStart, T &fixEnd)
{
MPT_ASSERT(insEnd >= insStart);
const T insLength = insEnd - insStart + 1;
if(fixStart >= insEnd)
{
fixStart += insLength;
}
if(fixEnd >= insEnd)
{
fixEnd += insLength;
}
}
// Delete a range of items [delStart, delEnd], and possibly shift item fix to the left.
template<typename T>
void DeleteItem(const T delStart, const T delEnd, T &fix)
{
MPT_ASSERT(delEnd >= delStart);
if(fix > delEnd)
{
fix -= (delEnd - delStart + 1);
}
}
// Delete a range of items [delStart, delEnd], and possibly shift items in range [fixStart, fixEnd] to the left.
template<typename T>
void DeleteRange(const T delStart, const T delEnd, T &fixStart, T &fixEnd)
{
MPT_ASSERT(delEnd >= delStart);
const T delLength = delEnd - delStart + 1;
if(delStart < fixStart && delEnd < fixStart)
{
// cut part is before loop start
fixStart -= delLength;
fixEnd -= delLength;
} else if(delStart < fixStart && delEnd < fixEnd)
{
// cut part is partly before loop start
fixStart = delStart;
fixEnd -= delLength;
} else if(delStart >= fixStart && delEnd < fixEnd)
{
// cut part is in the loop
fixEnd -= delLength;
} else if(delStart >= fixStart && delStart < fixEnd && delEnd > fixEnd)
{
// cut part is partly before loop end
fixEnd = delStart;
}
}
} // namespace Util
namespace Util
{
template<typename T, std::size_t n>
class fixed_size_queue
{
private:
T buffer[n+1];
std::size_t read_position;
std::size_t write_position;
public:
fixed_size_queue() : read_position(0), write_position(0)
{
return;
}
void clear()
{
read_position = 0;
write_position = 0;
}
std::size_t read_size() const
{
if ( write_position > read_position )
{
return write_position - read_position;
} else if ( write_position < read_position )
{
return write_position - read_position + n + 1;
} else
{
return 0;
}
}
std::size_t write_size() const
{
if ( write_position > read_position )
{
return read_position - write_position + n;
} else if ( write_position < read_position )
{
return read_position - write_position - 1;
} else
{
return n;
}
}
bool push( const T & v )
{
if ( !write_size() )
{
return false;
}
buffer[write_position] = v;
write_position = ( write_position + 1 ) % ( n + 1 );
return true;
}
bool pop() {
if ( !read_size() )
{
return false;
}
read_position = ( read_position + 1 ) % ( n + 1 );
return true;
}
T peek() {
if ( !read_size() )
{
return T();
}
return buffer[read_position];
}
const T * peek_p()
{
if ( !read_size() )
{
return nullptr;
}
return &(buffer[read_position]);
}
const T * peek_next_p()
{
if ( read_size() < 2 )
{
return nullptr;
}
return &(buffer[(read_position+1)%(n+1)]);
}
};
} // namespace Util
namespace Util
{
std::vector<std::byte> HexToBin(const mpt::ustring &src);
mpt::ustring BinToHex(mpt::const_byte_span src);
template <typename T> inline mpt::ustring BinToHex(mpt::span<T> src) { return Util::BinToHex(mpt::byte_cast<mpt::const_byte_span>(src)); }
} // namespace Util
#if defined(MODPLUG_TRACKER) || (defined(LIBOPENMPT_BUILD) && defined(LIBOPENMPT_BUILD_TEST))
namespace mpt
{
// Wrapper around std::getenv.
// Instead of returning null pointer if the environment variable is not set,
// this wrapper returns the provided default value.
std::string getenv(const std::string &env_var, const std::string &def = std::string());
} // namespace mpt
#endif // MODPLUG_TRACKER || (LIBOPENMPT_BUILD && LIBOPENMPT_BUILD_TEST)
#if MPT_OS_WINDOWS
template <typename Tstring, typename Tbuf, typename Tsize>
Tstring ParseMaybeNullTerminatedStringFromBufferWithSizeInBytes(const Tbuf *buf, Tsize sizeBytes)
{
// REG_SZ may contain a single NUL terminator, multiple NUL terminators, or no NUL terminator at all
return Tstring(reinterpret_cast<const typename Tstring::value_type*>(buf), reinterpret_cast<const typename Tstring::value_type*>(buf) + (sizeBytes / sizeof(typename Tstring::value_type))).c_str();
}
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,145 @@
/*
* mptAssert.h
* -----------
* Purpose: assert and static_assert
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "BuildSettings.h"
#include "mptBaseMacros.h"
OPENMPT_NAMESPACE_BEGIN
// 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
#if MPT_CHECKER_ASSUME_ASSERTIONS
#ifdef NDEBUG
#error "Builds for static analyzers depend on 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(MPT_WITH_MFC) && !defined(MPT_CPPCHECK_CUSTOM)
#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 // !MPT_WITH_MFC
#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 // MPT_WITH_MFC
#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(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #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(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #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 { if constexpr(!(0)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } MPT_WHILE_0
#define MPT_ASSERT(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS(expr) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } MPT_WHILE_0
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) MPT_DO { if(!(expr)) { AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #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 mpt::source_location &loc, const char *expr, const char *msg=nullptr);
#endif // MPT_ASSERT_HANDLER_NEEDED
#define MPT_CONSTEXPR11_ASSERT static_assert
#define MPT_CONSTEXPR14_ASSERT static_assert
#define MPT_CONSTEXPR17_ASSERT static_assert
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,272 @@
/*
* mptBaseMacros.h
* ---------------
* Purpose: Basic 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
#include "BuildSettings.h"
#include <array>
#include <iterator>
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <stddef.h>
#include <stdint.h>
OPENMPT_NAMESPACE_BEGIN
#define MPT_PP_DEFER(m, ...) m(__VA_ARGS__)
#define MPT_PP_STRINGIFY(x) #x
#define MPT_PP_JOIN_HELPER(a, b) a ## b
#define MPT_PP_JOIN(a, b) MPT_PP_JOIN_HELPER(a, b)
#define MPT_PP_UNIQUE_IDENTIFIER(prefix) MPT_PP_JOIN(prefix , __LINE__)
#if MPT_COMPILER_MSVC
#define MPT_WARNING(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
#define MPT_WARNING_STATEMENT(text) __pragma(message(__FILE__ "(" MPT_PP_DEFER(MPT_PP_STRINGIFY, __LINE__) "): Warning: " text))
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#define MPT_WARNING(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
#define MPT_WARNING_STATEMENT(text) _Pragma(MPT_PP_STRINGIFY(GCC warning text))
#else
// portable #pragma message or #warning replacement
#define MPT_WARNING(text) \
static inline int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) () noexcept { \
int warning [[deprecated("Warning: " text)]] = 0; \
return warning; \
} \
/**/
#define MPT_WARNING_STATEMENT(text) \
int MPT_PP_UNIQUE_IDENTIFIER(MPT_WARNING_NAME) = [](){ \
int warning [[deprecated("Warning: " text)]] = 0; \
return warning; \
}() \
/**/
#endif
// Advanced inline attributes
#if MPT_COMPILER_MSVC
#define MPT_FORCEINLINE __forceinline
#define MPT_NOINLINE __declspec(noinline)
#elif MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#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_CONSTEXPR14_FUN constexpr MPT_FORCEINLINE
#define MPT_CONSTEXPR17_FUN constexpr MPT_FORCEINLINE
#if MPT_CXX_AT_LEAST(20)
#define MPT_CONSTEXPR20_FUN constexpr MPT_FORCEINLINE
#define MPT_CONSTEXPR20_VAR constexpr
#else // !C++20
#define MPT_CONSTEXPR20_FUN MPT_FORCEINLINE
#define MPT_CONSTEXPR20_VAR const
#endif // C++20
namespace mpt
{
template <auto V> struct constant_value { static constexpr decltype(V) value() { return V; } };
#define MPT_FORCE_CONSTEXPR(expr) (mpt::constant_value<( expr )>::value())
} // namespace mpt
#if MPT_CXX_AT_LEAST(20)
#define MPT_IS_CONSTANT_EVALUATED20() std::is_constant_evaluated()
#define MPT_IS_CONSTANT_EVALUATED() std::is_constant_evaluated()
#else // !C++20
#define MPT_IS_CONSTANT_EVALUATED20() false
// this pessimizes the case for C++17 by always assuming constexpr context, which implies always running constexpr-friendly code
#define MPT_IS_CONSTANT_EVALUATED() true
#endif // C++20
namespace mpt
{
template <typename T>
struct stdarray_extent : std::integral_constant<std::size_t, 0> {};
template <typename T, std::size_t N>
struct stdarray_extent<std::array<T, N>> : std::integral_constant<std::size_t, N> {};
template <typename T>
struct is_stdarray : std::false_type {};
template <typename T, std::size_t N>
struct is_stdarray<std::array<T, N>> : std::true_type {};
// mpt::extent is the same as std::extent,
// but also works for std::array,
// and asserts that the given type is actually an array type instead of returning 0.
// use as:
// mpt::extent<decltype(expr)>()
// mpt::extent<decltype(variable)>()
// mpt::extent<decltype(type)>()
// mpt::extent<type>()
template <typename T>
constexpr std::size_t extent() noexcept
{
using Tarray = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
static_assert(std::is_array<Tarray>::value || mpt::is_stdarray<Tarray>::value);
if constexpr(mpt::is_stdarray<Tarray>::value)
{
return mpt::stdarray_extent<Tarray>();
} else
{
return std::extent<Tarray>();
}
}
} // namespace mpt
// legacy
#if MPT_COMPILER_MSVC
OPENMPT_NAMESPACE_END
#include <cstdlib>
#include <stdlib.h>
OPENMPT_NAMESPACE_BEGIN
#define MPT_ARRAY_COUNT(x) _countof(x)
#else
#define MPT_ARRAY_COUNT(x) (sizeof((x))/sizeof((x)[0]))
#endif
#define CountOf(x) MPT_ARRAY_COUNT(x)
// Use MPT_RESTRICT to indicate that a pointer is guaranteed to not be aliased.
#if MPT_COMPILER_MSVC || MPT_COMPILER_GCC || MPT_COMPILER_CLANG
#define MPT_RESTRICT __restrict
#else
#define MPT_RESTRICT
#endif
#define MPT_ATTR_NODISCARD [[nodiscard]]
#define MPT_DISCARD(expr) static_cast<void>(expr)
#if MPT_COMPILER_MSVC
#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
#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_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)
#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
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,290 @@
/*
* mptBaseTypes.h
* --------------
* Purpose: Basic data type definitions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "BuildSettings.h"
#include "mptBaseMacros.h"
#include <array>
#include <limits>
#if MPT_CXX_AT_LEAST(20)
#include <source_location>
#endif // C++20
#include <cstddef>
#include <cstdint>
#include <stdint.h>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
template <bool cond, typename Ta, typename Tb>
struct select_type
{
};
template <typename Ta, typename Tb>
struct select_type<true, Ta, Tb>
{
using type = Ta;
};
template <typename Ta, typename Tb>
struct select_type<false, Ta, Tb>
{
using type = Tb;
};
} // namespace mpt
using int8 = std::int8_t;
using int16 = std::int16_t;
using int32 = std::int32_t;
using int64 = std::int64_t;
using uint8 = std::uint8_t;
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
using uint64 = std::uint64_t;
constexpr int8 int8_min = std::numeric_limits<int8>::min();
constexpr int16 int16_min = std::numeric_limits<int16>::min();
constexpr int32 int32_min = std::numeric_limits<int32>::min();
constexpr int64 int64_min = std::numeric_limits<int64>::min();
constexpr int8 int8_max = std::numeric_limits<int8>::max();
constexpr int16 int16_max = std::numeric_limits<int16>::max();
constexpr int32 int32_max = std::numeric_limits<int32>::max();
constexpr int64 int64_max = std::numeric_limits<int64>::max();
constexpr uint8 uint8_max = std::numeric_limits<uint8>::max();
constexpr uint16 uint16_max = std::numeric_limits<uint16>::max();
constexpr uint32 uint32_max = std::numeric_limits<uint32>::max();
constexpr uint64 uint64_max = std::numeric_limits<uint64>::max();
// fp half
// n/a
// fp single
using single = float;
constexpr single operator"" _fs(long double lit)
{
return static_cast<single>(lit);
}
// fp double
constexpr double operator"" _fd(long double lit)
{
return static_cast<double>(lit);
}
// fp extended
constexpr long double operator"" _fe(long double lit)
{
return static_cast<long double>(lit);
}
// fp quad
// n/a
using float32 = mpt::select_type<sizeof(float) == 4,
float
,
mpt::select_type<sizeof(double) == 4,
double
,
mpt::select_type<sizeof(long double) == 4,
long double
,
float
>::type
>::type
>::type;
constexpr float32 operator"" _f32(long double lit)
{
return static_cast<float32>(lit);
}
using float64 = mpt::select_type<sizeof(float) == 8,
float
,
mpt::select_type<sizeof(double) == 8,
double
,
mpt::select_type<sizeof(long double) == 8,
long double
,
double
>::type
>::type
>::type;
constexpr float64 operator"" _f64(long double lit)
{
return static_cast<float64>(lit);
}
namespace mpt
{
template <typename T>
struct float_traits
{
static constexpr bool is_float = !std::numeric_limits<T>::is_integer;
static constexpr bool is_hard = is_float && !MPT_COMPILER_QUIRK_FLOAT_EMULATED;
static constexpr bool is_soft = is_float && MPT_COMPILER_QUIRK_FLOAT_EMULATED;
static constexpr bool is_float32 = is_float && (sizeof(T) == 4);
static constexpr bool is_float64 = is_float && (sizeof(T) == 8);
static constexpr bool is_native_endian = is_float && !MPT_COMPILER_QUIRK_FLOAT_NOTNATIVEENDIAN;
static constexpr bool is_ieee754_binary = is_float && std::numeric_limits<T>::is_iec559 && !MPT_COMPILER_QUIRK_FLOAT_NOTIEEE754;
static constexpr bool is_ieee754_binary32 = is_float && is_ieee754_binary && is_float32;
static constexpr bool is_ieee754_binary64 = is_float && is_ieee754_binary && is_float64;
static constexpr bool is_ieee754_binary32ne = is_float && is_ieee754_binary && is_float32 && is_native_endian;
static constexpr bool is_ieee754_binary64ne = is_float && is_ieee754_binary && is_float64 && is_native_endian;
};
} // namespace mpt
#if MPT_COMPILER_QUIRK_FLOAT_PREFER32
using nativefloat = float32;
#elif MPT_COMPILER_QUIRK_FLOAT_PREFER64
using nativefloat = float64;
#else
// prefer smaller floats, but try to use IEEE754 floats
using nativefloat = mpt::select_type<std::numeric_limits<float>::is_iec559,
float
,
mpt::select_type<std::numeric_limits<double>::is_iec559,
double
,
mpt::select_type<std::numeric_limits<long double>::is_iec559,
long double
,
float
>::type
>::type
>::type;
#endif
constexpr nativefloat operator"" _nf(long double lit)
{
return static_cast<nativefloat>(lit);
}
static_assert(sizeof(std::uintptr_t) == sizeof(void*));
static_assert(std::numeric_limits<unsigned char>::digits == 8);
static_assert(sizeof(char) == 1);
static_assert(sizeof(std::byte) == 1);
static_assert(alignof(std::byte) == 1);
namespace mpt {
inline constexpr int arch_bits = sizeof(void*) * 8;
inline constexpr std::size_t pointer_size = sizeof(void*);
} // namespace mpt
static_assert(mpt::arch_bits == static_cast<int>(mpt::pointer_size) * 8);
namespace mpt {
template <typename T>
struct limits
{
static constexpr typename std::remove_cv<T>::type min() noexcept { return std::numeric_limits<typename std::remove_cv<T>::type>::min(); }
static constexpr typename std::remove_cv<T>::type max() noexcept { return std::numeric_limits<typename std::remove_cv<T>::type>::max(); }
};
} // namespace mpt
namespace mpt
{
#if MPT_CXX_AT_LEAST(20)
using std::source_location;
#define MPT_SOURCE_LOCATION_CURRENT() std::source_location::current()
#else // !C++20
// compatible with std::experimental::source_location from Library Fundamentals TS v2.
struct source_location
{
private:
const char* m_file_name;
const char* m_function_name;
uint32 m_line;
uint32 m_column;
public:
constexpr source_location() noexcept
: m_file_name("")
, m_function_name("")
, m_line(0)
, m_column(0)
{
}
constexpr source_location(const char* file, const char* function, uint32 line, uint32 column) noexcept
: m_file_name(file)
, m_function_name(function)
, m_line(line)
, m_column(column)
{
}
source_location(const source_location&) = default;
source_location(source_location&&) = default;
//static constexpr current() noexcept; // use MPT_SOURCE_LOCATION_CURRENT()
static constexpr source_location current(const char* file, const char* function, uint32 line, uint32 column) noexcept
{
return source_location(file, function, line, column);
}
constexpr uint32 line() const noexcept
{
return m_line;
}
constexpr uint32 column() const noexcept
{
return m_column;
}
constexpr const char* file_name() const noexcept
{
return m_file_name;
}
constexpr const char* function_name() const noexcept
{
return m_function_name;
}
};
#define MPT_SOURCE_LOCATION_CURRENT() mpt::source_location::current( __FILE__ , __func__ , __LINE__ , 0 )
#endif // C++20
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,680 @@
/*
* mptBaseUtils.h
* --------------
* 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.
*/
#pragma once
#include "BuildSettings.h"
#include "mptBaseMacros.h"
#include "mptBaseTypes.h"
#include <algorithm>
#if MPT_CXX_AT_LEAST(20)
#include <bit>
#endif
#include <limits>
#include <numeric>
#include <utility>
#include <cmath>
#include <cstdlib>
#include <math.h>
#include <stdlib.h>
#if MPT_COMPILER_MSVC
#include <intrin.h>
#endif
OPENMPT_NAMESPACE_BEGIN
// cmath fixups
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923
#endif
#ifndef M_LN2
#define M_LN2 0.69314718055994530942
#endif
namespace mpt
{
template <typename T, std::size_t N, typename Tx>
MPT_CONSTEXPR14_FUN std::array<T, N> init_array(const Tx & x)
{
std::array<T, N> result{};
for(std::size_t i = 0; i < N; ++i)
{
result[i] = x;
}
return result;
}
} // namespace mpt
namespace mpt
{
// Work-around for the requirement of at least 1 non-throwing function argument combination in C++ (17,2a).
template <typename Exception>
MPT_CONSTEXPR14_FUN bool constexpr_throw_helper(Exception && e, bool really = true)
{
//return !really ? really : throw std::forward<Exception>(e);
if(really)
{
throw std::forward<Exception>(e);
}
// cppcheck-suppress identicalConditionAfterEarlyExit
return really;
}
template <typename Exception>
MPT_CONSTEXPR14_FUN bool constexpr_throw(Exception && e)
{
return mpt::constexpr_throw_helper(std::forward<Exception>(e));
}
template <typename T, typename Exception>
constexpr T constexpr_throw_helper(Exception && e, bool really = true)
{
//return !really ? really : throw std::forward<Exception>(e);
if(really)
{
throw std::forward<Exception>(e);
}
return T{};
}
template <typename T, typename Exception>
constexpr T constexpr_throw(Exception && e)
{
return mpt::constexpr_throw_helper<T>(std::forward<Exception>(e));
}
} // namespace mpt
namespace mpt {
// Modulo with more intuitive behaviour for some contexts:
// Instead of being symmetrical around 0, the pattern for positive numbers is repeated in the negative range.
// For example, wrapping_modulo(-1, m) == (m - 1).
// Behaviour is undefined if m<=0.
template<typename T, typename M>
MPT_CONSTEXPR11_FUN auto wrapping_modulo(T x, M m) -> decltype(x % m)
{
return (x >= 0) ? (x % m) : (m - 1 - ((-1 - x) % m));
}
template<typename T, typename D>
MPT_CONSTEXPR11_FUN auto wrapping_divide(T x, D d) -> decltype(x / d)
{
return (x >= 0) ? (x / d) : (((x + 1) / d) - 1);
}
} // namespace mpt
namespace mpt {
// Saturate the value of src to the domain of Tdst
template <typename Tdst, typename Tsrc>
inline Tdst saturate_cast(Tsrc src)
{
// This code tries not only to obviously avoid overflows but also to avoid signed/unsigned comparison warnings and type truncation warnings (which in fact would be safe here) by explicit casting.
static_assert(std::numeric_limits<Tdst>::is_integer);
static_assert(std::numeric_limits<Tsrc>::is_integer);
if constexpr(std::numeric_limits<Tdst>::is_signed && std::numeric_limits<Tsrc>::is_signed)
{
if constexpr(sizeof(Tdst) >= sizeof(Tsrc))
{
return static_cast<Tdst>(src);
} else
{
return static_cast<Tdst>(std::max(static_cast<Tsrc>(std::numeric_limits<Tdst>::min()), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
}
} else if constexpr(!std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed)
{
if constexpr(sizeof(Tdst) >= sizeof(Tsrc))
{
return static_cast<Tdst>(src);
} else
{
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
}
} else if constexpr(std::numeric_limits<Tdst>::is_signed && !std::numeric_limits<Tsrc>::is_signed)
{
if constexpr(sizeof(Tdst) > sizeof(Tsrc))
{
return static_cast<Tdst>(src);
} else if constexpr(sizeof(Tdst) == sizeof(Tsrc))
{
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
} else
{
return static_cast<Tdst>(std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max())));
}
} else // Tdst unsigned, Tsrc signed
{
if constexpr(sizeof(Tdst) >= sizeof(Tsrc))
{
return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), src));
} else
{
return static_cast<Tdst>(std::max(static_cast<Tsrc>(0), std::min(src, static_cast<Tsrc>(std::numeric_limits<Tdst>::max()))));
}
}
}
template <typename Tdst>
inline Tdst saturate_cast(double src)
{
if(src >= static_cast<double>(std::numeric_limits<Tdst>::max()))
{
return std::numeric_limits<Tdst>::max();
}
if(src <= static_cast<double>(std::numeric_limits<Tdst>::min()))
{
return std::numeric_limits<Tdst>::min();
}
return static_cast<Tdst>(src);
}
template <typename Tdst>
inline Tdst saturate_cast(float src)
{
if(src >= static_cast<float>(std::numeric_limits<Tdst>::max()))
{
return std::numeric_limits<Tdst>::max();
}
if(src <= static_cast<float>(std::numeric_limits<Tdst>::min()))
{
return std::numeric_limits<Tdst>::min();
}
return static_cast<Tdst>(src);
}
#if MPT_CXX_AT_LEAST(20)
using std::popcount;
using std::has_single_bit;
using std::bit_ceil;
using std::bit_floor;
using std::bit_width;
using std::rotl;
using std::rotr;
#else
// C++20 <bit> header.
// Note that we do not use SFINAE here but instead rely on static_assert.
template <typename T>
MPT_CONSTEXPR14_FUN int popcount(T val) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
int result = 0;
while(val > 0)
{
if(val & 0x1)
{
result++;
}
val >>= 1;
}
return result;
}
template <typename T>
MPT_CONSTEXPR14_FUN bool has_single_bit(T x) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
return mpt::popcount(x) == 1;
}
template <typename T>
MPT_CONSTEXPR14_FUN T bit_ceil(T x) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
T result = 1;
while(result < x)
{
T newresult = result << 1;
if(newresult < result)
{
return 0;
}
result = newresult;
}
return result;
}
template <typename T>
MPT_CONSTEXPR14_FUN T bit_floor(T x) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
if(x == 0)
{
return 0;
}
T result = 1;
do
{
T newresult = result << 1;
if(newresult < result)
{
return result;
}
result = newresult;
} while(result <= x);
return result >> 1;
}
template <typename T>
MPT_CONSTEXPR14_FUN T bit_width(T x) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
T result = 0;
while(x > 0)
{
x >>= 1;
result += 1;
}
return result;
}
namespace detail
{
template <typename T>
MPT_CONSTEXPR14_FUN T rotl(T x, int r) noexcept
{
auto N = std::numeric_limits<T>::digits;
return (x >> (N - r)) | (x << r);
}
template <typename T>
MPT_CONSTEXPR14_FUN T rotr(T x, int r) noexcept
{
auto N = std::numeric_limits<T>::digits;
return (x << (N - r)) | (x >> r);
}
} // namespace detail
template <typename T>
MPT_CONSTEXPR14_FUN T rotl(T x, int s) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
auto N = std::numeric_limits<T>::digits;
auto r = s % N;
return (s < 0) ? detail::rotr(x, -s) : ((x >> (N - r)) | (x << r));
}
template <typename T>
MPT_CONSTEXPR14_FUN T rotr(T x, int s) noexcept
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::is_unsigned<T>::value);
auto N = std::numeric_limits<T>::digits;
auto r = s % N;
return (s < 0) ? detail::rotl(x, -s) : ((x << (N - r)) | (x >> r));
}
#endif
} // namespace mpt
namespace Util
{
namespace detail
{
template <typename Tmod, Tmod m>
struct ModIfNotZeroImpl
{
template <typename Tval>
inline Tval mod(Tval x)
{
static_assert(std::numeric_limits<Tmod>::is_integer);
static_assert(!std::numeric_limits<Tmod>::is_signed);
static_assert(std::numeric_limits<Tval>::is_integer);
static_assert(!std::numeric_limits<Tval>::is_signed);
return static_cast<Tval>(x % m);
}
};
template <> struct ModIfNotZeroImpl<uint8 , 0> { template <typename Tval> inline Tval mod(Tval x) { return x; } };
template <> struct ModIfNotZeroImpl<uint16, 0> { template <typename Tval> inline Tval mod(Tval x) { return x; } };
template <> struct ModIfNotZeroImpl<uint32, 0> { template <typename Tval> inline Tval mod(Tval x) { return x; } };
template <> struct ModIfNotZeroImpl<uint64, 0> { template <typename Tval> inline Tval mod(Tval x) { return x; } };
} // namespace detail
// Returns x % m if m != 0, x otherwise.
// i.e. "return (m == 0) ? x : (x % m);", but without causing a warning with stupid older compilers
template <typename Tmod, Tmod m, typename Tval>
inline Tval ModIfNotZero(Tval x)
{
return detail::ModIfNotZeroImpl<Tmod, m>().mod(x);
}
// Returns true iff Tdst can represent the value val.
// Use as if(Util::TypeCanHoldValue<uint8>(-1)).
template <typename Tdst, typename Tsrc>
inline bool TypeCanHoldValue(Tsrc val)
{
return (static_cast<Tsrc>(mpt::saturate_cast<Tdst>(val)) == val);
}
// Grows x with an exponential factor suitable for increasing buffer sizes.
// Clamps the result at limit.
// And avoids integer overflows while doing its business.
// The growth factor is 1.5, rounding down, execpt for the initial x==1 case.
template <typename T, typename Tlimit>
inline T ExponentialGrow(const T &x, const Tlimit &limit)
{
MPT_ASSERT(x > 0);
MPT_ASSERT(limit > 0);
if(x == 1)
{
return 2;
}
T add = std::min(x >> 1, std::numeric_limits<T>::max() - x);
return std::min(x + add, mpt::saturate_cast<T>(limit));
}
template <typename T>
inline T ExponentialGrow(const T &x)
{
return Util::ExponentialGrow(x, std::numeric_limits<T>::max());
}
} //namespace Util
// Limits 'val' to given range. If 'val' is less than 'lowerLimit', 'val' is set to value 'lowerLimit'.
// Similarly if 'val' is greater than 'upperLimit', 'val' is set to value 'upperLimit'.
// If 'lowerLimit' > 'upperLimit', 'val' won't be modified.
template<class T, class C>
inline void Limit(T& val, const C lowerLimit, const C upperLimit)
{
if(lowerLimit > upperLimit) return;
if(val < lowerLimit) val = lowerLimit;
else if(val > upperLimit) val = upperLimit;
}
// Like Limit, but returns value
template<class T, class C>
inline T Clamp(T val, const C lowerLimit, const C upperLimit)
{
if(val < lowerLimit) return lowerLimit;
else if(val > upperLimit) return upperLimit;
else return val;
}
// Check if val is in [lo,hi] without causing compiler warnings
// if theses checks are always true due to the domain of T.
// GCC does not warn if the type is templated.
template<typename T, typename C>
inline bool IsInRange(T val, C lo, C hi)
{
return lo <= val && val <= hi;
}
// Like Limit, but with upperlimit only.
template<class T, class C>
inline void LimitMax(T& val, const C upperLimit)
{
if(val > upperLimit)
val = upperLimit;
}
// Returns sign of a number (-1 for negative numbers, 1 for positive numbers, 0 for 0)
template <class T>
int sgn(T value)
{
return (value > T(0)) - (value < T(0));
}
// mpt::rshift_signed
// mpt::lshift_signed
// Shift a signed integer value in a well-defined manner.
// Does the same thing as MSVC would do. This is verified by the test suite.
namespace mpt
{
template <typename T>
MPT_FORCEINLINE auto rshift_signed_standard(T x, int y) -> decltype(x >> y)
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
typedef decltype(x >> y) result_type;
typedef typename std::make_unsigned<result_type>::type unsigned_result_type;
const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
result_type rx = x;
unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
urx += roffset;
urx >>= y;
urx -= roffset >> y;
return static_cast<result_type>(urx);
}
template <typename T>
MPT_FORCEINLINE auto lshift_signed_standard(T x, int y) -> decltype(x << y)
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
typedef decltype(x << y) result_type;
typedef typename std::make_unsigned<result_type>::type unsigned_result_type;
const unsigned_result_type roffset = static_cast<unsigned_result_type>(1) << ((sizeof(result_type) * 8) - 1);
result_type rx = x;
unsigned_result_type urx = static_cast<unsigned_result_type>(rx);
urx += roffset;
urx <<= y;
urx -= roffset << y;
return static_cast<result_type>(urx);
}
#if MPT_COMPILER_SHIFT_SIGNED
template <typename T>
MPT_FORCEINLINE auto rshift_signed_undefined(T x, int y) -> decltype(x >> y)
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
return x >> y;
}
template <typename T>
MPT_FORCEINLINE auto lshift_signed_undefined(T x, int y) -> decltype(x << y)
{
static_assert(std::numeric_limits<T>::is_integer);
static_assert(std::numeric_limits<T>::is_signed);
return x << y;
}
template <typename T>
MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y)
{
return mpt::rshift_signed_undefined(x, y);
}
template <typename T>
MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y)
{
return mpt::lshift_signed_undefined(x, y);
}
#else
template <typename T>
MPT_FORCEINLINE auto rshift_signed(T x, int y) -> decltype(x >> y)
{
return mpt::rshift_signed_standard(x, y);
}
template <typename T>
MPT_FORCEINLINE auto lshift_signed(T x, int y) -> decltype(x << y)
{
return mpt::lshift_signed_standard(x, y);
}
#endif
template<typename>
struct array_size;
template <typename T, std::size_t N>
struct array_size<std::array<T, N>>
{
static constexpr std::size_t size = N;
};
template <typename T, std::size_t N>
struct array_size<T[N]>
{
static constexpr std::size_t size = N;
};
} // namespace mpt
namespace Util
{
// Returns maximum value of given integer type.
template <class T> constexpr T MaxValueOfType(const T&) {static_assert(std::numeric_limits<T>::is_integer == true, "Only integer types are allowed."); return (std::numeric_limits<T>::max)();}
} // namespace Util
namespace mpt
{
#if MPT_OS_DJGPP
inline double round(const double& val) { return ::round(val); }
inline float round(const float& val) { return ::roundf(val); }
#else // !MPT_OS_DJGPP
// C++11 std::round
using std::round;
#endif // MPT_OS_DJGPP
// Rounds given double value to nearest integer value of type T.
// Out-of-range values are saturated to the specified integer type's limits.
template <class T> inline T saturate_round(double val)
{
static_assert(std::numeric_limits<T>::is_integer == true, "Type is a not an integer");
return mpt::saturate_cast<T>(mpt::round(val));
}
template <class T> inline T saturate_round(float val)
{
static_assert(std::numeric_limits<T>::is_integer == true, "Type is a not an integer");
return mpt::saturate_cast<T>(mpt::round(val));
}
}
namespace Util {
// Multiply two 32-bit integers, receive 64-bit result.
// MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul.
MPT_FORCEINLINE int64 mul32to64(int32 a, int32 b)
{
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
return __emul(a, b);
#else
return static_cast<int64>(a) * b;
#endif
}
MPT_FORCEINLINE uint64 mul32to64_unsigned(uint32 a, uint32 b)
{
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
return __emulu(a, b);
#else
return static_cast<uint64>(a) * b;
#endif
}
MPT_FORCEINLINE int32 muldiv(int32 a, int32 b, int32 c)
{
return mpt::saturate_cast<int32>( mul32to64( a, b ) / c );
}
MPT_FORCEINLINE int32 muldivr(int32 a, int32 b, int32 c)
{
return mpt::saturate_cast<int32>( ( mul32to64( a, b ) + ( c / 2 ) ) / c );
}
// Do not use overloading because catching unsigned version by accident results in slower X86 code.
MPT_FORCEINLINE uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c)
{
return mpt::saturate_cast<uint32>( mul32to64_unsigned( a, b ) / c );
}
MPT_FORCEINLINE uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c)
{
return mpt::saturate_cast<uint32>( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c );
}
MPT_FORCEINLINE int32 muldivrfloor(int64 a, uint32 b, uint32 c)
{
a *= b;
a += c / 2u;
return (a >= 0) ? mpt::saturate_cast<int32>(a / c) : mpt::saturate_cast<int32>((a - (c - 1)) / c);
}
// rounds x up to multiples of target
template <typename T>
inline T AlignUp(T x, T target)
{
return ((x + (target - 1)) / target) * target;
}
// rounds x down to multiples of target
template <typename T>
inline T AlignDown(T x, T target)
{
return (x / target) * target;
}
} // namespace Util
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,449 @@
/*
* 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
#if defined(MPT_ENABLE_FILEIO)
#if MPT_COMPILER_MSVC
#include <tchar.h>
#endif // MPT_COMPILER_MSVC
#endif // MPT_ENABLE_FILEIO
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 != 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);
}
bool SetFilesystemCompression(const mpt::PathString &filename)
{
DWORD attributes = GetFileAttributes(filename.AsNativePrefixed().c_str());
if(attributes == INVALID_FILE_ATTRIBUTES)
{
return false;
}
if(attributes & FILE_ATTRIBUTE_COMPRESSED)
{
return true;
}
HANDLE hFile = CreateFile(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);
return result;
}
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#ifdef MODPLUG_TRACKER
namespace mpt {
#if MPT_COMPILER_MSVC
mpt::tstring SafeOutputFile::convert_mode(std::ios_base::openmode mode, FlushMode flushMode)
{
mpt::tstring fopen_mode;
switch(mode & ~(std::ios_base::ate | std::ios_base::binary))
{
case std::ios_base::in:
fopen_mode = _T("r");
break;
case std::ios_base::out:
[[fallthrough]];
case std::ios_base::out | std::ios_base::trunc:
fopen_mode = _T("w");
break;
case std::ios_base::app:
[[fallthrough]];
case std::ios_base::out | std::ios_base::app:
fopen_mode = _T("a");
break;
case std::ios_base::out | std::ios_base::in:
fopen_mode = _T("r+");
break;
case std::ios_base::out | std::ios_base::in | std::ios_base::trunc:
fopen_mode = _T("w+");
break;
case std::ios_base::out | std::ios_base::in | std::ios_base::app:
[[fallthrough]];
case std::ios_base::in | std::ios_base::app:
fopen_mode = _T("a+");
break;
}
if(fopen_mode.empty())
{
return fopen_mode;
}
if(mode & std::ios_base::binary)
{
fopen_mode += _T("b");
}
if(flushMode == FlushMode::Full)
{
fopen_mode += _T("c"); // force commit on fflush (MSVC specific)
}
return fopen_mode;
}
FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode)
{
mpt::tstring fopen_mode = convert_mode(mode, flushMode);
if(fopen_mode.empty())
{
return nullptr;
}
FILE *f =
#ifdef UNICODE
_wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str())
#else
fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str())
#endif
;
if(!f)
{
return nullptr;
}
if(mode & std::ios_base::ate)
{
if(fseek(f, 0, SEEK_END) != 0)
{
fclose(f);
f = nullptr;
return nullptr;
}
}
m_f = f;
return f;
}
#endif // MPT_COMPILER_MSVC
// cppcheck-suppress exceptThrowInDestructor
SafeOutputFile::~SafeOutputFile() noexcept(false)
{
const bool mayThrow = (std::uncaught_exceptions() == 0);
if(!stream())
{
#if MPT_COMPILER_MSVC
if(m_f)
{
fclose(m_f);
}
#endif // MPT_COMPILER_MSVC
return;
}
if(!stream().rdbuf())
{
#if MPT_COMPILER_MSVC
if(m_f)
{
fclose(m_f);
}
#endif // MPT_COMPILER_MSVC
return;
}
#if MPT_COMPILER_MSVC
if(!m_f)
{
return;
}
#endif // MPT_COMPILER_MSVC
bool errorOnFlush = false;
if(m_FlushMode != FlushMode::None)
{
try
{
if(stream().rdbuf()->pubsync() != 0)
{
errorOnFlush = true;
}
} catch(const std::exception &)
{
errorOnFlush = true;
#if MPT_COMPILER_MSVC
if(m_FlushMode != FlushMode::None)
{
if(fflush(m_f) != 0)
{
errorOnFlush = true;
}
}
if(fclose(m_f) != 0)
{
errorOnFlush = true;
}
#endif // MPT_COMPILER_MSVC
if(mayThrow)
{
// ignore errorOnFlush here, and re-throw the earlier exception
// cppcheck-suppress exceptThrowInDestructor
throw;
}
}
}
#if MPT_COMPILER_MSVC
if(m_FlushMode != FlushMode::None)
{
if(fflush(m_f) != 0)
{
errorOnFlush = true;
}
}
if(fclose(m_f) != 0)
{
errorOnFlush = true;
}
#endif // MPT_COMPILER_MSVC
if(mayThrow && errorOnFlush && (stream().exceptions() & (std::ios::badbit | std::ios::failbit)))
{
// cppcheck-suppress exceptThrowInDestructor
throw std::ios_base::failure(std::string("Error flushing file buffers."));
}
}
} // namespace mpt
#endif // MODPLUG_TRACKER
#ifdef MODPLUG_TRACKER
namespace mpt {
LazyFileRef & LazyFileRef::operator = (const std::vector<std::byte> &data)
{
mpt::ofstream file(m_Filename, std::ios::binary);
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::WriteRaw(file, mpt::as_span(data));
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, mpt::as_span(data));
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, mpt::as_span(data));
mpt::IO::Flush(file);
return *this;
}
LazyFileRef::operator std::vector<std::byte> () const
{
mpt::ifstream file(m_Filename, std::ios::binary);
if(!mpt::IO::IsValid(file))
{
return std::vector<std::byte>();
}
file.exceptions(std::ios_base::failbit | std::ios_base::badbit);
mpt::IO::SeekEnd(file);
std::vector<std::byte> buf(mpt::saturate_cast<std::size_t>(mpt::IO::TellRead(file)));
mpt::IO::SeekBegin(file);
mpt::IO::ReadRaw(file, mpt::as_span(buf));
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, mpt::as_span(buf));
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, mpt::as_span(buf));
return std::string(buf.begin(), buf.end());
}
} // namespace mpt
#endif // MODPLUG_TRACKER
bool InputFile::DefaultToLargeAddressSpaceUsage()
{
return false;
}
InputFile::InputFile()
: m_IsCached(false)
{
return;
}
InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching)
: m_IsCached(false)
{
Open(filename, allowWholeFileCaching);
}
InputFile::~InputFile()
{
return;
}
bool InputFile::Open(const mpt::PathString &filename, bool allowWholeFileCaching)
{
m_IsCached = false;
m_Cache.resize(0);
m_Cache.shrink_to_fit();
m_Filename = filename;
m_File.open(m_Filename, std::ios::binary | std::ios::in);
if(allowWholeFileCaching)
{
if(mpt::IO::IsReadSeekable(m_File))
{
if(!mpt::IO::SeekEnd(m_File))
{
m_File.close();
return false;
}
mpt::IO::Offset filesize = mpt::IO::TellRead(m_File);
if(!mpt::IO::SeekBegin(m_File))
{
m_File.close();
return false;
}
if(Util::TypeCanHoldValue<std::size_t>(filesize))
{
std::size_t buffersize = mpt::saturate_cast<std::size_t>(filesize);
m_Cache.resize(buffersize);
if(mpt::IO::ReadRaw(m_File, mpt::as_span(m_Cache)) != filesize)
{
m_File.close();
return false;
}
if(!mpt::IO::SeekBegin(m_File))
{
m_File.close();
return false;
}
m_IsCached = true;
return true;
}
}
}
return m_File.good();
}
bool InputFile::IsValid() const
{
return m_File.good();
}
bool InputFile::IsCached() const
{
return m_IsCached;
}
const mpt::PathString& InputFile::GetFilenameRef() const
{
return m_Filename;
}
std::istream* InputFile::GetStream()
{
MPT_ASSERT(!m_IsCached);
return &m_File;
}
mpt::const_byte_span InputFile::GetCache()
{
MPT_ASSERT(m_IsCached);
return mpt::as_span(m_Cache);
}
#else // !MPT_ENABLE_FILEIO
MPT_MSVC_WORKAROUND_LNK4221(mptFileIO)
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,291 @@
/*
* mptFileIO.h
* -----------
* Purpose: A wrapper around std::fstream, 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
#include "BuildSettings.h"
#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>
#if MPT_COMPILER_MSVC
#include <cstdio>
#endif // !MPT_COMPILER_MSVC
#if MPT_COMPILER_MSVC
#include <stdio.h>
#endif // !MPT_COMPILER_MSVC
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_FILEIO)
// 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);
bool SetFilesystemCompression(const mpt::PathString &filename);
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
namespace mpt
{
#if MPT_COMPILER_GCC && MPT_OS_WINDOWS
// GCC C++ library has no wchar_t overloads
#define MPT_FSTREAM_DO_CONVERSIONS_ANSI
#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::Charset::Locale, filename.AsNative()).c_str(), mode);
#else
base.open(filename.AsNativePrefixed().c_str(), mode);
#endif
}
} // namespace detail
class SafeOutputFile;
class fstream
: public std::fstream
{
private:
typedef std::fstream Tbase;
public:
friend SafeOutputFile;
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);
}
void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete;
void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete;
#if MPT_OS_WINDOWS
void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete;
void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) = delete;
#endif
};
class ifstream
: public std::ifstream
{
private:
typedef std::ifstream Tbase;
public:
friend SafeOutputFile;
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);
}
void open(const char * filename, std::ios_base::openmode mode = std::ios_base::in) = delete;
void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::in) = delete;
#if MPT_OS_WINDOWS
void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::in) = delete;
void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::in) = delete;
#endif
};
class ofstream
: public std::ofstream
{
private:
typedef std::ofstream Tbase;
public:
friend SafeOutputFile;
public:
ofstream() {}
ofstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
#if MPT_COMPILER_MSVC
protected:
ofstream(FILE * file)
: std::ofstream(file)
{
}
#endif // MPT_COMPILER_MSVC
public:
void open(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
void open(const char * filename, std::ios_base::openmode mode = std::ios_base::out) = delete;
void open(const std::string & filename, std::ios_base::openmode mode = std::ios_base::out) = delete;
#if MPT_OS_WINDOWS
void open(const wchar_t * filename, std::ios_base::openmode mode = std::ios_base::out) = delete;
void open(const std::wstring & filename, std::ios_base::openmode mode = std::ios_base::out) = delete;
#endif
};
enum class FlushMode
{
None = 0, // no explicit flushes at all
Single = 1, // explicitly flush higher-leverl API layers
Full = 2, // explicitly flush *all* layers, up to and including disk write caches
};
inline FlushMode FlushModeFromBool(bool flush)
{
return flush ? FlushMode::Full : FlushMode::None;
}
#ifdef MODPLUG_TRACKER
class SafeOutputFile
{
private:
FlushMode m_FlushMode;
#if MPT_COMPILER_MSVC
FILE *m_f = nullptr;
#endif // MPT_COMPILER_MSVC
mpt::ofstream m_s;
#if MPT_COMPILER_MSVC
static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode);
FILE * internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode);
#endif // MPT_COMPILER_MSVC
public:
SafeOutputFile() = delete;
explicit SafeOutputFile(const mpt::PathString &filename, std::ios_base::openmode mode = std::ios_base::out, FlushMode flushMode = FlushMode::Full)
: m_FlushMode(flushMode)
#if MPT_COMPILER_MSVC
, m_s(internal_fopen(filename, mode | std::ios_base::out, flushMode))
#else // !MPT_COMPILER_MSVC
, m_s(filename, mode)
#endif // MPT_COMPILER_MSVC
{
if(!stream().is_open())
stream().setstate(mpt::ofstream::failbit);
}
mpt::ofstream& stream()
{
return m_s;
}
operator mpt::ofstream& ()
{
return stream();
}
const mpt::ofstream& stream() const
{
return m_s;
}
operator const mpt::ofstream& () const
{
return stream();
}
operator bool() const
{
return stream() ? true : false;
}
bool operator!() const
{
return stream().operator!();
}
~SafeOutputFile() noexcept(false);
};
#endif // MODPLUG_TRACKER
#ifdef MODPLUG_TRACKER
// 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<std::byte> &data);
LazyFileRef & operator = (const std::vector<char> &data);
LazyFileRef & operator = (const std::string &data);
operator std::vector<std::byte> () const;
operator std::vector<char> () const;
operator std::string () const;
};
#endif // MODPLUG_TRACKER
} // namespace mpt
class InputFile
{
private:
mpt::PathString m_Filename;
mpt::ifstream m_File;
bool m_IsCached;
std::vector<std::byte> m_Cache;
public:
static bool DefaultToLargeAddressSpaceUsage();
public:
InputFile();
InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage());
~InputFile();
bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = DefaultToLargeAddressSpaceUsage());
bool IsValid() const;
bool IsCached() const;
const mpt::PathString& GetFilenameRef() const;
std::istream* GetStream();
mpt::const_byte_span GetCache();
};
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,867 @@
/*
* 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
#include <tchar.h>
#endif
OPENMPT_NAMESPACE_BEGIN
#if MPT_OS_WINDOWS
namespace mpt
{
RawPathString PathString::AsNativePrefixed() const
{
#if MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00)
// For WinRT on Windows 8, there is no official wy to determine an absolute path.
return path;
#else
if(path.length() < MAX_PATH || path.substr(0, 4) == PL_("\\\\?\\"))
{
// Path is short enough or already in prefixed form
return path;
}
const RawPathString absPath = mpt::GetAbsolutePath(*this).AsNative();
if(absPath.substr(0, 2) == PL_("\\\\"))
{
// Path is a network share: \\server\foo.bar -> \\?\UNC\server\foo.bar
return PL_("\\\\?\\UNC") + absPath.substr(1);
} else
{
// Regular file: C:\foo.bar -> \\?\C:\foo.bar
return PL_("\\\\?\\") + absPath;
}
#endif
}
#if !MPT_OS_WINDOWS_WINRT
int PathString::CompareNoCase(const PathString & a, const PathString & b)
{
return lstrcmpi(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] == PC_(':'))
{
// Drive letter
root = path.substr(0, 2) + PC_('\\');
startPos = 2;
} else if(path.substr(0, 2) == PL_("\\\\"))
{
// Network share
root = PL_("\\\\");
startPos = 2;
} else if(path.substr(0, 2) == PL_(".\\") || path.substr(0, 2) == PL_("./"))
{
// Special case for relative paths
root = PL_(".\\");
startPos = 2;
} else if(path.size() >= 1 && (path[0] == PC_('\\') || path[0] == PC_('/')))
{
// Special case for relative paths
root = PL_("\\");
startPos = 1;
}
while(startPos < path.size())
{
auto pos = path.find_first_of(PL_("\\/"), startPos);
if(pos == RawPathString::npos)
pos = path.size();
mpt::RawPathString dir = path.substr(startPos, pos - startPos);
if(dir == PL_(".."))
{
// Go back one directory
if(!components.empty())
{
components.pop_back();
}
} else if(dir == PL_("."))
{
// 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 + PL_("\\");
}
if(!components.empty())
result.pop_back();
return mpt::PathString(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) == PL_("\\\\?\\UNC\\"))
{
p = PL_("\\\\") + p.substr(8);
} else if(p.substr(0, 4) == PL_("\\\\?\\"))
{
p = p.substr(4);
}
if (p.length() >= 2 && (
p.substr(0, 2) == PL_("\\\\")
|| p.substr(0, 2) == PL_("\\/")
|| p.substr(0, 2) == PL_("/\\")
|| p.substr(0, 2) == PL_("//")
))
{ // UNC
mpt::RawPathString::size_type first_slash = p.substr(2).find_first_of(PL_("\\/"));
if(first_slash != mpt::RawPathString::npos)
{
mpt::RawPathString::size_type second_slash = p.substr(2 + first_slash + 1).find_first_of(PL_("\\/"));
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] == PC_(':')))
{
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(PL_("\\/"));
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(PL_("."));
if(last_dot == mpt::RawPathString::npos)
{
if(fname) *fname = mpt::PathString::FromNative(p);
if(ext) *ext = mpt::PathString();
} else if(last_dot == 0)
{
if(fname) *fname = mpt::PathString::FromNative(p);
if(ext) *ext = mpt::PathString();
} else if(p == PL_(".") || p == PL_(".."))
{
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 = ::GetFileAttributes(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 = ::GetFileAttributes(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 ::PathFileExists(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 = *this;
if(path.empty())
{
return result;
}
if(!_tcsncicmp(relativeTo.AsNative().c_str(), AsNative().c_str(), relativeTo.AsNative().length()))
{
// Path is OpenMPT's directory or a sub directory ("C:\OpenMPT\Somepath" => ".\Somepath")
result = P_(".\\"); // ".\"
result += mpt::PathString::FromNative(AsNative().substr(relativeTo.AsNative().length()));
} else if(!_tcsncicmp(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 = *this;
if(path.empty())
{
return result;
}
if(path.length() >= 2 && path.at(0) == PC_('\\') && path.at(1) != PC_('\\'))
{
// 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 += mpt::PathString(path);
} else if(path.length() >= 2 && path.substr(0, 2) == PL_(".\\"))
{
// 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;
}
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
bool PathString::IsPathSeparator(RawPathString::value_type c)
{
#if MPT_OS_WINDOWS
return (c == PC_('\\')) || (c == PC_('/'));
#else
return c == PC_('/');
#endif
}
RawPathString::value_type PathString::GetDefaultPathSeparator()
{
#if MPT_OS_WINDOWS
return PC_('\\');
#else
return PC_('/');
#endif
}
} // namespace mpt
namespace mpt
{
bool PathIsAbsolute(const mpt::PathString &path) {
mpt::RawPathString rawpath = path.AsNative();
#if MPT_OS_WINDOWS
if(rawpath.substr(0, 8) == PL_("\\\\?\\UNC\\"))
{
return true;
}
if(rawpath.substr(0, 4) == PL_("\\\\?\\"))
{
return true;
}
if(rawpath.substr(0, 2) == PL_("\\\\"))
{
return true; // UNC
}
if(rawpath.substr(0, 2) == PL_("//"))
{
return true; // UNC
}
return (rawpath.length()) >= 3 && (rawpath[1] == ':') && mpt::PathString::IsPathSeparator(rawpath[2]);
#else
return (rawpath.length() >= 1) && mpt::PathString::IsPathSeparator(rawpath[0]);
#endif
}
#if MPT_OS_WINDOWS
#if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00))
mpt::PathString GetAbsolutePath(const mpt::PathString &path)
{
DWORD size = GetFullPathName(path.AsNative().c_str(), 0, nullptr, nullptr);
if(size == 0)
{
return path;
}
std::vector<TCHAR> fullPathName(size, TEXT('\0'));
if(GetFullPathName(path.AsNative().c_str(), size, fullPathName.data(), nullptr) == 0)
{
return path;
}
return mpt::PathString::FromNative(fullPathName.data());
}
#endif
#ifdef MODPLUG_TRACKER
bool DeleteWholeDirectoryTree(mpt::PathString path)
{
if(path.AsNative().empty())
{
return false;
}
if(PathIsRelative(path.AsNative().c_str()) == TRUE)
{
return false;
}
if(!path.FileOrDirectoryExists())
{
return true;
}
if(!path.IsDirectory())
{
return false;
}
path.EnsureTrailingSlash();
HANDLE hFind = NULL;
WIN32_FIND_DATA wfd;
MemsetZero(wfd);
hFind = FindFirstFile((path + P_("*.*")).AsNative().c_str(), &wfd);
if(hFind != NULL && hFind != INVALID_HANDLE_VALUE)
{
do
{
mpt::PathString filename = mpt::PathString::FromNative(wfd.cFileName);
if(filename != P_(".") && filename != P_(".."))
{
filename = path + filename;
if(filename.IsDirectory())
{
if(!DeleteWholeDirectoryTree(filename))
{
return false;
}
} else if(filename.IsFile())
{
if(DeleteFile(filename.AsNative().c_str()) == 0)
{
return false;
}
}
}
} while(FindNextFile(hFind, &wfd));
FindClose(hFind);
}
if(RemoveDirectory(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 GetExecutablePath()
{
std::vector<TCHAR> exeFileName(MAX_PATH);
while(GetModuleFileName(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 = GetSystemDirectory(nullptr, 0);
std::vector<TCHAR> path(size + 1);
if(!GetSystemDirectory(path.data(), size + 1))
{
return mpt::PathString();
}
return mpt::PathString::FromNative(path.data()) + P_("\\");
}
#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 = GetTempPath(0, nullptr);
if(size)
{
std::vector<TCHAR> tempPath(size + 1);
if(GetTempPath(size + 1, tempPath.data()))
{
return mpt::PathString::FromNative(tempPath.data());
}
}
// use exe directory as fallback
return mpt::GetExecutablePath();
}
mpt::PathString CreateTempFileName(const mpt::PathString &fileNamePrefix, const mpt::PathString &fileNameExtension)
{
mpt::PathString filename = mpt::GetTempDirectory();
filename += (!fileNamePrefix.empty() ? fileNamePrefix + P_("_") : mpt::PathString());
filename += mpt::PathString::FromUnicode(mpt::UUID::GenerateLocalUseOnly().ToUString());
filename += (!fileNameExtension.empty() ? P_(".") + 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())
{
DeleteFile(filename.AsNative().c_str());
}
}
#ifdef MODPLUG_TRACKER
TempDirGuard::TempDirGuard(const mpt::PathString &dirname_)
: dirname(dirname_.WithTrailingSlash())
{
if(dirname.empty())
{
return;
}
if(::CreateDirectory(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(MPT_WITH_MFC)
void SanitizeFilename(CString &str)
{
for(int i = 0; i < str.GetLength(); i++)
{
str.SetAt(i, SanitizeFilenameChar(str.GetAt(i)));
}
}
#endif // MPT_WITH_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 += P_(" (");
bool first = true;
for(const auto &ext : extensions)
{
if(first)
{
first = false;
} else
{
filter += P_(",");
}
filter += P_("*.");
filter += ext;
}
filter += P_(")");
}
filter += P_("|");
{
bool first = true;
for(const auto &ext : extensions)
{
if(first)
{
first = false;
} else
{
filter += P_(";");
}
filter += P_("*.");
filter += ext;
}
}
filter += P_("|");
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 += P_(";");
}
filter += P_("*.");
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 ? P_(";") : P_("")) + 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 ? P_(";") : P_("")) + filter;
}
#endif // MODPLUG_TRACKER
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,508 @@
/*
* 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 "BuildSettings.h"
#include <vector>
#include "FlagSet.h"
OPENMPT_NAMESPACE_BEGIN
#define MPT_DEPRECATED_PATH
//#define MPT_DEPRECATED_PATH [[deprecated]]
namespace mpt
{
#if MPT_OS_WINDOWS
typedef mpt::winstring RawPathString;
#else // !MPT_OS_WINDOWS
typedef std::string RawPathString;
#endif // if MPT_OS_WINDOWS
class PathString
{
private:
RawPathString path;
private:
explicit 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. "OpenMPT"
PathString GetFileExt() const; // Extension including dot, e.g. ".exe"
PathString GetFullFileName() const; // File name + extension, e.g. "OpenMPT.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
static bool IsPathSeparator(RawPathString::value_type c);
static RawPathString::value_type GetDefaultPathSeparator();
#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(path.empty())
{
return false;
}
RawPathString::value_type c = path[path.length() - 1];
return IsPathSeparator(c);
}
mpt::PathString &EnsureTrailingSlash()
{
if(!path.empty() && !HasTrailingSlash())
{
path += GetDefaultPathSeparator();
}
return *this;
}
mpt::PathString WithoutTrailingSlash() const
{
mpt::PathString result = *this;
while(result.HasTrailingSlash())
{
if(result.Length() == 1)
{
return result;
}
result = mpt::PathString(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::Charset::Locale, path); }
#endif
std::string ToUTF8() const { return mpt::ToCharset(mpt::Charset::UTF8, path); }
std::wstring ToWide() const { return mpt::ToWide(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::ToWin(mpt::Charset::Locale, path)); }
static PathString FromLocaleSilent(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::Locale, path)); }
#endif
static PathString FromUTF8(const std::string &path) { return PathString(mpt::ToWin(mpt::Charset::UTF8, path)); }
static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToWin(path)); }
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToWin(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(MPT_WITH_MFC)
// CString TCHAR, so this is CHAR or WCHAR, depending on UNICODE
CString ToCString() const { return mpt::ToCString(path); }
static PathString FromCString(const CString &path) { return PathString(mpt::ToWin(path)); }
#endif // MPT_WITH_MFC
// 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::Charset::UTF8, mpt::Charset::Locale, path); }
#if MPT_WSTRING_CONVERT
std::wstring ToWide() const { return mpt::ToWide(mpt::Charset::Locale, path); }
#endif
mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::Locale, 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::Charset::Locale, mpt::Charset::UTF8, path)); }
#if MPT_WSTRING_CONVERT
static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, path)); }
#endif
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::Locale, 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::Charset::UTF8, path); }
#endif
mpt::ustring ToUnicode() const { return mpt::ToUnicode(mpt::Charset::UTF8, path); }
static PathString FromUTF8(const std::string &path) { return PathString(path); }
#if MPT_WSTRING_CONVERT
static PathString FromWide(const std::wstring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, path)); }
#endif
static PathString FromUnicode(const mpt::ustring &path) { return PathString(mpt::ToCharset(mpt::Charset::UTF8, 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)
[[deprecated]] mpt::PathString Simplify() const { return PathString(path); }
#endif // MPT_OS_WINDOWS
};
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#if MPT_OS_WINDOWS
#ifdef UNICODE
[[deprecated]] inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
#else
MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); }
#endif
#else
MPT_DEPRECATED_PATH inline std::string ToString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
#endif
#endif
inline mpt::ustring ToUString(const mpt::PathString & x) { return x.ToUnicode(); }
#if MPT_WSTRING_FORMAT
inline std::wstring ToWString(const mpt::PathString & x) { return x.ToWide(); }
#endif
} // namespace mpt
#if MPT_OS_WINDOWS
#ifdef UNICODE
#define MPT_PATHSTRING_LITERAL(x) ( L ## x )
#define MPT_PATHSTRING(x) mpt::PathString::FromNative( L ## x )
#else
#define MPT_PATHSTRING_LITERAL(x) ( x )
#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x )
#endif
#else // !MPT_OS_WINDOWS
#define MPT_PATHSTRING_LITERAL(x) ( x )
#define MPT_PATHSTRING(x) mpt::PathString::FromNative( x )
#endif // MPT_OS_WINDOWS
#define PC_(x) MPT_PATHSTRING_LITERAL(x)
#define PL_(x) MPT_PATHSTRING_LITERAL(x)
#define P_(x) MPT_PATHSTRING(x)
namespace mpt
{
bool PathIsAbsolute(const mpt::PathString &path);
#if MPT_OS_WINDOWS
#if !(MPT_OS_WINDOWS_WINRT && (_WIN32_WINNT < 0x0a00))
// Returns the absolute path for a potentially relative path and removes ".." or "." components. (same as GetFullPathNameW)
mpt::PathString GetAbsolutePath(const mpt::PathString &path);
#endif
#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 executable path or an empty string (if unknown), e.g. "C:\mptrack\"
mpt::PathString GetExecutablePath();
#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 = P_("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(MPT_WITH_MFC)
void SanitizeFilename(CString &str);
#endif // MPT_WITH_MFC
#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(U_("*")).Description(U_("All Files")).AddExtension(P_("*"));
}
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,344 @@
/*
* 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 <chrono>
#include <cmath>
#include <cstdlib>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
template <typename T>
static T log2(T x)
{
return std::log(x) / std::log(static_cast<T>(2));
}
static MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x)
{
return detail::lower_bound_entropy_bits(x);
}
template <typename T>
static MPT_CONSTEXPR14_FUN 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;
}
#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
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
{
uint64be time;
time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock().now().time_since_epoch()).count();
std::byte bytes[sizeof(time)];
std::memcpy(bytes, &time, sizeof(time));
hash(std::begin(bytes), std::end(bytes));
}
#if !defined(MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK)
{
uint64be time;
time = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock().now().time_since_epoch()).count();
std::byte bytes[sizeof(time)];
std::memcpy(bytes, &time, sizeof(time));
hash(std::begin(bytes), std::end(bytes));
}
#endif // !MPT_COMPILER_QUIRK_CHRONO_NO_HIGH_RESOLUTION_CLOCK
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()
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
: rd_reliable(false)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
try
{
prd = std::make_unique<std::random_device>();
rd_reliable = ((*prd).entropy() > 0.0);
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e);
} catch(const std::exception &)
{
rd_reliable = false;
}
if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{
init_fallback();
}
}
sane_random_device::sane_random_device(const std::string & token_)
: token(token_)
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
, rd_reliable(false)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
try
{
prd = std::make_unique<std::random_device>(token);
rd_reliable = ((*prd).entropy() > 0.0);
} MPT_EXCEPTION_CATCH_OUT_OF_MEMORY(e)
{
MPT_EXCEPTION_RETHROW_OUT_OF_MEMORY(e);
} catch(const std::exception &)
{
rd_reliable = false;
}
if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{
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 = std::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 = std::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;
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
if(prd)
{
try
{
if constexpr(std::random_device::min() != 0 || !mpt::is_mask(std::random_device::max()))
{ // insane std::random_device
// This implementation is not exactly uniformly distributed but good enough
// for OpenMPT.
double rd_min = static_cast<double>(std::random_device::min());
double rd_max = static_cast<double>(std::random_device::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>((*prd)()) - 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(std::random_device::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>((*prd)());
} else
{
result = result | static_cast<result_type>((*prd)());
}
}
}
} catch(const std::exception &)
{
rd_reliable = false;
init_fallback();
}
} else
{
rd_reliable = false;
}
if(!rd_reliable)
#endif // MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
{ // 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::default_prng> *g_global_prng = nullptr;
void set_global_random_device(mpt::random_device *rd)
{
g_rd = rd;
}
void set_global_prng(mpt::thread_safe_prng<mpt::default_prng> *prng)
{
g_global_prng = prng;
}
mpt::random_device & global_random_device()
{
return *g_rd;
}
mpt::thread_safe_prng<mpt::default_prng> & global_prng()
{
return *g_global_prng;
}
#else
mpt::random_device & global_random_device()
{
static mpt::random_device g_rd;
return g_rd;
}
mpt::thread_safe_prng<mpt::default_prng> & global_prng()
{
static mpt::thread_safe_prng<mpt::default_prng> g_global_prng(mpt::make_prng<mpt::default_prng>(global_random_device()));
return g_global_prng;
}
#endif // MODPLUG_TRACKER && !MPT_BUILD_WINESUPPORT
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,733 @@
/*
* 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 "BuildSettings.h"
#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 constexpr uint32 FUZZER_RNG_SEED = 3141592653u; // pi
#endif // MPT_BUILD_FUZZER
namespace detail
{
MPT_CONSTEXPR14_FUN int lower_bound_entropy_bits(unsigned int x)
{
if(x >= 0xffffffffu)
{
return 32;
} else if(x >= 0x7fffffffu)
{
return 31;
} else if(x >= 0x3fffffffu)
{
return 30;
} else if(x >= 0x1fffffffu)
{
return 29;
} else if(x >= 0x0fffffffu)
{
return 28;
} else if(x >= 0x07ffffffu)
{
return 27;
} else if(x >= 0x03ffffffu)
{
return 26;
} else if(x >= 0x01ffffffu)
{
return 25;
} else if(x >= 0x00ffffffu)
{
return 24;
} else if(x >= 0x007fffffu)
{
return 23;
} else if(x >= 0x003fffffu)
{
return 22;
} else if(x >= 0x001fffffu)
{
return 21;
} else if(x >= 0x000fffffu)
{
return 20;
} else if(x >= 0x0007ffffu)
{
return 19;
} else if(x >= 0x0003ffffu)
{
return 18;
} else if(x >= 0x0001ffffu)
{
return 17;
} else if(x >= 0x0000ffffu)
{
return 16;
} else if(x >= 0x00007fffu)
{
return 15;
} else if(x >= 0x00003fffu)
{
return 14;
} else if(x >= 0x00001fffu)
{
return 13;
} else if(x >= 0x00000fffu)
{
return 12;
} else if(x >= 0x000007ffu)
{
return 11;
} else if(x >= 0x000003ffu)
{
return 10;
} else if(x >= 0x000001ffu)
{
return 9;
} else if(x >= 0x000000ffu)
{
return 8;
} else if(x >= 0x0000007fu)
{
return 7;
} else if(x >= 0x0000003fu)
{
return 6;
} else if(x >= 0x0000001fu)
{
return 5;
} else if(x >= 0x0000000fu)
{
return 4;
} else if(x >= 0x00000007u)
{
return 3;
} else if(x >= 0x00000003u)
{
return 2;
} else if(x >= 0x00000001u)
{
return 1;
}
return 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)
{
if constexpr(rng_bits < (sizeof(T) * 8))
{
constexpr 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(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
{
if constexpr(rng_bits < (sizeof(T) * 8))
{
constexpr 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 constexpr(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(required_entropy_bits, sizeof(T) * 8); entropy += rng_bits)
{
if constexpr(rng_bits < (sizeof(T) * 8))
{
constexpr 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 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
{
const int mantissa_bits = std::numeric_limits<T>::digits;
return ((b - a) * static_cast<T>(mpt::random<uint64, mantissa_bits>(rng)) / static_cast<T>((static_cast<uint64>(1u) << mantissa_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;
template <typename Tstate, typename Tvalue, Tstate x1, Tstate x2, Tstate x3, Tstate x4, int rol1, int rol2>
class modplug
{
public:
typedef Tstate state_type;
typedef Tvalue result_type;
private:
state_type state1;
state_type state2;
public:
template <typename Trng>
explicit inline modplug(Trng &rd)
: state1(mpt::random<state_type>(rd))
, state2(mpt::random<state_type>(rd))
{
}
explicit inline modplug(state_type seed1, state_type seed2)
: state1(seed1)
, state2(seed2)
{
}
public:
static MPT_CONSTEXPR11_FUN result_type min()
{
return static_cast<result_type>(0);
}
static MPT_CONSTEXPR11_FUN result_type max()
{
return std::numeric_limits<result_type>::max();
}
static MPT_CONSTEXPR11_FUN int result_bits()
{
static_assert(std::is_integral<result_type>::value);
static_assert(std::is_unsigned<result_type>::value);
return std::numeric_limits<result_type>::digits;
}
inline result_type operator()()
{
state_type a = state1;
state_type b = state2;
a = mpt::rotl(a, rol1);
a ^= x1;
a += x2 + (b * x3);
b += mpt::rotl(a, rol2) * x4;
state1 = a;
state2 = b;
result_type result = static_cast<result_type>(b);
return result;
}
};
typedef modplug<uint32, uint32, 0x10204080u, 0x78649E7Du, 4, 5, 1, 16> modplug_dither;
} // 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_CONSTEXPR14_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;
#if !defined(MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE)
std::unique_ptr<std::random_device> prd;
bool rd_reliable;
#endif // !MPT_COMPILER_QUIRK_RANDOM_NO_RANDOM_DEVICE
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> {
enum : 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)
{
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
std::seed_seq seed(values->begin(), values->end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::mt19937_64> {
enum : 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)
{
std::unique_ptr<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>> values = std::make_unique<mpt::seed_seq_values<seed_bits / sizeof(unsigned int)>>(rd);
std::seed_seq seed(values->begin(), values->end());
return rng_type(seed);
}
};
template <> struct engine_traits<std::ranlux24_base> {
enum : 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> {
enum : 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> {
enum : 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> {
enum : 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_musl good_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::ranlux48 good_prng;
#endif // MPT_BUILD_FUZZER
typedef mpt::good_prng default_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::default_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::default_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,703 @@
/*
* 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 "BuildSettings.h"
#include "mptAlloc.h"
#include "mptBaseTypes.h"
#include "mptSpan.h"
#include <algorithm>
#include <limits>
#include <string>
#include <string_view>
#include <cstring>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
template <typename T> inline span<T> as_span(std::basic_string<T> & str) { return span<T>(&(str[0]), str.length()); }
template <typename T> inline span<const T> as_span(const std::basic_string<T> & str) { return span<const T>(&(str[0]), str.length()); }
template <typename T> inline std::vector<typename std::remove_const<T>::type> make_vector(const std::basic_string<T> & str) { return std::vector<typename std::remove_const<T>::type>(str.begin(), str.end()); }
// string_traits abstract the API of underlying string classes, in particular they allow adopting to CString without having to specialize for CString explicitly
template <typename Tstring>
struct string_traits
{
using string_type = Tstring;
using size_type = typename string_type::size_type;
using char_type = typename string_type::value_type;
static inline std::size_t length(const string_type &str) { return str.length(); }
static inline void reserve(string_type &str, std::size_t size) { str.reserve(size); }
static inline string_type& append(string_type &str, const string_type &a) { return str.append(a); }
static inline string_type& append(string_type &str, string_type &&a) { return str.append(std::move(a)); }
static inline string_type& append(string_type &str, std::size_t count, char_type c) { return str.append(count, c); }
static inline string_type pad(string_type str, std::size_t left, std::size_t right)
{
str.insert(str.begin(), left, char_type(' '));
str.insert(str.end(), right, char_type(' '));
return str;
}
};
#if defined(MPT_WITH_MFC)
template <>
struct string_traits<CString>
{
using string_type = CString;
using size_type = int;
using char_type = typename CString::XCHAR;
static inline size_type length(const string_type &str) { return str.GetLength(); }
static inline void reserve(string_type &str, size_type size) { str.Preallocate(size); }
static inline string_type& append(string_type &str, const string_type &a) { str += a; return str; }
static inline string_type& append(string_type &str, size_type count, char_type c) { while(count--) str.AppendChar(c); return str; }
static inline string_type pad(const string_type &str, size_type left, size_type right)
{
CString tmp;
while(left--) tmp.AppendChar(char_type(' '));
tmp += str;
while(right--) tmp.AppendChar(char_type(' '));
return tmp;
}
};
#endif // MPT_WITH_MFC
namespace String
{
template <typename Tstring> struct Traits {
static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; }
static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; }
};
template <> struct Traits<std::string> {
static MPT_FORCEINLINE const char * GetDefaultWhitespace() noexcept { return " \n\r\t"; }
static MPT_FORCEINLINE bool IsLineEnding(char c) noexcept { return c == '\r' || c == '\n'; }
};
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
template <> struct Traits<std::wstring> {
static MPT_FORCEINLINE const wchar_t * GetDefaultWhitespace() noexcept { return L" \n\r\t"; }
static MPT_FORCEINLINE bool IsLineEnding(wchar_t c) noexcept { return c == L'\r' || c == L'\n'; }
};
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
// 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
inline std::string truncate(std::string str, std::size_t maxLen)
{
if(str.length() > maxLen)
{
str.resize(maxLen);
}
return str;
}
enum class Charset {
UTF8,
ASCII, // strictly 7-bit ASCII
ISO8859_1,
ISO8859_15,
CP437,
CP437AMS,
CP437AMS2,
Windows1252,
#if defined(MPT_ENABLE_CHARSET_LOCALE)
Locale, // CP_ACP on windows, current C locale otherwise
#endif // MPT_ENABLE_CHARSET_LOCALE
};
// source code / preprocessor (i.e. # token)
inline constexpr Charset CharsetSource = Charset::ASCII;
// debug log files
inline constexpr Charset CharsetLogfile = Charset::UTF8;
// std::clog / std::cout / std::cerr
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS && defined(MPT_ENABLE_CHARSET_LOCALE)
inline constexpr Charset CharsetStdIO = Charset::Locale;
#else
inline constexpr Charset CharsetStdIO = Charset::UTF8;
#endif
// std::exception::what()
#if defined(MPT_ENABLE_CHARSET_LOCALE)
inline constexpr Charset CharsetException = Charset::Locale;
#else
inline constexpr Charset CharsetException = Charset::UTF8;
#endif
// Locale in tracker builds, UTF8 in non-locale-aware libopenmpt builds.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
inline constexpr Charset CharsetLocaleOrUTF8 = Charset::Locale;
#else
inline constexpr Charset CharsetLocaleOrUTF8 = Charset::UTF8;
#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 )
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
#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 )
#else // MPT_COMPILER_QUIRK_NO_WCHAR
#define MPT_WCHAR_TYPE char32_t
#define MPT_WCHAR(x) U ## x
#define MPT_WLITERAL(x) U ## x
#define MPT_WSTRING(x) std::u32string( U ## x )
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
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 > >
#if defined(MPT_ENABLE_CHARSET_LOCALE)
using lstring = MPT_ENCODED_STRING_TYPE(mpt::Charset::Locale);
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
template <typename Tchar> struct windows_char_traits { };
template <> struct windows_char_traits<char> { using string_type = mpt::lstring; };
template <> struct windows_char_traits<wchar_t> { using string_type = std::wstring; };
#ifdef UNICODE
using tstring = windows_char_traits<wchar_t>::string_type;
#else
using tstring = windows_char_traits<char>::string_type;
#endif
using winstring = mpt::tstring;
#endif // MPT_OS_WINDOWS
#if MPT_ENABLE_U8STRING
#if MPT_CXX_AT_LEAST(20)
using u8string = std::u8string;
#define MPT_U8CHAR_TYPE char8_t
#define MPT_U8CHAR(x) u8 ## x
#define MPT_U8LITERAL(x) u8 ## x
#define MPT_U8STRING(x) std::u8string( u8 ## x )
#else // !C++20
using u8string = MPT_ENCODED_STRING_TYPE(mpt::Charset::UTF8);
#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 // C++20
#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.
inline std::wstring ToWide(const std::wstring &str) { return str; }
inline std::wstring ToWide(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
std::wstring ToWide(Charset from, const std::string &str);
inline std::wstring ToWide(Charset from, const char * str) { return ToWide(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::wstring ToWide(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#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);
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);
inline std::string ToCharset(Charset to, Charset from, const char * str) { return ToCharset(to, from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::string ToCharset(Charset to, const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#if MPT_WSTRING_CONVERT
mpt::lstring ToLocale(const std::wstring &str);
inline mpt::lstring ToLocale(const wchar_t * str) { return ToLocale(str ? std::wstring(str): std::wstring()); }
#endif
mpt::lstring ToLocale(Charset from, const std::string &str);
inline mpt::lstring ToLocale(Charset from, const char * str) { return ToLocale(from, str ? std::string(str): std::string()); }
inline mpt::lstring ToLocale(const mpt::lstring &str) { return str; }
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
#if MPT_WSTRING_CONVERT
mpt::winstring ToWin(const std::wstring &str);
inline mpt::winstring ToWin(const wchar_t * str) { return ToWin(str ? std::wstring(str): std::wstring()); }
#endif
mpt::winstring ToWin(Charset from, const std::string &str);
inline mpt::winstring ToWin(Charset from, const char * str) { return ToWin(from, str ? std::string(str): std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::winstring ToWin(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#endif // MPT_OS_WINDOWS
#if defined(MPT_WITH_MFC)
#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.
inline CString ToCString(const CString &str) { return str; }
CString ToCString(const std::wstring &str);
inline CString ToCString(const wchar_t * str) { return ToCString(str ? std::wstring(str) : std::wstring()); }
CString ToCString(Charset from, const std::string &str);
inline CString ToCString(Charset from, const char * str) { return ToCString(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
CString ToCString(const mpt::lstring &str);
mpt::lstring ToLocale(const CString &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
mpt::winstring ToWin(const CString &str);
#endif // MPT_OS_WINDOWS
// 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);
#endif // MPT_WITH_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
using ustring = std::wstring;
using uchar = 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
using ustring = mpt::u8string;
using uchar = MPT_U8CHAR_TYPE;
#define MPT_UCHAR(x) MPT_U8CHAR( x )
#define MPT_ULITERAL(x) MPT_U8LITERAL( x )
#define MPT_USTRING(x) MPT_U8STRING( x )
#endif // MPT_USTRING_MODE_UTF8
#define UC_(x) MPT_UCHAR(x)
#define UL_(x) MPT_ULITERAL(x)
#define U_(x) MPT_USTRING(x)
#if MPT_USTRING_MODE_WIDE
#if !(MPT_WSTRING_CONVERT)
#error "MPT_USTRING_MODE_WIDE depends on MPT_WSTRING_CONVERT)"
#endif
inline mpt::ustring ToUnicode(const std::wstring &str) { return str; }
inline mpt::ustring ToUnicode(const wchar_t * str) { return (str ? std::wstring(str) : std::wstring()); }
inline mpt::ustring ToUnicode(Charset from, const std::string &str) { return ToWide(from, str); }
inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
inline mpt::ustring ToUnicode(const mpt::lstring &str) { return ToWide(str); }
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
inline mpt::ustring ToUnicode(const CString &str) { return ToWide(str); }
#endif // MFC
#else // !MPT_USTRING_MODE_WIDE
inline mpt::ustring ToUnicode(const mpt::ustring &str) { return str; }
#if MPT_WSTRING_CONVERT
mpt::ustring ToUnicode(const std::wstring &str);
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);
inline mpt::ustring ToUnicode(Charset from, const char * str) { return ToUnicode(from, str ? std::string(str) : std::string()); }
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::ustring ToUnicode(const mpt::lstring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
mpt::ustring ToUnicode(const CString &str);
#endif // MPT_WITH_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(MPT_ENABLE_CHARSET_LOCALE)
mpt::lstring ToLocale(const mpt::ustring &str);
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
mpt::winstring ToWin(const mpt::ustring &str);
#endif // MPT_OS_WINDOWS
#if defined(MPT_WITH_MFC)
CString ToCString(const mpt::ustring &str);
#endif // MPT_WITH_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::Charset::UTF8, x )
mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str);
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(std::string_view a, std::string_view b);
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::Charset::UTF8, bool tryUTF8 = true>
class BasicAnyString : public mpt::ustring
{
private:
static mpt::ustring From8bit(const std::string &str)
{
if constexpr(charset == mpt::Charset::UTF8)
{
return mpt::ToUnicode(mpt::Charset::UTF8, str);
} else
{
// auto utf8 detection
if constexpr(tryUTF8)
{
if(mpt::IsUTF8(str))
{
return mpt::ToUnicode(mpt::Charset::UTF8, str);
} else
{
return mpt::ToUnicode(charset, str);
}
} else
{
return mpt::ToUnicode(charset, str);
}
}
}
public:
// 8 bit
BasicAnyString(const char *str) : mpt::ustring(From8bit(str ? str : std::string())) { }
BasicAnyString(const std::string str) : mpt::ustring(From8bit(str)) { }
// locale
#if defined(MPT_ENABLE_CHARSET_LOCALE)
BasicAnyString(const mpt::lstring str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif // MPT_ENABLE_CHARSET_LOCALE
// 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(MPT_WITH_MFC)
BasicAnyString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif // MPT_WITH_MFC
// 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:
// locale
#if defined(MPT_ENABLE_CHARSET_LOCALE)
AnyUnicodeString(const mpt::lstring &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif // MPT_ENABLE_CHARSET_LOCALE
// 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(MPT_WITH_MFC)
AnyUnicodeString(const CString &str) : mpt::ustring(mpt::ToUnicode(str)) { }
#endif // MPT_WITH_MFC
// 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)
using AnyString = BasicAnyString<mpt::Charset::Locale, true>;
#elif MPT_OS_WINDOWS
using AnyString = BasicAnyString<mpt::Charset::Windows1252, true>;
#else
using AnyString = BasicAnyString<mpt::Charset::ISO8859_1, true>;
#endif
// AnyStringLocale
// char-based strings are assumed to be in locale encoding.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
using AnyStringLocale = BasicAnyString<mpt::Charset::Locale, false>;
#else
using AnyStringLocale = BasicAnyString<mpt::Charset::UTF8, false>;
#endif
// AnyStringUTF8orLocale
// char-based strings are tried in UTF8 first, if this fails, locale is used.
#if defined(MPT_ENABLE_CHARSET_LOCALE)
using AnyStringUTF8orLocale = BasicAnyString<mpt::Charset::Locale, true>;
#else
using AnyStringUTF8orLocale = BasicAnyString<mpt::Charset::UTF8, false>;
#endif
// AnyStringUTF8
// char-based strings are assumed to be in UTF8.
using AnyStringUTF8 = BasicAnyString<mpt::Charset::UTF8, false>;
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,117 @@
/*
* mptStringBuffer.cpp
* -------------------
* 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.
*/
#include "stdafx.h"
#include "mptStringBuffer.h"
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace String
{
namespace detail
{
std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize)
{
std::string dest;
const char *src = 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 null-terminated string, stopping at null.
dest.assign(src, std::find(src, src + srcSize, '\0'));
} else if(mode == spacePadded || mode == spacePaddedNull)
{
// Copy string over.
dest.assign(src, src + srcSize);
// Convert null characters to spaces.
std::transform(dest.begin(), dest.end(), dest.begin(), [] (char c) -> char { return (c != '\0') ? c : ' '; });
// Trim trailing spaces.
dest = mpt::String::RTrim(dest, std::string(" "));
}
return dest;
}
void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::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);
}
}
} // namespace detail
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,565 @@
/*
* mptStringBuffer.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 "BuildSettings.h"
#include "mptMemory.h"
#include "mptString.h"
#include <algorithm>
#include <string>
#include <vector>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace String
{
enum ReadWriteMode : uint8
{
// Reading / Writing: Standard null-terminated string handling.
nullTerminated = 1,
// 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 = 2,
// Reading: String may contain null characters anywhere. They should be treated as spaces.
// Writing: A space-padded string is written.
spacePadded = 3,
// 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 = 4,
};
namespace detail
{
std::string ReadStringBuffer(String::ReadWriteMode mode, const char *srcBuffer, std::size_t srcSize);
void WriteStringBuffer(String::ReadWriteMode mode, char *destBuffer, const std::size_t destSize, const char *srcBuffer, const std::size_t srcSize);
} // namespace detail
} // namespace String
template <typename Tstring, typename Tchar>
class StringBufRefImpl
{
private:
Tchar * buf;
std::size_t size;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
explicit StringBufRefImpl(Tchar * buf, std::size_t size)
: buf(buf)
, size(size)
{
static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type));
MPT_ASSERT(size > 0);
}
StringBufRefImpl(const StringBufRefImpl &) = delete;
StringBufRefImpl(StringBufRefImpl &&) = default;
StringBufRefImpl & operator = (const StringBufRefImpl &) = delete;
StringBufRefImpl & operator = (StringBufRefImpl &&) = delete;
operator Tstring () const
{
std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
return Tstring(buf, buf + len);
}
bool empty() const
{
return buf[0] == Tchar('\0');
}
StringBufRefImpl & operator = (const Tstring & str)
{
std::fill(buf, buf + size, Tchar('\0'));
std::copy(str.data(), str.data() + std::min(str.length(), size - 1), buf);
std::fill(buf + std::min(str.length(), size - 1), buf + size, Tchar('\0'));
return *this;
}
};
template <typename Tstring, typename Tchar>
class StringBufRefImpl<Tstring, const Tchar>
{
private:
const Tchar * buf;
std::size_t size;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
explicit StringBufRefImpl(const Tchar * buf, std::size_t size)
: buf(buf)
, size(size)
{
static_assert(sizeof(Tchar) == sizeof(typename Tstring::value_type));
MPT_ASSERT(size > 0);
}
StringBufRefImpl(const StringBufRefImpl &) = delete;
StringBufRefImpl(StringBufRefImpl &&) = default;
StringBufRefImpl & operator = (const StringBufRefImpl &) = delete;
StringBufRefImpl & operator = (StringBufRefImpl &&) = delete;
operator Tstring () const
{
std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
return Tstring(buf, buf + len);
}
bool empty() const
{
return buf[0] == Tchar('\0');
}
};
namespace String {
template <typename Tstring, typename Tchar, std::size_t size>
inline StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type> ReadTypedBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tstring, typename Tchar>
inline StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type> ReadTypedBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<Tstring, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tstring, typename Tchar, std::size_t size>
inline StringBufRefImpl<Tstring, Tchar> WriteTypedBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<Tstring, Tchar>(buf, size);
}
template <typename Tstring, typename Tchar>
inline StringBufRefImpl<Tstring, Tchar> WriteTypedBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<Tstring, Tchar>(buf, size);
}
} // namespace String
namespace String {
template <typename Tchar, std::size_t size>
inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type> ReadAutoBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar>
inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type> ReadAutoBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar, std::size_t size>
inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar> WriteAutoBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar>(buf, size);
}
template <typename Tchar>
inline StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar> WriteAutoBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<typename std::basic_string<typename std::remove_const<Tchar>::type>, Tchar>(buf, size);
}
} // namespace String
template <std::size_t len, mpt::String::ReadWriteMode mode = static_cast<mpt::String::ReadWriteMode>(0)> struct charbuf;
template <std::size_t len>
struct charbuf<len, static_cast<mpt::String::ReadWriteMode>(0)>
{
public:
typedef char Tchar;
using char_type = Tchar;
using string_type = std::basic_string<Tchar>;
constexpr std::size_t static_length() const { return len; }
public:
Tchar buf[len];
public:
charbuf()
{
Clear(buf);
}
charbuf(const charbuf &) = default;
charbuf(charbuf &&) = default;
charbuf & operator = (const charbuf &) = default;
charbuf & operator = (charbuf &&) = default;
const Tchar & operator[](std::size_t i) const { return buf[i]; }
std::string str() const { return static_cast<std::string>(*this); }
operator string_type () const
{
return mpt::String::ReadAutoBuf(buf);
}
bool empty() const
{
return mpt::String::ReadAutoBuf(buf).empty();
}
charbuf & operator = (const string_type & str)
{
mpt::String::WriteAutoBuf(buf) = str;
return *this;
}
public:
friend bool operator!=(const std::string & a, const charbuf & b) { return a != b.str(); }
friend bool operator!=(const charbuf & a, const std::string & b) { return a.str() != b; }
friend bool operator==(const std::string & a, const charbuf & b) { return a == b.str(); }
friend bool operator==(const charbuf & a, const std::string & b) { return a.str() == b; }
};
template <typename Tchar>
class StringModeBufRefImpl
{
private:
Tchar * buf;
std::size_t size;
String::ReadWriteMode mode;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
StringModeBufRefImpl(Tchar * buf, std::size_t size, String::ReadWriteMode mode)
: buf(buf)
, size(size)
, mode(mode)
{
static_assert(sizeof(Tchar) == 1);
}
StringModeBufRefImpl(const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl(StringModeBufRefImpl &&) = default;
StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete;
operator std::string () const
{
return String::detail::ReadStringBuffer(mode, buf, size);
}
bool empty() const
{
return String::detail::ReadStringBuffer(mode, buf, size).empty();
}
StringModeBufRefImpl & operator = (const std::string & str)
{
String::detail::WriteStringBuffer(mode, buf, size, str.data(), str.size());
return *this;
}
};
template <typename Tchar>
class StringModeBufRefImpl<const Tchar>
{
private:
const Tchar * buf;
std::size_t size;
String::ReadWriteMode mode;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
StringModeBufRefImpl(const Tchar * buf, std::size_t size, String::ReadWriteMode mode)
: buf(buf)
, size(size)
, mode(mode)
{
static_assert(sizeof(Tchar) == 1);
}
StringModeBufRefImpl(const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl(StringModeBufRefImpl &&) = default;
StringModeBufRefImpl & operator = (const StringModeBufRefImpl &) = delete;
StringModeBufRefImpl & operator = (StringModeBufRefImpl &&) = delete;
operator std::string () const
{
return String::detail::ReadStringBuffer(mode, buf, size);
}
bool empty() const
{
return String::detail::ReadStringBuffer(mode, buf, size).empty();
}
};
namespace String {
template <typename Tchar, std::size_t size>
inline StringModeBufRefImpl<typename std::add_const<Tchar>::type> ReadBuf(String::ReadWriteMode mode, Tchar (&buf)[size])
{
return StringModeBufRefImpl<typename std::add_const<Tchar>::type>(buf, size, mode);
}
template <typename Tchar>
inline StringModeBufRefImpl<typename std::add_const<Tchar>::type> ReadBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size)
{
return StringModeBufRefImpl<typename std::add_const<Tchar>::type>(buf, size, mode);
}
template <typename Tchar, std::size_t size>
inline StringModeBufRefImpl<Tchar> WriteBuf(String::ReadWriteMode mode, Tchar (&buf)[size])
{
return StringModeBufRefImpl<Tchar>(buf, size, mode);
}
template <typename Tchar>
inline StringModeBufRefImpl<Tchar> WriteBuf(String::ReadWriteMode mode, Tchar * buf, std::size_t size)
{
return StringModeBufRefImpl<Tchar>(buf, size, mode);
}
} // namespace String
template <std::size_t len, mpt::String::ReadWriteMode mode>
struct charbuf
{
public:
typedef char Tchar;
using char_type = Tchar;
using string_type = std::basic_string<Tchar>;
public:
Tchar buf[len];
public:
charbuf() = default;
charbuf(const charbuf &) = default;
charbuf(charbuf &&) = default;
charbuf & operator = (const charbuf &) = default;
charbuf & operator = (charbuf &&) = default;
operator string_type () const
{
return mpt::String::ReadBuf(mode, buf);
}
bool empty() const
{
return mpt::String::ReadBuf(mode, buf).empty();
}
charbuf & operator = (const string_type & str)
{
mpt::String::WriteBuf(mode, buf) = str;
return *this;
}
};
// see MPT_BINARY_STRUCT
template <std::size_t len, mpt::String::ReadWriteMode mode>
struct is_binary_safe<typename mpt::charbuf<len, mode>> : public std::true_type { };
template <std::size_t len>
struct is_binary_safe<typename mpt::charbuf<len, static_cast<mpt::String::ReadWriteMode>(0)>> : public std::false_type { };
static_assert(sizeof(mpt::charbuf<7>) == 7);
static_assert(alignof(mpt::charbuf<7>) == 1);
static_assert(std::is_standard_layout<mpt::charbuf<7>>::value);
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
namespace String {
template <typename Tchar, std::size_t size>
inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type> ReadWinBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar>
inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type> ReadWinBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar, std::size_t size>
inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar> WriteWinBuf(Tchar (&buf)[size])
{
return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar>(buf, size);
}
template <typename Tchar>
inline StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar> WriteWinBuf(Tchar * buf, std::size_t size)
{
return StringBufRefImpl<typename mpt::windows_char_traits<typename std::remove_const<Tchar>::type>::string_type, Tchar>(buf, size);
}
} // namespace String
#if defined(MPT_WITH_MFC)
template <typename Tchar>
class CStringBufRefImpl
{
private:
Tchar * buf;
std::size_t size;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
explicit CStringBufRefImpl(Tchar * buf, std::size_t size)
: buf(buf)
, size(size)
{
MPT_ASSERT(size > 0);
}
CStringBufRefImpl(const CStringBufRefImpl &) = delete;
CStringBufRefImpl(CStringBufRefImpl &&) = default;
CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete;
CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete;
operator CString () const
{
std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
return CString(buf, mpt::saturate_cast<int>(len));
}
CStringBufRefImpl & operator = (const CString & str)
{
std::fill(buf, buf + size, Tchar('\0'));
std::copy(str.GetString(), str.GetString() + std::min(static_cast<std::size_t>(str.GetLength()), size - 1), buf);
buf[size - 1] = Tchar('\0');
return *this;
}
};
template <typename Tchar>
class CStringBufRefImpl<const Tchar>
{
private:
const Tchar * buf;
std::size_t size;
public:
// cppcheck false-positive
// cppcheck-suppress uninitMemberVar
explicit CStringBufRefImpl(const Tchar * buf, std::size_t size)
: buf(buf)
, size(size)
{
MPT_ASSERT(size > 0);
}
CStringBufRefImpl(const CStringBufRefImpl &) = delete;
CStringBufRefImpl(CStringBufRefImpl &&) = default;
CStringBufRefImpl & operator = (const CStringBufRefImpl &) = delete;
CStringBufRefImpl & operator = (CStringBufRefImpl &&) = delete;
operator CString () const
{
std::size_t len = std::find(buf, buf + size, Tchar('\0')) - buf; // terminate at \0
return CString(buf, mpt::saturate_cast<int>(len));
}
};
namespace String {
template <typename Tchar, std::size_t size>
inline CStringBufRefImpl<typename std::add_const<Tchar>::type> ReadCStringBuf(Tchar (&buf)[size])
{
return CStringBufRefImpl<typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar>
inline CStringBufRefImpl<typename std::add_const<Tchar>::type> ReadCStringBuf(Tchar * buf, std::size_t size)
{
return CStringBufRefImpl<typename std::add_const<Tchar>::type>(buf, size);
}
template <typename Tchar, std::size_t size>
inline CStringBufRefImpl<Tchar> WriteCStringBuf(Tchar (&buf)[size])
{
return CStringBufRefImpl<Tchar>(buf, size);
}
template <typename Tchar>
inline CStringBufRefImpl<Tchar> WriteCStringBuf(Tchar * buf, std::size_t size)
{
return CStringBufRefImpl<Tchar>(buf, size);
}
} // namespace String
#endif // MPT_WITH_MFC
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
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;
}
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
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;
}
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
// 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;
}
}
}
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,522 @@
/*
* 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"
#if MPT_COMPILER_MSVC
#define MPT_FORMAT_CXX17_INT 1
#else
#define MPT_FORMAT_CXX17_INT 0
#endif
#if MPT_FORMAT_CXX17_INT
#if MPT_MSVC_AT_LEAST(2019,0) && MPT_MSVC_BEFORE(2019,2)
#if !(defined(UNICODE) || defined(_UNICODE))
// work-around https://developercommunity.visualstudio.com/content/problem/629849/mfc-headers-conflict-with-c17-charconv-header-in-m.html
#pragma push_macro("_M2")
#undef _M2
#endif
#endif
#include <charconv>
#if MPT_MSVC_AT_LEAST(2019,0) && MPT_MSVC_BEFORE(2019,2)
#if !(defined(UNICODE) || defined(_UNICODE))
// work-around https://developercommunity.visualstudio.com/content/problem/629849/mfc-headers-conflict-with-c17-charconv-header-in-m.html
#pragma pop_macro("_M2")
#endif
#endif
#endif // MPT_FORMAT_CXX17_INT
#include <iomanip>
#include <locale>
#include <sstream>
#include <string>
#if MPT_FORMAT_CXX17_INT
#include <system_error>
#endif // MPT_FORMAT_CXX17_INT
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> inline void SaneInsert(Tstream & s, const bool & x) { s << static_cast<int>(x); }
template<typename Tstream> inline void SaneInsert(Tstream & s, const signed char & x) { s << static_cast<signed int>(x); }
template<typename Tstream> inline void SaneInsert(Tstream & s, const unsigned char & x) { s << static_cast<unsigned int>(x); }
#if MPT_FORMAT_CXX17_INT
#if MPT_WSTRING_FORMAT
static std::wstring ToWideSimple(const std::string &nstr)
{
std::wstring wstr(nstr.size(), L'\0');
for(std::size_t i = 0; i < nstr.size(); ++i)
{
wstr[i] = static_cast<unsigned char>(nstr[i]);
}
return wstr;
}
#endif // MPT_WSTRING_FORMAT
template<typename T>
static inline std::string ToChars(const T & x, int base = 10)
{
std::string str(1, '\0');
bool done = false;
while(!done)
{
std::to_chars_result result = std::to_chars(str.data(), str.data() + str.size(), x, base);
if(result.ec != std::errc{})
{
str.resize(Util::ExponentialGrow(str.size()), '\0');
} else
{
str.resize(result.ptr - str.data());
done = true;
}
}
return str;
}
template<typename T>
static inline std::string ToStringHelperInt(const T & x)
{
return ToChars(x);
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring ToWStringHelperInt(const T & x)
{
return ToWideSimple(ToChars(x));
}
#endif
#else // !MPT_FORMAT_CXX17_INT
template<typename T>
static inline std::string ToStringHelperInt(const T & x)
{
std::ostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring ToWStringHelperInt(const T & x)
{
std::wostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#endif
#endif // MPT_FORMAT_CXX17_INT
template<typename T>
static inline std::string ToStringHelperFloat(const T & x)
{
std::ostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring ToWStringHelperFloat(const T & x)
{
std::wostringstream o;
o.imbue(std::locale::classic());
SaneInsert(o, x);
return o.str();
}
#endif
std::string ToString(const bool & x) { return ToStringHelperInt(static_cast<int>(x)); }
std::string ToString(const signed char & x) { return ToStringHelperInt(x); }
std::string ToString(const unsigned char & x) { return ToStringHelperInt(x); }
std::string ToString(const signed short & x) { return ToStringHelperInt(x); }
std::string ToString(const unsigned short & x) { return ToStringHelperInt(x); }
std::string ToString(const signed int & x) { return ToStringHelperInt(x); }
std::string ToString(const unsigned int & x) { return ToStringHelperInt(x); }
std::string ToString(const signed long & x) { return ToStringHelperInt(x); }
std::string ToString(const unsigned long & x) { return ToStringHelperInt(x); }
std::string ToString(const signed long long & x) { return ToStringHelperInt(x); }
std::string ToString(const unsigned long long & x) { return ToStringHelperInt(x); }
std::string ToString(const float & x) { return ToStringHelperFloat(x); }
std::string ToString(const double & x) { return ToStringHelperFloat(x); }
std::string ToString(const long double & x) { return ToStringHelperFloat(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); }
#endif
#if defined(MPT_WITH_MFC)
mpt::ustring ToUString(const CString & x) { return mpt::ToUnicode(x); }
#endif // MPT_WITH_MFC
#if MPT_USTRING_MODE_WIDE
mpt::ustring ToUString(const bool & x) { return ToWStringHelperInt(static_cast<int>(x)); }
mpt::ustring ToUString(const signed char & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const unsigned char & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const signed short & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const unsigned short & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const signed int & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const unsigned int & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const signed long & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const unsigned long & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const signed long long & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const unsigned long long & x) { return ToWStringHelperInt(x); }
mpt::ustring ToUString(const float & x) { return ToWStringHelperFloat(x); }
mpt::ustring ToUString(const double & x) { return ToWStringHelperFloat(x); }
mpt::ustring ToUString(const long double & x) { return ToWStringHelperFloat(x); }
#endif
#if MPT_USTRING_MODE_UTF8
mpt::ustring ToUString(const bool & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(static_cast<int>(x))); }
mpt::ustring ToUString(const signed char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const unsigned char & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const signed short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const unsigned short & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const signed int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const unsigned int & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const signed long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const unsigned long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const signed long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const unsigned long long & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperInt(x)); }
mpt::ustring ToUString(const float & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); }
mpt::ustring ToUString(const double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); }
mpt::ustring ToUString(const long double & x) { return mpt::ToUnicode(mpt::Charset::UTF8, ToStringHelperFloat(x)); }
#endif
#if MPT_WSTRING_FORMAT
#if MPT_USTRING_MODE_UTF8
std::wstring ToWString(const mpt::ustring & x) { return mpt::ToWide(x); }
#endif
#if defined(MPT_WITH_MFC)
std::wstring ToWString(const CString & x) { return mpt::ToWide(x); }
#endif // MPT_WITH_MFC
std::wstring ToWString(const bool & x) { return ToWStringHelperInt(static_cast<int>(x)); }
std::wstring ToWString(const signed char & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const unsigned char & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const signed short & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const unsigned short & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const signed int & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const unsigned int & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const signed long & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const unsigned long & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const signed long long & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const unsigned long long & x) { return ToWStringHelperInt(x); }
std::wstring ToWString(const float & x) { return ToWStringHelperFloat(x); }
std::wstring ToWString(const double & x) { return ToWStringHelperFloat(x); }
std::wstring ToWString(const long double & x) { return ToWStringHelperFloat(x); }
#endif
template <typename Tchar>
struct NumPunct : std::numpunct<Tchar>
{
private:
unsigned int group;
char sep;
public:
NumPunct(unsigned int g, char s)
: group(g)
, sep(s)
{}
std::string do_grouping() const override
{
return std::string(1, static_cast<char>(group));
}
Tchar do_thousands_sep() const override
{
return static_cast<Tchar>(sep);
}
};
template<typename Tostream, typename T>
static inline void ApplyFormat(Tostream & o, const FormatSpec & format, const T &)
{
MPT_MAYBE_CONSTANT_IF(!std::numeric_limits<T>::is_integer)
{
if(format.GetGroup() > 0)
{
o.imbue(std::locale(o.getloc(), new NumPunct<typename Tostream::char_type>(format.GetGroup(), format.GetGroupSep())));
}
}
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; }
MPT_MAYBE_CONSTANT_IF(!std::numeric_limits<T>::is_integer)
{
if(f & fmt_base::FillOff) { /* nothing */ }
else if(f & fmt_base::FillNul) { o << std::setw(width) << std::setfill(typename Tostream::char_type('0')); }
}
if(precision != -1) { o << std::setprecision(precision); }
}
template<typename Tstring>
static inline Tstring PostProcessCase(Tstring str, const FormatSpec & format)
{
FormatFlags f = format.GetFlags();
if(f & fmt_base::CaseUpp)
{
for(auto & c : str)
{
if('a' <= c && c <= 'z')
{
c -= 'a' - 'A';
}
}
}
return str;
}
template<typename Tstring>
static inline Tstring PostProcessDigits(Tstring str, const FormatSpec & format)
{
FormatFlags f = format.GetFlags();
std::size_t width = format.GetWidth();
if(f & fmt_base::FillNul)
{
auto pos = str.begin();
if(str.length() > 0)
{
if(str[0] == typename Tstring::value_type('+'))
{
pos++;
width++;
} else if(str[0] == typename Tstring::value_type('-'))
{
pos++;
width++;
}
}
if(str.length() < width)
{
str.insert(pos, width - str.length(), '0');
}
}
return str;
}
template<typename Tstring>
static inline Tstring PostProcessGroup(Tstring str, const FormatSpec & format)
{
if(format.GetGroup() > 0)
{
const unsigned int groupSize = format.GetGroup();
const char groupSep = format.GetGroupSep();
std::size_t len = str.length();
for(std::size_t n = 0; n < len; ++n)
{
if(n > 0 && (n % groupSize) == 0)
{
if(!(n == (len - 1) && (str[0] == typename Tstring::value_type('+') || str[0] == typename Tstring::value_type('-'))))
{
str.insert(str.begin() + (len - n), 1, groupSep);
}
}
}
}
return str;
}
#if MPT_FORMAT_CXX17_INT
template<typename T>
static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f)
{
int base = 10;
if(f.GetFlags() & fmt_base::BaseDec) { base = 10; }
if(f.GetFlags() & fmt_base::BaseHex) { base = 16; }
return PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f);
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f)
{
int base = 10;
if(f.GetFlags() & fmt_base::BaseDec) { base = 10; }
if(f.GetFlags() & fmt_base::BaseHex) { base = 16; }
return ToWideSimple(PostProcessGroup(PostProcessDigits(PostProcessCase(ToChars(x, base), f), f), f));
}
#endif
#else // !MPT_FORMAT_CXX17_INT
template<typename T>
static inline std::string FormatValHelperInt(const T & x, const FormatSpec & f)
{
MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed<T>::value)
{
if(x == std::numeric_limits<T>::min())
{
return std::string(1, '-') + FormatValHelperInt(static_cast<typename std::make_unsigned<T>::type>(x), f);
} else MPT_MAYBE_CONSTANT_IF(x < 0)
{
return std::string(1, '-') + FormatValHelperInt(static_cast<typename std::make_unsigned<T>::type>(0-x), f);
} else
{
return FormatValHelperInt(static_cast<typename std::make_unsigned<T>::type>(x), f);
}
}
std::ostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f, x);
SaneInsert(o, x);
return PostProcessGroup(PostProcessDigits(o.str(), f), f);
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring FormatValWHelperInt(const T & x, const FormatSpec & f)
{
MPT_MAYBE_CONSTANT_IF((f.GetFlags() & fmt_base::BaseHex) && std::is_signed<T>::value)
{
if(x == std::numeric_limits<T>::min())
{
return std::wstring(1, L'-') + FormatValWHelperInt(static_cast<typename std::make_unsigned<T>::type>(x), f);
} else MPT_MAYBE_CONSTANT_IF(x < 0)
{
return std::wstring(1, L'-') + FormatValWHelperInt(static_cast<typename std::make_unsigned<T>::type>(0-x), f);
} else
{
return FormatValWHelperInt(static_cast<typename std::make_unsigned<T>::type>(x), f);
}
}
std::wostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f, x);
SaneInsert(o, x);
return PostProcessGroup(PostProcessDigits(o.str(), f), f);
}
#endif
#endif // MPT_FORMAT_CXX17_INT
template<typename T>
static inline std::string FormatValHelperFloat(const T & x, const FormatSpec & f)
{
std::ostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f, x);
SaneInsert(o, x);
return o.str();
}
#if MPT_WSTRING_FORMAT
template<typename T>
static inline std::wstring FormatValWHelperFloat(const T & x, const FormatSpec & f)
{
std::wostringstream o;
o.imbue(std::locale::classic());
ApplyFormat(o, f, x);
SaneInsert(o, x);
return o.str();
}
#endif
std::string FormatVal(const bool & x, const FormatSpec & f) { return FormatValHelperInt(static_cast<int>(x), f); }
std::string FormatVal(const signed char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const unsigned char & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const signed short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const unsigned short & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const signed int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const unsigned int & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const signed long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const unsigned long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const signed long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const unsigned long long & x, const FormatSpec & f) { return FormatValHelperInt(x, f); }
std::string FormatVal(const float & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); }
std::string FormatVal(const double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); }
std::string FormatVal(const long double & x, const FormatSpec & f) { return FormatValHelperFloat(x, f); }
#if MPT_USTRING_MODE_WIDE
mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast<int>(x), f); }
mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
#endif
#if MPT_USTRING_MODE_UTF8
mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(static_cast<int>(x), f)); }
mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperInt(x, f)); }
mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); }
mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); }
mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::ToUnicode(mpt::Charset::UTF8, FormatValHelperFloat(x, f)); }
#endif
#if MPT_WSTRING_FORMAT
std::wstring FormatValW(const bool & x, const FormatSpec & f) { return FormatValWHelperInt(static_cast<int>(x), f); }
std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return FormatValWHelperInt(x, f); }
std::wstring FormatValW(const float & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
std::wstring FormatValW(const double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
std::wstring FormatValW(const long double & x, const FormatSpec & f) { return FormatValWHelperFloat(x, f); }
#endif
} // namespace mpt
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,737 @@
/*
* 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 "BuildSettings.h"
#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.
// 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 mpt::tsrtring CString
// mpt::fmt mpt::wfmt mpt::ufmt mpt::tfmt mpt::cfmt
// mpt::format("%1") mpt::wformat(L"%1") mpt::uformat(MPT_ULITERAL(%1) mpt::tformat(_T("%1")) mpt::cformat(_T("%1"))
// mpt::format("%1") mpt::format(L"%1") mpt::format(MPT_USTRING(%1)) mpt::format(mpt::tstring(_T("%1")) mpt::format(CString(_T("%1"))
// 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::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
{
// ToUString() 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 ToUString().
// fallback to member function ToUString()
#if MPT_USTRING_MODE_UTF8
template <typename T> auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); }
#else
template <typename T> auto ToString(const T & x) -> decltype(mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString())) { return mpt::ToCharset(mpt::CharsetLocaleOrUTF8, x.ToUString()); }
#endif
inline std::string ToString(const std::string & x) { return x; }
inline std::string ToString(const char * const & x) { return x; }
std::string ToString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if MPT_WSTRING_FORMAT
std::string ToString(const std::wstring & x) = delete; // Unknown encoding.
std::string ToString(const wchar_t * const & x) = delete; // Unknown encoding.
std::string ToString(const wchar_t &x ) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif
#if MPT_USTRING_MODE_UTF8
std::string ToString(const mpt::ustring & x) = delete; // Unknown encoding.
#endif
#if defined(MPT_WITH_MFC)
std::string ToString(const CString & x) = delete; // unknown encoding
#endif // MPT_WITH_MFC
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(); }
inline mpt::ustring ToUString(const mpt::ustring & x) { return x; }
mpt::ustring ToUString(const std::string & x) = delete; // Unknown encoding.
mpt::ustring ToUString(const char * const & x) = delete; // 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::ustring ToUString(const char & x) = delete; // 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::ustring ToUString(const wchar_t & x) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif
#if defined(MPT_WITH_MFC)
mpt::ustring ToUString(const CString & x);
#endif // MPT_WITH_MFC
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
std::wstring ToWString(const std::string & x) = delete; // Unknown encoding.
std::wstring ToWString(const char * const & x) = delete; // 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.
std::wstring ToWString(const char & x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
inline std::wstring ToWString(const std::wstring & x) { return x; }
inline std::wstring ToWString(const wchar_t * const & x) { return x; }
std::wstring ToWString(const wchar_t & x) = delete; // 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(MPT_WITH_MFC)
std::wstring ToWString(const CString & x);
#endif // MPT_WITH_MFC
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);
// fallback to member function ToUString()
template <typename T> auto ToWString(const T & x) -> decltype(mpt::ToWide(x.ToUString())) { return mpt::ToWide(x.ToUString()); }
#endif
#if defined(MPT_ENABLE_CHARSET_LOCALE)
template <typename T> struct ToLocaleHelper { mpt::lstring operator () (const T & v) { return mpt::ToLocale(ToUString(v)); } };
template <> struct ToLocaleHelper<mpt::lstring> { mpt::lstring operator () (const mpt::lstring & v) { return v; } };
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
template <typename T> struct ToCStringHelper { CString operator () (const T & v) { return mpt::ToCString(ToUString(v)); } };
template <> struct ToCStringHelper<CString> { CString operator () (const CString & v) { return v; } };
#endif // MPT_WITH_MFC
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(MPT_ENABLE_CHARSET_LOCALE)
template <> struct ToStringTFunctor<mpt::lstring> { template <typename T> inline mpt::lstring operator() (const T & x) { return mpt::ToLocaleHelper<T>()(x); } };
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
template <> struct ToStringTFunctor<CString> { template <typename T> inline CString operator() (const T & x) { return mpt::ToCStringHelper<T>()(x); } };
#endif // MPT_WITH_MFC
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
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;
std::string FormatVal(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
std::string FormatVal(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
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);
mpt::ustring FormatValU(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
mpt::ustring FormatValU(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
mpt::ustring FormatValU(const bool & x, const FormatSpec & f);
mpt::ustring FormatValU(const signed char & x, const FormatSpec & f);
mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f);
mpt::ustring FormatValU(const signed short & x, const FormatSpec & f);
mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f);
mpt::ustring FormatValU(const signed int & x, const FormatSpec & f);
mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f);
mpt::ustring FormatValU(const signed long & x, const FormatSpec & f);
mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f);
mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f);
mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f);
mpt::ustring FormatValU(const float & x, const FormatSpec & f);
mpt::ustring FormatValU(const double & x, const FormatSpec & f);
mpt::ustring FormatValU(const long double & x, const FormatSpec & f);
#if MPT_WSTRING_FORMAT
std::wstring FormatValW(const char & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
std::wstring FormatValW(const wchar_t & x, const FormatSpec & f) = delete; // deprecated to catch potential API mis-use, use std::wstring(1, x) instead
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
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); } };
template <> struct FormatValTFunctor<mpt::ustring> { template <typename T> inline mpt::ustring operator() (const T & x, const FormatSpec & f) { return FormatValU(x, f); } };
#if MPT_USTRING_MODE_UTF8 && 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 defined(MPT_ENABLE_CHARSET_LOCALE)
template <> struct FormatValTFunctor<mpt::lstring> { template <typename T> inline mpt::lstring operator() (const T & x, const FormatSpec & f) { return mpt::ToLocale(mpt::Charset::Locale, FormatVal(x, f)); } };
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC )
#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 // !UNICODE
template <> struct FormatValTFunctor<CString> { template <typename T> inline CString operator() (const T & x, const FormatSpec & f) { return mpt::ToCString(mpt::Charset::Locale, FormatVal(x, f)); } };
#endif // UNICODE
#endif // MPT_WITH_MFC
class FormatSpec
{
private:
FormatFlags flags;
std::size_t width;
int precision;
unsigned int group;
char group_sep;
public:
MPT_CONSTEXPR11_FUN FormatSpec() noexcept : flags(0), width(0), precision(-1), group(0), group_sep(',') {}
MPT_CONSTEXPR11_FUN FormatFlags GetFlags() const noexcept { return flags; }
MPT_CONSTEXPR11_FUN std::size_t GetWidth() const noexcept { return width; }
MPT_CONSTEXPR11_FUN int GetPrecision() const noexcept { return precision; }
MPT_CONSTEXPR11_FUN unsigned int GetGroup() const noexcept { return group; }
MPT_CONSTEXPR11_FUN char GetGroupSep() const noexcept { return group_sep; }
MPT_CONSTEXPR14_FUN FormatSpec & SetFlags(FormatFlags f) noexcept { flags = f; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & SetWidth(std::size_t w) noexcept { width = w; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & SetPrecision(int p) noexcept { precision = p; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & SetGroup(unsigned int g) noexcept { group = g; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & SetGroupSep(char s) noexcept { group_sep = s; return *this; }
public:
MPT_CONSTEXPR14_FUN FormatSpec & BaseDec() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseDec; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & BaseHex() noexcept { flags &= ~(fmt_base::BaseDec|fmt_base::BaseHex); flags |= fmt_base::BaseHex; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & CaseLow() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseLow; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & CaseUpp() noexcept { flags &= ~(fmt_base::CaseLow|fmt_base::CaseUpp); flags |= fmt_base::CaseUpp; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & FillOff() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillOff; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & FillNul() noexcept { flags &= ~(fmt_base::FillOff|fmt_base::FillNul); flags |= fmt_base::FillNul; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & NotaNrm() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaNrm; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & NotaFix() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaFix; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & NotaSci() noexcept { flags &= ~(fmt_base::NotaNrm|fmt_base::NotaFix|fmt_base::NotaSci); flags |= fmt_base::NotaSci; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & Width(std::size_t w) noexcept { width = w; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & Prec(int p) noexcept { precision = p; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & Group(unsigned int g) noexcept { group = g; return *this; }
MPT_CONSTEXPR14_FUN FormatSpec & GroupSep(char s) noexcept { group_sep = s; return *this; }
public:
MPT_CONSTEXPR14_FUN FormatSpec & Dec() noexcept { return BaseDec(); }
MPT_CONSTEXPR14_FUN FormatSpec & Hex() noexcept { return BaseHex(); }
MPT_CONSTEXPR14_FUN FormatSpec & Low() noexcept { return CaseLow(); }
MPT_CONSTEXPR14_FUN FormatSpec & Upp() noexcept { return CaseUpp(); }
MPT_CONSTEXPR14_FUN FormatSpec & Off() noexcept { return FillOff(); }
MPT_CONSTEXPR14_FUN FormatSpec & Nul() noexcept { return FillNul(); }
MPT_CONSTEXPR14_FUN FormatSpec & Nrm() noexcept { return NotaNrm(); }
MPT_CONSTEXPR14_FUN FormatSpec & Fix() noexcept { return NotaFix(); }
MPT_CONSTEXPR14_FUN FormatSpec & Sci() noexcept { return NotaSci(); }
public:
MPT_CONSTEXPR14_FUN FormatSpec & Decimal() noexcept { return BaseDec(); }
MPT_CONSTEXPR14_FUN FormatSpec & Hexadecimal() noexcept { return BaseHex(); }
MPT_CONSTEXPR14_FUN FormatSpec & Lower() noexcept { return CaseLow(); }
MPT_CONSTEXPR14_FUN FormatSpec & Upper() noexcept { return CaseUpp(); }
MPT_CONSTEXPR14_FUN FormatSpec & FillNone() noexcept { return FillOff(); }
MPT_CONSTEXPR14_FUN FormatSpec & FillZero() noexcept { return FillNul(); }
MPT_CONSTEXPR14_FUN FormatSpec & FloatNormal() noexcept { return NotaNrm(); }
MPT_CONSTEXPR14_FUN FormatSpec & FloatFixed() noexcept { return NotaFix(); }
MPT_CONSTEXPR14_FUN FormatSpec & FloatScientific() noexcept { return NotaSci(); }
MPT_CONSTEXPR14_FUN FormatSpec & Precision(int p) noexcept { return Prec(p); }
};
template <typename Tdst, typename Tsrc>
struct pointer_cast_helper
{
Tdst operator()(const Tsrc & src) const { return src; }
};
template <typename Tdst, typename Tptr>
struct pointer_cast_helper<Tdst, const Tptr*>
{
Tdst operator()(const Tptr * const & src) const { return reinterpret_cast<const Tdst>(src); }
};
template <typename Tdst, typename Tptr>
struct pointer_cast_helper<Tdst, Tptr*>
{
Tdst operator()(const Tptr * const & src) const { return reinterpret_cast<const Tdst>(src); }
};
template <typename Tdst, typename Tsrc>
Tdst pointer_cast(const Tsrc & src)
{
return pointer_cast_helper<Tdst, Tsrc>()(src);
}
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 fmt(const T& x, const FormatSpec& f)
{
return FormatValTFunctor<Tstring>()(x, f);
}
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 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 dec(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillOff().Group(g).GroupSep(s));
}
template<int width, typename T>
static inline Tstring dec0(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseDec().FillNul().Width(width).Group(g).GroupSep(s));
}
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 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 hex(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillOff().Group(g).GroupSep(s));
}
template<typename T>
static inline Tstring HEX(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillOff().Group(g).GroupSep(s));
}
template<int width, typename T>
static inline Tstring hex0(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseLow().FillNul().Width(width).Group(g).GroupSep(s));
}
template<int width, typename T>
static inline Tstring HEX0(unsigned int g, char s, const T& x)
{
static_assert(std::numeric_limits<T>::is_integer);
return FormatValTFunctor<Tstring>()(x, FormatSpec().BaseHex().CaseUpp().FillNul().Width(width).Group(g).GroupSep(s));
}
template<typename T>
static inline Tstring flt(const T& x, int precision = -1)
{
static_assert(std::is_floating_point<T>::value);
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaNrm().FillOff().Precision(precision));
}
template<typename T>
static inline Tstring fix(const T& x, int precision = -1)
{
static_assert(std::is_floating_point<T>::value);
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaFix().FillOff().Precision(precision));
}
template<typename T>
static inline Tstring sci(const T& x, int precision = -1)
{
static_assert(std::is_floating_point<T>::value);
return FormatValTFunctor<Tstring>()(x, FormatSpec().NotaSci().FillOff().Precision(precision));
}
template<typename T>
static inline Tstring ptr(const T& x)
{
static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
return hex0<mpt::pointer_size * 2>(pointer_cast<const std::uintptr_t>(x));
}
template<typename T>
static inline Tstring PTR(const T& x)
{
static_assert(std::is_pointer<T>::value || std::is_same<T, std::uintptr_t>::value || std::is_same<T, std::intptr_t>::value, "");
return HEX0<mpt::pointer_size * 2>(pointer_cast<const std::uintptr_t>(x));
}
static inline Tstring pad_left(std::size_t width_, const Tstring &str)
{
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return traits::pad(str, width, 0);
}
static inline Tstring pad_right(std::size_t width_, const Tstring &str)
{
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return traits::pad(str, 0, width);
}
static inline Tstring left(std::size_t width_, const Tstring &str)
{
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, 0, width - traits::length(str)) : str;
}
static inline Tstring right(std::size_t width_, const Tstring &str)
{
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, width - traits::length(str), 0) : str;
}
static inline Tstring center(std::size_t width_, const Tstring &str)
{
typedef mpt::string_traits<Tstring> traits;
typename traits::size_type width = static_cast<typename traits::size_type>(width_);
return (traits::length(str) < width) ? traits::pad(str, (width - traits::length(str)) / 2, (width - traits::length(str) + 1) / 2) : str;
}
}; // 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(MPT_ENABLE_CHARSET_LOCALE)
typedef fmtT<mpt::lstring> lfmt;
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
typedef fmtT<mpt::tstring> tfmt;
#endif
#if defined(MPT_WITH_MFC)
typedef fmtT<CString> cfmt;
#endif // MPT_WITH_MFC
} // 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; };
#if !defined(MPT_COMPILER_QUIRK_NO_WCHAR)
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; };
#endif // !MPT_COMPILER_QUIRK_NO_WCHAR
#if MPT_USTRING_MODE_UTF8
template <> struct to_string_type<mpt::ustring > { typedef mpt::ustring type; };
#endif
#if defined(MPT_ENABLE_CHARSET_LOCALE)
template <> struct to_string_type<mpt::lstring > { typedef mpt::lstring type; };
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
template <> struct to_string_type<CString > { typedef CString type; };
#endif // MPT_WITH_MFC
template <typename T, std::size_t N> struct to_string_type<T [N]> { typedef typename to_string_type<T>::type type; };
} // namespace detail
} // namespace String
template<typename Tformat>
class message_formatter
{
public:
typedef typename mpt::String::detail::to_string_type<Tformat>::type Tstring;
private:
Tstring format;
private:
MPT_NOINLINE Tstring do_format(mpt::span<const Tstring> vals) const
{
typedef typename mpt::string_traits<Tstring> traits;
Tstring result;
const typename traits::size_type len = traits::length(format);
traits::reserve(result, len);
for(typename traits::size_type pos = 0; pos != len; ++pos)
{
typename traits::char_type c = format[pos];
if(pos + 1 != len && c == typename traits::char_type('%'))
{
pos++;
c = format[pos];
if(typename traits::char_type('1') <= c && c <= typename traits::char_type('9'))
{
const std::size_t n = c - typename traits::char_type('0') - 1;
if(n < std::size(vals))
{
traits::append(result, vals[n]);
}
continue;
} else if(c != typename traits::char_type('%'))
{
traits::append(result, 1, typename traits::char_type('%'));
}
}
traits::append(result, 1, c);
}
return result;
}
public:
message_formatter(Tstring format_)
: format(std::move(format_))
{
}
public:
template<typename ...Ts>
Tstring operator() (const Ts&... xs) const
{
const std::array<Tstring, sizeof...(xs)> vals{{ToStringTFunctor<Tstring>()(xs)...}};
return do_format(mpt::as_span(vals));
}
}; // struct message_formatter<Tformat>
template<typename Tformat>
message_formatter<typename mpt::String::detail::to_string_type<Tformat>::type> format(Tformat format)
{
typedef typename mpt::String::detail::to_string_type<Tformat>::type Tstring;
return message_formatter<Tstring>(Tstring(std::move(format)));
}
#if MPT_WSTRING_FORMAT
static inline message_formatter<std::wstring> wformat(std::wstring format)
{
return message_formatter<std::wstring>(std::move(format));
}
#endif
static inline message_formatter<mpt::ustring> uformat(mpt::ustring format)
{
return message_formatter<mpt::ustring>(std::move(format));
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
static inline message_formatter<mpt::lstring> lformat(mpt::lstring format)
{
return message_formatter<mpt::lstring>(std::move(format));
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
static inline message_formatter<mpt::tstring> tformat(mpt::tstring format)
{
return message_formatter<mpt::tstring>(std::move(format));
}
#endif
#if defined(MPT_WITH_MFC)
static inline message_formatter<CString> cformat(CString format)
{
return message_formatter<CString>(std::move(format));
}
#endif // MPT_WITH_MFC
} // namespace mpt
namespace mpt { namespace String {
// Combine a vector of values into a string, separated with the given separator.
// No escaping is performed.
template<typename T>
mpt::ustring Combine(const std::vector<T> &vals, const mpt::ustring &sep=U_(","))
{
mpt::ustring str;
for(std::size_t i = 0; i < vals.size(); ++i)
{
if(i > 0)
{
str += sep;
}
str += mpt::ufmt::val(vals[i]);
}
return str;
}
template<typename T>
std::string Combine(const std::vector<T> &vals, const std::string &sep=std::string(","))
{
std::string str;
for(std::size_t i = 0; i < vals.size(); ++i)
{
if(i > 0)
{
str += sep;
}
str += mpt::fmt::val(vals[i]);
}
return str;
}
} } // namespace mpt::String
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,250 @@
/*
* 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
#include "BuildSettings.h"
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(MPT_WITH_MFC)
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::Charset::UTF8, str));
#else // !UNICODE
return ConvertStrTo<T>(mpt::ToCharset(mpt::Charset::Locale, str));
#endif // UNICODE
}
#endif // MPT_WITH_MFC
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::Charset::UTF8, 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
#if defined(MPT_ENABLE_CHARSET_LOCALE)
template<typename T>
inline T ConvertStrTo(const mpt::lstring &str)
{
return ConvertStrTo<T>(mpt::ToCharset(mpt::Charset::Locale, str));
}
template<> inline mpt::lstring ConvertStrTo(const mpt::lstring &str) { return str; }
#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::Charset::UTF8, 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::Charset::UTF8, str));
}
#endif
} // namespace Parse
} // namespace String
} // namespace mpt
namespace mpt { namespace String {
// Split the given string at separator positions into individual values returned as a vector.
// An empty string results in an empty vector.
// Leading or trailing separators result in a default-constructed element being inserted before or after the other elements.
template<typename T>
std::vector<T> Split(const mpt::ustring &str, const mpt::ustring &sep=U_(","))
{
std::vector<T> vals;
std::size_t pos = 0;
while(str.find(sep, pos) != std::string::npos)
{
vals.push_back(ConvertStrTo<T>(str.substr(pos, str.find(sep, pos) - pos)));
pos = str.find(sep, pos) + sep.length();
}
if(!vals.empty() || (str.substr(pos).length() > 0))
{
vals.push_back(ConvertStrTo<T>(str.substr(pos)));
}
return vals;
}
template<typename T>
std::vector<T> Split(const std::string &str, const std::string &sep=std::string(","))
{
std::vector<T> vals;
std::size_t pos = 0;
while(str.find(sep, pos) != std::string::npos)
{
vals.push_back(ConvertStrTo<T>(str.substr(pos, str.find(sep, pos) - pos)));
pos = str.find(sep, pos) + sep.length();
}
if(!vals.empty() || (str.substr(pos).length() > 0))
{
vals.push_back(ConvertStrTo<T>(str.substr(pos)));
}
return vals;
}
} } // namespace mpt::String
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,309 @@
/*
* 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 "mptStringBuffer.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 ToUString(uint64 time100ns)
{
constexpr 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);
TCHAR buf[bufsize];
GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &systime, TEXT("yyyy-MM-dd"), buf, bufsize);
result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf)));
result.append(U_(" "));
GetTimeFormat(LOCALE_SYSTEM_DEFAULT, TIME_FORCE24HOURFORMAT, &systime, TEXT("HH:mm:ss"), buf, bufsize);
result.append(mpt::ToUnicode(mpt::String::ReadWinBuf(buf)));
result.append(U_("."));
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 = U_("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 += U_("-") + mpt::ufmt::dec0<2>(date.tm_mon + 1);
if(date.tm_mday < 1 || date.tm_mday > 31)
{
return result;
}
result += U_("-") + 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 += U_("T");
if(date.tm_isdst > 0)
{
tz = U_("+01:00");
}
result += mpt::ufmt::dec0<2>(date.tm_hour) + U_(":") + mpt::ufmt::dec0<2>(date.tm_min);
if(date.tm_sec < 0 || date.tm_sec > 61)
{
return result + tz;
}
result += U_(":") + 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 = std::clamp(mpt::saturate_cast<UINT>(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,116 @@
/*
* 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 "BuildSettings.h"
#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 ToUString(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,737 @@
/*
* 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 <sstream>
#include "misc_util.h"
OPENMPT_NAMESPACE_BEGIN
namespace srlztn
{
#ifdef MPT_ALL_LOGGING
#define SSB_LOGGING
#endif
#ifdef SSB_LOGGING
#define SSB_LOG(x) MPT_LOG(LogDebug, "serialization", 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(str.size(), static_cast<std::size_t>((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(id >> 4, uint32(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::Charset::ISO8859_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 tstrWriteHeader[] = UL_("Write header with ID = %1\n");
static const mpt::uchar tstrWriteProgress[] = UL_("Wrote entry: {num, id, rpos, size} = {%1, %2, %3, %4}\n");
static const mpt::uchar tstrWritingMap[] = UL_("Writing map to rpos: %1\n");
static const mpt::uchar tstrMapEntryWrite[] = UL_("Writing map entry: id=%1, rpos=%2, size=%3\n");
static const mpt::uchar strWriteNote[] = UL_("Write note: ");
static const mpt::uchar tstrEndOfStream[] = UL_("End of stream(rpos): %1\n");
static const mpt::uchar tstrReadingHeader[] = UL_("Read header with expected ID = %1\n");
static const mpt::uchar strNoMapInFile[] = UL_("No map in the file.\n");
static const mpt::uchar strIdMismatch[] = UL_("ID mismatch, terminating read.\n");
static const mpt::uchar strIdMatch[] = UL_("ID match, continuing reading.\n");
static const mpt::uchar tstrReadingMap[] = UL_("Reading map from rpos: %1\n");
static const mpt::uchar tstrEndOfMap[] = UL_("End of map(rpos): %1\n");
static const mpt::uchar tstrReadProgress[] = UL_("Read entry: {num, id, rpos, size, desc} = {%1, %2, %3, %4, %5}\n");
static const mpt::uchar tstrNoEntryFound[] = UL_("No entry with id %1 found.\n");
static const mpt::uchar strReadNote[] = UL_("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& os)
: oStrm(os)
, m_posEntrycount(0)
, m_posMapPosField(0)
{
return;
}
SsbRead::SsbRead(std::istream& is)
: iStrm(is)
, 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(U_("%1: 0x%2\n"))(strWriteNote, mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(mpt::format(U_("%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() : U_(""),
(pRe) ? pRe->rposStart : 0,
(pRe && pRe->nSize != invalidDatasize) ? mpt::ufmt::val(pRe->nSize) : U_(""),
U_("")));
#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() : U_(""),
rposDataStart,
nDatasize));
std::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)
{
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>(iStrm.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 = oStrm.tellp() - posBeforeWrite;
MPT_MAYBE_CONSTANT_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++)
oStrm.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)
{
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];
Clear(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;
MPT_MAYBE_CONSTANT_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()
{
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);
iStrm.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)
{
iStrm.clear();
if (GetFlag(RwfRMapCached) == false)
CacheMap();
if (m_nFixedEntrySize > 0 && GetFlag(RwfRMapHasStartpos) == false && GetFlag(RwfRMapHasSize) == false)
iStrm.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)
iStrm.seekg(m_posStart + Postype(mapData[i].rposStart));
return &mapData[i];
}
}
}
return nullptr;
}
void SsbWrite::FinishWrite()
{
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);
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);
}
// Seek to end.
oStrm.seekp(std::max(posMapEnd, posDataEnd));
SSB_LOG(mpt::format(mpt::ustring(tstrEndOfStream))(oStrm.tellp() - m_posStart));
}
} // namespace srlztn
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,553 @@
/*
* 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 "BuildSettings.h"
#include "../common/mptBaseTypes.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;
std::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;
std::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::byte_cast<const char*>(mpt::as_raw_memory(valle).data()), mpt::byte_cast<const char*>(mpt::as_raw_memory(valle).data() + 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:
enum : 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& iStrm;
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& oStrm;
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 = oStrm.tellp();
Func(oStrm, 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 = iStrm.tellg();
if (pE != nullptr || GetFlag(RwfRMapHasId) == false)
Func(iStrm, 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)
{
iStrm.clear();
if (iter->rposStart != 0)
iStrm.seekg(m_posStart + Postype(iter->rposStart));
const Postype pos = iStrm.tellg();
func(iStrm, 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,154 @@
/*
* 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 defined(MPT_WITH_MFC)
// cppcheck-suppress missingInclude
#include <afx.h> // MFC core
// cppcheck-suppress missingInclude
#include <afxwin.h> // MFC standard components
// cppcheck-suppress missingInclude
#include <afxext.h> // MFC extensions
// cppcheck-suppress missingInclude
#include <afxcmn.h> // MFC support for Windows Common Controls
// cppcheck-suppress missingInclude
#include <afxcview.h>
// cppcheck-suppress missingInclude
#include <afxdlgs.h>
#ifdef MPT_MFC_FULL
// cppcheck-suppress missingInclude
#include <afxlistctrl.h>
#endif // MPT_MFC_FULL
// cppcheck-suppress missingInclude
#include <afxole.h>
#endif // MPT_WITH_MFC
#if MPT_OS_WINDOWS
#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
// this will be available everywhere
#include "../common/mptBaseMacros.h"
// <array>
// <iterator>
// <type_traits>
// <cstddef>
// <cstdint>
#include "../common/mptBaseTypes.h"
// "mptBaseMacros.h"
// <array>
// <limits>
// <type_traits>
// <cstdint>
#include "../common/mptAssert.h"
// "mptBaseMacros.h"
#include "../common/mptBaseUtils.h"
// <algorithm>
// <bit>
// <limits>
// <numeric>
// <utility>
#include "../common/mptException.h"
// <exception>
// <new>
// <afx.h>
#include "../common/mptSpan.h"
// "mptBaseTypes.h"
// <array>
// <iterator>
#include "../common/mptMemory.h"
// "mptAssert.h"
// "mptBaseTypes.h"
// "mptSpan.h"
// <utility>
// <type_traits>
// <cstring>
#include "../common/mptAlloc.h"
// "mptBaseMacros.h"
// "mptMemory.h"
// "mptSpan.h"
// <array>
// <memory>
// <new>
// <vector>
#include "../common/mptString.h"
// <algorithm>
// <limits>
// <string>
// <string_view>
// <type_traits>
// <cstring>
#include "../common/mptStringBuffer.h"
#include "../common/mptOSError.h"
// "mptException.h"
// "mptString.h"
// <exception>
// <stdexcept>
#include "../common/mptExceptionText.h"
// "mptException.h"
// "mptString.h"
// <exception>
#include "../common/mptStringFormat.h"
#include "../common/mptPathString.h"
#include "../common/Logging.h"
// <atomic>
#include "../common/misc_util.h"
// <stdexcept>
// <vector>
// for std::abs
#include <cstdlib>
#include <stdlib.h>
#include <cmath>
#include <math.h>
//{{AFX_INSERT_LOCATION}}
// Microsoft Developer Studio will insert additional declarations immediately before the previous line.

View File

@ -0,0 +1,795 @@
/*
* 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
#define MPT_MAKE_VERSION_NUMERIC_HELPER(prefix,v0,v1,v2,v3) Version( prefix ## v0 , prefix ## v1 , prefix ## v2 , prefix ## v3 )
#define MPT_MAKE_VERSION_NUMERIC(v0,v1,v2,v3) MPT_MAKE_VERSION_NUMERIC_HELPER(0x, v0, v1, v2, v3)
#define MPT_VERSION_CURRENT MPT_MAKE_VERSION_NUMERIC(VER_MAJORMAJOR,VER_MAJOR,VER_MINOR,VER_MINORMINOR)
static_assert((MPT_VERSION_CURRENT.GetRawVersion() & 0xffffu) != 0x0000u, "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).");
Version Version::Current() noexcept
{
return MPT_VERSION_CURRENT;
}
mpt::ustring Version::GetOpenMPTVersionString() const
{
return U_("OpenMPT ") + ToUString();
}
Version Version::Parse(const mpt::ustring &s)
{
uint32 result = 0;
std::vector<mpt::ustring> numbers = mpt::String::Split<mpt::ustring>(s, U_("."));
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 Version(result);
}
mpt::ustring Version::ToUString() const
{
uint32 v = m_Version;
if(v == 0)
{
// Unknown version
return U_("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(U_("%1.%2"))(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF));
} else
{
// Full version info available
return mpt::format(U_("%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));
}
}
Version Version::WithoutTestNumber() const noexcept
{
return Version(m_Version & 0xFFFFFF00u);
}
Version Version::WithoutPatchOrTestNumbers() const noexcept
{
return Version(m_Version & 0xFFFF0000u);
}
bool Version::IsTestVersion() const noexcept
{
return (
// Legacy
(*this > MPT_V("1.17.02.54") && *this < MPT_V("1.18.02.00") && *this != MPT_V("1.18.00.00"))
||
// Test builds have non-zero VER_MINORMINOR
(*this > MPT_V("1.18.02.00") && ((m_Version & 0xFFFFFF00u) != m_Version))
);
}
namespace Source {
static mpt::ustring GetUrl()
{
#ifdef OPENMPT_VERSION_URL
return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_URL);
#else
return mpt::ustring();
#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
MPT_WARNING_STATEMENT("SVN revision unknown. Please check your build system.");
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 mpt::ustring GetSourceDate()
{
#if defined(OPENMPT_VERSION_DATE)
return mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_VERSION_DATE);
#else
return mpt::ustring();
#endif
}
} // namespace Source
SourceInfo::SourceInfo()
: m_Url(Source::GetUrl())
, m_Revision(Source::GetRevision())
, m_IsDirty(Source::IsDirty())
, m_HasMixedRevisions(Source::HasMixedRevisions())
, m_IsPackage(Source::IsPackage())
, m_Date(Source::GetSourceDate())
{
}
mpt::ustring SourceInfo::GetUrlWithRevision() const
{
if(m_Url.empty() || (m_Revision == 0))
{
return mpt::ustring();
}
return m_Url + UL_("@") + mpt::ufmt::val(m_Revision);
}
mpt::ustring SourceInfo::GetStateString() const
{
mpt::ustring retval;
if(m_IsDirty)
{
retval += UL_("+dirty");
}
if(m_HasMixedRevisions)
{
retval += UL_("+mixed");
}
if(retval.empty())
{
retval += UL_("clean");
}
if(m_IsPackage)
{
retval += UL_("-pkg");
}
return retval;
}
SourceInfo SourceInfo::Current()
{
return SourceInfo();
}
namespace Build {
bool IsReleasedBuild()
{
return !(Version::Current().IsTestVersion() || IsDebugBuild() || Source::IsDirty() || Source::HasMixedRevisions());
}
bool IsDebugBuild()
{
#if defined(MPT_BUILD_DEBUG) || defined(DEBUG) || defined(_DEBUG)
return true;
#else
return false;
#endif
}
mpt::ustring GetBuildDateString()
{
mpt::ustring result;
#ifdef MODPLUG_TRACKER
#if defined(OPENMPT_BUILD_DATE)
result = mpt::ToUnicode(mpt::Charset::ASCII, OPENMPT_BUILD_DATE );
#else
result = mpt::ToUnicode(mpt::Charset::ASCII, __DATE__ " " __TIME__ );
#endif
#else // !MODPLUG_TRACKER
result = SourceInfo::Current().Date();
#endif // MODPLUG_TRACKER
return result;
}
static mpt::ustring GetBuildFlagsString()
{
mpt::ustring retval;
#ifdef MODPLUG_TRACKER
if(Version::Current().IsTestVersion())
{
retval += UL_(" TEST");
}
#endif // MODPLUG_TRACKER
if(IsDebugBuild())
{
retval += UL_(" DEBUG");
}
return retval;
}
mpt::ustring GetBuildFeaturesString()
{
mpt::ustring retval;
#ifdef LIBOPENMPT_BUILD
retval = UL_("")
#if defined(MPT_WITH_ZLIB)
UL_(" +ZLIB")
#endif
#if defined(MPT_WITH_MINIZ)
UL_(" +MINIZ")
#endif
#if !defined(MPT_WITH_ZLIB) && !defined(MPT_WITH_MINIZ)
UL_(" -INFLATE")
#endif
#if defined(MPT_WITH_MPG123)
UL_(" +MPG123")
#endif
#if defined(MPT_WITH_MINIMP3)
UL_(" +MINIMP3")
#endif
#if defined(MPT_WITH_MEDIAFOUNDATION)
UL_(" +MF")
#endif
#if !defined(MPT_WITH_MPG123) && !defined(MPT_WITH_MINIMP3) && !defined(MPT_WITH_MEDIAFOUNDATION)
UL_(" -MP3")
#endif
#if defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)
UL_(" +VORBIS")
#endif
#if defined(MPT_WITH_STBVORBIS)
UL_(" +STBVORBIS")
#endif
#if !(defined(MPT_WITH_OGG) && defined(MPT_WITH_VORBIS) && defined(MPT_WITH_VORBISFILE)) && !defined(MPT_WITH_STBVORBIS)
UL_(" -VORBIS")
#endif
#if !defined(NO_PLUGINS)
UL_(" +PLUGINS")
#else
UL_(" -PLUGINS")
#endif
#if defined(MPT_WITH_DMO)
UL_(" +DMO")
#endif
;
#endif
#ifdef MODPLUG_TRACKER
retval += UL_("")
#if defined(UNICODE)
UL_(" UNICODE")
#else
UL_(" ANSI")
#endif
#ifdef NO_VST
UL_(" NO_VST")
#endif
#ifndef MPT_WITH_DMO
UL_(" NO_DMO")
#endif
#ifdef NO_PLUGINS
UL_(" NO_PLUGINS")
#endif
#ifndef MPT_WITH_ASIO
UL_(" NO_ASIO")
#endif
;
#endif
return retval;
}
mpt::ustring GetBuildCompilerString()
{
mpt::ustring retval;
#if MPT_COMPILER_GENERIC
retval += U_("Generic C++11 Compiler");
#elif MPT_COMPILER_MSVC
#if defined(_MSC_FULL_VER) && defined(_MSC_BUILD) && (_MSC_BUILD > 0)
retval += mpt::format(U_("Microsoft Compiler %1.%2.%3.%4"))
( _MSC_FULL_VER / 10000000
, mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100)
, mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000)
, mpt::ufmt::dec0<2>(_MSC_BUILD)
);
#elif defined(_MSC_FULL_VER)
retval += mpt::format(U_("Microsoft Compiler %1.%2.%3"))
( _MSC_FULL_VER / 10000000
, mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100)
, mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000)
);
#else
retval += mpt::format(U_("Microsoft Compiler %1.%2"))(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100);
#endif
#elif MPT_COMPILER_GCC
retval += mpt::format(U_("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(U_("Clang %1.%2.%3"))(MPT_COMPILER_CLANG_VERSION / 10000, (MPT_COMPILER_CLANG_VERSION / 100) % 100, MPT_COMPILER_CLANG_VERSION % 100);
#else
retval += U_("unknown");
#endif
return retval;
}
static mpt::ustring GetRevisionString()
{
mpt::ustring result;
if(Source::GetRevision() == 0)
{
return result;
}
result = U_("-r") + mpt::ufmt::val(Source::GetRevision());
if(Source::HasMixedRevisions())
{
result += UL_("!");
}
if(Source::IsDirty())
{
result += UL_("+");
}
if(Source::IsPackage())
{
result += UL_("p");
}
return result;
}
mpt::ustring GetVersionString(FlagSet<Build::Strings> strings)
{
std::vector<mpt::ustring> result;
if(strings[StringVersion])
{
result.push_back(mpt::ufmt::val(Version::Current()));
}
if(strings[StringRevision])
{
if(!IsReleasedBuild())
{
result.push_back(GetRevisionString());
}
}
if(strings[StringBitness])
{
result.push_back(mpt::format(U_(" %1 bit"))(mpt::arch_bits));
}
if(strings[StringSourceInfo])
{
const SourceInfo sourceInfo = SourceInfo::Current();
if(!sourceInfo.GetUrlWithRevision().empty())
{
result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetUrlWithRevision()));
}
if(!sourceInfo.Date().empty())
{
result.push_back(mpt::format(U_(" (%1)"))(sourceInfo.Date()));
}
if(!sourceInfo.GetStateString().empty())
{
result.push_back(mpt::format(U_(" %1"))(sourceInfo.GetStateString()));
}
}
if(strings[StringBuildFlags])
{
if(!IsReleasedBuild())
{
result.push_back(GetBuildFlagsString());
}
}
if(strings[StringBuildFeatures])
{
result.push_back(GetBuildFeaturesString());
}
return mpt::String::Trim(mpt::String::Combine<mpt::ustring>(result, U_("")));
}
mpt::ustring GetVersionStringPure()
{
FlagSet<Build::Strings> strings;
strings |= Build::StringVersion;
strings |= Build::StringRevision;
#ifdef MODPLUG_TRACKER
strings |= Build::StringBitness;
#endif
return GetVersionString(strings);
}
mpt::ustring GetVersionStringSimple()
{
FlagSet<Build::Strings> strings;
strings |= Build::StringVersion;
strings |= Build::StringRevision;
strings |= Build::StringBuildFlags;
return GetVersionString(strings);
}
mpt::ustring GetVersionStringExtended()
{
FlagSet<Build::Strings> strings;
strings |= Build::StringVersion;
strings |= Build::StringRevision;
#ifdef MODPLUG_TRACKER
strings |= Build::StringBitness;
#endif
#ifndef MODPLUG_TRACKER
strings |= Build::StringSourceInfo;
#endif
strings |= Build::StringBuildFlags;
#ifdef MODPLUG_TRACKER
strings |= Build::StringBuildFeatures;
#endif
return GetVersionString(strings);
}
mpt::ustring GetURL(Build::Url key)
{
mpt::ustring result;
switch(key)
{
case Url::Website:
#ifdef LIBOPENMPT_BUILD
result = U_("https://lib.openmpt.org/");
#else
result = U_("https://openmpt.org/");
#endif
break;
case Url::Download:
#ifdef MODPLUG_TRACKER
result = IsReleasedBuild() ? U_("https://openmpt.org/download") : U_("https://builds.openmpt.org/builds/");
#else
result = U_("https://lib.openmpt.org/libopenmpt/download/");
#endif
break;
case Url::Forum:
result = U_("https://forum.openmpt.org/");
break;
case Url::Bugtracker:
result = U_("https://bugs.openmpt.org/");
break;
case Url::Updates:
result = U_("https://openmpt.org/download");
break;
case Url::TopPicks:
result = U_("https://openmpt.org/top_picks");
break;
}
return result;
}
mpt::ustring GetFullCreditsString()
{
return mpt::ToUnicode(mpt::Charset::UTF8,
#ifdef MODPLUG_TRACKER
"OpenMPT / ModPlug Tracker\n"
#else
"libopenmpt (based on OpenMPT / ModPlug Tracker)\n"
#endif
"\n"
"Copyright \xC2\xA9 2004-2021 Contributors\n"
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
"\n"
"Contributors:\n"
"Johannes Schultz (2008-2021)\n"
"J\xC3\xB6rn Heusipp (2012-2021)\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 (https://coda.s3m.us/)\n"
"Jo\xC3\xA3o Baptista de Paula e Silva (https://joaobapt.com/)\n"
"kode54 (https://kode54.net/)\n"
"Revenant (https://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"
"https://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"
"Benjamin \"BeRo\" Rosseaux for his independent VST header\n"
"https://blog.rosseaux.net/\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"
"Sergei \"x0r\" Kolzun for various hints on Scream Tracker 2 compatibility\n"
"https://github.com/viiri/st2play\n"
"\n"
"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"
"Shayde / Reality Productions for Opal OPL3 emulator\n"
"https://www.3eality.com/\n"
"\n"
"Ryuhei Mori for TinyFFT\n"
"https://github.com/ryuhei-mori/tinyfft\n"
"\n"
#ifdef MPT_WITH_ZLIB
"Jean-loup Gailly and Mark Adler for zlib\n"
"https://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"
"https://rarlab.com/\n"
"\n"
#endif
#ifdef MPT_WITH_PORTAUDIO
"PortAudio contributors\n"
"http://www.portaudio.com/\n"
"\n"
#endif
#ifdef MPT_WITH_RTAUDIO
"Gary P. Scavone, McGill University\n"
"https://www.music.mcgill.ca/~gary/rtaudio/\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"
"https://mpg123.de/\n"
"\n"
#endif
#ifdef MPT_WITH_MINIMP3
"Lion (github.com/lieff) for minimp3\n"
"https://github.com/lieff/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_LAME)
"The LAME project for LAME\n"
"https://lame.sourceforge.io/\n"
"\n"
#endif
#if defined(MPT_WITH_NLOHMANNJSON)
"Niels Lohmann et al. for nlohmann-json\n"
"https://github.com/nlohmann/json\n"
"\n"
#endif
#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"
"https://www.codeproject.com/Articles/21257/_\n"
"\n"
"\xc3\x9alfur Kolka for application icon, splash and about screen\n"
"https://www.behance.net/ulfurkolka\n"
"\n"
"Nobuyuki for 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, Teimoso, 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(
"Copyright (c) 2004-2021, 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 Build
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,311 @@
/*
* 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 "BuildSettings.h"
#include "mptString.h"
#include "FlagSet.h"
#include <stdexcept>
OPENMPT_NAMESPACE_BEGIN
class Version
{
private:
uint32 m_Version; // e.g. 0x01170208
public:
enum class Field
{
Major,
Minor,
Patch,
Test,
};
public:
static Version Current() noexcept;
public:
MPT_CONSTEXPR11_FUN Version() noexcept
: m_Version(0)
{}
explicit MPT_CONSTEXPR11_FUN Version(uint32 version) noexcept
: m_Version(version)
{}
explicit MPT_CONSTEXPR11_FUN Version(uint8 v1, uint8 v2, uint8 v3, uint8 v4) noexcept
: m_Version((static_cast<uint32>(v1) << 24) | (static_cast<uint32>(v2) << 16) | (static_cast<uint32>(v3) << 8) | (static_cast<uint32>(v4) << 0))
{}
public:
mpt::ustring ToUString() const; // e.g "1.17.02.08"
// Returns numerical version value from given version string.
static Version Parse(const mpt::ustring &s);
public:
explicit MPT_CONSTEXPR11_FUN operator bool () const noexcept
{
return m_Version != 0;
}
MPT_CONSTEXPR11_FUN bool operator ! () const noexcept
{
return m_Version == 0;
}
MPT_CONSTEXPR11_FUN uint32 GetRawVersion() const noexcept
{
return m_Version;
}
MPT_FORCEINLINE Version Masked(uint32 mask) const noexcept
{
return Version(m_Version & mask);
}
MPT_CONSTEXPR11_FUN uint8 GetField(Field field) const noexcept
{
return
(field == Field::Major) ? static_cast<uint8>((m_Version >> 24) & 0xffu) :
(field == Field::Minor) ? static_cast<uint8>((m_Version >> 16) & 0xffu) :
(field == Field::Patch) ? static_cast<uint8>((m_Version >> 8) & 0xffu) :
(field == Field::Test ) ? static_cast<uint8>((m_Version >> 0) & 0xffu) :
0u;
}
// 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.
Version WithoutTestNumber() const noexcept;
Version WithoutPatchOrTestNumbers() const noexcept;
public:
// Return a OpenMPT version string suitable for file format tags
mpt::ustring GetOpenMPTVersionString() const; // e.g. "OpenMPT 1.17.02.08"
// Returns true if a given version number is from a test build, false if it's a release build.
bool IsTestVersion() const noexcept;
public:
struct LiteralParser
{
public:
// Work-around for GCC 5 which complains about instanciating non-literal type inside a constexpr function when using mpt::constexpr_throw(std::runtime_error("")).
struct ParseException {};
private:
static MPT_CONSTEXPR11_FUN uint8 NibbleFromChar(char x)
{
return
('0' <= x && x <= '9') ? static_cast<uint8>(x - '0' + 0) :
('a' <= x && x <= 'z') ? static_cast<uint8>(x - 'a' + 10) :
('A' <= x && x <= 'Z') ? static_cast<uint8>(x - 'A' + 10) :
mpt::constexpr_throw<uint8>(std::domain_error(""));
}
public:
static MPT_CONSTEXPR14_FUN Version Parse(const char * str, std::size_t len)
{
// 0123456789
// 1.23.45.67
uint8 v[4] = {0, 0, 0, 0};
std::size_t field = 0;
std::size_t fieldlen = 0;
for(std::size_t i = 0; i < len; ++i)
{
char c = str[i];
if(c == '.')
{
if(field >= 3)
{
mpt::constexpr_throw(ParseException());
}
if(fieldlen == 0)
{
mpt::constexpr_throw(ParseException());
}
field++;
fieldlen = 0;
} else if(('0' <= c && c <= '9') || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'))
{
fieldlen++;
if(fieldlen > 2)
{
mpt::constexpr_throw(ParseException());
}
v[field] <<= 4;
v[field] |= NibbleFromChar(c);
} else
{
mpt::constexpr_throw(ParseException());
}
}
if(fieldlen == 0)
{
mpt::constexpr_throw(ParseException());
}
return Version(v[0], v[1], v[2], v[3]);
}
};
};
MPT_CONSTEXPR11_FUN bool operator == (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() == b.GetRawVersion();
}
MPT_CONSTEXPR11_FUN bool operator != (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() != b.GetRawVersion();
}
MPT_CONSTEXPR11_FUN bool operator <= (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() <= b.GetRawVersion();
}
MPT_CONSTEXPR11_FUN bool operator >= (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() >= b.GetRawVersion();
}
MPT_CONSTEXPR11_FUN bool operator < (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() < b.GetRawVersion();
}
MPT_CONSTEXPR11_FUN bool operator > (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() > b.GetRawVersion();
}
MPT_CONSTEXPR14_FUN Version operator "" _LiteralVersionImpl (const char * str, std::size_t len)
{
return Version::LiteralParser::Parse(str, len);
}
// Create Version object from version string and check syntax, all at compile time.
// cppcheck false-positive
// cppcheck-suppress preprocessorErrorDirective
#define MPT_V(strver) Version{MPT_FORCE_CONSTEXPR(( strver ## _LiteralVersionImpl ).GetRawVersion())}
class SourceInfo
{
private:
mpt::ustring m_Url; // svn repository url (or empty string)
int m_Revision; // svn revision (or 0)
bool m_IsDirty; // svn working copy is dirty (or false)
bool m_HasMixedRevisions; // svn working copy has mixed revisions (or false)
bool m_IsPackage; // source code originates from a packaged version of the source code
mpt::ustring m_Date; // svn date (or empty string)
private:
SourceInfo();
public:
static SourceInfo Current();
public:
const mpt::ustring & Url() const { return m_Url; }
int Revision() const { return m_Revision; }
bool IsDirty() const { return m_IsDirty; }
bool HasMixedRevisions() const { return m_HasMixedRevisions; }
bool IsPackage() const { return m_IsPackage; }
const mpt::ustring & Date() const { return m_Date; }
public:
mpt::ustring GetUrlWithRevision() const; // i.e. "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234" or empty string
mpt::ustring GetStateString() const; // i.e. "+dirty" or "clean"
};
namespace Build
{
// Returns true if all conditions for an official release build are met
bool IsReleasedBuild();
// Return true if this is a debug build with no optimizations
bool IsDebugBuild();
// 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)
mpt::ustring GetBuildDateString();
// Return a string decribing some of the build features
mpt::ustring GetBuildFeaturesString(); // e.g. " NO_VST NO_DSOUND"
// Return a string describing the compiler version used for building.
mpt::ustring 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.
mpt::ustring GetVersionString(FlagSet<Build::Strings> strings);
// Returns a pure version string
mpt::ustring GetVersionStringPure(); // e.g. "1.17.02.08-r1234+ 32 bit"
// Returns a simple version string
mpt::ustring GetVersionStringSimple(); // e.g. "1.17.02.08-r1234+ TEST"
// Returns Version::CurrentAsString() 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)
mpt::ustring GetVersionStringExtended(); // e.g. "1.17.02.08-r1234+ 32 bit DEBUG"
enum class Url
{
Website,
Download,
Forum,
Bugtracker,
Updates,
TopPicks,
};
// Returns a URL for the respective key.
mpt::ustring GetURL(Build::Url 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 Build
OPENMPT_NAMESPACE_END

View File

@ -0,0 +1,23 @@
/*
* versionNumber.h
* ---------------
* Purpose: OpenMPT version number.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "BuildSettings.h"
OPENMPT_NAMESPACE_BEGIN
// Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 29
#define VER_MINOR 15
#define VER_MINORMINOR 00
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\xDE\xAD\xBE\xAF"
j2b="MUSE\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"
#s3m="SCRI"
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 NO_MPG123=1 CHECKED_ADDRESS=1

View File

@ -0,0 +1,13 @@
#!/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=300M none $FUZZING_TEMPDIR
rm -rf $FUZZING_TEMPDIR/bin
mkdir $FUZZING_TEMPDIR/bin
cp -d ../../bin/* $FUZZING_TEMPDIR/bin/
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p exploit -f $FUZZING_TEMPDIR/infile01 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -D -M fuzzer01 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile01

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p coe -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,6 @@
#!/usr/bin/env bash
cd "${0%/*}"
. ./fuzz-settings.sh
#export AFL_PRELOAD=$AFL_DIR/libdislocator.so
LD_LIBRARY_PATH=$FUZZING_TEMPDIR/bin $AFL_DIR/afl-fuzz -p explore -f $FUZZING_TEMPDIR/infile03 -x all_formats.dict -t $FUZZING_TIMEOUT $FUZZING_INPUT -o $FUZZING_FINDINGS_DIR -S fuzzer03 $FUZZING_TEMPDIR/bin/fuzz $FUZZING_TEMPDIR/infile03

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_DIR=afl

View File

@ -0,0 +1,59 @@
/*
* 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 450 // shouldn't match OpenMPT's internal mix buffer size (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;
}
}
openmpt_module_set_position_seconds( mod, 1.0 );
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
openmpt_module_set_position_order_row( mod, 3, 16 );
openmpt_module_read_mono( mod, SAMPLERATE, BUFFERSIZE, buffer );
/* 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%/*}"
GET_AFL_VERSION?="$(wget --quiet -O - "https://api.github.com/repos/AFLplusplus/AFLplusplus/releases/latest" | grep -Po '"tag_name": "\K.*?(?=")')"
AFL_FILENAME="$GET_AFL_VERSION.tar.gz"
AFL_URL="https://github.com/AFLplusplus/AFLplusplus/archive/$AFL_FILENAME"
rm $AFL_FILENAME
wget $AFL_URL || exit
tar -xzvf $AFL_FILENAME
rm $AFL_FILENAME
cd AFLplusplus-*
make source-only || exit
cd ..
rm -rf afl
mv AFLplusplus-* afl

View File

@ -0,0 +1,51 @@
libopenmpt fuzz suite
=====================
In this directory, you can find the necessary tools for fuzzing libopenmpt with
the American Fuzzy Lop fuzzer (afl++).
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-main.sh`: Script to launch the main fuzzing process. If you want to
use just one fuzzer instance, run this one.
* `fuzz-secondary[1|2].sh`: Scripts 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. The two
scripts are set up to use different exploration strategies
* `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 afl++.
You can also make it download from a specific branch or tag, e.g.
`GET_AFL_VERSION=stable ./get-afl.sh` to download the latest stable but
unreleased code.
Prerequisites
=============
* [afl++](https://github.com/AFLplusplus/AFLplusplus) - 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-LTO mode.
How to use
==========
* Run `get-afl.sh`, or manually extract afl to `contrib/fuzzing/afl`, use
`make source-only` to build. If building fails because `llvm-config` cannot be
found, try prepending `LLVM_CONFIG=/usr/bin/llvm-config-12` 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-main.sh` for the first (deterministic) instance of afl-fuzz.
* For a "secondary" instance to run on another core, run `fuzz-secondary1.sh`
and/or `fuzz-secondary2.sh`.
* If you want to make use of even more cores, create more copies
`fuzz-secondary2.sh` and adjust "infile03" / "fuzzer03" to
"infile04" / "fuzzer04" and so o (they need to be unique). Try variying the
fuzzing strategey (the -p parameter) to get results more quickly.

View File

@ -0,0 +1,26 @@
Copyright (c) 2004-2021, 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.

View File

@ -0,0 +1,46 @@
ACLOCAL_AMFLAGS = -I m4 --install
EXTRA_DIST =
EXTRA_DIST += LICENSE
EXTRA_DIST += libopenmpt_modplug.pc.in
EXTRA_DIST += libmodplug.pc.in
EXTRA_DIST += test.sh
MOSTLYCLEANFILES =
dist_doc_DATA =
dist_doc_DATA += LICENSE
nobase_dist_doc_DATA =
bin_PROGRAMS =
check_PROGRAMS =
lib_LTLIBRARIES =
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA =
nobase_include_HEADERS =
if ENABLE_LIBOPENMPT_MODPLUG
lib_LTLIBRARIES += libopenmpt_modplug.la
libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined
libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS)
libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS)
libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS)
libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS)
libopenmpt_modplug_la_SOURCES =
libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c
libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp
endif
if ENABLE_LIBMODPLUG
pkgconfig_DATA += libmodplug.pc
lib_LTLIBRARIES += libmodplug.la
libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined
nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h
libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS)
libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS)
libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS)
libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS)
libmodplug_la_SOURCES =
libmodplug_la_SOURCES += libopenmpt_modplug.c
libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp
endif

View File

@ -0,0 +1,2 @@
#!/usr/bin/env sh
autoreconf -i

View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
./autogen.sh
./configure
make distclean

View File

@ -0,0 +1,81 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* define if the compiler supports basic C++11 syntax */
#undef HAVE_CXX11
/* define if the compiler supports basic C++14 syntax */
#undef HAVE_CXX14
/* define if the compiler supports basic C++17 syntax */
#undef HAVE_CXX17
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES

View File

@ -0,0 +1,66 @@
AC_INIT([libopenmpt-modplug], [0.8.8.5-openmpt1], [https://bugs.openmpt.org/], [libopenmpt-modplug], [https://lib.openmpt.org/])
AC_PREREQ([2.68])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_FILES([Makefile libopenmpt_modplug.pc libmodplug.pc])
AM_INIT_AUTOMAKE([1.11 -Wall -Werror foreign subdir-objects])
AM_PROG_AR
LT_INIT
AC_SYS_LARGEFILE
PKG_PROG_PKG_CONFIG([0.24])
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_PROG_INSTALL
LIBOPENMPT_REQUIRES_PRIVATE=
LIBOPENMPT_LIBS_PRIVATE=
# We want a modern C compiler
AC_PROG_CC_STDC
#AC_PROG_CC_C99
# We need C++11 support
AX_CXX_COMPILE_STDCXX(17, [noext], [optional])
AS_IF([test "x$HAVE_CXX17" != "x1"],
[
AX_CXX_COMPILE_STDCXX(14, [noext], [optional])
AS_IF([test "x$HAVE_CXX14" != "x1"],
[
AX_CXX_COMPILE_STDCXX(11, [noext], [mandatory])
],[]
)
],[]
)
AC_LANG_PUSH([C])
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CFLAGS="$CFLAGS -fvisibility=hidden"])
AX_CFLAGS_WARN_ALL
AC_LANG_POP([C])
AC_LANG_PUSH([C++])
AX_CHECK_COMPILE_FLAG([-fvisibility=hidden], [CXXFLAGS="$CXXFLAGS -fvisibility=hidden"])
AX_CXXFLAGS_WARN_ALL
AC_LANG_POP([C++])
PKG_CHECK_MODULES([LIBOPENMPT], [libopenmpt])
# libmodplug emulation
AC_ARG_ENABLE([libopenmpt_modplug], AS_HELP_STRING([--disable-libopenmpt_modplug], [Disable the libopenmpt_modplug emulation library of the libmodplug interface.]))
AM_CONDITIONAL([ENABLE_LIBOPENMPT_MODPLUG], [test "x$enable_libopenmpt_modplug" != "xno"])
# libmodplug replacement
AC_ARG_ENABLE([libmodplug], AS_HELP_STRING([--enable-libmodplug], [Enable libmodplug replacement library based on libopenmpt.
WARNING: This will replace your current libmodplug installation.
CAUTION: The emulation of the libmodplug interface is not complete as libmodplug exposes lots of internal implementation details. If any of those is used by an application, the emulation via libopenmpt will fail and/or crash.
]))
AM_CONDITIONAL([ENABLE_LIBMODPLUG], [test "x$enable_libmodplug" = "xyes"])
AC_OUTPUT

View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
./test.sh
./autogen.sh
./configure
make dist

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: @PACKAGE_VERSION@
Requires.private: libopenmpt
Libs: -L${libdir} -lmodplug
Libs.private:
Cflags: -I${includedir}

View File

@ -0,0 +1,186 @@
/*
* This source code is public domain.
*
* Authors: Kenton Varda <temporal@gauge3d.org> (C interface wrapper)
*/
#ifndef MODPLUG_MODPLUG_H
#define MODPLUG_MODPLUG_H
#ifdef __cplusplus
extern "C" {
#endif
#if defined(_WIN32) || defined(__CYGWIN__)
# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */
# define MODPLUG_EXPORT __declspec(dllexport)
# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */
# define MODPLUG_EXPORT
# else
# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */
# endif
/* FIXME: USE VISIBILITY ATTRIBUTES HERE */
#elif defined(MODPLUG_BUILD)
#define MODPLUG_EXPORT
#else
#define MODPLUG_EXPORT
#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. */
MODPLUG_EXPORT ModPlugFile* ModPlug_Load(const void* data, int size);
/* Unload a mod file. */
MODPLUG_EXPORT 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. */
MODPLUG_EXPORT 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. */
MODPLUG_EXPORT 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. */
MODPLUG_EXPORT 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. */
MODPLUG_EXPORT 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. */
MODPLUG_EXPORT void ModPlug_GetSettings(ModPlug_Settings* settings);
MODPLUG_EXPORT void ModPlug_SetSettings(const ModPlug_Settings* settings);
/* New ModPlug API Functions */
/* NOTE: Master Volume (1-512) */
MODPLUG_EXPORT unsigned int ModPlug_GetMasterVolume(ModPlugFile* file) ;
MODPLUG_EXPORT void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol) ;
MODPLUG_EXPORT int ModPlug_GetCurrentSpeed(ModPlugFile* file);
MODPLUG_EXPORT int ModPlug_GetCurrentTempo(ModPlugFile* file);
MODPLUG_EXPORT int ModPlug_GetCurrentOrder(ModPlugFile* file);
MODPLUG_EXPORT int ModPlug_GetCurrentPattern(ModPlugFile* file);
MODPLUG_EXPORT int ModPlug_GetCurrentRow(ModPlugFile* file);
MODPLUG_EXPORT int ModPlug_GetPlayingChannels(ModPlugFile* file);
MODPLUG_EXPORT void ModPlug_SeekOrder(ModPlugFile* file,int order);
MODPLUG_EXPORT int ModPlug_GetModuleType(ModPlugFile* file);
MODPLUG_EXPORT 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)*/
MODPLUG_EXPORT char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath);
/*Export to a Extended Module (XM). EXPERIMENTAL (only works on Little-Endian platforms)*/
MODPLUG_EXPORT char ModPlug_ExportXM(ModPlugFile* file, const char* filepath);
/*Export to a Amiga MOD file. EXPERIMENTAL.*/
MODPLUG_EXPORT char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath);
/*Export to a Impulse Tracker IT file. Should work OK in Little-Endian & Big-Endian platforms :-) */
MODPLUG_EXPORT char ModPlug_ExportIT(ModPlugFile* file, const char* filepath);
#endif // MODPLUG_NO_FILESAVE
MODPLUG_EXPORT unsigned int ModPlug_NumInstruments(ModPlugFile* file);
MODPLUG_EXPORT unsigned int ModPlug_NumSamples(ModPlugFile* file);
MODPLUG_EXPORT unsigned int ModPlug_NumPatterns(ModPlugFile* file);
MODPLUG_EXPORT unsigned int ModPlug_NumChannels(ModPlugFile* file);
MODPLUG_EXPORT unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff);
MODPLUG_EXPORT unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff);
/*
* Retrieve pattern note-data
*/
MODPLUG_EXPORT 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)
*/
MODPLUG_EXPORT void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc) ;
MODPLUG_EXPORT 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,146 @@
/*
* 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 MODPLUG_STDAFX_H
#define MODPLUG_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
#ifndef isblank
#define isblank(c) ((c) == ' ' || (c) == '\t')
#endif
#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
#if defined(_WIN32) || defined(__CYGWIN__)
# if defined(MODPLUG_BUILD) && defined(DLL_EXPORT) /* building libmodplug as a dll for windows */
# define MODPLUG_EXPORT __declspec(dllexport)
# elif defined(MODPLUG_BUILD) || defined(MODPLUG_STATIC) /* building or using static libmodplug for windows */
# define MODPLUG_EXPORT
# else
# define MODPLUG_EXPORT __declspec(dllimport) /* using libmodplug dll for windows */
# endif
/* FIXME: USE VISIBILITY ATTRIBUTES HERE */
#elif defined(MODPLUG_BUILD)
#define MODPLUG_EXPORT
#else
#define MODPLUG_EXPORT
#endif
#endif

View File

@ -0,0 +1,604 @@
/*
* libopenmpt_modplug.c
* --------------------
* Purpose: libopenmpt emulation of the libmodplug interface
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#ifndef NO_LIBMODPLUG
#ifdef LIBOPENMPT_BUILD_DLL
#undef LIBOPENMPT_BUILD_DLL
#endif
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif /* _MSC_VER */
#include <libopenmpt/libopenmpt.h>
#include <limits.h>
#include <math.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MODPLUG_BUILD
#ifdef _MSC_VER
#ifdef MPT_BUILD_MSVC_SHARED
#define DLL_EXPORT
#endif /* MPT_BUILD_MSVC_SHARED */
#ifdef MPT_BUILD_MSVC_STATIC
#define MODPLUG_STATIC
#endif /* MPT_BUILD_MSVC_STATIC */
#endif /* _MSC_VER */
#ifdef _MSC_VER
#define LIBOPENMPT_MODPLUG_API
#else /* !_MSC_VER */
#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT
#endif /* _MSC_VER */
#include "libmodplug/modplug.h"
/* from libmodplug/sndfile.h */
/* header is not c clean */
#define MIXING_ATTENUATION 4
#define MOD_TYPE_NONE 0x0
#define MOD_TYPE_MOD 0x1
#define MOD_TYPE_S3M 0x2
#define MOD_TYPE_XM 0x4
#define MOD_TYPE_MED 0x8
#define MOD_TYPE_MTM 0x10
#define MOD_TYPE_IT 0x20
#define MOD_TYPE_669 0x40
#define MOD_TYPE_ULT 0x80
#define MOD_TYPE_STM 0x100
#define MOD_TYPE_FAR 0x200
#define MOD_TYPE_WAV 0x400
#define MOD_TYPE_AMF 0x800
#define MOD_TYPE_AMS 0x1000
#define MOD_TYPE_DSM 0x2000
#define MOD_TYPE_MDL 0x4000
#define MOD_TYPE_OKT 0x8000
#define MOD_TYPE_MID 0x10000
#define MOD_TYPE_DMF 0x20000
#define MOD_TYPE_PTM 0x40000
#define MOD_TYPE_DBM 0x80000
#define MOD_TYPE_MT2 0x100000
#define MOD_TYPE_AMF0 0x200000
#define MOD_TYPE_PSM 0x400000
#define MOD_TYPE_J2B 0x800000
#define MOD_TYPE_ABC 0x1000000
#define MOD_TYPE_PAT 0x2000000
#define MOD_TYPE_UMX 0x80000000 // Fake type
#define BUFFER_COUNT 1024
struct _ModPlugFile {
openmpt_module* mod;
signed short* buf;
signed int* mixerbuf;
char* name;
char* message;
ModPlug_Settings settings;
ModPlugMixerProc mixerproc;
ModPlugNote** patterns;
};
static ModPlug_Settings globalsettings = {
MODPLUG_ENABLE_OVERSAMPLING|MODPLUG_ENABLE_NOISE_REDUCTION,
2,
16,
44100,
MODPLUG_RESAMPLE_LINEAR,
128,
256,
0,
0,
0,
0,
0,
0,
0
};
static int32_t modplugresamplingmode_to_filterlength(int mode)
{
if(mode<0){
return 1;
}
switch(mode){
case MODPLUG_RESAMPLE_NEAREST: return 1; break;
case MODPLUG_RESAMPLE_LINEAR: return 2; break;
case MODPLUG_RESAMPLE_SPLINE: return 4; break;
case MODPLUG_RESAMPLE_FIR: return 8; break;
}
return 8;
}
LIBOPENMPT_MODPLUG_API ModPlugFile* ModPlug_Load(const void* data, int size)
{
ModPlugFile* file = malloc(sizeof(ModPlugFile));
const char* name = NULL;
const char* message = NULL;
if(!file) return NULL;
memset(file,0,sizeof(ModPlugFile));
memcpy(&file->settings,&globalsettings,sizeof(ModPlug_Settings));
file->mod = openmpt_module_create_from_memory2(data,size,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
if(!file->mod){
free(file);
return NULL;
}
file->buf = malloc(BUFFER_COUNT*sizeof(signed short)*4);
if(!file->buf){
openmpt_module_destroy(file->mod);
free(file);
return NULL;
}
openmpt_module_set_repeat_count(file->mod,file->settings.mLoopCount);
name = openmpt_module_get_metadata(file->mod,"title");
if(name){
file->name = malloc(strlen(name)+1);
if(file->name){
strcpy(file->name,name);
}
openmpt_free_string(name);
name = NULL;
}else{
file->name = malloc(strlen("")+1);
if(file->name){
strcpy(file->name,"");
}
}
message = openmpt_module_get_metadata(file->mod,"message");
if(message){
file->message = malloc(strlen(message)+1);
if(file->message){
strcpy(file->message,message);
}
openmpt_free_string(message);
message = NULL;
}else{
file->message = malloc(strlen("")+1);
if(file->message){
strcpy(file->message,"");
}
}
openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_STEREOSEPARATION_PERCENT,file->settings.mStereoSeparation*100/128);
openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH,modplugresamplingmode_to_filterlength(file->settings.mResamplingMode));
return file;
}
LIBOPENMPT_MODPLUG_API void ModPlug_Unload(ModPlugFile* file)
{
int p;
if(!file) return;
if(file->patterns){
for(p=0;p<openmpt_module_get_num_patterns(file->mod);p++){
if(file->patterns[p]){
free(file->patterns[p]);
file->patterns[p] = NULL;
}
}
free(file->patterns);
file->patterns = NULL;
}
if(file->mixerbuf){
free(file->mixerbuf);
file->mixerbuf = NULL;
}
openmpt_module_destroy(file->mod);
file->mod = NULL;
free(file->name);
file->name = NULL;
free(file->message);
file->message = NULL;
free(file->buf);
file->buf = NULL;
free(file);
}
LIBOPENMPT_MODPLUG_API int ModPlug_Read(ModPlugFile* file, void* buffer, int size)
{
int framesize;
int framecount;
int frames;
int rendered;
int frame;
int channel;
int totalrendered;
signed short* in;
signed int* mixbuf;
unsigned char* buf8;
signed short* buf16;
signed int* buf32;
if(!file) return 0;
framesize = file->settings.mBits/8*file->settings.mChannels;
framecount = size/framesize;
buf8 = buffer;
buf16 = buffer;
buf32 = buffer;
totalrendered = 0;
while(framecount>0){
frames = framecount;
if(frames>BUFFER_COUNT){
frames = BUFFER_COUNT;
}
if(file->settings.mChannels==1){
rendered = (int)openmpt_module_read_mono(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0]);
}else if(file->settings.mChannels==2){
rendered = (int)openmpt_module_read_stereo(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1]);
}else if(file->settings.mChannels==4){
rendered = (int)openmpt_module_read_quad(file->mod,file->settings.mFrequency,frames,&file->buf[frames*0],&file->buf[frames*1],&file->buf[frames*2],&file->buf[frames*3]);
}else{
return 0;
}
in = file->buf;
if(file->mixerproc&&file->mixerbuf){
mixbuf=file->mixerbuf;
for(frame=0;frame<frames;frame++){
for(channel=0;channel<file->settings.mChannels;channel++){
*mixbuf = in[frames*channel+frame]<<(32-16-1-MIXING_ATTENUATION);
mixbuf++;
}
}
file->mixerproc(file->mixerbuf,file->settings.mChannels*frames,file->settings.mChannels);
mixbuf=file->mixerbuf;
for(frame=0;frame<frames;frame++){
for(channel=0;channel<file->settings.mChannels;channel++){
in[frames*channel+frame] = *mixbuf>>(32-16-1-MIXING_ATTENUATION);
mixbuf++;
}
}
}
if(file->settings.mBits==8){
for(frame=0;frame<frames;frame++){
for(channel=0;channel<file->settings.mChannels;channel++){
*buf8 = in[frames*channel+frame]/256+0x80;
buf8++;
}
}
}else if(file->settings.mBits==16){
for(frame=0;frame<frames;frame++){
for(channel=0;channel<file->settings.mChannels;channel++){
*buf16 = in[frames*channel+frame];
buf16++;
}
}
}else if(file->settings.mBits==32){
for(frame=0;frame<frames;frame++){
for(channel=0;channel<file->settings.mChannels;channel++){
*buf32 = in[frames*channel+frame] << (32-16-1-MIXING_ATTENUATION);
buf32++;
}
}
}else{
return 0;
}
totalrendered += rendered;
framecount -= frames;
if(!rendered) break;
}
memset(((char*)buffer)+totalrendered*framesize,0,size-totalrendered*framesize);
return totalrendered*framesize;
}
LIBOPENMPT_MODPLUG_API const char* ModPlug_GetName(ModPlugFile* file)
{
if(!file) return NULL;
return file->name;
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetLength(ModPlugFile* file)
{
if(!file) return 0;
return (int)(openmpt_module_get_duration_seconds(file->mod)*1000.0);
}
LIBOPENMPT_MODPLUG_API void ModPlug_Seek(ModPlugFile* file, int millisecond)
{
if(!file) return;
openmpt_module_set_position_seconds(file->mod,(double)millisecond*0.001);
}
LIBOPENMPT_MODPLUG_API void ModPlug_GetSettings(ModPlug_Settings* settings)
{
if(!settings) return;
memcpy(settings,&globalsettings,sizeof(ModPlug_Settings));
}
LIBOPENMPT_MODPLUG_API void ModPlug_SetSettings(const ModPlug_Settings* settings)
{
if(!settings) return;
memcpy(&globalsettings,settings,sizeof(ModPlug_Settings));
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_GetMasterVolume(ModPlugFile* file)
{
int32_t val;
if(!file) return 0;
val = 0;
if(!openmpt_module_get_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,&val)) return 128;
return (unsigned int)(128.0*pow(10.0,val*0.0005));
}
LIBOPENMPT_MODPLUG_API void ModPlug_SetMasterVolume(ModPlugFile* file,unsigned int cvol)
{
if(!file) return;
openmpt_module_set_render_param(file->mod,OPENMPT_MODULE_RENDER_MASTERGAIN_MILLIBEL,(int32_t)(2000.0*log10(cvol/128.0)));
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentSpeed(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_speed(file->mod);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentTempo(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_tempo(file->mod);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentOrder(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_order(file->mod);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentPattern(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_pattern(file->mod);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetCurrentRow(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_row(file->mod);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetPlayingChannels(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_current_playing_channels(file->mod);
}
LIBOPENMPT_MODPLUG_API void ModPlug_SeekOrder(ModPlugFile* file,int order)
{
if(!file) return;
openmpt_module_set_position_order_row(file->mod,order,0);
}
LIBOPENMPT_MODPLUG_API int ModPlug_GetModuleType(ModPlugFile* file)
{
const char* type;
int retval;
if(!file) return 0;
type = openmpt_module_get_metadata(file->mod,"type");
retval = MOD_TYPE_NONE;
if(!type){
return retval;
}
if(!strcmp(type,"mod")){
retval = MOD_TYPE_MOD;
}else if(!strcmp(type,"s3m")){
retval = MOD_TYPE_S3M;
}else if(!strcmp(type,"xm")){
retval = MOD_TYPE_XM;
}else if(!strcmp(type,"med")){
retval = MOD_TYPE_MED;
}else if(!strcmp(type,"mtm")){
retval = MOD_TYPE_MTM;
}else if(!strcmp(type,"it")){
retval = MOD_TYPE_IT;
}else if(!strcmp(type,"669")){
retval = MOD_TYPE_669;
}else if(!strcmp(type,"ult")){
retval = MOD_TYPE_ULT;
}else if(!strcmp(type,"stm")){
retval = MOD_TYPE_STM;
}else if(!strcmp(type,"far")){
retval = MOD_TYPE_FAR;
}else if(!strcmp(type,"s3m")){
retval = MOD_TYPE_WAV;
}else if(!strcmp(type,"amf")){
retval = MOD_TYPE_AMF;
}else if(!strcmp(type,"ams")){
retval = MOD_TYPE_AMS;
}else if(!strcmp(type,"dsm")){
retval = MOD_TYPE_DSM;
}else if(!strcmp(type,"mdl")){
retval = MOD_TYPE_MDL;
}else if(!strcmp(type,"okt")){
retval = MOD_TYPE_OKT;
}else if(!strcmp(type,"mid")){
retval = MOD_TYPE_MID;
}else if(!strcmp(type,"dmf")){
retval = MOD_TYPE_DMF;
}else if(!strcmp(type,"ptm")){
retval = MOD_TYPE_PTM;
}else if(!strcmp(type,"dbm")){
retval = MOD_TYPE_DBM;
}else if(!strcmp(type,"mt2")){
retval = MOD_TYPE_MT2;
}else if(!strcmp(type,"amf0")){
retval = MOD_TYPE_AMF0;
}else if(!strcmp(type,"psm")){
retval = MOD_TYPE_PSM;
}else if(!strcmp(type,"j2b")){
retval = MOD_TYPE_J2B;
}else if(!strcmp(type,"abc")){
retval = MOD_TYPE_ABC;
}else if(!strcmp(type,"pat")){
retval = MOD_TYPE_PAT;
}else if(!strcmp(type,"umx")){
retval = MOD_TYPE_UMX;
}else{
retval = MOD_TYPE_IT; /* fallback, most complex type */
}
openmpt_free_string(type);
return retval;
}
LIBOPENMPT_MODPLUG_API char* ModPlug_GetMessage(ModPlugFile* file)
{
if(!file) return NULL;
return file->message;
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumInstruments(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_num_instruments(file->mod);
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumSamples(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_num_samples(file->mod);
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumPatterns(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_num_patterns(file->mod);
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_NumChannels(ModPlugFile* file)
{
if(!file) return 0;
return openmpt_module_get_num_channels(file->mod);
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_SampleName(ModPlugFile* file, unsigned int qual, char* buff)
{
const char* str;
char buf[32];
if(!file) return 0;
str = openmpt_module_get_sample_name(file->mod,qual-1);
memset(buf,0,32);
if(str){
strncpy(buf,str,31);
openmpt_free_string(str);
}
if(buff){
strncpy(buff,buf,32);
}
return (unsigned int)strlen(buf);
}
LIBOPENMPT_MODPLUG_API unsigned int ModPlug_InstrumentName(ModPlugFile* file, unsigned int qual, char* buff)
{
const char* str;
char buf[32];
if(!file) return 0;
str = openmpt_module_get_instrument_name(file->mod,qual-1);
memset(buf,0,32);
if(str){
strncpy(buf,str,31);
openmpt_free_string(str);
}
if(buff){
strncpy(buff,buf,32);
}
return (unsigned int)strlen(buf);
}
LIBOPENMPT_MODPLUG_API ModPlugNote* ModPlug_GetPattern(ModPlugFile* file, int pattern, unsigned int* numrows)
{
int c;
int r;
int numr;
int numc;
ModPlugNote note;
if(!file) return NULL;
if(numrows){
*numrows = openmpt_module_get_pattern_num_rows(file->mod,pattern);
}
if(pattern<0||pattern>=openmpt_module_get_num_patterns(file->mod)){
return NULL;
}
if(!file->patterns){
file->patterns = malloc(sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern));
if(!file->patterns) return NULL;
memset(file->patterns,0,sizeof(ModPlugNote*)*openmpt_module_get_pattern_num_rows(file->mod,pattern));
}
if(!file->patterns[pattern]){
file->patterns[pattern] = malloc(sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod));
if(!file->patterns[pattern]) return NULL;
memset(file->patterns[pattern],0,sizeof(ModPlugNote)*openmpt_module_get_pattern_num_rows(file->mod,pattern)*openmpt_module_get_num_channels(file->mod));
}
numr = openmpt_module_get_pattern_num_rows(file->mod,pattern);
numc = openmpt_module_get_num_channels(file->mod);
for(r=0;r<numr;r++){
for(c=0;c<numc;c++){
memset(&note,0,sizeof(ModPlugNote));
note.Note = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_NOTE);
note.Instrument = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_INSTRUMENT);
note.VolumeEffect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUMEEFFECT);
note.Effect = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_EFFECT);
note.Volume = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_VOLUME);
note.Parameter = openmpt_module_get_pattern_row_channel_command(file->mod,pattern,r,c,OPENMPT_MODULE_COMMAND_PARAMETER);
memcpy(&file->patterns[pattern][r*numc+c],&note,sizeof(ModPlugNote));
}
}
return file->patterns[pattern];
}
LIBOPENMPT_MODPLUG_API void ModPlug_InitMixerCallback(ModPlugFile* file,ModPlugMixerProc proc)
{
if(!file) return;
if(!file->mixerbuf){
file->mixerbuf = malloc(BUFFER_COUNT*sizeof(signed int)*4);
}
file->mixerproc = proc;
}
LIBOPENMPT_MODPLUG_API void ModPlug_UnloadMixerCallback(ModPlugFile* file)
{
if(!file) return;
file->mixerproc = NULL;
if(file->mixerbuf){
free(file->mixerbuf);
file->mixerbuf = NULL;
}
}
LIBOPENMPT_MODPLUG_API char ModPlug_ExportS3M(ModPlugFile* file, const char* filepath)
{
(void)file;
/* not implemented */
fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportS3M(%s) not implemented.\n",filepath);
return 0;
}
LIBOPENMPT_MODPLUG_API char ModPlug_ExportXM(ModPlugFile* file, const char* filepath)
{
(void)file;
/* not implemented */
fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportXM(%s) not implemented.\n",filepath);
return 0;
}
LIBOPENMPT_MODPLUG_API char ModPlug_ExportMOD(ModPlugFile* file, const char* filepath)
{
(void)file;
/* not implemented */
fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportMOD(%s) not implemented.\n",filepath);
return 0;
}
LIBOPENMPT_MODPLUG_API char ModPlug_ExportIT(ModPlugFile* file, const char* filepath)
{
(void)file;
/* not implemented */
fprintf(stderr,"libopenmpt-modplug: error: ModPlug_ExportIT(%s) not implemented.\n",filepath);
return 0;
}
#endif /* NO_LIBMODPLUG */

View File

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

View File

@ -0,0 +1,887 @@
/*
* libopenmpt_modplug_cpp.cpp
* --------------------------
* Purpose: libopenmpt emulation of the libmodplug c++ interface
* Notes : WARNING! THIS IS A HACK!
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#ifndef NO_LIBMODPLUG
/*
***********************************************************************
WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
***********************************************************************
This is a dirty hack to emulate just so much of the libmodplug c++
interface so that the current known users (mainly xmms-modplug itself,
gstreamer modplug, audacious, and stuff based on those) work. This is
neither a complete nor a correct implementation.
Metadata and other state is not provided or updated.
*/
#ifdef UNICODE
#undef UNICODE
#endif
#ifdef _UNICODE
#undef _UNICODE
#endif
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif /* _MSC_VER */
#include <libopenmpt/libopenmpt.hpp>
#include <string>
#include <vector>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define MODPLUG_BUILD
#ifdef _MSC_VER
/* libmodplug C++ header is broken for MSVC DLL builds */
#define MODPLUG_STATIC
#endif /* _MSC_VER */
#ifdef _MSC_VER
#define LIBOPENMPT_MODPLUG_API
#else /* !_MSC_VER */
#define LIBOPENMPT_MODPLUG_API LIBOPENMPT_API_HELPER_EXPORT
#endif /* _MSC_VER */
class LIBOPENMPT_MODPLUG_API CSoundFile;
#include "libmodplug/stdafx.h"
#include "libmodplug/sndfile.h"
namespace {
template <class T>
void Clear( T & x )
{
std::memset( &x, 0, sizeof(T) );
}
}
//#define mpcpplog() fprintf(stderr, "%s %i\n", __func__, __LINE__)
#define mpcpplog() do{}while(0)
#define UNUSED(x) (void)((x))
union self_t {
CHAR CompressionTable[16];
openmpt::module * self_;
};
static void set_self( CSoundFile * that, openmpt::module * self_ ) {
self_t self_union;
Clear(self_union);
self_union.self_ = self_;
std::memcpy( that->CompressionTable, self_union.CompressionTable, sizeof( self_union.CompressionTable ) );
}
static openmpt::module * get_self( const CSoundFile * that ) {
self_t self_union;
Clear(self_union);
std::memcpy( self_union.CompressionTable, that->CompressionTable, sizeof( self_union.CompressionTable ) );
return self_union.self_;
}
#define mod ( get_self( this ) )
#define update_state() \
if ( mod ) m_nCurrentPattern = mod->get_current_order(); \
if ( mod ) m_nPattern = mod->get_current_pattern(); \
if ( mod ) m_nMusicSpeed = mod->get_current_speed(); \
if ( mod ) m_nMusicTempo = mod->get_current_tempo(); \
/**/
UINT CSoundFile::m_nXBassDepth = 0;
UINT CSoundFile::m_nXBassRange = 0;
UINT CSoundFile::m_nReverbDepth = 0;
UINT CSoundFile::m_nReverbDelay = 0;
UINT CSoundFile::gnReverbType = 0;
UINT CSoundFile::m_nProLogicDepth = 0;
UINT CSoundFile::m_nProLogicDelay = 0;
UINT CSoundFile::m_nStereoSeparation = 128;
UINT CSoundFile::m_nMaxMixChannels = 256;
LONG CSoundFile::m_nStreamVolume = 0x8000;
DWORD CSoundFile::gdwSysInfo = 0;
DWORD CSoundFile::gdwSoundSetup = 0;
DWORD CSoundFile::gdwMixingFreq = 44100;
DWORD CSoundFile::gnBitsPerSample = 16;
DWORD CSoundFile::gnChannels = 2;
UINT CSoundFile::gnAGC = 0;
UINT CSoundFile::gnVolumeRampSamples = 0;
UINT CSoundFile::gnVUMeter = 0;
UINT CSoundFile::gnCPUUsage = 0;
LPSNDMIXHOOKPROC CSoundFile::gpSndMixHook = 0;
PMIXPLUGINCREATEPROC CSoundFile::gpMixPluginCreateProc = 0;
CSoundFile::CSoundFile() {
mpcpplog();
Clear(Chn);
Clear(ChnMix);
Clear(Ins);
Clear(Headers);
Clear(ChnSettings);
Clear(Patterns);
Clear(PatternSize);
Clear(Order);
Clear(m_MidiCfg);
Clear(m_MixPlugins);
Clear(m_nDefaultSpeed);
Clear(m_nDefaultTempo);
Clear(m_nDefaultGlobalVolume);
Clear(m_dwSongFlags);
Clear(m_nChannels);
Clear(m_nMixChannels);
Clear(m_nMixStat);
Clear(m_nBufferCount);
Clear(m_nType);
Clear(m_nSamples);
Clear(m_nInstruments);
Clear(m_nTickCount);
Clear(m_nTotalCount);
Clear(m_nPatternDelay);
Clear(m_nFrameDelay);
Clear(m_nMusicSpeed);
Clear(m_nMusicTempo);
Clear(m_nNextRow);
Clear(m_nRow);
Clear(m_nPattern);
Clear(m_nCurrentPattern);
Clear(m_nNextPattern);
Clear(m_nRestartPos);
Clear(m_nMasterVolume);
Clear(m_nGlobalVolume);
Clear(m_nSongPreAmp);
Clear(m_nFreqFactor);
Clear(m_nTempoFactor);
Clear(m_nOldGlbVolSlide);
Clear(m_nMinPeriod);
Clear(m_nMaxPeriod);
Clear(m_nRepeatCount);
Clear(m_nInitialRepeatCount);
Clear(m_nGlobalFadeSamples);
Clear(m_nGlobalFadeMaxSamples);
Clear(m_nMaxOrderPosition);
Clear(m_nPatternNames);
Clear(m_lpszSongComments);
Clear(m_lpszPatternNames);
Clear(m_szNames);
Clear(CompressionTable);
}
CSoundFile::~CSoundFile() {
mpcpplog();
Destroy();
}
BOOL CSoundFile::Create( LPCBYTE lpStream, DWORD dwMemLength ) {
mpcpplog();
try {
openmpt::module * m = new openmpt::module( lpStream, dwMemLength );
set_self( this, m );
std::strncpy( m_szNames[0], mod->get_metadata("title").c_str(), sizeof( m_szNames[0] ) - 1 );
m_szNames[0][ sizeof( m_szNames[0] ) - 1 ] = '\0';
std::string type = mod->get_metadata("type");
m_nType = MOD_TYPE_NONE;
if ( type == "mod" ) {
m_nType = MOD_TYPE_MOD;
} else if ( type == "s3m" ) {
m_nType = MOD_TYPE_S3M;
} else if ( type == "xm" ) {
m_nType = MOD_TYPE_XM;
} else if ( type == "med" ) {
m_nType = MOD_TYPE_MED;
} else if ( type == "mtm" ) {
m_nType = MOD_TYPE_MTM;
} else if ( type == "it" ) {
m_nType = MOD_TYPE_IT;
} else if ( type == "669" ) {
m_nType = MOD_TYPE_669;
} else if ( type == "ult" ) {
m_nType = MOD_TYPE_ULT;
} else if ( type == "stm" ) {
m_nType = MOD_TYPE_STM;
} else if ( type == "far" ) {
m_nType = MOD_TYPE_FAR;
} else if ( type == "s3m" ) {
m_nType = MOD_TYPE_WAV;
} else if ( type == "amf" ) {
m_nType = MOD_TYPE_AMF;
} else if ( type == "ams" ) {
m_nType = MOD_TYPE_AMS;
} else if ( type == "dsm" ) {
m_nType = MOD_TYPE_DSM;
} else if ( type == "mdl" ) {
m_nType = MOD_TYPE_MDL;
} else if ( type == "okt" ) {
m_nType = MOD_TYPE_OKT;
} else if ( type == "mid" ) {
m_nType = MOD_TYPE_MID;
} else if ( type == "dmf" ) {
m_nType = MOD_TYPE_DMF;
} else if ( type == "ptm" ) {
m_nType = MOD_TYPE_PTM;
} else if ( type == "dbm" ) {
m_nType = MOD_TYPE_DBM;
} else if ( type == "mt2" ) {
m_nType = MOD_TYPE_MT2;
} else if ( type == "amf0" ) {
m_nType = MOD_TYPE_AMF0;
} else if ( type == "psm" ) {
m_nType = MOD_TYPE_PSM;
} else if ( type == "j2b" ) {
m_nType = MOD_TYPE_J2B;
} else if ( type == "abc" ) {
m_nType = MOD_TYPE_ABC;
} else if ( type == "pat" ) {
m_nType = MOD_TYPE_PAT;
} else if ( type == "umx" ) {
m_nType = MOD_TYPE_UMX;
} else {
m_nType = MOD_TYPE_IT; // fallback, most complex type
}
m_nChannels = mod->get_num_channels();
m_nMasterVolume = 128;
m_nSamples = mod->get_num_samples();
update_state();
return TRUE;
} catch ( ... ) {
Destroy();
return FALSE;
}
}
BOOL CSoundFile::Destroy() {
mpcpplog();
if ( mod ) {
delete mod;
set_self( this, 0 );
}
return TRUE;
}
UINT CSoundFile::GetNumChannels() const {
mpcpplog();
return mod->get_num_channels();
}
static std::int32_t vol128_To_millibel( unsigned int vol ) {
return static_cast<std::int32_t>( 2000.0 * std::log10( static_cast<int>( vol ) / 128.0 ) );
}
BOOL CSoundFile::SetMasterVolume( UINT vol, BOOL bAdjustAGC ) {
UNUSED(bAdjustAGC);
mpcpplog();
m_nMasterVolume = vol;
mod->set_render_param( openmpt::module::RENDER_MASTERGAIN_MILLIBEL, vol128_To_millibel( m_nMasterVolume ) );
return TRUE;
}
UINT CSoundFile::GetNumPatterns() const {
mpcpplog();
return mod->get_num_patterns();
}
UINT CSoundFile::GetNumInstruments() const {
mpcpplog();
return mod->get_num_instruments();
}
void CSoundFile::SetCurrentOrder( UINT nOrder ) {
mpcpplog();
mod->set_position_order_row( nOrder, 0 );
update_state();
}
UINT CSoundFile::GetSampleName( UINT nSample, LPSTR s ) const {
mpcpplog();
char buf[32];
std::memset( buf, 0, 32 );
if ( mod ) {
std::vector<std::string> names = mod->get_sample_names();
if ( 1 <= nSample && nSample <= names.size() ) {
std::strncpy( buf, names[ nSample - 1 ].c_str(), 31 );
}
}
if ( s ) {
std::strncpy( s, buf, 32 );
}
return static_cast<UINT>( std::strlen( buf ) );
}
UINT CSoundFile::GetInstrumentName( UINT nInstr, LPSTR s ) const {
mpcpplog();
char buf[32];
std::memset( buf, 0, 32 );
if ( mod ) {
std::vector<std::string> names = mod->get_instrument_names();
if ( 1 <= nInstr && nInstr <= names.size() ) {
std::strncpy( buf, names[ nInstr - 1 ].c_str(), 31 );
}
}
if ( s ) {
std::strncpy( s, buf, 32 );
}
return static_cast<UINT>( std::strlen( buf ) );
}
void CSoundFile::LoopPattern( int nPat, int nRow ) {
UNUSED(nPat);
UNUSED(nRow);
mpcpplog();
// todo
}
void CSoundFile::CheckCPUUsage( UINT nCPU ) {
UNUSED(nCPU);
mpcpplog();
}
BOOL CSoundFile::SetPatternName( UINT nPat, LPCSTR lpszName ) {
UNUSED(nPat);
mpcpplog();
if ( !lpszName ) {
return FALSE;
}
// todo
return TRUE;
}
BOOL CSoundFile::GetPatternName( UINT nPat, LPSTR lpszName, UINT cbSize ) const {
UNUSED(nPat);
mpcpplog();
if ( !lpszName || cbSize <= 0 ) {
return FALSE;
}
std::memset( lpszName, 0, cbSize );
// todo
return TRUE;
}
BOOL CSoundFile::ReadXM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadS3M(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMod(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMed(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadSTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadIT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::Read669(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadUlt(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadWav(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadDSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadFAR(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadAMS(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadAMS2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMDL(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadOKT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadDMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadPTM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadDBM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadAMF(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMT2(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadPSM(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadJ2B(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadUMX(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::TestABC(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::TestMID(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::ReadPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
BOOL CSoundFile::TestPAT(LPCBYTE lpStream, DWORD dwMemLength) { UNUSED(lpStream); UNUSED(dwMemLength); mpcpplog(); return FALSE; }
#ifndef MODPLUG_NO_FILESAVE
UINT CSoundFile::WriteSample( FILE * f, MODINSTRUMENT * pins, UINT nFlags, UINT nMaxLen ) {
UNUSED(f);
UNUSED(pins);
UNUSED(nFlags);
UNUSED(nMaxLen);
mpcpplog();
return 0;
}
BOOL CSoundFile::SaveXM( LPCSTR lpszFileName, UINT nPacking ) {
UNUSED(lpszFileName);
UNUSED(nPacking);
mpcpplog();
return FALSE;
}
BOOL CSoundFile::SaveS3M( LPCSTR lpszFileName, UINT nPacking ) {
UNUSED(lpszFileName);
UNUSED(nPacking);
mpcpplog();
return FALSE;
}
BOOL CSoundFile::SaveMod( LPCSTR lpszFileName, UINT nPacking ) {
UNUSED(lpszFileName);
UNUSED(nPacking);
mpcpplog();
return FALSE;
}
BOOL CSoundFile::SaveIT( LPCSTR lpszFileName, UINT nPacking ) {
UNUSED(lpszFileName);
UNUSED(nPacking);
mpcpplog();
return FALSE;
}
#endif
UINT CSoundFile::GetBestSaveFormat() const {
mpcpplog();
return MOD_TYPE_IT;
}
UINT CSoundFile::GetSaveFormats() const {
mpcpplog();
return MOD_TYPE_IT;
}
void CSoundFile::ConvertModCommand( MODCOMMAND * ) const {
mpcpplog();
}
void CSoundFile::S3MConvert( MODCOMMAND * m, BOOL bIT ) const {
UNUSED(m);
UNUSED(bIT);
mpcpplog();
}
void CSoundFile::S3MSaveConvert( UINT * pcmd, UINT * pprm, BOOL bIT ) const {
UNUSED(pcmd);
UNUSED(pprm);
UNUSED(bIT);
mpcpplog();
}
WORD CSoundFile::ModSaveCommand( const MODCOMMAND * m, BOOL bXM ) const {
UNUSED(m);
UNUSED(bXM);
mpcpplog();
return 0;
}
VOID CSoundFile::ResetChannels() {
mpcpplog();
}
UINT CSoundFile::CreateStereoMix( int count ) {
UNUSED(count);
mpcpplog();
return 0;
}
BOOL CSoundFile::FadeSong( UINT msec ) {
UNUSED(msec);
mpcpplog();
return TRUE;
}
BOOL CSoundFile::GlobalFadeSong( UINT msec ) {
UNUSED(msec);
mpcpplog();
return TRUE;
}
BOOL CSoundFile::InitPlayer( BOOL bReset ) {
UNUSED(bReset);
mpcpplog();
return TRUE;
}
BOOL CSoundFile::SetMixConfig( UINT nStereoSeparation, UINT nMaxMixChannels ) {
UNUSED(nMaxMixChannels);
mpcpplog();
m_nStereoSeparation = nStereoSeparation;
return TRUE;
}
DWORD CSoundFile::InitSysInfo() {
mpcpplog();
return 0;
}
void CSoundFile::SetAGC( BOOL b ) {
UNUSED(b);
mpcpplog();
}
void CSoundFile::ResetAGC() {
mpcpplog();
}
void CSoundFile::ProcessAGC( int count ) {
UNUSED(count);
mpcpplog();
}
BOOL CSoundFile::SetWaveConfig( UINT nRate, UINT nBits, UINT nChannels, BOOL bMMX ) {
UNUSED(bMMX);
mpcpplog();
gdwMixingFreq = nRate;
gnBitsPerSample = nBits;
gnChannels = nChannels;
return TRUE;
}
BOOL CSoundFile::SetWaveConfigEx( BOOL bSurround, BOOL bNoOverSampling, BOOL bReverb, BOOL hqido, BOOL bMegaBass, BOOL bNR, BOOL bEQ ) {
UNUSED(bSurround);
UNUSED(bReverb);
UNUSED(hqido);
UNUSED(bMegaBass);
UNUSED(bEQ);
mpcpplog();
DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE);
if ( bNoOverSampling ) {
d |= SNDMIX_NORESAMPLING;
} else if ( !hqido ) {
d |= 0;
} else if ( !bNR ) {
d |= SNDMIX_HQRESAMPLER;
} else {
d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE);
}
gdwSoundSetup = d;
return TRUE;
}
BOOL CSoundFile::SetResamplingMode( UINT nMode ) {
mpcpplog();
DWORD d = gdwSoundSetup & ~(SNDMIX_NORESAMPLING|SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE);
switch ( nMode ) {
case SRCMODE_NEAREST:
d |= SNDMIX_NORESAMPLING;
break;
case SRCMODE_LINEAR:
break;
case SRCMODE_SPLINE:
d |= SNDMIX_HQRESAMPLER;
break;
case SRCMODE_POLYPHASE:
d |= (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE);
break;
default:
return FALSE;
break;
}
gdwSoundSetup = d;
return TRUE;
}
BOOL CSoundFile::SetReverbParameters( UINT nDepth, UINT nDelay ) {
UNUSED(nDepth);
UNUSED(nDelay);
mpcpplog();
return TRUE;
}
BOOL CSoundFile::SetXBassParameters( UINT nDepth, UINT nRange ) {
UNUSED(nDepth);
UNUSED(nRange);
mpcpplog();
return TRUE;
}
BOOL CSoundFile::SetSurroundParameters( UINT nDepth, UINT nDelay ) {
UNUSED(nDepth);
UNUSED(nDelay);
mpcpplog();
return TRUE;
}
UINT CSoundFile::GetMaxPosition() const {
mpcpplog();
// rows in original, just use seconds here
if ( mod ) return static_cast<UINT>( mod->get_duration_seconds() + 0.5 );
return 0;
}
DWORD CSoundFile::GetLength( BOOL bAdjust, BOOL bTotal ) {
UNUSED(bAdjust);
UNUSED(bTotal);
mpcpplog();
if ( mod ) return static_cast<DWORD>( mod->get_duration_seconds() + 0.5 );
return 0;
}
UINT CSoundFile::GetSongComments( LPSTR s, UINT cbsize, UINT linesize ) {
UNUSED(linesize);
mpcpplog();
if ( !s ) {
return 0;
}
if ( cbsize <= 0 ) {
return 0;
}
if ( !mod ) {
s[0] = '\0';
return 1;
}
std::strncpy( s, mod->get_metadata("message").c_str(), cbsize );
s[ cbsize - 1 ] = '\0';
return static_cast<UINT>( std::strlen( s ) + 1 );
}
UINT CSoundFile::GetRawSongComments( LPSTR s, UINT cbsize, UINT linesize ) {
UNUSED(linesize);
mpcpplog();
if ( !s ) {
return 0;
}
if ( cbsize <= 0 ) {
return 0;
}
if ( !mod ) {
s[0] = '\0';
return 1;
}
std::strncpy( s, mod->get_metadata("message_raw").c_str(), cbsize );
s[ cbsize - 1 ] = '\0';
return static_cast<UINT>( std::strlen( s ) + 1 );
}
void CSoundFile::SetCurrentPos( UINT nPos ) {
mpcpplog();
if ( mod ) mod->set_position_seconds( nPos );
update_state();
}
UINT CSoundFile::GetCurrentPos() const {
mpcpplog();
if ( mod ) return static_cast<UINT>( mod->get_position_seconds() + 0.5 );
return 0;
}
static int get_stereo_separation() {
mpcpplog();
return CSoundFile::m_nStereoSeparation * 100 / 128;
}
static int get_filter_length() {
mpcpplog();
if ( ( CSoundFile::gdwSoundSetup & (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) == (SNDMIX_HQRESAMPLER|SNDMIX_ULTRAHQSRCMODE) ) {
return 8;
} else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_HQRESAMPLER ) == SNDMIX_HQRESAMPLER ) {
return 4;
} else if ( ( CSoundFile::gdwSoundSetup & SNDMIX_NORESAMPLING ) == SNDMIX_NORESAMPLING ) {
return 1;
} else {
return 2;
}
}
static std::size_t get_sample_size() {
return (CSoundFile::gnBitsPerSample/8);
}
static std::size_t get_num_channels() {
return CSoundFile::gnChannels;
}
static std::size_t get_frame_size() {
return get_sample_size() * get_num_channels();
}
static int get_samplerate() {
return CSoundFile::gdwMixingFreq;
}
UINT CSoundFile::Read( LPVOID lpBuffer, UINT cbBuffer ) {
mpcpplog();
if ( !mod ) {
return 0;
}
mpcpplog();
if ( !lpBuffer ) {
return 0;
}
mpcpplog();
if ( cbBuffer <= 0 ) {
return 0;
}
mpcpplog();
if ( get_samplerate() <= 0 ) {
return 0;
}
mpcpplog();
if ( get_sample_size() != 1 && get_sample_size() != 2 && get_sample_size() != 4 ) {
return 0;
}
mpcpplog();
if ( get_num_channels() != 1 && get_num_channels() != 2 && get_num_channels() != 4 ) {
return 0;
}
mpcpplog();
std::memset( lpBuffer, 0, cbBuffer );
const std::size_t frames_torender = cbBuffer / get_frame_size();
short * out = reinterpret_cast<short*>( lpBuffer );
std::vector<short> tmpbuf;
if ( get_sample_size() == 1 || get_sample_size() == 4 ) {
tmpbuf.resize( frames_torender * get_num_channels() );
out = &tmpbuf[0];
}
mod->set_render_param( openmpt::module::RENDER_STEREOSEPARATION_PERCENT, get_stereo_separation() );
mod->set_render_param( openmpt::module::RENDER_INTERPOLATIONFILTER_LENGTH, get_filter_length() );
std::size_t frames_rendered = 0;
if ( get_num_channels() == 1 ) {
frames_rendered = mod->read( get_samplerate(), frames_torender, out );
} else if ( get_num_channels() == 4 ) {
frames_rendered = mod->read_interleaved_quad( get_samplerate(), frames_torender, out );
} else {
frames_rendered = mod->read_interleaved_stereo( get_samplerate(), frames_torender, out );
}
if ( get_sample_size() == 1 ) {
unsigned char * dst = reinterpret_cast<unsigned char*>( lpBuffer );
for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) {
dst[sample] = ( tmpbuf[sample] / 0x100 ) + 0x80;
}
} else if ( get_sample_size() == 4 ) {
int * dst = reinterpret_cast<int*>( lpBuffer );
for ( std::size_t sample = 0; sample < frames_rendered * get_num_channels(); ++sample ) {
dst[sample] = tmpbuf[sample] << (32-16-1-MIXING_ATTENUATION);
}
}
update_state();
return static_cast<UINT>( frames_rendered );
}
/*
gstreamer modplug calls:
mSoundFile->Create
mSoundFile->Destroy
mSoundFile->SetWaveConfig
mSoundFile->SetWaveConfigEx
mSoundFile->SetResamplingMode
mSoundFile->SetSurroundParameters
mSoundFile->SetXBassParameters
mSoundFile->SetReverbParameters
mSoundFile->GetMaxPosition (inline, -> GetLength)
mSoundFile->GetSongTime
mSoundFile->GetTitle (inline)
mSoundFile->GetSongComments
mSoundFile->SetCurrentPos
mSoundFile->Read
mSoundFile->GetCurrentPos
mSoundFile->GetMusicTempo (inline)
*/
// really very internal symbols, probably nothing calls these directly
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunknown-pragmas"
#pragma clang diagnostic ignored "-Wunused-parameter"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4100)
#endif
BOOL CSoundFile::ReadNote() { mpcpplog(); return 0; }
BOOL CSoundFile::ProcessRow() { mpcpplog(); return 0; }
BOOL CSoundFile::ProcessEffects() { mpcpplog(); return 0; }
UINT CSoundFile::GetNNAChannel(UINT nChn) const { mpcpplog(); return 0; }
void CSoundFile::CheckNNA(UINT nChn, UINT instr, int note, BOOL bForceCut) { mpcpplog(); }
void CSoundFile::NoteChange(UINT nChn, int note, BOOL bPorta, BOOL bResetEnv) { mpcpplog(); }
void CSoundFile::InstrumentChange(MODCHANNEL *pChn, UINT instr, BOOL bPorta,BOOL bUpdVol,BOOL bResetEnv) { mpcpplog(); }
void CSoundFile::PortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::PortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::FinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::FinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::ExtraFinePortamentoUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::ExtraFinePortamentoDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::TonePortamento(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::Vibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::FineVibrato(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::VolumeSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::PanningSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::ChannelVolSlide(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::FineVolumeUp(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::FineVolumeDown(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::Tremolo(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::Panbrello(MODCHANNEL *pChn, UINT param) { mpcpplog(); }
void CSoundFile::RetrigNote(UINT nChn, UINT param) { mpcpplog(); }
void CSoundFile::NoteCut(UINT nChn, UINT nTick) { mpcpplog(); }
void CSoundFile::KeyOff(UINT nChn) { mpcpplog(); }
int CSoundFile::PatternLoop(MODCHANNEL *, UINT param) { mpcpplog(); return 0; }
void CSoundFile::ExtendedMODCommands(UINT nChn, UINT param) { mpcpplog(); }
void CSoundFile::ExtendedS3MCommands(UINT nChn, UINT param) { mpcpplog(); }
void CSoundFile::ExtendedChannelEffect(MODCHANNEL *, UINT param) { mpcpplog(); }
void CSoundFile::ProcessMidiMacro(UINT nChn, LPCSTR pszMidiMacro, UINT param) { mpcpplog(); }
void CSoundFile::SetupChannelFilter(MODCHANNEL *pChn, BOOL bReset, int flt_modifier) const { mpcpplog(); }
void CSoundFile::DoFreqSlide(MODCHANNEL *pChn, LONG nFreqSlide) { mpcpplog(); }
void CSoundFile::SetTempo(UINT param) { mpcpplog(); }
void CSoundFile::SetSpeed(UINT param) { mpcpplog(); }
void CSoundFile::GlobalVolSlide(UINT param) { mpcpplog(); }
DWORD CSoundFile::IsSongFinished(UINT nOrder, UINT nRow) const { mpcpplog(); return 0; }
BOOL CSoundFile::IsValidBackwardJump(UINT nStartOrder, UINT nStartRow, UINT nJumpOrder, UINT nJumpRow) const { mpcpplog(); return 0; }
UINT CSoundFile::PackSample(int &sample, int next) { mpcpplog(); return 0; }
BOOL CSoundFile::CanPackSample(LPSTR pSample, UINT nLen, UINT nPacking, BYTE *result) { mpcpplog(); return 0; }
UINT CSoundFile::ReadSample(MODINSTRUMENT *pIns, UINT nFlags, LPCSTR pMemFile, DWORD dwMemLength) { mpcpplog(); return 0; }
BOOL CSoundFile::DestroySample(UINT nSample) { mpcpplog(); return 0; }
BOOL CSoundFile::DestroyInstrument(UINT nInstr) { mpcpplog(); return 0; }
BOOL CSoundFile::IsSampleUsed(UINT nSample) { mpcpplog(); return 0; }
BOOL CSoundFile::IsInstrumentUsed(UINT nInstr) { mpcpplog(); return 0; }
BOOL CSoundFile::RemoveInstrumentSamples(UINT nInstr) { mpcpplog(); return 0; }
UINT CSoundFile::DetectUnusedSamples(BOOL *) { mpcpplog(); return 0; }
BOOL CSoundFile::RemoveSelectedSamples(BOOL *) { mpcpplog(); return 0; }
void CSoundFile::AdjustSampleLoop(MODINSTRUMENT *pIns) { mpcpplog(); }
BOOL CSoundFile::ReadInstrumentFromSong(UINT nInstr, CSoundFile *, UINT nSrcInstrument) { mpcpplog(); return 0; }
BOOL CSoundFile::ReadSampleFromSong(UINT nSample, CSoundFile *, UINT nSrcSample) { mpcpplog(); return 0; }
UINT CSoundFile::GetNoteFromPeriod(UINT period) const { mpcpplog(); return 0; }
UINT CSoundFile::GetPeriodFromNote(UINT note, int nFineTune, UINT nC4Speed) const { mpcpplog(); return 0; }
UINT CSoundFile::GetFreqFromPeriod(UINT period, UINT nC4Speed, int nPeriodFrac) const { mpcpplog(); return 0; }
void CSoundFile::ResetMidiCfg() { mpcpplog(); }
UINT CSoundFile::MapMidiInstrument(DWORD dwProgram, UINT nChannel, UINT nNote) { mpcpplog(); return 0; }
BOOL CSoundFile::ITInstrToMPT(const void *p, INSTRUMENTHEADER *penv, UINT trkvers) { mpcpplog(); return 0; }
UINT CSoundFile::SaveMixPlugins(FILE *f, BOOL bUpdate) { mpcpplog(); return 0; }
UINT CSoundFile::LoadMixPlugins(const void *pData, UINT nLen) { mpcpplog(); return 0; }
#ifndef NO_FILTER
DWORD CSoundFile::CutOffToFrequency(UINT nCutOff, int flt_modifier) const { mpcpplog(); return 0; }
#endif
DWORD CSoundFile::TransposeToFrequency(int transp, int ftune) { mpcpplog(); return 0; }
int CSoundFile::FrequencyToTranspose(DWORD freq) { mpcpplog(); return 0; }
void CSoundFile::FrequencyToTranspose(MODINSTRUMENT *psmp) { mpcpplog(); }
MODCOMMAND *CSoundFile::AllocatePattern(UINT rows, UINT nchns) { mpcpplog(); return 0; }
signed char* CSoundFile::AllocateSample(UINT nbytes) { mpcpplog(); return 0; }
void CSoundFile::FreePattern(LPVOID pat) { mpcpplog(); }
void CSoundFile::FreeSample(LPVOID p) { mpcpplog(); }
UINT CSoundFile::Normalize24BitBuffer(LPBYTE pbuffer, UINT cbsizebytes, DWORD lmax24, DWORD dwByteInc) { mpcpplog(); return 0; }
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#endif // NO_LIBMODPLUG

View File

@ -0,0 +1,972 @@
# ===========================================================================
# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
#
# DESCRIPTION
#
# Check for baseline language coverage in the compiler for the specified
# version of the C++ standard. If necessary, add switches to CXX and
# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
# or '14' (for the C++14 standard).
#
# The second argument, if specified, indicates whether you insist on an
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
# -std=c++11). If neither is specified, you get whatever works, with
# preference for an extended mode.
#
# The third argument, if specified 'mandatory' or if left unspecified,
# indicates that baseline support for the specified C++ standard is
# required and that the macro should error out if no mode with that
# support is found. If specified 'optional', then configuration proceeds
# regardless, after defining HAVE_CXX${VERSION} if and only if a
# supporting mode is found.
#
# LICENSE
#
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 9
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
dnl (serial version number 13).
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
[$1], [14], [ax_cxx_compile_alternatives="14 1y"],
[$1], [17], [ax_cxx_compile_alternatives="17 1z"],
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$2], [], [],
[$2], [ext], [],
[$2], [noext], [],
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
for alternative in ${ax_cxx_compile_alternatives}; do
switch="-std=gnu++${alternative}"
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
fi])
m4_if([$2], [ext], [], [dnl
if test x$ac_success = xno; then
dnl HP's aCC needs +std=c++11 according to:
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
dnl Cray's crayCC needs "-h std=c++11"
for alternative in ${ax_cxx_compile_alternatives}; do
for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
$cachevar,
[ac_save_CXX="$CXX"
CXX="$CXX $switch"
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
[eval $cachevar=yes],
[eval $cachevar=no])
CXX="$ac_save_CXX"])
if eval test x\$$cachevar = xyes; then
CXX="$CXX $switch"
if test -n "$CXXCPP" ; then
CXXCPP="$CXXCPP $switch"
fi
ac_success=yes
break
fi
done
if test x$ac_success = xyes; then
break
fi
done
fi])
AC_LANG_POP([C++])
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
if test x$ac_success = xno; then
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
fi
fi
if test x$ac_success = xno; then
HAVE_CXX$1=0
AC_MSG_NOTICE([No compiler with C++$1 support was found])
else
HAVE_CXX$1=1
AC_DEFINE(HAVE_CXX$1,1,
[define if the compiler supports basic C++$1 syntax])
fi
AC_SUBST(HAVE_CXX$1)
])
dnl Test body for checking C++11 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
)
dnl Test body for checking C++14 support
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
)
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
_AX_CXX_COMPILE_STDCXX_testbody_new_in_17
)
dnl Tests for new features in C++11
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
// If the compiler admits that it is not ready for C++11, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201103L
#error "This is not a C++11 compiler"
#else
namespace cxx11
{
namespace test_static_assert
{
template <typename T>
struct check
{
static_assert(sizeof(int) <= sizeof(T), "not big enough");
};
}
namespace test_final_override
{
struct Base
{
virtual void f() {}
};
struct Derived : public Base
{
virtual void f() override {}
};
}
namespace test_double_right_angle_brackets
{
template < typename T >
struct check {};
typedef check<void> single_type;
typedef check<check<void>> double_type;
typedef check<check<check<void>>> triple_type;
typedef check<check<check<check<void>>>> quadruple_type;
}
namespace test_decltype
{
int
f()
{
int a = 1;
decltype(a) b = 2;
return a + b;
}
}
namespace test_type_deduction
{
template < typename T1, typename T2 >
struct is_same
{
static const bool value = false;
};
template < typename T >
struct is_same<T, T>
{
static const bool value = true;
};
template < typename T1, typename T2 >
auto
add(T1 a1, T2 a2) -> decltype(a1 + a2)
{
return a1 + a2;
}
int
test(const int c, volatile int v)
{
static_assert(is_same<int, decltype(0)>::value == true, "");
static_assert(is_same<int, decltype(c)>::value == false, "");
static_assert(is_same<int, decltype(v)>::value == false, "");
auto ac = c;
auto av = v;
auto sumi = ac + av + 'x';
auto sumf = ac + av + 1.0;
static_assert(is_same<int, decltype(ac)>::value == true, "");
static_assert(is_same<int, decltype(av)>::value == true, "");
static_assert(is_same<int, decltype(sumi)>::value == true, "");
static_assert(is_same<int, decltype(sumf)>::value == false, "");
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
return (sumf > 0.0) ? sumi : add(c, v);
}
}
namespace test_noexcept
{
int f() { return 0; }
int g() noexcept { return 0; }
static_assert(noexcept(f()) == false, "");
static_assert(noexcept(g()) == true, "");
}
namespace test_constexpr
{
template < typename CharT >
unsigned long constexpr
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
{
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
}
template < typename CharT >
unsigned long constexpr
strlen_c(const CharT *const s) noexcept
{
return strlen_c_r(s, 0UL);
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("1") == 1UL, "");
static_assert(strlen_c("example") == 7UL, "");
static_assert(strlen_c("another\0example") == 7UL, "");
}
namespace test_rvalue_references
{
template < int N >
struct answer
{
static constexpr int value = N;
};
answer<1> f(int&) { return answer<1>(); }
answer<2> f(const int&) { return answer<2>(); }
answer<3> f(int&&) { return answer<3>(); }
void
test()
{
int i = 0;
const int c = 0;
static_assert(decltype(f(i))::value == 1, "");
static_assert(decltype(f(c))::value == 2, "");
static_assert(decltype(f(0))::value == 3, "");
}
}
namespace test_uniform_initialization
{
struct test
{
static const int zero {};
static const int one {1};
};
static_assert(test::zero == 0, "");
static_assert(test::one == 1, "");
}
namespace test_lambdas
{
void
test1()
{
auto lambda1 = [](){};
auto lambda2 = lambda1;
lambda1();
lambda2();
}
int
test2()
{
auto a = [](int i, int j){ return i + j; }(1, 2);
auto b = []() -> int { return '0'; }();
auto c = [=](){ return a + b; }();
auto d = [&](){ return c; }();
auto e = [a, &b](int x) mutable {
const auto identity = [](int y){ return y; };
for (auto i = 0; i < a; ++i)
a += b--;
return x + identity(a + b);
}(0);
return a + b + c + d + e;
}
int
test3()
{
const auto nullary = [](){ return 0; };
const auto unary = [](int x){ return x; };
using nullary_t = decltype(nullary);
using unary_t = decltype(unary);
const auto higher1st = [](nullary_t f){ return f(); };
const auto higher2nd = [unary](nullary_t f1){
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
};
return higher1st(nullary) + higher2nd(nullary)(unary);
}
}
namespace test_variadic_templates
{
template <int...>
struct sum;
template <int N0, int... N1toN>
struct sum<N0, N1toN...>
{
static constexpr auto value = N0 + sum<N1toN...>::value;
};
template <>
struct sum<>
{
static constexpr auto value = 0;
};
static_assert(sum<>::value == 0, "");
static_assert(sum<1>::value == 1, "");
static_assert(sum<23>::value == 23, "");
static_assert(sum<1, 2>::value == 3, "");
static_assert(sum<5, 5, 11>::value == 21, "");
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
}
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
// because of this.
namespace test_template_alias_sfinae
{
struct foo {};
template<typename T>
using member = typename T::member_type;
template<typename T>
void func(...) {}
template<typename T>
void func(member<T>*) {}
void test();
void test() { func<foo>(0); }
}
} // namespace cxx11
#endif // __cplusplus >= 201103L
]])
dnl Tests for new features in C++14
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
// If the compiler admits that it is not ready for C++14, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus < 201402L
#error "This is not a C++14 compiler"
#else
namespace cxx14
{
namespace test_polymorphic_lambdas
{
int
test()
{
const auto lambda = [](auto&&... args){
const auto istiny = [](auto x){
return (sizeof(x) == 1UL) ? 1 : 0;
};
const int aretiny[] = { istiny(args)... };
return aretiny[0];
};
return lambda(1, 1L, 1.0f, '1');
}
}
namespace test_binary_literals
{
constexpr auto ivii = 0b0000000000101010;
static_assert(ivii == 42, "wrong value");
}
namespace test_generalized_constexpr
{
template < typename CharT >
constexpr unsigned long
strlen_c(const CharT *const s) noexcept
{
auto length = 0UL;
for (auto p = s; *p; ++p)
++length;
return length;
}
static_assert(strlen_c("") == 0UL, "");
static_assert(strlen_c("x") == 1UL, "");
static_assert(strlen_c("test") == 4UL, "");
static_assert(strlen_c("another\0test") == 7UL, "");
}
namespace test_lambda_init_capture
{
int
test()
{
auto x = 0;
const auto lambda1 = [a = x](int b){ return a + b; };
const auto lambda2 = [a = lambda1(x)](){ return a; };
return lambda2();
}
}
namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
static_assert(ten_million == 100000000, "");
}
namespace test_return_type_deduction
{
auto f(int& x) { return x; }
decltype(auto) g(int& x) { return x; }
template < typename T1, typename T2 >
struct is_same
{
static constexpr auto value = false;
};
template < typename T >
struct is_same<T, T>
{
static constexpr auto value = true;
};
int
test()
{
auto x = 0;
static_assert(is_same<int, decltype(f(x))>::value, "");
static_assert(is_same<int&, decltype(g(x))>::value, "");
return x;
}
}
} // namespace cxx14
#endif // __cplusplus >= 201402L
]])
dnl Tests for new features in C++17
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
// If the compiler admits that it is not ready for C++17, why torture it?
// Hopefully, this will speed up the test.
#ifndef __cplusplus
#error "This is not a C++ compiler"
#elif __cplusplus <= 201402L
#error "This is not a C++17 compiler"
#else
#if defined(__clang__)
#define REALLY_CLANG
#else
#if defined(__GNUC__)
#define REALLY_GCC
#endif
#endif
#include <initializer_list>
#include <utility>
#include <type_traits>
namespace cxx17
{
#if !defined(REALLY_CLANG)
namespace test_constexpr_lambdas
{
// TODO: test it with clang++ from git
constexpr int foo = [](){return 42;}();
}
#endif // !defined(REALLY_CLANG)
namespace test::nested_namespace::definitions
{
}
namespace test_fold_expression
{
template<typename... Args>
int multiply(Args... args)
{
return (args * ... * 1);
}
template<typename... Args>
bool all(Args... args)
{
return (args && ...);
}
}
namespace test_extended_static_assert
{
static_assert (true);
}
namespace test_auto_brace_init_list
{
auto foo = {5};
auto bar {5};
static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
static_assert(std::is_same<int, decltype(bar)>::value);
}
namespace test_typename_in_template_template_parameter
{
template<template<typename> typename X> struct D;
}
namespace test_fallthrough_nodiscard_maybe_unused_attributes
{
int f1()
{
return 42;
}
[[nodiscard]] int f2()
{
[[maybe_unused]] auto unused = f1();
switch (f1())
{
case 17:
f1();
[[fallthrough]];
case 42:
f1();
}
return f1();
}
}
namespace test_extended_aggregate_initialization
{
struct base1
{
int b1, b2 = 42;
};
struct base2
{
base2() {
b3 = 42;
}
int b3;
};
struct derived : base1, base2
{
int d;
};
derived d1 {{1, 2}, {}, 4}; // full initialization
derived d2 {{}, {}, 4}; // value-initialized bases
}
namespace test_general_range_based_for_loop
{
struct iter
{
int i;
int& operator* ()
{
return i;
}
const int& operator* () const
{
return i;
}
iter& operator++()
{
++i;
return *this;
}
};
struct sentinel
{
int i;
};
bool operator== (const iter& i, const sentinel& s)
{
return i.i == s.i;
}
bool operator!= (const iter& i, const sentinel& s)
{
return !(i == s);
}
struct range
{
iter begin() const
{
return {0};
}
sentinel end() const
{
return {5};
}
};
void f()
{
range r {};
for (auto i : r)
{
[[maybe_unused]] auto v = i;
}
}
}
namespace test_lambda_capture_asterisk_this_by_value
{
struct t
{
int i;
int foo()
{
return [*this]()
{
return i;
}();
}
};
}
namespace test_enum_class_construction
{
enum class byte : unsigned char
{};
byte foo {42};
}
namespace test_constexpr_if
{
template <bool cond>
int f ()
{
if constexpr(cond)
{
return 13;
}
else
{
return 42;
}
}
}
namespace test_selection_statement_with_initializer
{
int f()
{
return 13;
}
int f2()
{
if (auto i = f(); i > 0)
{
return 3;
}
switch (auto i = f(); i + 4)
{
case 17:
return 2;
default:
return 1;
}
}
}
#if !defined(REALLY_CLANG)
namespace test_template_argument_deduction_for_class_templates
{
// TODO: test it with clang++ from git
template <typename T1, typename T2>
struct pair
{
pair (T1 p1, T2 p2)
: m1 {p1},
m2 {p2}
{}
T1 m1;
T2 m2;
};
void f()
{
[[maybe_unused]] auto p = pair{13, 42u};
}
}
#endif // !defined(REALLY_CLANG)
namespace test_non_type_auto_template_parameters
{
template <auto n>
struct B
{};
B<5> b1;
B<'a'> b2;
}
#if !defined(REALLY_CLANG)
namespace test_structured_bindings
{
// TODO: test it with clang++ from git
int arr[2] = { 1, 2 };
std::pair<int, int> pr = { 1, 2 };
auto f1() -> int(&)[2]
{
return arr;
}
auto f2() -> std::pair<int, int>&
{
return pr;
}
struct S
{
int x1 : 2;
volatile double y1;
};
S f3()
{
return {};
}
auto [ x1, y1 ] = f1();
auto& [ xr1, yr1 ] = f1();
auto [ x2, y2 ] = f2();
auto& [ xr2, yr2 ] = f2();
const auto [ x3, y3 ] = f3();
}
#endif // !defined(REALLY_CLANG)
#if !defined(REALLY_CLANG)
namespace test_exception_spec_type_system
{
// TODO: test it with clang++ from git
struct Good {};
struct Bad {};
void g1() noexcept;
void g2();
template<typename T>
Bad
f(T*, T*);
template<typename T1, typename T2>
Good
f(T1*, T2*);
static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
}
#endif // !defined(REALLY_CLANG)
namespace test_inline_variables
{
template<class T> void f(T)
{}
template<class T> inline T g(T)
{
return T{};
}
template<> inline void f<>(int)
{}
template<> int g<>(int)
{
return 5;
}
}
} // namespace cxx17
#endif // __cplusplus <= 201402L
]])

View File

@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
./autogen.sh
./configure
make
make distcheck
make distclean

View File

@ -0,0 +1,26 @@
Copyright (c) 2004-2021, 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.

View File

@ -0,0 +1,46 @@
ACLOCAL_AMFLAGS = -I m4 --install
EXTRA_DIST =
EXTRA_DIST += LICENSE
EXTRA_DIST += libopenmpt_modplug.pc.in
EXTRA_DIST += libmodplug.pc.in
EXTRA_DIST += test.sh
MOSTLYCLEANFILES =
dist_doc_DATA =
dist_doc_DATA += LICENSE
nobase_dist_doc_DATA =
bin_PROGRAMS =
check_PROGRAMS =
lib_LTLIBRARIES =
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA =
nobase_include_HEADERS =
if ENABLE_LIBOPENMPT_MODPLUG
lib_LTLIBRARIES += libopenmpt_modplug.la
libopenmpt_modplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined
libopenmpt_modplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS)
libopenmpt_modplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS)
libopenmpt_modplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS)
libopenmpt_modplug_la_LIBADD = $(LIBOPENMPT_LIBS)
libopenmpt_modplug_la_SOURCES =
libopenmpt_modplug_la_SOURCES += libopenmpt_modplug.c
libopenmpt_modplug_la_SOURCES += libopenmpt_modplug_cpp.cpp
endif
if ENABLE_LIBMODPLUG
pkgconfig_DATA += libmodplug.pc
lib_LTLIBRARIES += libmodplug.la
libmodplug_la_LDFLAGS = -version-info 1:0:0 -no-undefined
nobase_include_HEADERS += libmodplug/modplug.h libmodplug/sndfile.h libmodplug/stdafx.h
libmodplug_la_CPPFLAGS = -I$(srcdir)/ $(LIBOPENMPT_CPPFLAGS)
libmodplug_la_CXXFLAGS = $(LIBOPENMPT_CFLAGS)
libmodplug_la_CFLAGS = $(LIBOPENMPT_CFLAGS)
libmodplug_la_LIBADD = $(LIBOPENMPT_LIBS)
libmodplug_la_SOURCES =
libmodplug_la_SOURCES += libopenmpt_modplug.c
libmodplug_la_SOURCES += libopenmpt_modplug_cpp.cpp
endif

View File

@ -0,0 +1,2 @@
#!/usr/bin/env sh
autoreconf -i

View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -e
./autogen.sh
./configure
make distclean

View File

@ -0,0 +1,81 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* define if the compiler supports basic C++11 syntax */
#undef HAVE_CXX11
/* define if the compiler supports basic C++14 syntax */
#undef HAVE_CXX14
/* define if the compiler supports basic C++17 syntax */
#undef HAVE_CXX17
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES

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