diff options
| author | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
|---|---|---|
| committer | sumuel <samuel@yakubos.org> | 2026-08-17 20:44:55 +0000 |
| commit | 76424950e373d3b04ac3dd13019151bfba3e8423 (patch) | |
| tree | 4c3cfdbda039e592b9186be3e28f8d7cfd439e8a /src/common/impl/FFPlatform_windows.c | |
Add the files
Diffstat (limited to 'src/common/impl/FFPlatform_windows.c')
| -rw-r--r-- | src/common/impl/FFPlatform_windows.c | 313 |
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); +} |