Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -0,0 +1,683 @@
/*
* 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 "mpt/base/detect_compiler.hpp"
#include "mpt/base/detect_os.hpp"
#include "mpt/base/detect_quirks.hpp"
// set windows version early so that we can deduce dependencies from SDK version
#if MPT_OS_WINDOWS
#if !defined(WINVER) && !defined(_WIN32_WINDOWS) && !defined(_WIN32_WINNT)
#if MPT_COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#endif // MPT_COMPILER_CLANG
#define _WIN32_WINNT 0x0601 // _WIN32_WINNT_WIN7
#if MPT_COMPILER_CLANG
#pragma clang diagnostic pop
#endif // MPT_COMPILER_CLANG
#endif
#ifndef WINVER
#if defined(_WIN32_WINNT)
#define WINVER _WIN32_WINNT
#elif defined(_WIN32_WINDOWS)
#define WINVER _WIN32_WINDOWS
#endif
#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
#define MPT_INLINE_NS mptx
#elif defined(LIBOPENMPT_BUILD)
// nothing
#define MPT_INLINE_NS mpt_libopenmpt
#else
#error "either MODPLUG_TRACKER or LIBOPENMPT_BUILD has to be defined"
#endif // MODPLUG_TRACKER || LIBOPENMPT_BUILD
#if defined(LIBOPENMPT_BUILD)
// Fixup dependencies which are currently not used in libopenmpt itself,
// however might be set by some build systems like autotools anyway for simplicity.
#ifdef MPT_WITH_FLAC
#undef MPT_WITH_FLAC
#endif
#endif // LIBOPENMPT_BUILD
// 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) && !defined(MPT_BUILD_UPDATESIGNTOOL)
#define MPT_WITH_MFC
#endif // !MPT_BUILD_WINESUPPORT && !MPT_BUILD_UPDATESIGNTOOL
#endif // MPT_OS_WINDOWS
// OpenMPT-only dependencies
#define MPT_WITH_ANCIENT
#if !defined(MPT_BUILD_RETRO) && !MPT_COMPILER_CLANG && !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
#if defined(MPT_BUILD_RETRO)
#define MPT_WITH_DIRECTSOUND
#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
#define MPT_WITH_VST
// 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
#if (_WIN32_WINNT >= 0x0601)
#define MPT_WITH_MEDIAFOUNDATION
#endif
#endif
//#define MPT_WITH_MINIMP3
//#define MPT_WITH_MINIZ
#define MPT_WITH_MPG123
#define MPT_WITH_OGG
//#define MPT_WITH_STBVORBIS
#define MPT_WITH_VORBIS
#define MPT_WITH_VORBISFILE
#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
#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 global ComponentManager
#define MPT_COMPONENT_MANAGER 1
// Support for externally linked samples e.g. in MPTM files
#define MPT_EXTERNAL_SAMPLES
// Support mpt::ChartsetLocale
#define MPT_ENABLE_CHARSET_LOCALE
// Use architecture-specific intrinsics
#define MPT_ENABLE_ARCH_INTRINSICS
#if !defined(MPT_BUILD_RETRO)
#define MPT_ENABLE_UPDATE
#endif // !MPT_BUILD_RETRO
// 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
// (HACK) Define to build without any plugin support
//#define NO_PLUGINS
#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 MPT_ALL_LOGGING
#define MPT_COMPONENT_MANAGER 0
//#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 architecture-specifid intrinsics in library builds. There is just about no codepath which would use it anyway.
//#define MPT_ENABLE_ARCH_INTRINSICS
#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_PLUGINS
#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
#elif MPT_OS_MACOSX_OR_IOS
#elif MPT_OS_DJGPP
#endif
#if (MPT_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE)) || defined(MODPLUG_TRACKER)
// Use wide strings for MSVC because this is the native encoding on
// microsoft platforms.
// 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_COMPILER_MSVC && !defined(MPT_USTRING_MODE_UTF8_FORCE)) || MPT_OS_WINDOWS || 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::ToAString 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(MPT_ENABLE_ARCH_INTRINSICS)
#if MPT_COMPILER_MSVC && defined(_M_IX86)
#define MPT_ENABLE_ARCH_X86
#define MPT_ENABLE_ARCH_INTRINSICS_SSE
#define MPT_ENABLE_ARCH_INTRINSICS_SSE2
#elif MPT_COMPILER_MSVC && defined(_M_X64)
#define MPT_ENABLE_ARCH_AMD64
#define MPT_ENABLE_ARCH_INTRINSICS_SSE
#define MPT_ENABLE_ARCH_INTRINSICS_SSE2
#endif // arch
#endif // MPT_ENABLE_ARCH_INTRINSICS
#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_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(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.
#if defined(MPT_WITH_VST)
#undef MPT_WITH_VST
#endif
#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
#define _CSTRING_DISABLE_NARROW_WIDE_CONVERSION
#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
#ifndef NOMINMAX
#define NOMINMAX // Macros min(a,b) and max(a,b)
#endif
#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_FORMAT_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
#pragma warning(default:4800) // Implicit conversion from 'int' to bool. Possible information loss
#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
#if MPT_OS_WINDOWS
#ifndef UNICODE
#define MPT_CHECK_WINDOWS_IGNORE_WARNING_NO_UNICODE
#endif // !UNICODE
#endif // MPT_OS_WINDOWS
#ifdef MPT_WITH_ANCIENT
#ifdef MPT_BUILD_MSVC_SHARED
#define ANCIENT_API_DECLSPEC_DLLIMPORT
#endif
#endif
#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
#ifdef __cplusplus
#include "mpt/base/namespace.hpp"
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
#ifndef MPT_NO_NAMESPACE
using namespace ::mpt;
#endif
} // namespace mpt
OPENMPT_NAMESPACE_END
#endif

View file

@ -0,0 +1,475 @@
/*
* 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 "mpt/mutex/mutex.hpp"
#include "Logging.h"
OPENMPT_NAMESPACE_BEGIN
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(MODPLUG_TRACKER)
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 // MODPLUG_TRACKER
#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_GLOBAL(LogInformation, "Components",
MPT_UFORMAT("Constructing Component {}")
( 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_ComponentListHeadEmpty = {nullptr, nullptr};
static ComponentListEntry *g_ComponentListHead = &g_ComponentListHeadEmpty;
return g_ComponentListHead;
}
bool ComponentListPush(ComponentListEntry *entry)
{
mpt::lock_guard<mpt::mutex> guard(ComponentListMutex());
#if MPT_MSVC_BEFORE(2019,0)
// Guard against VS2017 compiler bug causing repeated initialization of inline variables.
// See <https://developercommunity.visualstudio.com/t/static-inline-variable-gets-destroyed-multiple-tim/297876>.
if(entry->next)
{
return false;
}
#endif
entry->next = ComponentListHead();
ComponentListHead() = entry;
return true;
}
static std::shared_ptr<ComponentManager> g_ComponentManager;
void ComponentManager::Init(const IComponentManagerSettings &settings)
{
MPT_LOG_GLOBAL(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_GLOBAL(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)
{
if(entry->reg)
{
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_GLOBAL(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
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,508 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/mutex/mutex.hpp"
#include <map>
#include <vector>
#include "../common/misc_util.h"
#if defined(MODPLUG_TRACKER)
#include "../misc/mptLibrary.h"
#endif
OPENMPT_NAMESPACE_BEGIN
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:
~ComponentBase() override;
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(MODPLUG_TRACKER)
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) do { if(!Bind( func , libName , #func )) { SetBindFailed(); } } while(0)
#define MPT_COMPONENT_BIND_OPTIONAL(libName, func) Bind( func , libName , #func )
#define MPT_COMPONENT_BIND_SYMBOL(libName, symbol, func) do { if(!Bind( func , libName , symbol )) { SetBindFailed(); } } 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) do { if(!Bind( func , libName , #func MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } 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) do { if(!Bind( func , libName , symbol MPT_COMPONENT_BINDWIN_SUFFIX )) { SetBindFailed(); } } 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 // MODPLUG_TRACKER
#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);
template <typename TComponent>
struct ComponentRegisterer
{
static inline void RegisterComponent(ComponentManager &componentManager)
{
componentManager.Register(ComponentFactory<TComponent>());
}
static inline ComponentListEntry &GetComponentListEntry()
{
static ComponentListEntry s_ComponentListEntry = {nullptr, &RegisterComponent};
return s_ComponentListEntry;
}
static inline bool g_ComponentRegistered = ComponentListPush(&GetComponentListEntry());
};
#define MPT_DECLARE_COMPONENT_MEMBERS(name, settingsKey) \
public: \
static constexpr const char *g_ID = #name ; \
static constexpr const char *g_SettingsKey = settingsKey ; \
static inline ComponentRegisterer< name > s_ComponentRegisterer; \
/**/
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(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;
}
#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();
}
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,86 @@
/*
* Dither.h
* --------
* Purpose: Dithering when converting to lower resolution sample formats.
* 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
#include "openmpt/all/BuildSettings.hpp"
#include "mpt/base/macros.hpp"
#include "mpt/string/types.hpp"
#include "openmpt/soundbase/Dither.hpp"
#include "openmpt/soundbase/DitherModPlug.hpp"
#include "openmpt/soundbase/DitherNone.hpp"
#include "openmpt/soundbase/DitherSimple.hpp"
#include "mptRandom.h"
#include <vector>
#include <variant>
#include <cstddef>
OPENMPT_NAMESPACE_BEGIN
using Dither_Default = Dither_Simple;
class DitherNamesOpenMPT
{
public:
static mpt::ustring GetModeName(std::size_t mode)
{
mpt::ustring result;
switch(mode)
{
case 0:
// no dither
result = MPT_USTRING("no");
break;
case 1:
// chosen by OpenMPT code, might change
result = MPT_USTRING("default");
break;
case 2:
// rectangular, 0.5 bit depth, no noise shaping (original ModPlug Tracker)
result = MPT_USTRING("0.5 bit");
break;
case 3:
// rectangular, 1 bit depth, simple 1st order noise shaping
result = MPT_USTRING("1 bit");
break;
default:
result = MPT_USTRING("");
break;
}
return result;
}
};
using DithersOpenMPT =
Dithers<std::variant<MultiChannelDither<Dither_None>, MultiChannelDither<Dither_Default>, MultiChannelDither<Dither_ModPlug>, MultiChannelDither<Dither_Simple>>, DitherNamesOpenMPT, 4, 1, 0, mpt::good_prng>;
struct DithersWrapperOpenMPT
: DithersOpenMPT
{
template <typename Trd>
DithersWrapperOpenMPT(Trd &rd, std::size_t mode = DithersOpenMPT::DefaultDither, std::size_t channels = DithersOpenMPT::DefaultChannels)
: DithersOpenMPT(rd, mode, channels)
{
return;
}
};
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,453 @@
/*
* FileReader.h
* ------------
* Purpose: A basic class for transparent reading of memory-based files.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#pragma once
#include "openmpt/all/BuildSettings.hpp"
#include "mpt/io_read/filecursor.hpp"
#include "mpt/io_read/filecursor_filename_traits.hpp"
#include "mpt/io_read/filecursor_traits_filedata.hpp"
#include "mpt/io_read/filecursor_traits_memory.hpp"
#include "mpt/io_read/filereader.hpp"
#include "openmpt/base/Types.hpp"
#include "mptPathString.h"
#include "mptStringBuffer.h"
#include <algorithm>
#include <array>
#include <limits>
#include <optional>
#include <string>
#include <vector>
#include <cstring>
#include "FileReaderFwd.h"
OPENMPT_NAMESPACE_BEGIN
namespace FileReaderExt
{
// Read a string of length srcSize into fixed-length char array destBuffer using a given read mode.
// The file cursor is advanced by "srcSize" bytes.
// Returns true if at least one byte could be read or 0 bytes were requested.
template<mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor>
bool ReadString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type srcSize)
{
typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly.
typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available
mpt::String::WriteAutoBuf(destBuffer) = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
return (realSrcSize > 0 || srcSize == 0);
}
// Read a string of length srcSize into a std::string dest using a given read mode.
// The file cursor is advanced by "srcSize" bytes.
// Returns true if at least one character could be read or 0 characters were requested.
template<mpt::String::ReadWriteMode mode, typename TFileCursor>
bool ReadString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type srcSize)
{
dest.clear();
typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly.
typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available
dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
return (realSrcSize > 0 || srcSize == 0);
}
// Read a string of length srcSize into a mpt::charbuf dest using a given read mode.
// The file cursor is advanced by "srcSize" bytes.
// Returns true if at least one character could be read or 0 characters were requested.
template<mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor>
bool ReadString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type srcSize)
{
typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly.
typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available
dest = mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize);
return (realSrcSize > 0 || srcSize == 0);
}
// Read a charset encoded string of length srcSize into a mpt::ustring dest using a given read mode.
// The file cursor is advanced by "srcSize" bytes.
// Returns true if at least one character could be read or 0 characters were requested.
template<mpt::String::ReadWriteMode mode, typename TFileCursor>
bool ReadString(TFileCursor &f, mpt::ustring &dest, mpt::Charset charset, const typename TFileCursor::pos_type srcSize)
{
dest.clear();
typename TFileCursor::PinnedView source = f.ReadPinnedView(srcSize); // Make sure the string is cached properly.
typename TFileCursor::pos_type realSrcSize = source.size(); // In case fewer bytes are available
dest = mpt::ToUnicode(charset, mpt::String::ReadBuf(mode, mpt::byte_cast<const char*>(source.data()), realSrcSize));
return (realSrcSize > 0 || srcSize == 0);
}
// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode.
// The file cursor is advanced by the string length.
// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize, typename TFileCursor>
bool ReadSizedString(TFileCursor &f, char (&destBuffer)[destSize], const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
{
mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs
if(!mpt::IO::FileReader::Read(f, srcSize))
{
return false;
}
return FileReaderExt::ReadString<mode>(f, destBuffer, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
}
// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a std::string dest using a given read mode.
// The file cursor is advanced by the string length.
// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
template<typename Tsize, mpt::String::ReadWriteMode mode, typename TFileCursor>
bool ReadSizedString(TFileCursor &f, std::string &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
{
mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs
if(!mpt::IO::FileReader::Read(f, srcSize))
{
return false;
}
return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
}
// Read a string with a preprended length field of type Tsize (must be a packed<*,*> type) into a mpt::charbuf dest using a given read mode.
// The file cursor is advanced by the string length.
// Returns true if the size field could be read and at least one character could be read or 0 characters were requested.
template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len, typename TFileCursor>
bool ReadSizedString(TFileCursor &f, mpt::charbuf<len> &dest, const typename TFileCursor::pos_type maxLength = std::numeric_limits<typename TFileCursor::pos_type>::max())
{
mpt::packed<typename Tsize::base_type, typename Tsize::endian_type> srcSize; // Enforce usage of a packed type by ensuring that the passed type has the required typedefs
if(!mpt::IO::FileReader::Read(f, srcSize))
{
return false;
}
return FileReaderExt::ReadString<mode>(f, dest, std::min(static_cast<typename TFileCursor::pos_type>(srcSize), maxLength));
}
} // namespace FileReaderExt
namespace detail {
template <typename Ttraits, typename Tfilenametraits>
using FileCursor = mpt::IO::FileCursor<Ttraits, Tfilenametraits>;
template <typename Ttraits, typename Tfilenametraits>
class FileReader
: public FileCursor<Ttraits, Tfilenametraits>
{
private:
using traits_type = Ttraits;
using filename_traits_type = Tfilenametraits;
public:
using pos_type = typename traits_type::pos_type;
using off_t = pos_type;
using data_type = typename traits_type::data_type;
using ref_data_type = typename traits_type::ref_data_type;
using shared_data_type = typename traits_type::shared_data_type;
using value_data_type = typename traits_type::value_data_type;
using shared_filename_type = typename filename_traits_type::shared_filename_type;
public:
// Initialize invalid file reader object.
FileReader()
{
return;
}
FileReader(const FileCursor<Ttraits, Tfilenametraits> &other)
: FileCursor<Ttraits, Tfilenametraits>(other)
{
return;
}
FileReader(FileCursor<Ttraits, Tfilenametraits> &&other)
: FileCursor<Ttraits, Tfilenametraits>(std::move(other))
{
return;
}
// Initialize file reader object with pointer to data and data length.
template <typename Tbyte>
explicit FileReader(mpt::span<Tbyte> bytedata, shared_filename_type filename = shared_filename_type{})
: FileCursor<Ttraits, Tfilenametraits>(bytedata, std::move(filename))
{
return;
}
// Initialize file reader object based on an existing file reader object window.
explicit FileReader(value_data_type other, shared_filename_type filename = shared_filename_type{})
: FileCursor<Ttraits, Tfilenametraits>(std::move(other), std::move(filename))
{
return;
}
public:
template <typename T>
bool Read(T &target)
{
return mpt::IO::FileReader::Read(*this, target);
}
template <typename T>
T ReadIntLE()
{
return mpt::IO::FileReader::ReadIntLE<T>(*this);
}
template <typename T>
T ReadIntBE()
{
return mpt::IO::FileReader::ReadIntLE<T>(*this);
}
template <typename T>
T ReadTruncatedIntLE(pos_type size)
{
return mpt::IO::FileReader::ReadTruncatedIntLE<T>(*this, size);
}
template <typename T>
T ReadSizedIntLE(pos_type size)
{
return mpt::IO::FileReader::ReadSizedIntLE<T>(*this, size);
}
uint32 ReadUint32LE()
{
return mpt::IO::FileReader::ReadUint32LE(*this);
}
uint32 ReadUint32BE()
{
return mpt::IO::FileReader::ReadUint32BE(*this);
}
int32 ReadInt32LE()
{
return mpt::IO::FileReader::ReadInt32LE(*this);
}
int32 ReadInt32BE()
{
return mpt::IO::FileReader::ReadInt32BE(*this);
}
uint32 ReadUint24LE()
{
return mpt::IO::FileReader::ReadUint24LE(*this);
}
uint32 ReadUint24BE()
{
return mpt::IO::FileReader::ReadUint24BE(*this);
}
uint16 ReadUint16LE()
{
return mpt::IO::FileReader::ReadUint16LE(*this);
}
uint16 ReadUint16BE()
{
return mpt::IO::FileReader::ReadUint16BE(*this);
}
int16 ReadInt16LE()
{
return mpt::IO::FileReader::ReadInt16LE(*this);
}
int16 ReadInt16BE()
{
return mpt::IO::FileReader::ReadInt16BE(*this);
}
char ReadChar()
{
return mpt::IO::FileReader::ReadChar(*this);
}
uint8 ReadUint8()
{
return mpt::IO::FileReader::ReadUint8(*this);
}
int8 ReadInt8()
{
return mpt::IO::FileReader::ReadInt8(*this);
}
float ReadFloatLE()
{
return mpt::IO::FileReader::ReadFloatLE(*this);
}
float ReadFloatBE()
{
return mpt::IO::FileReader::ReadFloatBE(*this);
}
double ReadDoubleLE()
{
return mpt::IO::FileReader::ReadDoubleLE(*this);
}
double ReadDoubleBE()
{
return mpt::IO::FileReader::ReadDoubleBE(*this);
}
template <typename T>
bool ReadStruct(T &target)
{
return mpt::IO::FileReader::ReadStruct(*this, target);
}
template <typename T>
size_t ReadStructPartial(T &target, size_t partialSize = sizeof(T))
{
return mpt::IO::FileReader::ReadStructPartial(*this, target, partialSize);
}
bool ReadNullString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
{
return mpt::IO::FileReader::ReadNullString(*this, dest, maxLength);
}
bool ReadLine(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
{
return mpt::IO::FileReader::ReadLine(*this, dest, maxLength);
}
template<typename T, std::size_t destSize>
bool ReadArray(T (&destArray)[destSize])
{
return mpt::IO::FileReader::ReadArray(*this, destArray);
}
template<typename T, std::size_t destSize>
bool ReadArray(std::array<T, destSize> &destArray)
{
return mpt::IO::FileReader::ReadArray(*this, destArray);
}
template <typename T, std::size_t destSize>
std::array<T, destSize> ReadArray()
{
return mpt::IO::FileReader::ReadArray<T, destSize>(*this);
}
template<typename T>
bool ReadVector(std::vector<T> &destVector, size_t destSize)
{
return mpt::IO::FileReader::ReadVector(*this, destVector, destSize);
}
template<size_t N>
bool ReadMagic(const char (&magic)[N])
{
return mpt::IO::FileReader::ReadMagic(*this, magic);
}
template<typename T>
bool ReadVarInt(T &target)
{
return mpt::IO::FileReader::ReadVarInt(*this, target);
}
template <typename T>
using Item = mpt::IO::FileReader::Chunk<T, FileReader>;
template <typename T>
using ChunkList = mpt::IO::FileReader::ChunkList<T, FileReader>;
template<typename T>
Item<T> ReadNextChunk(off_t alignment)
{
return mpt::IO::FileReader::ReadNextChunk<T, FileReader>(*this, alignment);
}
template<typename T>
ChunkList<T> ReadChunks(off_t alignment)
{
return mpt::IO::FileReader::ReadChunks<T, FileReader>(*this, alignment);
}
template<typename T>
ChunkList<T> ReadChunksUntil(off_t alignment, decltype(T().GetID()) stopAtID)
{
return mpt::IO::FileReader::ReadChunksUntil<T, FileReader>(*this, alignment, stopAtID);
}
template<mpt::String::ReadWriteMode mode, size_t destSize>
bool ReadString(char (&destBuffer)[destSize], const pos_type srcSize)
{
return FileReaderExt::ReadString<mode>(*this, destBuffer, srcSize);
}
template<mpt::String::ReadWriteMode mode>
bool ReadString(std::string &dest, const pos_type srcSize)
{
return FileReaderExt::ReadString<mode>(*this, dest, srcSize);
}
template<mpt::String::ReadWriteMode mode, std::size_t len>
bool ReadString(mpt::charbuf<len> &dest, const pos_type srcSize)
{
return FileReaderExt::ReadString<mode>(*this, dest, srcSize);
}
template<mpt::String::ReadWriteMode mode>
bool ReadString(mpt::ustring &dest, mpt::Charset charset, const pos_type srcSize)
{
return FileReaderExt::ReadString<mode>(*this, dest, charset, srcSize);
}
template<typename Tsize, mpt::String::ReadWriteMode mode, size_t destSize>
bool ReadSizedString(char (&destBuffer)[destSize], const pos_type maxLength = std::numeric_limits<pos_type>::max())
{
return FileReaderExt::ReadSizedString<Tsize, mode>(*this, destBuffer, maxLength);
}
template<typename Tsize, mpt::String::ReadWriteMode mode>
bool ReadSizedString(std::string &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
{
return FileReaderExt::ReadSizedString<Tsize, mode>(*this, dest, maxLength);
}
template<typename Tsize, mpt::String::ReadWriteMode mode, std::size_t len>
bool ReadSizedString(mpt::charbuf<len> &dest, const pos_type maxLength = std::numeric_limits<pos_type>::max())
{
return FileReaderExt::ReadSizedString<Tsize, mode, len>(*this, dest, maxLength);
}
};
} // namespace detail
using FileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
using FileReader = detail::FileReader<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
using ChunkReader = FileReader;
using MemoryFileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
using MemoryFileReader = detail::FileReader<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,65 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/namespace.hpp"
namespace mpt {
inline namespace MPT_INLINE_NS {
namespace IO {
class FileCursorTraitsMemory;
class FileCursorTraitsFileData;
class FileCursorFilenameTraitsNone;
template <typename Tpath>
class FileCursorFilenameTraits;
template <typename Ttraits, typename Tfilenametraits>
class FileCursor;
}
}
}
OPENMPT_NAMESPACE_BEGIN
namespace mpt {
} // namespace mpt
namespace detail {
template <typename Ttraits, typename Tfilenametraits>
using FileCursor = mpt::IO::FileCursor<Ttraits, Tfilenametraits>;
template <typename Ttraits, typename Tfilenametraits>
class FileReader;
} // namespace detail
namespace mpt {
class PathString;
} // namespace mpt
using FileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
using FileReader = detail::FileReader<mpt::IO::FileCursorTraitsFileData, mpt::IO::FileCursorFilenameTraits<mpt::PathString>>;
using MemoryFileCursor = detail::FileCursor<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
using MemoryFileReader = detail::FileReader<mpt::IO::FileCursorTraitsMemory, mpt::IO::FileCursorFilenameTraitsNone>;
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,430 @@
/*
* 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 "mpt/io/base.hpp"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#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
{
#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) noexcept
{
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 GlobalLogger::SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &text) const
{
#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::trim_right(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 std::optional<mpt::ofstream> s_logfile;
if(!s_logfile)
{
s_logfile.emplace(P_("mptrack.log"), std::ios::app);
}
if(s_logfile)
{
mpt::IO::WriteText(*s_logfile, mpt::ToCharset(mpt::CharsetLogfile, MPT_UFORMAT("{}+{} {}({}): {} [{}]\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_UFORMAT("{}({}): +{} {} [{}]\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
}
#if defined(MODPLUG_TRACKER)
namespace Trace {
#if MPT_OS_WINDOWS
// Debugging functionality will use simple globals.
std::atomic<bool> g_Enabled{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::afmt::fix(period) << std::endl;
f << "Events/second: " << mpt::afmt::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_AFORMAT("0x{}")(mpt::afmt::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::afmt::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,228 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "openmpt/logging/Logger.hpp"
#include "mptPathString.h"
#include "mptString.h"
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
#include <atomic>
#endif
OPENMPT_NAMESPACE_BEGIN
/*
Build time logging configuration:
* #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_GLOBAL(LogWarning, "sounddev", "some message");
MPT_LOG_GLOBAL(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_GLOBAL(LogWarning, "sounddev", MPT_UFORMAT("Some message: foo={}, bar=0x{}")(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_GLOBAL(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.
*/
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
{
#if defined(MPT_LOG_GLOBAL_LEVEL_STATIC)
#if (MPT_LOG_GLOBAL_LEVEL <= 0)
// All logging has beeen statically disabled.
// All logging code gets compiled and immediately dead-code eliminated.
#define MPT_LOG_IS_DISABLED
#endif
inline 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) noexcept;
#else
MPT_FORCEINLINE bool IsFacilityActive(const char * /*facility*/ ) noexcept { return true; }
#endif
class GlobalLogger final
: public ILogger
{
public:
GlobalLogger() = default;
~GlobalLogger() final = default;
public:
bool IsLevelActive(LogLevel level) const noexcept override
{
return (mpt::log::GlobalLogLevel >= level);
}
bool IsFacilityActive(const char *facility) const noexcept override
{
return mpt::log::IsFacilityActive(facility);
}
void SendLogMessage(const mpt::source_location &loc, LogLevel level, const char *facility, const mpt::ustring &message) const override;
};
#define MPT_LOG_GLOBAL(level, facility, text) MPT_LOG(mpt::log::GlobalLogger{}, (level), (facility), (text))
#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() do { if(mpt::log::Trace::g_Enabled) { mpt::log::Trace::Trace(MPT_SOURCE_LOCATION_CURRENT()); } } while(0)
} // namespace Trace
#else // !MODPLUG_TRACKER
#define MPT_TRACE_SCOPE() do { } while(0)
#define MPT_TRACE() do { } 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::afmt::right(6, mpt::afmt::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,128 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/mutex/mutex.hpp"
#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) do { } while(0)
#define OPENMPT_PROFILE_FUNCTION(cat) do { } while(0)
#endif // USE_PROFILER
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,221 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/span.hpp"
#include "mpt/exception_text/exception_text.hpp"
#include "mptAssert.h"
#include "mptBaseMacros.h"
#include "mptBaseTypes.h"
#include "mptBaseUtils.h"
#include "mptString.h"
// old
#include "mptBaseUtils.h"
#include "mptStringFormat.h"
#include "mptStringParse.h"
#include "mptTime.h"
#include <stdexcept>
#include <optional>
#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;
}
}
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
#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,139 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/source_location.hpp"
#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) do { } 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) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0)
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } 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) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0)
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0)
#ifndef MPT_ASSERT_HANDLER_NEEDED
#define MPT_ASSERT_HANDLER_NEEDED
#endif
#else // !NO_ASSERTS
#define MPT_ASSERT_NOTREACHED() do { if constexpr(!(0)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), "0"); } MPT_CHECKER_ASSUME(0); } while(0)
#define MPT_ASSERT(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0)
#define MPT_ASSERT_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } while(0)
#define MPT_ASSERT_ALWAYS(expr) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr); } MPT_CHECKER_ASSUME(expr); } while(0)
#define MPT_ASSERT_ALWAYS_MSG(expr, msg) do { if(!(expr)) { OPENMPT_NAMESPACE::AssertHandler(MPT_SOURCE_LOCATION_CURRENT(), #expr, msg); } MPT_CHECKER_ASSUME(expr); } 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
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,98 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/preprocessor.hpp"
#include "mpt/base/compiletime_warning.hpp"
#include "mpt/base/macros.hpp"
#if MPT_CXX_AT_LEAST(20)
#include <version>
#else // !C++20
#include <array>
#endif // C++20
#include <array>
#include <iterator>
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <stddef.h>
#include <stdint.h>
OPENMPT_NAMESPACE_BEGIN
#define MPT_UNREFERENCED_PARAMETER(x) MPT_UNUSED(x)
#define MPT_UNUSED_VARIABLE(x) MPT_UNUSED(x)
// C++17 std::size
#if MPT_CXX_AT_LEAST(17)
namespace mpt
{
using std::size;
} // namespace mpt
#else
namespace mpt
{
template <typename T>
MPT_CONSTEXPR11_FUN auto size(const T &v) -> decltype(v.size())
{
return v.size();
}
template <typename T, std::size_t N>
MPT_CONSTEXPR11_FUN std::size_t size(const T (&)[N]) noexcept
{
return N;
}
} // namespace mpt
#endif
// 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)
#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,54 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/integer.hpp"
#include "mpt/base/floatingpoint.hpp"
#include "mpt/base/pointer.hpp"
#include "mpt/base/check_platform.hpp"
#include "mpt/base/source_location.hpp"
#include "openmpt/base/Types.hpp"
#include "mptBaseMacros.h"
#include <array>
#include <limits>
#include <type_traits>
#include <cstddef>
#include <cstdint>
#include <stdint.h>
OPENMPT_NAMESPACE_BEGIN
constexpr inline int8 int8_min = std::numeric_limits<int8>::min();
constexpr inline int16 int16_min = std::numeric_limits<int16>::min();
constexpr inline int32 int32_min = std::numeric_limits<int32>::min();
constexpr inline int64 int64_min = std::numeric_limits<int64>::min();
constexpr inline int8 int8_max = std::numeric_limits<int8>::max();
constexpr inline int16 int16_max = std::numeric_limits<int16>::max();
constexpr inline int32 int32_max = std::numeric_limits<int32>::max();
constexpr inline int64 int64_max = std::numeric_limits<int64>::max();
constexpr inline uint8 uint8_max = std::numeric_limits<uint8>::max();
constexpr inline uint16 uint16_max = std::numeric_limits<uint16>::max();
constexpr inline uint32 uint32_max = std::numeric_limits<uint32>::max();
constexpr inline uint64 uint64_max = std::numeric_limits<uint64>::max();
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,177 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/algorithm.hpp"
#include "mpt/base/arithmetic_shift.hpp"
#include "mpt/base/array.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/base/constexpr_throw.hpp"
#include "mpt/base/math.hpp"
#include "mpt/base/memory.hpp"
#include "mpt/base/numeric.hpp"
#include "mpt/base/saturate_cast.hpp"
#include "mpt/base/saturate_round.hpp"
#include "mpt/base/utility.hpp"
#include "mpt/base/wrapping_divide.hpp"
#include "mptBaseMacros.h"
#include "mptBaseTypes.h"
#include <algorithm>
#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
template <typename T>
inline void Clear(T& x)
{
static_assert(!std::is_pointer<T>::value);
mpt::reset(x);
}
// Memset given object to zero.
template <class T>
inline void MemsetZero(T& a)
{
static_assert(std::is_pointer<T>::value == false, "Won't memset pointers.");
mpt::memclear(a);
}
// 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;
}
// Like Limit, but with upperlimit only.
template<class T, class C>
inline void LimitMax(T& val, const C upperLimit)
{
if(val > upperLimit)
val = upperLimit;
}
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 Util {
// Multiply two 32-bit integers, receive 64-bit result.
// MSVC generates unnecessarily complicated code for the unoptimized variant using _allmul.
MPT_CONSTEXPR20_FUN int64 mul32to64(int32 a, int32 b)
{
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
{
return static_cast<int64>(a) * b;
} else
{
return __emul(a, b);
}
#else
return static_cast<int64>(a) * b;
#endif
}
MPT_CONSTEXPR20_FUN uint64 mul32to64_unsigned(uint32 a, uint32 b)
{
#if MPT_COMPILER_MSVC && (defined(_M_IX86) || defined(_M_X64))
MPT_MAYBE_CONSTANT_IF(MPT_IS_CONSTANT_EVALUATED20())
{
return static_cast<uint64>(a) * b;
} else
{
return __emulu(a, b);
}
#else
return static_cast<uint64>(a) * b;
#endif
}
MPT_CONSTEXPR20_FUN int32 muldiv(int32 a, int32 b, int32 c)
{
return mpt::saturate_cast<int32>( mul32to64( a, b ) / c );
}
MPT_CONSTEXPR20_FUN 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_CONSTEXPR20_FUN uint32 muldiv_unsigned(uint32 a, uint32 b, uint32 c)
{
return mpt::saturate_cast<uint32>( mul32to64_unsigned( a, b ) / c );
}
MPT_CONSTEXPR20_FUN uint32 muldivr_unsigned(uint32 a, uint32 b, uint32 c)
{
return mpt::saturate_cast<uint32>( ( mul32to64_unsigned( a, b ) + ( c / 2u ) ) / c );
}
constexpr 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);
}
} // namespace Util
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,608 @@
/*
* 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"
#if defined(MPT_ENABLE_FILEIO)
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
#include "mpt/system_error/system_error.hpp"
#include "FileReader.h"
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#endif // MPT_ENABLE_FILEIO
#if defined(MPT_ENABLE_FILEIO)
#include <stdexcept>
#endif // MPT_ENABLE_FILEIO
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
#include <windows.h>
#include <WinIoCtl.h>
#include <io.h>
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#if defined(MPT_ENABLE_FILEIO)
#if MPT_COMPILER_MSVC
#include <stdio.h>
#include <tchar.h>
#endif // MPT_COMPILER_MSVC
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_BEGIN
#if defined(MPT_ENABLE_FILEIO)
#if !defined(MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS)
#if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR)
#if MPT_GCC_BEFORE(9,1,0)
MPT_WARNING("Warning: MinGW with GCC earlier than 9.1 detected. Standard library does neither provide std::fstream wchar_t overloads nor std::filesystem with wchar_t support. Unicode filename support is thus unavailable.")
#endif // MPT_GCC_AT_LEAST(9,1,0)
#endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
#endif // !MPT_BUILD_SILENCE_LIBOPENMPT_CONFIGURATION_WARNINGS
#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;
}
std::FILE * SafeOutputFile::internal_fopen(const mpt::PathString &filename, std::ios_base::openmode mode, FlushMode flushMode)
{
m_f = nullptr;
mpt::tstring fopen_mode = convert_mode(mode, flushMode);
if(fopen_mode.empty())
{
return nullptr;
}
std::FILE *f =
#ifdef UNICODE
_wfopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str())
#else
std::fopen(filename.AsNativePrefixed().c_str(), fopen_mode.c_str())
#endif
;
if(!f)
{
return nullptr;
}
if(mode & std::ios_base::ate)
{
if(std::fseek(f, 0, SEEK_END) != 0)
{
std::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)
{
std::fclose(m_f);
}
#endif // MPT_COMPILER_MSVC
if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit)))
{
// cppcheck-suppress exceptThrowInDestructor
throw std::ios_base::failure(std::string("Error before flushing file buffers."));
}
return;
}
if(!stream().rdbuf())
{
#if MPT_COMPILER_MSVC
if(m_f)
{
std::fclose(m_f);
}
#endif // MPT_COMPILER_MSVC
if(mayThrow && (stream().exceptions() & (std::ios::badbit | std::ios::failbit)))
{
// cppcheck-suppress exceptThrowInDestructor
throw std::ios_base::failure(std::string("Error before flushing file buffers."));
}
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(std::fflush(m_f) != 0)
{
errorOnFlush = true;
}
}
if(std::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(std::fflush(m_f) != 0)
{
errorOnFlush = true;
}
}
if(std::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 mpt::buffer_cast<std::string>(buf);
}
} // namespace mpt
#endif // MODPLUG_TRACKER
InputFile::InputFile(const mpt::PathString &filename, bool allowWholeFileCaching)
: m_IsValid(false)
, m_IsCached(false)
{
MPT_ASSERT(!filename.empty());
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(mpt::in_range<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)).size() != mpt::saturate_cast<std::size_t>(filesize))
{
m_File.close();
return false;
}
if(!mpt::IO::SeekBegin(m_File))
{
m_File.close();
return false;
}
m_IsCached = true;
m_IsValid = true;
return true;
}
}
}
m_IsValid = true;
return m_File.good();
}
bool InputFile::IsValid() const
{
return m_IsValid && m_File.good();
}
bool InputFile::IsCached() const
{
return m_IsCached;
}
mpt::PathString InputFile::GetFilename() 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);
}
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
OnDiskFileWrapper::OnDiskFileWrapper(FileCursor &file, const mpt::PathString &fileNameExtension)
: m_IsTempFile(false)
{
try
{
file.Rewind();
if(!file.GetOptionalFileName())
{
const mpt::PathString tempName = mpt::CreateTempFileName(P_("OpenMPT"), fileNameExtension);
#if MPT_OS_WINDOWS && MPT_OS_WINDOWS_WINRT
#if (_WIN32_WINNT < 0x0602)
#define MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
#endif
#endif
#ifdef MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
mpt::ofstream f(tempName, std::ios::binary);
if(!f)
{
throw std::runtime_error("Error creating temporary file.");
}
while(!file.EndOfFile())
{
FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL);
std::size_t towrite = view.size();
std::size_t written = 0;
do
{
std::size_t chunkSize = mpt::saturate_cast<std::size_t>(towrite);
bool chunkOk = false;
chunkOk = mpt::IO::WriteRaw(f, mpt::const_byte_span(view.data() + written, chunkSize));
if(!chunkOk)
{
throw std::runtime_error("Incomplete Write.");
}
towrite -= chunkSize;
written += chunkSize;
} while(towrite > 0);
}
f.close();
#else // !MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
HANDLE hFile = NULL;
#if MPT_OS_WINDOWS_WINRT
hFile = mpt::windows::CheckFileHANDLE(CreateFile2(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, NULL));
#else
hFile = mpt::windows::CheckFileHANDLE(CreateFile(tempName.AsNative().c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, NULL));
#endif
while(!file.EndOfFile())
{
FileCursor::PinnedView view = file.ReadPinnedView(mpt::IO::BUFFERSIZE_NORMAL);
std::size_t towrite = view.size();
std::size_t written = 0;
do
{
DWORD chunkSize = mpt::saturate_cast<DWORD>(towrite);
DWORD chunkDone = 0;
try
{
mpt::windows::CheckBOOL(WriteFile(hFile, view.data() + written, chunkSize, &chunkDone, NULL));
} catch(...)
{
CloseHandle(hFile);
hFile = NULL;
throw;
}
if(chunkDone != chunkSize)
{
CloseHandle(hFile);
hFile = NULL;
throw std::runtime_error("Incomplete WriteFile().");
}
towrite -= chunkDone;
written += chunkDone;
} while(towrite > 0);
}
CloseHandle(hFile);
hFile = NULL;
#endif // MPT_ONDISKFILEWRAPPER_NO_CREATEFILE
m_Filename = tempName;
m_IsTempFile = true;
} else
{
m_Filename = file.GetOptionalFileName().value();
}
} catch (const std::runtime_error &)
{
m_IsTempFile = false;
m_Filename = mpt::PathString();
}
}
OnDiskFileWrapper::~OnDiskFileWrapper()
{
if(m_IsTempFile)
{
DeleteFile(m_Filename.AsNative().c_str());
m_IsTempFile = false;
}
m_Filename = mpt::PathString();
}
bool OnDiskFileWrapper::IsValid() const
{
return !m_Filename.empty();
}
mpt::PathString OnDiskFileWrapper::GetFilename() const
{
return m_Filename;
}
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#else // !MPT_ENABLE_FILEIO
MPT_MSVC_WORKAROUND_LNK4221(mptFileIO)
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,398 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#if defined(MPT_ENABLE_FILEIO)
#include "mpt/io_read/filecursor_memory.hpp"
#include "mpt/io_read/filecursor_stdstream.hpp"
#include "../common/mptString.h"
#include "../common/mptPathString.h"
#include "../common/FileReaderFwd.h"
#if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR)
#if MPT_GCC_AT_LEAST(9,1,0)
#include <filesystem>
#endif // MPT_GCC_AT_LEAST(9,1,0)
#endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
#include <fstream>
#include <ios>
#include <ostream>
#include <streambuf>
#include <utility>
#if MPT_COMPILER_MSVC
#include <cstdio>
#endif // !MPT_COMPILER_MSVC
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_OS_WINDOWS
#endif // MODPLUG_TRACKER
#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
{
namespace detail
{
template<typename Tbase>
inline void fstream_open(Tbase & base, const mpt::PathString & filename, std::ios_base::openmode mode)
{
#if defined(MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR)
#if MPT_GCC_AT_LEAST(9,1,0)
base.open(static_cast<std::filesystem::path>(filename.AsNative()), mode);
#else // !MPT_GCC_AT_LEAST(9,1,0)
// Warning: MinGW with GCC earlier than 9.1 detected. Standard library does neither provide std::fstream wchar_t overloads nor std::filesystem with wchar_t support. Unicode filename support is thus unavailable.
base.open(mpt::ToCharset(mpt::Charset::Locale, filename.AsNative()).c_str(), mode);
#endif // MPT_GCC_AT_LEAST(9,1,0)
#else // !MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
base.open(filename.AsNativePrefixed().c_str(), mode);
#endif // MPT_COMPILER_QUIRK_WINDOWS_FSTREAM_NO_WCHAR
}
} // namespace detail
// We cannot rely on implicit conversion of mpt::PathString to std::filesystem::path when constructing std::fstream
// because of broken overload implementation in GCC libstdc++ 8, 9, 10.
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95642
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90704
class fstream
: public std::fstream
{
private:
typedef std::fstream Tbase;
public:
fstream() {}
fstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
#if MPT_COMPILER_MSVC
protected:
fstream(std::FILE * file)
: std::fstream(file)
{
}
#endif // MPT_COMPILER_MSVC
public:
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:
ifstream() {}
ifstream(const mpt::PathString & filename, std::ios_base::openmode mode = std::ios_base::in)
{
detail::fstream_open<Tbase>(*this, filename, mode);
}
#if MPT_COMPILER_MSVC
protected:
ifstream(std::FILE * file)
: std::ifstream(file)
{
}
#endif // MPT_COMPILER_MSVC
public:
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:
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(std::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
std::FILE *m_f = nullptr;
#else // !MPT_COMPILER_MSVC
mpt::ofstream m_s;
#endif // MPT_COMPILER_MSVC
#if MPT_COMPILER_MSVC
class FILEostream
: public mpt::ofstream
{
public:
FILEostream(std::FILE * file)
: mpt::ofstream(file)
{
return;
}
};
FILEostream m_s;
static mpt::tstring convert_mode(std::ios_base::openmode mode, FlushMode flushMode);
std::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_IsValid;
bool m_IsCached;
std::vector<std::byte> m_Cache;
public:
InputFile(const mpt::PathString &filename, bool allowWholeFileCaching = false);
~InputFile();
bool IsValid() const;
bool IsCached() const;
mpt::PathString GetFilename() const;
std::istream& GetStream();
mpt::const_byte_span GetCache();
private:
bool Open(const mpt::PathString &filename, bool allowWholeFileCaching = false);
};
template <typename Targ1>
inline FileCursor make_FileCursor(Targ1 &&arg1)
{
return mpt::IO::make_FileCursor<mpt::PathString>(std::forward<Targ1>(arg1));
}
template <typename Targ1, typename Targ2>
inline FileCursor make_FileCursor(Targ1 &&arg1, Targ2 &&arg2)
{
return mpt::IO::make_FileCursor<mpt::PathString>(std::forward<Targ1>(arg1), std::forward<Targ2>(arg2));
}
// templated in order to reduce header inter-dependencies
class InputFile;
template <typename TInputFile, std::enable_if_t<std::is_same<TInputFile, InputFile>::value, bool> = true>
inline FileCursor make_FileCursor(TInputFile &file)
{
if(!file.IsValid())
{
return FileCursor();
}
if(file.IsCached())
{
return mpt::IO::make_FileCursor<mpt::PathString>(file.GetCache(), std::make_shared<mpt::PathString>(file.GetFilename()));
} else
{
return mpt::IO::make_FileCursor<mpt::PathString>(file.GetStream(), std::make_shared<mpt::PathString>(file.GetFilename()));
}
}
template <typename Targ1>
inline FileCursor GetFileReader(Targ1 &&arg1)
{
return make_FileCursor(std::forward<Targ1>(arg1));
}
template <typename Targ1, typename Targ2>
inline FileCursor GetFileReader(Targ1 &&arg1, Targ2 &&arg2)
{
return make_FileCursor(std::forward<Targ1>(arg1), std::forward<Targ2>(arg2));
}
#if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
class OnDiskFileWrapper
{
private:
mpt::PathString m_Filename;
bool m_IsTempFile;
public:
OnDiskFileWrapper(FileCursor& file, const mpt::PathString& fileNameExtension = P_("tmp"));
~OnDiskFileWrapper();
public:
bool IsValid() const;
mpt::PathString GetFilename() const;
}; // class OnDiskFileWrapper
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#endif // MPT_ENABLE_FILEIO
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,872 @@
/*
* 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 "mpt/uuid/uuid.hpp"
#include "misc_util.h"
#include "mptRandom.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 defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
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 = {};
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 = {};
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 // MODPLUG_TRACKER && MPT_OS_WINDOWS
#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[0] == PC_('\\') && path[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 = {};
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 defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
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());
}
#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 // MODPLUG_TRACKER && MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && 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(mpt::global_prng()).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());
}
}
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 && MPT_OS_WINDOWS
} // 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;
}
#if MPT_CXX_AT_LEAST(20)
static inline char8_t SanitizeFilenameChar(char8_t c)
{
if( c == u8'\\' ||
c == u8'\"' ||
c == u8'/' ||
c == u8':' ||
c == u8'?' ||
c == u8'<' ||
c == u8'>' ||
c == u8'|' ||
c == u8'*')
{
c = u8'_';
}
return c;
}
#endif
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,505 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mptString.h"
#include "mpt/base/namespace.hpp"
#include <vector>
#include "openmpt/base/FlagSet.hpp"
#define MPT_DEPRECATED_PATH
//#define MPT_DEPRECATED_PATH [[deprecated]]
OPENMPT_NAMESPACE_BEGIN
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(PathString && other) noexcept
: path(std::move(other.path))
{
return;
}
PathString & assign(const PathString & other)
{
path = other.path;
return *this;
}
PathString & assign(PathString && other) noexcept
{
path = std::move(other.path);
return *this;
}
PathString & operator = (const PathString & other)
{
return assign(other);
}
PathString &operator = (PathString && other) noexcept
{
return assign(std::move(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 defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
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;
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 ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUnicode()); }
#else
MPT_DEPRECATED_PATH inline std::string ToAString(const mpt::PathString & x) { return mpt::ToCharset(mpt::Charset::Locale, x.AsNative()); }
#endif
#else
MPT_DEPRECATED_PATH inline std::string ToAString(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 defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
// Returns the application executable path or an empty string (if unknown), e.g. "C:\mptrack\"
mpt::PathString GetExecutablePath();
#if !MPT_OS_WINDOWS_WINRT
// Returns the system directory path, e.g. "C:\Windows\System32\"
mpt::PathString GetSystemPath();
#endif // !MPT_OS_WINDOWS_WINRT
#endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
#if defined(MODPLUG_TRACKER) && 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();
};
// 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 && MPT_OS_WINDOWS
} // 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)
{
mpt::append(m_MimeTypes, type.m_MimeTypes);
mpt::append(m_Extensions, type.m_Extensions);
mpt::append(m_Prefixes, type.m_Prefixes);
}
}
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,62 @@
/*
* 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"
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
#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,140 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/bit.hpp"
#include "mpt/mutex/mutex.hpp"
#ifdef MODPLUG_TRACKER
#include "mpt/random/crand.hpp"
#endif // MODPLUG_TRACKER
#include "mpt/random/default_engines.hpp"
#include "mpt/random/device.hpp"
#include "mpt/random/engine.hpp"
#include "mpt/random/engine_lcg.hpp"
#include "mpt/random/seed.hpp"
#include <limits>
#include <random>
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
{
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_CONSTEXPRINLINE typename engine_traits<Trng>::result_type min()
{
return Trng::min();
}
static MPT_CONSTEXPRINLINE typename engine_traits<Trng>::result_type max()
{
return Trng::max();
}
static MPT_CONSTEXPRINLINE 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()();
}
};
#ifdef MPT_BUILD_FUZZER
// Use deterministic seeding
using random_device = deterministc_random_device;
#else // !MPT_BUILD_FUZZER
// mpt::random_device always generates 32 bits of entropy
using random_device = mpt::sane_random_device;
#endif // MPT_BUILD_FUZZER
#ifdef MPT_BUILD_FUZZER
// Use fast PRNGs in order to not waste time fuzzing more complex PRNG
// implementations.
using fast_prng = deterministic_fast_engine;
using good_prng = deterministic_good_engine;
#else // !MPT_BUILD_FUZZER
// We cannot use std::minstd_rand here because it has not a power-of-2 sized
// output domain which we rely upon.
using fast_prng = fast_engine; // about 3 ALU operations, ~32bit of state, suited for inner loops
using good_prng = good_engine;
#endif // MPT_BUILD_FUZZER
using default_prng = mpt::good_prng;
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

View file

@ -0,0 +1,826 @@
/*
* mptString.cpp
* -------------
* 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.
*/
#include "stdafx.h"
#include "mptString.h"
#include "mpt/string/types.hpp"
#include "mpt/string/utility.hpp"
#include "mpt/string_transcode/transcode.hpp"
#include <locale>
#include <string>
#include <vector>
#include <cstdlib>
#if defined(MODPLUG_TRACKER)
#include <cwctype>
#endif // MODPLUG_TRACKER
#if defined(MODPLUG_TRACKER)
#include <wctype.h>
#endif // MODPLUG_TRACKER
#if MPT_OS_WINDOWS
#include <windows.h>
#endif // MPT_OS_WINDOWS
OPENMPT_NAMESPACE_BEGIN
/*
Quick guide to the OpenMPT string type jungle
=============================================
This quick guide is only meant as a hint. There may be valid reasons to not
honor the recommendations found here. Staying consistent with surrounding and/or
related code sections may also be important.
List of string types
--------------------
* std::string (OpenMPT, libopenmpt)
C++ string of unspecifed 8bit encoding. Try to always document the
encoding if not clear from context. Do not use unless there is an obvious
reason to do so.
* std::wstring (OpenMPT)
UTF16 (on windows) or UTF32 (otherwise). Do not use unless there is an
obvious reason to do so.
* mpt::lstring (OpenMPT)
OpenMPT locale string type. The encoding is always CP_ACP. Do not use unless
there is an obvious reason to do so.
* char* (OpenMPT, libopenmpt)
C string of unspecified encoding. Use only for static literals or in
performance critical inner loops where full control and avoidance of memory
allocations is required.
* wchar_t* (OpenMPT)
C wide string. Use only if Unicode is required for static literals or in
performance critical inner loops where full control and avoidance of memory
allocation is required.
* mpt::winstring (OpenMPT)
OpenMPT type-safe string to interface with native WinAPI, either encoded in
locale/CP_ACP (if !UNICODE) or UTF16 (if UNICODE).
* CString (OpenMPT)
MFC string type, either encoded in locale/CP_ACP (if !UNICODE) or UTF16 (if
UNICODE). Specify literals with _T(""). Use in MFC GUI code.
* CStringA (OpenMPT)
MFC ANSI string type. The encoding is always CP_ACP. Do not use.
* CStringW (OpenMPT)
MFC Unicode string type. Do not use.
* mpt::PathString (OpenMPT, libopenmpt)
String type representing paths and filenames. Always use for these in order
to avoid potentially lossy conversions. Use P_("") macro for
literals.
* mpt::ustring (OpenMPT, libopenmpt)
The default unicode string type. Can be encoded in UTF8 or UTF16 or UTF32,
depending on MPT_USTRING_MODE_* and sizeof(wchar_t). Literals can written as
U_(""). Use as your default string type if no other string type is
a measurably better fit.
* MPT_UTF8 (OpenMPT, libopenmpt)
Macro that generates a mpt::ustring from string literals containing
non-ascii characters. In order to keep the source code in ascii encoding,
always express non-ascii characters using explicit \x23 escaping. Note that
depending on the underlying type of mpt::ustring, MPT_UTF8 *requires* a
runtime conversion. Only use for string literals containing non-ascii
characters (use MPT_USTRING otherwise).
* MPT_ULITERAL / MPT_UCHAR / mpt::uchar (OpenMPT, libopenmpt)
Macros which generate string literals, char literals and the char literal
type respectively. These are especially useful in constexpr contexts or
global data where MPT_USTRING is either unusable or requires a global
contructor to run. Do NOT use as a performance optimization in place of
MPT_USTRING however, because MPT_USTRING can be converted to C++11/14 user
defined literals eventually, while MPT_ULITERAL cannot because of constexpr
requirements.
* mpt::RawPathString (OpenMPT, libopenmpt)
Internal representation of mpt::PathString. Only use for parsing path
fragments.
* mpt::u8string (OpenMPT, libopenmpt)
Internal representation of mpt::ustring. Do not use directly. Ever.
* std::basic_string<char> (OpenMPT)
Same as std::string. Do not use std::basic_string in the templated form.
* std::basic_string<wchar_t> (OpenMPT)
Same as std::wstring. Do not use std::basic_string in the templated form.
The following string types are available in order to avoid the need to overload
functions on a huge variety of string types. Use only ever as function argument
types.
Note that the locale charset is not available on all libopenmpt builds (in which
case the option is ignored or a sensible fallback is used; these types are
always available).
All these types publicly inherit from mpt::ustring and do not contain any
additional state. This means that they work the same way as mpt::ustring does
and do support type-slicing for both, read and write accesses.
These types only add conversion constructors for all string types that have a
defined encoding and for all 8bit string types using the specified encoding
heuristic.
* AnyUnicodeString (OpenMPT, libopenmpt)
Is constructible from any Unicode string.
* AnyString (OpenMPT, libopenmpt)
Tries to do the smartest auto-magic we can do.
* AnyStringLocale (OpenMPT, libopenmpt)
char-based strings are assumed to be in locale encoding.
* AnyStringUTF8orLocale (OpenMPT, libopenmpt)
char-based strings are tried in UTF8 first, if this fails, locale is used.
* AnyStringUTF8 (OpenMPT, libopenmpt)
char-based strings are assumed to be in UTF8.
Encoding of 8bit strings
------------------------
8bit strings have an unspecified encoding. When the string is contained within a
CSoundFile object, the encoding is most likely CSoundFile::GetCharsetInternal(),
otherwise, try to gather the most probable encoding from surrounding or related
code sections.
Decision tree to help deciding which string type to use
-------------------------------------------------------
if in libopenmpt
if in libopenmpt c++ interface
T = std::string, the encoding is utf8
elif in libopenmpt c interface
T = char*, the encoding is utf8
elif performance critical inner loop
T = char*, document the encoding if not clear from context
elif string literal containing non-ascii characters
T = MPT_UTF8
elif path or file
if parsing path fragments
T = mpt::RawPathString
template your function on the concrete underlying string type
(std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS
else
T = mpt::PathString
fi
else
T = mpt::ustring
fi
else
if performance critical inner loop
if needs unicode support
T = mpt::uchar* / MPT_ULITERAL
else
T = char*, document the encoding if not clear from context
fi
elif string literal containing non-ascii characters
T = MPT_UTF8
elif path or file
if parsing path fragments
T = mpt::RawPathString
template your function on the concrete underlying string type
(std::string and std::wstring) or use preprocessor MPT_OS_WINDOWS
else
T = mpt::PathString
fi
elif winapi interfacing code
T = mpt::winstring
elif mfc/gui code
T = CString
else
if constexpr context or global data
T = mpt::uchar* / MPT_ULITERAL
else
T = mpt::ustring
fi
fi
fi
This boils down to: Prefer mpt::PathString and mpt::ustring, and only use any
other string type if there is an obvious reason to do so.
Character set conversions
-------------------------
Character set conversions in OpenMPT are always fuzzy.
Behaviour in case of an invalid source encoding and behaviour in case of an
unrepresentable destination encoding can be any of the following:
* The character is replaced by some replacement character ('?' or L'\ufffd' in
most cases).
* The character is replaced by a similar character (either semantically
similiar or visually similar).
* The character is transcribed with some ASCII text.
* The character is discarded.
* Conversion stops at this very character.
Additionally. conversion may stop or continue on \0 characters in the middle of
the string.
Behaviour can vary from one conversion tuple to any other.
If you need to ensure lossless conversion, do a roundtrip conversion and check
for equality.
Unicode handling
----------------
OpenMPT is generally not aware of and does not handle different Unicode
normalization forms.
You should be aware of the following possibilities:
* Conversion between UTF8, UTF16, UTF32 may or may not change between NFC and
NFD.
* Conversion from any non-Unicode 8bit encoding can result in both, NFC or NFD
forms.
* Conversion to any non-Unicode 8bit encoding may or may not involve
conversion to NFC, NFD, NFKC or NFKD during the conversion. This in
particular means that conversion of decomposed german umlauts to ISO8859-1
may fail.
* Changing the normalization form of path strings may render the file
inaccessible.
Unicode BOM may or may not be preserved and/or discarded during conversion.
Invalid Unicode code points may be treated as invalid or as valid characters
when converting between different Unicode encodings.
Interfacing with WinAPI
-----------------------
When in MFC code, use CString.
When in non MFC code, either use std::wstring when directly interfacing with
APIs only available in WCHAR variants, or use mpt::winstring and
mpt::WinStringBuf helpers otherwise.
Specify TCHAR string literals with _T("foo") in mptrack/, and with TEXT("foo")
in common/ or sounddev/. _T() requires <tchar.h> which is specific to the MSVC
runtime and not portable across compilers. TEXT() is from <windows.h>. We use
_T() in mptrack/ only because it is shorter.
*/
namespace mpt { namespace String {
#define C(x) (mpt::char_value((x)))
// AMS1 actually only supports ASCII plus the modified control characters and no high chars at all.
// Just default to CP437 for those to keep things simple.
static constexpr char32_t CharsetTableCP437AMS[256] = {
C(' '),0x0001,0x0002,0x0003,0x00e4,0x0005,0x00e5,0x0007,0x0008,0x0009,0x000a,0x000b,0x000c,0x000d,0x00c4,0x00c5, // differs from CP437
0x0010,0x0011,0x0012,0x0013,0x00f6,0x0015,0x0016,0x0017,0x0018,0x00d6,0x001a,0x001b,0x001c,0x001d,0x001e,0x001f, // differs from CP437
0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302,
0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
};
// AMS2: Looking at Velvet Studio's bitmap font (TPIC32.PCX), these appear to be the only supported non-ASCII chars.
static constexpr char32_t CharsetTableCP437AMS2[256] = {
C(' '),0x00a9,0x221a,0x00b7,C('0'),C('1'),C('2'),C('3'),C('4'),C('5'),C('6'),C('7'),C('8'),C('9'),C('A'),C('B'), // differs from CP437
C('C'),C('D'),C('E'),C('F'),C(' '),0x00a7,C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '),C(' '), // differs from CP437
0x0020,0x0021,0x0022,0x0023,0x0024,0x0025,0x0026,0x0027,0x0028,0x0029,0x002a,0x002b,0x002c,0x002d,0x002e,0x002f,
0x0030,0x0031,0x0032,0x0033,0x0034,0x0035,0x0036,0x0037,0x0038,0x0039,0x003a,0x003b,0x003c,0x003d,0x003e,0x003f,
0x0040,0x0041,0x0042,0x0043,0x0044,0x0045,0x0046,0x0047,0x0048,0x0049,0x004a,0x004b,0x004c,0x004d,0x004e,0x004f,
0x0050,0x0051,0x0052,0x0053,0x0054,0x0055,0x0056,0x0057,0x0058,0x0059,0x005a,0x005b,0x005c,0x005d,0x005e,0x005f,
0x0060,0x0061,0x0062,0x0063,0x0064,0x0065,0x0066,0x0067,0x0068,0x0069,0x006a,0x006b,0x006c,0x006d,0x006e,0x006f,
0x0070,0x0071,0x0072,0x0073,0x0074,0x0075,0x0076,0x0077,0x0078,0x0079,0x007a,0x007b,0x007c,0x007d,0x007e,0x2302,
0x00c7,0x00fc,0x00e9,0x00e2,0x00e4,0x00e0,0x00e5,0x00e7,0x00ea,0x00eb,0x00e8,0x00ef,0x00ee,0x00ec,0x00c4,0x00c5,
0x00c9,0x00e6,0x00c6,0x00f4,0x00f6,0x00f2,0x00fb,0x00f9,0x00ff,0x00d6,0x00dc,0x00a2,0x00a3,0x00a5,0x20a7,0x0192,
0x00e1,0x00ed,0x00f3,0x00fa,0x00f1,0x00d1,0x00aa,0x00ba,0x00bf,0x2310,0x00ac,0x00bd,0x00bc,0x00a1,0x00ab,0x00bb,
0x2591,0x2592,0x2593,0x2502,0x2524,0x2561,0x2562,0x2556,0x2555,0x2563,0x2551,0x2557,0x255d,0x255c,0x255b,0x2510,
0x2514,0x2534,0x252c,0x251c,0x2500,0x253c,0x255e,0x255f,0x255a,0x2554,0x2569,0x2566,0x2560,0x2550,0x256c,0x2567,
0x2568,0x2564,0x2565,0x2559,0x2558,0x2552,0x2553,0x256b,0x256a,0x2518,0x250c,0x2588,0x2584,0x258c,0x2590,0x2580,
0x03b1,0x00df,0x0393,0x03c0,0x03a3,0x03c3,0x00b5,0x03c4,0x03a6,0x0398,0x03a9,0x03b4,0x221e,0x03c6,0x03b5,0x2229,
0x2261,0x00b1,0x2265,0x2264,0x2320,0x2321,0x00f7,0x2248,0x00b0,0x2219,0x00b7,0x221a,0x207f,0x00b2,0x25a0,0x00a0
};
#undef C
// templated on 8bit strings because of type-safe variants
template<typename Tdststring>
static Tdststring EncodeImpl(Charset charset, const mpt::widestring &src)
{
static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
static_assert(mpt::is_character<typename Tdststring::value_type>::value);
switch(charset)
{
#if defined(MPT_ENABLE_CHARSET_LOCALE)
case Charset::Locale: return mpt::encode<Tdststring>(mpt::logical_encoding::locale, src); break;
#endif
case Charset::UTF8: return mpt::encode<Tdststring>(mpt::common_encoding::utf8, src); break;
case Charset::ASCII: return mpt::encode<Tdststring>(mpt::common_encoding::ascii, src); break;
case Charset::ISO8859_1: return mpt::encode<Tdststring>(mpt::common_encoding::iso8859_1, src); break;
case Charset::ISO8859_15: return mpt::encode<Tdststring>(mpt::common_encoding::iso8859_15, src); break;
case Charset::CP850: return mpt::encode<Tdststring>(mpt::common_encoding::cp850, src); break;
case Charset::CP437: return mpt::encode<Tdststring>(mpt::common_encoding::cp437, src); break;
case Charset::CP437AMS: return mpt::encode<Tdststring>(CharsetTableCP437AMS, src); break;
case Charset::CP437AMS2: return mpt::encode<Tdststring>(CharsetTableCP437AMS2, src); break;
case Charset::Windows1252: return mpt::encode<Tdststring>(mpt::common_encoding::windows1252, src); break;
case Charset::Amiga: return mpt::encode<Tdststring>(mpt::common_encoding::amiga, src); break;
case Charset::RISC_OS: return mpt::encode<Tdststring>(mpt::common_encoding::riscos, src); break;
case Charset::ISO8859_1_no_C1: return mpt::encode<Tdststring>(mpt::common_encoding::iso8859_1_no_c1, src); break;
case Charset::ISO8859_15_no_C1: return mpt::encode<Tdststring>(mpt::common_encoding::iso8859_15_no_c1, src); break;
case Charset::Amiga_no_C1: return mpt::encode<Tdststring>(mpt::common_encoding::amiga_no_c1, src); break;
}
return Tdststring();
}
// templated on 8bit strings because of type-safe variants
template<typename Tsrcstring>
static mpt::widestring DecodeImpl(Charset charset, const Tsrcstring &src)
{
static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
static_assert(mpt::is_character<typename Tsrcstring::value_type>::value);
switch(charset)
{
#if defined(MPT_ENABLE_CHARSET_LOCALE)
case Charset::Locale: return mpt::decode<Tsrcstring>(mpt::logical_encoding::locale, src); break;
#endif
case Charset::UTF8: return mpt::decode<Tsrcstring>(mpt::common_encoding::utf8, src); break;
case Charset::ASCII: return mpt::decode<Tsrcstring>(mpt::common_encoding::ascii, src); break;
case Charset::ISO8859_1: return mpt::decode<Tsrcstring>(mpt::common_encoding::iso8859_1, src); break;
case Charset::ISO8859_15: return mpt::decode<Tsrcstring>(mpt::common_encoding::iso8859_15, src); break;
case Charset::CP850: return mpt::decode<Tsrcstring>(mpt::common_encoding::cp850, src); break;
case Charset::CP437: return mpt::decode<Tsrcstring>(mpt::common_encoding::cp437, src); break;
case Charset::CP437AMS: return mpt::decode<Tsrcstring>(CharsetTableCP437AMS, src); break;
case Charset::CP437AMS2: return mpt::decode<Tsrcstring>(CharsetTableCP437AMS2, src); break;
case Charset::Windows1252: return mpt::decode<Tsrcstring>(mpt::common_encoding::windows1252, src); break;
case Charset::Amiga: return mpt::decode<Tsrcstring>(mpt::common_encoding::amiga, src); break;
case Charset::RISC_OS: return mpt::decode<Tsrcstring>(mpt::common_encoding::riscos, src); break;
case Charset::ISO8859_1_no_C1: return mpt::decode<Tsrcstring>(mpt::common_encoding::iso8859_1_no_c1, src); break;
case Charset::ISO8859_15_no_C1: return mpt::decode<Tsrcstring>(mpt::common_encoding::iso8859_15_no_c1, src); break;
case Charset::Amiga_no_C1: return mpt::decode<Tsrcstring>(mpt::common_encoding::amiga_no_c1, src); break;
}
return mpt::widestring();
}
// templated on 8bit strings because of type-safe variants
template<typename Tdststring, typename Tsrcstring>
static Tdststring ConvertImpl(Charset to, Charset from, const Tsrcstring &src)
{
static_assert(sizeof(typename Tdststring::value_type) == sizeof(char));
static_assert(sizeof(typename Tsrcstring::value_type) == sizeof(char));
if(to == from)
{
const typename Tsrcstring::value_type * src_beg = src.data();
const typename Tsrcstring::value_type * src_end = src_beg + src.size();
return Tdststring(reinterpret_cast<const typename Tdststring::value_type *>(src_beg), reinterpret_cast<const typename Tdststring::value_type *>(src_end));
}
return EncodeImpl<Tdststring>(to, DecodeImpl(from, src));
}
} // namespace String
bool IsUTF8(const std::string &str)
{
return mpt::is_utf8(str);
}
#if MPT_WSTRING_CONVERT
std::wstring ToWide(Charset from, const std::string &str)
{
return String::DecodeImpl(from, str);
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::wstring ToWide(const mpt::lstring &str)
{
return String::DecodeImpl(Charset::Locale, str);
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#endif
#if MPT_WSTRING_CONVERT
std::string ToCharset(Charset to, const std::wstring &str)
{
return String::EncodeImpl<std::string>(to, str);
}
#endif
std::string ToCharset(Charset to, Charset from, const std::string &str)
{
return String::ConvertImpl<std::string>(to, from, str);
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
std::string ToCharset(Charset to, const mpt::lstring &str)
{
return String::ConvertImpl<std::string>(to, Charset::Locale, str);
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#if MPT_WSTRING_CONVERT
mpt::lstring ToLocale(const std::wstring &str)
{
return String::EncodeImpl<mpt::lstring>(Charset::Locale, str);
}
#endif
mpt::lstring ToLocale(Charset from, const std::string &str)
{
return String::ConvertImpl<mpt::lstring>(Charset::Locale, from, str);
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
#if MPT_WSTRING_CONVERT
mpt::winstring ToWin(const std::wstring &str)
{
#ifdef UNICODE
return str;
#else
return ToLocale(str);
#endif
}
#endif
mpt::winstring ToWin(Charset from, const std::string &str)
{
#ifdef UNICODE
return ToWide(from, str);
#else
return ToLocale(from, str);
#endif
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::winstring ToWin(const mpt::lstring &str)
{
#ifdef UNICODE
return ToWide(str);
#else
return str;
#endif
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#endif // MPT_OS_WINDOWS
#if defined(MPT_WITH_MFC)
CString ToCString(const std::wstring &str)
{
#ifdef UNICODE
return str.c_str();
#else
return ToCharset(Charset::Locale, str).c_str();
#endif
}
CString ToCString(Charset from, const std::string &str)
{
#ifdef UNICODE
return ToWide(from, str).c_str();
#else
return ToCharset(Charset::Locale, from, str).c_str();
#endif
}
std::wstring ToWide(const CString &str)
{
#ifdef UNICODE
return str.GetString();
#else
return ToWide(Charset::Locale, str.GetString());
#endif
}
std::string ToCharset(Charset to, const CString &str)
{
#ifdef UNICODE
return ToCharset(to, str.GetString());
#else
return ToCharset(to, Charset::Locale, str.GetString());
#endif
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
CString ToCString(const mpt::lstring &str)
{
#ifdef UNICODE
return ToWide(str).c_str();
#else
return str.c_str();
#endif
}
mpt::lstring ToLocale(const CString &str)
{
#ifdef UNICODE
return String::EncodeImpl<mpt::lstring>(Charset::Locale, str.GetString());
#else
return str.GetString();
#endif
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
mpt::winstring ToWin(const CString &str)
{
return str.GetString();
}
#endif // MPT_OS_WINDOWS
#endif // MPT_WITH_MFC
#if MPT_USTRING_MODE_WIDE
// inline
#else // !MPT_USTRING_MODE_WIDE
#if MPT_WSTRING_CONVERT
mpt::ustring ToUnicode(const std::wstring &str)
{
return String::EncodeImpl<mpt::ustring>(mpt::Charset::UTF8, str);
}
#endif
mpt::ustring ToUnicode(Charset from, const std::string &str)
{
return String::ConvertImpl<mpt::ustring>(mpt::Charset::UTF8, from, str);
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::ustring ToUnicode(const mpt::lstring &str)
{
return String::ConvertImpl<mpt::ustring>(mpt::Charset::UTF8, mpt::Charset::Locale, str);
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if defined(MPT_WITH_MFC)
mpt::ustring ToUnicode(const CString &str)
{
#ifdef UNICODE
return String::EncodeImpl<mpt::ustring>(mpt::Charset::UTF8, str.GetString());
#else // !UNICODE
return String::ConvertImpl<mpt::ustring, std::string>(mpt::Charset::UTF8, mpt::Charset::Locale, str.GetString());
#endif // UNICODE
}
#endif // MPT_WITH_MFC
#endif // MPT_USTRING_MODE_WIDE
#if MPT_USTRING_MODE_WIDE
// nothing, std::wstring overloads will catch all stuff
#else // !MPT_USTRING_MODE_WIDE
#if MPT_WSTRING_CONVERT
std::wstring ToWide(const mpt::ustring &str)
{
return String::DecodeImpl<mpt::ustring>(mpt::Charset::UTF8, str);
}
#endif
std::string ToCharset(Charset to, const mpt::ustring &str)
{
return String::ConvertImpl<std::string, mpt::ustring>(to, mpt::Charset::UTF8, str);
}
#if defined(MPT_ENABLE_CHARSET_LOCALE)
mpt::lstring ToLocale(const mpt::ustring &str)
{
return String::ConvertImpl<mpt::lstring, mpt::ustring>(mpt::Charset::Locale, mpt::Charset::UTF8, str);
}
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
mpt::winstring ToWin(const mpt::ustring &str)
{
#ifdef UNICODE
return String::DecodeImpl<mpt::ustring>(mpt::Charset::UTF8, str);
#else
return String::ConvertImpl<mpt::lstring, mpt::ustring>(mpt::Charset::Locale, mpt::Charset::UTF8, str);
#endif
}
#endif // MPT_OS_WINDOWS
#if defined(MPT_WITH_MFC)
CString ToCString(const mpt::ustring &str)
{
#ifdef UNICODE
return String::DecodeImpl<mpt::ustring>(mpt::Charset::UTF8, str).c_str();
#else // !UNICODE
return String::ConvertImpl<std::string, mpt::ustring>(mpt::Charset::Locale, mpt::Charset::UTF8, str).c_str();
#endif // UNICODE
}
#endif // MPT_WITH_MFC
#endif // MPT_USTRING_MODE_WIDE
static mpt::Charset CharsetFromCodePage(uint16 codepage, mpt::Charset fallback, bool * isFallback = nullptr)
{
mpt::Charset result = fallback;
switch(codepage)
{
case 65001:
result = mpt::Charset::UTF8;
if(isFallback) *isFallback = false;
break;
case 20127:
result = mpt::Charset::ASCII;
if(isFallback) *isFallback = false;
break;
case 28591:
result = mpt::Charset::ISO8859_1;
if(isFallback) *isFallback = false;
break;
case 28605:
result = mpt::Charset::ISO8859_15;
if(isFallback) *isFallback = false;
break;
case 437:
result = mpt::Charset::CP437;
if(isFallback) *isFallback = false;
break;
case 1252:
result = mpt::Charset::Windows1252;
if(isFallback) *isFallback = false;
break;
default:
result = fallback;
if(isFallback) *isFallback = true;
break;
}
return result;
}
mpt::ustring ToUnicode(uint16 codepage, mpt::Charset fallback, const std::string &str)
{
#if MPT_OS_WINDOWS
mpt::ustring result;
bool noCharsetMatch = true;
mpt::Charset charset = mpt::CharsetFromCodePage(codepage, fallback, &noCharsetMatch);
if(noCharsetMatch && mpt::has_codepage(codepage))
{
result = mpt::ToUnicode(mpt::decode<std::string>(codepage, str));
} else
{
result = mpt::ToUnicode(charset, str);
}
return result;
#else // !MPT_OS_WINDOWS
return mpt::ToUnicode(mpt::CharsetFromCodePage(codepage, fallback), str);
#endif // MPT_OS_WINDOWS
}
char ToLowerCaseAscii(char c)
{
return mpt::to_lower_ascii(c);
}
char ToUpperCaseAscii(char c)
{
return mpt::to_upper_ascii(c);
}
std::string ToLowerCaseAscii(std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), static_cast<char(*)(char)>(&mpt::ToLowerCaseAscii));
return s;
}
std::string ToUpperCaseAscii(std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), static_cast<char(*)(char)>(&mpt::ToUpperCaseAscii));
return s;
}
int CompareNoCaseAscii(const char *a, const char *b, std::size_t n)
{
while(n--)
{
unsigned char ac = mpt::char_value(mpt::ToLowerCaseAscii(*a));
unsigned char bc = mpt::char_value(mpt::ToLowerCaseAscii(*b));
if(ac != bc)
{
return ac < bc ? -1 : 1;
} else if(!ac && !bc)
{
return 0;
}
++a;
++b;
}
return 0;
}
int CompareNoCaseAscii(std::string_view a, std::string_view b)
{
for(std::size_t i = 0; i < std::min(a.length(), b.length()); ++i)
{
unsigned char ac = mpt::char_value(mpt::ToLowerCaseAscii(a[i]));
unsigned char bc = mpt::char_value(mpt::ToLowerCaseAscii(b[i]));
if(ac != bc)
{
return ac < bc ? -1 : 1;
} else if(!ac && !bc)
{
return 0;
}
}
if(a.length() == b.length())
{
return 0;
}
return a.length() < b.length() ? -1 : 1;
}
int CompareNoCaseAscii(const std::string &a, const std::string &b)
{
return CompareNoCaseAscii(std::string_view(a), std::string_view(b));
}
#if defined(MODPLUG_TRACKER)
mpt::ustring ToLowerCase(const mpt::ustring &s)
{
#if defined(MPT_WITH_MFC)
#if defined(UNICODE)
CString tmp = mpt::ToCString(s);
tmp.MakeLower();
return mpt::ToUnicode(tmp);
#else // !UNICODE
CStringW tmp = mpt::ToWide(s).c_str();
tmp.MakeLower();
return mpt::ToUnicode(tmp.GetString());
#endif // UNICODE
#else // !MPT_WITH_MFC
std::wstring ws = mpt::ToWide(s);
std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower);
return mpt::ToUnicode(ws);
#endif // MPT_WITH_MFC
}
mpt::ustring ToUpperCase(const mpt::ustring &s)
{
#if defined(MPT_WITH_MFC)
#if defined(UNICODE)
CString tmp = mpt::ToCString(s);
tmp.MakeUpper();
return mpt::ToUnicode(tmp);
#else // !UNICODE
CStringW tmp = mpt::ToWide(s).c_str();
tmp.MakeUpper();
return mpt::ToUnicode(tmp.GetString());
#endif // UNICODE
#else // !MPT_WITH_MFC
std::wstring ws = mpt::ToWide(s);
std::transform(ws.begin(), ws.end(), ws.begin(), &std::towlower);
return mpt::ToUnicode(ws);
#endif // MPT_WITH_MFC
}
#endif // MODPLUG_TRACKER
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,446 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/alloc.hpp"
#include "mpt/base/span.hpp"
#include "mpt/string/types.hpp"
#include "mpt/string/utility.hpp"
#include "mptBaseTypes.h"
#include <algorithm>
#include <limits>
#include <string>
#include <string_view>
#include <cstring>
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
namespace String
{
template <typename Tstring, typename Tstring2, typename Tstring3>
inline Tstring Replace(Tstring str, const Tstring2 &oldStr, const Tstring3 &newStr)
{
return mpt::replace(str, oldStr, newStr);
}
} // namespace String
enum class Charset {
UTF8,
ASCII, // strictly 7-bit ASCII
ISO8859_1,
ISO8859_15,
CP850,
CP437,
CP437AMS,
CP437AMS2,
Windows1252,
Amiga,
RISC_OS,
ISO8859_1_no_C1,
ISO8859_15_no_C1,
Amiga_no_C1,
#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
// getenv
#if defined(MPT_ENABLE_CHARSET_LOCALE)
inline constexpr Charset CharsetEnvironment = Charset::Locale;
#else
inline constexpr Charset CharsetEnvironment = 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
// 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);
#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
#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::trim_right(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,299 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/string/buffer.hpp"
#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
namespace String {
using mpt::ReadTypedBuf;
using mpt::WriteTypedBuf;
} // namespace String
namespace String {
using mpt::ReadAutoBuf;
using mpt::WriteAutoBuf;
} // namespace String
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, const std::array<Tchar, size> &buf)
{
return StringModeBufRefImpl<typename std::add_const<Tchar>::type>(buf.data(), size, mode);
}
template <typename Tchar, std::size_t size>
inline StringModeBufRefImpl<typename std::add_const<Tchar>::type> ReadBuf(String::ReadWriteMode mode, const 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, const 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, std::array<Tchar, size> &buf)
{
return StringModeBufRefImpl<Tchar>(buf.data(), 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 modecharbuf
{
public:
typedef char Tchar;
using char_type = Tchar;
using string_type = std::basic_string<Tchar>;
public:
Tchar buf[len];
public:
modecharbuf() = default;
modecharbuf(const modecharbuf &) = default;
modecharbuf(modecharbuf &&) = default;
modecharbuf & operator = (const modecharbuf &) = default;
modecharbuf & operator = (modecharbuf &&) = default;
operator string_type () const
{
return mpt::String::ReadBuf(mode, buf);
}
bool empty() const
{
return mpt::String::ReadBuf(mode, buf).empty();
}
modecharbuf & 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>
constexpr bool declare_binary_safe(const typename mpt::modecharbuf<len, mode> &) { return true; }
//struct is_binary_safe<typename mpt::modecharbuf<len, mode>> : public std::true_type { };
static_assert(sizeof(mpt::modecharbuf<7, mpt::String::ReadWriteMode::nullTerminated>) == 7);
static_assert(alignof(mpt::modecharbuf<7, mpt::String::ReadWriteMode::nullTerminated>) == 1);
static_assert(std::is_standard_layout<mpt::modecharbuf<7, mpt::String::ReadWriteMode::nullTerminated>>::value);
#ifdef MODPLUG_TRACKER
#if MPT_OS_WINDOWS
namespace String {
using mpt::ReadWinBuf;
using mpt::WriteWinBuf;
} // namespace String
#if defined(MPT_WITH_MFC)
namespace String {
using mpt::ReadCStringBuf;
using mpt::WriteCStringBuf;
} // 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
#if MPT_COMPILER_MSVC
#pragma warning(pop)
#endif // MPT_COMPILER_MSVC
} // namespace String
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,143 @@
/*
* mptStringFormat.cpp
* -------------------
* Purpose: Convert other types to strings.
* Notes : Currently none.
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptStringFormat.h"
#include "mpt/format/default_floatingpoint.hpp"
#include "mpt/format/default_integer.hpp"
#include "mpt/format/helpers.hpp"
#include "mpt/format/simple_floatingpoint.hpp"
#include "mpt/format/simple_integer.hpp"
OPENMPT_NAMESPACE_BEGIN
namespace mpt
{
std::string ToAString(const bool & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const signed char & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const unsigned char & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const signed short & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const unsigned short & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const signed int & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const unsigned int & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const signed long & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const unsigned long & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const signed long long & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const unsigned long long & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const float & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const double & x) { return mpt::format_value_default<std::string>(x); }
std::string ToAString(const long double & x) { return mpt::format_value_default<std::string>(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
mpt::ustring ToUString(const bool & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const signed char & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const unsigned char & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const signed short & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const unsigned short & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const signed int & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const unsigned int & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const signed long & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const unsigned long & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const signed long long & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const unsigned long long & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const float & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const double & x) { return mpt::format_value_default<mpt::ustring>(x); }
mpt::ustring ToUString(const long double & x) { return mpt::format_value_default<mpt::ustring>(x); }
#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 mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const signed char & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const unsigned char & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const signed short & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const unsigned short & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const signed int & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const unsigned int & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const signed long & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const unsigned long & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const signed long long & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const unsigned long long & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const float & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const double & x) { return mpt::format_value_default<std::wstring>(x); }
std::wstring ToWString(const long double & x) { return mpt::format_value_default<std::wstring>(x); }
#endif
std::string FormatValA(const bool & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const signed char & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const signed short & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const signed int & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const signed long & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const signed long long & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const float & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const double & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
std::string FormatValA(const long double & x, const FormatSpec & f) { return mpt::format_simple<std::string>(x, f); }
mpt::ustring FormatValU(const bool & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const signed char & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const signed short & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const signed int & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const signed long & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const signed long long & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const float & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const double & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
mpt::ustring FormatValU(const long double & x, const FormatSpec & f) { return mpt::format_simple<mpt::ustring>(x, f); }
#if MPT_WSTRING_FORMAT
std::wstring FormatValW(const bool & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const signed char & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const unsigned char & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const signed short & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const unsigned short & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const signed int & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const unsigned int & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const signed long & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const unsigned long & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const signed long long & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const unsigned long long & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const float & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const double & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
std::wstring FormatValW(const long double & x, const FormatSpec & f) { return mpt::format_simple<std::wstring>(x, f); }
#endif
} // namespace mpt
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,554 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/base/pointer.hpp"
#include "mpt/format/message.hpp"
#include "mpt/format/simple_spec.hpp"
#include "mpt/string/types.hpp"
#include <stdexcept>
#include "mptString.h"
#include "openmpt/base/FlagSet.hpp"
OPENMPT_NAMESPACE_BEGIN
// The following section demands a rationale.
// 1. mpt::afmt::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.
// ToAString() 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.
// When formatting integers, it is recommended to use mpt::afmt::dec or mpt::afmt::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 FormatValA(int, format) can be
// used.
// 3. mpt::format(format)(...) provides simplified and type-safe message and localization string formatting.
// The only specifier allowed is '{}' enclosing a number 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::afmt mpt::wfmt mpt::ufmt mpt::tfmt mpt::cfmt
// MPT_AFORMAT("{}") MPT_WFORMAT("{}") MPT_UFORMAT("{}") MPT_TFORMAT("{}") MPT_CFORMAT("{}")
// 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> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding
#else
#if defined(MPT_ENABLE_CHARSET_LOCALE)
template <typename T> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::Locale, x.ToUString())) { return mpt::ToCharset(mpt::Charset::Locale, x.ToUString()); } // unknown encoding
#else // !MPT_ENABLE_CHARSET_LOCALE
template <typename T> [[deprecated]] auto ToAString(const T & x) -> decltype(mpt::ToCharset(mpt::Charset::UTF8, x.ToUString())) { return mpt::ToCharset(mpt::Charset::UTF8, x.ToUString()); } // unknown encoding
#endif // MPT_ENABLE_CHARSET_LOCALE
#endif
inline std::string ToAString(const std::string & x) { return x; }
inline std::string ToAString(const char * const & x) { return x; }
std::string ToAString(const char &x) = delete; // deprecated to catch potential API mis-use, use std::string(1, x) instead
#if MPT_WSTRING_FORMAT
std::string ToAString(const std::wstring & x) = delete; // Unknown encoding.
std::string ToAString(const wchar_t * const & x) = delete; // Unknown encoding.
std::string ToAString(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 ToAString(const mpt::ustring & x) = delete; // Unknown encoding.
#endif
#if defined(MPT_WITH_MFC)
std::string ToAString(const CString & x) = delete; // unknown encoding
#endif // MPT_WITH_MFC
std::string ToAString(const bool & x);
std::string ToAString(const signed char & x);
std::string ToAString(const unsigned char & x);
std::string ToAString(const signed short & x);
std::string ToAString(const unsigned short & x);
std::string ToAString(const signed int & x);
std::string ToAString(const unsigned int & x);
std::string ToAString(const signed long & x);
std::string ToAString(const unsigned long & x);
std::string ToAString(const signed long long & x);
std::string ToAString(const unsigned long long & x);
std::string ToAString(const float & x);
std::string ToAString(const double & x);
std::string ToAString(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 ToAString(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 ToStringFormatter {
template <typename Tstring, typename T>
static inline Tstring format(const T& value) {
return ToStringTFunctor<Tstring>()(value);
}
};
using FormatSpec = mpt::format_simple_spec;
using FormatFlags = mpt::format_simple_flags;
using fmt_base = mpt::format_simple_base;
std::string FormatValA(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 FormatValA(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 FormatValA(const bool & x, const FormatSpec & f);
std::string FormatValA(const signed char & x, const FormatSpec & f);
std::string FormatValA(const unsigned char & x, const FormatSpec & f);
std::string FormatValA(const signed short & x, const FormatSpec & f);
std::string FormatValA(const unsigned short & x, const FormatSpec & f);
std::string FormatValA(const signed int & x, const FormatSpec & f);
std::string FormatValA(const unsigned int & x, const FormatSpec & f);
std::string FormatValA(const signed long & x, const FormatSpec & f);
std::string FormatValA(const unsigned long & x, const FormatSpec & f);
std::string FormatValA(const signed long long & x, const FormatSpec & f);
std::string FormatValA(const unsigned long long & x, const FormatSpec & f);
std::string FormatValA(const float & x, const FormatSpec & f);
std::string FormatValA(const double & x, const FormatSpec & f);
std::string FormatValA(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 FormatValA(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, FormatValA(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, FormatValA(x, f)); } };
#endif // UNICODE
#endif // MPT_WITH_MFC
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>(mpt::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>(mpt::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> afmt;
#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
#define MPT_AFORMAT(f) mpt::format_message<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(f)>(f)
#if MPT_WSTRING_FORMAT
#define MPT_WFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count( L ## f ), std::wstring>( L ## f )
#endif
#define MPT_UFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(MPT_ULITERAL(f)), mpt::ustring>(MPT_ULITERAL(f))
#if defined(MPT_ENABLE_CHARSET_LOCALE)
#define MPT_LFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(f), mpt::lstring>(f)
#endif // MPT_ENABLE_CHARSET_LOCALE
#if MPT_OS_WINDOWS
#define MPT_TFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(TEXT(f)), mpt::tstring>(TEXT(f))
#endif
#if defined(MPT_WITH_MFC)
#define MPT_CFORMAT(f) mpt::format_message_typed<mpt::ToStringFormatter, mpt::parse_format_string_argument_count(TEXT(f)), CString>(TEXT(f))
#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::afmt::val(vals[i]);
}
return str;
}
} } // namespace mpt::String
template <typename enum_t, typename store_t>
mpt::ustring ToUString(FlagSet<enum_t, store_t> flagset)
{
mpt::ustring str(flagset.size_bits(), UC_('0'));
for(std::size_t x = 0; x < flagset.size_bits(); ++x)
{
str[flagset.size_bits() - x - 1] = (flagset.value().as_bits() & (static_cast<typename FlagSet<enum_t>::store_type>(1) << x) ? UC_('1') : UC_('0'));
}
return str;
}
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,98 @@
/*
* 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 "mpt/parse/parse.hpp"
OPENMPT_NAMESPACE_BEGIN
template<typename T>
inline T ConvertStrToHelper(const std::string &str)
{
return mpt::ConvertStringTo<T>(str);
}
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)
{
return mpt::ConvertStringTo<T>(str);
}
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)
{
return mpt::ConvertHexStringTo<T>(str);
}
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 "openmpt/all/BuildSettings.hpp"
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,307 @@
/*
* mptTime.cpp
* -----------
* Purpose: Various time utility functions.
* Notes : (currently none)
* Authors: OpenMPT Devs
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
*/
#include "stdafx.h"
#include "mptTime.h"
#include "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 = {};
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 = {};
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 "openmpt/all/BuildSettings.hpp"
#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,721 @@
/*
* 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 "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include <array>
#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_GLOBAL(LogDebug, "serialization", x)
#else
#define SSB_LOG(x) do { } 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(std::numeric_limits<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>((std::numeric_limits<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'};
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_UFORMAT("{}: 0x{}")(U_("Write note: "), mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const SsbStatus s)
{
m_Status |= s;
SSB_LOG(MPT_UFORMAT("{}: 0x{}")(U_("Read note: "), mpt::ufmt::hex(s)));
}
void SsbRead::AddReadNote(const ReadEntry* const pRe, const NumType nNum)
{
m_Status |= SNT_PROGRESS;
SSB_LOG(MPT_UFORMAT("Read entry: {{num, id, rpos, size, desc}} = {{{}, {}, {}, {}, {}}}")(
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_UFORMAT("Wrote entry: {{num, id, rpos, size}} = {{{}, {}, {}, {}}}")(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_UFORMAT("Writing map entry: id={}, rpos={}, size={}")(
(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 >= static_cast<uint16>(std::numeric_limits<uint16>::max() >> 2))
{
FinishWrite();
AddWriteNote(SNW_MAX_WRITE_COUNT_REACHED);
}
}
void SsbWrite::BeginWrite(const ID &id, const uint64& nVersion)
{
SSB_LOG(MPT_UFORMAT("Write header with ID = {}")(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_UFORMAT("No entry with id {} found.")(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_UFORMAT("Read header with expected ID = {}")(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);
std::array<char, 256> storedIdBuf;
storedIdBuf = {};
if(storedIdLen > 0)
{
iStrm.read(storedIdBuf.data(), storedIdLen);
}
if(!(id == ID(storedIdBuf.data(), storedIdLen)))
{
AddReadNote(SNR_OBJECTCLASS_IDMISMATCH);
}
if ((m_Status & SNT_FAILURE) != 0)
{
SSB_LOG(U_("ID mismatch, terminating read."));
return;
}
SSB_LOG(U_("ID match, continuing reading."));
// 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(U_("No map in the file."));
}
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_UFORMAT("Reading map from rpos: {}")(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_UFORMAT("End of map(rpos): {}")(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_UFORMAT("Writing map to rpos: {}")(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_UFORMAT("End of stream(rpos): {}")(oStrm.tellp() - m_posStart));
}
} // namespace srlztn
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,555 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mpt/io/io.hpp"
#include "mpt/io/io_stdstream.hpp"
#include "openmpt/base/Endian.hpp"
#include "../common/mptBaseTypes.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 = std::numeric_limits<uint16>::max(),
IdSizeMaxFixedSize = (std::numeric_limits<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,141 @@
/*
* 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 openmpt/all/BuildSettings.hpp.
* 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 "openmpt/all/BuildSettings.hpp"
#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
#include "mpt/base/span.hpp"
#include "mpt/check/libc.hpp"
#if defined(MPT_WITH_MFC)
#include "mpt/check/mfc.hpp"
#endif
#if MPT_OS_WINDOWS
#include "mpt/check/windows.hpp"
#endif
#include "mpt/exception_text/exception_text.hpp"
#include "mpt/out_of_memory/out_of_memory.hpp"
#include "mpt/system_error/system_error.hpp"
#include "openmpt/base/Types.hpp"
#include "openmpt/logging/Logger.hpp"
#include <memory>
#include <new>
// this will be available everywhere
#include "../common/mptBaseMacros.h"
// <version>
// <array>
// <iterator>
// <type_traits>
// <cstddef>
// <cstdint>
#include "../common/mptBaseTypes.h"
// "openmpt/base/Types.hpp"
// "mptBaseMacros.h"
// <array>
// <limits>
// <type_traits>
// <cstdint>
#include "../common/mptAssert.h"
// "mptBaseMacros.h"
#include "../common/mptBaseUtils.h"
// <algorithm>
// <bit>
// <limits>
// <numeric>
// <utility>
#include "../common/mptString.h"
// <algorithm>
// <limits>
// <string>
// <string_view>
// <type_traits>
// <cstring>
#include "../common/mptStringBuffer.h"
#include "../common/mptStringFormat.h"
// <stdexcept>
#include "../common/mptPathString.h"
#include "../common/Logging.h"
// "openmpt/logging/Logger.hpp"
// <atomic>
#include "../common/misc_util.h"
// <stdexcept>
// <optional>
// <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,827 @@
/*
* 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_UFORMAT("{}.{}")(mpt::ufmt::HEX((v >> 24) & 0xFF), mpt::ufmt::HEX0<2>((v >> 16) & 0xFF));
} else
{
// Full version info available
return MPT_UFORMAT("{}.{}.{}.{}")(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();
}
VersionWithRevision VersionWithRevision::Current()
{
return {Version::Current(), static_cast<uint64>(SourceInfo::Current().Revision())};
}
VersionWithRevision VersionWithRevision::Parse(const mpt::ustring &s)
{
Version version = Version::Parse(mpt::ustring());
uint64 revision = 0;
const auto tokens = mpt::String::Split<mpt::ustring>(s, U_("-"));
if(tokens.size() >= 1)
{
version = Version::Parse(tokens[0]);
}
if(tokens.size() >= 2)
{
revision = ConvertStrTo<uint64>(tokens[1].substr(1));
}
return {version, revision};
}
mpt::ustring VersionWithRevision::ToUString() const
{
if(!HasRevision())
{
return mpt::ufmt::val(version);
}
if(!version.IsTestVersion())
{
return mpt::ufmt::val(version);
}
return MPT_UFORMAT("{}-r{}")(version, revision);
}
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 defined(MPT_BUILD_RETRO)
retval += UL_(" RETRO");
#endif // MPT_BUILD_RETRO
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
#ifndef MPT_WITH_VST
UL_(" NO_VST")
#endif
#ifndef MPT_WITH_DMO
UL_(" NO_DMO")
#endif
#ifdef NO_PLUGINS
UL_(" NO_PLUGINS")
#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_UFORMAT("Microsoft Compiler {}.{}.{}.{}")
( _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_UFORMAT("Microsoft Compiler {}.{}.{}")
( _MSC_FULL_VER / 10000000
, mpt::ufmt::dec0<2>((_MSC_FULL_VER / 100000) % 100)
, mpt::ufmt::dec0<5>(_MSC_FULL_VER % 100000)
);
#else
retval += MPT_UFORMAT("Microsoft Compiler {}.{}")(MPT_COMPILER_MSVC_VERSION / 100, MPT_COMPILER_MSVC_VERSION % 100);
#endif
#elif MPT_COMPILER_GCC
retval += MPT_UFORMAT("GNU Compiler Collection {}.{}.{}")(MPT_COMPILER_GCC_VERSION / 10000, (MPT_COMPILER_GCC_VERSION / 100) % 100, MPT_COMPILER_GCC_VERSION % 100);
#elif MPT_COMPILER_CLANG
retval += MPT_UFORMAT("Clang {}.{}.{}")(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[StringSourceInfo])
{
const SourceInfo sourceInfo = SourceInfo::Current();
if(!sourceInfo.GetUrlWithRevision().empty())
{
result.push_back(MPT_UFORMAT(" {}")(sourceInfo.GetUrlWithRevision()));
}
if(!sourceInfo.Date().empty())
{
result.push_back(MPT_UFORMAT(" ({})")(sourceInfo.Date()));
}
if(!sourceInfo.GetStateString().empty())
{
result.push_back(MPT_UFORMAT(" {}")(sourceInfo.GetStateString()));
}
}
if(strings[StringBuildFlags])
{
if(!IsReleasedBuild())
{
result.push_back(GetBuildFlagsString());
}
}
if(strings[StringBuildFeatures])
{
result.push_back(GetBuildFeaturesString());
}
return mpt::trim(mpt::String::Combine<mpt::ustring>(result, U_("")));
}
mpt::ustring GetVersionStringPure()
{
FlagSet<Build::Strings> strings;
strings |= Build::StringVersion;
strings |= Build::StringRevision;
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;
#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 / Open ModPlug Tracker\n"
#else
"libopenmpt (based on OpenMPT / Open ModPlug Tracker)\n"
#endif
"\n"
"Copyright \xC2\xA9 2004-2022 OpenMPT Project Developers and Contributors\n"
"Copyright \xC2\xA9 1997-2003 Olivier Lapicque\n"
"\n"
"Developers:\n"
"Johannes Schultz (2008-2022)\n"
"J\xC3\xB6rn Heusipp (2012-2022)\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 contributors:\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
#ifdef MPT_WITH_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_ANCIENT
"Teemu Suutari for ancient\n"
"https://github.com/temisu/ancient\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 for RtAudio\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"
#ifdef MPT_WITH_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-2022, OpenMPT Project Developers and 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 COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"" "\n"
"AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE" "\n"
"IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE" "\n"
"DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE" "\n"
"FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL" "\n"
"DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR" "\n"
"SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER" "\n"
"CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY," "\n"
"OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE" "\n"
"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." "\n"
);
}
} // namespace Build
OPENMPT_NAMESPACE_END

View file

@ -0,0 +1,397 @@
/*
* 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 "openmpt/all/BuildSettings.hpp"
#include "mptString.h"
#include "openmpt/base/FlagSet.hpp"
#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_CONSTEXPRINLINE Version() noexcept
: m_Version(0)
{}
explicit MPT_CONSTEXPRINLINE Version(uint32 version) noexcept
: m_Version(version)
{}
explicit MPT_CONSTEXPRINLINE 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_CONSTEXPRINLINE operator bool () const noexcept
{
return m_Version != 0;
}
MPT_CONSTEXPRINLINE bool operator ! () const noexcept
{
return m_Version == 0;
}
MPT_CONSTEXPRINLINE uint32 GetRawVersion() const noexcept
{
return m_Version;
}
MPT_FORCEINLINE Version Masked(uint32 mask) const noexcept
{
return Version(m_Version & mask);
}
MPT_CONSTEXPRINLINE 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_CONSTEXPRINLINE 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_CONSTEXPRINLINE 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_CONSTEXPRINLINE bool operator == (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() == b.GetRawVersion();
}
MPT_CONSTEXPRINLINE bool operator != (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() != b.GetRawVersion();
}
MPT_CONSTEXPRINLINE bool operator <= (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() <= b.GetRawVersion();
}
MPT_CONSTEXPRINLINE bool operator >= (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() >= b.GetRawVersion();
}
MPT_CONSTEXPRINLINE bool operator < (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() < b.GetRawVersion();
}
MPT_CONSTEXPRINLINE bool operator > (const Version &a, const Version &b) noexcept
{
return a.GetRawVersion() > b.GetRawVersion();
}
MPT_CONSTEXPRINLINE 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) MPT_FORCE_CONSTEXPR(Version{( 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"
};
struct VersionWithRevision
{
Version version;
uint64 revision;
static VersionWithRevision Current();
static VersionWithRevision Parse(const mpt::ustring &s);
mpt::ustring ToUString() const;
constexpr bool HasRevision() const noexcept
{
return revision != 0;
}
constexpr bool IsEqualTo(VersionWithRevision other) const noexcept
{
return version == other.version && revision == other.revision;
}
constexpr bool IsEquivalentTo(VersionWithRevision other) const noexcept
{
if(version == other.version && revision == other.revision)
{
return true;
}
if(HasRevision() && other.HasRevision())
{
return false;
}
return version == other.version;
}
constexpr bool IsNewerThan(VersionWithRevision other) const noexcept
{
if(version < other.version)
{
return false;
}
if(version > other.version)
{
return true;
}
if(!HasRevision() && !other.HasRevision())
{
return false;
}
if(HasRevision() && other.HasRevision())
{
if(revision < other.revision)
{
return false;
}
if(revision > other.revision)
{
return true;
}
return false;
}
return false;
}
constexpr bool IsOlderThan(VersionWithRevision other) const noexcept
{
if(version < other.version)
{
return true;
}
if(version > other.version)
{
return false;
}
if(!HasRevision() && !other.HasRevision())
{
return false;
}
if(HasRevision() && other.HasRevision())
{
if(revision < other.revision)
{
return true;
}
if(revision > other.revision)
{
return false;
}
return false;
}
return false;
}
};
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+"
StringSourceInfo = 1<<5, // "https://source.openmpt.org/svn/openmpt/trunk/OpenMPT@1234 (2016-01-02) +dirty"
StringBuildFlags = 1<<6, // "TEST DEBUG"
StringBuildFeatures = 1<<7, // "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+"
// 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+ 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 "openmpt/all/BuildSettings.hpp"
OPENMPT_NAMESPACE_BEGIN
// Version definitions. The only thing that needs to be changed when changing version number.
#define VER_MAJORMAJOR 1
#define VER_MAJOR 30
#define VER_MINOR 07
#define VER_MINORMINOR 00
OPENMPT_NAMESPACE_END