- brought in GZDoom's gameconfigfile so that the settings can be transitioned to a more robust implementation.

This commit is contained in:
Christoph Oelckers 2019-10-21 21:36:05 +02:00
parent 0d737ee130
commit bc8578e153
17 changed files with 2188 additions and 61 deletions

View file

@ -500,6 +500,7 @@ set( PLAT_WIN32_SOURCES
audiolib/src/driver_directsound.cpp
glad/src/glad_wgl.c
platform/win32/winbits.cpp
platform/win32/i_specialpaths.cpp
# This needs a rework anyway in order to consolidate the startup dialogs so don't bother to sort in properly.
#startgtk.game.cpp
@ -519,11 +520,13 @@ set( PLAT_SDL_SOURCES
set( PLAT_UNIX_SOURCES
platform/gtk/dynamicgtk.cpp
platform/gtk/gtkbits.cpp
platform/unix/i_specialpaths.cpp
)
set( PLAT_OSX_SOURCES
platform/macos/SDLMain.mm
platform/macos/osxbits.mm
platform/macos/i_specialpaths.mm
)
set( PLAT_COCOA_SOURCES
@ -782,7 +785,9 @@ set (PCH_SOURCES
build/src/voxmodel.cpp
common/rts.cpp
common/gameconfigfile.cpp
common/utility/cmdlib.cpp
common/utility/m_argv.cpp
common/utility/files.cpp
common/utility/files_decompress.cpp

View file

@ -0,0 +1,502 @@
/*
** 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"
#define GAMENAME "Demolition"
#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 + "/*", true);
SetValueForKey ("Path", "$PROGDIR", true);
SetValueForKey ("Path", "$PROGDIR/*", true);
SetValueForKey ("Path", local_app_support + "/*", true);
#elif !defined(__unix__)
SetValueForKey ("Path", "$HOME/*", true);
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/local/share", true);
SetValueForKey ("Path", "/usr/local/share/games", true);
SetValueForKey ("Path", "/usr/share/doom", true);
SetValueForKey ("Path", "/usr/share/games", true);
#endif
}
// 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/local/share/doom", true);
SetValueForKey ("Path", "/usr/local/share/games/doom", true);
SetValueForKey ("Path", "/usr/share/doom", true);
SetValueForKey ("Path", "/usr/share/games/doom", 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);
}
CreateSectionAtStart("Global.Autoload");
// The same goes for auto-exec files.
CreateStandardAutoExec("ShadowWarrior.AutoExec", true);
CreateStandardAutoExec("IonFury.AutoExec", true);
CreateStandardAutoExec("RedneckRides.AutoExec", true);
CreateStandardAutoExec("Redneck.AutoExec", true);
CreateStandardAutoExec("WW2GI.AutoExec", true);
CreateStandardAutoExec("Nam.AutoExec", true);
CreateStandardAutoExec("DukeNukem3D.AutoExec", true);
CreateStandardAutoExec("ShadowWarrior.AutoLoad", true);
CreateStandardAutoExec("IonFury.AutoLoad", true);
CreateStandardAutoExec("RedneckRides.AutoLoad", true);
CreateStandardAutoExec("Redneck.AutoLoad", true);
CreateStandardAutoExec("WW2GI.AutoLoad", true);
CreateStandardAutoExec("Nam.AutoLoad", true);
CreateStandardAutoExec("DukeNukem3D.AutoLoad", true);
// Move search paths back to the top.
MoveSectionToStart("SoundfontSearch.Directories");
MoveSectionToStart("FileSearch.Directories");
MoveSectionToStart("IWADSearch.Directories");
SetSectionNote("DukeNukem3D.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",
"# 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.
#if 0
strncpy (subsection, "LocalServerInfo", sublen);
if (SetSection (section))
{
ReadCVars (0);
}
strncpy (subsection, "Player", sublen);
if (SetSection (section))
{
ReadCVars (0);
}
#endif
strncpy (subsection, "ConsoleAliases", sublen);
if (SetSection (section))
{
const char *name = NULL;
while (NextInSection (key, value))
{
if (stricmp (key, "Name") == 0)
{
name = value;
}
else if (stricmp (key, "Command") == 0 && name != NULL)
{
//C_SetAlias (name, value); does not exist yet but will.
name = NULL;
}
}
}
}
// 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 }
};
*/
const char *key, *value;
sublen = countof(section) - 1 - snprintf(section, countof(section), "%s.", gamename);
subsection = section + countof(section) - sublen - 1;
section[countof(section) - 1] = '\0';
//C_SetDefaultBindings ();
/*
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 ()
{
#if 0
strncpy (subsection, "NetServerInfo", sublen);
if (SetSection (section))
{
ReadCVars (0);
}
if (bModSetup)
{
mysnprintf(subsection, sublen, "NetServerInfo.Mod");
if (SetSection(section))
{
ReadCVars(CVAR_MOD|CVAR_SERVERINFO|CVAR_IGNORE);
}
}
#endif
}
// 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);
#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.AutoExec", game);
// If <game>.AutoExec section does not exist, create it
// with a default autoexec.cfg file present.
CreateStandardAutoExec(section, false);
// Run any files listed in the <game>.AutoExec 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());
}
*/

View file

@ -0,0 +1,76 @@
/*
** gameconfigfile.h
**
**---------------------------------------------------------------------------
** 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 OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifndef __GAMECONFIGFILE_H__
#define __GAMECONFIGFILE_H__
#include "configfile.h"
class FArgs;
class FileWriter;
class FGameConfigFile : public FConfigFile
{
public:
FGameConfigFile ();
~FGameConfigFile ();
void DoAutoloadSetup (/*FIWadManager *iwad_man*/);
void DoGlobalSetup ();
void DoGameSetup (const char *gamename);
void DoKeySetup (const char *gamename);
void DoModSetup (const char *gamename);
void ArchiveGlobalData ();
void ArchiveGameData (const char *gamename);
void AddAutoexec (FArgs *list, const char *gamename);
FString GetConfigPath (bool tryProg);
void ReadNetVars ();
protected:
void WriteCommentHeader (FileWriter *file) const;
void CreateStandardAutoExec (const char *section, bool start);
private:
void SetRavenDefaults (bool isHexen);
void ReadCVars (unsigned flags);
bool bModSetup;
char section[64];
char *subsection;
size_t sublen;
};
extern FGameConfigFile *GameConfig;
#endif //__GAMECONFIGFILE_H__

View file

@ -0,0 +1,18 @@
#pragma once
#include "zstring.h"
#ifdef __unix__
FString GetUserFile (const char *path);
#endif
FString M_GetAppDataPath(bool create);
FString M_GetAutoexecPath();
FString M_GetConfigPath(bool for_reading);
FString M_GetScreenshotsPath();
FString M_GetSavegamesPath();
FString M_GetDocumentsPath();
#ifdef __APPLE__
FString M_GetMacAppSupportPath(const bool create = true);
void M_GetMacSearchDirectories(FString& user_docs, FString& user_app_support, FString& local_app_support);
#endif // __APPLE__

View file

@ -0,0 +1,732 @@
/*
** cmdlib.cpp
** Misc utilities (mostly file handling stuff
**
**---------------------------------------------------------------------------
** Copyright 1999-2016 Randy Heit
** Copyright 2019 Christoph Oelckers
** 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 OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#ifdef _WIN32
#include <direct.h>
#include <io.h>
#else
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#if !defined(__sun)
#include <fts.h>
#endif
#endif
#include "cmdlib.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
extern FString progdir;
//==========================================================================
//
// IsSeperator
//
// Returns true if the character is a path seperator.
//
//==========================================================================
static inline bool IsSeperator (int c)
{
if (c == '/')
return true;
#ifdef _WIN32
if (c == '\\' || c == ':')
return true;
#endif
return false;
}
//==========================================================================
//
// FileExists
//
// Returns true if the given path exists and is a readable file.
//
//==========================================================================
bool FileExists (const char *filename)
{
bool isdir;
bool res = DirEntryExists(filename, &isdir);
return res && !isdir;
}
//==========================================================================
//
// DirExists
//
// Returns true if the given path exists and is a directory.
//
//==========================================================================
bool DirExists(const char *filename)
{
bool isdir;
bool res = DirEntryExists(filename, &isdir);
return res && isdir;
}
//==========================================================================
//
// DirEntryExists
//
// Returns true if the given path exists, be it a directory or a file.
//
//==========================================================================
bool DirEntryExists(const char *pathname, bool *isdir)
{
if (isdir) *isdir = false;
if (pathname == NULL || *pathname == 0)
return false;
#ifndef _WIN32
struct stat info;
bool res = stat(pathname, &info) == 0;
#else
// Windows must use the wide version of stat to preserve non-standard paths.
auto wstr = WideString(pathname);
struct _stat64i32 info;
bool res = _wstat64i32(wstr.c_str(), &info) == 0;
#endif
if (isdir) *isdir = !!(info.st_mode & S_IFDIR);
return res;
}
//==========================================================================
//
// DefaultExtension -- FString version
//
// Appends the extension to a pathname if it does not already have one.
//
//==========================================================================
void DefaultExtension (FString &path, const char *extension)
{
const char *src = &path[int(path.Len())-1];
while (src != &path[0] && !IsSeperator(*src))
{
if (*src == '.')
return; // it has an extension
src--;
}
path += extension;
}
//==========================================================================
//
// ExtractFilePath
//
// Returns the directory part of a pathname.
//
// FIXME: should include the slash, otherwise
// backing to an empty path will be wrong when appending a slash
//
//==========================================================================
FString ExtractFilePath (const char *path)
{
const char *src;
src = path + strlen(path) - 1;
//
// back up until a \ or the start
//
while (src != path && !IsSeperator(*(src-1)))
src--;
return FString(path, src - path);
}
//==========================================================================
//
// ExtractFileBase
//
// Returns the file part of a pathname, optionally including the extension.
//
//==========================================================================
FString ExtractFileBase (const char *path, bool include_extension)
{
const char *src, *dot;
src = path + strlen(path) - 1;
if (src >= path)
{
// back up until a / or the start
while (src != path && !IsSeperator(*(src-1)))
src--;
// Check for files with drive specification but no path
#if defined(_WIN32)
if (src == path && src[0] != 0)
{
if (src[1] == ':')
src += 2;
}
#endif
if (!include_extension)
{
dot = src;
while (*dot && *dot != '.')
{
dot++;
}
return FString(src, dot - src);
}
else
{
return FString(src);
}
}
return FString();
}
//==========================================================================
//
// IsNum
//
// [RH] Returns true if the specified string is a valid decimal number
//
//==========================================================================
bool IsNum (const char *str)
{
while (*str)
{
if (((*str < '0') || (*str > '9')) && (*str != '-'))
{
return false;
}
str++;
}
return true;
}
//==========================================================================
//
// CheckWildcards
//
// [RH] Checks if text matches the wildcard pattern using ? or *
//
//==========================================================================
bool CheckWildcards (const char *pattern, const char *text)
{
if (pattern == NULL || text == NULL)
return true;
while (*pattern)
{
if (*pattern == '*')
{
char stop = tolower (*++pattern);
while (*text && tolower(*text) != stop)
{
text++;
}
if (*text && tolower(*text) == stop)
{
if (CheckWildcards (pattern, text++))
{
return true;
}
pattern--;
}
}
else if (*pattern == '?' || tolower(*pattern) == tolower(*text))
{
pattern++;
text++;
}
else
{
return false;
}
}
return (*pattern | *text) == 0;
}
//==========================================================================
//
// CreatePath
//
// Creates a directory including all levels necessary
//
//==========================================================================
#ifdef _WIN32
void DoCreatePath(const char *fn)
{
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
_splitpath_s(fn, drive, sizeof drive, dir, sizeof dir, nullptr, 0, nullptr, 0);
if ('\0' == *dir)
{
// Root/current/parent directory always exists
return;
}
char path[_MAX_PATH];
_makepath_s(path, sizeof path, drive, dir, nullptr, nullptr);
if ('\0' == *path)
{
// No need to process empty relative path
return;
}
// Remove trailing path separator(s)
for (size_t i = strlen(path); 0 != i; --i)
{
char& lastchar = path[i - 1];
if ('/' == lastchar || '\\' == lastchar)
{
lastchar = '\0';
}
else
{
break;
}
}
// Create all directories for given path
if ('\0' != *path)
{
DoCreatePath(path);
_mkdir(path);
}
}
void CreatePath(const char *fn)
{
char c = fn[strlen(fn)-1];
if (c != '\\' && c != '/')
{
FString name(fn);
name += '/';
DoCreatePath(name);
}
else
{
DoCreatePath(fn);
}
}
#else
void CreatePath(const char *fn)
{
char *copy, *p;
if (fn[0] == '/' && fn[1] == '\0')
{
return;
}
p = copy = strdup(fn);
do
{
p = strchr(p + 1, '/');
if (p != NULL)
{
*p = '\0';
}
if (!DirEntryExists(copy) && mkdir(copy, 0755) == -1)
{
// failed
free(copy);
return;
}
if (p != NULL)
{
*p = '/';
}
} while (p);
free(copy);
}
#endif
//==========================================================================
//
// strbin -- In-place version
//
// [RH] Replaces the escape sequences in a string with actual escaped characters.
// This operation is done in-place. The result is the new length of the string.
//
//==========================================================================
int strbin (char *str)
{
char *start = str;
char *p = str, c;
int i;
while ( (c = *p++) ) {
if (c != '\\') {
*str++ = c;
} else {
switch (*p) {
case 'a':
*str++ = '\a';
break;
case 'b':
*str++ = '\b';
break;
case 'c':
*str++ = '\034'; // TEXTCOLOR_ESCAPE
break;
case 'f':
*str++ = '\f';
break;
case 'n':
*str++ = '\n';
break;
case 't':
*str++ = '\t';
break;
case 'r':
*str++ = '\r';
break;
case 'v':
*str++ = '\v';
break;
case '?':
*str++ = '\?';
break;
case '\n':
break;
case 'x':
case 'X':
c = 0;
for (i = 0; i < 2; i++)
{
p++;
if (*p >= '0' && *p <= '9')
c = (c << 4) + *p-'0';
else if (*p >= 'a' && *p <= 'f')
c = (c << 4) + 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c = (c << 4) + 10 + *p-'A';
else
{
p--;
break;
}
}
*str++ = c;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = *p - '0';
for (i = 0; i < 2; i++)
{
p++;
if (*p >= '0' && *p <= '7')
c = (c << 3) + *p - '0';
else
{
p--;
break;
}
}
*str++ = c;
break;
default:
*str++ = *p;
break;
}
p++;
}
}
*str = 0;
return int(str - start);
}
//==========================================================================
//
// strbin1 -- String-creating version
//
// [RH] Replaces the escape sequences in a string with actual escaped characters.
// The result is a new string.
//
//==========================================================================
FString strbin1 (const char *start)
{
FString result;
const char *p = start;
char c;
int i;
while ( (c = *p++) ) {
if (c != '\\') {
result << c;
} else {
switch (*p) {
case 'a':
result << '\a';
break;
case 'b':
result << '\b';
break;
case 'c':
result << '\034'; // TEXTCOLOR_ESCAPE
break;
case 'f':
result << '\f';
break;
case 'n':
result << '\n';
break;
case 't':
result << '\t';
break;
case 'r':
result << '\r';
break;
case 'v':
result << '\v';
break;
case '?':
result << '\?';
break;
case '\n':
break;
case 'x':
case 'X':
c = 0;
for (i = 0; i < 2; i++)
{
p++;
if (*p >= '0' && *p <= '9')
c = (c << 4) + *p-'0';
else if (*p >= 'a' && *p <= 'f')
c = (c << 4) + 10 + *p-'a';
else if (*p >= 'A' && *p <= 'F')
c = (c << 4) + 10 + *p-'A';
else
{
p--;
break;
}
}
result << c;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
c = *p - '0';
for (i = 0; i < 2; i++)
{
p++;
if (*p >= '0' && *p <= '7')
c = (c << 3) + *p - '0';
else
{
p--;
break;
}
}
result << c;
break;
default:
result << *p;
break;
}
p++;
}
}
return result;
}
//==========================================================================
//
// 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;
}
//==========================================================================
//
// NicePath
//
// Handles paths with leading ~ characters on Unix as well as environment
// variable substitution. On Windows, this is identical to ExpandEnvVars.
//
//==========================================================================
FString NicePath(const char *path)
{
#ifdef _WIN32
return ExpandEnvVars(path);
#else
if (path == NULL || *path == '\0')
{
return FString("");
}
if (*path != '~')
{
return ExpandEnvVars(path);
}
passwd *pwstruct;
const char *slash;
if (path[1] == '/' || path[1] == '\0')
{ // Get my home directory
pwstruct = getpwuid(getuid());
slash = path + 1;
}
else
{ // Get somebody else's home directory
slash = strchr(path, '/');
if (slash == NULL)
{
slash = path + strlen(path);
}
FString who(path, slash - path);
pwstruct = getpwnam(who);
}
if (pwstruct == NULL)
{
return ExpandEnvVars(path);
}
FString where(pwstruct->pw_dir);
if (*slash != '\0')
{
where += ExpandEnvVars(slash);
}
return where;
#endif
}
//==========================================================================
//
//
//
//==========================================================================
bool IsAbsPath(const char *name)
{
if (IsSeperator(name[0])) return true;
#ifdef _WIN32
/* [A-Za-z]: (for Windows) */
if (isalpha(name[0]) && name[1] == ':') return true;
#endif /* _WIN32 */
return 0;
}

View file

@ -0,0 +1,56 @@
// cmdlib.h
#ifndef __CMDLIB__
#define __CMDLIB__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include "zstring.h"
template <typename T, size_t N>
char(&_ArraySizeHelper(T(&array)[N]))[N];
#define countof( array ) (sizeof( _ArraySizeHelper( array ) ))
// the dec offsetof macro doesnt work very well...
#define myoffsetof(type,identifier) ((size_t)&((type *)alignof(type))->identifier - alignof(type))
bool FileExists (const char *filename);
bool DirExists(const char *filename);
bool DirEntryExists (const char *pathname, bool *isdir = nullptr);
extern FString progdir;
void DefaultExtension (FString &path, const char *extension);
FString ExtractFilePath (const char *path);
FString ExtractFileBase (const char *path, bool keep_extension=false);
struct FScriptPosition;
bool IsNum (const char *str); // [RH] added
bool CheckWildcards (const char *pattern, const char *text);
int strbin (char *str);
FString strbin1 (const char *start);
void CreatePath(const char * fn);
FString ExpandEnvVars(const char *searchpathstring);
FString NicePath(const char *path);
struct FFileList
{
FString Filename;
bool isDirectory;
};
bool IsAbsPath(const char*);
#endif

View file

@ -34,6 +34,7 @@
#include <string.h>
#include "m_argv.h"
#include "zstring.h"
//===========================================================================
//
@ -84,7 +85,7 @@ FArgs::FArgs(int argc, const char** argv)
//
//===========================================================================
FArgs::FArgs(int argc, const std::string *argv)
FArgs::FArgs(int argc, FString *argv)
{
AppendArgs(argc, argv);
}
@ -111,7 +112,7 @@ FArgs &FArgs::operator=(const FArgs &other)
void FArgs::SetArgs(int argc, char **argv)
{
Argv.resize(argc);
Argv.Resize(argc);
for (int i = 0; i < argc; ++i)
{
Argv[i] = argv[i];
@ -126,7 +127,7 @@ void FArgs::SetArgs(int argc, char **argv)
void FArgs::FlushArgs()
{
Argv.clear();
Argv.Clear();
}
//===========================================================================
@ -140,9 +141,9 @@ void FArgs::FlushArgs()
int FArgs::CheckParm(const char *check, int start) const
{
for (unsigned i = start; i < Argv.size(); ++i)
for (unsigned i = start; i < Argv.Size(); ++i)
{
if (0 == stricmp(check, Argv[i].c_str()))
if (0 == stricmp(check, Argv[i]))
{
return i;
}
@ -159,7 +160,7 @@ int FArgs::CheckParm(const char *check, int start) const
//
//===========================================================================
int FArgs::CheckParmList(const char *check, const std::string **strings, int start) const
int FArgs::CheckParmList(const char *check, FString **strings, int start) const
{
unsigned int i, parmat = CheckParm(check, start);
@ -171,7 +172,7 @@ int FArgs::CheckParmList(const char *check, const std::string **strings, int sta
}
return 0;
}
for (i = ++parmat; i < Argv.size(); ++i)
for (i = ++parmat; i < Argv.Size(); ++i)
{
if (Argv[i][0] == '-' || Argv[i][1] == '+')
{
@ -198,10 +199,10 @@ const char *FArgs::CheckValue(const char *check) const
{
int i = CheckParm(check);
if (i > 0 && i < (int)Argv.size() - 1)
if (i > 0 && i < (int)Argv.Size() - 1)
{
i++;
return Argv[i][0] != '+' && Argv[i][0] != '-' ? Argv[i].c_str() : nullptr;
return Argv[i][0] != '+' && Argv[i][0] != '-' ? Argv[i].GetChars() : nullptr;
}
else
{
@ -218,21 +219,21 @@ const char *FArgs::CheckValue(const char *check) const
//
//===========================================================================
std::string FArgs::TakeValue(const char *check)
FString FArgs::TakeValue(const char *check)
{
int i = CheckParm(check);
std::string out;
FString out;
if (i > 0 && i < (int)Argv.size())
if (i > 0 && i < (int)Argv.Size())
{
if (i < (int)Argv.size() - 1 && Argv[i+1][0] != '+' && Argv[i+1][0] != '-')
if (i < (int)Argv.Size() - 1 && Argv[i+1][0] != '+' && Argv[i+1][0] != '-')
{
out = Argv[i+1];
Argv.erase(Argv.begin() + i, Argv.begin() + (i+2)); // Delete the parm and its value.
Argv.Delete(i, 2); // Delete the parm and its value.
}
else
{
Argv.erase(Argv.begin() + i); // Just delete the parm, since it has no value.
Argv.Delete(i); // Just delete the parm, since it has no value.
}
}
return out;
@ -248,13 +249,13 @@ void FArgs::RemoveArgs(const char *check)
{
int i = CheckParm(check);
if (i > 0 && i < (int)Argv.size() - 1)
if (i > 0 && i < (int)Argv.Size() - 1)
{
do
{
RemoveArg(i);
}
while (Argv[i][0] != '+' && Argv[i][0] != '-' && i < (int)Argv.size() - 1);
while (Argv[i][0] != '+' && Argv[i][0] != '-' && i < (int)Argv.Size() - 1);
}
}
@ -268,20 +269,20 @@ void FArgs::RemoveArgs(const char *check)
const char *FArgs::GetArg(int arg) const
{
return ((unsigned)arg < Argv.size()) ? Argv[arg].c_str() : nullptr;
return ((unsigned)arg < Argv.Size()) ? Argv[arg].GetChars() : nullptr;
}
//===========================================================================
//
// FArgs :: GetArgList
//
// Returns a pointer to the std::string at a particular position.
// Returns a pointer to the FString at a particular position.
//
//===========================================================================
const std::string *FArgs::GetArgList(int arg) const
FString *FArgs::GetArgList(int arg) const
{
return ((unsigned)arg < Argv.size()) ? &Argv[arg] : nullptr;
return ((unsigned)arg < Argv.Size()) ? &Argv[arg] : nullptr;
}
//===========================================================================
@ -292,7 +293,7 @@ const std::string *FArgs::GetArgList(int arg) const
int FArgs::NumArgs() const
{
return (int)Argv.size();
return (int)Argv.Size();
}
//===========================================================================
@ -304,26 +305,27 @@ int FArgs::NumArgs() const
//
//===========================================================================
void FArgs::AppendArg(std::string arg)
void FArgs::AppendArg(FString arg)
{
Argv.push_back(arg);
Argv.Push(arg);
}
//===========================================================================
//
// FArgs :: AppendArgs
//
// Adds an array of std::strings to argv.
// Adds an array of FStrings to argv.
//
//===========================================================================
void FArgs::AppendArgs(int argc, const std::string *argv)
void FArgs::AppendArgs(int argc, const FString *argv)
{
if (argv != nullptr && argc > 0)
if (argv != NULL && argc > 0)
{
Argv.Grow(argc);
for (int i = 0; i < argc; ++i)
{
Argv.push_back(argv[i]);
Argv.Push(argv[i]);
}
}
}
@ -338,7 +340,7 @@ void FArgs::AppendArgs(int argc, const std::string *argv)
void FArgs::RemoveArg(int argindex)
{
Argv.erase(Argv.begin() + argindex);
Argv.Delete(argindex);
}
//===========================================================================
@ -354,19 +356,19 @@ void FArgs::RemoveArg(int argindex)
void FArgs::CollectFiles(const char *param, const char *extension)
{
std::vector<std::string> work;
TArray<FString> work;
unsigned int i;
size_t extlen = extension == nullptr ? 0 : strlen(extension);
// Step 1: Find suitable arguments before the first switch.
i = 1;
while (i < Argv.size() && Argv[i][0] != '-' && Argv[i][0] != '+')
while (i < Argv.Size() && Argv[i][0] != '-' && Argv[i][0] != '+')
{
bool useit;
if (extlen > 0)
{ // Argument's extension must match.
size_t len = Argv[i].length();
size_t len = Argv[i].Len();
useit = (len >= extlen && stricmp(&Argv[i][len - extlen], extension) == 0);
}
else
@ -375,8 +377,8 @@ void FArgs::CollectFiles(const char *param, const char *extension)
}
if (useit)
{
work.push_back(Argv[i]);
RemoveArg(i);
work.Push(Argv[i]);
Argv.Delete(i);
}
else
{
@ -387,11 +389,11 @@ void FArgs::CollectFiles(const char *param, const char *extension)
// Step 2: Find each occurence of -param and add its arguments to work.
while ((i = CheckParm(param, i)) > 0)
{
RemoveArg(i);
while (i < Argv.size() && Argv[i][0] != '-' && Argv[i][0] != '+')
Argv.Delete(i);
while (i < Argv.Size() && Argv[i][0] != '-' && Argv[i][0] != '+')
{
work.push_back(Argv[i]);
RemoveArg(i);
work.Push(Argv[i]);
Argv.Delete(i);
}
}
@ -404,10 +406,10 @@ void FArgs::CollectFiles(const char *param, const char *extension)
#endif
// Step 3: Add work back to Argv, as long as it's non-empty.
if (work.size() > 0)
if (work.Size() > 0)
{
Argv.push_back(param);
AppendArgs(work.size(), &work[0]);
Argv.Push(param);
AppendArgs(work.Size(), &work[0]);
}
}
@ -423,7 +425,7 @@ void FArgs::CollectFiles(const char *param, const char *extension)
FArgs *FArgs::GatherFiles(const char *param) const
{
const std::string *files;
FString *files;
int filecount;
filecount = CheckParmList(param, &files);

View file

@ -34,8 +34,8 @@
#ifndef __M_ARGV_H__
#define __M_ARGV_H__
#include <vector>
#include <string>
#include "tarray.h"
#include "zstring.h"
//
// MISC
@ -47,12 +47,12 @@ public:
FArgs(const FArgs &args);
FArgs(int argc, char **argv);
FArgs(int argc, const char** argv);
FArgs(int argc, const std::string *argv);
FArgs(int argc, FString *argv);
FArgs &operator=(const FArgs &other);
void AppendArg(std::string arg);
void AppendArgs(int argc, const std::string *argv);
void AppendArg(FString arg);
void AppendArgs(int argc, const FString *argv);
void RemoveArg(int argindex);
void RemoveArgs(const char *check);
void SetArgs(int argc, char **argv);
@ -61,16 +61,16 @@ public:
void SetArg(int argnum, const char *arg);
int CheckParm(const char *check, int start=1) const; // Returns the position of the given parameter in the arg list (0 if not found).
int CheckParmList(const char *check, const std::string **strings, int start=1) const;
int CheckParmList(const char *check, FString **strings, int start=1) const;
const char *CheckValue(const char *check) const;
const char *GetArg(int arg) const;
const std::string *GetArgList(int arg) const;
std::string TakeValue(const char *check);
FString *GetArgList(int arg) const;
FString TakeValue(const char *check);
int NumArgs() const;
void FlushArgs();
private:
std::vector<std::string> Argv;
TArray<FString> Argv;
};
extern FArgs *Args;

View file

@ -6332,7 +6332,7 @@ int G_FPSLimit(void)
EDUKE32_STATIC_ASSERT(sizeof(actor_t)%4 == 0);
EDUKE32_STATIC_ASSERT(sizeof(DukePlayer_t)%4 == 0);
int app_main()
int app_main(int argc, const char * const*argv)
{
#ifndef NETCODE_DISABLE
if (enet_initialize() != 0)

View file

@ -1724,8 +1724,6 @@ void G_SetupFilenameBasedMusic(char *nameBuf, const char *fileName)
for (auto & ext : exts)
{
buildvfs_kfd kFile;
Bmemcpy(p+1, ext, Bstrlen(ext) + 1);
if (testkopen(nameBuf, 0))

View file

@ -1463,8 +1463,6 @@ void gameDisplay3DRScreen()
if (testkopen("3dr.ivf", 0) || testkopen("3dr.anm", 0))
{
Anim_Play("3dr.anm");
kclose(i);
Anim_Play("3dr.anm");
G_FadePalette(0, 0, 0, 252);
I_ClearAllInput();
}

View file

@ -0,0 +1,222 @@
/*
** i_specialpaths.mm
** Gets special system folders where data should be stored. (macOS version)
**
**---------------------------------------------------------------------------
** Copyright 2013-2016 Randy Heit
** Copyright 2016 Christoph Oelckers
** 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 OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#import <Foundation/NSFileManager.h>
#include "cmdlib.h"
#include "m_misc.h"
#include "version.h" // for GAMENAME
static FString GetSpecialPath(const NSSearchPathDirectory kind, const BOOL create = YES, const NSSearchPathDomainMask domain = NSUserDomainMask)
{
NSURL* url = [[NSFileManager defaultManager] URLForDirectory:kind
inDomain:domain
appropriateForURL:nil
create:create
error:nil];
char utf8path[PATH_MAX];
if ([url getFileSystemRepresentation:utf8path
maxLength:sizeof utf8path])
{
return utf8path;
}
return FString();
}
FString M_GetMacAppSupportPath(const bool create)
{
return GetSpecialPath(NSApplicationSupportDirectory, create);
}
void M_GetMacSearchDirectories(FString& user_docs, FString& user_app_support, FString& local_app_support)
{
FString path = GetSpecialPath(NSDocumentDirectory);
user_docs = path.IsEmpty()
? "~/" GAME_DIR
: (path + "/" GAME_DIR);
#define LIBRARY_APPSUPPORT "/Library/Application Support/"
path = M_GetMacAppSupportPath();
user_app_support = path.IsEmpty()
? "~" LIBRARY_APPSUPPORT GAME_DIR
: (path + "/" GAME_DIR);
path = GetSpecialPath(NSApplicationSupportDirectory, YES, NSLocalDomainMask);
local_app_support = path.IsEmpty()
? LIBRARY_APPSUPPORT GAME_DIR
: (path + "/" GAME_DIR);
#undef LIBRARY_APPSUPPORT
}
//===========================================================================
//
// M_GetAppDataPath macOS
//
// Returns the path for the AppData folder.
//
//===========================================================================
FString M_GetAppDataPath(bool create)
{
FString path = M_GetMacAppSupportPath(create);
if (path.IsEmpty())
{
path = progdir;
}
path += "/" GAMENAMELOWERCASE;
if (create) CreatePath(path);
return path;
}
//===========================================================================
//
// M_GetAutoexecPath macOS
//
// Returns the expected location of autoexec.cfg.
//
//===========================================================================
FString M_GetAutoexecPath()
{
FString path = GetSpecialPath(NSDocumentDirectory);
if (path.IsNotEmpty())
{
path += "/" GAME_DIR "/autoexec.cfg";
}
return path;
}
//===========================================================================
//
// M_GetConfigPath macOS
//
// Returns the path to the config file. On Windows, this can vary for reading
// vs writing. i.e. If $PROGDIR/zdoom-<user>.ini does not exist, it will try
// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini.
//
//===========================================================================
FString M_GetConfigPath(bool for_reading)
{
FString path = GetSpecialPath(NSLibraryDirectory);
if (path.IsNotEmpty())
{
// There seems to be no way to get Preferences path via NSFileManager
path += "/Preferences/";
CreatePath(path);
if (!DirExists(path))
{
path = FString();
}
}
return path + GAMENAMELOWERCASE ".ini";
}
//===========================================================================
//
// M_GetScreenshotsPath macOS
//
// Returns the path to the default screenshots directory.
//
//===========================================================================
FString M_GetScreenshotsPath()
{
FString path = GetSpecialPath(NSDocumentDirectory);
if (path.IsEmpty())
{
path = "~/";
}
else
{
path += "/" GAME_DIR "/Screenshots/";
}
return path;
}
//===========================================================================
//
// M_GetSavegamesPath macOS
//
// Returns the path to the default save games directory.
//
//===========================================================================
FString M_GetSavegamesPath()
{
FString path = GetSpecialPath(NSDocumentDirectory);
if (path.IsNotEmpty())
{
path += "/" GAME_DIR "/Savegames/";
}
return path;
}
//===========================================================================
//
// M_GetDocumentsPath macOS
//
// Returns the path to the default documents directory.
//
//===========================================================================
FString M_GetDocumentsPath()
{
FString path = GetSpecialPath(NSDocumentDirectory);
if (path.IsNotEmpty())
{
path += "/" GAME_DIR "/";
}
return path;
}

View file

@ -0,0 +1,187 @@
/*
** i_specialpaths.cpp
** Gets special system folders where data should be stored. (Unix version)
**
**---------------------------------------------------------------------------
** Copyright 2013-2016 Randy Heit
** Copyright 2016 Christoph Oelckers
** 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 OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <sys/stat.h>
#include <sys/types.h>
#include "i_system.h"
#include "cmdlib.h"
#include "doomerrors.h"
#include "version.h" // for GAMENAME
FString GetUserFile (const char *file)
{
FString path;
struct stat info;
path = NicePath("$HOME/" GAME_DIR "/");
if (stat (path, &info) == -1)
{
struct stat extrainfo;
// Sanity check for $HOME/.config
FString configPath = NicePath("$HOME/.config/");
if (stat (configPath, &extrainfo) == -1)
{
if (mkdir (configPath, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
{
I_FatalError ("Failed to create $HOME/.config directory:\n%s", strerror(errno));
}
}
else if (!S_ISDIR(extrainfo.st_mode))
{
I_FatalError ("$HOME/.config must be a directory");
}
// This can be removed after a release or two
// Transfer the old zdoom directory to the new location
bool moved = false;
FString oldpath = NicePath("$HOME/." GAMENAMELOWERCASE "/");
if (stat (oldpath, &extrainfo) != -1)
{
if (rename(oldpath, path) == -1)
{
I_Error ("Failed to move old " GAMENAMELOWERCASE " directory (%s) to new location (%s).",
oldpath.GetChars(), path.GetChars());
}
else
moved = true;
}
if (!moved && mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR) == -1)
{
I_FatalError ("Failed to create %s directory:\n%s",
path.GetChars(), strerror (errno));
}
}
else
{
if (!S_ISDIR(info.st_mode))
{
I_FatalError ("%s must be a directory", path.GetChars());
}
}
path += file;
return path;
}
//===========================================================================
//
// M_GetAppDataPath Unix
//
// Returns the path for the AppData folder.
//
//===========================================================================
FString M_GetAppDataPath(bool create)
{
// Don't use GAME_DIR and such so that ZDoom and its child ports can
// share the node cache.
FString path = NicePath("$HOME/.config/" GAMENAMELOWERCASE);
if (create)
{
CreatePath(path);
}
return path;
}
//===========================================================================
//
// M_GetAutoexecPath Unix
//
// Returns the expected location of autoexec.cfg.
//
//===========================================================================
FString M_GetAutoexecPath()
{
return GetUserFile("autoexec.cfg");
}
//===========================================================================
//
// M_GetConfigPath Unix
//
// Returns the path to the config file. On Windows, this can vary for reading
// vs writing. i.e. If $PROGDIR/zdoom-<user>.ini does not exist, it will try
// to read from $PROGDIR/zdoom.ini, but it will never write to zdoom.ini.
//
//===========================================================================
FString M_GetConfigPath(bool for_reading)
{
return GetUserFile(GAMENAMELOWERCASE ".ini");
}
//===========================================================================
//
// M_GetScreenshotsPath Unix
//
// Returns the path to the default screenshots directory.
//
//===========================================================================
FString M_GetScreenshotsPath()
{
return NicePath("$HOME/" GAME_DIR "/screenshots/");
}
//===========================================================================
//
// M_GetSavegamesPath Unix
//
// Returns the path to the default save games directory.
//
//===========================================================================
FString M_GetSavegamesPath()
{
return NicePath("$HOME/" GAME_DIR);
}
//===========================================================================
//
// M_GetDocumentsPath Unix
//
// Returns the path to the default documents directory.
//
//===========================================================================
FString M_GetDocumentsPath()
{
return NicePath("$HOME/" GAME_DIR);
}

View file

@ -0,0 +1,336 @@
/*
** i_specialpaths.cpp
** Gets special system folders where data should be stored. (Windows version)
**
**---------------------------------------------------------------------------
** Copyright 2013-2016 Randy Heit
** Copyright 2016 Christoph Oelckers
** 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 OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include <windows.h>
#include <lmcons.h>
#include <shlobj.h>
#include "i_specialpaths.h"
#include "printf.h"
#include "cmdlib.h"
//#include "version.h" // for GAMENAME
// Stuff that needs to be set up later.
extern FString progdir;
static bool batchrun;
#define GAMENAMELOWERCASE "demolition"
#define GAMENAME "Demolition"
#define GAME_DIR "demolition"
// Vanilla MinGW does not have folder ids
#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
static const GUID FOLDERID_LocalAppData = { 0xf1b32785, 0x6fba, 0x4fcf, 0x9d, 0x55, 0x7b, 0x8e, 0x7f, 0x15, 0x70, 0x91 };
static const GUID FOLDERID_RoamingAppData = { 0x3eb685db, 0x65f9, 0x4cf6, 0xa0, 0x3a, 0xe3, 0xef, 0x65, 0x72, 0x9f, 0x3d };
static const GUID FOLDERID_SavedGames = { 0x4c5c32ff, 0xbb9d, 0x43b0, 0xb5, 0xb4, 0x2d, 0x72, 0xe5, 0x4e, 0xaa, 0xa4 };
static const GUID FOLDERID_Documents = { 0xfdd39ad0, 0x238f, 0x46af, 0xad, 0xb4, 0x6c, 0x85, 0x48, 0x03, 0x69, 0xc7 };
static const GUID FOLDERID_Pictures = { 0x33e28130, 0x4e1e, 0x4676, 0x83, 0x5a, 0x98, 0x39, 0x5c, 0x3b, 0xc3, 0xbb };
#endif
//===========================================================================
//
// IsProgramDirectoryWritable
//
// If the program directory is writable, then dump everything in there for
// historical reasons. Otherwise, known folders get used instead.
//
//===========================================================================
bool UseKnownFolders()
{
// Cache this value so the semantics don't change during a single run
// of the program. (e.g. Somebody could add write access while the
// program is running.)
static int iswritable = -1;
HANDLE file;
if (iswritable >= 0)
{
return !iswritable;
}
std::wstring testpath = progdir.WideString() + L"writest";
file = CreateFile(testpath.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL);
if (file != INVALID_HANDLE_VALUE)
{
CloseHandle(file);
if (!batchrun) Printf("Using program directory for storage\n");
iswritable = true;
return false;
}
if (!batchrun) Printf("Using known folders for storage\n");
iswritable = false;
return true;
}
//===========================================================================
//
// GetKnownFolder
//
// Returns the known_folder if SHGetKnownFolderPath is available, otherwise
// returns the shell_folder using SHGetFolderPath.
//
//===========================================================================
bool GetKnownFolder(int shell_folder, REFKNOWNFOLDERID known_folder, bool create, FString &path)
{
PWSTR wpath;
if (FAILED(SHGetKnownFolderPath(known_folder, create ? KF_FLAG_CREATE : 0, NULL, &wpath)))
{
return false;
}
path = wpath;
CoTaskMemFree(wpath);
return true;
}
//===========================================================================
//
// M_GetAppDataPath Windows
//
// Returns the path for the AppData folder.
//
//===========================================================================
FString M_GetAppDataPath(bool create)
{
FString path;
if (!GetKnownFolder(CSIDL_LOCAL_APPDATA, FOLDERID_LocalAppData, create, path))
{ // Failed (e.g. On Win9x): use program directory
path = progdir;
}
// Don't use GAME_DIR and such so that demolition and its child ports can
// share the node cache.
path += "/" GAMENAMELOWERCASE;
path.Substitute("//", "/"); // needed because progdir ends with a slash.
if (create)
{
CreatePath(path);
}
return path;
}
//===========================================================================
//
// M_GetAutoexecPath Windows
//
// Returns the expected location of autoexec.cfg.
//
//===========================================================================
FString M_GetAutoexecPath()
{
return "$PROGDIR/autoexec.cfg";
}
//===========================================================================
//
// M_GetConfigPath Windows
//
// Returns the path to the config file. On Windows, this can vary for reading
// vs writing. i.e. If $PROGDIR/demolition-<user>.ini does not exist, it will try
// to read from $PROGDIR/demolition.ini, but it will never write to demolition.ini.
//
//===========================================================================
FString M_GetConfigPath(bool for_reading)
{
FString path;
HRESULT hr;
path.Format("%s" GAMENAMELOWERCASE "_portable.ini", progdir.GetChars());
if (FileExists(path))
{
return path;
}
path = "";
// Construct a user-specific config name
if (UseKnownFolders() && GetKnownFolder(CSIDL_APPDATA, FOLDERID_RoamingAppData, true, path))
{
path += "/" GAME_DIR;
CreatePath(path);
path += "/" GAMENAMELOWERCASE ".ini";
}
else
{ // construct "$PROGDIR/demolition-$USER.ini"
WCHAR uname[UNLEN+1];
DWORD unamelen = UNLEN;
path = progdir;
hr = GetUserNameW(uname, &unamelen);
if (SUCCEEDED(hr) && uname[0] != 0)
{
// Is it valid for a user name to have slashes?
// Check for them and substitute just in case.
auto probe = uname;
while (*probe != 0)
{
if (*probe == '\\' || *probe == '/')
*probe = '_';
++probe;
}
path << GAMENAMELOWERCASE "-" << FString(uname) << ".ini";
}
else
{ // Couldn't get user name, so just use demolition.ini
path += GAMENAMELOWERCASE ".ini";
}
}
// If we are reading the config file, check if it exists. If not, fallback
// to $PROGDIR/demolition.ini
if (for_reading)
{
if (!FileExists(path))
{
path = progdir;
path << GAMENAMELOWERCASE ".ini";
}
}
return path;
}
//===========================================================================
//
// M_GetScreenshotsPath Windows
//
// Returns the path to the default screenshots directory.
//
//===========================================================================
// I'm not sure when FOLDERID_Screenshots was added, but it was probably
// for Windows 8, since it's not in the v7.0 Windows SDK.
static const GUID MyFOLDERID_Screenshots = { 0xb7bede81, 0xdf94, 0x4682, 0xa7, 0xd8, 0x57, 0xa5, 0x26, 0x20, 0xb8, 0x6f };
FString M_GetScreenshotsPath()
{
FString path;
if (!UseKnownFolders())
{
return progdir;
}
else if (GetKnownFolder(-1, MyFOLDERID_Screenshots, true, path))
{
path << "/" GAMENAME;
}
else if (GetKnownFolder(CSIDL_MYPICTURES, FOLDERID_Pictures, true, path))
{
path << "/Screenshots/" GAMENAME;
}
else
{
return progdir;
}
CreatePath(path);
return path;
}
//===========================================================================
//
// M_GetSavegamesPath Windows
//
// Returns the path to the default save games directory.
//
//===========================================================================
FString M_GetSavegamesPath()
{
FString path;
if (!UseKnownFolders())
{
return progdir;
}
// Try standard Saved Games folder
else if (GetKnownFolder(-1, FOLDERID_SavedGames, true, path))
{
path << "/" GAMENAME;
}
// Try defacto My Documents/My Games folder
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
{
// I assume since this isn't a standard folder, it doesn't have
// a localized name either.
path << "/My Games/" GAMENAME;
CreatePath(path);
}
else
{
path = progdir;
}
return path;
}
//===========================================================================
//
// M_GetDocumentsPath Windows
//
// Returns the path to the default documents directory.
//
//===========================================================================
FString M_GetDocumentsPath()
{
FString path;
// A portable INI means that this storage location should also be portable.
path.Format("%s" GAMENAME "_portable.ini", progdir.GetChars());
if (FileExists(path))
{
return progdir;
}
if (!UseKnownFolders())
{
return progdir;
}
// Try defacto My Documents/My Games folder
else if (GetKnownFolder(CSIDL_PERSONAL, FOLDERID_Documents, true, path))
{
// I assume since this isn't a standard folder, it doesn't have
// a localized name either.
path << "/My Games/" GAMENAME;
CreatePath(path);
}
else
{
path = progdir;
}
return path;
}

View file

@ -3555,8 +3555,6 @@ void Net_SendUserMapName(void)
void Net_ReceiveUserMapName(uint8_t *pbuf, int32_t packbufleng)
{
int32_t i;
Bstrcpy(boardfilename,(char *)pbuf+1);
boardfilename[packbufleng-1] = 0;
Bcorrectfilename(boardfilename,0);

View file

@ -157,7 +157,6 @@ static int osdcmd_changelevel(osdcmdptr_t parm)
static int osdcmd_map(osdcmdptr_t parm)
{
int32_t i;
char filename[BMAX_PATH];
const int32_t wildcardp = parm->numparms==1 &&

View file

@ -2279,8 +2279,6 @@ void G_SetupFilenameBasedMusic(char *nameBuf, const char *fileName, int levelNum
for (auto & ext : exts)
{
int32_t kFile;
Bmemcpy(p+1, ext, Bstrlen(ext) + 1);
if (testkopen(nameBuf, 0))