aboutsummaryrefslogtreecommitdiff
path: root/Src/external_dependencies/openmpt-trunk/misc
diff options
context:
space:
mode:
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/misc')
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/WriteMemoryDump.h75
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptCPU.cpp258
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptCPU.h93
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptColor.cpp95
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptColor.h47
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptLibrary.cpp553
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptLibrary.h108
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptMutex.h80
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptOS.cpp551
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptOS.h161
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptOSException.h180
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptWine.cpp750
-rw-r--r--Src/external_dependencies/openmpt-trunk/misc/mptWine.h131
13 files changed, 3082 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/misc/WriteMemoryDump.h b/Src/external_dependencies/openmpt-trunk/misc/WriteMemoryDump.h
new file mode 100644
index 00000000..8661e1e3
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/WriteMemoryDump.h
@@ -0,0 +1,75 @@
+/*
+ * WriteMemoryDump.h
+ * -----------------
+ * Purpose: Code for writing memory dumps to a file.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#if MPT_COMPILER_MSVC
+#pragma warning(push)
+#pragma warning(disable:4091) // 'typedef ': ignored on left of '' when no variable is declared
+#endif // MPT_COMPILER_MSVC
+#include <dbghelp.h>
+#if MPT_COMPILER_MSVC
+#pragma warning(pop)
+#endif // MPT_COMPILER_MSVC
+
+OPENMPT_NAMESPACE_BEGIN
+
+typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+ );
+
+static bool WriteMemoryDump(_EXCEPTION_POINTERS *pExceptionInfo, const TCHAR *filename, bool fullMemDump)
+{
+ bool result = false;
+
+ HMODULE hDll = ::LoadLibrary(_T("DBGHELP.DLL"));
+ if (hDll)
+ {
+ MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDll, "MiniDumpWriteDump");
+ if (pDump)
+ {
+
+ HANDLE hFile = ::CreateFile(filename, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
+
+ if(pExceptionInfo)
+ {
+ ExInfo.ThreadId = ::GetCurrentThreadId();
+ ExInfo.ExceptionPointers = pExceptionInfo;
+ ExInfo.ClientPointers = NULL;
+ }
+
+ pDump(GetCurrentProcess(), GetCurrentProcessId(), hFile,
+ fullMemDump ?
+ (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo
+#if MPT_COMPILER_MSVC
+ | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation
+#endif
+ )
+ :
+ MiniDumpNormal,
+ pExceptionInfo ? &ExInfo : NULL, NULL, NULL);
+ ::CloseHandle(hFile);
+
+ result = true;
+ }
+ }
+ ::FreeLibrary(hDll);
+ }
+ return result;
+}
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptCPU.cpp b/Src/external_dependencies/openmpt-trunk/misc/mptCPU.cpp
new file mode 100644
index 00000000..56b7bd63
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptCPU.cpp
@@ -0,0 +1,258 @@
+/*
+ * mptCPU.cpp
+ * ----------
+ * Purpose: CPU feature detection.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+#include "mptCPU.h"
+
+#include "../common/mptStringBuffer.h"
+
+#if defined(MPT_ENABLE_ARCH_INTRINSICS)
+#if MPT_COMPILER_MSVC && (defined(MPT_ENABLE_ARCH_X86) || defined(MPT_ENABLE_ARCH_AMD64))
+#include <intrin.h>
+#endif // MPT_COMPILER_MSVC && (MPT_ENABLE_ARCH_X86 || MPT_ENABLE_ARCH_AMD64)
+#endif // MPT_ENABLE_ARCH_INTRINSICS
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace CPU
+{
+
+
+#if defined(MPT_ENABLE_ARCH_INTRINSICS)
+
+
+uint32 EnabledFeatures = 0;
+
+
+#if MPT_COMPILER_MSVC && (defined(MPT_ENABLE_ARCH_X86) || defined(MPT_ENABLE_ARCH_AMD64))
+
+
+typedef char cpuid_result_string[12];
+
+
+struct cpuid_result {
+ uint32 a;
+ uint32 b;
+ uint32 c;
+ uint32 d;
+ std::string as_string() const
+ {
+ cpuid_result_string result;
+ result[0+0] = (b >> 0) & 0xff;
+ result[0+1] = (b >> 8) & 0xff;
+ result[0+2] = (b >>16) & 0xff;
+ result[0+3] = (b >>24) & 0xff;
+ result[4+0] = (d >> 0) & 0xff;
+ result[4+1] = (d >> 8) & 0xff;
+ result[4+2] = (d >>16) & 0xff;
+ result[4+3] = (d >>24) & 0xff;
+ result[8+0] = (c >> 0) & 0xff;
+ result[8+1] = (c >> 8) & 0xff;
+ result[8+2] = (c >>16) & 0xff;
+ result[8+3] = (c >>24) & 0xff;
+ return std::string(result, result + 12);
+ }
+ std::string as_string4() const
+ {
+ std::string result;
+ result.push_back(static_cast<uint8>((a >> 0) & 0xff));
+ result.push_back(static_cast<uint8>((a >> 8) & 0xff));
+ result.push_back(static_cast<uint8>((a >> 16) & 0xff));
+ result.push_back(static_cast<uint8>((a >> 24) & 0xff));
+ result.push_back(static_cast<uint8>((b >> 0) & 0xff));
+ result.push_back(static_cast<uint8>((b >> 8) & 0xff));
+ result.push_back(static_cast<uint8>((b >> 16) & 0xff));
+ result.push_back(static_cast<uint8>((b >> 24) & 0xff));
+ result.push_back(static_cast<uint8>((c >> 0) & 0xff));
+ result.push_back(static_cast<uint8>((c >> 8) & 0xff));
+ result.push_back(static_cast<uint8>((c >> 16) & 0xff));
+ result.push_back(static_cast<uint8>((c >> 24) & 0xff));
+ result.push_back(static_cast<uint8>((d >> 0) & 0xff));
+ result.push_back(static_cast<uint8>((d >> 8) & 0xff));
+ result.push_back(static_cast<uint8>((d >> 16) & 0xff));
+ result.push_back(static_cast<uint8>((d >> 24) & 0xff));
+ return result;
+ }
+};
+
+
+static cpuid_result cpuid(uint32 function)
+{
+ cpuid_result result;
+ int CPUInfo[4];
+ __cpuid(CPUInfo, function);
+ result.a = CPUInfo[0];
+ result.b = CPUInfo[1];
+ result.c = CPUInfo[2];
+ result.d = CPUInfo[3];
+ return result;
+}
+
+
+static cpuid_result cpuidex(uint32 function_a, uint32 function_c)
+{
+ cpuid_result result;
+ int CPUInfo[4];
+ __cpuidex(CPUInfo, function_a, function_c);
+ result.a = CPUInfo[0];
+ result.b = CPUInfo[1];
+ result.c = CPUInfo[2];
+ result.d = CPUInfo[3];
+ return result;
+}
+
+
+Info::Info()
+{
+
+ cpuid_result VendorString = cpuid(0x00000000u);
+ mpt::String::WriteAutoBuf(VendorID) = VendorString.as_string();
+ if(VendorString.a >= 0x00000001u)
+ {
+ cpuid_result StandardFeatureFlags = cpuid(0x00000001u);
+ CPUID = StandardFeatureFlags.a;
+ uint32 BaseStepping = (StandardFeatureFlags.a >> 0) & 0x0f;
+ uint32 BaseModel = (StandardFeatureFlags.a >> 4) & 0x0f;
+ uint32 BaseFamily = (StandardFeatureFlags.a >> 8) & 0x0f;
+ uint32 ExtModel = (StandardFeatureFlags.a >> 16) & 0x0f;
+ uint32 ExtFamily = (StandardFeatureFlags.a >> 20) & 0xff;
+ if(BaseFamily == 0xf)
+ {
+ Family = static_cast<uint16>(ExtFamily + BaseFamily);
+ } else
+ {
+ Family = static_cast<uint16>(BaseFamily);
+ }
+ if((BaseFamily == 0x6) || (BaseFamily == 0xf))
+ {
+ Model = static_cast<uint8>((ExtModel << 4) | (BaseModel << 0));
+ } else
+ {
+ Model = static_cast<uint8>(BaseModel);
+ }
+ Stepping = static_cast<uint8>(BaseStepping);
+ if(StandardFeatureFlags.d & (1<<23)) AvailableFeatures |= feature::mmx;
+ if(StandardFeatureFlags.d & (1<<25)) AvailableFeatures |= feature::sse;
+ if(StandardFeatureFlags.d & (1<<26)) AvailableFeatures |= feature::sse2;
+ if(StandardFeatureFlags.c & (1<< 0)) AvailableFeatures |= feature::sse3;
+ if(StandardFeatureFlags.c & (1<< 9)) AvailableFeatures |= feature::ssse3;
+ if(StandardFeatureFlags.c & (1<<19)) AvailableFeatures |= feature::sse4_1;
+ if(StandardFeatureFlags.c & (1<<20)) AvailableFeatures |= feature::sse4_2;
+ if(StandardFeatureFlags.c & (1<<28)) AvailableFeatures |= feature::avx;
+ }
+ if(VendorString.a >= 0x00000007u)
+ {
+ cpuid_result ExtendedFeatures = cpuidex(0x00000007u, 0x00000000u);
+ if(ExtendedFeatures.b & (1<< 5)) AvailableFeatures |= feature::avx2;
+ }
+
+ cpuid_result ExtendedVendorString = cpuid(0x80000000u);
+ if(ExtendedVendorString.a >= 0x80000001u)
+ {
+ cpuid_result ExtendedFeatureFlags = cpuid(0x80000001u);
+ if(ExtendedFeatureFlags.d & (1<<29)) AvailableFeatures |= feature::lm;
+ }
+ if(ExtendedVendorString.a >= 0x80000004u)
+ {
+ mpt::String::WriteAutoBuf(BrandID) = cpuid(0x80000002u).as_string4() + cpuid(0x80000003u).as_string4() + cpuid(0x80000004u).as_string4();
+ }
+
+}
+
+
+#elif MPT_COMPILER_MSVC && (defined(MPT_ENABLE_ARCH_X86) || defined(MPT_ENABLE_ARCH_AMD64))
+
+
+Info::Info()
+{
+
+ if(IsProcessorFeaturePresent(PF_MMX_INSTRUCTIONS_AVAILABLE) != 0) AvailableFeatures |= feature::mmx;
+ if(IsProcessorFeaturePresent(PF_XMMI_INSTRUCTIONS_AVAILABLE) != 0) AvailableFeatures |= feature::sse;
+ if(IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE) != 0) AvailableFeatures |= feature::sse2;
+ if(IsProcessorFeaturePresent(PF_SSE3_INSTRUCTIONS_AVAILABLE) != 0) AvailableFeatures |= feature::sse3;
+
+}
+
+
+#else // !(MPT_COMPILER_MSVC && (MPT_ENABLE_ARCH_X86 || MPT_ENABLE_ARCH_AMD64))
+
+
+Info::Info()
+{
+ return;
+}
+
+
+#endif // MPT_COMPILER_MSVC && (MPT_ENABLE_ARCH_X86 || MPT_ENABLE_ARCH_AMD64)
+
+
+const Info & Info::Get()
+{
+ static Info info;
+ return info;
+}
+
+
+struct InfoInitializer
+{
+ InfoInitializer()
+ {
+ Info::Get();
+ }
+};
+
+
+static InfoInitializer g_InfoInitializer;
+
+
+void EnableAvailableFeatures()
+{
+ EnabledFeatures = Info::Get().AvailableFeatures;
+}
+
+
+#endif // MPT_ENABLE_ARCH_INTRINSICS
+
+
+uint32 GetMinimumFeatures()
+{
+ uint32 flags = 0;
+ #ifdef MPT_ENABLE_ARCH_INTRINSICS
+ #if MPT_COMPILER_MSVC
+ #if defined(_M_X64)
+ flags |= feature::lm | feature::sse | feature::sse2;
+ #elif defined(_M_IX86)
+ #if defined(_M_IX86_FP)
+ #if (_M_IX86_FP >= 2)
+ flags |= feature::sse | feature::sse2;
+ #elif (_M_IX86_FP == 1)
+ flags |= feature::sse;
+ #endif
+ #endif
+ #endif
+ #if defined(__AVX__)
+ flags |= feature::avx;
+ #endif
+ #if defined(__AVX2__)
+ flags |= feature::avx2;
+ #endif
+ #endif
+ #endif // MPT_ENABLE_ARCH_INTRINSICS
+ return flags;
+}
+
+
+
+} // namespace CPU
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptCPU.h b/Src/external_dependencies/openmpt-trunk/misc/mptCPU.h
new file mode 100644
index 00000000..e260c6f1
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptCPU.h
@@ -0,0 +1,93 @@
+/*
+ * mptCPU.h
+ * --------
+ * Purpose: CPU feature detection.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace CPU
+{
+
+
+namespace feature {
+inline constexpr uint32 lm = 0x00004; // Processor supports long mode (amd64)
+inline constexpr uint32 mmx = 0x00010; // Processor supports MMX instructions
+inline constexpr uint32 sse = 0x00100; // Processor supports SSE instructions
+inline constexpr uint32 sse2 = 0x00200; // Processor supports SSE2 instructions
+inline constexpr uint32 sse3 = 0x00400; // Processor supports SSE3 instructions
+inline constexpr uint32 ssse3 = 0x00800; // Processor supports SSSE3 instructions
+inline constexpr uint32 sse4_1 = 0x01000; // Processor supports SSE4.1 instructions
+inline constexpr uint32 sse4_2 = 0x02000; // Processor supports SSE4.2 instructions
+inline constexpr uint32 avx = 0x10000; // Processor supports AVX instructions
+inline constexpr uint32 avx2 = 0x20000; // Processor supports AVX2 instructions
+} // namespace feature
+
+
+#ifdef MPT_ENABLE_ARCH_INTRINSICS
+
+
+extern uint32 EnabledFeatures;
+
+
+struct Info
+{
+public:
+ uint32 AvailableFeatures = 0;
+ uint32 CPUID = 0;
+ char VendorID[16+1] = {};
+ char BrandID[4*4*3+1] = {};
+ uint16 Family = 0;
+ uint8 Model = 0;
+ uint8 Stepping = 0;
+private:
+ Info();
+public:
+ static const Info & Get();
+};
+
+
+void EnableAvailableFeatures();
+
+
+struct AvailableFeaturesEnabler
+{
+ AvailableFeaturesEnabler()
+ {
+ EnableAvailableFeatures();
+ }
+};
+
+
+// enabled processor features for inline asm and intrinsics
+MPT_FORCEINLINE uint32 GetEnabledFeatures()
+{
+ return EnabledFeatures;
+}
+
+MPT_FORCEINLINE bool HasFeatureSet(uint32 features)
+{
+ return features == (GetEnabledFeatures() & features);
+}
+
+
+#endif // MPT_ENABLE_ARCH_INTRINSICS
+
+
+uint32 GetMinimumFeatures();
+
+
+} // namespace CPU
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptColor.cpp b/Src/external_dependencies/openmpt-trunk/misc/mptColor.cpp
new file mode 100644
index 00000000..8b0b91ac
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptColor.cpp
@@ -0,0 +1,95 @@
+/*
+ * mptColor.cpp
+ * ------------
+ * Purpose: Color space conversion and other color-related code.
+ * 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 "mptColor.h"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace mpt::Color
+{
+
+uint8 GetLuma(uint8 r, uint8 g, uint8 b) noexcept
+{
+ return mpt::saturate_cast<uint8>(r * 0.299f + g * 0.587f + b * 0.114f);
+}
+
+
+HSV RGB::ToHSV() const noexcept
+{
+ const auto min = std::min({r, g, b});
+ const auto max = std::max({r, g, b});
+ const auto delta = max - min;
+
+ HSV hsv;
+ hsv.v = max;
+ if(delta < 0.00001f)
+ {
+ hsv.s = 0;
+ hsv.h = 0;
+ return hsv;
+ }
+ if(max > 0.0f)
+ {
+ hsv.s = (delta / max);
+ } else
+ {
+ // black
+ hsv.s = 0.0f;
+ hsv.h = 0.0f;
+ return hsv;
+ }
+ if(r >= max)
+ hsv.h = (g - b) / delta;
+ else if(g >= max)
+ hsv.h = 2.0f + (b - r) / delta;
+ else
+ hsv.h = 4.0f + (r - g) / delta;
+
+ if(hsv.h < 0.0f)
+ hsv.h += 6.0f;
+
+ hsv.h *= 60.0f;
+
+ return hsv;
+}
+
+
+RGB HSV::ToRGB() const noexcept
+{
+ // Optimization for greyscale
+ if(s <= 0.0f)
+ return {v, v, v};
+
+ const float hh = h / 60.0f;
+ const int region = static_cast<int>(hh);
+ const float fract = hh - region;
+ const float p = v * (1.0f - s);
+ const float q = v * (1.0f - (s * fract));
+ const float t = v * (1.0f - (s * (1.0f - fract)));
+
+ switch(region % 6)
+ {
+ default:
+ case 0: return {v, t, p};
+ case 1: return {q, v, p};
+ case 2: return {p, v, t};
+ case 3: return {p, q, v};
+ case 4: return {t, p, v};
+ case 5: return {v, p, q};
+ }
+}
+
+
+} // namespace mpt::Color
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptColor.h b/Src/external_dependencies/openmpt-trunk/misc/mptColor.h
new file mode 100644
index 00000000..c2fdc412
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptColor.h
@@ -0,0 +1,47 @@
+/*
+ * mptColor.h
+ * ----------
+ * Purpose: Color space conversion and other color-related code.
+ * 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
+
+
+namespace mpt::Color
+{
+
+uint8 GetLuma(uint8 r, uint8 g, uint8 b) noexcept;
+
+struct HSV;
+
+struct RGB
+{
+ float r; // 0...1
+ float g; // 0...1
+ float b; // 0...1
+
+ HSV ToHSV() const noexcept;
+};
+
+struct HSV
+{
+ float h; // angle in degrees
+ float s; // 0...1
+ float v; // 0...1
+
+ RGB ToRGB() const noexcept;
+};
+
+} // namespace mpt::Color
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.cpp b/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.cpp
new file mode 100644
index 00000000..e924a90b
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.cpp
@@ -0,0 +1,553 @@
+/*
+ * mptLibrary.cpp
+ * --------------
+ * Purpose: Shared library handling.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+#include "mptLibrary.h"
+
+#include "mpt/osinfo/windows_version.hpp"
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#elif MPT_OS_ANDROID
+#include <dlfcn.h>
+#elif defined(MPT_WITH_LTDL)
+#include <ltdl.h>
+#elif defined(MPT_WITH_DL)
+#include <dlfcn.h>
+#endif
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace mpt
+{
+
+
+#if MPT_OS_WINDOWS
+
+
+// KB2533623 / Win8
+#ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
+#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
+#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
+#endif
+#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
+#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
+#endif
+
+
+class LibraryHandle
+{
+
+private:
+
+ HMODULE hModule;
+
+public:
+
+ LibraryHandle(const mpt::LibraryPath &path)
+ : hModule(NULL)
+ {
+
+#if MPT_OS_WINDOWS_WINRT
+
+#if (_WIN32_WINNT < 0x0602)
+ (void)path;
+ hModule = NULL; // unsupported
+#else
+ switch(path.GetSearchPath())
+ {
+ case mpt::LibrarySearchPath::Default:
+ hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0);
+ break;
+ case mpt::LibrarySearchPath::Application:
+ hModule = LoadPackagedLibrary(path.GetFileName().AsNative().c_str(), 0);
+ break;
+ case mpt::LibrarySearchPath::System:
+ hModule = NULL; // Only application packaged libraries can be loaded dynamically in WinRT
+ break;
+ case mpt::LibrarySearchPath::FullPath:
+ hModule = NULL; // Absolute path is not supported in WinRT
+ break;
+ case mpt::LibrarySearchPath::Invalid:
+ MPT_ASSERT_NOTREACHED();
+ break;
+ }
+#endif
+
+#else // !MPT_OS_WINDOWS_WINRT
+
+#if (_WIN32_WINNT >= 0x0602)
+ bool hasKB2533623 = true;
+#else
+ // Check for KB2533623:
+ bool hasKB2533623 = false;
+ mpt::osinfo::windows::Version WindowsVersion = mpt::osinfo::windows::Version::Current();
+ if(WindowsVersion.IsAtLeast(mpt::osinfo::windows::Version::Win8))
+ {
+ hasKB2533623 = true;
+ } else if(WindowsVersion.IsAtLeast(mpt::osinfo::windows::Version::WinVista))
+ {
+ HMODULE hKernel32DLL = LoadLibrary(TEXT("kernel32.dll"));
+ if(hKernel32DLL)
+ {
+ if(::GetProcAddress(hKernel32DLL, "SetDefaultDllDirectories") != nullptr)
+ {
+ hasKB2533623 = true;
+ }
+ FreeLibrary(hKernel32DLL);
+ hKernel32DLL = NULL;
+ }
+ }
+#endif
+
+ MPT_MAYBE_CONSTANT_IF(hasKB2533623)
+ {
+ switch(path.GetSearchPath())
+ {
+ case mpt::LibrarySearchPath::Default:
+ hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
+ break;
+ case mpt::LibrarySearchPath::System:
+ hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
+ break;
+#if defined(MODPLUG_TRACKER)
+ // Using restricted search paths applies to potential DLL dependencies
+ // recursively.
+ // This fails loading for e.g. Codec or Plugin DLLs in application
+ // directory if they depend on the MSVC C or C++ runtime (which is
+ // located in the system directory).
+ // Just rely on the default search path here.
+ case mpt::LibrarySearchPath::Application:
+ {
+ const mpt::PathString dllPath = mpt::GetExecutablePath();
+ if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
+ {
+ hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str());
+ }
+ }
+ break;
+ case mpt::LibrarySearchPath::FullPath:
+ hModule = LoadLibrary(path.GetFileName().AsNative().c_str());
+ break;
+#else
+ // For libopenmpt, do the safe thing.
+ case mpt::LibrarySearchPath::Application:
+ hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
+ break;
+ case mpt::LibrarySearchPath::FullPath:
+ hModule = LoadLibraryEx(path.GetFileName().AsNative().c_str(), NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR);
+ break;
+#endif
+ case mpt::LibrarySearchPath::Invalid:
+ MPT_ASSERT_NOTREACHED();
+ break;
+ }
+ } else
+ {
+ switch(path.GetSearchPath())
+ {
+ case mpt::LibrarySearchPath::Default:
+ hModule = LoadLibrary(path.GetFileName().AsNative().c_str());
+ break;
+ case mpt::LibrarySearchPath::Application:
+ {
+ const mpt::PathString dllPath = mpt::GetExecutablePath();
+ if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
+ {
+ hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str());
+ }
+ }
+ break;
+ case mpt::LibrarySearchPath::System:
+ {
+ const mpt::PathString dllPath = mpt::GetSystemPath();
+ if(!dllPath.empty() && mpt::PathIsAbsolute(dllPath) && dllPath.IsDirectory())
+ {
+ hModule = LoadLibrary((dllPath + path.GetFileName()).AsNative().c_str());
+ }
+ }
+ break;
+ case mpt::LibrarySearchPath::FullPath:
+ hModule = LoadLibrary(path.GetFileName().AsNative().c_str());
+ break;
+ case mpt::LibrarySearchPath::Invalid:
+ MPT_ASSERT_NOTREACHED();
+ break;
+ }
+ }
+
+#endif // MPT_OS_WINDOWS_WINRT
+
+ }
+
+ LibraryHandle(const LibraryHandle &) = delete;
+
+ LibraryHandle & operator=(const LibraryHandle &) = delete;
+
+ ~LibraryHandle()
+ {
+ if(IsValid())
+ {
+ FreeLibrary(hModule);
+ }
+ hModule = NULL;
+ }
+
+public:
+
+ bool IsValid() const
+ {
+ return (hModule != NULL);
+ }
+
+ FuncPtr GetProcAddress(const std::string &symbol) const
+ {
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+ return reinterpret_cast<FuncPtr>(::GetProcAddress(hModule, symbol.c_str()));
+ }
+
+};
+
+
+#elif MPT_OS_ANDROID
+
+
+// Fake implementation.
+// Load shared objects from the JAVA side of things.
+class LibraryHandle
+{
+
+public:
+
+ LibraryHandle(const mpt::LibraryPath &path)
+ {
+ return;
+ }
+
+ LibraryHandle(const LibraryHandle &) = delete;
+
+ LibraryHandle & operator=(const LibraryHandle &) = delete;
+
+ ~LibraryHandle()
+ {
+ return;
+ }
+
+public:
+
+ bool IsValid() const
+ {
+ return true;
+ }
+
+ FuncPtr GetProcAddress(const std::string &symbol) const
+ {
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+ return reinterpret_cast<FuncPtr>(dlsym(0, symbol.c_str()));
+ }
+
+};
+
+
+
+#elif defined(MPT_WITH_LTDL)
+
+
+class LibraryHandle
+{
+
+private:
+
+ bool inited;
+ lt_dlhandle handle;
+
+public:
+
+ LibraryHandle(const mpt::LibraryPath &path)
+ : inited(false)
+ , handle(0)
+ {
+ if(lt_dlinit() != 0)
+ {
+ return;
+ }
+ inited = true;
+ handle = lt_dlopenext(path.GetFileName().AsNative().c_str());
+ }
+
+ LibraryHandle(const LibraryHandle &) = delete;
+
+ LibraryHandle & operator=(const LibraryHandle &) = delete;
+
+ ~LibraryHandle()
+ {
+ if(IsValid())
+ {
+ lt_dlclose(handle);
+ }
+ handle = 0;
+ if(inited)
+ {
+ lt_dlexit();
+ inited = false;
+ }
+ }
+
+public:
+
+ bool IsValid() const
+ {
+ return handle != 0;
+ }
+
+ FuncPtr GetProcAddress(const std::string &symbol) const
+ {
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+ return reinterpret_cast<FuncPtr>(lt_dlsym(handle, symbol.c_str()));
+ }
+
+};
+
+
+#elif defined(MPT_WITH_DL)
+
+
+class LibraryHandle
+{
+
+private:
+
+ void* handle;
+
+public:
+
+ LibraryHandle(const mpt::LibraryPath &path)
+ : handle(NULL)
+ {
+ handle = dlopen(path.GetFileName().AsNative().c_str(), RTLD_NOW);
+ }
+
+ LibraryHandle(const LibraryHandle &) = delete;
+
+ LibraryHandle & operator=(const LibraryHandle &) = delete;
+
+ ~LibraryHandle()
+ {
+ if(IsValid())
+ {
+ dlclose(handle);
+ }
+ handle = NULL;
+ }
+
+public:
+
+ bool IsValid() const
+ {
+ return handle != NULL;
+ }
+
+ FuncPtr GetProcAddress(const std::string &symbol) const
+ {
+ if(!IsValid())
+ {
+ return NULL;
+ }
+ return reinterpret_cast<FuncPtr>(dlsym(handle, symbol.c_str()));
+ }
+
+};
+
+
+#else // MPT_OS
+
+
+// dummy implementation
+
+class LibraryHandle
+{
+public:
+
+ LibraryHandle(const mpt::LibraryPath &path)
+ {
+ MPT_UNREFERENCED_PARAMETER(path);
+ return;
+ }
+
+ LibraryHandle(const LibraryHandle &) = delete;
+
+ LibraryHandle & operator=(const LibraryHandle &) = delete;
+
+ ~LibraryHandle()
+ {
+ return;
+ }
+
+public:
+
+ bool IsValid() const
+ {
+ return false;
+ }
+
+ FuncPtr GetProcAddress(const std::string &symbol) const
+ {
+ MPT_UNREFERENCED_PARAMETER(symbol);
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+ return nullptr;
+ }
+
+};
+
+
+#endif // MPT_OS
+
+
+LibraryPath::LibraryPath(mpt::LibrarySearchPath searchPath, const mpt::PathString &fileName)
+ : searchPath(searchPath)
+ , fileName(fileName)
+{
+ return;
+}
+
+
+mpt::LibrarySearchPath LibraryPath::GetSearchPath() const
+{
+ return searchPath;
+}
+
+
+mpt::PathString LibraryPath::GetFileName() const
+{
+ return fileName;
+}
+
+
+mpt::PathString LibraryPath::GetDefaultPrefix()
+{
+ #if MPT_OS_WINDOWS
+ return P_("");
+ #elif MPT_OS_ANDROID
+ return P_("lib");
+ #elif defined(MPT_WITH_LTDL)
+ return P_("lib");
+ #elif defined(MPT_WITH_DL)
+ return P_("lib");
+ #else
+ return P_("lib");
+ #endif
+}
+
+
+mpt::PathString LibraryPath::GetDefaultSuffix()
+{
+ #if MPT_OS_WINDOWS
+ return P_(".dll");
+ #elif MPT_OS_ANDROID
+ return P_(".so");
+ #elif defined(MPT_WITH_LTDL)
+ return P_(""); // handled by libltdl
+ #elif defined(MPT_WITH_DL)
+ return P_(".so");
+ #else
+ return mpt::PathString();
+ #endif
+}
+
+
+LibraryPath LibraryPath::App(const mpt::PathString &basename)
+{
+ return LibraryPath(mpt::LibrarySearchPath::Application, GetDefaultPrefix() + basename + GetDefaultSuffix());
+}
+
+
+LibraryPath LibraryPath::AppFullName(const mpt::PathString &fullname)
+{
+ return LibraryPath(mpt::LibrarySearchPath::Application, fullname + GetDefaultSuffix());
+}
+
+
+LibraryPath LibraryPath::System(const mpt::PathString &basename)
+{
+ return LibraryPath(mpt::LibrarySearchPath::System, GetDefaultPrefix() + basename + GetDefaultSuffix());
+}
+
+
+LibraryPath LibraryPath::FullPath(const mpt::PathString &path)
+{
+ return LibraryPath(mpt::LibrarySearchPath::FullPath, path);
+}
+
+
+Library::Library()
+{
+ return;
+}
+
+
+Library::Library(const mpt::LibraryPath &path)
+{
+ if(path.GetSearchPath() == mpt::LibrarySearchPath::Invalid)
+ {
+ return;
+ }
+ if(path.GetFileName().empty())
+ {
+ return;
+ }
+ m_Handle = std::make_shared<LibraryHandle>(path);
+}
+
+
+void Library::Unload()
+{
+ *this = mpt::Library();
+}
+
+
+bool Library::IsValid() const
+{
+ return m_Handle && m_Handle->IsValid();
+}
+
+
+FuncPtr Library::GetProcAddress(const std::string &symbol) const
+{
+ if(!IsValid())
+ {
+ return nullptr;
+ }
+ return m_Handle->GetProcAddress(symbol);
+}
+
+
+} // namespace mpt
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.h b/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.h
new file mode 100644
index 00000000..e5cfce25
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptLibrary.h
@@ -0,0 +1,108 @@
+/*
+ * mptLibrary.h
+ * ------------
+ * Purpose: Shared library handling.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace mpt
+{
+
+
+typedef void* (*FuncPtr)(); // pointer to function returning void*
+
+class LibraryHandle;
+
+enum class LibrarySearchPath
+{
+ Invalid,
+ Default,
+ Application,
+ System,
+ FullPath,
+};
+
+class LibraryPath
+{
+
+private:
+
+ mpt::LibrarySearchPath searchPath;
+ mpt::PathString fileName;
+
+private:
+
+ LibraryPath(mpt::LibrarySearchPath searchPath, const mpt::PathString &fileName);
+
+public:
+
+ mpt::LibrarySearchPath GetSearchPath() const;
+
+ mpt::PathString GetFileName() const;
+
+public:
+
+ // "lib" on Unix-like systems, "" on Windows
+ static mpt::PathString GetDefaultPrefix();
+
+ // ".so" or ".dylib" or ".dll"
+ static mpt::PathString GetDefaultSuffix();
+
+ // Returns the library path in the application directory, with os-specific prefix and suffix added to basename.
+ // e.g.: basename = "unmo3" --> "libunmo3.so" / "apppath/unmo3.dll"
+ static LibraryPath App(const mpt::PathString &basename);
+
+ // Returns the library path in the application directory, with os-specific suffix added to fullname.
+ // e.g.: fullname = "libunmo3" --> "libunmo3.so" / "apppath/libunmo3.dll"
+ static LibraryPath AppFullName(const mpt::PathString &fullname);
+
+ // Returns a system library name with os-specific prefix and suffix added to basename, but without any full path in order to be searched in the default search path.
+ // e.g.: basename = "unmo3" --> "libunmo3.so" / "unmo3.dll"
+ static LibraryPath System(const mpt::PathString &basename);
+
+ // Returns a system library name with os-specific suffix added to path.
+ // e.g.: path = "somepath/foo" --> "somepath/foo.so" / "somepath/foo.dll"
+ static LibraryPath FullPath(const mpt::PathString &path);
+
+};
+
+class Library
+{
+protected:
+ std::shared_ptr<LibraryHandle> m_Handle;
+public:
+ Library();
+ Library(const mpt::LibraryPath &path);
+public:
+ void Unload();
+ bool IsValid() const;
+ FuncPtr GetProcAddress(const std::string &symbol) const;
+ template <typename Tfunc>
+ bool Bind(Tfunc * & f, const std::string &symbol) const
+ {
+ #if !(MPT_OS_WINDOWS && MPT_COMPILER_GCC)
+ // MinGW64 std::is_function is always false for non __cdecl functions.
+ // See https://connect.microsoft.com/VisualStudio/feedback/details/774720/stl-is-function-bug .
+ static_assert(std::is_function<Tfunc>::value);
+ #endif
+ const FuncPtr addr = GetProcAddress(symbol);
+ f = reinterpret_cast<Tfunc*>(addr);
+ return (addr != nullptr);
+ }
+};
+
+} // namespace mpt
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptMutex.h b/Src/external_dependencies/openmpt-trunk/misc/mptMutex.h
new file mode 100644
index 00000000..060dbbdf
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptMutex.h
@@ -0,0 +1,80 @@
+/*
+ * mptMutex.h
+ * ----------
+ * Purpose: Partially implement c++ mutexes as far as openmpt needs them. Can eventually go away when we only support c++11 compilers some time.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "mpt/mutex/mutex.hpp"
+
+OPENMPT_NAMESPACE_BEGIN
+
+namespace mpt {
+
+class recursive_mutex_with_lock_count {
+
+private:
+
+ mpt::recursive_mutex mutex;
+
+#if MPT_COMPILER_MSVC
+ _Guarded_by_(mutex)
+#endif // MPT_COMPILER_MSVC
+ long lockCount;
+
+public:
+
+ recursive_mutex_with_lock_count()
+ : lockCount(0)
+ {
+ return;
+ }
+
+ ~recursive_mutex_with_lock_count()
+ {
+ return;
+ }
+
+#if MPT_COMPILER_MSVC
+ _Acquires_lock_(mutex)
+#endif // MPT_COMPILER_MSVC
+ void lock()
+ {
+ mutex.lock();
+ lockCount++;
+ }
+
+#if MPT_COMPILER_MSVC
+ _Requires_lock_held_(mutex) _Releases_lock_(mutex)
+#endif // MPT_COMPILER_MSVC
+ void unlock()
+ {
+ lockCount--;
+ mutex.unlock();
+ }
+
+public:
+
+ bool IsLockedByCurrentThread() // DEBUGGING only
+ {
+ bool islocked = false;
+ if(mutex.try_lock())
+ {
+ islocked = (lockCount > 0);
+ mutex.unlock();
+ }
+ return islocked;
+ }
+
+};
+
+} // namespace mpt
+
+OPENMPT_NAMESPACE_END
+
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptOS.cpp b/Src/external_dependencies/openmpt-trunk/misc/mptOS.cpp
new file mode 100644
index 00000000..b86d768a
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptOS.cpp
@@ -0,0 +1,551 @@
+/*
+ * mptOS.cpp
+ * ---------
+ * Purpose: Operating system version information.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+#include "mptOS.h"
+
+#include "mpt/binary/hex.hpp"
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace mpt
+{
+namespace OS
+{
+namespace Windows
+{
+
+
+#if MPT_OS_WINDOWS
+
+namespace {
+struct WindowsVersionCache
+{
+ mpt::osinfo::windows::Version version;
+ WindowsVersionCache() noexcept
+ : version(mpt::osinfo::windows::Version::Current())
+ {
+ }
+};
+}
+
+static mpt::osinfo::windows::Version GatherWindowsVersionFromCache() noexcept
+{
+ static WindowsVersionCache gs_WindowsVersionCache;
+ return gs_WindowsVersionCache.version;
+}
+
+#endif // MPT_OS_WINDOWS
+
+
+mpt::osinfo::windows::Version Version::Current() noexcept
+{
+ #if MPT_OS_WINDOWS
+ #ifdef MODPLUG_TRACKER
+ return GatherWindowsVersionFromCache();
+ #else // !MODPLUG_TRACKER
+ return mpt::osinfo::windows::Version::Current();
+ #endif // MODPLUG_TRACKER
+ #else // !MPT_OS_WINDOWS
+ return mpt::osinfo::windows::Version::NoWindows();
+ #endif // MPT_OS_WINDOWS
+}
+
+
+static constexpr struct { mpt::osinfo::windows::Version version; const mpt::uchar * name; bool showDetails; } versionMap[] =
+{
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinNewer, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 22000, 0 }, UL_("Windows 11 (or newer)"), false },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 22000, 0 }, UL_("Windows 11"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19044, 0 }, UL_("Windows 10 21H2"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19043, 0 }, UL_("Windows 10 21H1"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19042, 0 }, UL_("Windows 10 20H2"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 19041, 0 }, UL_("Windows 10 2004"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 18363, 0 }, UL_("Windows 10 1909"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 18362, 0 }, UL_("Windows 10 1903"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 17763, 0 }, UL_("Windows 10 1809"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 17134, 0 }, UL_("Windows 10 1803"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 16299, 0 }, UL_("Windows 10 1709"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 15063, 0 }, UL_("Windows 10 1703"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 14393, 0 }, UL_("Windows 10 1607"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 10586, 0 }, UL_("Windows 10 1511"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win10, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 10240, 0 }, UL_("Windows 10 1507"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win81, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 8.1"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win8, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 8"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win7, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 7"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinVista, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows Vista"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinXP64, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows XP x64 / Windows Server 2003"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinXP, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows XP"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::Win2000, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows 2000"), true },
+ { mpt::osinfo::windows::Version{ mpt::osinfo::windows::Version::WinNT4, mpt::osinfo::windows::Version::ServicePack{ 0, 0 }, 0, 0 }, UL_("Windows NT4"), true }
+};
+
+
+mpt::ustring Version::GetName(mpt::osinfo::windows::Version version)
+{
+ mpt::ustring name = U_("Generic Windows NT");
+ bool showDetails = false;
+ for(const auto &v : versionMap)
+ {
+ if(version.IsAtLeast(v.version))
+ {
+ name = v.name;
+ showDetails = v.showDetails;
+ break;
+ }
+ }
+ name += U_(" (");
+ name += MPT_UFORMAT("Version {}.{}")(version.GetSystem().Major, version.GetSystem().Minor);
+ if(showDetails)
+ {
+ if(version.GetServicePack().HasServicePack())
+ {
+ if(version.GetServicePack().Minor)
+ {
+ name += MPT_UFORMAT(" Service Pack {}.{}")(version.GetServicePack().Major, version.GetServicePack().Minor);
+ } else
+ {
+ name += MPT_UFORMAT(" Service Pack {}")(version.GetServicePack().Major);
+ }
+ }
+ if(version.GetBuild() != 0)
+ {
+ name += MPT_UFORMAT(" (Build {})")(version.GetBuild());
+ }
+ }
+ name += U_(")");
+ mpt::ustring result = name;
+ #if defined(MODPLUG_TRACKER) && MPT_OS_WINDOWS
+ if(mpt::OS::Windows::IsWine())
+ {
+ mpt::OS::Wine::VersionContext v;
+ if(v.Version().IsValid())
+ {
+ result = MPT_UFORMAT("Wine {} ({})")(
+ v.Version().AsString()
+ , name
+ );
+ } else
+ {
+ result = MPT_UFORMAT("Wine (unknown version: '{}') ({})")(
+ mpt::ToUnicode(mpt::Charset::UTF8, v.RawVersion())
+ , name
+ );
+ }
+ }
+ #endif // MODPLUG_TRACKER && MPT_OS_WINDOWS
+ return result;
+}
+
+
+mpt::osinfo::windows::Version Version::GetMinimumKernelLevel() noexcept
+{
+ uint64 minimumKernelVersion = 0;
+ #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
+ #if defined(MPT_BUILD_RETRO)
+ minimumKernelVersion = std::max(minimumKernelVersion, static_cast<uint64>(mpt::osinfo::windows::Version::WinXP));
+ #else
+ minimumKernelVersion = std::max(minimumKernelVersion, static_cast<uint64>(mpt::osinfo::windows::Version::WinVista));
+ #endif
+ #endif
+ return mpt::osinfo::windows::Version(mpt::osinfo::windows::Version::System(minimumKernelVersion), mpt::osinfo::windows::Version::ServicePack(0, 0), 0, 0);
+}
+
+
+mpt::osinfo::windows::Version Version::GetMinimumAPILevel() noexcept
+{
+ #if MPT_OS_WINDOWS
+ return mpt::osinfo::windows::Version::FromSDK();
+ #else // !MPT_OS_WINDOWS
+ return mpt::osinfo::windows::Version::NoWindows();
+ #endif // MPT_OS_WINDOWS
+}
+
+
+#if MPT_OS_WINDOWS
+
+
+#ifndef PROCESSOR_ARCHITECTURE_NEUTRAL
+#define PROCESSOR_ARCHITECTURE_NEUTRAL 11
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_ARM64
+#define PROCESSOR_ARCHITECTURE_ARM64 12
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64
+#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_IA32_ON_ARM64
+#define PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 14
+#endif
+
+
+struct OSArchitecture
+{
+ uint16 ProcessorArchitectur;
+ Architecture Host;
+ Architecture Process;
+};
+static constexpr OSArchitecture architectures [] = {
+ { PROCESSOR_ARCHITECTURE_INTEL , Architecture::x86 , Architecture::x86 },
+ { PROCESSOR_ARCHITECTURE_AMD64 , Architecture::amd64 , Architecture::amd64 },
+ { PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 , Architecture::amd64 , Architecture::x86 },
+ { PROCESSOR_ARCHITECTURE_ARM , Architecture::arm , Architecture::arm },
+ { PROCESSOR_ARCHITECTURE_ARM64 , Architecture::arm64 , Architecture::arm64 },
+ { PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64, Architecture::arm64 , Architecture::arm },
+ { PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 , Architecture::arm64 , Architecture::x86 },
+ { PROCESSOR_ARCHITECTURE_MIPS , Architecture::mips , Architecture::mips },
+ { PROCESSOR_ARCHITECTURE_PPC , Architecture::ppc , Architecture::ppc },
+ { PROCESSOR_ARCHITECTURE_SHX , Architecture::shx , Architecture::shx },
+ { PROCESSOR_ARCHITECTURE_ALPHA , Architecture::alpha , Architecture::alpha },
+ { PROCESSOR_ARCHITECTURE_ALPHA64 , Architecture::alpha64, Architecture::alpha64 },
+ { PROCESSOR_ARCHITECTURE_IA64 , Architecture::ia64 , Architecture::ia64 },
+ { PROCESSOR_ARCHITECTURE_MSIL , Architecture::unknown, Architecture::unknown },
+ { PROCESSOR_ARCHITECTURE_NEUTRAL , Architecture::unknown, Architecture::unknown },
+ { PROCESSOR_ARCHITECTURE_UNKNOWN , Architecture::unknown, Architecture::unknown }
+};
+
+
+struct HostArchitecture
+{
+ Architecture Host;
+ Architecture Process;
+ EmulationLevel Emulation;
+};
+static constexpr HostArchitecture hostArchitectureCanRun [] = {
+ { Architecture::x86 , Architecture::x86 , EmulationLevel::Native },
+ { Architecture::amd64 , Architecture::amd64 , EmulationLevel::Native },
+ { Architecture::amd64 , Architecture::x86 , EmulationLevel::Virtual },
+ { Architecture::arm , Architecture::arm , EmulationLevel::Native },
+ { Architecture::arm64 , Architecture::arm64 , EmulationLevel::Native },
+ { Architecture::arm64 , Architecture::arm , EmulationLevel::Virtual },
+ { Architecture::arm64 , Architecture::x86 , EmulationLevel::Software },
+ { Architecture::arm64 , Architecture::amd64 , EmulationLevel::Software },
+ { Architecture::mips , Architecture::mips , EmulationLevel::Native },
+ { Architecture::ppc , Architecture::ppc , EmulationLevel::Native },
+ { Architecture::shx , Architecture::shx , EmulationLevel::Native },
+ { Architecture::alpha , Architecture::alpha , EmulationLevel::Native },
+ { Architecture::alpha64, Architecture::alpha64, EmulationLevel::Native },
+ { Architecture::alpha64, Architecture::alpha , EmulationLevel::Virtual },
+ { Architecture::ia64 , Architecture::ia64 , EmulationLevel::Native },
+ { Architecture::ia64 , Architecture::x86 , EmulationLevel::Hardware }
+};
+
+
+struct ArchitectureInfo
+{
+ Architecture Arch;
+ int Bitness;
+ const mpt::uchar * Name;
+};
+static constexpr ArchitectureInfo architectureInfo [] = {
+ { Architecture::x86 , 32, UL_("x86") },
+ { Architecture::amd64 , 64, UL_("amd64") },
+ { Architecture::arm , 32, UL_("arm") },
+ { Architecture::arm64 , 64, UL_("arm64") },
+ { Architecture::mips , 32, UL_("mips") },
+ { Architecture::ppc , 32, UL_("ppc") },
+ { Architecture::shx , 32, UL_("shx") },
+ { Architecture::alpha , 32, UL_("alpha") },
+ { Architecture::alpha64, 64, UL_("alpha64") },
+ { Architecture::ia64 , 64, UL_("ia64") }
+};
+
+
+int Bitness(Architecture arch) noexcept
+{
+ for(const auto &info : architectureInfo)
+ {
+ if(arch == info.Arch)
+ {
+ return info.Bitness;
+ }
+ }
+ return 0;
+}
+
+
+mpt::ustring Name(Architecture arch)
+{
+ for(const auto &info : architectureInfo)
+ {
+ if(arch == info.Arch)
+ {
+ return info.Name;
+ }
+ }
+ return mpt::ustring();
+}
+
+
+Architecture GetHostArchitecture() noexcept
+{
+ SYSTEM_INFO systemInfo = {};
+ GetNativeSystemInfo(&systemInfo);
+ for(const auto &arch : architectures)
+ {
+ if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur)
+ {
+ return arch.Host;
+ }
+ }
+ return Architecture::unknown;
+}
+
+
+Architecture GetProcessArchitecture() noexcept
+{
+ SYSTEM_INFO systemInfo = {};
+ GetSystemInfo(&systemInfo);
+ for(const auto &arch : architectures)
+ {
+ if(systemInfo.wProcessorArchitecture == arch.ProcessorArchitectur)
+ {
+ return arch.Process;
+ }
+ }
+ return Architecture::unknown;
+}
+
+
+EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept
+{
+ for(const auto & can : hostArchitectureCanRun)
+ {
+ if(can.Host == host && can.Process == process)
+ {
+ return can.Emulation;
+ }
+ }
+ return EmulationLevel::NA;
+}
+
+
+std::vector<Architecture> GetSupportedProcessArchitectures(Architecture host)
+{
+ std::vector<Architecture> result;
+ for(const auto & entry : hostArchitectureCanRun)
+ {
+ if(entry.Host == host)
+ {
+ result.push_back(entry.Process);
+ }
+ }
+ return result;
+}
+
+
+uint64 GetSystemMemorySize()
+{
+ MEMORYSTATUSEX memoryStatus = {};
+ memoryStatus.dwLength = sizeof(MEMORYSTATUSEX);
+ if(GlobalMemoryStatusEx(&memoryStatus) == 0)
+ {
+ return 0;
+ }
+ return memoryStatus.ullTotalPhys;
+}
+
+
+#endif // MPT_OS_WINDOWS
+
+
+#if defined(MODPLUG_TRACKER)
+
+
+#if MPT_OS_WINDOWS
+
+static bool GatherSystemIsWine()
+{
+ bool SystemIsWine = false;
+ std::optional<mpt::library> NTDLL = mpt::library::load({ mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("ntdll.dll"), mpt::library::path_suffix::none });
+ if(NTDLL)
+ {
+ SystemIsWine = (NTDLL->get_address("wine_get_version") != nullptr);
+ }
+ return SystemIsWine;
+}
+
+namespace {
+struct SystemIsWineCache
+{
+ bool SystemIsWine;
+ SystemIsWineCache()
+ : SystemIsWine(GatherSystemIsWine())
+ {
+ return;
+ }
+ SystemIsWineCache(bool isWine)
+ : SystemIsWine(isWine)
+ {
+ return;
+ }
+};
+}
+
+#endif // MPT_OS_WINDOWS
+
+static bool SystemIsWine(bool allowDetection = true)
+{
+ #if MPT_OS_WINDOWS
+ static SystemIsWineCache gs_SystemIsWineCache = allowDetection ? SystemIsWineCache() : SystemIsWineCache(false);
+ if(!allowDetection)
+ { // catch too late calls of PreventWineDetection
+ MPT_ASSERT(!gs_SystemIsWineCache.SystemIsWine);
+ }
+ return gs_SystemIsWineCache.SystemIsWine;
+ #else
+ MPT_UNREFERENCED_PARAMETER(allowDetection);
+ return false;
+ #endif
+}
+
+void PreventWineDetection()
+{
+ SystemIsWine(false);
+}
+
+bool IsOriginal()
+{
+ return mpt::OS::Windows::Version::Current().IsWindows() && !SystemIsWine();
+}
+
+bool IsWine()
+{
+ return mpt::OS::Windows::Version::Current().IsWindows() && SystemIsWine();
+}
+
+
+#endif // MODPLUG_TRACKER
+
+
+} // namespace Windows
+} // namespace OS
+} // namespace mpt
+
+
+
+namespace mpt
+{
+namespace OS
+{
+namespace Wine
+{
+
+
+Version::Version()
+{
+ return;
+}
+
+
+Version::Version(const mpt::ustring &rawVersion)
+ : mpt::osinfo::windows::wine::version()
+{
+ if(rawVersion.empty())
+ {
+ return;
+ }
+ std::vector<uint8> version = mpt::String::Split<uint8>(rawVersion, U_("."));
+ if(version.size() < 2)
+ {
+ return;
+ }
+ mpt::ustring parsedVersion = mpt::String::Combine(version, U_("."));
+ std::size_t len = std::min(parsedVersion.length(), rawVersion.length());
+ if(len == 0)
+ {
+ return;
+ }
+ if(parsedVersion.substr(0, len) != rawVersion.substr(0, len))
+ {
+ return;
+ }
+ valid = true;
+ vmajor = version[0];
+ vminor = version[1];
+ vupdate = (version.size() >= 3) ? version[2] : 0;
+}
+
+
+Version::Version(uint8 vmajor, uint8 vminor, uint8 vupdate)
+ : mpt::osinfo::windows::wine::version(vmajor, vminor, vupdate)
+{
+ return;
+}
+
+
+mpt::ustring Version::AsString() const
+{
+ return mpt::ufmt::dec(GetMajor()) + U_(".") + mpt::ufmt::dec(GetMinor()) + U_(".") + mpt::ufmt::dec(GetUpdate());
+}
+
+
+
+mpt::OS::Wine::Version GetMinimumWineVersion()
+{
+ mpt::OS::Wine::Version minimumWineVersion = mpt::OS::Wine::Version(0,0,0);
+ #if MPT_OS_WINDOWS && MPT_COMPILER_MSVC
+ minimumWineVersion = mpt::OS::Wine::Version(1,8,0);
+ #endif
+ return minimumWineVersion;
+}
+
+
+VersionContext::VersionContext()
+ : m_IsWine(false)
+ , m_HostClass(mpt::osinfo::osclass::Unknown)
+{
+ #if MPT_OS_WINDOWS
+ m_IsWine = mpt::OS::Windows::IsWine();
+ if(!m_IsWine)
+ {
+ return;
+ }
+ std::optional<mpt::library> NTDLL = mpt::library::load({mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("ntdll.dll"), mpt::library::path_suffix::none});
+ if(NTDLL)
+ {
+ const char * (__cdecl * wine_get_version)(void) = nullptr;
+ const char * (__cdecl * wine_get_build_id)(void) = nullptr;
+ void (__cdecl * wine_get_host_version)(const char * *, const char * *) = nullptr;
+ NTDLL->bind(wine_get_version, "wine_get_version");
+ NTDLL->bind(wine_get_build_id, "wine_get_build_id");
+ NTDLL->bind(wine_get_host_version, "wine_get_host_version");
+ const char * wine_version = nullptr;
+ const char * wine_build_id = nullptr;
+ const char * wine_host_sysname = nullptr;
+ const char * wine_host_release = nullptr;
+ wine_version = wine_get_version ? wine_get_version() : "";
+ wine_build_id = wine_get_build_id ? wine_get_build_id() : "";
+ if(wine_get_host_version)
+ {
+ wine_get_host_version(&wine_host_sysname, &wine_host_release);
+ }
+ m_RawVersion = wine_version ? wine_version : "";
+ m_RawBuildID = wine_build_id ? wine_build_id : "";
+ m_RawHostSysName = wine_host_sysname ? wine_host_sysname : "";
+ m_RawHostRelease = wine_host_release ? wine_host_release : "";
+ }
+ m_Version = mpt::OS::Wine::Version(mpt::ToUnicode(mpt::Charset::UTF8, m_RawVersion));
+ m_HostClass = mpt::osinfo::get_class_from_sysname(m_RawHostSysName);
+ #endif // MPT_OS_WINDOWS
+}
+
+
+} // namespace Wine
+} // namespace OS
+} // namespace mpt
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptOS.h b/Src/external_dependencies/openmpt-trunk/misc/mptOS.h
new file mode 100644
index 00000000..c6640bd2
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptOS.h
@@ -0,0 +1,161 @@
+/*
+ * mptOS.h
+ * -------
+ * Purpose: Operating system version information.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "mpt/library/library.hpp"
+#include "mpt/osinfo/class.hpp"
+#include "mpt/osinfo/windows_version.hpp"
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+namespace mpt
+{
+namespace OS
+{
+namespace Windows
+{
+
+namespace Version
+{
+
+ inline constexpr auto WinNT4 = mpt::osinfo::windows::Version::WinNT4;
+ inline constexpr auto Win2000 = mpt::osinfo::windows::Version::Win2000;
+ inline constexpr auto WinXP = mpt::osinfo::windows::Version::WinXP;
+ inline constexpr auto WinXP64 = mpt::osinfo::windows::Version::WinXP64;
+ inline constexpr auto WinVista = mpt::osinfo::windows::Version::WinVista;
+ inline constexpr auto Win7 = mpt::osinfo::windows::Version::Win7;
+ inline constexpr auto Win8 = mpt::osinfo::windows::Version::Win8;
+ inline constexpr auto Win81 = mpt::osinfo::windows::Version::Win81;
+ inline constexpr auto Win10 = mpt::osinfo::windows::Version::Win10;
+ inline constexpr auto WinNewer = mpt::osinfo::windows::Version::WinNewer;
+
+ mpt::osinfo::windows::Version Current() noexcept;
+
+ mpt::ustring GetName(mpt::osinfo::windows::Version version);
+ mpt::ustring GetNameShort(mpt::osinfo::windows::Version version);
+
+ mpt::osinfo::windows::Version GetMinimumKernelLevel() noexcept;
+ mpt::osinfo::windows::Version GetMinimumAPILevel() noexcept;
+
+} // namespace Version
+
+#if MPT_OS_WINDOWS
+
+enum class Architecture
+{
+ unknown = -1,
+
+ x86 = 0x0401,
+ amd64 = 0x0801,
+ arm = 0x0402,
+ arm64 = 0x0802,
+
+ mips = 0x0403,
+ ppc = 0x0404,
+ shx = 0x0405,
+
+ alpha = 0x0406,
+ alpha64 = 0x0806,
+
+ ia64 = 0x0807,
+};
+
+enum class EmulationLevel
+{
+ Native,
+ Virtual,
+ Hardware,
+ Software,
+ NA,
+};
+
+int Bitness(Architecture arch) noexcept;
+
+mpt::ustring Name(Architecture arch);
+
+Architecture GetHostArchitecture() noexcept;
+Architecture GetProcessArchitecture() noexcept;
+
+EmulationLevel HostCanRun(Architecture host, Architecture process) noexcept;
+
+std::vector<Architecture> GetSupportedProcessArchitectures(Architecture host);
+
+uint64 GetSystemMemorySize();
+
+#endif // MPT_OS_WINDOWS
+
+
+#if defined(MODPLUG_TRACKER)
+
+void PreventWineDetection();
+
+bool IsOriginal();
+bool IsWine();
+
+#endif // MODPLUG_TRACKER
+
+} // namespace Windows
+} // namespace OS
+} // namespace mpt
+
+
+namespace mpt
+{
+namespace OS
+{
+namespace Wine
+{
+
+class Version
+ : public mpt::osinfo::windows::wine::version
+{
+public:
+ Version();
+ Version(uint8 vmajor, uint8 vminor, uint8 vupdate);
+ explicit Version(const mpt::ustring &version);
+public:
+ mpt::ustring AsString() const;
+};
+
+mpt::OS::Wine::Version GetMinimumWineVersion();
+
+class VersionContext
+{
+protected:
+ bool m_IsWine;
+ std::string m_RawVersion;
+ std::string m_RawBuildID;
+ std::string m_RawHostSysName;
+ std::string m_RawHostRelease;
+ mpt::OS::Wine::Version m_Version;
+ mpt::osinfo::osclass m_HostClass;
+public:
+ VersionContext();
+public:
+ bool IsWine() const { return m_IsWine; }
+ std::string RawVersion() const { return m_RawVersion; }
+ std::string RawBuildID() const { return m_RawBuildID; }
+ std::string RawHostSysName() const { return m_RawHostSysName; }
+ std::string RawHostRelease() const { return m_RawHostRelease; }
+ mpt::OS::Wine::Version Version() const { return m_Version; }
+ mpt::osinfo::osclass HostClass() const { return m_HostClass; }
+};
+
+} // namespace Wine
+} // namespace OS
+} // namespace mpt
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptOSException.h b/Src/external_dependencies/openmpt-trunk/misc/mptOSException.h
new file mode 100644
index 00000000..a85f8bda
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptOSException.h
@@ -0,0 +1,180 @@
+/*
+ * mptOSException.h
+ * ----------------
+ * Purpose: platform-specific exception/signal 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 "mptBaseMacros.h"
+#include "mptBaseTypes.h"
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif // MPT_OS_WINDOWS
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+#if MPT_OS_WINDOWS
+
+
+namespace Windows
+{
+
+
+namespace SEH
+{
+
+
+struct Code
+{
+private:
+ DWORD m_Code;
+public:
+ constexpr Code(DWORD code) noexcept
+ : m_Code(code)
+ {
+ return;
+ }
+public:
+ constexpr DWORD code() const noexcept
+ {
+ return m_Code;
+ }
+};
+
+
+template <typename Tfn, typename Tfilter, typename Thandler>
+auto TryFilterHandleThrow(const Tfn &fn, const Tfilter &filter, const Thandler &handler) -> decltype(fn())
+{
+ static_assert(std::is_trivially_copy_assignable<decltype(fn())>::value);
+ static_assert(std::is_trivially_copy_constructible<decltype(fn())>::value);
+ static_assert(std::is_trivially_move_assignable<decltype(fn())>::value);
+ static_assert(std::is_trivially_move_constructible<decltype(fn())>::value);
+ DWORD code = 0;
+ __try
+ {
+ return fn();
+ } __except(filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ code = GetExceptionCode();
+ }
+ throw Windows::SEH::Code(code);
+}
+
+
+template <typename Tfn, typename Tfilter, typename Thandler>
+void TryFilterHandleVoid(const Tfn &fn, const Tfilter &filter, const Thandler &handler)
+{
+ static_assert(std::is_same<decltype(fn()), void>::value || std::is_trivially_copy_assignable<decltype(fn())>::value);
+ static_assert(std::is_same<decltype(fn()), void>::value || std::is_trivially_copy_constructible<decltype(fn())>::value);
+ static_assert(std::is_same<decltype(fn()), void>::value || std::is_trivially_move_assignable<decltype(fn())>::value);
+ static_assert(std::is_same<decltype(fn()), void>::value || std::is_trivially_move_constructible<decltype(fn())>::value);
+ __try
+ {
+ fn();
+ return;
+ } __except(filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ DWORD code = GetExceptionCode();
+ handler(code);
+ }
+ return;
+}
+
+
+template <typename Tfn, typename Tfilter, typename Thandler>
+auto TryFilterHandleDefault(const Tfn &fn, const Tfilter &filter, const Thandler &handler, decltype(fn()) def = decltype(fn()){}) -> decltype(fn())
+{
+ static_assert(std::is_trivially_copy_assignable<decltype(fn())>::value);
+ static_assert(std::is_trivially_copy_constructible<decltype(fn())>::value);
+ static_assert(std::is_trivially_move_assignable<decltype(fn())>::value);
+ static_assert(std::is_trivially_move_constructible<decltype(fn())>::value);
+ auto result = def;
+ __try
+ {
+ result = fn();
+ } __except(filter(GetExceptionCode(), GetExceptionInformation()))
+ {
+ DWORD code = GetExceptionCode();
+ result = handler(code);
+ }
+ return result;
+}
+
+
+template <typename Tfn>
+auto TryReturnOrThrow(const Tfn &fn) -> decltype(fn())
+{
+ return TryFilterHandleThrow(
+ fn,
+ [](auto code, auto eptr)
+ {
+ MPT_UNREFERENCED_PARAMETER(code);
+ MPT_UNREFERENCED_PARAMETER(eptr);
+ return EXCEPTION_EXECUTE_HANDLER;
+ },
+ [](auto code)
+ {
+ throw Windows::SEH::Code(code);
+ });
+}
+
+
+template <typename Tfn>
+DWORD TryOrError(const Tfn &fn)
+{
+ DWORD result = DWORD{0};
+ TryFilterHandleVoid(
+ fn,
+ [](auto code, auto eptr)
+ {
+ MPT_UNREFERENCED_PARAMETER(code);
+ MPT_UNREFERENCED_PARAMETER(eptr);
+ return EXCEPTION_EXECUTE_HANDLER;
+ },
+ [&result](auto code)
+ {
+ result = code;
+ });
+ return result;
+}
+
+
+template <typename Tfn>
+auto TryReturnOrDefault(const Tfn &fn, decltype(fn()) def = decltype(fn()){}) -> decltype(fn())
+{
+ return TryFilterHandleDefault(
+ fn,
+ [](auto code, auto eptr)
+ {
+ MPT_UNREFERENCED_PARAMETER(code);
+ MPT_UNREFERENCED_PARAMETER(eptr);
+ return EXCEPTION_EXECUTE_HANDLER;
+ },
+ [def](auto code)
+ {
+ MPT_UNREFERENCED_PARAMETER(code);
+ return def;
+ });
+}
+
+
+} // namspace SEH
+
+
+} // namespace Windows
+
+
+#endif // MPT_OS_WINDOWS
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptWine.cpp b/Src/external_dependencies/openmpt-trunk/misc/mptWine.cpp
new file mode 100644
index 00000000..0219f915
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptWine.cpp
@@ -0,0 +1,750 @@
+/*
+ * mptWine.cpp
+ * -----------
+ * Purpose: Wine stuff.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#include "stdafx.h"
+#include "mptWine.h"
+
+#include "mptOS.h"
+#include "../common/mptFileIO.h"
+
+#include <deque>
+#include <map>
+
+#if MPT_OS_WINDOWS
+#include <windows.h>
+#endif
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+#if MPT_OS_WINDOWS
+
+
+namespace mpt
+{
+namespace Wine
+{
+
+
+
+Context::Context(mpt::OS::Wine::VersionContext versionContext)
+ : m_VersionContext(versionContext)
+ , wine_get_dos_file_name(nullptr)
+ , wine_get_unix_file_name(nullptr)
+{
+ if(!mpt::OS::Windows::IsWine())
+ {
+ throw mpt::Wine::Exception("Wine not detected.");
+ }
+ if(!m_VersionContext.Version().IsValid())
+ {
+ throw mpt::Wine::Exception("Unknown Wine version detected.");
+ }
+ m_Kernel32 = std::make_shared<std::optional<mpt::library>>(mpt::library::load({ mpt::library::path_search::system, mpt::library::path_prefix::none, MPT_PATH("kernel32.dll"), mpt::library::path_suffix::none }));
+ if(!m_Kernel32->has_value())
+ {
+ throw mpt::Wine::Exception("Could not load Wine kernel32.dll.");
+ }
+ if(!(*m_Kernel32)->bind(wine_get_unix_file_name, "wine_get_unix_file_name"))
+ {
+ throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_unix_file_name.");
+ }
+ if(!(*m_Kernel32)->bind(wine_get_dos_file_name, "wine_get_dos_file_name"))
+ {
+ throw mpt::Wine::Exception("Could not bind Wine kernel32.dll:wine_get_dos_file_name.");
+ }
+ {
+ std::string out;
+ std::string err;
+ try
+ {
+ if(ExecutePosixShellCommand("uname -m", out, err) != 0)
+ {
+ throw mpt::Wine::Exception("Wine 'uname -m' failed.");
+ }
+ if(!err.empty())
+ {
+ throw mpt::Wine::Exception("Wine 'uname -m' failed.");
+ }
+ out = mpt::trim(out, std::string("\r\n"));
+ m_Uname_m = out;
+ } catch(const std::exception &)
+ {
+ m_Uname_m = std::string();
+ }
+ }
+ try
+ {
+ m_HOME = GetPosixEnvVar("HOME");
+ } catch(const std::exception &)
+ {
+ m_HOME = std::string();
+ }
+ try
+ {
+ m_XDG_DATA_HOME = GetPosixEnvVar("XDG_DATA_HOME");
+ if(m_XDG_DATA_HOME.empty())
+ {
+ m_XDG_DATA_HOME = m_HOME + "/.local/share";
+ }
+ } catch(const std::exception &)
+ {
+ m_XDG_DATA_HOME = std::string();
+ }
+ try
+ {
+ m_XDG_CACHE_HOME = GetPosixEnvVar("XDG_CACHE_HOME");
+ if(m_XDG_CACHE_HOME.empty())
+ {
+ m_XDG_CACHE_HOME = m_HOME + "/.cache";
+ }
+ } catch(const std::exception &)
+ {
+ m_XDG_CACHE_HOME = std::string();
+ }
+ try
+ {
+ m_XDG_CONFIG_HOME = GetPosixEnvVar("XDG_CONFIG_HOME");
+ if(m_XDG_CONFIG_HOME.empty())
+ {
+ m_XDG_CONFIG_HOME = m_HOME + "/.config";
+ }
+ } catch(const std::exception &)
+ {
+ m_XDG_CONFIG_HOME = std::string();
+ }
+}
+
+
+std::string Context::PathToPosix(mpt::PathString windowsPath)
+{
+ std::string result;
+ if(windowsPath.empty())
+ {
+ return result;
+ }
+ if(windowsPath.Length() >= 32000)
+ {
+ throw mpt::Wine::Exception("Path too long.");
+ }
+ LPSTR tmp = nullptr;
+ tmp = wine_get_unix_file_name(windowsPath.ToWide().c_str());
+ if(!tmp)
+ {
+ throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_unix_file_name failed.");
+ }
+ result = tmp;
+ HeapFree(GetProcessHeap(), 0, tmp);
+ tmp = nullptr;
+ return result;
+}
+
+mpt::PathString Context::PathToWindows(std::string hostPath)
+{
+ mpt::PathString result;
+ if(hostPath.empty())
+ {
+ return result;
+ }
+ if(hostPath.length() >= 32000)
+ {
+ throw mpt::Wine::Exception("Path too long.");
+ }
+ LPWSTR tmp = nullptr;
+ tmp = wine_get_dos_file_name(hostPath.c_str());
+ if(!tmp)
+ {
+ throw mpt::Wine::Exception("Wine kernel32.dll:wine_get_dos_file_name failed.");
+ }
+ result = mpt::PathString::FromWide(tmp);
+ HeapFree(GetProcessHeap(), 0, tmp);
+ tmp = nullptr;
+ return result;
+}
+
+std::string Context::PathToPosixCanonical(mpt::PathString windowsPath)
+{
+ std::string result;
+ std::string hostPath = PathToPosix(windowsPath);
+ if(hostPath.empty())
+ {
+ return result;
+ }
+ std::string output;
+ std::string error;
+ int exitcode = ExecutePosixShellCommand(std::string() + "readlink -f " + EscapePosixShell(hostPath), output, error);
+ if(!error.empty())
+ {
+ throw mpt::Wine::Exception("Wine readlink failed: " + error);
+ }
+ if(exitcode != 0 && exitcode != 1)
+ {
+ throw mpt::Wine::Exception("Wine readlink failed.");
+ }
+ std::string trimmedOutput = mpt::trim(output, std::string("\r\n"));
+ result = trimmedOutput;
+ return result;
+}
+
+
+static void ExecutePosixCommandProgressDefault(void * /*userdata*/ )
+{
+ ::Sleep(10);
+ return;
+}
+
+static ExecuteProgressResult ExecutePosixShellScriptProgressDefault(void * /*userdata*/ )
+{
+ ::Sleep(10);
+ return ExecuteProgressContinueWaiting;
+}
+
+
+std::string Context::EscapePosixShell(std::string line)
+{
+ const char escape_chars [] = { '|', '&', ';', '<', '>', '(', ')', '$', '`', '"', '\'', ' ', '\t' };
+ const char maybe_escape_chars [] = { '*', '?', '[', '#', '~', '=', '%' };
+ line = mpt::replace(line, std::string("\\"), std::string("\\\\"));
+ for(char c : escape_chars)
+ {
+ line = mpt::replace(line, std::string(1, c), std::string("\\") + std::string(1, c));
+ }
+ for(char c : maybe_escape_chars)
+ {
+ line = mpt::replace(line, std::string(1, c), std::string("\\") + std::string(1, c));
+ }
+ return line;
+}
+
+
+ExecResult Context::ExecutePosixShellScript(std::string script, FlagSet<ExecFlags> flags, std::map<std::string, std::vector<char> > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata)
+{
+ // Relevant documentation:
+ // https://stackoverflow.com/questions/6004070/execute-shell-commands-from-program-running-in-wine
+ // https://www.winehq.org/pipermail/wine-bugs/2014-January/374918.html
+ // https://bugs.winehq.org/show_bug.cgi?id=34730
+
+ if(!progress) progress = &ExecutePosixCommandProgressDefault;
+ if(!progressCancel) progressCancel = &ExecutePosixShellScriptProgressDefault;
+
+ if(flags[ExecFlagInteractive]) flags.reset(ExecFlagSilent);
+ if(flags[ExecFlagSplitOutput]) flags.set(ExecFlagSilent);
+
+ std::vector<mpt::PathString> tempfiles;
+
+ progress(userdata);
+
+ mpt::TempDirGuard dirWindowsTemp(mpt::CreateTempFileName());
+ if(dirWindowsTemp.GetDirname().empty())
+ {
+ throw mpt::Wine::Exception("Creating temporary directoy failed.");
+ }
+ const std::string dirPosix = PathToPosix(dirWindowsTemp.GetDirname());
+ if(dirPosix.empty())
+ {
+ throw mpt::Wine::Exception("mpt::Wine::ConvertWindowsPathToHost returned empty path.");
+ }
+ const std::string dirPosixEscape = EscapePosixShell(dirPosix);
+ const mpt::PathString dirWindows = dirWindowsTemp.GetDirname();
+
+ progress(userdata);
+
+ // write the script to disk
+ mpt::PathString scriptFilenameWindows = dirWindows + P_("script.sh");
+ {
+ mpt::ofstream tempfile(scriptFilenameWindows, std::ios::binary);
+ tempfile << script;
+ tempfile.flush();
+ if(!tempfile)
+ {
+ throw mpt::Wine::Exception("Error writing script.sh.");
+ }
+ }
+ const std::string scriptFilenamePosix = PathToPosix(scriptFilenameWindows);
+ if(scriptFilenamePosix.empty())
+ {
+ throw mpt::Wine::Exception("Error converting script.sh path.");
+ }
+ const std::string scriptFilenamePosixEscape = EscapePosixShell(scriptFilenamePosix);
+
+ progress(userdata);
+
+ // create a wrapper that will call the script and gather result.
+ mpt::PathString wrapperstarterFilenameWindows = dirWindows + P_("wrapperstarter.sh");
+ {
+ mpt::ofstream tempfile(wrapperstarterFilenameWindows, std::ios::binary);
+ std::string wrapperstarterscript;
+ wrapperstarterscript += std::string() + "#!/usr/bin/env sh" "\n";
+ wrapperstarterscript += std::string() + "exec /usr/bin/env sh " + dirPosixEscape + "wrapper.sh" "\n";
+ tempfile << wrapperstarterscript;
+ tempfile.flush();
+ if(!tempfile)
+ {
+ throw mpt::Wine::Exception("Error writing wrapper.sh.");
+ }
+ }
+ mpt::PathString wrapperFilenameWindows = dirWindows + P_("wrapper.sh");
+ std::string cleanupscript;
+ {
+ mpt::ofstream tempfile(wrapperFilenameWindows, std::ios::binary);
+ std::string wrapperscript;
+ if(!flags[ExecFlagSilent])
+ {
+ wrapperscript += "printf \"\\033]0;" + title + "\\a\"" "\n";
+ }
+ wrapperscript += "chmod u+x " + scriptFilenamePosixEscape + "\n";
+ wrapperscript += "cd " + dirPosixEscape + "filetree" "\n";
+ if(flags[ExecFlagInteractive])
+ { // no stdout/stderr capturing for interactive scripts
+ wrapperscript += scriptFilenamePosixEscape + "\n";
+ wrapperscript += "MPT_RESULT=$?" "\n";
+ wrapperscript += "echo ${MPT_RESULT} > " + dirPosixEscape + "exit" "\n";
+ } else if(flags[ExecFlagSplitOutput])
+ {
+ wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 4>" + dirPosixEscape + "exit 1>" + dirPosixEscape + "out 2>" + dirPosixEscape + "err" "\n";
+ } else
+ {
+ wrapperscript += "(" + scriptFilenamePosixEscape + "; echo $? >&4) 2>&1 4>" + dirPosixEscape + "exit | tee " + dirPosixEscape + "out" "\n";
+ }
+ wrapperscript += "echo done > " + dirPosixEscape + "done" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "done" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "exit" "\n";
+ if(flags[ExecFlagInteractive])
+ {
+ // nothing
+ } else if(flags[ExecFlagSplitOutput])
+ {
+ cleanupscript += "rm " + dirPosixEscape + "out" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "err" "\n";
+ } else
+ {
+ cleanupscript += "rm " + dirPosixEscape + "out" "\n";
+ }
+ cleanupscript += "rm -r " + dirPosixEscape + "filetree" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "script.sh" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "wrapper.sh" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "wrapperstarter.sh" "\n";
+ cleanupscript += "rm " + dirPosixEscape + "terminal.sh" "\n";
+ if(flags[ExecFlagAsync])
+ {
+ wrapperscript += cleanupscript;
+ cleanupscript.clear();
+ }
+ tempfile << wrapperscript;
+ tempfile.flush();
+ if(!tempfile)
+ {
+ throw mpt::Wine::Exception("Error writing wrapper.sh.");
+ }
+ }
+
+ progress(userdata);
+
+ ::CreateDirectory((dirWindows + P_("filetree")).AsNative().c_str(), nullptr);
+ for(const auto &file : filetree)
+ {
+ std::vector<mpt::ustring> path = mpt::String::Split<mpt::ustring>(mpt::ToUnicode(mpt::Charset::UTF8, file.first), U_("/"));
+ mpt::PathString combinedPath = dirWindows + P_("filetree") + P_("\\");
+ if(path.size() > 1)
+ {
+ for(std::size_t singlepath = 0; singlepath < path.size() - 1; ++singlepath)
+ {
+ if(path[singlepath].empty())
+ {
+ continue;
+ }
+ combinedPath += mpt::PathString::FromUnicode(path[singlepath]);
+ if(!combinedPath.IsDirectory())
+ {
+ if(::CreateDirectory(combinedPath.AsNative().c_str(), nullptr) == 0)
+ {
+ throw mpt::Wine::Exception("Error writing filetree.");
+ }
+ }
+ combinedPath += P_("\\");
+ }
+ }
+ try
+ {
+ mpt::LazyFileRef out(dirWindows + P_("filetree") + P_("\\") + mpt::PathString::FromUTF8(mpt::replace(file.first, std::string("/"), std::string("\\"))));
+ out = file.second;
+ } catch(std::exception &)
+ {
+ throw mpt::Wine::Exception("Error writing filetree.");
+ }
+ }
+
+ progress(userdata);
+
+ // create a wrapper that will find a suitable terminal and run the wrapper script in the terminal window.
+ mpt::PathString terminalWrapperFilenameWindows = dirWindows + P_("terminal.sh");
+ {
+ mpt::ofstream tempfile(terminalWrapperFilenameWindows, std::ios::binary);
+ // NOTE:
+ // Modern terminals detach themselves from the invoking shell if another instance is already present.
+ // This means we cannot rely on terminal invocation being syncronous.
+ static constexpr const char * terminals[] =
+ {
+ "x-terminal-emulator",
+ "konsole",
+ "mate-terminal",
+ "xfce4-terminal",
+ "gnome-terminal",
+ "uxterm",
+ "xterm",
+ "rxvt",
+ };
+ std::string terminalscript = "\n";
+ for(const std::string terminal : terminals)
+ {
+ // mate-terminal on Debian 8 cannot execute commands with arguments,
+ // thus we use a separate script that requires no arguments to execute.
+ terminalscript += "if command -v " + terminal + " 2>/dev/null 1>/dev/null ; then" "\n";
+ terminalscript += " chmod u+x " + dirPosixEscape + "wrapperstarter.sh" "\n";
+ terminalscript += " exec `command -v " + terminal + "` -e \"" + dirPosixEscape + "wrapperstarter.sh\"" "\n";
+ terminalscript += "fi" "\n";
+ }
+
+ tempfile << terminalscript;
+ tempfile.flush();
+ if(!tempfile)
+ {
+ return ExecResult::Error();
+ }
+ }
+
+ progress(userdata);
+
+ // build unix command line
+ std::string unixcommand;
+ bool createProcessSuccess = false;
+
+ if(!createProcessSuccess)
+ {
+
+ if(flags[ExecFlagSilent])
+ {
+ unixcommand = "/usr/bin/env sh \"" + dirPosixEscape + "wrapper.sh\"";
+ } else
+ {
+ unixcommand = "/usr/bin/env sh \"" + dirPosixEscape + "terminal.sh\"";
+ }
+
+ progress(userdata);
+
+ std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand);
+ std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title);
+ STARTUPINFOW startupInfo = {};
+ startupInfo.lpTitle = titleW.data();
+ startupInfo.cb = sizeof(startupInfo);
+ PROCESS_INFORMATION processInformation = {};
+
+ progress(userdata);
+
+ BOOL success = FALSE;
+ if(flags[ExecFlagSilent])
+ {
+ success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
+ } else
+ {
+ success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
+ }
+
+ progress(userdata);
+
+ createProcessSuccess = (success != FALSE);
+
+ progress(userdata);
+
+ if(success)
+ {
+
+ if(!flags[ExecFlagAsync])
+ {
+ // note: execution is not syncronous with all Wine versions,
+ // we additionally explicitly wait for "done" later
+ while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
+ { // wait
+ if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
+ {
+ CloseHandle(processInformation.hThread);
+ CloseHandle(processInformation.hProcess);
+ throw mpt::Wine::Exception("Canceled.");
+ }
+ }
+ }
+
+ progress(userdata);
+
+ CloseHandle(processInformation.hThread);
+ CloseHandle(processInformation.hProcess);
+
+ }
+
+ }
+
+ progress(userdata);
+
+ // Work around Wine being unable to execute PIE binaries on Debian 9.
+ // Luckily, /bin/bash is still non-PIE on Debian 9.
+
+ if(!createProcessSuccess)
+ {
+ if(flags[ExecFlagSilent])
+ {
+ unixcommand = "/bin/bash \"" + dirPosixEscape + "wrapper.sh\"";
+ } else
+ {
+ unixcommand = "/bin/bash \"" + dirPosixEscape + "terminal.sh\"";
+ }
+
+ progress(userdata);
+
+ std::wstring unixcommandW = mpt::ToWide(mpt::Charset::UTF8, unixcommand);
+ std::wstring titleW = mpt::ToWide(mpt::Charset::UTF8, title);
+ STARTUPINFOW startupInfo = {};
+ startupInfo.lpTitle = titleW.data();
+ startupInfo.cb = sizeof(startupInfo);
+ PROCESS_INFORMATION processInformation = {};
+
+ progress(userdata);
+
+ BOOL success = FALSE;
+ if(flags[ExecFlagSilent])
+ {
+ success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &startupInfo, &processInformation);
+ } else
+ {
+ success = CreateProcessW(NULL, unixcommandW.data(), NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupInfo, &processInformation);
+ }
+
+ progress(userdata);
+
+ createProcessSuccess = (success != FALSE);
+
+ progress(userdata);
+
+ if(success)
+ {
+ if(!flags[ExecFlagAsync])
+ {
+ // note: execution is not syncronous with all Wine versions,
+ // we additionally explicitly wait for "done" later
+ while(WaitForSingleObject(processInformation.hProcess, 0) == WAIT_TIMEOUT)
+ { // wait
+ if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
+ {
+ CloseHandle(processInformation.hThread);
+ CloseHandle(processInformation.hProcess);
+ throw mpt::Wine::Exception("Canceled.");
+ }
+ }
+ }
+
+ progress(userdata);
+
+ CloseHandle(processInformation.hThread);
+ CloseHandle(processInformation.hProcess);
+ }
+
+ }
+
+ progress(userdata);
+
+ if(!createProcessSuccess)
+ {
+ throw mpt::Wine::Exception("CreateProcess failed.");
+ }
+
+ progress(userdata);
+
+ if(flags[ExecFlagAsync])
+ {
+ ExecResult result;
+ result.exitcode = 0;
+ return result;
+ }
+
+ while(!(dirWindows + P_("done")).IsFile())
+ { // wait
+ if(progressCancel(userdata) != ExecuteProgressContinueWaiting)
+ {
+ throw mpt::Wine::Exception("Canceled.");
+ }
+ }
+
+ progress(userdata);
+
+ int exitCode = 0;
+ {
+ mpt::ifstream exitFile(dirWindows + P_("exit"), std::ios::binary);
+ if(!exitFile)
+ {
+ throw mpt::Wine::Exception("Script .exit file not found.");
+ }
+ std::string exitString;
+ exitFile >> exitString;
+ if(exitString.empty())
+ {
+ throw mpt::Wine::Exception("Script .exit file empty.");
+ }
+ exitCode = ConvertStrTo<int>(exitString);
+ }
+
+ progress(userdata);
+
+ std::string outputString;
+ if(!flags[ExecFlagInteractive])
+ {
+ mpt::ifstream outputFile(dirWindows + P_("out"), std::ios::binary);
+ if(outputFile)
+ {
+ outputFile.seekg(0, std::ios::end);
+ std::streampos outputFileSize = outputFile.tellg();
+ outputFile.seekg(0, std::ios::beg);
+ std::vector<char> outputFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(outputFileSize)));
+ outputFile.read(&outputFileBuf[0], outputFileBuf.size());
+ outputString = mpt::buffer_cast<std::string>(outputFileBuf);
+ }
+ }
+
+ progress(userdata);
+
+ std::string errorString;
+ if(flags[ExecFlagSplitOutput])
+ {
+ mpt::ifstream errorFile(dirWindows + P_("err"), std::ios::binary);
+ if(errorFile)
+ {
+ errorFile.seekg(0, std::ios::end);
+ std::streampos errorFileSize = errorFile.tellg();
+ errorFile.seekg(0, std::ios::beg);
+ std::vector<char> errorFileBuf(mpt::saturate_cast<std::size_t>(static_cast<std::streamoff>(errorFileSize)));
+ errorFile.read(&errorFileBuf[0], errorFileBuf.size());
+ errorString = mpt::buffer_cast<std::string>(errorFileBuf);
+ }
+ }
+
+ progress(userdata);
+
+ ExecResult result;
+ result.exitcode = exitCode;
+ result.output = outputString;
+ result.error = errorString;
+
+ std::deque<mpt::PathString> paths;
+ paths.push_back(dirWindows + P_("filetree"));
+ mpt::PathString basePath = (dirWindows + P_("filetree")).EnsureTrailingSlash();
+ while(!paths.empty())
+ {
+ mpt::PathString path = paths.front();
+ paths.pop_front();
+ 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;
+ filetree[filename.ToUTF8()] = std::vector<char>();
+ if(filename.IsDirectory())
+ {
+ paths.push_back(filename);
+ } else if(filename.IsFile())
+ {
+ try
+ {
+ mpt::LazyFileRef f(filename);
+ std::vector<char> buf = f;
+ mpt::PathString treeFilename = mpt::PathString::FromNative(filename.AsNative().substr(basePath.AsNative().length()));
+ result.filetree[treeFilename.ToUTF8()] = buf;
+ } catch (std::exception &)
+ {
+ // nothing?!
+ }
+ }
+ }
+ } while(FindNextFile(hFind, &wfd));
+ FindClose(hFind);
+ }
+ }
+
+ mpt::DeleteWholeDirectoryTree(dirWindows);
+
+ return result;
+
+}
+
+
+int Context::ExecutePosixShellCommand(std::string command, std::string & output, std::string & error)
+{
+ std::string script;
+ script += "#!/usr/bin/env sh" "\n";
+ script += "exec " + command + "\n";
+ mpt::Wine::ExecResult execResult = ExecutePosixShellScript
+ ( script
+ , mpt::Wine::ExecFlagSilent | mpt::Wine::ExecFlagSplitOutput, std::map<std::string, std::vector<char> >()
+ , std::string()
+ , nullptr
+ , nullptr
+ , nullptr
+ );
+ output = execResult.output;
+ error = execResult.error;
+ return execResult.exitcode;
+}
+
+
+std::string Context::GetPosixEnvVar(std::string var, std::string def)
+{
+ // We cannot use std::getenv here because Wine overrides SOME env vars,
+ // in particular, HOME is unset in the Wine environment.
+ // Instead, we just spawn a shell that will catch up a sane environment on
+ // its own.
+ std::string output;
+ std::string error;
+ int exitcode = ExecutePosixShellCommand(std::string() + "echo $" + var, output, error);
+ if(!error.empty())
+ {
+ throw mpt::Wine::Exception("Wine echo $var failed: " + error);
+ }
+ if(exitcode != 0)
+ {
+ throw mpt::Wine::Exception("Wine echo $var failed.");
+ }
+ std::string result = mpt::trim_right(output, std::string("\r\n"));
+ if(result.empty())
+ {
+ result = def;
+ }
+ return result;
+}
+
+
+} // namespace Wine
+} // namespace mpt
+
+
+#else // !MPT_OS_WINDOWS
+
+
+MPT_MSVC_WORKAROUND_LNK4221(mptWine)
+
+
+#endif // MPT_OS_WINDOWS
+
+
+OPENMPT_NAMESPACE_END
diff --git a/Src/external_dependencies/openmpt-trunk/misc/mptWine.h b/Src/external_dependencies/openmpt-trunk/misc/mptWine.h
new file mode 100644
index 00000000..c03623dd
--- /dev/null
+++ b/Src/external_dependencies/openmpt-trunk/misc/mptWine.h
@@ -0,0 +1,131 @@
+/*
+ * mptWine.h
+ * ---------
+ * Purpose: Wine stuff.
+ * Notes : (currently none)
+ * Authors: OpenMPT Devs
+ * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
+ */
+
+
+#pragma once
+
+#include "openmpt/all/BuildSettings.hpp"
+
+#include "mptOS.h"
+#include "mpt/library/library.hpp"
+#include "openmpt/base/FlagSet.hpp"
+
+#include <map>
+#include <optional>
+#include <string>
+#include <vector>
+
+
+OPENMPT_NAMESPACE_BEGIN
+
+
+#if MPT_OS_WINDOWS
+
+
+namespace mpt
+{
+
+namespace Wine
+{
+
+
+class Exception
+ : public std::runtime_error
+{
+public:
+ Exception(const std::string &text)
+ : std::runtime_error(text)
+ {
+ return;
+ }
+};
+
+
+typedef void (*ExecutePosixCommandProgress)(void *userdata);
+
+enum ExecuteProgressResult
+{
+ ExecuteProgressContinueWaiting = 0,
+ ExecuteProgressAsyncCancel = -1,
+};
+
+typedef ExecuteProgressResult (*ExecutePosixShellScriptProgress)(void *userdata);
+
+
+enum ExecFlags
+{
+ ExecFlagNone = 0,
+ ExecFlagSilent = 1<<0, // do not show a terminal window
+ ExecFlagInteractive = 1<<1, // allow interaction (prevents stdout and stderr capturing and implies !silent)
+ ExecFlagAsync = 1<<2, // do not wait for the script to finish
+ ExecFlagProgressWindow = 1<<3, // not implemented by mptOS
+ ExecFlagSplitOutput = 1<<4, // split stdout and stderr (implies silent)
+ ExecFlagsDefault = ExecFlagNone
+};
+MPT_DECLARE_ENUM(ExecFlags)
+
+struct ExecResult
+{
+ int exitcode;
+ std::string output;
+ std::string error;
+ std::map<std::string, std::vector<char> > filetree;
+ static ExecResult Error()
+ {
+ ExecResult result;
+ result.exitcode = -1;
+ return result;
+ }
+};
+
+
+class Context
+{
+protected:
+ mpt::OS::Wine::VersionContext m_VersionContext;
+ std::shared_ptr<std::optional<mpt::library>> m_Kernel32;
+private:
+ LPWSTR (*CDECL wine_get_dos_file_name)(LPCSTR str);
+ LPSTR (*CDECL wine_get_unix_file_name)(LPCWSTR str);
+protected:
+ std::string m_Uname_m;
+ std::string m_HOME;
+ std::string m_XDG_DATA_HOME;
+ std::string m_XDG_CACHE_HOME;
+ std::string m_XDG_CONFIG_HOME;
+public:
+ Context(mpt::OS::Wine::VersionContext versionContext);
+public:
+ std::string EscapePosixShell(std::string line);
+ std::string PathToPosix(mpt::PathString windowsPath);
+ mpt::PathString PathToWindows(std::string hostPath);
+ ExecResult ExecutePosixShellScript(std::string script, FlagSet<ExecFlags> flags, std::map<std::string, std::vector<char> > filetree, std::string title, ExecutePosixCommandProgress progress, ExecutePosixShellScriptProgress progressCancel, void *userdata);
+ int ExecutePosixShellCommand(std::string command, std::string & output, std::string & error);
+ std::string PathToPosixCanonical(mpt::PathString windowsPath);
+ std::string GetPosixEnvVar(std::string var, std::string def = std::string());
+public:
+ mpt::OS::Wine::VersionContext VersionContext() const { return m_VersionContext; }
+ std::shared_ptr<std::optional<mpt::library>> Kernel32() const { return m_Kernel32; }
+ std::string Uname_m() const { return m_Uname_m; }
+ std::string HOME() const { return m_HOME; }
+ std::string XDG_DATA_HOME() const { return m_XDG_DATA_HOME; }
+ std::string XDG_CACHE_HOME() const { return m_XDG_CACHE_HOME; }
+ std::string XDG_CONFIG_HOME() const { return m_XDG_CONFIG_HOME; }
+};
+
+
+} // namespace Wine
+
+} // namespace mpt
+
+
+#endif // MPT_OS_WINDOWS
+
+
+OPENMPT_NAMESPACE_END