diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index b9e79d641..9f2dbf60b 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -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 diff --git a/source/common/gameconfigfile.cpp b/source/common/gameconfigfile.cpp new file mode 100644 index 000000000..bc61d4e57 --- /dev/null +++ b/source/common/gameconfigfile.cpp @@ -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 + +#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(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 .AutoExec section does not exist, create it + // with a default autoexec.cfg file present. + CreateStandardAutoExec(section, false); + // Run any files listed in the .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()); +} +*/ \ No newline at end of file diff --git a/source/common/gameconfigfile.h b/source/common/gameconfigfile.h new file mode 100644 index 000000000..9b09fe676 --- /dev/null +++ b/source/common/gameconfigfile.h @@ -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__ diff --git a/source/common/i_specialpaths.h b/source/common/i_specialpaths.h new file mode 100644 index 000000000..a1ef210b1 --- /dev/null +++ b/source/common/i_specialpaths.h @@ -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__ diff --git a/source/common/utility/cmdlib.cpp b/source/common/utility/cmdlib.cpp new file mode 100644 index 000000000..90ceba980 --- /dev/null +++ b/source/common/utility/cmdlib.cpp @@ -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 +#include +#else +#include +#include +#include +#include +#if !defined(__sun) +#include +#endif +#endif +#include "cmdlib.h" + +#include +#include +#include + + +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; +} diff --git a/source/common/utility/cmdlib.h b/source/common/utility/cmdlib.h new file mode 100644 index 000000000..eec968e1f --- /dev/null +++ b/source/common/utility/cmdlib.h @@ -0,0 +1,56 @@ +// cmdlib.h + +#ifndef __CMDLIB__ +#define __CMDLIB__ + + +#include +#include +#include +#include +#include +#include +#include "zstring.h" + +template +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 diff --git a/source/common/utility/m_argv.cpp b/source/common/utility/m_argv.cpp index 4c7290de0..4788b7cff 100644 --- a/source/common/utility/m_argv.cpp +++ b/source/common/utility/m_argv.cpp @@ -34,6 +34,7 @@ #include #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 work; + TArray 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); diff --git a/source/common/utility/m_argv.h b/source/common/utility/m_argv.h index 1a723bb34..7f0ada822 100644 --- a/source/common/utility/m_argv.h +++ b/source/common/utility/m_argv.h @@ -34,8 +34,8 @@ #ifndef __M_ARGV_H__ #define __M_ARGV_H__ -#include -#include +#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 Argv; + TArray Argv; }; extern FArgs *Args; diff --git a/source/duke3d/src/game.cpp b/source/duke3d/src/game.cpp index f539039d1..6aeff3a74 100644 --- a/source/duke3d/src/game.cpp +++ b/source/duke3d/src/game.cpp @@ -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) diff --git a/source/duke3d/src/premap.cpp b/source/duke3d/src/premap.cpp index 6a855e8b8..749005dc5 100644 --- a/source/duke3d/src/premap.cpp +++ b/source/duke3d/src/premap.cpp @@ -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)) diff --git a/source/duke3d/src/screens.cpp b/source/duke3d/src/screens.cpp index d0fc29451..3a6156195 100644 --- a/source/duke3d/src/screens.cpp +++ b/source/duke3d/src/screens.cpp @@ -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(); } diff --git a/source/platform/macos/i_specialpaths.mm b/source/platform/macos/i_specialpaths.mm new file mode 100644 index 000000000..55522acf2 --- /dev/null +++ b/source/platform/macos/i_specialpaths.mm @@ -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 + +#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-.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; +} diff --git a/source/platform/unix/i_specialpaths.cpp b/source/platform/unix/i_specialpaths.cpp new file mode 100644 index 000000000..a63750620 --- /dev/null +++ b/source/platform/unix/i_specialpaths.cpp @@ -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 +#include +#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-.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); +} diff --git a/source/platform/win32/i_specialpaths.cpp b/source/platform/win32/i_specialpaths.cpp new file mode 100644 index 000000000..e9c9bd41b --- /dev/null +++ b/source/platform/win32/i_specialpaths.cpp @@ -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 +#include +#include + +#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-.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; +} diff --git a/source/rr/src/net.cpp b/source/rr/src/net.cpp index f607176e0..3425dcf7d 100644 --- a/source/rr/src/net.cpp +++ b/source/rr/src/net.cpp @@ -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); diff --git a/source/rr/src/osdcmds.cpp b/source/rr/src/osdcmds.cpp index 8696e3a43..f8daadce9 100644 --- a/source/rr/src/osdcmds.cpp +++ b/source/rr/src/osdcmds.cpp @@ -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 && diff --git a/source/rr/src/premap.cpp b/source/rr/src/premap.cpp index fd75ab255..fe86005c9 100644 --- a/source/rr/src/premap.cpp +++ b/source/rr/src/premap.cpp @@ -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))