mirror of
https://github.com/ZDoom/Raze.git
synced 2024-11-15 00:42:08 +00:00
- WIP search path code
The Steam/GOG path getters were taken out of the frontends. This also switches the Windows directory reader touse the wide string version to get Unicode file names. Some paths were added to the config file instead of hard coding them.
This commit is contained in:
parent
cfd9edbe71
commit
35342526a5
9 changed files with 690 additions and 1116 deletions
|
@ -1250,7 +1250,7 @@ int app_main(int argc, char const * const * argv)
|
|||
ParseOptions();
|
||||
G_ExtInit();
|
||||
|
||||
G_AddSearchPaths();
|
||||
//G_AddSearchPaths();
|
||||
|
||||
|
||||
#ifdef STARTUP_SETUP_WINDOW
|
||||
|
|
|
@ -260,321 +260,6 @@ void G_LoadGroups()
|
|||
pathsearchmode = bakpathsearchmode;
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
static int G_ReadRegistryValue(char const * const SubKey, char const * const Value, char * const Output, DWORD * OutputSize)
|
||||
{
|
||||
// KEY_WOW64_32KEY gets us around Wow6432Node on 64-bit builds
|
||||
REGSAM const wow64keys[] = { KEY_WOW64_32KEY, KEY_WOW64_64KEY };
|
||||
|
||||
for (auto &wow64key : wow64keys)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG keygood = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NULL, 0, KEY_READ | wow64key, &hkey);
|
||||
|
||||
if (keygood != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
LONG retval = SHGetValueA(hkey, SubKey, Value, NULL, Output, OutputSize);
|
||||
|
||||
RegCloseKey(hkey);
|
||||
|
||||
if (retval == ERROR_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined EDUKE32_OSX || defined __linux__ || defined EDUKE32_BSD
|
||||
static void G_AddSteamPaths(const char *basepath)
|
||||
{
|
||||
UNREFERENCED_PARAMETER(basepath);
|
||||
#if 0
|
||||
char buf[BMAX_PATH];
|
||||
|
||||
// PORT-TODO:
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot", basepath);
|
||||
addsearchpath(buf);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/dc", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/nw", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/vacation", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
#if defined EDUKE32_OSX
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/Duke Nukem 3D.app/drive_c/Program Files/Duke Nukem 3D", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// A bare-bones "parser" for Valve's KeyValues VDF format.
|
||||
// There is no guarantee this will function properly with ill-formed files.
|
||||
static void KeyValues_SkipWhitespace(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while (((*vdfbuf)[0] == ' ' || (*vdfbuf)[0] == '\n' || (*vdfbuf)[0] == '\r' || (*vdfbuf)[0] == '\t' || (*vdfbuf)[0] == '\0') && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
// comments
|
||||
if ((*vdfbuf) + 2 < vdfbufend && (*vdfbuf)[0] == '/' && (*vdfbuf)[1] == '/')
|
||||
{
|
||||
while ((*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
}
|
||||
static void KeyValues_SkipToEndOfQuotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
while ((*vdfbuf)[0] != '\"' && (*vdfbuf)[-1] != '\\' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipToEndOfUnquotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while ((*vdfbuf)[0] != ' ' && (*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && (*vdfbuf)[0] != '\t' && (*vdfbuf)[0] != '\0' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipNextWhatever(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return;
|
||||
|
||||
if ((*vdfbuf)[0] == '{')
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
do
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
}
|
||||
while ((*vdfbuf)[0] != '}');
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
else if ((*vdfbuf)[0] == '\"')
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
else if ((*vdfbuf)[0] != '}')
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static char* KeyValues_NormalizeToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
char *token = *vdfbuf;
|
||||
|
||||
if ((*vdfbuf)[0] == '\"' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
token++;
|
||||
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
// account for escape sequences
|
||||
char *writeseeker = token, *readseeker = token;
|
||||
while (readseeker <= *vdfbuf)
|
||||
{
|
||||
if (readseeker[0] == '\\')
|
||||
readseeker++;
|
||||
|
||||
writeseeker[0] = readseeker[0];
|
||||
|
||||
writeseeker++;
|
||||
readseeker++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
return token;
|
||||
}
|
||||
static void KeyValues_FindKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
char *ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
if (token != NULL) // pass in NULL to find the next key instead of a specific one
|
||||
while (Bstrcmp(ParentKey, token) != 0 && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static int32_t KeyValues_FindParentKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return 0;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] != '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
static char* KeyValues_FindKeyValue(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return NULL;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] == '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return NULL;
|
||||
|
||||
return KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
static void G_ParseSteamKeyValuesForPaths(const char *vdf)
|
||||
{
|
||||
buildvfs_fd fd = buildvfs_open_read(vdf);
|
||||
int32_t size = buildvfs_length(fd);
|
||||
char *vdfbufstart, *vdfbuf, *vdfbufend;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
vdfbufstart = vdfbuf = (char*)Xmalloc(size);
|
||||
size = (int32_t)buildvfs_read(fd, vdfbuf, size);
|
||||
buildvfs_close(fd);
|
||||
vdfbufend = vdfbuf + size;
|
||||
|
||||
if (KeyValues_FindParentKey(&vdfbuf, vdfbufend, "LibraryFolders"))
|
||||
{
|
||||
char *result;
|
||||
vdfbuf++;
|
||||
while ((result = KeyValues_FindKeyValue(&vdfbuf, vdfbufend, NULL)) != NULL)
|
||||
G_AddSteamPaths(result);
|
||||
}
|
||||
|
||||
Xfree(vdfbufstart);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void G_AddSearchPaths(void)
|
||||
{
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined __linux__ || defined EDUKE32_BSD
|
||||
char buf[BMAX_PATH];
|
||||
char *homepath = Bgethomedir();
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam", homepath);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam/steamapps/libraryfolders.vdf", homepath);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
Bfree(homepath);
|
||||
|
||||
addsearchpath("/usr/share/games/nblood");
|
||||
addsearchpath("/usr/local/share/games/nblood");
|
||||
#elif defined EDUKE32_OSX
|
||||
char buf[BMAX_PATH];
|
||||
int32_t i;
|
||||
char *applications[] = { osx_getapplicationsdir(0), osx_getapplicationsdir(1) };
|
||||
char *support[] = { osx_getsupportdir(0), osx_getsupportdir(1) };
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam", support[i]);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam/steamapps/libraryfolders.vdf", support[i]);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
#if 0
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Duke Nukem 3D.app/Contents/Resources/Duke Nukem 3D.boxer/C.harddisk", applications[i]);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/NBlood", support[i]);
|
||||
addsearchpath(buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bfree(applications[i]);
|
||||
Bfree(support[i]);
|
||||
}
|
||||
#elif defined (_WIN32)
|
||||
char buf[BMAX_PATH] = {0};
|
||||
DWORD bufsize;
|
||||
bool found = false;
|
||||
|
||||
// Blood: One Unit Whole Blood (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 299030)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
found = true;
|
||||
}
|
||||
|
||||
// Blood: One Unit Whole Blood (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGONEUNITONEBLOOD", "PATH", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
found = true;
|
||||
}
|
||||
|
||||
// Blood: Fresh Supply (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 1010750)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
strncat(buf, R"(\addons\Cryptic Passage)", 23);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
found = true;
|
||||
}
|
||||
|
||||
// Blood: Fresh Supply (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue(R"(SOFTWARE\Wow6432Node\GOG.com\Games\1374469660)", "path", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
strncat(buf, R"(\addons\Cryptic Passage)", 23);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
found = true;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void G_CleanupSearchPaths(void)
|
||||
{
|
||||
removesearchpaths_withuser(SEARCHPATH_REMOVE);
|
||||
|
|
|
@ -119,15 +119,14 @@ FDirectory::FDirectory(const char * directory)
|
|||
|
||||
int FDirectory::AddDirectory(const char *dirpath)
|
||||
{
|
||||
struct _finddata_t fileinfo;
|
||||
auto dirmatch = WideString(dirpath);
|
||||
struct _wfinddata_t fileinfo;
|
||||
intptr_t handle;
|
||||
FString dirmatch;
|
||||
int count = 0;
|
||||
|
||||
dirmatch = dirpath;
|
||||
dirmatch += '*';
|
||||
|
||||
if ((handle = _findfirst(dirmatch, &fileinfo)) == -1)
|
||||
if ((handle = _wfindfirst(wdirmatch, &fileinfo)) == -1)
|
||||
{
|
||||
Printf("Could not scan '%s': %s\n", dirpath, strerror(errno));
|
||||
}
|
||||
|
@ -141,18 +140,19 @@ int FDirectory::AddDirectory(const char *dirpath)
|
|||
// info from being included.)
|
||||
continue;
|
||||
}
|
||||
FString fi = FString(fileinfo.name);
|
||||
if (fileinfo.attrib & _A_SUBDIR)
|
||||
{
|
||||
|
||||
if (fileinfo.name[0] == '.' &&
|
||||
(fileinfo.name[1] == '\0' ||
|
||||
(fileinfo.name[1] == '.' && fileinfo.name[2] == '\0')))
|
||||
if (fi[0] == '.' &&
|
||||
(fi[1] == '\0' ||
|
||||
(fi[1] == '.' && fi[2] == '\0')))
|
||||
{
|
||||
// Do not record . and .. directories.
|
||||
continue;
|
||||
}
|
||||
FString newdir = dirpath;
|
||||
newdir << fileinfo.name << '/';
|
||||
newdir << fi << '/';
|
||||
count += AddDirectory(newdir);
|
||||
}
|
||||
else
|
||||
|
@ -163,10 +163,10 @@ int FDirectory::AddDirectory(const char *dirpath)
|
|||
continue;
|
||||
}
|
||||
|
||||
AddEntry(FString(dirpath) + fileinfo.name, fileinfo.size);
|
||||
AddEntry(FString(dirpath) + fi, fileinfo.size);
|
||||
count++;
|
||||
}
|
||||
} while (_findnext(handle, &fileinfo) == 0);
|
||||
} while (_wfindnext(handle, &fileinfo) == 0);
|
||||
_findclose(handle);
|
||||
}
|
||||
return count;
|
||||
|
|
|
@ -87,12 +87,15 @@ FGameConfigFile::FGameConfigFile ()
|
|||
SetValueForKey ("Path", "./*", true);
|
||||
#ifdef __APPLE__
|
||||
SetValueForKey ("Path", user_docs + "/*", true);
|
||||
SetValueForKey ("Path", user_app_support + "/*", true);
|
||||
SetValueForKey ("Path", user_app_support + "/EDuke32", true);
|
||||
SetValueForKey ("Path", user_app_support + "/JFDuke32", true);
|
||||
SetValueForKey ("Path", user_app_support + "/NBlood", true);
|
||||
SetValueForKey ("Path", "$PROGDIR", true);
|
||||
SetValueForKey ("Path", "$PROGDIR/*", true);
|
||||
SetValueForKey ("Path", local_app_support + "/*", true);
|
||||
SetValueForKey ("Path", local_app_support + "/EDuke32", true);
|
||||
SetValueForKey ("Path", local_app_support + "/JFDuke32", true);
|
||||
SetValueForKey ("Path", local_app_support + "/NBlood", true);
|
||||
#elif !defined(__unix__)
|
||||
SetValueForKey ("Path", "$HOME/*", true);
|
||||
SetValueForKey ("Path", "$PROGDIR", true);
|
||||
SetValueForKey ("Path", "$PROGDIR/*", true);
|
||||
#else
|
||||
|
@ -100,11 +103,15 @@ FGameConfigFile::FGameConfigFile ()
|
|||
// 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);
|
||||
SetValueForKey ("Path", "/usr/share/games/jfduke3d", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/jfduke3d", true);
|
||||
SetValueForKey ("Path", "/usr/share/games/eduke32", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/eduke32", true);
|
||||
SetValueForKey ("Path", "/usr/share/games/nblood", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/nblood", true);
|
||||
|
||||
#endif
|
||||
SetValueForKey ("Path", "$STEAM", true); // also covers GOG.
|
||||
}
|
||||
|
||||
// Set default search paths if none present
|
||||
|
@ -122,10 +129,12 @@ FGameConfigFile::FGameConfigFile ()
|
|||
#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);
|
||||
SetValueForKey ("Path", "/usr/share/games/jfduke3d", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/jfduke3d", true);
|
||||
SetValueForKey ("Path", "/usr/share/games/eduke32", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/eduke32", true);
|
||||
SetValueForKey ("Path", "/usr/share/games/nblood", true);
|
||||
SetValueForKey ("Path", "/usr/local/share/games/nblood", true);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
657
source/common/searchpaths.cpp
Normal file
657
source/common/searchpaths.cpp
Normal file
|
@ -0,0 +1,657 @@
|
|||
//-------------------------------------------------------------------------
|
||||
/*
|
||||
Copyright (C) 2010-2019 EDuke32 developers and contributors
|
||||
Copyright (C) 2019 Nuke.YKT
|
||||
Copyright (C) 2019 Christoph Oelckers
|
||||
|
||||
|
||||
This is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// Search path management
|
||||
//
|
||||
|
||||
|
||||
#if defined _WIN32
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static int G_ReadRegistryValue(char const * const SubKey, char const * const Value, char * const Output, DWORD * OutputSize)
|
||||
{
|
||||
// KEY_WOW64_32KEY gets us around Wow6432Node on 64-bit builds
|
||||
REGSAM const wow64keys[] = { KEY_WOW64_32KEY, KEY_WOW64_64KEY };
|
||||
|
||||
for (auto &wow64key : wow64keys)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG keygood = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NULL, 0, KEY_READ | wow64key, &hkey);
|
||||
|
||||
if (keygood != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
LONG retval = SHGetValueA(hkey, SubKey, Value, NULL, Output, OutputSize);
|
||||
|
||||
RegCloseKey(hkey);
|
||||
|
||||
if (retval == ERROR_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#elif defined (__APPLE__) || defined (__FreeBSD__) || defined(__OpenBSD__) || defined (__linux__)
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static void G_AddSteamPaths(TArray<FString> &searchpaths, const char *basepath)
|
||||
{
|
||||
FString path;
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
path.Format("%s/steamapps/common/Duke Nukem 3D/gameroot", basepath);
|
||||
searchpaths.Push(path);
|
||||
path.Format("%s/steamapps/common/Duke Nukem 3D/gameroot/addons/dc", basepath);
|
||||
searchpaths.Push(path);
|
||||
path.Format("%s/steamapps/common/Duke Nukem 3D/gameroot/addons/nw", basepath);
|
||||
searchpaths.Push(path);
|
||||
path.Format("%s/steamapps/common/Duke Nukem 3D/gameroot/addons/vacation", basepath);
|
||||
searchpaths.Push(path);
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
#ifdef __APPLE__
|
||||
path.Format("%s/steamapps/common/Duke Nukem 3D/Duke Nukem 3D.app/drive_c/Program Files/Duke Nukem 3D", basepath);
|
||||
searchpaths.Push(path);
|
||||
#endif
|
||||
|
||||
// NAM (Steam)
|
||||
#ifdef __APPLE__
|
||||
path.Format("%s/steamapps/common/Nam/Nam.app/Contents/Resources/Nam.boxer/C.harddisk/NAM", basepath);
|
||||
#else
|
||||
path.Format("%s/steamapps/common/Nam/NAM", basepath);
|
||||
#endif
|
||||
searchpaths.Push(path);
|
||||
|
||||
// WWII GI (Steam)
|
||||
path.Format("%s/steamapps/common/World War II GI/WW2GI", basepath);
|
||||
searchpaths.Push(path);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
// A bare-bones "parser" for Valve's KeyValues VDF format.
|
||||
// There is no guarantee this will function properly with ill-formed files.
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static void KeyValues_SkipWhitespace(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while (((*vdfbuf)[0] == ' ' || (*vdfbuf)[0] == '\n' || (*vdfbuf)[0] == '\r' || (*vdfbuf)[0] == '\t' || (*vdfbuf)[0] == '\0') && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
// comments
|
||||
if ((*vdfbuf) + 2 < vdfbufend && (*vdfbuf)[0] == '/' && (*vdfbuf)[1] == '/')
|
||||
{
|
||||
while ((*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
}
|
||||
static void KeyValues_SkipToEndOfQuotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
while ((*vdfbuf)[0] != '\"' && (*vdfbuf)[-1] != '\\' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipToEndOfUnquotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while ((*vdfbuf)[0] != ' ' && (*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && (*vdfbuf)[0] != '\t' && (*vdfbuf)[0] != '\0' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipNextWhatever(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return;
|
||||
|
||||
if ((*vdfbuf)[0] == '{')
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
do
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
}
|
||||
while ((*vdfbuf)[0] != '}');
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
else if ((*vdfbuf)[0] == '\"')
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
else if ((*vdfbuf)[0] != '}')
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static char* KeyValues_NormalizeToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
char *token = *vdfbuf;
|
||||
|
||||
if ((*vdfbuf)[0] == '\"' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
token++;
|
||||
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
// account for escape sequences
|
||||
char *writeseeker = token, *readseeker = token;
|
||||
while (readseeker <= *vdfbuf)
|
||||
{
|
||||
if (readseeker[0] == '\\')
|
||||
readseeker++;
|
||||
|
||||
writeseeker[0] = readseeker[0];
|
||||
|
||||
writeseeker++;
|
||||
readseeker++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
return token;
|
||||
}
|
||||
static void KeyValues_FindKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
char *ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
if (token != NULL) // pass in NULL to find the next key instead of a specific one
|
||||
while (Bstrcmp(ParentKey, token) != 0 && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static int32_t KeyValues_FindParentKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return 0;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] != '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
static char* KeyValues_FindKeyValue(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return NULL;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] == '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return NULL;
|
||||
|
||||
return KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
static void G_ParseSteamKeyValuesForPaths(TArray<FString> &searchpaths, const char *vdf)
|
||||
{
|
||||
FileReader fr;
|
||||
if (fr.Open(vdf))
|
||||
{
|
||||
auto data = fr.Read();
|
||||
if (data.Size() == 0) return;
|
||||
}
|
||||
|
||||
auto vdfvuf = (char*)data.Data();
|
||||
auto vdfbufend = vdfbuf + data.Size();
|
||||
|
||||
if (KeyValues_FindParentKey(&vdfbuf, vdfbufend, "LibraryFolders"))
|
||||
{
|
||||
char *result;
|
||||
vdfbuf++;
|
||||
while ((result = KeyValues_FindKeyValue(&vdfbuf, vdfbufend, NULL)) != NULL)
|
||||
G_AddSteamPaths(searchpaths, result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined (__FreeBSD__) || defined(__OpenBSD__) || defined (__linux__)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void G_AddExternalSearchPaths(TArray<FString> &searchpaths)
|
||||
{
|
||||
FString path;
|
||||
char *homepath = Bgethomedir();
|
||||
|
||||
path.Format("%s/.steam/steam", homepath);
|
||||
G_AddSteamPaths(searchpaths, buf);
|
||||
|
||||
path.Format("%s/.steam/steam/steamapps/libraryfolders.vdf", homepath);
|
||||
G_ParseSteamKeyValuesForPaths(searchpaths, buf);
|
||||
}
|
||||
|
||||
#elif defined __APPLE__
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void G_AddExternalSearchPaths(TArray<FString> &searchpaths)
|
||||
{
|
||||
char *applications[] = { osx_getapplicationsdir(0), osx_getapplicationsdir(1) };
|
||||
char *support[] = { osx_getsupportdir(0), osx_getsupportdir(1) };
|
||||
|
||||
FString path;
|
||||
|
||||
char buf[BMAX_PATH];
|
||||
int32_t i;
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
path.Format("%s/Steam", support[i]);
|
||||
G_AddSteamPaths(searchpaths, buf);
|
||||
|
||||
path.Format("%s/Steam/steamapps/libraryfolders.vdf", support[i]);
|
||||
G_ParseSteamKeyValuesForPaths(searchpaths, buf);
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
path.Format("%s/Duke Nukem 3D.app/Contents/Resources/Duke Nukem 3D.boxer/C.harddisk", applications[i]);
|
||||
searchpaths.Push(path);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Xfree(applications[i]);
|
||||
Xfree(support[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined (_WIN32)
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void G_AddExternalSearchPaths(TArray<FString> &searchpaths)
|
||||
{
|
||||
|
||||
char buf[BMAX_PATH] = {0};
|
||||
DWORD bufsize;
|
||||
|
||||
// Duke Nukem 3D: 20th Anniversary World Tour (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 434050)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 225140)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
strncpy(suffix, "/gameroot", remaining);
|
||||
searchpaths.Push(buf);
|
||||
strncpy(suffix, "/gameroot/addons/dc", remaining);
|
||||
searchpaths.Push(buf);
|
||||
strncpy(suffix, "/gameroot/addons/nw", remaining);
|
||||
searchpaths.Push(buf);
|
||||
strncpy(suffix, "/gameroot/addons/vacation", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 359850)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
strncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGDUKE3D", "PATH", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Duke Nukem 3D", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// 3D Realms Anthology
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Anthology", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// NAM (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 329650)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/NAM", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// WWII GI (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 376750)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
size_t const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/WW2GI", remaining);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Redneck Rampage (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGREDNECKRAMPAGE", "PATH", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Redneck Rampage Rides Again (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGCREDNECKRIDESAGAIN", "PATH", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Blood: One Unit Whole Blood (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 299030)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Blood: One Unit Whole Blood (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGONEUNITONEBLOOD", "PATH", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Blood: Fresh Supply (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 1010750)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
strncat(buf, R"(\addons\Cryptic Passage)", 23);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
|
||||
// Blood: Fresh Supply (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (!found && G_ReadRegistryValue(R"(SOFTWARE\Wow6432Node\GOG.com\Games\1374469660)", "path", buf, &bufsize))
|
||||
{
|
||||
searchpaths.Push(buf);
|
||||
strncat(buf, R"(\addons\Cryptic Passage)", 23);
|
||||
searchpaths.Push(buf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
void AddExpandedPath(TArray<FString> &searchpaths, const char *basepath)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
//==========================================================================
|
||||
//
|
||||
// Windows version
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void CollectSubdirectories(TArray<FString> &searchpath, const char *dirmatch)
|
||||
{
|
||||
struct _wfinddata_t fileinfo;
|
||||
intptr_t handle;
|
||||
FString dirpath;
|
||||
int count = 0;
|
||||
auto wdirmatch = WideString(dirmatch);
|
||||
|
||||
dirpath.Truncate(dirpath.Len()-1); // remove the '*'
|
||||
|
||||
if ((handle = _wfindfirst(wdirmatch, &fileinfo)) == -1)
|
||||
{
|
||||
// silently ignore non-existent paths
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
if (fileinfo.attrib & _A_HIDDEN)
|
||||
{
|
||||
// Skip hidden files and directories. (Prevents SVN bookkeeping
|
||||
// info from being included.)
|
||||
continue;
|
||||
}
|
||||
FString fi = FString(fileinfo.name);
|
||||
if (fileinfo.attrib & _A_SUBDIR)
|
||||
{
|
||||
|
||||
if (fi[0] == '.' &&
|
||||
(fi[1] == '\0' ||
|
||||
(fi[1] == '.' && fi[2] == '\0')))
|
||||
{
|
||||
// Do not record . and .. directories.
|
||||
continue;
|
||||
}
|
||||
FString newdir = dirpath + fi;
|
||||
count += AddDirectory(newdir);
|
||||
}
|
||||
} while (_wfindnext(handle, &fileinfo) == 0);
|
||||
_findclose(handle);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// add_dirs
|
||||
// 4.4BSD version
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FDirectory::AddDirectory(const char *dirpath)
|
||||
{
|
||||
char *argv [2] = { NULL, NULL };
|
||||
argv[0] = new char[strlen(dirpath)+1];
|
||||
strcpy(argv[0], dirpath);
|
||||
FTS *fts;
|
||||
FTSENT *ent;
|
||||
|
||||
fts = fts_open(argv, FTS_LOGICAL, NULL);
|
||||
if (fts == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const size_t namepos = strlen(dirpath);
|
||||
FString pathfix;
|
||||
|
||||
while ((ent = fts_read(fts)) != NULL)
|
||||
{
|
||||
if (ent->fts_info != FTS_D)
|
||||
{
|
||||
// We're only interested in getting directories.
|
||||
continue;
|
||||
}
|
||||
fts_set(fts, ent, FTS_SKIP);
|
||||
if (ent->fts_name[0] == '.')
|
||||
{
|
||||
// Skip hidden directories. (Prevents SVN bookkeeping
|
||||
// info from being included.)
|
||||
}
|
||||
|
||||
// Some implementations add an extra separator between
|
||||
// root of the hierarchy and entity's path.
|
||||
// It needs to be removed in order to resolve
|
||||
// lumps' relative paths properly.
|
||||
const char* path = ent->fts_path;
|
||||
|
||||
if ('/' == path[namepos])
|
||||
{
|
||||
pathfix = FString(path, namepos);
|
||||
pathfix.AppendCStrPart(&path[namepos + 1], ent->fts_pathlen - namepos - 1);
|
||||
|
||||
path = pathfix.GetChars();
|
||||
}
|
||||
|
||||
searchpaths.Push(path);
|
||||
}
|
||||
fts_close(fts);
|
||||
delete[] argv[0];
|
||||
}
|
||||
#endif
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CollectSearchPaths
|
||||
//
|
||||
// collect all paths in a local array for easier management
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
TArray<FString> CollectSearchPaths()
|
||||
{
|
||||
TArray<FString> searchpths;
|
||||
|
||||
if (GameConfig->SetSection("GameSearch.Directories"))
|
||||
{
|
||||
const char *key;
|
||||
const char *value;
|
||||
|
||||
while (GameConfig->NextInSection(key, value))
|
||||
{
|
||||
if (stricmp(key, "Path") == 0)
|
||||
{
|
||||
FString nice = NicePath(value);
|
||||
if (nice.Len() > 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (isalpha(nice[0] && nice[1] == ':' && nice[2] != '/') continue; // ignore drive relative paths because they are meaningless.
|
||||
#endif
|
||||
// A path ending with "/*" means to add all subdirectories.
|
||||
if (nice[nice.Len()-2] == '/' && nice[nice.Len()-1] == '*')
|
||||
{
|
||||
AddExpandedPath(searchpaths, nice);
|
||||
}
|
||||
// Checking Steam via a list entry allows easy removal if not wanted.
|
||||
else if (nice.CompareNoCase("$STEAM"))
|
||||
{
|
||||
G_AddExternalSearchPaths(searchpaths);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSearchPaths.Push(nice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Unify and remove trailing slashes
|
||||
for (auto &str : mSearchPaths)
|
||||
{
|
||||
str.Substitute("\\", "/");
|
||||
if (str.Back() == '/') str.Truncate(str.Len() - 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -315,20 +315,6 @@ void G_ExtInit(void)
|
|||
Xfree(homedir);
|
||||
}
|
||||
}
|
||||
|
||||
// JBF 20031220: Because it's annoying renaming GRP files whenever I want to test different game data
|
||||
#ifndef EDUKE32_STANDALONE
|
||||
if (g_grpNamePtr == NULL)
|
||||
{
|
||||
const char *cp = getenv("DUKE3DGRP");
|
||||
if (cp)
|
||||
{
|
||||
clearGrpNamePtr();
|
||||
g_grpNamePtr = dup_filename(cp);
|
||||
initprintf("Using \"%s\" as main GRP file\n", g_grpNamePtr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void G_ScanGroups(void)
|
||||
|
@ -492,31 +478,7 @@ void G_LoadGroups()
|
|||
pathsearchmode = bakpathsearchmode;
|
||||
}
|
||||
|
||||
#if defined _WIN32 && !defined EDUKE32_STANDALONE
|
||||
static int G_ReadRegistryValue(char const * const SubKey, char const * const Value, char * const Output, DWORD * OutputSize)
|
||||
{
|
||||
// KEY_WOW64_32KEY gets us around Wow6432Node on 64-bit builds
|
||||
REGSAM const wow64keys[] = { KEY_WOW64_32KEY, KEY_WOW64_64KEY };
|
||||
|
||||
for (auto &wow64key : wow64keys)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG keygood = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NULL, 0, KEY_READ | wow64key, &hkey);
|
||||
|
||||
if (keygood != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
LONG retval = SHGetValueA(hkey, SubKey, Value, NULL, Output, OutputSize);
|
||||
|
||||
RegCloseKey(hkey);
|
||||
|
||||
if (retval == ERROR_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void G_LoadAddon(void)
|
||||
{
|
||||
|
@ -545,357 +507,6 @@ static void G_LoadAddon(void)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifndef EDUKE32_STANDALONE
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined EDUKE32_OSX || defined __linux__ || defined EDUKE32_BSD
|
||||
static void G_AddSteamPaths(const char *basepath)
|
||||
{
|
||||
char buf[BMAX_PATH];
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot", basepath);
|
||||
addsearchpath(buf);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/dc", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/nw", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/vacation", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
#if defined EDUKE32_OSX
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/Duke Nukem 3D.app/drive_c/Program Files/Duke Nukem 3D", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
#endif
|
||||
|
||||
// NAM (Steam)
|
||||
#if defined EDUKE32_OSX
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Nam/Nam.app/Contents/Resources/Nam.boxer/C.harddisk/NAM", basepath);
|
||||
#else
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Nam/NAM", basepath);
|
||||
#endif
|
||||
addsearchpath_user(buf, SEARCHPATH_NAM);
|
||||
|
||||
// WWII GI (Steam)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/World War II GI/WW2GI", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_WW2GI);
|
||||
}
|
||||
|
||||
// A bare-bones "parser" for Valve's KeyValues VDF format.
|
||||
// There is no guarantee this will function properly with ill-formed files.
|
||||
static void KeyValues_SkipWhitespace(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while (((*vdfbuf)[0] == ' ' || (*vdfbuf)[0] == '\n' || (*vdfbuf)[0] == '\r' || (*vdfbuf)[0] == '\t' || (*vdfbuf)[0] == '\0') && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
// comments
|
||||
if ((*vdfbuf) + 2 < vdfbufend && (*vdfbuf)[0] == '/' && (*vdfbuf)[1] == '/')
|
||||
{
|
||||
while ((*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
}
|
||||
static void KeyValues_SkipToEndOfQuotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
while ((*vdfbuf)[0] != '\"' && (*vdfbuf)[-1] != '\\' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipToEndOfUnquotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while ((*vdfbuf)[0] != ' ' && (*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && (*vdfbuf)[0] != '\t' && (*vdfbuf)[0] != '\0' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipNextWhatever(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return;
|
||||
|
||||
if ((*vdfbuf)[0] == '{')
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
do
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
}
|
||||
while ((*vdfbuf)[0] != '}');
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
else if ((*vdfbuf)[0] == '\"')
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
else if ((*vdfbuf)[0] != '}')
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static char* KeyValues_NormalizeToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
char *token = *vdfbuf;
|
||||
|
||||
if ((*vdfbuf)[0] == '\"' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
token++;
|
||||
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
// account for escape sequences
|
||||
char *writeseeker = token, *readseeker = token;
|
||||
while (readseeker <= *vdfbuf)
|
||||
{
|
||||
if (readseeker[0] == '\\')
|
||||
readseeker++;
|
||||
|
||||
writeseeker[0] = readseeker[0];
|
||||
|
||||
writeseeker++;
|
||||
readseeker++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
return token;
|
||||
}
|
||||
static void KeyValues_FindKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
char *ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
if (token != NULL) // pass in NULL to find the next key instead of a specific one
|
||||
while (Bstrcmp(ParentKey, token) != 0 && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static int32_t KeyValues_FindParentKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return 0;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] != '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
static char* KeyValues_FindKeyValue(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return NULL;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] == '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return NULL;
|
||||
|
||||
return KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
static void G_ParseSteamKeyValuesForPaths(const char *vdf)
|
||||
{
|
||||
buildvfs_fd fd = buildvfs_open_read(vdf);
|
||||
int32_t size = buildvfs_length(fd);
|
||||
char *vdfbufstart, *vdfbuf, *vdfbufend;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
vdfbufstart = vdfbuf = (char*)Xmalloc(size);
|
||||
size = (int32_t)buildvfs_read(fd, vdfbuf, size);
|
||||
buildvfs_close(fd);
|
||||
vdfbufend = vdfbuf + size;
|
||||
|
||||
if (KeyValues_FindParentKey(&vdfbuf, vdfbufend, "LibraryFolders"))
|
||||
{
|
||||
char *result;
|
||||
vdfbuf++;
|
||||
while ((result = KeyValues_FindKeyValue(&vdfbuf, vdfbufend, NULL)) != NULL)
|
||||
G_AddSteamPaths(result);
|
||||
}
|
||||
|
||||
Xfree(vdfbufstart);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void G_AddSearchPaths(void)
|
||||
{
|
||||
#ifndef EDUKE32_STANDALONE
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined __linux__ || defined EDUKE32_BSD
|
||||
char buf[BMAX_PATH];
|
||||
char *homepath = Bgethomedir();
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam", homepath);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam/steamapps/libraryfolders.vdf", homepath);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
Xfree(homepath);
|
||||
|
||||
addsearchpath("/usr/share/games/jfduke3d");
|
||||
addsearchpath("/usr/local/share/games/jfduke3d");
|
||||
addsearchpath("/usr/share/games/eduke32");
|
||||
addsearchpath("/usr/local/share/games/eduke32");
|
||||
#elif defined EDUKE32_OSX
|
||||
char buf[BMAX_PATH];
|
||||
int32_t i;
|
||||
char *applications[] = { osx_getapplicationsdir(0), osx_getapplicationsdir(1) };
|
||||
char *support[] = { osx_getsupportdir(0), osx_getsupportdir(1) };
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam", support[i]);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam/steamapps/libraryfolders.vdf", support[i]);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Duke Nukem 3D.app/Contents/Resources/Duke Nukem 3D.boxer/C.harddisk", applications[i]);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/JFDuke3D", support[i]);
|
||||
addsearchpath(buf);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/EDuke32", support[i]);
|
||||
addsearchpath(buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Xfree(applications[i]);
|
||||
Xfree(support[i]);
|
||||
}
|
||||
#elif defined (_WIN32)
|
||||
char buf[BMAX_PATH] = {0};
|
||||
DWORD bufsize;
|
||||
|
||||
// Duke Nukem 3D: 20th Anniversary World Tour (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 434050)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 225140)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/gameroot", remaining);
|
||||
addsearchpath(buf);
|
||||
Bstrncpy(suffix, "/gameroot/addons/dc", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bstrncpy(suffix, "/gameroot/addons/nw", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bstrncpy(suffix, "/gameroot/addons/vacation", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 359850)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGDUKE3D", "PATH", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Duke Nukem 3D", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// 3D Realms Anthology
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Anthology", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// NAM (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 329650)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/NAM", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_NAM);
|
||||
}
|
||||
|
||||
// WWII GI (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 376750)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/WW2GI", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_WW2GI);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void G_CleanupSearchPaths(void)
|
||||
{
|
||||
|
|
|
@ -6289,7 +6289,7 @@ int app_main(int argc, const char * const*argv)
|
|||
initprintf(HEAD2 " %s\n", s_buildRev);
|
||||
PrintBuildInfo();
|
||||
|
||||
G_AddSearchPaths();
|
||||
//G_AddSearchPaths();
|
||||
|
||||
g_skillCnt = 4;
|
||||
ud.multimode = 1;
|
||||
|
|
|
@ -448,31 +448,6 @@ void G_LoadGroups()
|
|||
pathsearchmode = bakpathsearchmode;
|
||||
}
|
||||
|
||||
#if defined _WIN32
|
||||
static int G_ReadRegistryValue(char const * const SubKey, char const * const Value, char * const Output, DWORD * OutputSize)
|
||||
{
|
||||
// KEY_WOW64_32KEY gets us around Wow6432Node on 64-bit builds
|
||||
REGSAM const wow64keys[] = { KEY_WOW64_32KEY, KEY_WOW64_64KEY };
|
||||
|
||||
for (auto &wow64key : wow64keys)
|
||||
{
|
||||
HKEY hkey;
|
||||
LONG keygood = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NULL, 0, KEY_READ | wow64key, &hkey);
|
||||
|
||||
if (keygood != ERROR_SUCCESS)
|
||||
continue;
|
||||
|
||||
LONG retval = SHGetValueA(hkey, SubKey, Value, NULL, Output, OutputSize);
|
||||
|
||||
RegCloseKey(hkey);
|
||||
|
||||
if (retval == ERROR_SUCCESS)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void G_LoadAddon(void)
|
||||
{
|
||||
|
@ -499,371 +474,8 @@ static void G_LoadAddon(void)
|
|||
g_selectedGrp = grp;
|
||||
}
|
||||
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined EDUKE32_OSX || defined __linux__ || defined EDUKE32_BSD
|
||||
static void G_AddSteamPaths(const char *basepath)
|
||||
{
|
||||
char buf[BMAX_PATH];
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot", basepath);
|
||||
addsearchpath(buf);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/dc", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/nw", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/gameroot/addons/vacation", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
#if defined EDUKE32_OSX
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Duke Nukem 3D/Duke Nukem 3D.app/drive_c/Program Files/Duke Nukem 3D", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
#endif
|
||||
|
||||
// NAM (Steam)
|
||||
#if defined EDUKE32_OSX
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Nam/Nam.app/Contents/Resources/Nam.boxer/C.harddisk/NAM", basepath);
|
||||
#else
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/Nam/NAM", basepath);
|
||||
#endif
|
||||
addsearchpath_user(buf, SEARCHPATH_NAM);
|
||||
|
||||
#if 0
|
||||
// WWII GI (Steam)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/steamapps/common/World War II GI/WW2GI", basepath);
|
||||
addsearchpath_user(buf, SEARCHPATH_WW2GI);
|
||||
#endif
|
||||
}
|
||||
|
||||
// A bare-bones "parser" for Valve's KeyValues VDF format.
|
||||
// There is no guarantee this will function properly with ill-formed files.
|
||||
static void KeyValues_SkipWhitespace(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while (((*vdfbuf)[0] == ' ' || (*vdfbuf)[0] == '\n' || (*vdfbuf)[0] == '\r' || (*vdfbuf)[0] == '\t' || (*vdfbuf)[0] == '\0') && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
// comments
|
||||
if ((*vdfbuf) + 2 < vdfbufend && (*vdfbuf)[0] == '/' && (*vdfbuf)[1] == '/')
|
||||
{
|
||||
while ((*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
}
|
||||
static void KeyValues_SkipToEndOfQuotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
while ((*vdfbuf)[0] != '\"' && (*vdfbuf)[-1] != '\\' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipToEndOfUnquotedToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
while ((*vdfbuf)[0] != ' ' && (*vdfbuf)[0] != '\n' && (*vdfbuf)[0] != '\r' && (*vdfbuf)[0] != '\t' && (*vdfbuf)[0] != '\0' && *vdfbuf < vdfbufend)
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
static void KeyValues_SkipNextWhatever(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return;
|
||||
|
||||
if ((*vdfbuf)[0] == '{')
|
||||
{
|
||||
(*vdfbuf)++;
|
||||
do
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
}
|
||||
while ((*vdfbuf)[0] != '}');
|
||||
(*vdfbuf)++;
|
||||
}
|
||||
else if ((*vdfbuf)[0] == '\"')
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
else if ((*vdfbuf)[0] != '}')
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static char* KeyValues_NormalizeToken(char **vdfbuf, char * const vdfbufend)
|
||||
{
|
||||
char *token = *vdfbuf;
|
||||
|
||||
if ((*vdfbuf)[0] == '\"' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
token++;
|
||||
|
||||
KeyValues_SkipToEndOfQuotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
// account for escape sequences
|
||||
char *writeseeker = token, *readseeker = token;
|
||||
while (readseeker <= *vdfbuf)
|
||||
{
|
||||
if (readseeker[0] == '\\')
|
||||
readseeker++;
|
||||
|
||||
writeseeker[0] = readseeker[0];
|
||||
|
||||
writeseeker++;
|
||||
readseeker++;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
KeyValues_SkipToEndOfUnquotedToken(vdfbuf, vdfbufend);
|
||||
(*vdfbuf)[0] = '\0';
|
||||
|
||||
return token;
|
||||
}
|
||||
static void KeyValues_FindKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
char *ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
if (token != NULL) // pass in NULL to find the next key instead of a specific one
|
||||
while (Bstrcmp(ParentKey, token) != 0 && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
ParentKey = KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
}
|
||||
static int32_t KeyValues_FindParentKey(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return 0;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] != '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
static char* KeyValues_FindKeyValue(char **vdfbuf, char * const vdfbufend, const char *token)
|
||||
{
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
// end of scope
|
||||
if ((*vdfbuf)[0] == '}')
|
||||
return NULL;
|
||||
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
|
||||
// ignore the wrong type
|
||||
while ((*vdfbuf)[0] == '{' && *vdfbuf < vdfbufend)
|
||||
{
|
||||
KeyValues_SkipNextWhatever(vdfbuf, vdfbufend);
|
||||
KeyValues_FindKey(vdfbuf, vdfbufend, token);
|
||||
}
|
||||
|
||||
KeyValues_SkipWhitespace(vdfbuf, vdfbufend);
|
||||
|
||||
if (*vdfbuf == vdfbufend)
|
||||
return NULL;
|
||||
|
||||
return KeyValues_NormalizeToken(vdfbuf, vdfbufend);
|
||||
}
|
||||
|
||||
static void G_ParseSteamKeyValuesForPaths(const char *vdf)
|
||||
{
|
||||
buildvfs_fd fd = buildvfs_open_read(vdf);
|
||||
int32_t size = buildvfs_length(fd);
|
||||
char *vdfbufstart, *vdfbuf, *vdfbufend;
|
||||
|
||||
if (size <= 0)
|
||||
return;
|
||||
|
||||
vdfbufstart = vdfbuf = (char*)Xmalloc(size);
|
||||
size = (int32_t)buildvfs_read(fd, vdfbuf, size);
|
||||
buildvfs_close(fd);
|
||||
vdfbufend = vdfbuf + size;
|
||||
|
||||
if (KeyValues_FindParentKey(&vdfbuf, vdfbufend, "LibraryFolders"))
|
||||
{
|
||||
char *result;
|
||||
vdfbuf++;
|
||||
while ((result = KeyValues_FindKeyValue(&vdfbuf, vdfbufend, NULL)) != NULL)
|
||||
G_AddSteamPaths(result);
|
||||
}
|
||||
|
||||
Xfree(vdfbufstart);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void G_AddSearchPaths(void)
|
||||
{
|
||||
#ifndef EDUKE32_TOUCH_DEVICES
|
||||
#if defined __linux__ || defined EDUKE32_BSD
|
||||
char buf[BMAX_PATH];
|
||||
char *homepath = Bgethomedir();
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam", homepath);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/.steam/steam/steamapps/libraryfolders.vdf", homepath);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
Bfree(homepath);
|
||||
|
||||
addsearchpath("/usr/share/games/jfduke3d");
|
||||
addsearchpath("/usr/local/share/games/jfduke3d");
|
||||
addsearchpath("/usr/share/games/eduke32");
|
||||
addsearchpath("/usr/local/share/games/eduke32");
|
||||
#elif defined EDUKE32_OSX
|
||||
char buf[BMAX_PATH];
|
||||
int32_t i;
|
||||
char *applications[] = { osx_getapplicationsdir(0), osx_getapplicationsdir(1) };
|
||||
char *support[] = { osx_getsupportdir(0), osx_getsupportdir(1) };
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam", support[i]);
|
||||
G_AddSteamPaths(buf);
|
||||
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Steam/steamapps/libraryfolders.vdf", support[i]);
|
||||
G_ParseSteamKeyValuesForPaths(buf);
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
Bsnprintf(buf, sizeof(buf), "%s/Duke Nukem 3D.app/Contents/Resources/Duke Nukem 3D.boxer/C.harddisk", applications[i]);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bsnprintf(buf, sizeof(buf), "%s/JFDuke3D", support[i]);
|
||||
addsearchpath(buf);
|
||||
Bsnprintf(buf, sizeof(buf), "%s/EDuke32", support[i]);
|
||||
addsearchpath(buf);
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
Bfree(applications[i]);
|
||||
Bfree(support[i]);
|
||||
}
|
||||
#elif defined (_WIN32)
|
||||
char buf[BMAX_PATH] = {0};
|
||||
DWORD bufsize;
|
||||
|
||||
// Duke Nukem 3D: 20th Anniversary World Tour (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 434050)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Megaton Edition (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 225140)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/gameroot", remaining);
|
||||
addsearchpath(buf);
|
||||
Bstrncpy(suffix, "/gameroot/addons/dc", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bstrncpy(suffix, "/gameroot/addons/nw", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
Bstrncpy(suffix, "/gameroot/addons/vacation", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology (Steam) / Kill-A-Ton Collection 2015)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 359850)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D: Atomic Edition (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGDUKE3D", "PATH", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Duke Nukem 3D (3D Realms Anthology)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Duke Nukem 3D", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// 3D Realms Anthology
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\3DRealms\\Anthology", NULL, buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/Duke Nukem 3D", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_REMOVE);
|
||||
}
|
||||
|
||||
// Redneck Rampage (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGREDNECKRAMPAGE", "PATH", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_RR);
|
||||
}
|
||||
|
||||
// Redneck Rampage Rides Again (GOG.com)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue("SOFTWARE\\GOG.com\\GOGCREDNECKRIDESAGAIN", "PATH", buf, &bufsize))
|
||||
{
|
||||
addsearchpath_user(buf, SEARCHPATH_RRRA);
|
||||
}
|
||||
|
||||
// NAM (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 329650)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/NAM", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_NAM);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// WWII GI (Steam)
|
||||
bufsize = sizeof(buf);
|
||||
if (G_ReadRegistryValue(R"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Steam App 376750)", "InstallLocation", buf, &bufsize))
|
||||
{
|
||||
char * const suffix = buf + bufsize - 1;
|
||||
DWORD const remaining = sizeof(buf) - bufsize;
|
||||
|
||||
Bstrncpy(suffix, "/WW2GI", remaining);
|
||||
addsearchpath_user(buf, SEARCHPATH_WW2GI);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void G_CleanupSearchPaths(void)
|
||||
{
|
||||
|
|
|
@ -7664,7 +7664,7 @@ int app_main(int argc, char const * const * argv)
|
|||
initprintf(HEAD2 " %s\n", s_buildRev);
|
||||
PrintBuildInfo();
|
||||
|
||||
G_AddSearchPaths();
|
||||
//G_AddSearchPaths();
|
||||
|
||||
g_skillCnt = 4;
|
||||
ud.multimode = 1;
|
||||
|
|
Loading…
Reference in a new issue