mirror of
https://github.com/DrBeef/Raze.git
synced 2024-12-16 07:31:04 +00:00
539 lines
16 KiB
C++
539 lines
16 KiB
C++
/*
|
|
** gameconfigfile.cpp
|
|
** An .ini parser specifically for zdoom.ini
|
|
**
|
|
**---------------------------------------------------------------------------
|
|
** Copyright 1998-2008 Randy Heit
|
|
** All rights reserved.
|
|
**
|
|
** Redistribution and use in source and binary forms, with or without
|
|
** modification, are permitted provided that the following conditions
|
|
** are met:
|
|
**
|
|
** 1. Redistributions of source code must retain the above copyright
|
|
** notice, this list of conditions and the following disclaimer.
|
|
** 2. Redistributions in binary form must reproduce the above copyright
|
|
** notice, this list of conditions and the following disclaimer in the
|
|
** documentation and/or other materials provided with the distribution.
|
|
** 3. The name of the author may not be used to endorse or promote products
|
|
** derived from this software without specific prior written permission.
|
|
**
|
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OFf
|
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
**---------------------------------------------------------------------------
|
|
**
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "gameconfigfile.h"
|
|
#include "c_cvars.h"
|
|
#include "c_dispatch.h"
|
|
#include "c_bind.h"
|
|
#include "m_argv.h"
|
|
#include "cmdlib.h"
|
|
//#include "version.h"
|
|
#include "i_specialpaths.h"
|
|
//#include "v_font.h"
|
|
//#include "doomstat.h"
|
|
//#include "gi.h"
|
|
//#include "d_main.h"
|
|
#include "keyboard.h"
|
|
#include "control.h"
|
|
#include "printf.h"
|
|
#include "gamecontrol.h"
|
|
#include "version.h"
|
|
|
|
#define LASTRUNVERSION "1"
|
|
|
|
#if !defined _MSC_VER && !defined __APPLE__
|
|
//#include "i_system.h" // for SHARE_DIR
|
|
#endif // !_MSC_VER && !__APPLE__
|
|
|
|
FGameConfigFile::FGameConfigFile ()
|
|
{
|
|
#ifdef __APPLE__
|
|
FString user_docs, user_app_support, local_app_support;
|
|
M_GetMacSearchDirectories(user_docs, user_app_support, local_app_support);
|
|
#endif
|
|
|
|
FString pathname;
|
|
|
|
OkayToWrite = false; // Do not allow saving of the config before DoKeySetup()
|
|
bModSetup = false;
|
|
pathname = GetConfigPath (true);
|
|
ChangePathName (pathname);
|
|
LoadConfigFile ();
|
|
|
|
// If zdoom.ini was read from the program directory, switch
|
|
// to the user directory now. If it was read from the user
|
|
// directory, this effectively does nothing.
|
|
pathname = GetConfigPath (false);
|
|
ChangePathName (pathname);
|
|
|
|
// Set default IWAD search paths if none present
|
|
if (!SetSection ("GameSearch.Directories"))
|
|
{
|
|
SetSection ("GameSearch.Directories", true);
|
|
SetValueForKey ("Path", ".", true);
|
|
SetValueForKey ("Path", "./*", true);
|
|
#ifdef __APPLE__
|
|
SetValueForKey ("Path", user_docs + "/*", true);
|
|
SetValueForKey ("Path", user_app_support + "/EDuke32", true);
|
|
SetValueForKey ("Path", user_app_support + "/JFDuke32", true);
|
|
SetValueForKey ("Path", user_app_support + "/NBlood", true);
|
|
SetValueForKey ("Path", "$PROGDIR", true);
|
|
SetValueForKey ("Path", "$PROGDIR/*", true);
|
|
SetValueForKey ("Path", local_app_support + "/EDuke32", true);
|
|
SetValueForKey ("Path", local_app_support + "/JFDuke32", true);
|
|
SetValueForKey ("Path", local_app_support + "/NBlood", true);
|
|
SetValueForKey("Path", local_app_support + "/JFSW", true);
|
|
SetValueForKey("Path", local_app_support + "/VoidSW", true);
|
|
|
|
#elif !defined(__unix__)
|
|
SetValueForKey ("Path", "$PROGDIR", true);
|
|
SetValueForKey ("Path", "$PROGDIR/*", true);
|
|
#else
|
|
SetValueForKey ("Path", "$HOME/" GAME_DIR "/*", true);
|
|
// Arch Linux likes them in /usr/share/doom
|
|
// Debian likes them in /usr/share/games/doom
|
|
// I assume other distributions don't do anything radically different
|
|
SetValueForKey ("Path", "/usr/share/games/jfduke3d", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/jfduke3d", true);
|
|
SetValueForKey ("Path", "/usr/share/games/eduke32", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/eduke32", true);
|
|
SetValueForKey ("Path", "/usr/share/games/nblood", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/nblood", true);
|
|
SetValueForKey("Path", "/usr/share/games/jfsw", true);
|
|
SetValueForKey("Path", "/usr/local/share/games/jfsw", true);
|
|
SetValueForKey("Path", "/usr/share/games/voidsw", true);
|
|
SetValueForKey("Path", "/usr/local/share/games/voidsw", true);
|
|
|
|
#endif
|
|
SetValueForKey ("Path", "$STEAM", true); // also covers GOG.
|
|
}
|
|
|
|
// Set default search paths if none present
|
|
if (!SetSection ("FileSearch.Directories"))
|
|
{
|
|
SetSection ("FileSearch.Directories", true);
|
|
#ifdef __APPLE__
|
|
SetValueForKey ("Path", user_docs, true);
|
|
SetValueForKey ("Path", user_app_support, true);
|
|
SetValueForKey ("Path", "$PROGDIR", true);
|
|
SetValueForKey ("Path", local_app_support, true);
|
|
#elif !defined(__unix__)
|
|
SetValueForKey ("Path", "$PROGDIR", true);
|
|
SetValueForKey ("Path", "$GAMEDIR", true);
|
|
#else
|
|
SetValueForKey ("Path", "$HOME/" GAME_DIR, true);
|
|
SetValueForKey ("Path", SHARE_DIR, true);
|
|
SetValueForKey ("Path", "/usr/share/games/jfduke3d", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/jfduke3d", true);
|
|
SetValueForKey ("Path", "/usr/share/games/eduke32", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/eduke32", true);
|
|
SetValueForKey ("Path", "/usr/share/games/nblood", true);
|
|
SetValueForKey ("Path", "/usr/local/share/games/nblood", true);
|
|
#endif
|
|
}
|
|
|
|
// Set default search paths if none present
|
|
if (!SetSection("SoundfontSearch.Directories"))
|
|
{
|
|
SetSection("SoundfontSearch.Directories", true);
|
|
#ifdef __APPLE__
|
|
SetValueForKey("Path", user_docs + "/soundfonts", true);
|
|
SetValueForKey("Path", user_app_support + "/soundfonts", true);
|
|
SetValueForKey("Path", "$PROGDIR/soundfonts", true);
|
|
SetValueForKey("Path", local_app_support + "/soundfonts", true);
|
|
#elif !defined(__unix__)
|
|
SetValueForKey("Path", "$PROGDIR/soundfonts", true);
|
|
#else
|
|
SetValueForKey("Path", "$HOME/" GAME_DIR "/soundfonts", true);
|
|
SetValueForKey("Path", "/usr/local/share/" GAME_DIR "/soundfonts", true);
|
|
SetValueForKey("Path", "/usr/local/share/games/" GAME_DIR "/soundfonts", true);
|
|
SetValueForKey("Path", "/usr/share/" GAME_DIR "/soundfonts", true);
|
|
SetValueForKey("Path", "/usr/share/games/" GAME_DIR "/soundfonts", true);
|
|
#endif
|
|
}
|
|
|
|
// Add some self-documentation.
|
|
SetSectionNote("GameSearch.Directories",
|
|
"# These are the directories to automatically search for game data.\n"
|
|
"# Each directory should be on a separate line, preceded by Path=\n");
|
|
SetSectionNote("FileSearch.Directories",
|
|
"# These are the directories to search for add-ons added with the -file\n"
|
|
"# command line parameter, if they cannot be found with the path\n"
|
|
"# as-is. Layout is the same as for GameSearch.Directories\n");
|
|
SetSectionNote("SoundfontSearch.Directories",
|
|
"# These are the directories to search for soundfonts that let listed in the menu.\n"
|
|
"# Layout is the same as for GameSearch.Directories\n");
|
|
}
|
|
|
|
FGameConfigFile::~FGameConfigFile ()
|
|
{
|
|
}
|
|
|
|
void FGameConfigFile::WriteCommentHeader (FileWriter *file) const
|
|
{
|
|
file->Printf ("# This file was generated by " GAMENAME " %s\n", GetVersionString());
|
|
}
|
|
|
|
void FGameConfigFile::DoAutoloadSetup (/*FIWadManager *iwad_man*/)
|
|
{
|
|
// Create auto-load sections, so users know what's available.
|
|
// Note that this totem pole is the reverse of the order that
|
|
// they will appear in the file.
|
|
|
|
double last = 0;
|
|
if (SetSection ("LastRun"))
|
|
{
|
|
const char *lastver = GetValueForKey ("Version");
|
|
if (lastver != NULL) last = atof(lastver);
|
|
isInitialized = true;
|
|
}
|
|
|
|
CreateSectionAtStart("Global.Autoload");
|
|
|
|
// The same goes for auto-exec files.
|
|
CreateStandardAutoExec("ShadowWarrior.AutoLoad", true);
|
|
CreateStandardAutoExec("IonFury.AutoLoad", true);
|
|
CreateStandardAutoExec("Redneck.RidesAgain.AutoLoad", true);
|
|
CreateStandardAutoExec("Redneck.Redneck.AutoLoad", true);
|
|
CreateStandardAutoExec("WW2GI.AutoLoad", true);
|
|
CreateStandardAutoExec("Nam.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.DN3D.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.DukeDC.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.NWinter.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.Vacation.AutoLoad", true);
|
|
|
|
CreateStandardAutoExec("ShadowWarrior.AutoLoad", true);
|
|
CreateStandardAutoExec("IonFury.AutoLoad", true);
|
|
CreateStandardAutoExec("Redneck.RidesAgain.AutoLoad", true);
|
|
CreateStandardAutoExec("Redneck.Redneck.AutoLoad", true);
|
|
CreateStandardAutoExec("WW2GI.AutoLoad", true);
|
|
CreateStandardAutoExec("Nam.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.DN3D.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.DukeDC.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.NWinter.AutoLoad", true);
|
|
CreateStandardAutoExec("DukeNukem3D.Vacation.AutoLoad", true);
|
|
|
|
// Move search paths back to the top.
|
|
MoveSectionToStart("SoundfontSearch.Directories");
|
|
MoveSectionToStart("FileSearch.Directories");
|
|
MoveSectionToStart("IWADSearch.Directories");
|
|
|
|
SetSectionNote("DukeNukem3D.AutoLoad",
|
|
"# Files to automatically execute when running the corresponding game.\n"
|
|
"# Each file should be on its own line, preceded by Path=\n\n");
|
|
SetSectionNote("Global.Autoload",
|
|
"# Files to always load. These are loaded after the game data but before\n"
|
|
"# any files added with -file. Place each file on its own line, preceded\n"
|
|
"# by Path=\n");
|
|
SetSectionNote("DukeNukem3D.Autoload",
|
|
"# Files to automatically load depending on the game you are playing\n\n");
|
|
}
|
|
|
|
void FGameConfigFile::DoGlobalSetup ()
|
|
{
|
|
if (SetSection ("GlobalSettings.Unknown"))
|
|
{
|
|
ReadCVars (CVAR_GLOBALCONFIG);
|
|
}
|
|
if (SetSection ("GlobalSettings"))
|
|
{
|
|
ReadCVars (CVAR_GLOBALCONFIG);
|
|
}
|
|
if (SetSection ("LastRun"))
|
|
{
|
|
const char *lastver = GetValueForKey ("Version");
|
|
if (lastver != NULL)
|
|
{
|
|
//double last = atof (lastver);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void FGameConfigFile::DoGameSetup (const char *gamename)
|
|
{
|
|
const char *key;
|
|
const char *value;
|
|
|
|
sublen = countof(section) - 1 - snprintf (section, countof(section), "%s.", gamename);
|
|
subsection = section + countof(section) - sublen - 1;
|
|
section[countof(section) - 1] = '\0';
|
|
|
|
strncpy (subsection, "UnknownConsoleVariables", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
ReadCVars (0);
|
|
}
|
|
|
|
strncpy (subsection, "ConsoleVariables", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
ReadCVars (0);
|
|
}
|
|
|
|
// The NetServerInfo section will be read and override anything loaded
|
|
// here when it's determined that a netgame is being played.
|
|
strncpy (subsection, "LocalServerInfo", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
ReadCVars (0);
|
|
}
|
|
|
|
strncpy (subsection, "Player", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
ReadCVars (0);
|
|
}
|
|
strncpy (subsection, "ConsoleAliases", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
const char *name = NULL;
|
|
while (NextInSection (key, value))
|
|
{
|
|
FStringf cmd("alias %s \"%s\"", key, value);
|
|
C_DoCommand(cmd);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Moved from DoGameSetup so that it can happen after wads are loaded
|
|
void FGameConfigFile::DoKeySetup(const char *gamename)
|
|
{
|
|
static const struct { const char *label; FKeyBindings *bindings; } binders[] =
|
|
{
|
|
{ "Bindings", &Bindings },
|
|
{ "DoubleBindings", &DoubleBindings },
|
|
{ "AutomapBindings", &AutomapBindings },
|
|
{ NULL, NULL }
|
|
};
|
|
|
|
sublen = countof(section) - 1 - snprintf(section, countof(section), "%s.", gamename);
|
|
subsection = section + countof(section) - sublen - 1;
|
|
section[countof(section) - 1] = '\0';
|
|
|
|
//C_SetDefaultBindings ();
|
|
|
|
const char* key, * value;
|
|
for (int i = 0; binders[i].label != NULL; ++i)
|
|
{
|
|
strncpy(subsection, binders[i].label, sublen);
|
|
if (SetSection(section))
|
|
{
|
|
FKeyBindings *bindings = binders[i].bindings;
|
|
bindings->UnbindAll();
|
|
while (NextInSection(key, value))
|
|
{
|
|
bindings->DoBind(key, value);
|
|
}
|
|
}
|
|
}
|
|
OkayToWrite = true;
|
|
}
|
|
|
|
void FGameConfigFile::ReadNetVars ()
|
|
{
|
|
strncpy (subsection, "NetServerInfo", sublen);
|
|
if (SetSection (section))
|
|
{
|
|
ReadCVars (0);
|
|
}
|
|
}
|
|
|
|
// Read cvars from a cvar section of the ini. Flags are the flags to give
|
|
// to newly-created cvars that were not already defined.
|
|
void FGameConfigFile::ReadCVars (uint32_t flags)
|
|
{
|
|
const char *key, *value;
|
|
FBaseCVar *cvar;
|
|
UCVarValue val;
|
|
|
|
flags |= CVAR_ARCHIVE|CVAR_UNSETTABLE|CVAR_AUTO;
|
|
while (NextInSection (key, value))
|
|
{
|
|
cvar = FindCVar (key, NULL);
|
|
if (cvar == NULL)
|
|
{
|
|
cvar = new FStringCVar (key, NULL, flags);
|
|
}
|
|
val.String = const_cast<char *>(value);
|
|
cvar->SetGenericRep (val, CVAR_String);
|
|
}
|
|
}
|
|
|
|
void FGameConfigFile::ArchiveGameData (const char *gamename)
|
|
{
|
|
char section[32*3], *subsection;
|
|
|
|
sublen = countof(section) - 1 - snprintf (section, countof(section), "%s.", gamename);
|
|
subsection = section + countof(section) - 1 - sublen;
|
|
|
|
strncpy (subsection, "Player", sublen);
|
|
SetSection (section, true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_USERINFO);
|
|
|
|
strncpy (subsection, "ConsoleVariables", sublen);
|
|
SetSection (section, true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE);
|
|
|
|
strncpy(subsection, "VideoSettings", sublen);
|
|
SetSection(section, true);
|
|
ClearCurrentSection();
|
|
C_ArchiveCVars(this, CVAR_ARCHIVE|CVAR_VIDEOCONFIG);
|
|
|
|
#if 0
|
|
// Do not overwrite the serverinfo section if playing a netgame, and
|
|
// this machine was not the initial host.
|
|
if (!netgame || consoleplayer == 0)
|
|
{
|
|
strncpy (subsection, netgame ? "NetServerInfo" : "LocalServerInfo", sublen);
|
|
SetSection (section, true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_SERVERINFO);
|
|
}
|
|
#endif
|
|
|
|
strncpy (subsection, "UnknownConsoleVariables", sublen);
|
|
SetSection (section, true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_AUTO);
|
|
|
|
strncpy (subsection, "ConsoleAliases", sublen);
|
|
SetSection (section, true);
|
|
ClearCurrentSection ();
|
|
//C_ArchiveAliases (this);
|
|
|
|
//M_SaveCustomKeys (this, section, subsection, sublen);
|
|
|
|
strcpy (subsection, "Bindings");
|
|
SetSection (section, true);
|
|
Bindings.ArchiveBindings (this);
|
|
|
|
strncpy (subsection, "DoubleBindings", sublen);
|
|
SetSection (section, true);
|
|
DoubleBindings.ArchiveBindings (this);
|
|
|
|
strncpy (subsection, "AutomapBindings", sublen);
|
|
SetSection (section, true);
|
|
AutomapBindings.ArchiveBindings (this);
|
|
|
|
}
|
|
|
|
void FGameConfigFile::ArchiveGlobalData ()
|
|
{
|
|
SetSection ("LastRun", true);
|
|
ClearCurrentSection ();
|
|
SetValueForKey ("Version", LASTRUNVERSION);
|
|
|
|
SetSection ("GlobalSettings", true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_GLOBALCONFIG);
|
|
|
|
SetSection ("GlobalSettings.Unknown", true);
|
|
ClearCurrentSection ();
|
|
C_ArchiveCVars (this, CVAR_ARCHIVE|CVAR_GLOBALCONFIG|CVAR_AUTO);
|
|
}
|
|
|
|
FString FGameConfigFile::GetConfigPath (bool tryProg)
|
|
{
|
|
const char *pathval;
|
|
|
|
pathval = Args->CheckValue ("-config");
|
|
if (pathval != NULL)
|
|
{
|
|
return FString(pathval);
|
|
}
|
|
return M_GetConfigPath(tryProg);
|
|
}
|
|
|
|
void FGameConfigFile::CreateStandardAutoExec(const char *section, bool start)
|
|
{
|
|
if (!SetSection(section))
|
|
{
|
|
FString path = M_GetAutoexecPath();
|
|
SetSection (section, true);
|
|
SetValueForKey ("Path", path.GetChars());
|
|
}
|
|
if (start)
|
|
{
|
|
MoveSectionToStart(section);
|
|
}
|
|
}
|
|
|
|
void FGameConfigFile::AddAutoexec (FArgs *list, const char *game)
|
|
{
|
|
char section[64];
|
|
const char *key;
|
|
const char *value;
|
|
|
|
snprintf (section, countof(section), "%s.AutoLoad", game);
|
|
|
|
// If <game>.AutoLoad section does not exist, create it
|
|
// with a default autoexec.cfg file present.
|
|
CreateStandardAutoExec(section, false);
|
|
// Run any files listed in the <game>.AutoLoad section
|
|
if (!SectionIsEmpty())
|
|
{
|
|
while (NextInSection (key, value))
|
|
{
|
|
if (stricmp (key, "Path") == 0 && *value != '\0')
|
|
{
|
|
FString expanded_path = ExpandEnvVars(value);
|
|
if (FileExists(expanded_path))
|
|
{
|
|
list->AppendArg (ExpandEnvVars(value));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CCMD (whereisini)
|
|
{
|
|
FString path = M_GetConfigPath(false);
|
|
Printf ("%s\n", path.GetChars());
|
|
}
|
|
|
|
FGameConfigFile* GameConfig;
|
|
static FString GameName;
|
|
|
|
void G_LoadConfig()
|
|
{
|
|
GameConfig = new FGameConfigFile();
|
|
}
|
|
|
|
void G_ReadConfig(const char* game)
|
|
{
|
|
GameConfig->DoGlobalSetup();
|
|
GameConfig->DoGameSetup(game);
|
|
GameConfig->DoKeySetup(game);
|
|
FBaseCVar::EnableCallbacks();
|
|
GameName = game;
|
|
}
|
|
|
|
void G_SaveConfig()
|
|
{
|
|
GameConfig->ArchiveGlobalData();
|
|
GameConfig->ArchiveGameData(GameName);
|
|
CONFIG_WriteControllerSettings();
|
|
GameConfig->WriteConfigFile();
|
|
delete GameConfig;
|
|
GameConfig = nullptr;
|
|
}
|
|
|