game-capture-file-init.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. #include <windows.h>
  2. #include <strsafe.h>
  3. #include <shlobj.h>
  4. #include <aclapi.h>
  5. #include <obs-module.h>
  6. #include <util/windows/win-version.h>
  7. #include <util/platform.h>
  8. #include <util/c99defs.h>
  9. #include <util/base.h>
  10. /* ------------------------------------------------------------------------- */
  11. /* helper funcs */
  12. static bool has_elevation_internal()
  13. {
  14. SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
  15. PSID sid = NULL;
  16. BOOL elevated = false;
  17. BOOL success;
  18. success = AllocateAndInitializeSid(&sia, 2, SECURITY_BUILTIN_DOMAIN_RID,
  19. DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0,
  20. 0, 0, &sid);
  21. if (success && sid) {
  22. CheckTokenMembership(NULL, sid, &elevated);
  23. FreeSid(sid);
  24. }
  25. return elevated;
  26. }
  27. static bool has_elevation()
  28. {
  29. static bool elevated = false;
  30. static bool initialized = false;
  31. if (!initialized) {
  32. elevated = has_elevation_internal();
  33. initialized = true;
  34. }
  35. return elevated;
  36. }
  37. static bool add_aap_perms(const wchar_t *dir)
  38. {
  39. PSECURITY_DESCRIPTOR sd = NULL;
  40. PACL new_dacl = NULL;
  41. bool success = false;
  42. PACL dacl;
  43. if (GetNamedSecurityInfoW(dir, SE_FILE_OBJECT,
  44. DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
  45. NULL, &sd) != ERROR_SUCCESS) {
  46. goto fail;
  47. }
  48. EXPLICIT_ACCESSW ea = {0};
  49. ea.grfAccessPermissions = GENERIC_READ | GENERIC_EXECUTE;
  50. ea.grfAccessMode = GRANT_ACCESS;
  51. ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  52. ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
  53. ea.Trustee.ptstrName = L"ALL APPLICATION PACKAGES";
  54. if (SetEntriesInAclW(1, &ea, dacl, &new_dacl) != ERROR_SUCCESS) {
  55. goto fail;
  56. }
  57. if (SetNamedSecurityInfoW((wchar_t *)dir, SE_FILE_OBJECT,
  58. DACL_SECURITY_INFORMATION, NULL, NULL,
  59. new_dacl, NULL) != ERROR_SUCCESS) {
  60. goto fail;
  61. }
  62. success = true;
  63. fail:
  64. if (sd)
  65. LocalFree(sd);
  66. if (new_dacl)
  67. LocalFree(new_dacl);
  68. return success;
  69. }
  70. static inline bool file_exists(const wchar_t *path)
  71. {
  72. WIN32_FIND_DATAW wfd;
  73. HANDLE h = FindFirstFileW(path, &wfd);
  74. if (h == INVALID_HANDLE_VALUE)
  75. return false;
  76. FindClose(h);
  77. return true;
  78. }
  79. static LSTATUS get_reg(HKEY hkey, LPCWSTR sub_key, LPCWSTR value_name, bool b64)
  80. {
  81. HKEY key;
  82. LSTATUS status;
  83. DWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
  84. DWORD size = sizeof(DWORD);
  85. DWORD val;
  86. status = RegOpenKeyEx(hkey, sub_key, 0, KEY_READ | flags, &key);
  87. if (status == ERROR_SUCCESS) {
  88. status = RegQueryValueExW(key, value_name, NULL, NULL,
  89. (LPBYTE)&val, &size);
  90. RegCloseKey(key);
  91. }
  92. return status;
  93. }
  94. #define get_programdata_path(path, subpath) \
  95. do { \
  96. SHGetFolderPathW(NULL, CSIDL_COMMON_APPDATA, NULL, \
  97. SHGFP_TYPE_CURRENT, path); \
  98. StringCbCatW(path, sizeof(path), L"\\"); \
  99. StringCbCatW(path, sizeof(path), subpath); \
  100. } while (false)
  101. #define make_filename(str, name, ext) \
  102. do { \
  103. StringCbCatW(str, sizeof(str), name); \
  104. StringCbCatW(str, sizeof(str), b64 ? L"64" : L"32"); \
  105. StringCbCatW(str, sizeof(str), ext); \
  106. } while (false)
  107. /* ------------------------------------------------------------------------- */
  108. /* function to get the path to the hook */
  109. static bool programdata64_hook_exists = false;
  110. static bool programdata32_hook_exists = false;
  111. char *get_hook_path(bool b64)
  112. {
  113. wchar_t path[MAX_PATH];
  114. get_programdata_path(path, L"obs-studio-hook\\");
  115. make_filename(path, L"graphics-hook", L".dll");
  116. if ((b64 && programdata64_hook_exists) ||
  117. (!b64 && programdata32_hook_exists)) {
  118. char *path_utf8 = NULL;
  119. os_wcs_to_utf8_ptr(path, 0, &path_utf8);
  120. return path_utf8;
  121. }
  122. return obs_module_file(b64 ? "graphics-hook64.dll"
  123. : "graphics-hook32.dll");
  124. }
  125. /* ------------------------------------------------------------------------- */
  126. /* initialization */
  127. #define IMPLICIT_LAYERS L"SOFTWARE\\Khronos\\Vulkan\\ImplicitLayers"
  128. static bool update_hook_file(bool b64)
  129. {
  130. wchar_t temp[MAX_PATH];
  131. wchar_t src[MAX_PATH];
  132. wchar_t dst[MAX_PATH];
  133. wchar_t src_json[MAX_PATH];
  134. wchar_t dst_json[MAX_PATH];
  135. StringCbCopyW(temp, sizeof(temp),
  136. L"..\\..\\data\\obs-plugins\\"
  137. L"win-capture\\");
  138. make_filename(temp, L"obs-vulkan", L".json");
  139. if (_wfullpath(src_json, temp, MAX_PATH) == NULL)
  140. return false;
  141. StringCbCopyW(temp, sizeof(temp),
  142. L"..\\..\\data\\obs-plugins\\"
  143. L"win-capture\\");
  144. make_filename(temp, L"graphics-hook", L".dll");
  145. if (_wfullpath(src, temp, MAX_PATH) == NULL)
  146. return false;
  147. get_programdata_path(temp, L"obs-studio-hook\\");
  148. StringCbCopyW(dst_json, sizeof(dst_json), temp);
  149. StringCbCopyW(dst, sizeof(dst), temp);
  150. make_filename(dst_json, L"obs-vulkan", L".json");
  151. make_filename(dst, L"graphics-hook", L".dll");
  152. if (!file_exists(src)) {
  153. return false;
  154. }
  155. if (!file_exists(src_json)) {
  156. return false;
  157. }
  158. if (!file_exists(dst) || !file_exists(dst_json)) {
  159. CreateDirectoryW(temp, NULL);
  160. if (has_elevation())
  161. add_aap_perms(temp);
  162. if (!CopyFileW(src_json, dst_json, false))
  163. return false;
  164. if (!CopyFileW(src, dst, false))
  165. return false;
  166. return true;
  167. }
  168. if (has_elevation())
  169. add_aap_perms(temp);
  170. struct win_version_info ver_src = {0};
  171. struct win_version_info ver_dst = {0};
  172. if (!get_dll_ver(src, &ver_src))
  173. return false;
  174. if (!get_dll_ver(dst, &ver_dst))
  175. return false;
  176. /* if source is greater than dst, overwrite new file */
  177. while (win_version_compare(&ver_dst, &ver_src) < 0) {
  178. if (!CopyFileW(src_json, dst_json, false))
  179. return false;
  180. if (!CopyFileW(src, dst, false))
  181. return false;
  182. if (!get_dll_ver(dst, &ver_dst))
  183. return false;
  184. }
  185. /* do not use if major version incremented in target compared to
  186. * ours */
  187. if (ver_dst.major > ver_src.major) {
  188. return false;
  189. }
  190. return true;
  191. }
  192. #define warn(format, ...) \
  193. blog(LOG_WARNING, "%s: " format, "[Vulkan Capture Init]", ##__VA_ARGS__)
  194. /* Sets vulkan layer registry if it doesn't already exist */
  195. static void init_vulkan_registry(bool b64)
  196. {
  197. DWORD flags = b64 ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
  198. HKEY key = NULL;
  199. LSTATUS s;
  200. wchar_t path[MAX_PATH];
  201. get_programdata_path(path, L"obs-studio-hook\\");
  202. make_filename(path, L"obs-vulkan", L".json");
  203. s = get_reg(HKEY_LOCAL_MACHINE, IMPLICIT_LAYERS, path, b64);
  204. if (s == ERROR_FILE_NOT_FOUND) {
  205. s = get_reg(HKEY_CURRENT_USER, IMPLICIT_LAYERS, path, b64);
  206. if (s != ERROR_FILE_NOT_FOUND && s != ERROR_SUCCESS) {
  207. warn("Failed to query registry keys: %d", (int)s);
  208. goto finish;
  209. }
  210. if (s == ERROR_SUCCESS && has_elevation()) {
  211. s = RegOpenKeyEx(HKEY_CURRENT_USER, IMPLICIT_LAYERS, 0,
  212. KEY_WRITE | flags, &key);
  213. if (s == ERROR_SUCCESS) {
  214. RegDeleteValueW(key, path);
  215. RegCloseKey(key);
  216. s = -1;
  217. key = NULL;
  218. }
  219. }
  220. } else if (s != ERROR_SUCCESS) {
  221. warn("Failed to query registry keys: %d", (int)s);
  222. goto finish;
  223. }
  224. if (s == ERROR_SUCCESS) {
  225. goto finish;
  226. }
  227. HKEY type = has_elevation() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  228. DWORD temp;
  229. s = RegCreateKeyExW(type, IMPLICIT_LAYERS, 0, NULL, 0,
  230. KEY_WRITE | flags, NULL, &key, &temp);
  231. if (s != ERROR_SUCCESS) {
  232. warn("Failed to create registry key");
  233. goto finish;
  234. }
  235. DWORD zero = 0;
  236. s = RegSetValueExW(key, path, 0, REG_DWORD, (const BYTE *)&zero,
  237. sizeof(zero));
  238. if (s != ERROR_SUCCESS) {
  239. warn("Failed to set registry value");
  240. }
  241. finish:
  242. if (key)
  243. RegCloseKey(key);
  244. }
  245. void init_hook_files()
  246. {
  247. if (update_hook_file(true)) {
  248. programdata64_hook_exists = true;
  249. init_vulkan_registry(true);
  250. }
  251. if (update_hook_file(false)) {
  252. programdata32_hook_exists = true;
  253. init_vulkan_registry(false);
  254. }
  255. }