1 #include <obs-module.h>
2 #include <util/windows/win-version.h>
3 #include <util/platform.h>
4 #include <util/dstr.h>
5 #include <util/config-file.h>
6 #include <util/pipe.h>
7 
8 #include <windows.h>
9 #include "graphics-hook-info.h"
10 
11 extern struct graphics_offsets offsets32;
12 extern struct graphics_offsets offsets64;
13 
load_offsets_from_string(struct graphics_offsets * offsets,const char * str)14 static inline bool load_offsets_from_string(struct graphics_offsets *offsets,
15 					    const char *str)
16 {
17 	config_t *config;
18 
19 	if (config_open_string(&config, str) != CONFIG_SUCCESS) {
20 		return false;
21 	}
22 
23 	offsets->d3d8.present =
24 		(uint32_t)config_get_uint(config, "d3d8", "present");
25 
26 	offsets->d3d9.present =
27 		(uint32_t)config_get_uint(config, "d3d9", "present");
28 	offsets->d3d9.present_ex =
29 		(uint32_t)config_get_uint(config, "d3d9", "present_ex");
30 	offsets->d3d9.present_swap =
31 		(uint32_t)config_get_uint(config, "d3d9", "present_swap");
32 	offsets->d3d9.d3d9_clsoff =
33 		(uint32_t)config_get_uint(config, "d3d9", "d3d9_clsoff");
34 	offsets->d3d9.is_d3d9ex_clsoff =
35 		(uint32_t)config_get_uint(config, "d3d9", "is_d3d9ex_clsoff");
36 
37 	offsets->dxgi.present =
38 		(uint32_t)config_get_uint(config, "dxgi", "present");
39 	offsets->dxgi.present1 =
40 		(uint32_t)config_get_uint(config, "dxgi", "present1");
41 	offsets->dxgi.resize =
42 		(uint32_t)config_get_uint(config, "dxgi", "resize");
43 	offsets->dxgi2.release =
44 		(uint32_t)config_get_uint(config, "dxgi", "release");
45 
46 	config_close(config);
47 	return true;
48 }
49 
load_offsets_from_file(struct graphics_offsets * offsets,const char * file)50 static inline bool load_offsets_from_file(struct graphics_offsets *offsets,
51 					  const char *file)
52 {
53 	char *str = os_quick_read_utf8_file(file);
54 	bool success = false;
55 	if (str && *str)
56 		success = load_offsets_from_string(offsets, str);
57 	bfree(str);
58 	return success;
59 }
60 
config_ver_mismatch(config_t * ver_config,const char * section,struct win_version_info * ver)61 static inline bool config_ver_mismatch(config_t *ver_config,
62 				       const char *section,
63 				       struct win_version_info *ver)
64 {
65 	struct win_version_info config_ver;
66 	bool mismatch = false;
67 
68 #define get_sub_ver(subver)                                                    \
69 	config_ver.subver = (int)config_get_int(ver_config, section, #subver); \
70 	mismatch |= config_ver.subver != ver->subver;
71 
72 	get_sub_ver(major);
73 	get_sub_ver(minor);
74 	get_sub_ver(build);
75 	get_sub_ver(revis);
76 
77 #undef get_sub_ver
78 
79 	return mismatch;
80 }
81 
write_config_ver(config_t * ver_config,const char * section,struct win_version_info * ver)82 static inline void write_config_ver(config_t *ver_config, const char *section,
83 				    struct win_version_info *ver)
84 {
85 #define set_sub_ver(subver) \
86 	config_set_int(ver_config, section, #subver, ver->subver);
87 
88 	set_sub_ver(major);
89 	set_sub_ver(minor);
90 	set_sub_ver(build);
91 	set_sub_ver(revis);
92 
93 #undef set_sub_ver
94 }
95 
get_32bit_system_dll_ver(const wchar_t * system_lib,struct win_version_info * ver)96 static bool get_32bit_system_dll_ver(const wchar_t *system_lib,
97 				     struct win_version_info *ver)
98 {
99 	wchar_t path[MAX_PATH];
100 	UINT ret;
101 
102 #ifdef _WIN64
103 	ret = GetSystemWow64DirectoryW(path, MAX_PATH);
104 #else
105 	ret = GetSystemDirectoryW(path, MAX_PATH);
106 #endif
107 	if (!ret) {
108 		blog(LOG_ERROR,
109 		     "Failed to get windows 32bit system path: "
110 		     "%lu",
111 		     GetLastError());
112 		return false;
113 	}
114 
115 	wcscat(path, L"\\");
116 	wcscat(path, system_lib);
117 	return get_dll_ver(path, ver);
118 }
119 
cached_versions_match(void)120 bool cached_versions_match(void)
121 {
122 	struct win_version_info d3d8_ver = {0};
123 	struct win_version_info d3d9_ver = {0};
124 	struct win_version_info dxgi_ver = {0};
125 	bool ver_mismatch = false;
126 	config_t *config;
127 	char *ver_file;
128 	int ret;
129 
130 	ver_mismatch |= !get_32bit_system_dll_ver(L"d3d8.dll", &d3d8_ver);
131 	ver_mismatch |= !get_32bit_system_dll_ver(L"d3d9.dll", &d3d9_ver);
132 	ver_mismatch |= !get_32bit_system_dll_ver(L"dxgi.dll", &dxgi_ver);
133 
134 	ver_file = obs_module_config_path("version.ini");
135 	if (!ver_file)
136 		return false;
137 
138 	ret = config_open(&config, ver_file, CONFIG_OPEN_ALWAYS);
139 	if (ret != CONFIG_SUCCESS)
140 		goto failed;
141 
142 	ver_mismatch |= config_ver_mismatch(config, "d3d8", &d3d8_ver);
143 	ver_mismatch |= config_ver_mismatch(config, "d3d9", &d3d9_ver);
144 	ver_mismatch |= config_ver_mismatch(config, "dxgi", &dxgi_ver);
145 
146 	if (ver_mismatch) {
147 		write_config_ver(config, "d3d8", &d3d8_ver);
148 		write_config_ver(config, "d3d9", &d3d9_ver);
149 		write_config_ver(config, "dxgi", &dxgi_ver);
150 		config_save_safe(config, "tmp", NULL);
151 	}
152 
153 failed:
154 	bfree(ver_file);
155 	config_close(config);
156 	return !ver_mismatch;
157 }
158 
load_graphics_offsets(bool is32bit,bool use_hook_address_cache,const char * config_path)159 bool load_graphics_offsets(bool is32bit, bool use_hook_address_cache,
160 			   const char *config_path)
161 {
162 	char *offset_exe_path = NULL;
163 	struct dstr config_ini = {0};
164 	struct dstr offset_exe = {0};
165 	struct dstr str = {0};
166 	os_process_pipe_t *pp;
167 	bool success = false;
168 	char data[2048];
169 
170 #ifndef _WIN64
171 	if (!is32bit && !is_64_bit_windows()) {
172 		return true;
173 	}
174 #endif
175 
176 	dstr_copy(&offset_exe, "get-graphics-offsets");
177 	dstr_cat(&offset_exe, is32bit ? "32.exe" : "64.exe");
178 	offset_exe_path = obs_module_file(offset_exe.array);
179 
180 	pp = os_process_pipe_create(offset_exe_path, "r");
181 	if (!pp) {
182 		blog(LOG_INFO, "load_graphics_offsets: Failed to start '%s'",
183 		     offset_exe.array);
184 		goto error;
185 	}
186 
187 	for (;;) {
188 		size_t len =
189 			os_process_pipe_read(pp, (uint8_t *)data, sizeof(data));
190 		if (!len)
191 			break;
192 
193 		dstr_ncat(&str, data, len);
194 	}
195 
196 	if (dstr_is_empty(&str)) {
197 		blog(LOG_INFO,
198 		     "load_graphics_offsets: Failed to read "
199 		     "from '%s'",
200 		     offset_exe.array);
201 		goto error;
202 	}
203 
204 	if (use_hook_address_cache) {
205 		dstr_copy(&config_ini, config_path);
206 		dstr_cat(&config_ini, is32bit ? "32.ini" : "64.ini");
207 
208 		os_quick_write_utf8_file_safe(config_ini.array, str.array,
209 					      str.len, false, "tmp", NULL);
210 		dstr_free(&config_ini);
211 	}
212 
213 	success = load_offsets_from_string(is32bit ? &offsets32 : &offsets64,
214 					   str.array);
215 	if (!success) {
216 		blog(LOG_INFO, "load_graphics_offsets: Failed to load string");
217 	}
218 
219 	os_process_pipe_destroy(pp);
220 
221 error:
222 	bfree(offset_exe_path);
223 	dstr_free(&offset_exe);
224 	dstr_free(&str);
225 	return success;
226 }
227 
load_cached_graphics_offsets(bool is32bit,const char * config_path)228 bool load_cached_graphics_offsets(bool is32bit, const char *config_path)
229 {
230 	struct dstr config_ini = {0};
231 	bool success;
232 
233 	dstr_copy(&config_ini, config_path);
234 	dstr_cat(&config_ini, is32bit ? "32.ini" : "64.ini");
235 	success = load_offsets_from_file(is32bit ? &offsets32 : &offsets64,
236 					 config_ini.array);
237 	if (!success)
238 		success = load_graphics_offsets(is32bit, true, config_path);
239 
240 	dstr_free(&config_ini);
241 	return success;
242 }
243