1 /*
2 * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3 *
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on the
6 * source.
7 *
8 */
9 
10 #include "globalincs/pstypes.h"
11 #include "osapi/osregistry.h"
12 #include "osapi/osapi.h"
13 #include "cmdline/cmdline.h"
14 #include "osregistry.h"
15 
16 
17 #ifdef WIN32
18 #include <windows.h>
19 // Stupid Microsoft is not able to fix a simple compile warning: https://connect.microsoft.com/VisualStudio/feedback/details/1342304/level-1-compiler-warnings-in-windows-sdk-shipped-with-visual-studio
20 #pragma warning(push)
21 #pragma warning(disable: 4091) // ignored on left of '' when no variable is declared
22 #pragma warning(pop)
23 #include <sddl.h>
24 #endif
25 
26 namespace
27 {
28 	// ------------------------------------------------------------------------------------------------------------
29 	// REGISTRY FUNCTIONS
30 	//
31 
32 	char szCompanyName[128] = "Volition";
33 	char szAppName[128] = "FreeSpace2";
34 
35 	int Os_reg_inited = 0;
36 
37 #ifdef WIN32
38 	// os registry functions -------------------------------------------------------------
39 
40 	// This code is needed for compatibility with the old windows registry
41 	bool userSIDInitialized = false;
42 	bool userSIDValid = false;
43 	SCP_string userSID;
44 
get_user_sid(SCP_string & outStr)45 	bool get_user_sid(SCP_string& outStr)
46 	{
47 		HANDLE hToken = NULL;
48 		if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) == FALSE)
49 		{
50 			mprintf(("Failed to get process token! Error Code: %d\n", (int)GetLastError()));
51 
52 			return false;
53 		}
54 
55 		DWORD dwBufferSize;
56 		GetTokenInformation(hToken, TokenUser, NULL, 0, &dwBufferSize);
57 
58 		PTOKEN_USER ptkUser = (PTOKEN_USER) new byte[dwBufferSize];
59 
60 		if (GetTokenInformation(hToken, TokenUser, ptkUser, dwBufferSize, &dwBufferSize))
61 		{
62 			CloseHandle(hToken);
63 		}
64 
65 		if (IsValidSid(ptkUser->User.Sid) == FALSE)
66 		{
67 			mprintf(("Invalid SID structure detected!\n"));
68 
69 			delete[] ptkUser;
70 			return false;
71 		}
72 
73 		LPTSTR sidName = NULL;
74 		if (ConvertSidToStringSid(ptkUser->User.Sid, &sidName) == 0)
75 		{
76 			mprintf(("Failed to convert SID structure to string! Error Code: %d\n", (int)GetLastError()));
77 
78 			delete[] ptkUser;
79 			return false;
80 		}
81 
82 		outStr.assign(sidName);
83 
84 		LocalFree(sidName);
85 		delete[](byte*) ptkUser;
86 
87 		return true;
88 	}
89 
needsWOW64()90 	bool needsWOW64()
91 	{
92 #if IS_64BIT
93 		// 64-bit application always use the Wow6432Node
94 		return true;
95 #else
96 		BOOL bIsWow64 = FALSE;
97 		if (!IsWow64Process(GetCurrentProcess(), &bIsWow64))
98 		{
99 			mprintf(("Failed to determine if we run under Wow64, registry configuration may fail!\n"));
100 			return false;
101 		}
102 
103 		return bIsWow64 == TRUE;
104 #endif
105 	}
106 
get_registry_keyname(char * out_keyname,const char * section,bool alternate_path)107 	HKEY get_registry_keyname(char* out_keyname, const char* section, bool alternate_path) {
108 		if (!alternate_path) {
109 			// Use the original registry path, sometimes breaks for no reason which can be fixed by the code below
110 			if (section) {
111 				sprintf(out_keyname, "Software\\%s\\%s\\%s", szCompanyName, szAppName, section);
112 			}
113 			else {
114 				sprintf(out_keyname, "Software\\%s\\%s", szCompanyName, szAppName);
115 			}
116 			return HKEY_LOCAL_MACHINE;
117 		}
118 
119 		// Every compiler from Visual Studio 2008 onward should have support for UAC
120 		if (!userSIDInitialized)
121 		{
122 			OSVERSIONINFO versionInfo;
123 			versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
124 			GetVersionEx(&versionInfo);
125 
126 			// Windows Vista is 6.0 which is the first version requiring this
127 			if (versionInfo.dwMajorVersion >= 6)
128 			{
129 				userSIDValid = get_user_sid(userSID);
130 			}
131 			userSIDInitialized = true;
132 		}
133 #if !defined(_MSC_VER) || _MSC_VER >= 1400
134 		if (userSIDValid)
135 		{
136 			if (needsWOW64())
137 			{
138 				if (section) {
139 					sprintf(out_keyname, "%s_Classes\\VirtualStore\\MACHINE\\SOFTWARE\\WOW6432Node\\%s\\%s\\%s", userSID.c_str(), szCompanyName, szAppName, section);
140 				}
141 				else {
142 					sprintf(out_keyname, "%s_Classes\\VirtualStore\\MACHINE\\SOFTWARE\\WOW6432Node\\%s\\%s", userSID.c_str(), szCompanyName, szAppName);
143 				}
144 			}
145 			else
146 			{
147 				if (section) {
148 					sprintf(out_keyname, "%s_Classes\\VirtualStore\\MACHINE\\SOFTWARE\\%s\\%s\\%s", userSID.c_str(), szCompanyName, szAppName, section);
149 				}
150 				else {
151 					sprintf(out_keyname, "%s_Classes\\VirtualStore\\MACHINE\\SOFTWARE\\%s\\%s", userSID.c_str(), szCompanyName, szAppName);
152 				}
153 			}
154 
155 			return HKEY_USERS;
156 		}
157 		else
158 		{
159 			// This will probably fail
160 			if (section) {
161 				sprintf(out_keyname, "Software\\%s\\%s\\%s", szCompanyName, szAppName, section);
162 			}
163 			else {
164 				sprintf(out_keyname, "Software\\%s\\%s", szCompanyName, szAppName);
165 			}
166 
167 			return HKEY_LOCAL_MACHINE;
168 		}
169 #else
170 		if (section) {
171 			sprintf(out_keyname, "Software\\%s\\%s\\%s", szCompanyName, szAppName, section);
172 		}
173 		else {
174 			sprintf(out_keyname, "Software\\%s\\%s", szCompanyName, szAppName);
175 		}
176 
177 		return HKEY_LOCAL_MACHINE;
178 #endif
179 	}
180 
registry_write_string(const char * section,const char * name,const char * value)181 	void registry_write_string(const char *section, const char *name, const char *value)
182 	{
183 		HKEY hKey = NULL;
184 		DWORD dwDisposition;
185 		char keyname[1024];
186 		LONG lResult;
187 
188 		if (!Os_reg_inited) {
189 			return;
190 		}
191 
192 		HKEY useHKey = get_registry_keyname(keyname, section, Cmdline_alternate_registry_path);
193 
194 		lResult = RegCreateKeyEx(useHKey,						// Where to add it
195 			keyname,					// name of key
196 			0,						// DWORD reserved
197 			NULL,						// Object class
198 			REG_OPTION_NON_VOLATILE,	// Save to disk
199 			KEY_ALL_ACCESS,				// Allows all changes
200 			NULL,						// Default security attributes
201 			&hKey,						// Location to store key
202 			&dwDisposition);			// Location to store status of key
203 
204 		if (lResult != ERROR_SUCCESS) {
205 			goto Cleanup;
206 		}
207 
208 		if (!name) {
209 			goto Cleanup;
210 		}
211 
212 		lResult = RegSetValueEx(hKey,					// Handle to key
213 			name,					// The values name
214 			0,						// DWORD reserved
215 			REG_SZ,					// null terminated string
216 			(CONST BYTE *)value,	// value to set
217 			(DWORD)(strlen(value) + 1));	// How many bytes to set
218 
219 		if (lResult != ERROR_SUCCESS) {
220 			goto Cleanup;
221 		}
222 
223 
224 	Cleanup:
225 		if (hKey)
226 			RegCloseKey(hKey);
227 	}
228 
registry_write_uint(const char * section,const char * name,uint value)229 	void registry_write_uint(const char *section, const char *name, uint value)
230 	{
231 		HKEY hKey = NULL;
232 		DWORD dwDisposition;
233 		char keyname[1024];
234 		LONG lResult;
235 
236 		if (!Os_reg_inited) {
237 			return;
238 		}
239 
240 		HKEY useHKey = get_registry_keyname(keyname, section, Cmdline_alternate_registry_path);
241 
242 		lResult = RegCreateKeyEx(useHKey,						// Where to add it
243 			keyname,					// name of key
244 			0,							// DWORD reserved
245 			NULL,						// Object class
246 			REG_OPTION_NON_VOLATILE,	// Save to disk
247 			KEY_ALL_ACCESS,				// Allows all changes
248 			NULL,						// Default security attributes
249 			&hKey,						// Location to store key
250 			&dwDisposition);			// Location to store status of key
251 
252 		if (lResult != ERROR_SUCCESS) {
253 			goto Cleanup;
254 		}
255 
256 		if (!name) {
257 			goto Cleanup;
258 		}
259 
260 		lResult = RegSetValueEx(hKey,					// Handle to key
261 			name,					// The values name
262 			0,						// DWORD reserved
263 			REG_DWORD,				// null terminated string
264 			(CONST BYTE *)&value,	// value to set
265 			4);						// How many bytes to set
266 
267 		if (lResult != ERROR_SUCCESS) {
268 			goto Cleanup;
269 		}
270 
271 	Cleanup:
272 		if (hKey)
273 			RegCloseKey(hKey);
274 
275 	}
276 
277 	// Reads a string from the INI file.  If default is passed,
278 	// and the string isn't found, returns ptr to default otherwise
279 	// returns NULL;    Copy the return value somewhere before
280 	// calling os_read_string again, because it might reuse the
281 	// same buffer.
282 	static char registry_tmp_string_data[1024];
registry_read_string(const char * section,const char * name,const char * default_value)283 	const char * registry_read_string(const char *section, const char *name, const char *default_value)
284 	{
285 		HKEY hKey = NULL;
286 		DWORD dwType, dwLen;
287 		char keyname[1024];
288 		LONG lResult;
289 
290 		if (!Os_reg_inited) {
291 			return NULL;
292 		}
293 
294 		HKEY useHKey = get_registry_keyname(keyname, section, Cmdline_alternate_registry_path);
295 
296 		lResult = RegOpenKeyEx(useHKey,			// Where it is
297 			keyname,			// name of key
298 			0,					// DWORD reserved
299 			KEY_QUERY_VALUE,	// Allows all changes
300 			&hKey);			// Location to store key
301 
302 		if (lResult != ERROR_SUCCESS) {
303 			goto Cleanup;
304 		}
305 
306 		if (!name) {
307 			goto Cleanup;
308 		}
309 
310 		dwLen = 1024;
311 		lResult = RegQueryValueEx(hKey,									// Handle to key
312 			name,									// The values name
313 			NULL,									// DWORD reserved
314 			&dwType,								// What kind it is
315 			(ubyte *)&registry_tmp_string_data,				// value to set
316 			&dwLen);								// How many bytes to set
317 
318 		if (lResult != ERROR_SUCCESS) {
319 			goto Cleanup;
320 		}
321 
322 		default_value = registry_tmp_string_data;
323 
324 	Cleanup:
325 		if (hKey)
326 			RegCloseKey(hKey);
327 
328 		return default_value;
329 	}
330 
331 	// Reads a string from the INI file.  Default_value must
332 	// be passed, and if 'name' isn't found, then returns default_value
registry_read_uint(const char * section,const char * name,uint default_value)333 	uint registry_read_uint(const char *section, const char *name, uint default_value)
334 	{
335 		HKEY hKey = NULL;
336 		DWORD dwType, dwLen;
337 		char keyname[1024];
338 		LONG lResult;
339 		uint tmp_val;
340 
341 		if (!Os_reg_inited) {
342 			return default_value;
343 		}
344 
345 		HKEY useHKey = get_registry_keyname(keyname, section, Cmdline_alternate_registry_path);
346 
347 		lResult = RegOpenKeyEx(useHKey,			// Where it is
348 			keyname,			// name of key
349 			0,					// DWORD reserved
350 			KEY_QUERY_VALUE,	// Allows all changes
351 			&hKey);			// Location to store key
352 
353 		if (lResult != ERROR_SUCCESS) {
354 			goto Cleanup;
355 		}
356 
357 		if (!name) {
358 			goto Cleanup;
359 		}
360 
361 		dwLen = 4;
362 		lResult = RegQueryValueEx(hKey,				// Handle to key
363 			name,				// The values name
364 			NULL,				// DWORD reserved
365 			&dwType,			// What kind it is
366 			(ubyte *)&tmp_val,	// value to set
367 			&dwLen);			// How many bytes to set
368 
369 		if (lResult != ERROR_SUCCESS) {
370 			goto Cleanup;
371 		}
372 
373 		default_value = tmp_val;
374 
375 	Cleanup:
376 		if (hKey)
377 			RegCloseKey(hKey);
378 
379 		return default_value;
380 	}
381 #endif
382 }
383 
384 #ifdef WIN32
385 
filetime_to_timet(const FILETIME & ft)386 static time_t filetime_to_timet(const FILETIME& ft)
387 {
388 	ULARGE_INTEGER ull;
389 	ull.LowPart = ft.dwLowDateTime;
390 	ull.HighPart = ft.dwHighDateTime;
391 	return ull.QuadPart / 10000000ULL - 11644473600ULL;
392 }
393 
key_mod_time(bool alternate_path)394 static time_t key_mod_time(bool alternate_path) {
395 	char keyname[1024];
396 
397 	HKEY useHKey = get_registry_keyname(keyname, nullptr, alternate_path);
398 
399 	HKEY hKey = nullptr;
400 	auto lResult = RegOpenKeyEx(useHKey,            // Where it is
401 								keyname,            // name of key
402 								0,                    // DWORD reserved
403 								KEY_QUERY_VALUE,    // Allows all changes
404 								&hKey);            // Location to store key
405 
406 	if (lResult != ERROR_SUCCESS) {
407 		::RegCloseKey(hKey);
408 		return 0;
409 	}
410 
411 	FILETIME time;
412 	lResult = RegQueryInfoKey(hKey,
413 		nullptr,
414 		nullptr,
415 		nullptr,
416 		nullptr,
417 		nullptr,
418 		nullptr,
419 		nullptr,
420 		nullptr,
421 		nullptr,
422 		nullptr,
423 		&time);
424 	::RegCloseKey(hKey);
425 
426 	if (lResult != ERROR_SUCCESS) {
427 		return 0;
428 	}
429 	return filetime_to_timet(time);
430 }
431 
os_registry_get_last_modification_time()432 time_t os_registry_get_last_modification_time() {
433 	auto standard_time = key_mod_time(false);
434 	auto alternate_time = key_mod_time(true);
435 
436 	return std::max(standard_time, alternate_time);
437 }
438 
439 #endif
440 
441 // ------------------------------------------------------------------------------------------------------------
442 // REGISTRY DEFINES/VARS
443 //
444 #ifdef SCP_UNIX
445 // Initialize path of old pilot files
446 #ifdef __APPLE__
447 const char* Osreg_user_dir_legacy = "Library/FS2_Open";
448 #else
449 const char* Osreg_user_dir_legacy = ".fs2_open";
450 #endif // __APPLE__
451 #endif
452 
453 const char *Osreg_company_name = "Volition";
454 const char *Osreg_class_name = "FreeSpace2Class";
455 
456 const char *Osreg_app_name = "FreeSpace2";
457 const char *Osreg_title = "FreeSpace 2";
458 
459 const char *Osreg_config_file_name = "fs2_open.ini";
460 
461 #define DEFAULT_SECTION "Default"
462 
463 typedef struct KeyValue
464 {
465 	char *key;
466 	char *value;
467 
468 	struct KeyValue *next;
469 } KeyValue;
470 
471 typedef struct Section
472 {
473 	char *name;
474 
475 	struct KeyValue *pairs;
476 	struct Section *next;
477 } Section;
478 
479 typedef struct Profile
480 {
481 	struct Section *sections;
482 } Profile;
483 
484 // For string config functions
485 static char tmp_string_data[1024];
486 
487 // This code is needed for compatibility with the old windows registry
488 
read_line_from_file(FILE * fp)489 static char *read_line_from_file(FILE *fp)
490 {
491 	char *buf, *buf_start;
492 	int buflen, eol;
493 
494 	buflen = 80;
495 	buf = (char *)vm_malloc(buflen);
496 	buf_start = buf;
497 	eol = 0;
498 
499 	do {
500 		if (buf == NULL) {
501 			return NULL;
502 		}
503 
504 		if (fgets(buf_start, 80, fp) == NULL) {
505 			if (buf_start == buf) {
506 				vm_free(buf);
507 				return NULL;
508 			}
509 			else {
510 				*buf_start = 0;
511 				return buf;
512 			}
513 		}
514 
515 		auto len = strlen(buf_start);
516 
517 		if (buf_start[len - 1] == '\n') {
518 			buf_start[len - 1] = 0;
519 			eol = 1;
520 		}
521 		else {
522 			buflen += 80;
523 
524 			buf = (char *)vm_realloc(buf, buflen);
525 
526 			/* be sure to skip over the proper amount of nulls */
527 			buf_start = buf + (buflen - 80) - (buflen / 80) + 1;
528 		}
529 	} while (!eol);
530 
531 	return buf;
532 }
533 
trim_string(char * str)534 static char *trim_string(char *str)
535 {
536 	char *ptr;
537 
538 	if (str == NULL)
539 		return NULL;
540 
541 	/* kill any comment */
542 	ptr = strchr(str, ';');
543 	if (ptr)
544 		*ptr = 0;
545 	ptr = strchr(str, '#');
546 	if (ptr)
547 		*ptr = 0;
548 
549 	ptr = str;
550 	auto len = strlen(str);
551 	if (len > 0) {
552 		ptr += len - 1;
553 	}
554 
555 	while ((ptr > str) && isspace(*ptr)) {
556 		ptr--;
557 	}
558 
559 	if (*ptr) {
560 		ptr++;
561 		*ptr = 0;
562 	}
563 
564 	ptr = str;
565 	while (*ptr && isspace(*ptr)) {
566 		ptr++;
567 	}
568 
569 	return ptr;
570 }
571 
profile_read(const char * file)572 static Profile *profile_read(const char *file)
573 {
574 	FILE *fp = NULL;
575 	char *str;
576 
577 	if (os_is_legacy_mode()) {
578 #ifdef WIN32
579 		return nullptr; // No config file in legacy mode
580 #else
581 		// Try to use the config file at the old location
582 		char legacy_path[MAX_PATH_LEN];
583 		snprintf(legacy_path, MAX_PATH_LEN, "%s/%s/%s", getenv("HOME"), Osreg_user_dir_legacy, file);
584 
585 		fp = fopen(legacy_path, "rt");
586 #endif
587 	}
588 	else {
589 		fp = fopen(os_get_config_path(file).c_str(), "rt");
590 	}
591 
592 	if (fp == NULL)
593 		return NULL;
594 
595 	Profile *profile = (Profile *)vm_malloc(sizeof(Profile));
596 	profile->sections = NULL;
597 
598 	Section **sp_ptr = &(profile->sections);
599 	Section *sp = NULL;
600 
601 	KeyValue **kvp_ptr = NULL;
602 
603 	while ((str = read_line_from_file(fp)) != NULL) {
604 		char *ptr = trim_string(str);
605 
606 		if (*ptr == '[') {
607 			ptr++;
608 
609 			char *pend = strchr(ptr, ']');
610 			if (pend != NULL) {
611 				// if (pend[1]) { /* trailing garbage! */ }
612 
613 				*pend = 0;
614 
615 				if (*ptr) {
616 					sp = (Section *)vm_malloc(sizeof(Section));
617 					sp->next = NULL;
618 
619 					sp->name = vm_strdup(ptr);
620 					sp->pairs = NULL;
621 
622 					*sp_ptr = sp;
623 					sp_ptr = &(sp->next);
624 
625 					kvp_ptr = &(sp->pairs);
626 				} // else { /* null name! */ }
627 			} // else { /* incomplete section name! */ }
628 		}
629 		else {
630 			if (*ptr) {
631 				char *key = ptr;
632 				char *value = NULL;
633 
634 				ptr = strchr(ptr, '=');
635 				if (ptr != NULL) {
636 					*ptr = 0;
637 					ptr++;
638 
639 					value = ptr;
640 				} // else { /* random garbage! */ }
641 
642 				if (key && *key && value /* && *value */) {
643 					if (sp != NULL) {
644 						KeyValue *kvp = (KeyValue *)vm_malloc(sizeof(KeyValue));
645 
646 						kvp->key = vm_strdup(key);
647 						kvp->value = vm_strdup(value);
648 
649 						kvp->next = NULL;
650 
651 						*kvp_ptr = kvp;
652 						kvp_ptr = &(kvp->next);
653 					} // else { /* key/value with no section! */
654 				} // else { /* malformed key/value entry! */ }
655 			} // else it's just a comment or empty string
656 		}
657 
658 		vm_free(str);
659 	}
660 
661 	fclose(fp);
662 
663 	return profile;
664 }
665 
profile_free(Profile * profile)666 static void profile_free(Profile *profile)
667 {
668 	if (profile == NULL)
669 		return;
670 
671 	Section *sp = profile->sections;
672 	while (sp != NULL) {
673 		Section *st = sp;
674 		KeyValue *kvp = sp->pairs;
675 
676 		while (kvp != NULL) {
677 			KeyValue *kvt = kvp;
678 
679 			vm_free(kvp->key);
680 			vm_free(kvp->value);
681 
682 			kvp = kvp->next;
683 			vm_free(kvt);
684 		}
685 
686 		vm_free(sp->name);
687 
688 		sp = sp->next;
689 		vm_free(st);
690 	}
691 
692 	vm_free(profile);
693 }
694 
profile_update(Profile * profile,const char * section,const char * key,const char * value)695 static Profile *profile_update(Profile *profile, const char *section, const char *key, const char *value)
696 {
697 	if (profile == NULL) {
698 		profile = (Profile *)vm_malloc(sizeof(Profile));
699 
700 		profile->sections = NULL;
701 	}
702 
703 	KeyValue *kvp;
704 
705 	Section **sp_ptr = &(profile->sections);
706 	Section *sp = profile->sections;
707 
708 	while (sp != NULL) {
709 		if (strcmp(section, sp->name) == 0) {
710 			KeyValue **kvp_ptr = &(sp->pairs);
711 			kvp = sp->pairs;
712 
713 			while (kvp != NULL) {
714 				if (strcmp(key, kvp->key) == 0) {
715 					vm_free(kvp->value);
716 
717 					if (value == NULL) {
718 						*kvp_ptr = kvp->next;
719 
720 						vm_free(kvp->key);
721 						vm_free(kvp);
722 					}
723 					else {
724 						kvp->value = vm_strdup(value);
725 					}
726 
727 					/* all done */
728 					return profile;
729 				}
730 
731 				kvp_ptr = &(kvp->next);
732 				kvp = kvp->next;
733 			}
734 
735 			if (value != NULL) {
736 				/* key not found */
737 				kvp = (KeyValue *)vm_malloc(sizeof(KeyValue));
738 				kvp->next = NULL;
739 				kvp->key = vm_strdup(key);
740 				kvp->value = vm_strdup(value);
741 			}
742 
743 			*kvp_ptr = kvp;
744 
745 			/* all done */
746 			return profile;
747 		}
748 
749 		sp_ptr = &(sp->next);
750 		sp = sp->next;
751 	}
752 
753 	/* section not found */
754 	sp = (Section *)vm_malloc(sizeof(Section));
755 	sp->next = NULL;
756 	sp->name = vm_strdup(section);
757 
758 	kvp = (KeyValue *)vm_malloc(sizeof(KeyValue));
759 	kvp->next = NULL;
760 	kvp->key = vm_strdup(key);
761 	kvp->value = vm_strdup(value);
762 
763 	sp->pairs = kvp;
764 
765 	*sp_ptr = sp;
766 
767 	return profile;
768 }
769 
profile_get_value(Profile * profile,const char * section,const char * key)770 static char *profile_get_value(Profile *profile, const char *section, const char *key)
771 {
772 	if (profile == NULL)
773 		return NULL;
774 
775 	Section *sp = profile->sections;
776 
777 	while (sp != NULL) {
778 		if (stricmp(section, sp->name) == 0) {
779 			KeyValue *kvp = sp->pairs;
780 
781 			while (kvp != NULL) {
782 				if (strcmp(key, kvp->key) == 0) {
783 					return kvp->value;
784 				}
785 				kvp = kvp->next;
786 			}
787 		}
788 
789 		sp = sp->next;
790 	}
791 
792 	/* not found */
793 	return NULL;
794 }
795 
profile_save(Profile * profile,const char * file)796 static void profile_save(Profile *profile, const char *file)
797 {
798 	FILE *fp = NULL;
799 	char tmp[MAX_PATH] = "";
800 	char tmp2[MAX_PATH] = "";
801 
802 	if (profile == NULL)
803 		return;
804 
805 	fp = fopen(os_get_config_path(file).c_str(), "wt");
806 
807 	if (fp == NULL)
808 		return;
809 
810 	Section *sp = profile->sections;
811 
812 	while (sp != NULL) {
813 		sprintf(tmp, NOX("[%s]\n"), sp->name);
814 		fputs(tmp, fp);
815 
816 		KeyValue *kvp = sp->pairs;
817 		while (kvp != NULL) {
818 			sprintf(tmp2, NOX("%s=%s\n"), kvp->key, kvp->value);
819 			fputs(tmp2, fp);
820 			kvp = kvp->next;
821 		}
822 
823 		fprintf(fp, "\n");
824 
825 		sp = sp->next;
826 	}
827 
828 	fclose(fp);
829 }
830 
831 // os registry functions -------------------------------------------------------------
832 
833 static Profile* Osreg_profile = nullptr;
834 
835 // initialize the registry. setup default keys to use
os_init_registry_stuff(const char * company,const char * app)836 void os_init_registry_stuff(const char *company, const char *app)
837 {
838 	if (company) {
839 		strcpy_s(szCompanyName, company);
840 	}
841 	else {
842 		strcpy_s(szCompanyName, Osreg_company_name);
843 	}
844 
845 	if (app) {
846 		strcpy_s(szAppName, app);
847 	}
848 	else {
849 		strcpy_s(szAppName, Osreg_app_name);
850 	}
851 
852 	Osreg_profile = profile_read(Osreg_config_file_name);
853 
854 	Os_reg_inited = 1;
855 }
os_deinit_registry_stuff()856 void os_deinit_registry_stuff()
857 {
858 	if (Osreg_profile != nullptr) {
859 		profile_free(Osreg_profile);
860 		Osreg_profile = nullptr;
861 	}
862 }
os_config_has_value(const char * section,const char * name)863 bool os_config_has_value(const char* section, const char* name) {
864 #ifdef WIN32
865 	if (Osreg_profile == nullptr) {
866 		// No config file, fall back to registy
867 		return registry_read_string(section, name, nullptr) != nullptr;
868 	}
869 #endif
870 	if (section == nullptr)
871 		section = DEFAULT_SECTION;
872 
873 	char *ptr = profile_get_value(Osreg_profile, section, name);
874 
875 	return ptr != nullptr;
876 }
877 
os_config_read_string(const char * section,const char * name,const char * default_value)878 const char *os_config_read_string(const char *section, const char *name, const char *default_value)
879 {
880 #ifdef WIN32
881 	if (Osreg_profile == nullptr) {
882 		// No config file, fall back to registy
883 		return registry_read_string(section, name, default_value);
884 	}
885 #endif
886 	nprintf(("Registry", "os_config_read_string(): section = \"%s\", name = \"%s\", default value: \"%s\"\n",
887 		(section) ? section : DEFAULT_SECTION, name, (default_value) ? default_value : NOX("NULL")));
888 
889 	if (section == NULL)
890 		section = DEFAULT_SECTION;
891 
892 	char *ptr = profile_get_value(Osreg_profile, section, name);
893 
894 	if (ptr != NULL) {
895 		strncpy(tmp_string_data, ptr, 1023);
896 		default_value = tmp_string_data;
897 	}
898 
899 	return default_value;
900 }
901 
os_config_read_uint(const char * section,const char * name,unsigned int default_value)902 unsigned int os_config_read_uint(const char *section, const char *name, unsigned int default_value)
903 {
904 #ifdef WIN32
905 	if (Osreg_profile == nullptr) {
906 		// No config file, fall back to registy
907 		return registry_read_uint(section, name, default_value);
908 	}
909 #endif
910 
911 	if (section == NULL)
912 		section = DEFAULT_SECTION;
913 
914 	char *ptr = profile_get_value(Osreg_profile, section, name);
915 
916 	if (ptr != NULL) {
917 		default_value = atoi(ptr);
918 	}
919 
920 	return default_value;
921 }
922 
os_config_write_string(const char * section,const char * name,const char * value)923 void os_config_write_string(const char *section, const char *name, const char *value)
924 {
925 #ifdef WIN32
926 	// When there is no config file then it shouldn't be created because that would "hide" all previous settings
927 	// Instead fall back to writing the settings to the config file
928 	if (Osreg_profile == nullptr) {
929 		registry_write_string(section, name, value);
930 		return;
931 	}
932 #endif
933 
934 	if (section == NULL)
935 		section = DEFAULT_SECTION;
936 
937 	Osreg_profile = profile_update(Osreg_profile, section, name, value);
938 	profile_save(Osreg_profile, Osreg_config_file_name);
939 }
940 
os_config_write_uint(const char * section,const char * name,unsigned int value)941 void os_config_write_uint(const char *section, const char *name, unsigned int value)
942 {
943 #ifdef WIN32
944 	// When there is no config file then it shouldn't be created because that would "hide" all previous settings
945 	// Instead fall back to writing the settings to the config file
946 	if (Osreg_profile == nullptr) {
947 		registry_write_uint(section, name, value);
948 		return;
949 	}
950 #endif
951 
952 	if (section == NULL)
953 		section = DEFAULT_SECTION;
954 
955 	char buf[21];
956 
957 	snprintf(buf, 20, "%u", value);
958 
959 	Osreg_profile = profile_update(Osreg_profile, section, name, buf);
960 	profile_save(Osreg_profile, Osreg_config_file_name);
961 }
962 
963