Merge branch 'searchpath'

This commit is contained in:
Daniel Gibson 2017-08-05 18:40:44 +02:00
commit 11b3a0515a
12 changed files with 471 additions and 383 deletions

View file

@ -162,10 +162,8 @@ is searched at:
- The directory given with the *+set basedir /path/to/quake2_installation/* - The directory given with the *+set basedir /path/to/quake2_installation/*
commandline argument. commandline argument.
Yamagi Quake II has support for an alternative startup config. It may be a good If you're a package maintainer, please look at our documentation at
idea to install it, since it sets some global options to sane defaults. Copy [stuff/packaging.md](stuff/packaging.md).
yq2.cfg to the baseq2/ subdirectory in your gamedata directory. Usually yq2.cfg
can be found somewhere in /usr/share or /usr/local/share.
### Compiling from source ### Compiling from source

View file

@ -29,6 +29,8 @@
#if defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) #if defined(__linux) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#include <unistd.h> // readlink(), amongst others #include <unistd.h> // readlink(), amongst others
#include "../../common/header/shared.h"
#endif #endif
#ifdef __FreeBSD__ #ifdef __FreeBSD__
@ -64,11 +66,12 @@ static void SetExecutablePath(char* exePath)
exePath[0] = '\0'; exePath[0] = '\0';
} }
#elif defined(__linux) || defined(__NetBSD__) || defined(__OpenBSD__) #elif defined(__linux) || defined(__NetBSD__)
// all the platforms that have /proc/$pid/exe or similar that symlink the // all the platforms that have /proc/$pid/exe or similar that symlink the
// real executable - basiscally Linux and the BSDs except for FreeBSD which // real executable - basiscally Linux and the BSDs except for FreeBSD which
// doesn't enable proc by default and has a sysctl() for this // doesn't enable proc by default and has a sysctl() for this. OpenBSD once
// had /proc but removed it for security reasons.
char buf[PATH_MAX] = {0}; char buf[PATH_MAX] = {0};
#ifdef __linux #ifdef __linux
snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid()); snprintf(buf, sizeof(buf), "/proc/%d/exe", getpid());
@ -115,7 +118,13 @@ static void SetExecutablePath(char* exePath)
#else #else
#error "Unsupported Platform!" // feel free to add implementation for your platform and send me a patch // Several platforms (for example OpenBSD) donn't provide a
// reliable way to determine the executable path. Just return
// an empty string.
exePath[0] = '\0';
// feel free to add implementation for your platform and send a pull request.
#warning "SetExecutablePath() is unimplemented on this platform"
#endif #endif
} }
@ -124,18 +133,25 @@ const char *Sys_GetBinaryDir(void)
{ {
static char exeDir[PATH_MAX] = {0}; static char exeDir[PATH_MAX] = {0};
if(exeDir[0] != '\0') return exeDir; if(exeDir[0] != '\0') {
return exeDir;
}
SetExecutablePath(exeDir); SetExecutablePath(exeDir);
if (exeDir[0] == '\0') {
Com_Printf("Couldn't determine executable path. Using ./ instead.\n");
Q_strlcpy(exeDir, "./", sizeof(exeDir));
} else {
// cut off executable name // cut off executable name
char* lastSlash = strrchr(exeDir, '/'); char *lastSlash = strrchr(exeDir, '/');
#ifdef _WIN32 #ifdef _WIN32
char* lastBackSlash = strrchr(exeDir, '\\'); char* lastBackSlash = strrchr(exeDir, '\\');
if(lastSlash == NULL || lastBackSlash > lastSlash) lastSlash = lastBackSlash; if(lastSlash == NULL || lastBackSlash > lastSlash) lastSlash = lastBackSlash;
#endif // _WIN32 #endif // _WIN32
if(lastSlash != NULL) lastSlash[1] = '\0'; // cut off after last (back)slash if (lastSlash != NULL) lastSlash[1] = '\0'; // cut off after last (back)slash
}
return exeDir; return exeDir;
} }

View file

@ -33,6 +33,8 @@
#include "../../common/header/common.h" #include "../../common/header/common.h"
#include "header/unix.h" #include "header/unix.h"
qboolean is_portable;
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -47,6 +49,13 @@ main(int argc, char **argv)
/* register signal handler */ /* register signal handler */
registerHandler(); registerHandler();
/* Are we portable? */
for (i = 0; i < argc; i++) {
if (strcmp(argv[i], "-portable") == 0) {
is_portable = true;
}
}
/* Prevent running Quake II as root. Only very mad /* Prevent running Quake II as root. Only very mad
minded or stupid people even think about it. :) */ minded or stupid people even think about it. :) */
if (getuid() == 0) if (getuid() == 0)

View file

@ -65,6 +65,8 @@ int findhandle;
int argc; int argc;
char *argv[MAX_NUM_ARGVS]; char *argv[MAX_NUM_ARGVS];
qboolean is_portable;
/* ================================================================ */ /* ================================================================ */
void void
@ -647,31 +649,33 @@ void
Sys_RedirectStdout(void) Sys_RedirectStdout(void)
{ {
char *cur; char *cur;
char *home;
char *old; char *old;
char dir[MAX_OSPATH];
char path_stdout[MAX_OSPATH]; char path_stdout[MAX_OSPATH];
char path_stderr[MAX_OSPATH]; char path_stderr[MAX_OSPATH];
const char *tmp;
if (portable->value) if (is_portable) {
tmp = Sys_GetBinaryDir();
Q_strlcpy(dir, tmp, sizeof(dir));
} else {
tmp = Sys_GetHomeDir();
Q_strlcpy(dir, tmp, sizeof(dir));
}
if (dir == NULL)
{ {
return; return;
} }
home = Sys_GetHomeDir(); cur = old = dir;
if (home == NULL)
{
return;
}
cur = old = home;
while (cur != NULL) while (cur != NULL)
{ {
if ((cur - old) > 1) if ((cur - old) > 1)
{ {
*cur = '\0'; *cur = '\0';
Sys_Mkdir(home); Sys_Mkdir(dir);
*cur = '/'; *cur = '/';
} }
@ -679,8 +683,8 @@ Sys_RedirectStdout(void)
cur = strchr(old + 1, '/'); cur = strchr(old + 1, '/');
} }
snprintf(path_stdout, sizeof(path_stdout), "%s/%s", home, "stdout.txt"); snprintf(path_stdout, sizeof(path_stdout), "%s/%s", dir, "stdout.txt");
snprintf(path_stderr, sizeof(path_stderr), "%s/%s", home, "stderr.txt"); snprintf(path_stderr, sizeof(path_stderr), "%s/%s", dir, "stderr.txt");
freopen(path_stdout, "w", stdout); freopen(path_stdout, "w", stdout);
freopen(path_stderr, "w", stderr); freopen(path_stderr, "w", stderr);
@ -755,7 +759,21 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
/* Force DPI awareness */ /* Force DPI awareness */
Sys_SetHighDPIMode(); Sys_SetHighDPIMode();
/* FIXME: No one can see this! */ /* Parse the command line arguments */
ParseCommandLine(lpCmdLine);
/* Are we portable? */
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "-portable") == 0) {
is_portable = true;
}
}
/* Need to redirect stdout before anything happens. */
#ifndef DEDICATED_ONLY
Sys_RedirectStdout();
#endif
printf("Yamagi Quake II v%s\n", YQ2VERSION); printf("Yamagi Quake II v%s\n", YQ2VERSION);
printf("=====================\n\n"); printf("=====================\n\n");
@ -791,12 +809,10 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
printf("Platform: %s\n", YQ2OSTYPE); printf("Platform: %s\n", YQ2OSTYPE);
printf("Architecture: %s\n", YQ2ARCH); printf("Architecture: %s\n", YQ2ARCH);
/* Seed PRNG */ /* Seed PRNG */
randk_seed(); randk_seed();
/* Parse the command line arguments */
ParseCommandLine(lpCmdLine);
/* Call the initialization code */ /* Call the initialization code */
Qcommon_Init(argc, argv); Qcommon_Init(argc, argv);

View file

@ -785,7 +785,7 @@ Key_WriteConsoleHistory()
int i; int i;
char path[MAX_OSPATH]; char path[MAX_OSPATH];
if (portable->value) if (is_portable)
{ {
Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetBinaryDir()); Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetBinaryDir());
} }
@ -832,7 +832,7 @@ Key_ReadConsoleHistory()
char path[MAX_OSPATH]; char path[MAX_OSPATH];
if (portable->value) if (is_portable)
{ {
Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetBinaryDir()); Com_sprintf(path, sizeof(path), "%sconsole_history.txt", Sys_GetBinaryDir());
} }

View file

@ -136,7 +136,7 @@ Com_VPrintf(int print_level, const char *fmt, va_list argptr)
/* logfile */ /* logfile */
if (logfile_active && logfile_active->value) if (logfile_active && logfile_active->value)
{ {
char name[MAX_QPATH]; char name[MAX_OSPATH];
if (!logfile) if (!logfile)
{ {

View file

@ -252,7 +252,7 @@ Cvar_Set2(char *var_name, char *value, qboolean force)
if (!strcmp(var->name, "game")) if (!strcmp(var->name, "game"))
{ {
FS_SetGamedir(var->string); FS_BuildGameSpecificSearchPath(var->string);
} }
} }
@ -370,7 +370,7 @@ Cvar_GetLatchedVars(void)
if (!strcmp(var->name, "game")) if (!strcmp(var->name, "game"))
{ {
FS_SetGamedir(var->string); FS_BuildGameSpecificSearchPath(var->string);
} }
} }
} }

View file

@ -114,26 +114,28 @@ fsPackTypes_t fs_packtypes[] = {
}; };
char fs_gamedir[MAX_OSPATH]; char fs_gamedir[MAX_OSPATH];
static char fs_currentGame[MAX_QPATH]; qboolean file_from_pak;
static char fs_fileInPath[MAX_OSPATH];
static qboolean fs_fileInPack;
/* Set by FS_FOpenFile. */
int file_from_pak = 0;
#ifdef ZIP
int file_from_pk3 = 0;
char file_from_pk3_name[MAX_QPATH];
#endif
cvar_t *fs_homepath;
cvar_t *fs_basedir; cvar_t *fs_basedir;
cvar_t *fs_cddir; cvar_t *fs_cddir;
cvar_t *fs_gamedirvar; cvar_t *fs_gamedirvar;
cvar_t *fs_debug; cvar_t *fs_debug;
fsHandle_t *FS_GetFileByHandle(fileHandle_t f); fsHandle_t *FS_GetFileByHandle(fileHandle_t f);
char *Sys_GetCurrentDirectory(void);
// --------
// Raw search path, the actual search
// bath is build from this one.
typedef struct fsRawPath_s {
char path[MAX_OSPATH];
qboolean create;
struct fsRawPath_s *next;
} fsRawPath_t;
fsRawPath_t *fs_rawPath;
// --------
/* /*
* All of Quake's data access is through a hierchal file system, but the * All of Quake's data access is through a hierchal file system, but the
@ -343,11 +345,7 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
fsSearchPath_t *search; fsSearchPath_t *search;
int i; int i;
file_from_pak = 0; file_from_pak = false;
#ifdef ZIP
file_from_pk3 = 0;
#endif
handle = FS_HandleForFile(name, f); handle = FS_HandleForFile(name, f);
Q_strlcpy(handle->name, name, sizeof(handle->name)); Q_strlcpy(handle->name, name, sizeof(handle->name));
handle->mode = FS_READ; handle->mode = FS_READ;
@ -381,9 +379,6 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
if (Q_stricmp(pack->files[i].name, handle->name) == 0) if (Q_stricmp(pack->files[i].name, handle->name) == 0)
{ {
/* Found it! */ /* Found it! */
Com_FilePath(pack->name, fs_fileInPath, sizeof(fs_fileInPath));
fs_fileInPack = true;
if (fs_debug->value) if (fs_debug->value)
{ {
Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n",
@ -393,7 +388,7 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
if (pack->pak) if (pack->pak)
{ {
/* PAK */ /* PAK */
file_from_pak = 1; file_from_pak = true;
handle->file = fopen(pack->name, "rb"); handle->file = fopen(pack->name, "rb");
if (handle->file) if (handle->file)
@ -406,8 +401,7 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
else if (pack->pk3) else if (pack->pk3)
{ {
/* PK3 */ /* PK3 */
file_from_pk3 = 1; file_from_pak = true;
Q_strlcpy(file_from_pk3_name, strrchr(pack->name, '/') + 1, sizeof(file_from_pk3_name));
handle->zip = unzOpen(pack->name); handle->zip = unzOpen(pack->name);
if (handle->zip) if (handle->zip)
@ -449,10 +443,6 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
if (handle->file) if (handle->file)
{ {
/* Found it! */
Q_strlcpy(fs_fileInPath, search->path, sizeof(fs_fileInPath));
fs_fileInPack = false;
if (fs_debug->value) if (fs_debug->value)
{ {
Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n", Com_Printf("FS_FOpenFile: '%s' (found in '%s').\n",
@ -463,11 +453,6 @@ FS_FOpenFile(const char *name, fileHandle_t *f, qboolean gamedir_only)
} }
} }
} }
/* Not found! */
fs_fileInPath[0] = 0;
fs_fileInPack = false;
if (fs_debug->value) if (fs_debug->value)
{ {
Com_Printf("FS_FOpenFile: couldn't find '%s'.\n", handle->name); Com_Printf("FS_FOpenFile: couldn't find '%s'.\n", handle->name);
@ -809,171 +794,6 @@ FS_LoadPK3(const char *packPath)
} }
#endif #endif
/*
* Adds the directory to the head of the path, then loads and adds pak1.pak
* pak2.pak ...
*
* Extended all functionality to include Quake III .pk3
*/
void
FS_AddGameDirectory(const char *dir)
{
char **list; /* File list. */
char path[MAX_OSPATH]; /* Path to PAK / PK3. */
int i, j; /* Loop counters. */
int nfiles; /* Number of files in list. */
fsSearchPath_t *search; /* Search path. */
fsPack_t *pack; /* PAK / PK3 file. */
pack = NULL;
/* Set game directory. */
Q_strlcpy(fs_gamedir, dir, sizeof(fs_gamedir));
/* Create directory if it does not exist. */
FS_CreatePath(fs_gamedir);
/* Add the directory to the search path. */
search = Z_Malloc(sizeof(fsSearchPath_t));
Q_strlcpy(search->path, dir, sizeof(search->path));
search->next = fs_searchPaths;
fs_searchPaths = search;
/* Add numbered pack files in sequence. */
for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++)
{
for (j = 0; j < MAX_PAKS; j++)
{
Com_sprintf(path, sizeof(path), "%s/pak%d.%s",
dir, j, fs_packtypes[i].suffix);
switch (fs_packtypes[i].format)
{
case PAK:
pack = FS_LoadPAK(path);
break;
#ifdef ZIP
case PK3:
pack = FS_LoadPK3(path);
break;
#endif
}
if (pack == NULL)
{
continue;
}
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pack;
search->next = fs_searchPaths;
fs_searchPaths = search;
}
}
/* Add not numbered pack files. */
for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++)
{
Com_sprintf(path, sizeof(path), "%s/*.%s", dir, fs_packtypes[i].suffix);
if ((list = FS_ListFiles(path, &nfiles, 0, SFF_SUBDIR)) == NULL)
{
continue;
}
Com_sprintf(path, sizeof(path), "%s/pak*.%s",
dir, fs_packtypes[i].suffix);
for (j = 0; j < nfiles - 1; j++)
{
/* Skip all packs starting with "pak" */
if (glob_match(path, list[j]))
{
continue;
}
switch (fs_packtypes[i].format)
{
case PAK:
pack = FS_LoadPAK(list[j]);
break;
#ifdef ZIP
case PK3:
pack = FS_LoadPK3(list[j]);
break;
#endif
}
if (pack == NULL)
{
continue;
}
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pack;
search->next = fs_searchPaths;
fs_searchPaths = search;
}
FS_FreeList(list, nfiles);
}
}
/*
* Use ~/.quake2/dir as fs_gamedir.
*/
void
FS_AddHomeAsGameDirectory(char *dir)
{
char *home;
char gdir[MAX_OSPATH];
size_t len;
if (portable->value)
{
return;
}
home = Sys_GetHomeDir();
if (home == NULL)
{
return;
}
len = snprintf(gdir, sizeof(gdir), "%s%s/", home, dir);
FS_CreatePath(gdir);
if ((len > 0) && (len < sizeof(gdir)) && (gdir[len - 1] == '/'))
{
gdir[len - 1] = 0;
}
Q_strlcpy(fs_gamedir, gdir, sizeof(fs_gamedir));
FS_AddGameDirectory(gdir);
}
void FS_AddBinaryDirAsGameDirectory(const char* dir)
{
char gdir[MAX_OSPATH];
const char *datadir = Sys_GetBinaryDir();
if(datadir[0] == '\0')
{
return;
}
int len = snprintf(gdir, sizeof(gdir), "%s%s/", datadir, dir);
printf("Using binary dir %s to fetch paks\n", gdir);
if ((len > 0) && (len < sizeof(gdir)) && (gdir[len - 1] == '/'))
{
gdir[len - 1] = 0;
}
FS_AddGameDirectory(gdir);
}
/* /*
* Allows enumerating all of the directories in the search path. * Allows enumerating all of the directories in the search path.
*/ */
@ -1060,97 +880,6 @@ FS_Path_f(void)
#endif #endif
} }
/*
* Sets the gamedir and path to a different directory.
*/
void
FS_SetGamedir(char *dir)
{
int i;
fsSearchPath_t *next;
if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/"))
{
Com_Printf("Gamedir should be a single filename, not a path.\n");
return;
}
/* Do not set BASEDIR as gamedir. It was already set by FS_InitFilesystem()
* and setting it again f*cks the search pathes up. Yes, this a hack. */
if(!Q_stricmp(dir, BASEDIRNAME))
{
return;
}
/* Free up any current game dir info. */
while (fs_searchPaths != fs_baseSearchPaths)
{
if (fs_searchPaths->pack)
{
if (fs_searchPaths->pack->pak)
{
fclose(fs_searchPaths->pack->pak);
}
#ifdef ZIP
if (fs_searchPaths->pack->pk3)
{
unzClose(fs_searchPaths->pack->pk3);
}
#endif
Z_Free(fs_searchPaths->pack->files);
Z_Free(fs_searchPaths->pack);
}
next = fs_searchPaths->next;
Z_Free(fs_searchPaths);
fs_searchPaths = next;
}
/* Close open files for game dir. */
for (i = 0; i < MAX_HANDLES; i++)
{
if (strstr(fs_handles[i].name, dir) &&
((fs_handles[i].file != NULL)
#ifdef ZIP
|| (fs_handles[i].zip != NULL)
#endif
))
{
FS_FCloseFile(i);
}
}
/* Flush all data, so it will be forced to reload. */
if ((dedicated != NULL) && (dedicated->value != 1))
{
Cbuf_AddText("vid_restart\nsnd_restart\n");
}
Com_sprintf(fs_gamedir, sizeof(fs_gamedir), "%s/%s",
fs_basedir->string, dir);
if ((strcmp(dir, BASEDIRNAME) == 0) || (*dir == 0))
{
Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET);
Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO);
}
else
{
Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET);
if (fs_cddir->string[0] == '\0')
{
FS_AddGameDirectory(va("%s/%s", fs_cddir->string, dir));
}
FS_AddGameDirectory(va("%s/%s", fs_basedir->string, dir));
FS_AddBinaryDirAsGameDirectory(dir);
FS_AddHomeAsGameDirectory(dir);
}
}
/* /*
* Creates a filelink_t. * Creates a filelink_t.
*/ */
@ -1523,67 +1252,327 @@ FS_Dir_f(void)
} }
} }
// --------
void
FS_AddDirToSearchPath(char *dir, qboolean create) {
char **list;
char path[MAX_OSPATH];
int i, j;
int nfiles;
fsPack_t *pack = NULL;
fsSearchPath_t *search;
size_t len = strlen(dir);
// The directory must not end with an /. It would
// f*ck up the logic in other parts of the game...
if (dir[len - 1] == '/')
{
dir[len - 1] = '\0';
}
else if (dir[len - 1] == '\\') {
dir[len - 1] = '\0';
}
// Set the current directory as game directory. This
// is somewhat fragile since the game directory MUST
// be the last directory added to the search path.
Q_strlcpy(fs_gamedir, dir, sizeof(fs_gamedir));
if (create) {
FS_CreatePath(fs_gamedir);
}
// Add the directory itself.
search = Z_Malloc(sizeof(fsSearchPath_t));
Q_strlcpy(search->path, dir, sizeof(search->path));
search->next = fs_searchPaths;
fs_searchPaths = search;
// We need to add numbered paks in te directory in
// sequence and all other paks after them. Otherwise
// the gamedata may break.
for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) {
for (j = 0; j < MAX_PAKS; j++) {
Com_sprintf(path, sizeof(path), "%s/pak%d.%s", dir, j, fs_packtypes[i].suffix);
switch (fs_packtypes[i].format)
{
case PAK:
pack = FS_LoadPAK(path);
break;
#ifdef ZIP
case PK3:
pack = FS_LoadPK3(path);
break;
#endif
}
if (pack == NULL)
{
continue;
}
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pack;
search->next = fs_searchPaths;
fs_searchPaths = search;
}
}
// And as said above all other pak files.
for (i = 0; i < sizeof(fs_packtypes) / sizeof(fs_packtypes[0]); i++) {
Com_sprintf(path, sizeof(path), "%s/*.%s", dir, fs_packtypes[i].suffix);
// Nothing here, next pak type please.
if ((list = FS_ListFiles(path, &nfiles, 0, SFF_SUBDIR)) == NULL)
{
continue;
}
Com_sprintf(path, sizeof(path), "%s/pak*.%s", dir, fs_packtypes[i].suffix);
for (j = 0; j < nfiles - 1; j++)
{
// If the pak starts with the string 'pak' it's ignored.
// This is somewhat stupid, it would be better to ignore
// just pak%d...
if (glob_match(path, list[j]))
{
continue;
}
switch (fs_packtypes[i].format)
{
case PAK:
pack = FS_LoadPAK(list[j]);
break;
#ifdef ZIP
case PK3:
pack = FS_LoadPK3(list[j]);
break;
#endif
}
if (pack == NULL)
{
continue;
}
search = Z_Malloc(sizeof(fsSearchPath_t));
search->pack = pack;
search->next = fs_searchPaths;
fs_searchPaths = search;
}
FS_FreeList(list, nfiles);
}
}
void FS_BuildGenericSearchPath(void) {
// We may not use the va() function from shared.c
// since it's buffersize is 1024 while most OS have
// a maximum path size of 4096...
char path[MAX_OSPATH];
fsRawPath_t *search = fs_rawPath;
while (search != NULL) {
Com_sprintf(path, sizeof(path), "%s/%s", search->path, BASEDIRNAME);
FS_AddDirToSearchPath(path, search->create);
search = search->next;
}
// Until here we've added the generic directories to the
// search path. Save the current head node so we can
// distinguish generic and specialized directories.
fs_baseSearchPaths = fs_searchPaths;
// We need to create the screenshot directory since the
// render dll doesn't link the filesystem stuff.
Com_sprintf(path, sizeof(path), "%s/scrnshot", fs_gamedir);
Sys_Mkdir(path);
}
void
FS_BuildGameSpecificSearchPath(char *dir)
{
// We may not use the va() function from shared.c
// since it's buffersize is 1024 while most OS have
// a maximum path size of 4096...
char path[MAX_OSPATH];
int i;
fsRawPath_t *search;
fsSearchPath_t *next;
// This is against PEBCAK. The user may give us paths like
// xatrix/ or even /home/stupid/quake2/xatrix.
if (!*dir || !strcmp(dir, ".") || strstr(dir, "..") || strstr(dir, "/") || strstr(dir, "\\"))
{
Com_Printf("Gamedir should be a single filename, not a path.\n");
return;
}
// BASEDIR is already added as a generic directory. Adding it
// again as a specialised directory breaks the logic in other
// parts of the code. This may happen if the user does something
// like ./quake2 +set game baseq2
if(!Q_stricmp(dir, BASEDIRNAME))
{
return;
}
// We may already have specialised directories in our search
// path. This can happen if the server changes the mod. Let's
// remove them.
while (fs_searchPaths != fs_baseSearchPaths)
{
if (fs_searchPaths->pack)
{
if (fs_searchPaths->pack->pak)
{
fclose(fs_searchPaths->pack->pak);
}
#ifdef ZIP
if (fs_searchPaths->pack->pk3)
{
unzClose(fs_searchPaths->pack->pk3);
}
#endif
Z_Free(fs_searchPaths->pack->files);
Z_Free(fs_searchPaths->pack);
}
next = fs_searchPaths->next;
Z_Free(fs_searchPaths);
fs_searchPaths = next;
}
/* Close open files for game dir. */
for (i = 0; i < MAX_HANDLES; i++)
{
if (strstr(fs_handles[i].name, dir) &&
((fs_handles[i].file != NULL)
#ifdef ZIP
|| (fs_handles[i].zip != NULL)
#endif
))
{
FS_FCloseFile(i);
}
}
// Enforce a renderer and sound backend restart to
// purge all internal caches. This is rather hacky
// but Quake II doesn't have a better mechanism...
if ((dedicated != NULL) && (dedicated->value != 1))
{
Cbuf_AddText("vid_restart\nsnd_restart\n");
}
// The game was reset to baseq2. Nothing to do here.
if ((Q_stricmp(dir, BASEDIRNAME) == 0) || (*dir == 0)) {
Cvar_FullSet("gamedir", "", CVAR_SERVERINFO | CVAR_NOSET);
Cvar_FullSet("game", "", CVAR_LATCH | CVAR_SERVERINFO);
// fs_gamedir must be reset to the last
// dir of the generic search path.
Q_strlcpy(fs_gamedir, fs_baseSearchPaths->path, sizeof(fs_gamedir));
} else {
Cvar_FullSet("gamedir", dir, CVAR_SERVERINFO | CVAR_NOSET);
search = fs_rawPath;
while (search != NULL) {
Com_sprintf(path, sizeof(path), "%s/%s", search->path, dir);
FS_AddDirToSearchPath(path, search->create);
search = search->next;
}
}
// We need to create the screenshot directory since the
// render dll doesn't link the filesystem stuff.
Com_sprintf(path, sizeof(path), "%s/scrnshot", fs_gamedir);
Sys_Mkdir(path);
}
// --------
void FS_AddDirToRawPath (const char *dir, qboolean create) {
fsRawPath_t *search;
// Add the directory
search = Z_Malloc(sizeof(fsRawPath_t));
Q_strlcpy(search->path, dir, sizeof(search->path));
search->create = create;
search->next = fs_rawPath;
fs_rawPath = search;
}
void FS_BuildRawPath(void) {
// Add $HOME/.yq2 (MUST be the last dir!)
if (!is_portable) {
const char *homedir = Sys_GetHomeDir();
if (homedir != NULL) {
FS_AddDirToRawPath(homedir, true);
}
}
// Add $binarydir
const char *binarydir = Sys_GetBinaryDir();
if(binarydir[0] != '\0')
{
FS_AddDirToRawPath(binarydir, false);
}
// Add $basedir/
FS_AddDirToRawPath(fs_basedir->string, false);
// Add SYSTEMDIR
#ifdef SYSTEMWIDE
FS_AddDirToRawPath(SYSTEMDIR, false);
#endif
// The CD must be the last directory of the path,
// otherwise we cannot be sure that the game won't
// stream the videos from the CD.
if (fs_cddir->string[0] != '\0') {
FS_AddDirToRawPath(fs_cddir->string, false);
}
}
// --------
void void
FS_InitFilesystem(void) FS_InitFilesystem(void)
{ {
char scrnshotdir[MAX_OSPATH]; // Register FS commands.
/* Register FS commands. */
Cmd_AddCommand("path", FS_Path_f); Cmd_AddCommand("path", FS_Path_f);
Cmd_AddCommand("link", FS_Link_f); Cmd_AddCommand("link", FS_Link_f);
Cmd_AddCommand("dir", FS_Dir_f); Cmd_AddCommand("dir", FS_Dir_f);
/* basedir <path> Allows the game to run from outside the data tree. */ // Register cvars
fs_basedir = Cvar_Get("basedir", fs_basedir = Cvar_Get("basedir", ".", CVAR_NOSET);
#ifdef SYSTEMWIDE
SYSTEMDIR,
#else
".",
#endif
CVAR_NOSET);
/* cddir <path> Logically concatenates the cddir after the basedir to
allow the game to run from outside the data tree. */
fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET); fs_cddir = Cvar_Get("cddir", "", CVAR_NOSET);
fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO);
if (fs_cddir->string[0] != '\0')
{
FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_cddir->string));
}
/* Debug flag. */
fs_debug = Cvar_Get("fs_debug", "0", 0); fs_debug = Cvar_Get("fs_debug", "0", 0);
/* Game directory. */ // Build search path
fs_gamedirvar = Cvar_Get("game", "", CVAR_LATCH | CVAR_SERVERINFO); FS_BuildRawPath();
FS_BuildGenericSearchPath();
/* Current directory. */
fs_homepath = Cvar_Get("homepath", Sys_GetCurrentDirectory(), CVAR_NOSET);
/* Add baseq2 to search path. */
FS_AddGameDirectory(va("%s/" BASEDIRNAME, fs_basedir->string));
FS_AddBinaryDirAsGameDirectory(BASEDIRNAME);
FS_AddHomeAsGameDirectory(BASEDIRNAME);
/* Any set gamedirs will be freed up to here. */
fs_baseSearchPaths = fs_searchPaths;
Q_strlcpy(fs_currentGame, BASEDIRNAME, sizeof(fs_currentGame));
/* Check for game override. */
if (fs_gamedirvar->string[0] != '\0') if (fs_gamedirvar->string[0] != '\0')
{ {
FS_SetGamedir(fs_gamedirvar->string); FS_BuildGameSpecificSearchPath(fs_gamedirvar->string);
} }
/* Create directory if it does not exist. */ // Debug output
FS_CreatePath(fs_gamedir);
/* create the scrnshots directory if it doesn't exist
* (do it here instead of in ref_gl so ref_gl doesn't need mkdir)
*/
Com_sprintf(scrnshotdir, sizeof(scrnshotdir), "%s/scrnshot", FS_Gamedir());
Sys_Mkdir(scrnshotdir);
Com_Printf("Using '%s' for writing.\n", fs_gamedir); Com_Printf("Using '%s' for writing.\n", fs_gamedir);
} }

View file

@ -643,7 +643,7 @@ void Pmove(pmove_t *pmove);
#define SFF_INPACK 0x20 #define SFF_INPACK 0x20
extern int file_from_pak; extern qboolean file_from_pak;
typedef int fileHandle_t; typedef int fileHandle_t;
@ -680,7 +680,7 @@ char **FS_ListFiles2(char *findname, int *numfiles,
void FS_FreeList(char **list, int nfiles); void FS_FreeList(char **list, int nfiles);
void FS_InitFilesystem(void); void FS_InitFilesystem(void);
void FS_SetGamedir(char *dir); void FS_BuildGameSpecificSearchPath(char *dir);
char *FS_Gamedir(void); char *FS_Gamedir(void);
char *FS_NextPath(char *prevpath); char *FS_NextPath(char *prevpath);
int FS_LoadFile(char *path, void **buffer); int FS_LoadFile(char *path, void **buffer);
@ -726,7 +726,9 @@ extern cvar_t *modder;
extern cvar_t *dedicated; extern cvar_t *dedicated;
extern cvar_t *host_speeds; extern cvar_t *host_speeds;
extern cvar_t *log_stats; extern cvar_t *log_stats;
extern cvar_t *portable;
/* Hack for portable client */
extern qboolean is_portable;
extern FILE *log_stats_file; extern FILE *log_stats_file;

View file

@ -213,14 +213,6 @@ Qcommon_Init(int argc, char **argv)
Cbuf_AddEarlyCommands(false); Cbuf_AddEarlyCommands(false);
Cbuf_Execute(); Cbuf_Execute();
/* Be portable, don't add HOME to the search path
* This is needed by Sys_RedirectStdout() on Windows*/
portable = Cvar_Get("portable", "0", 0);
#ifndef DEDICATED_ONLY
Sys_RedirectStdout();
#endif
FS_InitFilesystem(); FS_InitFilesystem();
Cbuf_AddText("exec default.cfg\n"); Cbuf_AddText("exec default.cfg\n");

View file

@ -296,7 +296,7 @@ SV_BeginDownload_f(void)
extern cvar_t *allow_download_models; extern cvar_t *allow_download_models;
extern cvar_t *allow_download_sounds; extern cvar_t *allow_download_sounds;
extern cvar_t *allow_download_maps; extern cvar_t *allow_download_maps;
extern int file_from_pak; extern qboolean file_from_pak;
int offset = 0; int offset = 0;
name = Cmd_Argv(1); name = Cmd_Argv(1);

66
stuff/packaging.md Normal file
View file

@ -0,0 +1,66 @@
# Notes for Package Maintainers
Our 7.00 release caused some trouble for package maintainers
(see https://github.com/yquake2/yquake2/issues/214), so we decided to finally
properly document how we think it should be done and what assumptions
Yamagi Quake II makes regarding binary locations etc.
## Where you should put the executables
Yamagi Quake II expects all binaries (executables and libs) to be in the same directory
(or, in the case of game.so/.dll/dylib, in the mod-specific subdirectory).
So the binary directory should look somehow like this *(on Unix-like systems;
on Windows and OSX it's very similar but with different extensions: .dll or
.dylib instead of .so, and the executables have .exe file extension on Windows of course)*:
* /path/to/yamagi-quake2/
- quake2
- q2ded
- ref_gl1.so
- ref_gl3.so
- baseq2/
* game.so
- xatrix/
* game.so
- ... *(the same for other addons/mods)*
Yamagi Quake2 will get the directory the `quake2` executable is in from the system
and then look in that directory (and nowhere else!) for `ref_*.so`.
It will look for `game.so` there first, but if it's not found in the binary directory,
it will look for it in all directories that are also searched for game
data (SYSTEMDIR, basedir, $HOME/.yq2/). This is for better compatibility with mods that
might ship their own game.so.
You can **just symlink the executables to a directory in your $PATH**, like /usr/bin/.
(*Except on OpenBSD, which does not provide a way to get the executable directory,
there you'll need a shellscript that first does a `cd /path/to/yamagi-quake2/` and
then executes `./quake2`*)
We want all binaries to be in the same directory to ensure that people don't accidentally
update only parts of their Yamagi Quake II installtion, so they'd end up with a new
quake2 executable and old render libraries (`ref_*.so`) and report weird bugs.
## The SYSTEMWIDE and SYSTEMDIR options
The Makefile allows you to enable the `SYSTEMWIDE` feature (`WITH_SYSTEMWIDE=yes`) and
lets you specify the directory that will be used (`SYSTEMDIR`, `WITH_SYSTEMDIR=/your/custom/path/`).
If you don't set SYSTEMDIR, it defaults to `/usr/share/games/quake2`, which is what debian uses.
The `SYSTEMDIR` was meant to contain just the game data, *not* the binaries, and allows
several Quake2 source ports to share the same game data.
Unfortunately, we didn't document this assumption, so some packages used it for both binaries
and data, just binaries or - which causes most trouble - only for the game libs, but not the
executables. The latter case doesn't work anymore since we (re)introduced the render libs,
as they need to be located next to the executable, and if the executable is in /usr/bin/ you
don't want to put libs next to it.
Anyway: If you use `SYSTEMWIDE`/`SYSTEMDIR`, please use it for game data.
You *can* also put the binaries in there, but in that case please put all of them
(including executables) in there, as explained above.
## Alternative startup config
Yamagi Quake II has support for an alternative startup config.
It may be a good idea to install it, since it sets some global options to sane defaults.
Copy yq2.cfg to the baseq2/ subdirectory in the gamedata (`SYSTEMDIR`) directory.