- Moved ExpandEnvVars() from d_main.cpp to cmdlib.cpp.

- AutoExec paths now support the same variable expansion as the search paths.
  Additionally, on Windows, the default autoexec path is now relative to
  $PROGDIR, rather than using a fixed path to the executable's current
  directory.
- All usable Autoload and AutoExec sections are now created at the top of
  the config file along with some brief explanatory notes so they are
  readily visible to anyone who wants to edit them.


SVN r1307 (trunk)
This commit is contained in:
Randy Heit 2008-12-07 00:50:04 +00:00
parent fb75c46806
commit ef3b57fb8f
8 changed files with 466 additions and 101 deletions

View file

@ -1,3 +1,13 @@
December 6, 2008
- Moved ExpandEnvVars() from d_main.cpp to cmdlib.cpp.
- AutoExec paths now support the same variable expansion as the search paths.
Additionally, on Windows, the default autoexec path is now relative to
$PROGDIR, rather than using a fixed path to the executable's current
directory.
- All usable Autoload and AutoExec sections are now created at the top of
the config file along with some brief explanatory notes so they are
readily visible to anyone who wants to edit them.
December 6, 2008 (Changes by Graf Zahl)
- Fixed: G_DoPlayDemo did not free the demobuffer or the CVAR backups when it
failed to start the demo.
@ -19,7 +29,8 @@ December 1, 2008 (Changes by Graf Zahl)
December 1, 2008
- Restored the multiplayer scoreboard's former centering so that it doesn't
look awful on widescreen intermissions.
look awful on widescreen intermissions. Also changed the column widths to
be font-dependant rather than fixed proportions of the screen width.
- Fixed horizontal positioning of 'finished' on the Raven games when scaled.
November 30, 2008 (Changes by Graf Zahl)

View file

@ -469,3 +469,68 @@ int strbin (char *str)
*str = 0;
return str - start;
}
//==========================================================================
//
// ExpandEnvVars
//
// Expands environment variable references in a string. Intended primarily
// for use with IWAD search paths in config files.
//
//==========================================================================
FString ExpandEnvVars(const char *searchpathstring)
{
static const char envvarnamechars[] =
"01234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
"abcdefghijklmnopqrstuvwxyz";
if (searchpathstring == NULL)
return FString("");
const char *dollar = strchr(searchpathstring, '$');
if (dollar == NULL)
{
return FString(searchpathstring);
}
const char *nextchars = searchpathstring;
FString out = FString(searchpathstring, dollar - searchpathstring);
while ( (dollar != NULL) && (*nextchars != 0) )
{
size_t length = strspn(dollar + 1, envvarnamechars);
if (length != 0)
{
FString varname = FString(dollar + 1, length);
if (stricmp(varname, "progdir") == 0)
{
out += progdir;
}
else
{
char *varvalue = getenv(varname);
if ( (varvalue != NULL) && (strlen(varvalue) != 0) )
{
out += varvalue;
}
}
}
else
{
out += '$';
}
nextchars = dollar + length + 1;
dollar = strchr(nextchars, '$');
if (dollar != NULL)
{
out += FString(nextchars, dollar - nextchars);
}
}
if (*nextchars != 0)
{
out += nextchars;
}
return out;
}

View file

@ -55,4 +55,6 @@ int strbin (char *str);
void CreatePath(const char * fn);
FString ExpandEnvVars(const char *searchpathstring);
#endif

View file

@ -3,7 +3,7 @@
** Implements the basic .ini parsing class
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -30,11 +30,6 @@
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
** This could have been done with a lot less source code using the STL and
** maps, but how much larger would the object code be?
**
** Regardless of object size, I had not considered the possibility of using
** the STL when I wrote this.
*/
#include <stdio.h>
@ -46,6 +41,12 @@
#define READBUFFERSIZE 256
//====================================================================
//
// FConfigFile Constructor
//
//====================================================================
FConfigFile::FConfigFile ()
{
Sections = CurrentSection = NULL;
@ -54,6 +55,12 @@ FConfigFile::FConfigFile ()
PathName = "";
}
//====================================================================
//
// FConfigFile Constructor
//
//====================================================================
FConfigFile::FConfigFile (const char *pathname,
void (*nosechandler)(const char *pathname, FConfigFile *config, void *userdata),
void *userdata)
@ -65,6 +72,12 @@ FConfigFile::FConfigFile (const char *pathname,
LoadConfigFile (nosechandler, userdata);
}
//====================================================================
//
// FConfigFile Copy Constructor
//
//====================================================================
FConfigFile::FConfigFile (const FConfigFile &other)
{
Sections = CurrentSection = NULL;
@ -74,6 +87,12 @@ FConfigFile::FConfigFile (const FConfigFile &other)
*this = other;
}
//====================================================================
//
// FConfigFile Destructor
//
//====================================================================
FConfigFile::~FConfigFile ()
{
FConfigSection *section = Sections;
@ -90,11 +109,18 @@ FConfigFile::~FConfigFile ()
delete[] (char *)entry;
entry = nextentry;
}
section->~FConfigSection();
delete[] (char *)section;
section = nextsection;
}
}
//====================================================================
//
// FConfigFile Copy Operator
//
//====================================================================
FConfigFile &FConfigFile::operator = (const FConfigFile &other)
{
FConfigSection *fromsection, *tosection;
@ -116,6 +142,14 @@ FConfigFile &FConfigFile::operator = (const FConfigFile &other)
return *this;
}
//====================================================================
//
// FConfigFile :: ClearConfig
//
// Removes all sections and entries from the config file.
//
//====================================================================
void FConfigFile::ClearConfig ()
{
CurrentSection = Sections;
@ -130,11 +164,73 @@ void FConfigFile::ClearConfig ()
LastSectionPtr = &Sections;
}
//====================================================================
//
// FConfigFile :: ChangePathName
//
//====================================================================
void FConfigFile::ChangePathName (const char *pathname)
{
PathName = pathname;
}
//====================================================================
//
// FConfigFile :: CreateSectionAtStart
//
// Creates the section at the start of the file if it does not exist.
// Otherwise, simply moves the section to the start of the file.
//
//====================================================================
void FConfigFile::CreateSectionAtStart (const char *name)
{
NewConfigSection (name);
MoveSectionToStart (name);
}
//====================================================================
//
// FConfigFile :: MoveSectionToStart
//
// Moves the named section to the start of the file if it exists.
// Otherwise, does nothing.
//
//====================================================================
void FConfigFile::MoveSectionToStart (const char *name)
{
FConfigSection *section = FindSection (name);
if (section != NULL)
{
FConfigSection **prevsec = &Sections;
while (*prevsec != NULL && *prevsec != section)
{
prevsec = &((*prevsec)->Next);
}
*prevsec = section->Next;
section->Next = Sections;
Sections = section;
if (LastSectionPtr == &section->Next)
{
LastSectionPtr = prevsec;
}
}
}
//====================================================================
//
// FConfigFile :: SetSection
//
// Sets the current section to the named one, optionally creating it
// if it does not exist. Returns true if the section exists (even if
// it was newly created), false otherwise.
//
//====================================================================
bool FConfigFile::SetSection (const char *name, bool allowCreate)
{
FConfigSection *section = FindSection (name);
@ -151,6 +247,14 @@ bool FConfigFile::SetSection (const char *name, bool allowCreate)
return false;
}
//====================================================================
//
// FConfigFile :: SetFirstSection
//
// Sets the current section to the first one in the file.
//
//====================================================================
bool FConfigFile::SetFirstSection ()
{
CurrentSection = Sections;
@ -162,6 +266,14 @@ bool FConfigFile::SetFirstSection ()
return false;
}
//====================================================================
//
// FConfigFile :: SetNextSection
//
// Advances the current section to the next one in the file.
//
//====================================================================
bool FConfigFile::SetNextSection ()
{
if (CurrentSection != NULL)
@ -176,6 +288,14 @@ bool FConfigFile::SetNextSection ()
return false;
}
//====================================================================
//
// FConfigFile :: GetCurrentSection
//
// Returns the name of the current section.
//
//====================================================================
const char *FConfigFile::GetCurrentSection () const
{
if (CurrentSection != NULL)
@ -185,6 +305,14 @@ const char *FConfigFile::GetCurrentSection () const
return NULL;
}
//====================================================================
//
// FConfigFile :: ClearCurrentSection
//
// Removes all entries from the current section.
//
//====================================================================
void FConfigFile::ClearCurrentSection ()
{
if (CurrentSection != NULL)
@ -204,6 +332,30 @@ void FConfigFile::ClearCurrentSection ()
}
}
//====================================================================
//
// FConfigFile :: SectionIsEmpty
//
// Returns true if the current section has no entries. If there is
// no current section, it is also considered empty.
//
//====================================================================
bool FConfigFile::SectionIsEmpty()
{
return (CurrentSection == NULL) || (CurrentSection->RootEntry == NULL);
}
//====================================================================
//
// FConfigFile :: NextInSection
//
// Provides the next key/value pair in the current section. Returns
// true if there was another, false otherwise.
//
//====================================================================
bool FConfigFile::NextInSection (const char *&key, const char *&value)
{
FConfigEntry *entry = CurrentEntry;
@ -217,6 +369,15 @@ bool FConfigFile::NextInSection (const char *&key, const char *&value)
return true;
}
//====================================================================
//
// FConfigFile :: GetValueForKey
//
// Returns the value for the specified key in the current section,
// returning NULL if the key does not exist.
//
//====================================================================
const char *FConfigFile::GetValueForKey (const char *key) const
{
FConfigEntry *entry = FindEntry (CurrentSection, key);
@ -228,6 +389,16 @@ const char *FConfigFile::GetValueForKey (const char *key) const
return NULL;
}
//====================================================================
//
// FConfigFile :: SetValueForKey
//
// Sets they key/value pair as specified in the current section. If
// duplicates are allowed, it always creates a new pair. Otherwise, it
// will overwrite the value of an existing key with the same name.
//
//====================================================================
void FConfigFile::SetValueForKey (const char *key, const char *value, bool duplicates)
{
if (CurrentSection != NULL)
@ -245,6 +416,12 @@ void FConfigFile::SetValueForKey (const char *key, const char *value, bool dupli
}
}
//====================================================================
//
// FConfigFile :: FindSection
//
//====================================================================
FConfigFile::FConfigSection *FConfigFile::FindSection (const char *name) const
{
FConfigSection *section = Sections;
@ -256,6 +433,12 @@ FConfigFile::FConfigSection *FConfigFile::FindSection (const char *name) const
return section;
}
//====================================================================
//
// FConfigFile :: FindEntry
//
//====================================================================
FConfigFile::FConfigEntry *FConfigFile::FindEntry (
FConfigFile::FConfigSection *section, const char *key) const
{
@ -268,15 +451,23 @@ FConfigFile::FConfigEntry *FConfigFile::FindEntry (
return probe;
}
//====================================================================
//
// FConfigFile :: NewConfigSection
//
//====================================================================
FConfigFile::FConfigSection *FConfigFile::NewConfigSection (const char *name)
{
FConfigSection *section;
char *memblock;
section = FindSection (name);
if (section == NULL)
{
size_t namelen = strlen (name);
section = (FConfigSection *)new char[sizeof(*section)+namelen];
memblock = new char[sizeof(*section)+namelen];
section = ::new(memblock) FConfigSection;
section->RootEntry = NULL;
section->LastEntryPtr = &section->RootEntry;
section->Next = NULL;
@ -288,6 +479,12 @@ FConfigFile::FConfigSection *FConfigFile::NewConfigSection (const char *name)
return section;
}
//====================================================================
//
// FConfigFile :: NewConfigEntry
//
//====================================================================
FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry (
FConfigFile::FConfigSection *section, const char *key, const char *value)
{
@ -306,6 +503,12 @@ FConfigFile::FConfigEntry *FConfigFile::NewConfigEntry (
return entry;
}
//====================================================================
//
// FConfigFile :: LoadConfigFile
//
//====================================================================
void FConfigFile::LoadConfigFile (void (*nosechandler)(const char *pathname, FConfigFile *config, void *userdata), void *userdata)
{
FILE *file = fopen (PathName, "r");
@ -326,6 +529,12 @@ void FConfigFile::LoadConfigFile (void (*nosechandler)(const char *pathname, FCo
}
}
//====================================================================
//
// FConfigFile :: ReadConfig
//
//====================================================================
bool FConfigFile::ReadConfig (void *file)
{
char readbuf[READBUFFERSIZE];
@ -394,11 +603,23 @@ bool FConfigFile::ReadConfig (void *file)
return true;
}
//====================================================================
//
// FConfigFile :: ReadLine
//
//====================================================================
char *FConfigFile::ReadLine (char *string, int n, void *file) const
{
return fgets (string, n, (FILE *)file);
}
//====================================================================
//
// FConfigFile :: WriteConfigFile
//
//====================================================================
bool FConfigFile::WriteConfigFile () const
{
FILE *file = fopen (PathName, "w");
@ -414,6 +635,10 @@ bool FConfigFile::WriteConfigFile () const
while (section != NULL)
{
entry = section->RootEntry;
if (section->Note.IsNotEmpty())
{
fprintf (file, "%s", section->Note.GetChars());
}
fprintf (file, "[%s]\n", section->Name);
while (entry != NULL)
{
@ -427,10 +652,24 @@ bool FConfigFile::WriteConfigFile () const
return true;
}
//====================================================================
//
// FConfigFile :: WriteCommentHeader
//
// Override in a subclass to write a header to the config file.
//
//====================================================================
void FConfigFile::WriteCommentHeader (FILE *file) const
{
}
//====================================================================
//
// FConfigFile :: FConfigEntry :: SetValue
//
//====================================================================
void FConfigFile::FConfigEntry::SetValue (const char *value)
{
if (Value != NULL)
@ -441,14 +680,62 @@ void FConfigFile::FConfigEntry::SetValue (const char *value)
strcpy (Value, value);
}
//====================================================================
//
// FConfigFile :: GetPosition
//
// Populates a struct with the current position of the parse cursor.
//
//====================================================================
void FConfigFile::GetPosition (FConfigFile::Position &pos) const
{
pos.Section = CurrentSection;
pos.Entry = CurrentEntry;
}
//====================================================================
//
// FConfigFile :: SetPosition
//
// Sets the parse cursor to a previously retrieved position.
//
//====================================================================
void FConfigFile::SetPosition (const FConfigFile::Position &pos)
{
CurrentSection = pos.Section;
CurrentEntry = pos.Entry;
}
//====================================================================
//
// FConfigFile :: SetSectionNote
//
// Sets a comment note to be inserted into the INI verbatim directly
// ahead of the section. Notes are lost when the INI is read so must
// be explicitly set to be maintained.
//
//====================================================================
void FConfigFile::SetSectionNote(const char *section, const char *note)
{
SetSectionNote(FindSection(section), note);
}
void FConfigFile::SetSectionNote(const char *note)
{
SetSectionNote(CurrentSection, note);
}
void FConfigFile::SetSectionNote(FConfigSection *section, const char *note)
{
if (section != NULL)
{
if (note == NULL)
{
note = "";
}
section->Note = note;
}
}

View file

@ -2,7 +2,7 @@
** configfile.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -50,12 +50,17 @@ public:
FConfigFile &operator= (const FConfigFile &other);
bool HaveSections () { return Sections != NULL; }
void CreateSectionAtStart (const char *name);
void MoveSectionToStart (const char *name);
void SetSectionNote (const char *section, const char *note);
void SetSectionNote (const char *note);
bool SetSection (const char *section, bool allowCreate=false);
bool SetFirstSection ();
bool SetNextSection ();
const char *GetCurrentSection () const;
void ClearCurrentSection ();
bool SectionIsEmpty ();
bool NextInSection (const char *&key, const char *&value);
const char *GetValueForKey (const char *key) const;
void SetValueForKey (const char *key, const char *value, bool duplicates=false);
@ -86,6 +91,7 @@ private:
FConfigEntry *RootEntry;
FConfigEntry **LastEntryPtr;
FConfigSection *Next;
FString Note;
char Name[1]; // + length of name
};
@ -99,6 +105,7 @@ private:
FConfigEntry *FindEntry (FConfigSection *section, const char *key) const;
FConfigSection *NewConfigSection (const char *name);
FConfigEntry *NewConfigEntry (FConfigSection *section, const char *key, const char *value);
void SetSectionNote (FConfigSection *section, const char *note);
public:
class Position

View file

@ -1656,71 +1656,6 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads)
return numfound;
}
//==========================================================================
//
// ExpandEnvVars
//
// Expands environment variable references in a string. Intended primarily
// for use with IWAD search paths in config files.
//
//==========================================================================
static FString ExpandEnvVars(const char *searchpathstring)
{
static const char envvarnamechars[] =
"01234567890"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"
"abcdefghijklmnopqrstuvwxyz";
if (searchpathstring == NULL)
return FString ("");
const char *dollar = strchr (searchpathstring,'$');
if (dollar == NULL)
{
return FString (searchpathstring);
}
const char *nextchars = searchpathstring;
FString out = FString (searchpathstring, dollar - searchpathstring);
while ( (dollar != NULL) && (*nextchars != 0) )
{
size_t length = strspn(dollar + 1, envvarnamechars);
if (length != 0)
{
FString varname = FString (dollar + 1, length);
if (stricmp (varname, "progdir") == 0)
{
out += progdir;
}
else
{
char *varvalue = getenv (varname);
if ( (varvalue != NULL) && (strlen(varvalue) != 0) )
{
out += varvalue;
}
}
}
else
{
out += '$';
}
nextchars = dollar + length + 1;
dollar = strchr (nextchars, '$');
if (dollar != NULL)
{
out += FString (nextchars, dollar - nextchars);
}
}
if (*nextchars != 0)
{
out += nextchars;
}
return out;
}
//==========================================================================
//
// CheckIWADinEnvDir

View file

@ -3,7 +3,7 @@
** An .ini parser specifically for zdoom.ini
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -120,6 +120,60 @@ FGameConfigFile::FGameConfigFile ()
#endif
SetValueForKey ("Path", "$DOOMWADDIR", true);
}
// 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.
CreateSectionAtStart("Chex3.Autoload");
CreateSectionAtStart("Chex.Autoload");
CreateSectionAtStart("Strife.Autoload");
CreateSectionAtStart("HexenDemo.Autoload");
CreateSectionAtStart("HexenDK.Autoload");
CreateSectionAtStart("Hexen.Autoload");
CreateSectionAtStart("Heretic.Autoload");
CreateSectionAtStart("FreeDM.Autoload");
CreateSectionAtStart("Freedoom1.Autoload");
CreateSectionAtStart("Freedoom.Autoload");
CreateSectionAtStart("Plutonia.Autoload");
CreateSectionAtStart("TNT.Autoload");
CreateSectionAtStart("Doom2.Autoload");
CreateSectionAtStart("Doom1.Autoload");
CreateSectionAtStart("Doom.Autoload");
CreateSectionAtStart("Global.Autoload");
// The same goes for auto-exec files.
CreateStandardAutoExec("Chex.AutoExec", true);
CreateStandardAutoExec("Strife.AutoExec", true);
CreateStandardAutoExec("Hexen.AutoExec", true);
CreateStandardAutoExec("Heretic.AutoExec", true);
CreateStandardAutoExec("Doom.AutoExec", true);
// Move search paths back to the top.
MoveSectionToStart("FileSearch.Directories");
MoveSectionToStart("IWADSearch.Directories");
// Add some self-documentation.
SetSectionNote("IWADSearch.Directories",
"# These are the directories to automatically search for IWADs.\n"
"# Each directory should be on a separate line, preceded by Path=\n");
SetSectionNote("FileSearch.Directories",
"# These are the directories to search for wads 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 IWADSearch.Directories\n");
SetSectionNote("Doom.AutoExec",
"# 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",
"# WAD files to always load. These are loaded after the IWAD but before\n"
"# any files added with -file. Place each file on its own line, preceded\n"
"# by Path=\n");
SetSectionNote("Doom.Autoload",
"# Wad files to automatically load depending on the game and IWAD you are\n"
"# playing. You may have have files that are loaded for all similar IWADs\n"
"# (the game) and files that are only loaded for particular IWADs. For example,\n"
"# any files listed under Doom.Autoload will be loaded for any version of Doom,\n"
"# but files listed under Doom2.Autoload will only load when you are\n"
"# playing Doom 2.\n\n");
}
FGameConfigFile::~FGameConfigFile ()
@ -128,8 +182,7 @@ FGameConfigFile::~FGameConfigFile ()
void FGameConfigFile::WriteCommentHeader (FILE *file) const
{
fprintf (file, "# This file was generated by " GAMENAME " " DOTVERSIONSTR " on %s"
"# It is not really meant to be modified outside of ZDoom, nyo.\n\n", myasctime ());
fprintf (file, "# This file was generated by " GAMENAME " " DOTVERSIONSTR " on %s\n", myasctime());
}
void FGameConfigFile::MigrateStub (const char *pathname, FConfigFile *config, void *userdata)
@ -507,6 +560,25 @@ FString FGameConfigFile::GetConfigPath (bool tryProg)
#endif
}
void FGameConfigFile::CreateStandardAutoExec(const char *section, bool start)
{
if (!SetSection(section))
{
FString path;
#ifndef unix
path = "$PROGDIR/autoexec.cfg";
#else
path = GetUserFile ("autoexec.cfg");
#endif
SetSection (section, true);
SetValueForKey ("Path", path.GetChars());
}
if (start)
{
MoveSectionToStart(section);
}
}
void FGameConfigFile::AddAutoexec (DArgs *list, const char *game)
{
char section[64];
@ -537,34 +609,19 @@ void FGameConfigFile::AddAutoexec (DArgs *list, const char *game)
{
// If <game>.AutoExec section does not exist, create it
// with a default autoexec.cfg file present.
if (!SetSection (section))
{
FString path;
#ifndef unix
if (Args->CheckParm ("-cdrom"))
{
path = CDROM_DIR "\\autoexec.cfg";
}
else
{
path = progdir;
path += "autoexec.cfg";
}
#else
path = GetUserFile ("autoexec.cfg");
#endif
SetSection (section, true);
SetValueForKey ("Path", path.GetChars());
}
CreateStandardAutoExec(section, false);
// Run any files listed in the <game>.AutoExec section
if (SetSection (section))
if (!SectionIsEmpty())
{
while (NextInSection (key, value))
{
if (stricmp (key, "Path") == 0 && FileExists (value))
if (stricmp (key, "Path") == 0 && *value != '\0')
{
list->AppendArg (value);
FString expanded_path = ExpandEnvVars(value);
if (FileExists(expanded_path))
{
list->AppendArg (ExpandEnvVars(value));
}
}
}
}

View file

@ -2,7 +2,7 @@
** gameconfigfile.h
**
**---------------------------------------------------------------------------
** Copyright 1998-2006 Randy Heit
** Copyright 1998-2008 Randy Heit
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
@ -56,6 +56,7 @@ public:
protected:
void WriteCommentHeader (FILE *file) const;
void CreateStandardAutoExec (const char *section, bool start);
private:
static void MigrateStub (const char *pathname, FConfigFile *config, void *userdata);