#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 #include #include #include #define SECURITY_WIN32 1 // For secext.h #include 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); }