Boston, MA 02111-1307, USA
*/
-// on UNIX platforms we need to define this so that video saving does not cause a SIGFSZ (file size) signal when a video clip exceeds 2GB
-#define _FILE_OFFSET_BITS 64
-
#include "quakedef.h"
#include <limits.h>
#ifdef WIN32
# include <direct.h>
# include <io.h>
+# include <shlobj.h>
#else
# include <pwd.h>
# include <sys/stat.h>
#endif
#include "fs.h"
+#include "wad.h"
// Win32 requires us to add O_BINARY, but the other OSes don't have it
#ifndef O_BINARY
# define O_NONBLOCK 0
#endif
+// largefile support for Win32
+#ifdef WIN32
+# define lseek _lseeki64
+#endif
/*
char fs_basedir[MAX_OSPATH];
// list of active game directories (empty if not running a mod)
-#define MAX_GAMEDIRS 16
int fs_numgamedirs = 0;
char fs_gamedirs[MAX_GAMEDIRS][MAX_QPATH];
cvar_t scr_screenshot_name = {0, "scr_screenshot_name","dp", "prefix name for saved screenshots (changes based on -game commandline, as well as which game mode is running)"};
+cvar_t fs_empty_files_in_pack_mark_deletions = {0, "fs_empty_files_in_pack_mark_deletions", "0", "if enabled, empty files in a pak/pk3 count as not existing but cancel the search in further packs, effectively allowing patch pak/pk3 files to 'delete' files"};
/*
*/
void FS_AddGameDirectory (const char *dir)
{
- stringlist_t *list, *current;
+ int i;
+ stringlist_t list;
searchpath_t *search;
char pakfile[MAX_OSPATH];
strlcpy (fs_gamedir, dir, sizeof (fs_gamedir));
- list = listdirectory(dir);
+ stringlistinit(&list);
+ listdirectory(&list, dir);
+ stringlistsort(&list);
// add any PAK package in the directory
- for (current = list;current;current = current->next)
+ for (i = 0;i < list.numstrings;i++)
{
- if (!strcasecmp(FS_FileExtension(current->text), "pak"))
+ if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak"))
{
- dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
+ dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
FS_AddPack_Fullpath(pakfile, NULL, false);
}
}
// add any PK3 package in the directory
- for (current = list;current;current = current->next)
+ for (i = 0;i < list.numstrings;i++)
{
- if (!strcasecmp(FS_FileExtension(current->text), "pk3"))
+ if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3"))
{
- dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text);
+ dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]);
FS_AddPack_Fullpath(pakfile, NULL, false);
}
}
- freedirectory(list);
+ stringlistfreecontents(&list);
// Add the directory to the search path
// (unpacked files have the priority over packed files)
*/
void FS_AddGameHierarchy (const char *dir)
{
-#ifndef WIN32
+ int i;
+ char userdir[MAX_QPATH];
+#ifdef WIN32
+ TCHAR mydocsdir[MAX_PATH + 1];
+#else
const char *homedir;
#endif
// Add the common game directory
FS_AddGameDirectory (va("%s%s/", fs_basedir, dir));
-#ifndef WIN32
+ *userdir = 0;
+
// Add the personal game directory
+#ifdef WIN32
+ if(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, mydocsdir) == S_OK)
+ dpsnprintf(userdir, sizeof(userdir), "%s/My Games/%s/", mydocsdir, gameuserdirname);
+ fprintf(stderr, "userdir = %s\n", userdir);
+#else
homedir = getenv ("HOME");
- if (homedir != NULL && homedir[0] != '\0')
- FS_AddGameDirectory (va("%s/.%s/%s/", homedir, gameuserdirname, dir));
+ if(homedir)
+ dpsnprintf(userdir, sizeof(userdir), "%s/.%s/", homedir, gameuserdirname);
#endif
+
+#ifdef WIN32
+ if(!COM_CheckParm("-mygames"))
+ {
+ int fd = open (va("%s%s/config.cfg", fs_basedir, dir), O_WRONLY | O_CREAT, 0666); // note: no O_TRUNC here!
+ if(fd >= 0)
+ {
+ close(fd);
+ *userdir = 0; // we have write access to the game dir, so let's use it
+ }
+ }
+#endif
+
+ if(COM_CheckParm("-nohome"))
+ *userdir = 0;
+
+ if((i = COM_CheckParm("-userdir")) && i < com_argc - 1)
+ dpsnprintf(userdir, sizeof(userdir), "%s/", com_argv[i+1]);
+
+ if (*userdir)
+ FS_AddGameDirectory(va("%s%s/", userdir, dir));
}
*/
void FS_ClearSearchPath (void)
{
+ // unload all packs and directory information, close all pack files
+ // (if a qfile is still reading a pack it won't be harmed because it used
+ // dup() to get its own handle already)
while (fs_searchpaths)
{
searchpath_t *search = fs_searchpaths;
fs_searchpaths = search->next;
if (search->pack)
{
+ // close the file
+ close(search->pack->handle);
+ // free any memory associated with it
if (search->pack->files)
Mem_Free(search->pack->files);
Mem_Free(search->pack);
if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE)
Con_Print("Playing registered version.\n");
}
+
+ // unload all wads so that future queries will return the new data
+ W_UnloadAll();
}
void FS_Rescan_f(void)
FS_ChangeGameDirs
================
*/
-extern void Host_SaveConfig_f (void);
+extern void Host_SaveConfig (void);
extern void Host_LoadConfig_f (void);
qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing)
{
int i;
- if (cls.state != ca_disconnected || sv.active)
- {
- if (complain)
- Con_Printf("Can not change gamedir while client is connected or server is running!\n");
- return false;
- }
-
if (fs_numgamedirs == numgamedirs)
{
for (i = 0;i < numgamedirs;i++)
}
}
- Host_SaveConfig_f();
+ // halt demo playback to close the file
+ CL_Disconnect();
+
+ Host_SaveConfig();
fs_numgamedirs = numgamedirs;
for (i = 0;i < fs_numgamedirs;i++)
// exec the new config
Host_LoadConfig_f();
- // reinitialize the loaded sounds
- S_Reload_f();
+ // unload all sounds so they will be reloaded from the new files as needed
+ S_UnloadAllSounds_f();
// reinitialize renderer (this reloads hud/console background/etc)
R_Modules_Restart();
for (i = 0;i < numgamedirs;i++)
strlcpy(gamedirs[i], Cmd_Argv(i+1), sizeof(gamedirs[i]));
- // allow gamedir change during demo loop
- if (cls.demoplayback)
- CL_Disconnect();
-
- if (cls.state != ca_disconnected || sv.active)
+ if ((cls.state == ca_connected && !cls.demoplayback) || sv.active)
{
// actually, changing during game would work fine, but would be stupid
Con_Printf("Can not change gamedir while client is connected or server is running!\n");
*/
qboolean FS_CheckGameDir(const char *gamedir)
{
- stringlist_t *list = listdirectory(va("%s%s/", fs_basedir, gamedir));
- if (list)
- freedirectory(list);
- return list != NULL;
+ qboolean success;
+ stringlist_t list;
+ stringlistinit(&list);
+ listdirectory(&list, va("%s%s/", fs_basedir, gamedir));
+ success = list.numstrings > 0;
+ stringlistfreecontents(&list);
+ return success;
}
void FS_Init_Commands(void)
{
Cvar_RegisterVariable (&scr_screenshot_name);
+ Cvar_RegisterVariable (&fs_empty_files_in_pack_mark_deletions);
Cmd_AddCommand ("gamedir", FS_GameDir_f, "changes active gamedir list (can take multiple arguments), not including base directory (example usage: gamedir ctf)");
Cmd_AddCommand ("fs_rescan", FS_Rescan_f, "rescans filesystem for new pack archives and any other changes");
*/
void FS_Shutdown (void)
{
+ // close all pack files and such
+ // (hopefully there aren't any other open files, but they'll be cleaned up
+ // by the OS anyway)
+ FS_ClearSearchPath();
Mem_FreePool (&fs_mempool);
}
// Found it
if (!diff)
{
+ if (fs_empty_files_in_pack_mark_deletions.integer && pak->files[middle].realsize == 0)
+ {
+ // yes, but the first one is empty so we treat it as not being there
+ if (!quiet && developer.integer >= 10)
+ Con_Printf("FS_FindFile: %s is marked as deleted\n", name);
+
+ if (index != NULL)
+ *index = -1;
+ return NULL;
+ }
+
if (!quiet && developer.integer >= 10)
Con_Printf("FS_FindFile: %s in %s\n",
pak->files[middle].name, pak->filename);
if (file->buff_ind < file->buff_len)
{
count = file->buff_len - file->buff_ind;
+ count = ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
+ done += count;
+ memcpy (buffer, &file->buff[file->buff_ind], count);
+ file->buff_ind += count;
- done += ((fs_offset_t)buffersize > count) ? count : (fs_offset_t)buffersize;
- memcpy (buffer, &file->buff[file->buff_ind], done);
- file->buff_ind += done;
-
- buffersize -= done;
+ buffersize -= count;
if (buffersize == 0)
return done;
}
*/
int FS_Getc (qfile_t* file)
{
- char c;
+ unsigned char c;
if (FS_Read (file, &c, 1) != 1)
return EOF;
default:
return -1;
}
- if (offset < 0 || offset > (long) file->real_length)
+ if (offset < 0 || offset > file->real_length)
return -1;
// If we have the data in our read buffer, we don't need to actually seek
}
+/*
+==================
+FS_FileType
+
+Look for a file in the packages and in the filesystem
+==================
+*/
+int FS_FileType (const char *filename)
+{
+ searchpath_t *search;
+ char fullpath[MAX_QPATH];
+
+ search = FS_FindFile (filename, NULL, true);
+ if(!search)
+ return FS_FILETYPE_NONE;
+
+ if(search->pack)
+ return FS_FILETYPE_FILE; // TODO can't check directories in paks yet, maybe later
+
+ dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, filename);
+ return FS_SysFileType(fullpath);
+}
+
+
/*
==================
FS_FileExists
Look for a file in the filesystem only
==================
*/
-qboolean FS_SysFileExists (const char *path)
+int FS_SysFileType (const char *path)
{
#if WIN32
- int desc;
+ DWORD result = GetFileAttributes(path);
- // TODO: use another function instead, to avoid opening the file
- desc = open (path, O_RDONLY | O_BINARY);
- if (desc < 0)
- return false;
+ if(result == INVALID_FILE_ATTRIBUTES)
+ return FS_FILETYPE_NONE;
- close (desc);
- return true;
+ if(result & FILE_ATTRIBUTE_DIRECTORY)
+ return FS_FILETYPE_DIRECTORY;
+
+ return FS_FILETYPE_FILE;
#else
struct stat buf;
if (stat (path,&buf) == -1)
- return false;
+ return FS_FILETYPE_NONE;
- return true;
+ if(S_ISDIR(buf.st_mode))
+ return FS_FILETYPE_DIRECTORY;
+
+ return FS_FILETYPE_FILE;
#endif
}
+qboolean FS_SysFileExists (const char *path)
+{
+ return FS_SysFileType (path) != FS_FILETYPE_NONE;
+}
+
void FS_mkdir (const char *path)
{
#if WIN32
fssearch_t *search;
searchpath_t *searchpath;
pack_t *pak;
- int i, basepathlength, numfiles, numchars;
- stringlist_t *dir, *dirfile, *liststart, *listcurrent, *listtemp;
+ int i, basepathlength, numfiles, numchars, resultlistindex, dirlistindex;
+ stringlist_t resultlist;
+ stringlist_t dirlist;
const char *slash, *backslash, *colon, *separator;
char *basepath;
char netpath[MAX_OSPATH];
return NULL;
}
+ stringlistinit(&resultlist);
+ stringlistinit(&dirlist);
search = NULL;
- liststart = NULL;
- listcurrent = NULL;
- listtemp = NULL;
slash = strrchr(pattern, '/');
backslash = strrchr(pattern, '\\');
colon = strrchr(pattern, ':');
{
if (matchpattern(temp, (char *)pattern, true))
{
- for (listtemp = liststart;listtemp;listtemp = listtemp->next)
- if (!strcmp(listtemp->text, temp))
+ for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+ if (!strcmp(resultlist.strings[resultlistindex], temp))
break;
- if (listtemp == NULL)
+ if (resultlistindex == resultlist.numstrings)
{
- listcurrent = stringlistappend(listcurrent, temp);
- if (liststart == NULL)
- liststart = listcurrent;
+ stringlistappend(&resultlist, temp);
if (!quiet)
Con_DPrintf("SearchPackFile: %s : %s\n", pak->filename, temp);
}
{
// get a directory listing and look at each name
dpsnprintf(netpath, sizeof (netpath), "%s%s", searchpath->filename, basepath);
- if ((dir = listdirectory(netpath)))
+ stringlistinit(&dirlist);
+ listdirectory(&dirlist, netpath);
+ for (dirlistindex = 0;dirlistindex < dirlist.numstrings;dirlistindex++)
{
- for (dirfile = dir;dirfile;dirfile = dirfile->next)
+ dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirlist.strings[dirlistindex]);
+ if (matchpattern(temp, (char *)pattern, true))
{
- dpsnprintf(temp, sizeof(temp), "%s%s", basepath, dirfile->text);
- if (matchpattern(temp, (char *)pattern, true))
+ for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+ if (!strcmp(resultlist.strings[resultlistindex], temp))
+ break;
+ if (resultlistindex == resultlist.numstrings)
{
- for (listtemp = liststart;listtemp;listtemp = listtemp->next)
- if (!strcmp(listtemp->text, temp))
- break;
- if (listtemp == NULL)
- {
- listcurrent = stringlistappend(listcurrent, temp);
- if (liststart == NULL)
- liststart = listcurrent;
- if (!quiet)
- Con_DPrintf("SearchDirFile: %s\n", temp);
- }
+ stringlistappend(&resultlist, temp);
+ if (!quiet)
+ Con_DPrintf("SearchDirFile: %s\n", temp);
}
}
- freedirectory(dir);
}
+ stringlistfreecontents(&dirlist);
}
}
- if (liststart)
+ if (resultlist.numstrings)
{
- liststart = stringlistsort(liststart);
- numfiles = 0;
+ stringlistsort(&resultlist);
+ numfiles = resultlist.numstrings;
numchars = 0;
- for (listtemp = liststart;listtemp;listtemp = listtemp->next)
- {
- numfiles++;
- numchars += (int)strlen(listtemp->text) + 1;
- }
+ for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
+ numchars += (int)strlen(resultlist.strings[resultlistindex]) + 1;
search = (fssearch_t *)Z_Malloc(sizeof(fssearch_t) + numchars + numfiles * sizeof(char *));
search->filenames = (char **)((char *)search + sizeof(fssearch_t));
search->filenamesbuffer = (char *)((char *)search + sizeof(fssearch_t) + numfiles * sizeof(char *));
search->numfilenames = (int)numfiles;
numfiles = 0;
numchars = 0;
- for (listtemp = liststart;listtemp;listtemp = listtemp->next)
+ for (resultlistindex = 0;resultlistindex < resultlist.numstrings;resultlistindex++)
{
size_t textlen;
search->filenames[numfiles] = search->filenamesbuffer + numchars;
- textlen = strlen(listtemp->text) + 1;
- memcpy(search->filenames[numfiles], listtemp->text, textlen);
+ textlen = strlen(resultlist.strings[resultlistindex]) + 1;
+ memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen);
numfiles++;
numchars += (int)textlen;
}
- if (liststart)
- stringlistfree(liststart);
}
+ stringlistfreecontents(&resultlist);
Mem_Free(basepath);
return search;