Initial community commit

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

View file

@ -0,0 +1,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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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