1 /*
2 ** gameconfigfile.cpp
3 ** An .ini parser specifically for zdoom.ini
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2008 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OFf
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include <stdio.h>
36 #include <time.h>
37 
38 #ifdef __APPLE__
39 #include <CoreServices/CoreServices.h>
40 #endif
41 
42 #ifdef _WIN32
43 #define WIN32_LEAN_AND_MEAN
44 #include <windows.h>
45 extern HWND Window;
46 #define USE_WINDOWS_DWORD
47 #endif
48 
49 #include "doomdef.h"
50 #include "gameconfigfile.h"
51 #include "c_cvars.h"
52 #include "c_dispatch.h"
53 #include "c_bind.h"
54 #include "gstrings.h"
55 #include "m_argv.h"
56 #include "cmdlib.h"
57 #include "version.h"
58 #include "m_misc.h"
59 #include "v_font.h"
60 #include "a_pickups.h"
61 #include "doomstat.h"
62 #include "i_system.h"
63 #include "gi.h"
64 #include "d_main.h"
65 
EXTERN_CVAR(Bool,con_centernotify)66 EXTERN_CVAR (Bool, con_centernotify)
67 EXTERN_CVAR (Int, msg0color)
68 EXTERN_CVAR (Color, color)
69 EXTERN_CVAR (Float, dimamount)
70 EXTERN_CVAR (Int, msgmidcolor)
71 EXTERN_CVAR (Int, msgmidcolor2)
72 EXTERN_CVAR (Bool, snd_pitched)
73 EXTERN_CVAR (Color, am_wallcolor)
74 EXTERN_CVAR (Color, am_fdwallcolor)
75 EXTERN_CVAR (Color, am_cdwallcolor)
76 EXTERN_CVAR (Float, spc_amp)
77 EXTERN_CVAR (Bool, wi_percents)
78 
79 FGameConfigFile::FGameConfigFile ()
80 {
81 #ifdef __APPLE__
82 	FString user_docs, user_app_support, local_app_support;
83 #endif
84 	FString pathname;
85 
86 	OkayToWrite = false;	// Do not allow saving of the config before DoGameSetup()
87 	bModSetup = false;
88 	pathname = GetConfigPath (true);
89 	ChangePathName (pathname);
90 	LoadConfigFile ();
91 
92 	// If zdoom.ini was read from the program directory, switch
93 	// to the user directory now. If it was read from the user
94 	// directory, this effectively does nothing.
95 	pathname = GetConfigPath (false);
96 	ChangePathName (pathname);
97 
98 	// Set default IWAD search paths if none present
99 	if (!SetSection ("IWADSearch.Directories"))
100 	{
101 		SetSection ("IWADSearch.Directories", true);
102 		SetValueForKey ("Path", ".", true);
103 		SetValueForKey ("Path", "$DOOMWADDIR", true);
104 #ifdef __APPLE__
105 		char cpath[PATH_MAX];
106 		FSRef folder;
107 
108 		if (noErr == FSFindFolder(kUserDomain, kDocumentsFolderType, kCreateFolder, &folder) &&
109 			noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
110 		{
111 			user_docs << cpath << "/" GAME_DIR;
112 			SetValueForKey("Path", user_docs, true);
113 		}
114 		else
115 		{
116 			SetValueForKey("Path", "~/" GAME_DIR, true);
117 		}
118 		if (noErr == FSFindFolder(kUserDomain, kApplicationSupportFolderType, kCreateFolder, &folder) &&
119 			noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
120 		{
121 			user_app_support << cpath << "/" GAME_DIR;
122 			SetValueForKey("Path", user_app_support, true);
123 		}
124 		SetValueForKey ("Path", "$PROGDIR", true);
125 		if (noErr == FSFindFolder(kLocalDomain, kApplicationSupportFolderType, kCreateFolder, &folder) &&
126 			noErr == FSRefMakePath(&folder, (UInt8*)cpath, PATH_MAX))
127 		{
128 			local_app_support << cpath << "/" GAME_DIR;
129 			SetValueForKey("Path", local_app_support, true);
130 		}
131 #elif !defined(__unix__)
132 		SetValueForKey ("Path", "$HOME", true);
133 		SetValueForKey ("Path", "$PROGDIR", true);
134 #else
135 		SetValueForKey ("Path", "~/" GAME_DIR, true);
136 		// Arch Linux likes them in /usr/share/doom
137 		// Debian likes them in /usr/share/games/doom
138 		// I assume other distributions don't do anything radically different
139 		SetValueForKey ("Path", "/usr/local/share/doom", true);
140 		SetValueForKey ("Path", "/usr/local/share/games/doom", true);
141 		SetValueForKey ("Path", "/usr/share/doom", true);
142 		SetValueForKey ("Path", "/usr/share/games/doom", true);
143 #endif
144 	}
145 
146 	// Set default search paths if none present
147 	if (!SetSection ("FileSearch.Directories"))
148 	{
149 		SetSection ("FileSearch.Directories", true);
150 #ifdef __APPLE__
151 		SetValueForKey ("Path", user_docs, true);
152 		SetValueForKey ("Path", user_app_support, true);
153 		SetValueForKey ("Path", "$PROGDIR", true);
154 		SetValueForKey ("Path", local_app_support, true);
155 #elif !defined(__unix__)
156 		SetValueForKey ("Path", "$PROGDIR", true);
157 #else
158 		SetValueForKey ("Path", "~/" GAME_DIR, true);
159 		SetValueForKey ("Path", SHARE_DIR, true);
160 #endif
161 		SetValueForKey ("Path", "$DOOMWADDIR", true);
162 	}
163 
164 	// Add some self-documentation.
165 	SetSectionNote("IWADSearch.Directories",
166 		"# These are the directories to automatically search for IWADs.\n"
167 		"# Each directory should be on a separate line, preceded by Path=\n");
168 	SetSectionNote("FileSearch.Directories",
169 		"# These are the directories to search for wads added with the -file\n"
170 		"# command line parameter, if they cannot be found with the path\n"
171 		"# as-is. Layout is the same as for IWADSearch.Directories\n");
172 }
173 
~FGameConfigFile()174 FGameConfigFile::~FGameConfigFile ()
175 {
176 }
177 
WriteCommentHeader(FILE * file) const178 void FGameConfigFile::WriteCommentHeader (FILE *file) const
179 {
180 	fprintf (file, "# This file was generated by " GAMENAME " %s on %s\n", GetVersionString(), myasctime());
181 }
182 
DoAutoloadSetup(FIWadManager * iwad_man)183 void FGameConfigFile::DoAutoloadSetup (FIWadManager *iwad_man)
184 {
185 	// Create auto-load sections, so users know what's available.
186 	// Note that this totem pole is the reverse of the order that
187 	// they will appear in the file.
188 
189 	double last = 0;
190 	if (SetSection ("LastRun"))
191 	{
192 		const char *lastver = GetValueForKey ("Version");
193 		if (lastver != NULL) last = atof(lastver);
194 	}
195 
196 	if (last < 211)
197 	{
198 		RenameSection("Chex3.Autoload", "chex.chex3.Autoload");
199 		RenameSection("Chex1.Autoload", "chex.chex1.Autoload");
200 		RenameSection("HexenDK.Autoload", "hexen.deathkings.Autoload");
201 		RenameSection("HereticSR.Autoload", "heretic.shadow.Autoload");
202 		RenameSection("FreeDM.Autoload", "doom.freedoom.freedm.Autoload");
203 		RenameSection("Freedoom2.Autoload", "doom.freedoom.phase2.Autoload");
204 		RenameSection("Freedoom1.Autoload", "doom.freedoom.phase1.Autoload");
205 		RenameSection("Freedoom.Autoload", "doom.freedoom.Autoload");
206 		RenameSection("DoomBFG.Autoload", "doom.doom1.bfg.Autoload");
207 		RenameSection("DoomU.Autoload", "doom.doom1.ultimate.Autoload");
208 		RenameSection("Doom1.Autoload", "doom.doom1.registered.Autoload");
209 		RenameSection("TNT.Autoload", "doom.doom2.tnt.Autoload");
210 		RenameSection("Plutonia.Autoload", "doom.doom2.plutonia.Autoload");
211 		RenameSection("Doom2BFG.Autoload", "doom.doom2.bfg.Autoload");
212 		RenameSection("Doom2.Autoload", "doom.doom2.commercial.Autoload");
213 	}
214 	const FString *pAuto;
215 	for (int num = 0; (pAuto = iwad_man->GetAutoname(num)) != NULL; num++)
216 	{
217 		if (!(iwad_man->GetIWadFlags(num) & GI_SHAREWARE))	// we do not want autoload sections for shareware IWADs (which may have an autoname for resource filtering)
218 		{
219 			FString workname = *pAuto;
220 
221 			while (workname.IsNotEmpty())
222 			{
223 				FString section = workname + ".Autoload";
224 				CreateSectionAtStart(section.GetChars());
225 				long dotpos = workname.LastIndexOf('.');
226 				if (dotpos < 0) break;
227 				workname.Truncate(dotpos);
228 			}
229 		}
230 	}
231 	CreateSectionAtStart("Global.Autoload");
232 
233 	// The same goes for auto-exec files.
234 	CreateStandardAutoExec("Chex.AutoExec", true);
235 	CreateStandardAutoExec("Strife.AutoExec", true);
236 	CreateStandardAutoExec("Hexen.AutoExec", true);
237 	CreateStandardAutoExec("Heretic.AutoExec", true);
238 	CreateStandardAutoExec("Doom.AutoExec", true);
239 
240 	// Move search paths back to the top.
241 	MoveSectionToStart("FileSearch.Directories");
242 	MoveSectionToStart("IWADSearch.Directories");
243 
244 	SetSectionNote("Doom.AutoExec",
245 		"# Files to automatically execute when running the corresponding game.\n"
246 		"# Each file should be on its own line, preceded by Path=\n\n");
247 	SetSectionNote("Global.Autoload",
248 		"# WAD files to always load. These are loaded after the IWAD but before\n"
249 		"# any files added with -file. Place each file on its own line, preceded\n"
250 		"# by Path=\n");
251 	SetSectionNote("Doom.Autoload",
252 		"# Wad files to automatically load depending on the game and IWAD you are\n"
253 		"# playing.  You may have have files that are loaded for all similar IWADs\n"
254 		"# (the game) and files that are only loaded for particular IWADs. For example,\n"
255 		"# any files listed under 'doom.Autoload' will be loaded for any version of Doom,\n"
256 		"# but files listed under 'doom.doom2.Autoload' will only load when you are\n"
257 		"# playing a Doom 2 based game (doom2.wad, tnt.wad or plutonia.wad), and files listed under\n"
258 		"# 'doom.doom2.commercial.Autoload' only when playing doom2.wad.\n\n");
259 }
260 
DoGlobalSetup()261 void FGameConfigFile::DoGlobalSetup ()
262 {
263 	if (SetSection ("GlobalSettings.Unknown"))
264 	{
265 		ReadCVars (CVAR_GLOBALCONFIG);
266 	}
267 	if (SetSection ("GlobalSettings"))
268 	{
269 		ReadCVars (CVAR_GLOBALCONFIG);
270 	}
271 	if (SetSection ("LastRun"))
272 	{
273 		const char *lastver = GetValueForKey ("Version");
274 		if (lastver != NULL)
275 		{
276 			double last = atof (lastver);
277 			if (last < 123.1)
278 			{
279 				FBaseCVar *noblitter = FindCVar ("vid_noblitter", NULL);
280 				if (noblitter != NULL)
281 				{
282 					noblitter->ResetToDefault ();
283 				}
284 			}
285 			if (last < 202)
286 			{
287 				// Make sure the Hexen hotkeys are accessible by default.
288 				if (SetSection ("Hexen.Bindings"))
289 				{
290 					SetValueForKey ("\\", "use ArtiHealth");
291 					SetValueForKey ("scroll", "+showscores");
292 					SetValueForKey ("0", "useflechette");
293 					SetValueForKey ("9", "use ArtiBlastRadius");
294 					SetValueForKey ("8", "use ArtiTeleport");
295 					SetValueForKey ("7", "use ArtiTeleportOther");
296 					SetValueForKey ("6", "use ArtiPork");
297 					SetValueForKey ("5", "use ArtiInvulnerability2");
298 				}
299 			}
300 			if (last < 204)
301 			{ // The old default for vsync was true, but with an unlimited framerate
302 			  // now, false is a better default.
303 				FBaseCVar *vsync = FindCVar ("vid_vsync", NULL);
304 				if (vsync != NULL)
305 				{
306 					vsync->ResetToDefault ();
307 				}
308 			}
309 			if (last < 206)
310 			{ // spc_amp is now a float, not an int.
311 				if (spc_amp > 16)
312 				{
313 					spc_amp = spc_amp / 16.f;
314 				}
315 			}
316 			if (last < 207)
317 			{ // Now that snd_midiprecache works again, you probably don't want it on.
318 				FBaseCVar *precache = FindCVar ("snd_midiprecache", NULL);
319 				if (precache != NULL)
320 				{
321 					precache->ResetToDefault();
322 				}
323 			}
324 			if (last < 208)
325 			{ // Weapon sections are no longer used, so tidy up the config by deleting them.
326 				const char *name;
327 				size_t namelen;
328 				bool more;
329 
330 				more = SetFirstSection();
331 				while (more)
332 				{
333 					name = GetCurrentSection();
334 					if (name != NULL &&
335 						(namelen = strlen(name)) > 12 &&
336 						strcmp(name + namelen - 12, ".WeaponSlots") == 0)
337 					{
338 						more = DeleteCurrentSection();
339 					}
340 					else
341 					{
342 						more = SetNextSection();
343 					}
344 				}
345 			}
346 			if (last < 209)
347 			{
348 				// menu dimming is now a gameinfo option so switch user override off
349 				FBaseCVar *dim = FindCVar ("dimamount", NULL);
350 				if (dim != NULL)
351 				{
352 					dim->ResetToDefault ();
353 				}
354 			}
355 			if (last < 210)
356 			{
357 				if (SetSection ("Hexen.Bindings"))
358 				{
359 					// These 2 were misnamed in earlier versions
360 					SetValueForKey ("6", "use ArtiPork");
361 					SetValueForKey ("5", "use ArtiInvulnerability2");
362 				}
363 			}
364 		}
365 	}
366 }
367 
DoGameSetup(const char * gamename)368 void FGameConfigFile::DoGameSetup (const char *gamename)
369 {
370 	const char *key;
371 	const char *value;
372 
373 	sublen = countof(section) - 1 - mysnprintf (section, countof(section), "%s.", gamename);
374 	subsection = section + countof(section) - sublen - 1;
375 	section[countof(section) - 1] = '\0';
376 
377 	strncpy (subsection, "UnknownConsoleVariables", sublen);
378 	if (SetSection (section))
379 	{
380 		ReadCVars (0);
381 	}
382 
383 	strncpy (subsection, "ConsoleVariables", sublen);
384 	if (SetSection (section))
385 	{
386 		ReadCVars (0);
387 	}
388 
389 	if (gameinfo.gametype & GAME_Raven)
390 	{
391 		SetRavenDefaults (gameinfo.gametype == GAME_Hexen);
392 	}
393 
394 	// The NetServerInfo section will be read and override anything loaded
395 	// here when it's determined that a netgame is being played.
396 	strncpy (subsection, "LocalServerInfo", sublen);
397 	if (SetSection (section))
398 	{
399 		ReadCVars (0);
400 	}
401 
402 	strncpy (subsection, "Player", sublen);
403 	if (SetSection (section))
404 	{
405 		ReadCVars (0);
406 	}
407 
408 	strncpy (subsection, "ConsoleAliases", sublen);
409 	if (SetSection (section))
410 	{
411 		const char *name = NULL;
412 		while (NextInSection (key, value))
413 		{
414 			if (stricmp (key, "Name") == 0)
415 			{
416 				name = value;
417 			}
418 			else if (stricmp (key, "Command") == 0 && name != NULL)
419 			{
420 				C_SetAlias (name, value);
421 				name = NULL;
422 			}
423 		}
424 	}
425 	OkayToWrite = true;
426 }
427 
428 // Moved from DoGameSetup so that it can happen after wads are loaded
DoKeySetup(const char * gamename)429 void FGameConfigFile::DoKeySetup(const char *gamename)
430 {
431 	static const struct { const char *label; FKeyBindings *bindings; } binders[] =
432 	{
433 		{ "Bindings", &Bindings },
434 		{ "DoubleBindings", &DoubleBindings },
435 		{ "AutomapBindings", &AutomapBindings },
436 		{ NULL, NULL }
437 	};
438 	const char *key, *value;
439 
440 	sublen = countof(section) - 1 - mysnprintf(section, countof(section), "%s.", gamename);
441 	subsection = section + countof(section) - sublen - 1;
442 	section[countof(section) - 1] = '\0';
443 
444 	C_SetDefaultBindings ();
445 
446 	for (int i = 0; binders[i].label != NULL; ++i)
447 	{
448 		strncpy(subsection, binders[i].label, sublen);
449 		if (SetSection(section))
450 		{
451 			FKeyBindings *bindings = binders[i].bindings;
452 			bindings->UnbindAll();
453 			while (NextInSection(key, value))
454 			{
455 				bindings->DoBind(key, value);
456 			}
457 		}
458 	}
459 }
460 
461 // Like DoGameSetup(), but for mod-specific cvars.
462 // Called after CVARINFO has been parsed.
DoModSetup(const char * gamename)463 void FGameConfigFile::DoModSetup(const char *gamename)
464 {
465 	mysnprintf(section, countof(section), "%s.Player.Mod", gamename);
466 	if (SetSection(section))
467 	{
468 		ReadCVars(CVAR_MOD|CVAR_USERINFO|CVAR_IGNORE);
469 	}
470 	mysnprintf(section, countof(section), "%s.LocalServerInfo.Mod", gamename);
471 	if (SetSection (section))
472 	{
473 		ReadCVars (CVAR_MOD|CVAR_SERVERINFO|CVAR_IGNORE);
474 	}
475 	// Signal that these sections should be rewritten when saving the config.
476 	bModSetup = true;
477 }
478 
ReadNetVars()479 void FGameConfigFile::ReadNetVars ()
480 {
481 	strncpy (subsection, "NetServerInfo", sublen);
482 	if (SetSection (section))
483 	{
484 		ReadCVars (0);
485 	}
486 	if (bModSetup)
487 	{
488 		mysnprintf(subsection, sublen, "NetServerInfo.Mod");
489 		if (SetSection(section))
490 		{
491 			ReadCVars(CVAR_MOD|CVAR_SERVERINFO|CVAR_IGNORE);
492 		}
493 	}
494 }
495 
496 // Read cvars from a cvar section of the ini. Flags are the flags to give
497 // to newly-created cvars that were not already defined.
ReadCVars(DWORD flags)498 void FGameConfigFile::ReadCVars (DWORD flags)
499 {
500 	const char *key, *value;
501 	FBaseCVar *cvar;
502 	UCVarValue val;
503 
504 	flags |= CVAR_ARCHIVE|CVAR_UNSETTABLE|CVAR_AUTO;
505 	while (NextInSection (key, value))
506 	{
507 		cvar = FindCVar (key, NULL);
508 		if (cvar == NULL)
509 		{
510 			cvar = new FStringCVar (key, NULL, flags);
511 		}
512 		val.String = const_cast<char *>(value);
513 		cvar->SetGenericRep (val, CVAR_String);
514 	}
515 }
516 
ArchiveGameData(const char * gamename)517 void FGameConfigFile::ArchiveGameData (const char *gamename)
518 {
519 	char section[32*3], *subsection;
520 
521 	sublen = countof(section) - 1 - mysnprintf (section, countof(section), "%s.", gamename);
522 	subsection = section + countof(section) - 1 - sublen;
523 
524 	strncpy (subsection, "Player", sublen);
525 	SetSection (section, true);
526 	ClearCurrentSection ();
527 	C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_USERINFO);
528 
529 	if (bModSetup)
530 	{
531 		strncpy (subsection + 6, ".Mod", sublen - 6);
532 		SetSection (section, true);
533 		ClearCurrentSection ();
534 		C_ArchiveCVars (this, CVAR_MOD|CVAR_ARCHIVE|CVAR_AUTO|CVAR_USERINFO);
535 	}
536 
537 	strncpy (subsection, "ConsoleVariables", sublen);
538 	SetSection (section, true);
539 	ClearCurrentSection ();
540 	C_ArchiveCVars (this, CVAR_ARCHIVE);
541 
542 	// Do not overwrite the serverinfo section if playing a netgame, and
543 	// this machine was not the initial host.
544 	if (!netgame || consoleplayer == 0)
545 	{
546 		strncpy (subsection, netgame ? "NetServerInfo" : "LocalServerInfo", sublen);
547 		SetSection (section, true);
548 		ClearCurrentSection ();
549 		C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_SERVERINFO);
550 
551 		if (bModSetup)
552 		{
553 			strncpy (subsection, netgame ? "NetServerInfo.Mod" : "LocalServerInfo.Mod", sublen);
554 			SetSection (section, true);
555 			ClearCurrentSection ();
556 			C_ArchiveCVars (this, CVAR_MOD|CVAR_ARCHIVE|CVAR_AUTO|CVAR_SERVERINFO);
557 		}
558 	}
559 
560 	strncpy (subsection, "UnknownConsoleVariables", sublen);
561 	SetSection (section, true);
562 	ClearCurrentSection ();
563 	C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_AUTO);
564 
565 	strncpy (subsection, "ConsoleAliases", sublen);
566 	SetSection (section, true);
567 	ClearCurrentSection ();
568 	C_ArchiveAliases (this);
569 
570 	M_SaveCustomKeys (this, section, subsection, sublen);
571 
572 	strcpy (subsection, "Bindings");
573 	SetSection (section, true);
574 	Bindings.ArchiveBindings (this);
575 
576 	strncpy (subsection, "DoubleBindings", sublen);
577 	SetSection (section, true);
578 	DoubleBindings.ArchiveBindings (this);
579 
580 	strncpy (subsection, "AutomapBindings", sublen);
581 	SetSection (section, true);
582 	AutomapBindings.ArchiveBindings (this);
583 }
584 
ArchiveGlobalData()585 void FGameConfigFile::ArchiveGlobalData ()
586 {
587 	SetSection ("LastRun", true);
588 	ClearCurrentSection ();
589 	SetValueForKey ("Version", LASTRUNVERSION);
590 
591 	SetSection ("GlobalSettings", true);
592 	ClearCurrentSection ();
593 	C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
594 
595 	SetSection ("GlobalSettings.Unknown", true);
596 	ClearCurrentSection ();
597 	C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_AUTO);
598 }
599 
GetConfigPath(bool tryProg)600 FString FGameConfigFile::GetConfigPath (bool tryProg)
601 {
602 	const char *pathval;
603 
604 	pathval = Args->CheckValue ("-config");
605 	if (pathval != NULL)
606 	{
607 		return FString(pathval);
608 	}
609 	return M_GetConfigPath(tryProg);
610 }
611 
CreateStandardAutoExec(const char * section,bool start)612 void FGameConfigFile::CreateStandardAutoExec(const char *section, bool start)
613 {
614 	if (!SetSection(section))
615 	{
616 		FString path = M_GetAutoexecPath();
617 		SetSection (section, true);
618 		SetValueForKey ("Path", path.GetChars());
619 	}
620 	if (start)
621 	{
622 		MoveSectionToStart(section);
623 	}
624 }
625 
AddAutoexec(DArgs * list,const char * game)626 void FGameConfigFile::AddAutoexec (DArgs *list, const char *game)
627 {
628 	char section[64];
629 	const char *key;
630 	const char *value;
631 
632 	mysnprintf (section, countof(section), "%s.AutoExec", game);
633 
634 	// If <game>.AutoExec section does not exist, create it
635 	// with a default autoexec.cfg file present.
636 	CreateStandardAutoExec(section, false);
637 	// Run any files listed in the <game>.AutoExec section
638 	if (!SectionIsEmpty())
639 	{
640 		while (NextInSection (key, value))
641 		{
642 			if (stricmp (key, "Path") == 0 && *value != '\0')
643 			{
644 				FString expanded_path = ExpandEnvVars(value);
645 				if (FileExists(expanded_path))
646 				{
647 					list->AppendArg (ExpandEnvVars(value));
648 				}
649 			}
650 		}
651 	}
652 }
653 
SetRavenDefaults(bool isHexen)654 void FGameConfigFile::SetRavenDefaults (bool isHexen)
655 {
656 	UCVarValue val;
657 
658 	val.Bool = false;
659 	wi_percents.SetGenericRepDefault (val, CVAR_Bool);
660 	val.Bool = true;
661 	con_centernotify.SetGenericRepDefault (val, CVAR_Bool);
662 	snd_pitched.SetGenericRepDefault (val, CVAR_Bool);
663 	val.Int = 9;
664 	msg0color.SetGenericRepDefault (val, CVAR_Int);
665 	val.Int = CR_WHITE;
666 	msgmidcolor.SetGenericRepDefault (val, CVAR_Int);
667 	val.Int = CR_YELLOW;
668 	msgmidcolor2.SetGenericRepDefault (val, CVAR_Int);
669 
670 	val.Int = 0x543b17;
671 	am_wallcolor.SetGenericRepDefault (val, CVAR_Int);
672 	val.Int = 0xd0b085;
673 	am_fdwallcolor.SetGenericRepDefault (val, CVAR_Int);
674 	val.Int = 0x734323;
675 	am_cdwallcolor.SetGenericRepDefault (val, CVAR_Int);
676 
677 	// Fix the Heretic/Hexen automap colors so they are correct.
678 	// (They were wrong on older versions.)
679 	if (*am_wallcolor == 0x2c1808 && *am_fdwallcolor == 0x887058 && *am_cdwallcolor == 0x4c3820)
680 	{
681 		am_wallcolor.ResetToDefault ();
682 		am_fdwallcolor.ResetToDefault ();
683 		am_cdwallcolor.ResetToDefault ();
684 	}
685 
686 	if (!isHexen)
687 	{
688 		val.Int = 0x3f6040;
689 		color.SetGenericRepDefault (val, CVAR_Int);
690 	}
691 }
692 
CCMD(whereisini)693 CCMD (whereisini)
694 {
695 	FString path = M_GetConfigPath(false);
696 	Printf ("%s\n", path.GetChars());
697 }
698