summaryrefslogtreecommitdiffstats
path: root/src/common/impl/FFPlatform_windows.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/impl/FFPlatform_windows.c')
-rw-r--r--src/common/impl/FFPlatform_windows.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/src/common/impl/FFPlatform_windows.c b/src/common/impl/FFPlatform_windows.c
new file mode 100644
index 0000000..083d20a
--- /dev/null
+++ b/src/common/impl/FFPlatform_windows.c
@@ -0,0 +1,313 @@
+#include "FFPlatform_private.h"
+#include "common/io.h"
+#include "common/library.h"
+#include "common/strutil.h"
+#include "common/windows/unicode.h"
+#include "common/windows/registry.h"
+#include "common/windows/nt.h"
+
+#include <stdalign.h>
+#include <windows.h>
+#include <shlobj.h>
+#include <sddl.h>
+
+#define SECURITY_WIN32 1 // For secext.h
+#include <secext.h>
+
+static void getExePath(FFPlatform* platform) {
+ wchar_t exePathW[MAX_PATH];
+
+ FF_AUTO_CLOSE_FD HANDLE hPath = CreateFileW(
+ ffGetPeb()->ProcessParameters->ImagePathName.Buffer,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hPath != INVALID_HANDLE_VALUE) {
+ DWORD len = GetFinalPathNameByHandleW(hPath, exePathW, MAX_PATH, FILE_NAME_NORMALIZED);
+ if (len > 0 && len < MAX_PATH) {
+ ffStrbufSetNWS(&platform->exePath, len, exePathW);
+ if (ffStrbufStartsWithS(&platform->exePath, "\\\\?\\")) {
+ ffStrbufSubstrAfter(&platform->exePath, 3);
+ }
+ }
+ }
+
+ if (platform->exePath.length == 0) {
+ PCUNICODE_STRING imagePathName = &ffGetPeb()->ProcessParameters->ImagePathName;
+ ffStrbufSetNWS(&platform->exePath, imagePathName->Length / sizeof(wchar_t), imagePathName->Buffer);
+ }
+
+ ffStrbufReplaceAllC(&platform->exePath, '\\', '/');
+}
+
+static void getHomeDir(FFPlatform* platform) {
+ PWSTR pPath = NULL;
+ if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DEFAULT, NULL, &pPath))) {
+ ffStrbufSetWS(&platform->homeDir, pPath);
+ ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
+ ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
+ } else {
+ ffStrbufSetS(&platform->homeDir, getenv("USERPROFILE"));
+ ffStrbufReplaceAllC(&platform->homeDir, '\\', '/');
+ ffStrbufEnsureEndsWithC(&platform->homeDir, '/');
+ }
+ CoTaskMemFree(pPath);
+}
+
+static void getCacheDir(FFPlatform* platform) {
+ PWSTR pPath = NULL;
+ if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_LocalAppData, KF_FLAG_DEFAULT, NULL, &pPath))) {
+ ffStrbufSetWS(&platform->cacheDir, pPath);
+ ffStrbufReplaceAllC(&platform->cacheDir, '\\', '/');
+ ffStrbufEnsureEndsWithC(&platform->cacheDir, '/');
+ } else {
+ ffStrbufAppend(&platform->cacheDir, &platform->homeDir);
+ ffStrbufAppendS(&platform->cacheDir, "AppData/Local/");
+ }
+ CoTaskMemFree(pPath);
+}
+
+static void platformPathAddKnownFolder(FFlist* dirs, REFKNOWNFOLDERID folderId) {
+ PWSTR pPath = NULL;
+ if (SUCCEEDED(SHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT, NULL, &pPath))) {
+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateWS(pPath);
+ CoTaskMemFree(pPath);
+ ffStrbufReplaceAllC(&buffer, '\\', '/');
+ ffStrbufEnsureEndsWithC(&buffer, '/');
+ if (!FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
+ ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
+ }
+ }
+}
+
+static void platformPathAddEnvSuffix(FFlist* dirs, const char* env, const char* suffix) {
+ const char* value = getenv(env);
+ if (!ffStrSet(value)) {
+ return;
+ }
+
+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreateA(64);
+ ffStrbufAppendS(&buffer, value);
+ ffStrbufReplaceAllC(&buffer, '\\', '/');
+ ffStrbufEnsureEndsWithC(&buffer, '/');
+ if (suffix) {
+ ffStrbufAppendS(&buffer, suffix);
+ ffStrbufEnsureEndsWithC(&buffer, '/');
+ }
+
+ if (ffPathExists(buffer.chars, FF_PATHTYPE_DIRECTORY) && !FF_LIST_CONTAINS(*dirs, &buffer, ffStrbufEqual)) {
+ ffStrbufInitMove(FF_LIST_ADD(FFstrbuf, *dirs), &buffer);
+ }
+}
+
+static void getConfigDirs(FFPlatform* platform) {
+ if (getenv("MSYSTEM")) {
+ // We are in MSYS2 / Git Bash
+ platformPathAddEnvSuffix(&platform->configDirs, "HOME", ".config/");
+ platformPathAddEnvSuffix(&platform->configDirs, "HOME", NULL);
+ platformPathAddEnvSuffix(&platform->configDirs, "MINGW_PREFIX", "etc");
+ }
+
+ ffPlatformPathAddHome(&platform->configDirs, platform, ".config/");
+ platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_ProgramData);
+ platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_RoamingAppData);
+ platformPathAddKnownFolder(&platform->configDirs, &FOLDERID_LocalAppData);
+ ffPlatformPathAddHome(&platform->configDirs, platform, "");
+}
+
+static void getDataDirs(FFPlatform* platform) {
+ if (getenv("MSYSTEM") && getenv("HOME")) {
+ // We are in MSYS2 / Git Bash
+ platformPathAddEnvSuffix(&platform->dataDirs, "HOME", ".local/share/");
+ platformPathAddEnvSuffix(&platform->dataDirs, "HOME", NULL);
+ platformPathAddEnvSuffix(&platform->dataDirs, "MINGW_PREFIX", "share");
+ }
+ ffPlatformPathAddHome(&platform->dataDirs, platform, ".local/share/");
+ platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_ProgramData);
+ platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_RoamingAppData);
+ platformPathAddKnownFolder(&platform->dataDirs, &FOLDERID_LocalAppData);
+ ffPlatformPathAddHome(&platform->dataDirs, platform, "");
+}
+
+static void getUserName(FFPlatform* platform) {
+ wchar_t buffer[256];
+ DWORD size = ARRAY_SIZE(buffer);
+ if (GetUserNameExW(NameDisplay, buffer, &size)) {
+ ffStrbufSetWS(&platform->fullUserName, buffer);
+ }
+
+ NTSYSAPI NTSTATUS NTAPI LsaGetUserName(
+ _Outptr_ PLSA_UNICODE_STRING * UserName,
+ _Outptr_opt_ PLSA_UNICODE_STRING * DomainName);
+ PLSA_UNICODE_STRING userName = NULL;
+ if (NT_SUCCESS(LsaGetUserName(&userName, NULL))) {
+ ffStrbufSetNWS(&platform->userName, userName->Length / sizeof(wchar_t), userName->Buffer);
+ RtlFreeUnicodeString(userName); // Required. userName.Buffer is allocated separately
+ LsaFreeMemory(userName);
+ } else {
+ ffStrbufSetS(&platform->userName, getenv("USERNAME"));
+ }
+
+ alignas(TOKEN_USER) char buf[SECURITY_MAX_SID_SIZE + sizeof(TOKEN_USER)];
+ if (NT_SUCCESS(NtQueryInformationToken(NtCurrentProcessToken(), TokenUser, buf, sizeof(buf), &size))) {
+ TOKEN_USER* tokenUser = (TOKEN_USER*) buf;
+ UNICODE_STRING sidString = { .Buffer = buffer, .Length = 0, .MaximumLength = sizeof(buffer) };
+ if (NT_SUCCESS(RtlConvertSidToUnicodeString(&sidString, tokenUser->User.Sid, FALSE))) {
+ ffStrbufSetNWS(&platform->sid, sidString.Length / sizeof(wchar_t), sidString.Buffer);
+ }
+ }
+}
+
+static void getHostName(FFPlatform* platform) {
+ wchar_t buffer[256];
+ DWORD len = ARRAY_SIZE(buffer);
+ if (GetComputerNameExW(ComputerNameDnsHostname, buffer, &len) && len > 0) {
+ ffStrbufSetNWS(&platform->hostName, len, buffer);
+ } else {
+ len = ARRAY_SIZE(buffer);
+ if (GetComputerNameExW(ComputerNameNetBIOS, buffer, &len) && len > 0) {
+ ffStrbufSetNWS(&platform->hostName, len, buffer);
+ }
+ }
+}
+
+static void getUserShell(FFPlatform* platform) {
+ // Works in MSYS2
+ const char* userShell = getenv("SHELL");
+ if (userShell) {
+ ffStrbufAppendS(&platform->userShell, userShell);
+ ffStrbufReplaceAllC(&platform->userShell, '\\', '/');
+ }
+}
+
+static const char* detectWine(void) {
+ const char* __cdecl wine_get_version(void);
+ void* hntdll = ffLibraryGetModule(L"ntdll.dll");
+ if (!hntdll) {
+ return NULL;
+ }
+ FF_LIBRARY_LOAD_SYMBOL_LAZY(hntdll, wine_get_version);
+ if (!ffwine_get_version) {
+ return NULL;
+ }
+ return ffwine_get_version();
+}
+
+static void getSystemReleaseAndVersion(FFPlatformSysinfo* info) {
+ FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
+ if (!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL)) {
+ return;
+ }
+
+ uint32_t ubr = 0;
+ ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
+ FF_ARG(ubr, L"UBR"),
+ FF_ARG(info->version, L"BuildLabEx"),
+ },
+ NULL);
+
+ PPEB_FULL peb = ffGetPeb();
+
+ ffStrbufSetF(&info->release,
+ "%u.%u.%u.%u",
+ (unsigned) peb->OSMajorVersion,
+ (unsigned) peb->OSMinorVersion,
+ (unsigned) peb->OSBuildNumber,
+ (unsigned) ubr);
+
+ const char* wineVersion = detectWine();
+ if (wineVersion) {
+ ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
+ } else {
+ ffStrbufSetStatic(&info->name, "WIN32_NT");
+ }
+}
+
+static void getSystemPageSize(FFPlatformSysinfo* info) {
+ SYSTEM_BASIC_INFORMATION sbi;
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(sbi), NULL))) {
+ info->pageSize = sbi.PhysicalPageSize;
+ } else {
+ info->pageSize = 4096;
+ }
+}
+
+static void getSystemArchitecture(FFPlatformSysinfo* info) {
+ SYSTEM_PROCESSOR_INFORMATION spi;
+ if (NT_SUCCESS(NtQuerySystemInformation(SystemProcessorInformation, &spi, sizeof(spi), NULL))) {
+ switch (spi.ProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ ffStrbufSetStatic(&info->architecture, "x86_64");
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ ffStrbufSetStatic(&info->architecture, "ia64");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ switch (spi.ProcessorLevel) {
+ case 4:
+ ffStrbufSetStatic(&info->architecture, "i486");
+ break;
+ case 5:
+ ffStrbufSetStatic(&info->architecture, "i586");
+ break;
+ case 6:
+ ffStrbufSetStatic(&info->architecture, "i686");
+ break;
+ default:
+ ffStrbufSetStatic(&info->architecture, "i386");
+ break;
+ }
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM64:
+ ffStrbufSetStatic(&info->architecture, "aarch64");
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ ffStrbufSetStatic(&info->architecture, "arm");
+ break;
+ case PROCESSOR_ARCHITECTURE_PPC:
+ ffStrbufSetStatic(&info->architecture, "ppc");
+ break;
+ case PROCESSOR_ARCHITECTURE_MIPS:
+ ffStrbufSetStatic(&info->architecture, "mips");
+ break;
+ case PROCESSOR_ARCHITECTURE_ALPHA:
+ ffStrbufSetStatic(&info->architecture, "alpha");
+ break;
+ case PROCESSOR_ARCHITECTURE_ALPHA64:
+ ffStrbufSetStatic(&info->architecture, "alpha64");
+ break;
+ case PROCESSOR_ARCHITECTURE_UNKNOWN:
+ default:
+ ffStrbufSetStatic(&info->architecture, "unknown");
+ break;
+ }
+ }
+}
+
+static void getCwd(FFPlatform* platform) {
+ PCURDIR cwd = &ffGetPeb()->ProcessParameters->CurrentDirectory;
+ ffStrbufSetNWS(&platform->cwd, cwd->DosPath.Length / sizeof(WCHAR), cwd->DosPath.Buffer);
+ ffStrbufReplaceAllC(&platform->cwd, '\\', '/');
+ ffStrbufEnsureEndsWithC(&platform->cwd, '/');
+}
+
+void ffPlatformInitImpl(FFPlatform* platform) {
+ platform->pid = (uint32_t) (uintptr_t) ffGetTeb()->ClientId.UniqueProcess;
+ getExePath(platform);
+ getCwd(platform);
+ getHomeDir(platform);
+ getCacheDir(platform);
+ getConfigDirs(platform);
+ getDataDirs(platform);
+
+ getUserName(platform);
+ getHostName(platform);
+ getUserShell(platform);
+
+ getSystemReleaseAndVersion(&platform->sysinfo);
+ getSystemArchitecture(&platform->sysinfo);
+ getSystemPageSize(&platform->sysinfo);
+}