diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/misc | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/misc')
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 |