platform-windows.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428
  1. /*
  2. * Copyright (c) 2023 Lain Bailey <[email protected]>
  3. *
  4. * Permission to use, copy, modify, and distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  9. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include <windows.h>
  17. #include <mmsystem.h>
  18. #include <shellapi.h>
  19. #include <shlobj.h>
  20. #include <intrin.h>
  21. #include <psapi.h>
  22. #include <math.h>
  23. #include <rpc.h>
  24. #include "base.h"
  25. #include "platform.h"
  26. #include "darray.h"
  27. #include "dstr.h"
  28. #include "obsconfig.h"
  29. #include "util_uint64.h"
  30. #include "windows/win-registry.h"
  31. #include "windows/win-version.h"
  32. #include "../../deps/w32-pthreads/pthread.h"
  33. #define MAX_SZ_LEN 256
  34. static bool have_clockfreq = false;
  35. static LARGE_INTEGER clock_freq;
  36. static uint32_t winver = 0;
  37. static char win_release_id[MAX_SZ_LEN] = "unavailable";
  38. static inline uint64_t get_clockfreq(void)
  39. {
  40. if (!have_clockfreq) {
  41. QueryPerformanceFrequency(&clock_freq);
  42. have_clockfreq = true;
  43. }
  44. return clock_freq.QuadPart;
  45. }
  46. static inline uint32_t get_winver(void)
  47. {
  48. if (!winver) {
  49. struct win_version_info ver;
  50. get_win_ver(&ver);
  51. winver = (ver.major << 8) | ver.minor;
  52. }
  53. return winver;
  54. }
  55. void *os_dlopen(const char *path)
  56. {
  57. struct dstr dll_name;
  58. wchar_t *wpath;
  59. wchar_t *wpath_slash;
  60. HMODULE h_library = NULL;
  61. if (!path)
  62. return NULL;
  63. dstr_init_copy(&dll_name, path);
  64. dstr_replace(&dll_name, "\\", "/");
  65. if (!dstr_find(&dll_name, ".dll"))
  66. dstr_cat(&dll_name, ".dll");
  67. os_utf8_to_wcs_ptr(dll_name.array, 0, &wpath);
  68. dstr_free(&dll_name);
  69. /* to make module dependency issues easier to deal with, allow
  70. * dynamically loaded libraries on windows to search for dependent
  71. * libraries that are within the library's own directory */
  72. wpath_slash = wcsrchr(wpath, L'/');
  73. if (wpath_slash) {
  74. *wpath_slash = 0;
  75. SetDllDirectoryW(wpath);
  76. *wpath_slash = L'/';
  77. }
  78. h_library = LoadLibraryW(wpath);
  79. bfree(wpath);
  80. if (wpath_slash)
  81. SetDllDirectoryW(NULL);
  82. if (!h_library) {
  83. DWORD error = GetLastError();
  84. /* don't print error for libraries that aren't meant to be
  85. * dynamically linked */
  86. if (error == ERROR_PROC_NOT_FOUND)
  87. return NULL;
  88. char *message = NULL;
  89. FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
  90. FORMAT_MESSAGE_ALLOCATE_BUFFER,
  91. NULL, error, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), (LPSTR)&message, 0, NULL);
  92. blog(LOG_INFO, "LoadLibrary failed for '%s': %s (%lu)", path, message, error);
  93. if (message)
  94. LocalFree(message);
  95. }
  96. return h_library;
  97. }
  98. void *os_dlsym(void *module, const char *func)
  99. {
  100. void *handle;
  101. handle = (void *)GetProcAddress(module, func);
  102. return handle;
  103. }
  104. void os_dlclose(void *module)
  105. {
  106. FreeLibrary(module);
  107. }
  108. static bool has_qt5_import(VOID *base, PIMAGE_NT_HEADERS nt_headers)
  109. {
  110. __try {
  111. PIMAGE_DATA_DIRECTORY data_dir;
  112. data_dir = &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
  113. if (data_dir->Size == 0)
  114. return false;
  115. PIMAGE_SECTION_HEADER section, last_section;
  116. section = IMAGE_FIRST_SECTION(nt_headers);
  117. last_section = section;
  118. /* find the section that contains the export directory */
  119. int i;
  120. for (i = 0; i < nt_headers->FileHeader.NumberOfSections; i++) {
  121. if (section->VirtualAddress <= data_dir->VirtualAddress) {
  122. last_section = section;
  123. section++;
  124. continue;
  125. } else {
  126. break;
  127. }
  128. }
  129. /* double check in case we exited early */
  130. if (last_section->VirtualAddress > data_dir->VirtualAddress ||
  131. section->VirtualAddress <= data_dir->VirtualAddress)
  132. return false;
  133. section = last_section;
  134. /* get a pointer to the import directory */
  135. PIMAGE_IMPORT_DESCRIPTOR import;
  136. import = (PIMAGE_IMPORT_DESCRIPTOR)((byte *)base + data_dir->VirtualAddress - section->VirtualAddress +
  137. section->PointerToRawData);
  138. while (import->Name != 0) {
  139. char *name = (char *)((byte *)base + import->Name - section->VirtualAddress +
  140. section->PointerToRawData);
  141. /* qt5? bingo, reject this library */
  142. if (astrcmpi_n(name, "qt5", 3) == 0) {
  143. return true;
  144. }
  145. import++;
  146. }
  147. } __except (EXCEPTION_EXECUTE_HANDLER) {
  148. /* we failed somehow, for compatibility assume no qt5 import */
  149. return false;
  150. }
  151. return false;
  152. }
  153. static bool has_obs_export(VOID *base, PIMAGE_NT_HEADERS nt_headers)
  154. {
  155. __try {
  156. PIMAGE_DATA_DIRECTORY data_dir;
  157. data_dir = &nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
  158. if (data_dir->Size == 0)
  159. return false;
  160. PIMAGE_SECTION_HEADER section, last_section;
  161. section = IMAGE_FIRST_SECTION(nt_headers);
  162. last_section = section;
  163. /* find the section that contains the export directory */
  164. int i;
  165. for (i = 0; i < nt_headers->FileHeader.NumberOfSections; i++) {
  166. if (section->VirtualAddress <= data_dir->VirtualAddress) {
  167. last_section = section;
  168. section++;
  169. continue;
  170. } else {
  171. break;
  172. }
  173. }
  174. /* double check in case we exited early */
  175. if (last_section->VirtualAddress > data_dir->VirtualAddress ||
  176. section->VirtualAddress <= data_dir->VirtualAddress)
  177. return false;
  178. section = last_section;
  179. /* get a pointer to the export directory */
  180. PIMAGE_EXPORT_DIRECTORY export;
  181. export = (PIMAGE_EXPORT_DIRECTORY)((byte *)base + data_dir->VirtualAddress - section->VirtualAddress +
  182. section->PointerToRawData);
  183. if (export->NumberOfNames == 0)
  184. return false;
  185. /* get a pointer to the export directory names */
  186. DWORD *names_ptr;
  187. names_ptr = (DWORD *)((byte *)base + export->AddressOfNames - section->VirtualAddress +
  188. section->PointerToRawData);
  189. /* iterate through each name and see if its an obs plugin */
  190. CHAR *name;
  191. size_t j;
  192. for (j = 0; j < export->NumberOfNames; j++) {
  193. name = (CHAR *)base + names_ptr[j] - section->VirtualAddress + section->PointerToRawData;
  194. if (!strcmp(name, "obs_module_load")) {
  195. return true;
  196. }
  197. }
  198. } __except (EXCEPTION_EXECUTE_HANDLER) {
  199. /* we failed somehow, for compatibility let's assume it
  200. * was a valid plugin and let the loader deal with it */
  201. return true;
  202. }
  203. return false;
  204. }
  205. void get_plugin_info(const char *path, bool *is_obs_plugin, bool *can_load)
  206. {
  207. struct dstr dll_name;
  208. wchar_t *wpath;
  209. HANDLE hFile = INVALID_HANDLE_VALUE;
  210. HANDLE hFileMapping = NULL;
  211. VOID *base = NULL;
  212. PIMAGE_DOS_HEADER dos_header;
  213. PIMAGE_NT_HEADERS nt_headers;
  214. *is_obs_plugin = false;
  215. *can_load = false;
  216. if (!path)
  217. return;
  218. dstr_init_copy(&dll_name, path);
  219. dstr_replace(&dll_name, "\\", "/");
  220. if (!dstr_find(&dll_name, ".dll"))
  221. dstr_cat(&dll_name, ".dll");
  222. os_utf8_to_wcs_ptr(dll_name.array, 0, &wpath);
  223. dstr_free(&dll_name);
  224. hFile = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  225. bfree(wpath);
  226. if (hFile == INVALID_HANDLE_VALUE)
  227. goto cleanup;
  228. hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
  229. if (hFileMapping == NULL)
  230. goto cleanup;
  231. base = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, 0);
  232. if (!base)
  233. goto cleanup;
  234. /* all mapped file i/o must be prepared to handle exceptions */
  235. __try {
  236. dos_header = (PIMAGE_DOS_HEADER)base;
  237. if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
  238. goto cleanup;
  239. nt_headers = (PIMAGE_NT_HEADERS)((byte *)dos_header + dos_header->e_lfanew);
  240. if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
  241. goto cleanup;
  242. *is_obs_plugin = has_obs_export(base, nt_headers);
  243. if (*is_obs_plugin) {
  244. *can_load = !has_qt5_import(base, nt_headers);
  245. }
  246. } __except (EXCEPTION_EXECUTE_HANDLER) {
  247. /* we failed somehow, for compatibility let's assume it
  248. * was a valid plugin and let the loader deal with it */
  249. *is_obs_plugin = true;
  250. *can_load = true;
  251. goto cleanup;
  252. }
  253. cleanup:
  254. if (base)
  255. UnmapViewOfFile(base);
  256. if (hFileMapping != NULL)
  257. CloseHandle(hFileMapping);
  258. if (hFile != INVALID_HANDLE_VALUE)
  259. CloseHandle(hFile);
  260. }
  261. bool os_is_obs_plugin(const char *path)
  262. {
  263. bool is_obs_plugin;
  264. bool can_load;
  265. get_plugin_info(path, &is_obs_plugin, &can_load);
  266. return is_obs_plugin && can_load;
  267. }
  268. union time_data {
  269. FILETIME ft;
  270. unsigned long long val;
  271. };
  272. struct os_cpu_usage_info {
  273. union time_data last_time, last_sys_time, last_user_time;
  274. DWORD core_count;
  275. };
  276. os_cpu_usage_info_t *os_cpu_usage_info_start(void)
  277. {
  278. struct os_cpu_usage_info *info = bzalloc(sizeof(*info));
  279. SYSTEM_INFO si;
  280. FILETIME dummy;
  281. GetSystemInfo(&si);
  282. GetSystemTimeAsFileTime(&info->last_time.ft);
  283. GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, &info->last_sys_time.ft, &info->last_user_time.ft);
  284. info->core_count = si.dwNumberOfProcessors;
  285. return info;
  286. }
  287. double os_cpu_usage_info_query(os_cpu_usage_info_t *info)
  288. {
  289. union time_data cur_time, cur_sys_time, cur_user_time;
  290. FILETIME dummy;
  291. double percent;
  292. if (!info)
  293. return 0.0;
  294. GetSystemTimeAsFileTime(&cur_time.ft);
  295. GetProcessTimes(GetCurrentProcess(), &dummy, &dummy, &cur_sys_time.ft, &cur_user_time.ft);
  296. percent = (double)(cur_sys_time.val - info->last_sys_time.val + (cur_user_time.val - info->last_user_time.val));
  297. percent /= (double)(cur_time.val - info->last_time.val);
  298. percent /= (double)info->core_count;
  299. info->last_time.val = cur_time.val;
  300. info->last_sys_time.val = cur_sys_time.val;
  301. info->last_user_time.val = cur_user_time.val;
  302. return percent * 100.0;
  303. }
  304. void os_cpu_usage_info_destroy(os_cpu_usage_info_t *info)
  305. {
  306. if (info)
  307. bfree(info);
  308. }
  309. bool os_sleepto_ns(uint64_t time_target)
  310. {
  311. const uint64_t freq = get_clockfreq();
  312. const LONGLONG count_target = util_mul_div64(time_target, freq, 1000000000);
  313. LARGE_INTEGER count;
  314. QueryPerformanceCounter(&count);
  315. const bool stall = count.QuadPart < count_target;
  316. if (stall) {
  317. const DWORD milliseconds = (DWORD)(((count_target - count.QuadPart) * 1000.0) / freq);
  318. if (milliseconds > 1)
  319. Sleep(milliseconds - 1);
  320. for (;;) {
  321. QueryPerformanceCounter(&count);
  322. if (count.QuadPart >= count_target)
  323. break;
  324. YieldProcessor();
  325. }
  326. }
  327. return stall;
  328. }
  329. bool os_sleepto_ns_fast(uint64_t time_target)
  330. {
  331. uint64_t current = os_gettime_ns();
  332. if (time_target < current)
  333. return false;
  334. do {
  335. uint64_t remain_ms = (time_target - current) / 1000000;
  336. if (!remain_ms)
  337. remain_ms = 1;
  338. Sleep((DWORD)remain_ms);
  339. current = os_gettime_ns();
  340. } while (time_target > current);
  341. return true;
  342. }
  343. void os_sleep_ms(uint32_t duration)
  344. {
  345. /* windows 8+ appears to have decreased sleep precision */
  346. if (get_winver() >= 0x0602 && duration > 0)
  347. duration--;
  348. Sleep(duration);
  349. }
  350. uint64_t os_gettime_ns(void)
  351. {
  352. LARGE_INTEGER current_time;
  353. QueryPerformanceCounter(&current_time);
  354. return util_mul_div64(current_time.QuadPart, 1000000000, get_clockfreq());
  355. }
  356. /* returns [folder]\[name] on windows */
  357. static int os_get_path_internal(char *dst, size_t size, const char *name, int folder)
  358. {
  359. wchar_t path_utf16[MAX_PATH];
  360. SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path_utf16);
  361. if (os_wcs_to_utf8(path_utf16, 0, dst, size) != 0) {
  362. if (!name || !*name) {
  363. return (int)strlen(dst);
  364. }
  365. if (strcat_s(dst, size, "\\") == 0) {
  366. if (strcat_s(dst, size, name) == 0) {
  367. return (int)strlen(dst);
  368. }
  369. }
  370. }
  371. return -1;
  372. }
  373. static char *os_get_path_ptr_internal(const char *name, int folder)
  374. {
  375. char *ptr;
  376. wchar_t path_utf16[MAX_PATH];
  377. struct dstr path;
  378. SHGetFolderPathW(NULL, folder, NULL, SHGFP_TYPE_CURRENT, path_utf16);
  379. os_wcs_to_utf8_ptr(path_utf16, 0, &ptr);
  380. dstr_init_move_array(&path, ptr);
  381. dstr_cat(&path, "\\");
  382. dstr_cat(&path, name);
  383. return path.array;
  384. }
  385. int os_get_config_path(char *dst, size_t size, const char *name)
  386. {
  387. return os_get_path_internal(dst, size, name, CSIDL_APPDATA);
  388. }
  389. char *os_get_config_path_ptr(const char *name)
  390. {
  391. return os_get_path_ptr_internal(name, CSIDL_APPDATA);
  392. }
  393. int os_get_program_data_path(char *dst, size_t size, const char *name)
  394. {
  395. return os_get_path_internal(dst, size, name, CSIDL_COMMON_APPDATA);
  396. }
  397. char *os_get_program_data_path_ptr(const char *name)
  398. {
  399. return os_get_path_ptr_internal(name, CSIDL_COMMON_APPDATA);
  400. }
  401. char *os_get_executable_path_ptr(const char *name)
  402. {
  403. char *ptr;
  404. char *slash;
  405. wchar_t path_utf16[MAX_PATH];
  406. struct dstr path;
  407. GetModuleFileNameW(NULL, path_utf16, MAX_PATH);
  408. os_wcs_to_utf8_ptr(path_utf16, 0, &ptr);
  409. dstr_init_move_array(&path, ptr);
  410. dstr_replace(&path, "\\", "/");
  411. slash = strrchr(path.array, '/');
  412. if (slash) {
  413. size_t len = slash - path.array + 1;
  414. dstr_resize(&path, len);
  415. }
  416. if (name && *name) {
  417. dstr_cat(&path, name);
  418. }
  419. return path.array;
  420. }
  421. bool os_file_exists(const char *path)
  422. {
  423. WIN32_FIND_DATAW wfd;
  424. HANDLE hFind;
  425. wchar_t *path_utf16;
  426. if (!os_utf8_to_wcs_ptr(path, 0, &path_utf16))
  427. return false;
  428. hFind = FindFirstFileW(path_utf16, &wfd);
  429. if (hFind != INVALID_HANDLE_VALUE)
  430. FindClose(hFind);
  431. bfree(path_utf16);
  432. return hFind != INVALID_HANDLE_VALUE;
  433. }
  434. size_t os_get_abs_path(const char *path, char *abspath, size_t size)
  435. {
  436. wchar_t wpath[MAX_PATH];
  437. wchar_t wabspath[MAX_PATH];
  438. size_t out_len = 0;
  439. size_t len;
  440. if (!abspath)
  441. return 0;
  442. len = os_utf8_to_wcs(path, 0, wpath, MAX_PATH);
  443. if (!len)
  444. return 0;
  445. if (_wfullpath(wabspath, wpath, MAX_PATH) != NULL)
  446. out_len = os_wcs_to_utf8(wabspath, 0, abspath, size);
  447. return out_len;
  448. }
  449. char *os_get_abs_path_ptr(const char *path)
  450. {
  451. char *ptr = bmalloc(MAX_PATH);
  452. if (!os_get_abs_path(path, ptr, MAX_PATH)) {
  453. bfree(ptr);
  454. ptr = NULL;
  455. }
  456. return ptr;
  457. }
  458. struct os_dir {
  459. HANDLE handle;
  460. WIN32_FIND_DATA wfd;
  461. bool first;
  462. struct os_dirent out;
  463. };
  464. os_dir_t *os_opendir(const char *path)
  465. {
  466. struct dstr path_str = {0};
  467. struct os_dir *dir = NULL;
  468. WIN32_FIND_DATA wfd;
  469. HANDLE handle;
  470. wchar_t *w_path;
  471. dstr_copy(&path_str, path);
  472. dstr_cat(&path_str, "/*.*");
  473. if (os_utf8_to_wcs_ptr(path_str.array, path_str.len, &w_path) > 0) {
  474. handle = FindFirstFileW(w_path, &wfd);
  475. if (handle != INVALID_HANDLE_VALUE) {
  476. dir = bzalloc(sizeof(struct os_dir));
  477. dir->handle = handle;
  478. dir->first = true;
  479. dir->wfd = wfd;
  480. }
  481. bfree(w_path);
  482. }
  483. dstr_free(&path_str);
  484. return dir;
  485. }
  486. static inline bool is_dir(WIN32_FIND_DATA *wfd)
  487. {
  488. return !!(wfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  489. }
  490. struct os_dirent *os_readdir(os_dir_t *dir)
  491. {
  492. if (!dir)
  493. return NULL;
  494. if (dir->first) {
  495. dir->first = false;
  496. } else {
  497. if (!FindNextFileW(dir->handle, &dir->wfd))
  498. return NULL;
  499. }
  500. os_wcs_to_utf8(dir->wfd.cFileName, 0, dir->out.d_name, sizeof(dir->out.d_name));
  501. dir->out.directory = is_dir(&dir->wfd);
  502. return &dir->out;
  503. }
  504. void os_closedir(os_dir_t *dir)
  505. {
  506. if (dir) {
  507. FindClose(dir->handle);
  508. bfree(dir);
  509. }
  510. }
  511. int64_t os_get_free_space(const char *path)
  512. {
  513. ULARGE_INTEGER remainingSpace;
  514. char abs_path[512];
  515. wchar_t w_abs_path[512];
  516. if (os_get_abs_path(path, abs_path, 512) > 0) {
  517. if (os_utf8_to_wcs(abs_path, 0, w_abs_path, 512) > 0) {
  518. BOOL success = GetDiskFreeSpaceExW(w_abs_path, (PULARGE_INTEGER)&remainingSpace, NULL, NULL);
  519. if (success)
  520. return (int64_t)remainingSpace.QuadPart;
  521. }
  522. }
  523. return -1;
  524. }
  525. static void make_globent(struct os_globent *ent, WIN32_FIND_DATA *wfd, const char *pattern)
  526. {
  527. struct dstr name = {0};
  528. struct dstr path = {0};
  529. char *slash;
  530. dstr_from_wcs(&name, wfd->cFileName);
  531. dstr_copy(&path, pattern);
  532. if (path.array) {
  533. slash = strrchr(path.array, '/');
  534. if (slash)
  535. dstr_resize(&path, slash + 1 - path.array);
  536. else
  537. dstr_free(&path);
  538. }
  539. dstr_cat_dstr(&path, &name);
  540. ent->path = path.array;
  541. ent->directory = is_dir(wfd);
  542. dstr_free(&name);
  543. }
  544. int os_glob(const char *pattern, int flags, os_glob_t **pglob)
  545. {
  546. DARRAY(struct os_globent) files;
  547. HANDLE handle;
  548. WIN32_FIND_DATA wfd;
  549. int ret = -1;
  550. wchar_t *w_path;
  551. da_init(files);
  552. if (os_utf8_to_wcs_ptr(pattern, 0, &w_path) > 0) {
  553. handle = FindFirstFileW(w_path, &wfd);
  554. if (handle != INVALID_HANDLE_VALUE) {
  555. do {
  556. struct os_globent ent = {0};
  557. make_globent(&ent, &wfd, pattern);
  558. if (ent.path)
  559. da_push_back(files, &ent);
  560. } while (FindNextFile(handle, &wfd));
  561. FindClose(handle);
  562. *pglob = bmalloc(sizeof(**pglob));
  563. (*pglob)->gl_pathc = files.num;
  564. (*pglob)->gl_pathv = files.array;
  565. ret = 0;
  566. }
  567. bfree(w_path);
  568. }
  569. if (ret != 0)
  570. *pglob = NULL;
  571. UNUSED_PARAMETER(flags);
  572. return ret;
  573. }
  574. void os_globfree(os_glob_t *pglob)
  575. {
  576. if (pglob) {
  577. for (size_t i = 0; i < pglob->gl_pathc; i++)
  578. bfree(pglob->gl_pathv[i].path);
  579. bfree(pglob->gl_pathv);
  580. bfree(pglob);
  581. }
  582. }
  583. int os_unlink(const char *path)
  584. {
  585. wchar_t *w_path;
  586. bool success;
  587. os_utf8_to_wcs_ptr(path, 0, &w_path);
  588. if (!w_path)
  589. return -1;
  590. success = !!DeleteFileW(w_path);
  591. bfree(w_path);
  592. return success ? 0 : -1;
  593. }
  594. int os_rmdir(const char *path)
  595. {
  596. wchar_t *w_path;
  597. bool success;
  598. os_utf8_to_wcs_ptr(path, 0, &w_path);
  599. if (!w_path)
  600. return -1;
  601. success = !!RemoveDirectoryW(w_path);
  602. bfree(w_path);
  603. return success ? 0 : -1;
  604. }
  605. int os_mkdir(const char *path)
  606. {
  607. wchar_t *path_utf16;
  608. BOOL success;
  609. if (!os_utf8_to_wcs_ptr(path, 0, &path_utf16))
  610. return MKDIR_ERROR;
  611. success = CreateDirectory(path_utf16, NULL);
  612. bfree(path_utf16);
  613. if (!success)
  614. return (GetLastError() == ERROR_ALREADY_EXISTS) ? MKDIR_EXISTS : MKDIR_ERROR;
  615. return MKDIR_SUCCESS;
  616. }
  617. int os_rename(const char *old_path, const char *new_path)
  618. {
  619. wchar_t *old_path_utf16 = NULL;
  620. wchar_t *new_path_utf16 = NULL;
  621. int code = -1;
  622. if (!os_utf8_to_wcs_ptr(old_path, 0, &old_path_utf16)) {
  623. return -1;
  624. }
  625. if (!os_utf8_to_wcs_ptr(new_path, 0, &new_path_utf16)) {
  626. goto error;
  627. }
  628. code = MoveFileExW(old_path_utf16, new_path_utf16, MOVEFILE_REPLACE_EXISTING) ? 0 : -1;
  629. error:
  630. bfree(old_path_utf16);
  631. bfree(new_path_utf16);
  632. return code;
  633. }
  634. int os_safe_replace(const char *target, const char *from, const char *backup)
  635. {
  636. wchar_t *wtarget = NULL;
  637. wchar_t *wfrom = NULL;
  638. wchar_t *wbackup = NULL;
  639. int code = -1;
  640. if (!target || !from)
  641. return -1;
  642. if (!os_utf8_to_wcs_ptr(target, 0, &wtarget))
  643. return -1;
  644. if (!os_utf8_to_wcs_ptr(from, 0, &wfrom))
  645. goto fail;
  646. if (backup && !os_utf8_to_wcs_ptr(backup, 0, &wbackup))
  647. goto fail;
  648. if (ReplaceFileW(wtarget, wfrom, wbackup, 0, NULL, NULL)) {
  649. code = 0;
  650. } else if (GetLastError() == ERROR_FILE_NOT_FOUND) {
  651. code = MoveFileExW(wfrom, wtarget, MOVEFILE_REPLACE_EXISTING) ? 0 : -1;
  652. }
  653. fail:
  654. bfree(wtarget);
  655. bfree(wfrom);
  656. bfree(wbackup);
  657. return code;
  658. }
  659. BOOL WINAPI DllMain(HINSTANCE hinst_dll, DWORD reason, LPVOID reserved)
  660. {
  661. switch (reason) {
  662. case DLL_PROCESS_ATTACH:
  663. timeBeginPeriod(1);
  664. #ifdef PTW32_STATIC_LIB
  665. pthread_win32_process_attach_np();
  666. #endif
  667. break;
  668. case DLL_PROCESS_DETACH:
  669. timeEndPeriod(1);
  670. #ifdef PTW32_STATIC_LIB
  671. pthread_win32_process_detach_np();
  672. #endif
  673. break;
  674. case DLL_THREAD_ATTACH:
  675. #ifdef PTW32_STATIC_LIB
  676. pthread_win32_thread_attach_np();
  677. #endif
  678. break;
  679. case DLL_THREAD_DETACH:
  680. #ifdef PTW32_STATIC_LIB
  681. pthread_win32_thread_detach_np();
  682. #endif
  683. break;
  684. }
  685. UNUSED_PARAMETER(hinst_dll);
  686. UNUSED_PARAMETER(reserved);
  687. return true;
  688. }
  689. os_performance_token_t *os_request_high_performance(const char *reason)
  690. {
  691. UNUSED_PARAMETER(reason);
  692. return NULL;
  693. }
  694. void os_end_high_performance(os_performance_token_t *token)
  695. {
  696. UNUSED_PARAMETER(token);
  697. }
  698. int os_copyfile(const char *file_in, const char *file_out)
  699. {
  700. wchar_t *file_in_utf16 = NULL;
  701. wchar_t *file_out_utf16 = NULL;
  702. int code = -1;
  703. if (!os_utf8_to_wcs_ptr(file_in, 0, &file_in_utf16)) {
  704. return -1;
  705. }
  706. if (!os_utf8_to_wcs_ptr(file_out, 0, &file_out_utf16)) {
  707. goto error;
  708. }
  709. code = CopyFileW(file_in_utf16, file_out_utf16, true) ? 0 : -1;
  710. error:
  711. bfree(file_in_utf16);
  712. bfree(file_out_utf16);
  713. return code;
  714. }
  715. char *os_getcwd(char *path, size_t size)
  716. {
  717. wchar_t *path_w;
  718. DWORD len;
  719. len = GetCurrentDirectoryW(0, NULL);
  720. if (!len)
  721. return NULL;
  722. path_w = bmalloc(((size_t)len + 1) * sizeof(wchar_t));
  723. GetCurrentDirectoryW(len + 1, path_w);
  724. os_wcs_to_utf8(path_w, (size_t)len, path, size);
  725. bfree(path_w);
  726. return path;
  727. }
  728. int os_chdir(const char *path)
  729. {
  730. wchar_t *path_w = NULL;
  731. size_t size;
  732. int ret;
  733. size = os_utf8_to_wcs_ptr(path, 0, &path_w);
  734. if (!path_w)
  735. return -1;
  736. ret = SetCurrentDirectoryW(path_w) ? 0 : -1;
  737. bfree(path_w);
  738. return ret;
  739. }
  740. typedef DWORD(WINAPI *get_file_version_info_size_w_t)(LPCWSTR module, LPDWORD unused);
  741. typedef BOOL(WINAPI *get_file_version_info_w_t)(LPCWSTR module, DWORD unused, DWORD len, LPVOID data);
  742. typedef BOOL(WINAPI *ver_query_value_w_t)(LPVOID data, LPCWSTR subblock, LPVOID *buf, PUINT sizeout);
  743. static get_file_version_info_size_w_t get_file_version_info_size = NULL;
  744. static get_file_version_info_w_t get_file_version_info = NULL;
  745. static ver_query_value_w_t ver_query_value = NULL;
  746. static bool ver_initialized = false;
  747. static bool ver_initialize_success = false;
  748. static bool initialize_version_functions(void)
  749. {
  750. HMODULE ver = GetModuleHandleW(L"version");
  751. ver_initialized = true;
  752. if (!ver) {
  753. ver = LoadLibraryW(L"version");
  754. if (!ver) {
  755. blog(LOG_ERROR, "Failed to load windows "
  756. "version library");
  757. return false;
  758. }
  759. }
  760. get_file_version_info_size = (get_file_version_info_size_w_t)GetProcAddress(ver, "GetFileVersionInfoSizeW");
  761. get_file_version_info = (get_file_version_info_w_t)GetProcAddress(ver, "GetFileVersionInfoW");
  762. ver_query_value = (ver_query_value_w_t)GetProcAddress(ver, "VerQueryValueW");
  763. if (!get_file_version_info_size || !get_file_version_info || !ver_query_value) {
  764. blog(LOG_ERROR, "Failed to load windows version "
  765. "functions");
  766. return false;
  767. }
  768. ver_initialize_success = true;
  769. return true;
  770. }
  771. bool get_dll_ver(const wchar_t *lib, struct win_version_info *ver_info)
  772. {
  773. VS_FIXEDFILEINFO *info = NULL;
  774. UINT len = 0;
  775. BOOL success;
  776. LPVOID data;
  777. DWORD size;
  778. char utf8_lib[512];
  779. if (!ver_initialized && !initialize_version_functions())
  780. return false;
  781. if (!ver_initialize_success)
  782. return false;
  783. os_wcs_to_utf8(lib, 0, utf8_lib, sizeof(utf8_lib));
  784. size = get_file_version_info_size(lib, NULL);
  785. if (!size) {
  786. blog(LOG_ERROR, "Failed to get %s version info size", utf8_lib);
  787. return false;
  788. }
  789. data = bmalloc(size);
  790. if (!get_file_version_info(lib, 0, size, data)) {
  791. blog(LOG_ERROR, "Failed to get %s version info", utf8_lib);
  792. bfree(data);
  793. return false;
  794. }
  795. success = ver_query_value(data, L"\\", (LPVOID *)&info, &len);
  796. if (!success || !info || !len) {
  797. blog(LOG_ERROR, "Failed to get %s version info value", utf8_lib);
  798. bfree(data);
  799. return false;
  800. }
  801. ver_info->major = (int)HIWORD(info->dwFileVersionMS);
  802. ver_info->minor = (int)LOWORD(info->dwFileVersionMS);
  803. ver_info->build = (int)HIWORD(info->dwFileVersionLS);
  804. ver_info->revis = (int)LOWORD(info->dwFileVersionLS);
  805. bfree(data);
  806. return true;
  807. }
  808. bool is_64_bit_windows(void)
  809. {
  810. #if defined(_WIN64)
  811. return true;
  812. #elif defined(_WIN32)
  813. BOOL b64 = false;
  814. return IsWow64Process(GetCurrentProcess(), &b64) && b64;
  815. #endif
  816. }
  817. bool is_arm64_windows(void)
  818. {
  819. #if defined(_M_ARM64) || defined(_M_ARM64EC)
  820. return true;
  821. #else
  822. USHORT processMachine;
  823. USHORT nativeMachine;
  824. bool result = IsWow64Process2(GetCurrentProcess(), &processMachine, &nativeMachine);
  825. return (result && (nativeMachine == IMAGE_FILE_MACHINE_ARM64));
  826. #endif
  827. }
  828. bool os_get_emulation_status(void)
  829. {
  830. #if defined(_M_ARM64) || defined(_M_ARM64EC)
  831. return false;
  832. #else
  833. return is_arm64_windows();
  834. #endif
  835. }
  836. void get_reg_dword(HKEY hkey, LPCWSTR sub_key, LPCWSTR value_name, struct reg_dword *info)
  837. {
  838. struct reg_dword reg = {0};
  839. HKEY key;
  840. LSTATUS status;
  841. status = RegOpenKeyEx(hkey, sub_key, 0, KEY_READ, &key);
  842. if (status != ERROR_SUCCESS) {
  843. info->status = status;
  844. info->size = 0;
  845. info->return_value = 0;
  846. return;
  847. }
  848. reg.size = sizeof(reg.return_value);
  849. reg.status = RegQueryValueExW(key, value_name, NULL, NULL, (LPBYTE)&reg.return_value, &reg.size);
  850. RegCloseKey(key);
  851. *info = reg;
  852. }
  853. #define WINVER_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
  854. static inline void rtl_get_ver(struct win_version_info *ver)
  855. {
  856. HMODULE ntdll = GetModuleHandleW(L"ntdll");
  857. if (!ntdll)
  858. return;
  859. NTSTATUS(WINAPI * get_ver)
  860. (RTL_OSVERSIONINFOEXW *) = (void *)GetProcAddress(ntdll, "RtlGetVersion");
  861. if (!get_ver) {
  862. return;
  863. }
  864. RTL_OSVERSIONINFOEXW osver = {0};
  865. osver.dwOSVersionInfoSize = sizeof(osver);
  866. NTSTATUS s = get_ver(&osver);
  867. if (s < 0) {
  868. return;
  869. }
  870. ver->major = osver.dwMajorVersion;
  871. ver->minor = osver.dwMinorVersion;
  872. ver->build = osver.dwBuildNumber;
  873. ver->revis = 0;
  874. }
  875. static inline bool get_reg_sz(HKEY key, const wchar_t *val, wchar_t *buf, DWORD size)
  876. {
  877. const LSTATUS status = RegGetValueW(key, NULL, val, RRF_RT_REG_SZ, NULL, buf, &size);
  878. return status == ERROR_SUCCESS;
  879. }
  880. static inline void get_reg_ver(struct win_version_info *ver)
  881. {
  882. HKEY key;
  883. DWORD size, dw_val;
  884. LSTATUS status;
  885. wchar_t str[MAX_SZ_LEN];
  886. status = RegOpenKeyW(HKEY_LOCAL_MACHINE, WINVER_REG_KEY, &key);
  887. if (status != ERROR_SUCCESS)
  888. return;
  889. size = sizeof(dw_val);
  890. status = RegQueryValueExW(key, L"CurrentMajorVersionNumber", NULL, NULL, (LPBYTE)&dw_val, &size);
  891. if (status == ERROR_SUCCESS)
  892. ver->major = (int)dw_val;
  893. status = RegQueryValueExW(key, L"CurrentMinorVersionNumber", NULL, NULL, (LPBYTE)&dw_val, &size);
  894. if (status == ERROR_SUCCESS)
  895. ver->minor = (int)dw_val;
  896. status = RegQueryValueExW(key, L"UBR", NULL, NULL, (LPBYTE)&dw_val, &size);
  897. if (status == ERROR_SUCCESS)
  898. ver->revis = (int)dw_val;
  899. if (get_reg_sz(key, L"CurrentBuildNumber", str, sizeof(str))) {
  900. ver->build = wcstol(str, NULL, 10);
  901. }
  902. const wchar_t *release_key = ver->build > 19041 ? L"DisplayVersion" : L"ReleaseId";
  903. if (get_reg_sz(key, release_key, str, sizeof(str))) {
  904. os_wcs_to_utf8(str, 0, win_release_id, MAX_SZ_LEN);
  905. }
  906. RegCloseKey(key);
  907. }
  908. static inline bool version_higher(struct win_version_info *cur, struct win_version_info *new)
  909. {
  910. if (new->major > cur->major) {
  911. return true;
  912. }
  913. if (new->major == cur->major) {
  914. if (new->minor > cur->minor) {
  915. return true;
  916. }
  917. if (new->minor == cur->minor) {
  918. if (new->build > cur->build) {
  919. return true;
  920. }
  921. if (new->build == cur->build) {
  922. return new->revis > cur->revis;
  923. }
  924. }
  925. }
  926. return false;
  927. }
  928. static inline void use_higher_ver(struct win_version_info *cur, struct win_version_info *new)
  929. {
  930. if (version_higher(cur, new))
  931. *cur = *new;
  932. }
  933. void get_win_ver(struct win_version_info *info)
  934. {
  935. static struct win_version_info ver = {0};
  936. static bool got_version = false;
  937. if (!info)
  938. return;
  939. if (!got_version) {
  940. struct win_version_info reg_ver = {0};
  941. struct win_version_info rtl_ver = {0};
  942. struct win_version_info nto_ver = {0};
  943. get_reg_ver(&reg_ver);
  944. rtl_get_ver(&rtl_ver);
  945. get_dll_ver(L"ntoskrnl.exe", &nto_ver);
  946. ver = reg_ver;
  947. use_higher_ver(&ver, &rtl_ver);
  948. use_higher_ver(&ver, &nto_ver);
  949. got_version = true;
  950. }
  951. *info = ver;
  952. }
  953. const char *get_win_release_id(void)
  954. {
  955. return win_release_id;
  956. }
  957. uint32_t get_win_ver_int(void)
  958. {
  959. return get_winver();
  960. }
  961. struct os_inhibit_info {
  962. bool active;
  963. };
  964. os_inhibit_t *os_inhibit_sleep_create(const char *reason)
  965. {
  966. UNUSED_PARAMETER(reason);
  967. return bzalloc(sizeof(struct os_inhibit_info));
  968. }
  969. bool os_inhibit_sleep_set_active(os_inhibit_t *info, bool active)
  970. {
  971. if (!info)
  972. return false;
  973. if (info->active == active)
  974. return false;
  975. if (active) {
  976. SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED |
  977. ES_DISPLAY_REQUIRED);
  978. } else {
  979. SetThreadExecutionState(ES_CONTINUOUS);
  980. }
  981. info->active = active;
  982. return true;
  983. }
  984. void os_inhibit_sleep_destroy(os_inhibit_t *info)
  985. {
  986. if (info) {
  987. os_inhibit_sleep_set_active(info, false);
  988. bfree(info);
  989. }
  990. }
  991. void os_breakpoint(void)
  992. {
  993. __debugbreak();
  994. }
  995. DWORD num_logical_cores(ULONG_PTR mask)
  996. {
  997. DWORD left_shift = sizeof(ULONG_PTR) * 8 - 1;
  998. DWORD bit_set_count = 0;
  999. ULONG_PTR bit_test = (ULONG_PTR)1 << left_shift;
  1000. for (DWORD i = 0; i <= left_shift; ++i) {
  1001. bit_set_count += ((mask & bit_test) ? 1 : 0);
  1002. bit_test /= 2;
  1003. }
  1004. return bit_set_count;
  1005. }
  1006. static int physical_cores = 0;
  1007. static int logical_cores = 0;
  1008. static bool core_count_initialized = false;
  1009. static void os_get_cores_internal(void)
  1010. {
  1011. PSYSTEM_LOGICAL_PROCESSOR_INFORMATION info = NULL, temp = NULL;
  1012. DWORD len = 0;
  1013. if (core_count_initialized)
  1014. return;
  1015. core_count_initialized = true;
  1016. GetLogicalProcessorInformation(info, &len);
  1017. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  1018. return;
  1019. info = malloc(len);
  1020. if (info) {
  1021. if (GetLogicalProcessorInformation(info, &len)) {
  1022. DWORD num = len / sizeof(*info);
  1023. temp = info;
  1024. for (DWORD i = 0; i < num; i++) {
  1025. if (temp->Relationship == RelationProcessorCore) {
  1026. ULONG_PTR mask = temp->ProcessorMask;
  1027. physical_cores++;
  1028. logical_cores += num_logical_cores(mask);
  1029. }
  1030. temp++;
  1031. }
  1032. }
  1033. free(info);
  1034. }
  1035. }
  1036. int os_get_physical_cores(void)
  1037. {
  1038. if (!core_count_initialized)
  1039. os_get_cores_internal();
  1040. return physical_cores;
  1041. }
  1042. int os_get_logical_cores(void)
  1043. {
  1044. if (!core_count_initialized)
  1045. os_get_cores_internal();
  1046. return logical_cores;
  1047. }
  1048. static inline bool os_get_sys_memory_usage_internal(MEMORYSTATUSEX *msex)
  1049. {
  1050. if (!GlobalMemoryStatusEx(msex))
  1051. return false;
  1052. return true;
  1053. }
  1054. uint64_t os_get_sys_free_size(void)
  1055. {
  1056. MEMORYSTATUSEX msex = {sizeof(MEMORYSTATUSEX)};
  1057. if (!os_get_sys_memory_usage_internal(&msex))
  1058. return 0;
  1059. return msex.ullAvailPhys;
  1060. }
  1061. static uint64_t total_memory = 0;
  1062. static bool total_memory_initialized = false;
  1063. static void os_get_sys_total_size_internal()
  1064. {
  1065. total_memory_initialized = true;
  1066. MEMORYSTATUSEX msex = {sizeof(MEMORYSTATUSEX)};
  1067. if (!os_get_sys_memory_usage_internal(&msex))
  1068. return;
  1069. total_memory = msex.ullTotalPhys;
  1070. }
  1071. uint64_t os_get_sys_total_size(void)
  1072. {
  1073. if (!total_memory_initialized)
  1074. os_get_sys_total_size_internal();
  1075. return total_memory;
  1076. }
  1077. static inline bool os_get_proc_memory_usage_internal(PROCESS_MEMORY_COUNTERS *pmc)
  1078. {
  1079. if (!GetProcessMemoryInfo(GetCurrentProcess(), pmc, sizeof(*pmc)))
  1080. return false;
  1081. return true;
  1082. }
  1083. bool os_get_proc_memory_usage(os_proc_memory_usage_t *usage)
  1084. {
  1085. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  1086. if (!os_get_proc_memory_usage_internal(&pmc))
  1087. return false;
  1088. usage->resident_size = pmc.WorkingSetSize;
  1089. usage->virtual_size = pmc.PagefileUsage;
  1090. return true;
  1091. }
  1092. uint64_t os_get_proc_resident_size(void)
  1093. {
  1094. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  1095. if (!os_get_proc_memory_usage_internal(&pmc))
  1096. return 0;
  1097. return pmc.WorkingSetSize;
  1098. }
  1099. uint64_t os_get_proc_virtual_size(void)
  1100. {
  1101. PROCESS_MEMORY_COUNTERS pmc = {sizeof(PROCESS_MEMORY_COUNTERS)};
  1102. if (!os_get_proc_memory_usage_internal(&pmc))
  1103. return 0;
  1104. return pmc.PagefileUsage;
  1105. }
  1106. uint64_t os_get_free_disk_space(const char *dir)
  1107. {
  1108. wchar_t *wdir = NULL;
  1109. os_utf8_to_wcs_ptr(dir, 0, &wdir);
  1110. if (!wdir)
  1111. return 0;
  1112. ULARGE_INTEGER free;
  1113. bool success = !!GetDiskFreeSpaceExW(wdir, &free, NULL, NULL);
  1114. bfree(wdir);
  1115. return success ? free.QuadPart : 0;
  1116. }
  1117. char *os_generate_uuid(void)
  1118. {
  1119. UUID uuid;
  1120. RPC_STATUS res = UuidCreate(&uuid);
  1121. if (res != RPC_S_OK && res != RPC_S_UUID_LOCAL_ONLY)
  1122. bcrash("Failed to get UUID, RPC_STATUS: %l", res);
  1123. struct dstr uuid_str = {0};
  1124. dstr_printf(&uuid_str, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.Data1, uuid.Data2, uuid.Data3,
  1125. uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3], uuid.Data4[4], uuid.Data4[5],
  1126. uuid.Data4[6], uuid.Data4[7]);
  1127. return uuid_str.array;
  1128. }