1
0

platform-windows.c 31 KB

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