X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=0c6901b3823836002769d861e6bab239c75a3bc2;hb=939f4320b463a667e8a9c5c5453781c825d80c16;hp=a0fdcde45b918da9c6e81c7b934d80ece2de1bca;hpb=d159736a199e4043ed98cde48f6d5716ec40845c;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index a0fdcde4..0c6901b3 100644 --- a/fs.c +++ b/fs.c @@ -1,7 +1,7 @@ /* DarkPlaces file system - Copyright (C) 2003-2005 Mathieu Olivier + Copyright (C) 2003-2006 Mathieu Olivier This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -22,6 +22,9 @@ 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 @@ -222,7 +225,6 @@ typedef struct pack_s int ignorecase; // PK3 ignores case int numfiles; packfile_t *files; - struct pack_s *next; } pack_t; @@ -247,6 +249,7 @@ FUNCTION PROTOTYPES void FS_Dir_f(void); void FS_Ls_f(void); +static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet); static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, fs_offset_t offset, fs_offset_t packsize, fs_offset_t realsize, int flags); @@ -262,8 +265,6 @@ VARIABLES mempool_t *fs_mempool; -pack_t *packlist = NULL; - searchpath_t *fs_searchpaths = NULL; #define MAX_FILES_IN_PACK 65536 @@ -271,7 +272,10 @@ searchpath_t *fs_searchpaths = NULL; char fs_gamedir[MAX_OSPATH]; char fs_basedir[MAX_OSPATH]; -qboolean fs_modified; // set true if using non-id files +// 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)"}; @@ -581,8 +585,6 @@ pack_t *FS_LoadPackPK3 (const char *packfile) pack->handle = packhandle; pack->numfiles = eocd.nbentries; pack->files = (packfile_t *)Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t)); - pack->next = packlist; - packlist = pack; real_nb_files = PK3_BuildFileList (pack, &eocd); if (real_nb_files < 0) @@ -786,18 +788,22 @@ pack_t *FS_LoadPackPAK (const char *packfile) return NULL; } + info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles); + lseek (packhandle, header.dirofs, SEEK_SET); + if(header.dirlen != read (packhandle, (void *)info, header.dirlen)) + { + Con_Printf("%s is an incomplete PAK, not loading\n", packfile); + Mem_Free(info); + close(packhandle); + return NULL; + } + pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t)); pack->ignorecase = false; // PAK is case sensitive strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = 0; pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t)); - pack->next = packlist; - packlist = pack; - - info = (dpackfile_t *)Mem_Alloc(tempmempool, sizeof(*info) * numpackfiles); - lseek (packhandle, header.dirofs, SEEK_SET); - read (packhandle, (void *)info, header.dirlen); // parse the directory for (i = 0;i < numpackfiles;i++) @@ -814,6 +820,136 @@ pack_t *FS_LoadPackPAK (const char *packfile) return pack; } +/* +================ +FS_AddPack_Fullpath + +Adds the given pack to the search path. +The pack type is autodetected by the file extension. + +Returns true if the file was successfully added to the +search path or if it was already included. + +If keep_plain_dirs is set, the pack will be added AFTER the first sequence of +plain directories. +================ +*/ +static qboolean FS_AddPack_Fullpath(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs) +{ + searchpath_t *search; + pack_t *pak = NULL; + const char *ext = FS_FileExtension(pakfile); + + for(search = fs_searchpaths; search; search = search->next) + { + if(search->pack && !strcasecmp(search->pack->filename, pakfile)) + { + if(already_loaded) + *already_loaded = true; + return true; // already loaded + } + } + + if(already_loaded) + *already_loaded = false; + + if(!strcasecmp(ext, "pak")) + pak = FS_LoadPackPAK (pakfile); + else if(!strcasecmp(ext, "pk3")) + pak = FS_LoadPackPK3 (pakfile); + else + Con_Printf("\"%s\" does not have a pack extension\n", pakfile); + + if (pak) + { + if(keep_plain_dirs) + { + // find the first item whose next one is a pack or NULL + searchpath_t *insertion_point = 0; + if(fs_searchpaths && !fs_searchpaths->pack) + { + insertion_point = fs_searchpaths; + for(;;) + { + if(!insertion_point->next) + break; + if(insertion_point->next->pack) + break; + insertion_point = insertion_point->next; + } + } + // If insertion_point is NULL, this means that either there is no + // item in the list yet, or that the very first item is a pack. In + // that case, we want to insert at the beginning... + if(!insertion_point) + { + search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); + search->pack = pak; + search->next = fs_searchpaths; + fs_searchpaths = search; + } + else + // otherwise we want to append directly after insertion_point. + { + search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); + search->pack = pak; + search->next = insertion_point->next; + insertion_point->next = search; + } + } + else + { + search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); + search->pack = pak; + search->next = fs_searchpaths; + fs_searchpaths = search; + } + return true; + } + else + { + Con_Printf("unable to load pak \"%s\"\n", pakfile); + return false; + } +} + + +/* +================ +FS_AddPack + +Adds the given pack to the search path and searches for it in the game path. +The pack type is autodetected by the file extension. + +Returns true if the file was successfully added to the +search path or if it was already included. + +If keep_plain_dirs is set, the pack will be added AFTER the first sequence of +plain directories. +================ +*/ +qboolean FS_AddPack(const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs) +{ + char fullpath[MAX_QPATH]; + int index; + searchpath_t *search; + + if(already_loaded) + *already_loaded = false; + + // then find the real name... + search = FS_FindFile(pakfile, &index, true); + if(!search || search->pack) + { + Con_Printf("could not find pak \"%s\"\n", pakfile); + return false; + } + + dpsnprintf(fullpath, sizeof(fullpath), "%s%s", search->filename, pakfile); + + return FS_AddPack_Fullpath(fullpath, already_loaded, keep_plain_dirs); +} + /* ================ @@ -825,53 +961,38 @@ then loads and adds pak1.pak pak2.pak ... */ void FS_AddGameDirectory (const char *dir) { - stringlist_t *list, *current; + int i; + stringlist_t list; searchpath_t *search; - pack_t *pak; 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 (matchpattern(current->text, "*.pak", true)) + if (!strcasecmp(FS_FileExtension(list.strings[i]), "pak")) { - dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text); - pak = FS_LoadPackPAK (pakfile); - if (pak) - { - search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); - search->pack = pak; - search->next = fs_searchpaths; - fs_searchpaths = search; - } - else - Con_Printf("unable to load pak \"%s\"\n", pakfile); + dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, list.strings[i]); + FS_AddPack_Fullpath(pakfile, NULL, false); } } - // add any PK3 package in the director - for (current = list;current;current = current->next) + // add any PK3 package in the directory + for (i = 0;i < list.numstrings;i++) { - if (matchpattern(current->text, "*.pk3", true)) + if (!strcasecmp(FS_FileExtension(list.strings[i]), "pk3")) { - dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text); - pak = FS_LoadPackPK3 (pakfile); - if (pak) - { - search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); - search->pack = pak; - search->next = fs_searchpaths; - fs_searchpaths = search; - } - else - Con_Printf("unable to load pak \"%s\"\n", pakfile); + 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) @@ -910,26 +1031,269 @@ void FS_AddGameHierarchy (const char *dir) FS_FileExtension ============ */ -static const char *FS_FileExtension (const char *in) +const char *FS_FileExtension (const char *in) { const char *separator, *backslash, *colon, *dot; separator = strrchr(in, '/'); backslash = strrchr(in, '\\'); - if (separator < backslash) + if (!separator || separator < backslash) separator = backslash; colon = strrchr(in, ':'); - if (separator < colon) + if (!separator || separator < colon) separator = colon; dot = strrchr(in, '.'); - if (dot == NULL || dot < separator) + if (dot == NULL || (separator && (dot < separator))) return ""; return dot + 1; } +/* +============ +FS_FileWithoutPath +============ +*/ +const char *FS_FileWithoutPath (const char *in) +{ + const char *separator, *backslash, *colon; + + separator = strrchr(in, '/'); + backslash = strrchr(in, '\\'); + if (!separator || separator < backslash) + separator = backslash; + colon = strrchr(in, ':'); + if (!separator || separator < colon) + separator = colon; + return separator ? separator + 1 : in; +} + + +/* +================ +FS_ClearSearchPath +================ +*/ +void FS_ClearSearchPath (void) +{ + while (fs_searchpaths) + { + searchpath_t *search = fs_searchpaths; + fs_searchpaths = search->next; + if (search->pack) + { + if (search->pack->files) + Mem_Free(search->pack->files); + Mem_Free(search->pack); + } + Mem_Free(search); + } +} + + +/* +================ +FS_Rescan +================ +*/ +void FS_Rescan (void) +{ + int i; + qboolean fs_modified = false; + + FS_ClearSearchPath(); + + // add the game-specific paths + // gamedirname1 (typically id1) + FS_AddGameHierarchy (gamedirname1); + // update the com_modname (used for server info) + strlcpy(com_modname, gamedirname1, sizeof(com_modname)); + + // add the game-specific path, if any + // (only used for mission packs and the like, which should set fs_modified) + if (gamedirname2) + { + fs_modified = true; + FS_AddGameHierarchy (gamedirname2); + } + + // -game + // Adds basedir/gamedir as an override game + // LordHavoc: now supports multiple -game directories + // set the com_modname (reported in server info) + for (i = 0;i < fs_numgamedirs;i++) + { + fs_modified = true; + FS_AddGameHierarchy (fs_gamedirs[i]); + // update the com_modname (used server info) + strlcpy (com_modname, fs_gamedirs[i], sizeof (com_modname)); + } + + // set the default screenshot name to either the mod name or the + // gamemode screenshot name + if (strcmp(com_modname, gamedirname1)) + Cvar_SetQuick (&scr_screenshot_name, com_modname); + else + Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); + + // If "-condebug" is in the command line, remove the previous log file + if (COM_CheckParm ("-condebug") != 0) + unlink (va("%s/qconsole.log", fs_gamedir)); + + // look for the pop.lmp file and set registered to true if it is found + if ((gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) && !FS_FileExists("gfx/pop.lmp")) + { + if (fs_modified) + Con_Print("Playing shareware version, with modification.\nwarning: most mods require full quake data.\n"); + else + Con_Print("Playing shareware version.\n"); + } + else + { + Cvar_Set ("registered", "1"); + if (gamemode == GAME_NORMAL || gamemode == GAME_HIPNOTIC || gamemode == GAME_ROGUE) + Con_Print("Playing registered version.\n"); + } +} + +void FS_Rescan_f(void) +{ + FS_Rescan(); +} + +/* +================ +FS_ChangeGameDirs +================ +*/ +extern void Host_SaveConfig_f (void); +extern void Host_LoadConfig_f (void); +qboolean FS_ChangeGameDirs(int numgamedirs, char gamedirs[][MAX_QPATH], qboolean complain, qboolean failmissing) +{ + int i; + + if (fs_numgamedirs == numgamedirs) + { + for (i = 0;i < numgamedirs;i++) + if (strcasecmp(fs_gamedirs[i], gamedirs[i])) + break; + if (i == numgamedirs) + return true; // already using this set of gamedirs, do nothing + } + + if (numgamedirs > MAX_GAMEDIRS) + { + if (complain) + Con_Printf("That is too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); + return false; // too many gamedirs + } + + for (i = 0;i < numgamedirs;i++) + { + // if string is nasty, reject it + if(FS_CheckNastyPath(gamedirs[i], true)) + { + if (complain) + Con_Printf("Nasty gamedir name rejected: %s\n", gamedirs[i]); + return false; // nasty gamedirs + } + } + + for (i = 0;i < numgamedirs;i++) + { + if (!FS_CheckGameDir(gamedirs[i]) && failmissing) + { + if (complain) + Con_Printf("Gamedir missing: %s%s/\n", fs_basedir, gamedirs[i]); + return false; // missing gamedirs + } + } + + Host_SaveConfig_f(); + + fs_numgamedirs = numgamedirs; + for (i = 0;i < fs_numgamedirs;i++) + strlcpy(fs_gamedirs[i], gamedirs[i], sizeof(fs_gamedirs[i])); + + // reinitialize filesystem to detect the new paks + FS_Rescan(); + + // exec the new config + Host_LoadConfig_f(); + + // reinitialize the loaded sounds + S_Reload_f(); + + // reinitialize renderer (this reloads hud/console background/etc) + R_Modules_Restart(); + + return true; +} + +/* +================ +FS_GameDir_f +================ +*/ +void FS_GameDir_f (void) +{ + int i; + int numgamedirs; + char gamedirs[MAX_GAMEDIRS][MAX_QPATH]; + + if (Cmd_Argc() < 2) + { + Con_Printf("gamedirs active:"); + for (i = 0;i < fs_numgamedirs;i++) + Con_Printf(" %s", fs_gamedirs[i]); + Con_Printf("\n"); + return; + } + + numgamedirs = Cmd_Argc() - 1; + if (numgamedirs > MAX_GAMEDIRS) + { + Con_Printf("Too many gamedirs (%i > %i)\n", numgamedirs, MAX_GAMEDIRS); + return; + } + + 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_connected || 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"); + return; + } + + FS_ChangeGameDirs(numgamedirs, gamedirs, true, true); +} + + +/* +================ +FS_CheckGameDir +================ +*/ +qboolean FS_CheckGameDir(const char *gamedir) +{ + qboolean success; + stringlist_t list; + stringlistinit(&list); + listdirectory(&list, va("%s%s/", fs_basedir, gamedir)); + success = list.numstrings > 0; + stringlistfreecontents(&list); + return success; +} + + /* ================ FS_Init @@ -938,12 +1302,16 @@ FS_Init void FS_Init (void) { int i; - searchpath_t *search; fs_mempool = Mem_AllocPool("file management", 0, NULL); - strcpy(fs_basedir, ""); - strcpy(fs_gamedir, ""); + strlcpy(fs_gamedir, "", sizeof(fs_gamedir)); + +// If the base directory is explicitly defined by the compilation process +#ifdef DP_FS_BASEDIR + strlcpy(fs_basedir, DP_FS_BASEDIR, sizeof(fs_basedir)); +#else + strlcpy(fs_basedir, "", sizeof(fs_basedir)); #ifdef MACOSX // FIXME: is there a better way to find the directory outside the .app? @@ -957,6 +1325,7 @@ void FS_Init (void) strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir)); fs_basedir[split - com_argv[0]] = 0; } +#endif #endif PK3_OpenLibrary (); @@ -977,97 +1346,45 @@ void FS_Init (void) if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\') strlcat(fs_basedir, "/", sizeof(fs_basedir)); - // -path [] ... - // Fully specifies the exact search path, overriding the generated one -// COMMANDLINEOPTION: Filesystem: -path specifies the full search path manually, overriding the generated one, example: -path c:\quake\id1 c:\quake\pak0.pak c:\quake\pak1.pak (not recommended) - i = COM_CheckParm ("-path"); - if (i) - { - fs_modified = true; - while (++i < com_argc) - { - if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-') - break; - - search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); - if (!strcasecmp (FS_FileExtension(com_argv[i]), "pak")) - { - search->pack = FS_LoadPackPAK (com_argv[i]); - if (!search->pack) - { - Con_Printf ("Couldn't load packfile: %s\n", com_argv[i]); - Mem_Free(search); - continue; - } - } - else if (!strcasecmp (FS_FileExtension (com_argv[i]), "pk3")) - { - search->pack = FS_LoadPackPK3 (com_argv[i]); - if (!search->pack) - { - Con_Printf ("Couldn't load packfile: %s\n", com_argv[i]); - Mem_Free(search); - continue; - } - } - else - strlcpy (search->filename, com_argv[i], sizeof (search->filename)); - search->next = fs_searchpaths; - fs_searchpaths = search; - } - return; - } - - // add the game-specific paths - // gamedirname1 (typically id1) - FS_AddGameHierarchy (gamedirname1); + if (!FS_CheckGameDir(gamedirname1)) + Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname1); - // add the game-specific path, if any - if (gamedirname2) - { - fs_modified = true; - FS_AddGameHierarchy (gamedirname2); - } - - // set the com_modname (reported in server info) - strlcpy(com_modname, gamedirname1, sizeof(com_modname)); + if (gamedirname2 && !FS_CheckGameDir(gamedirname2)) + Sys_Error("base gamedir %s%s/ not found!\n", fs_basedir, gamedirname2); // -game // Adds basedir/gamedir as an override game // LordHavoc: now supports multiple -game directories - for (i = 1;i < com_argc;i++) + for (i = 1;i < com_argc && fs_numgamedirs < MAX_GAMEDIRS;i++) { if (!com_argv[i]) continue; if (!strcmp (com_argv[i], "-game") && i < com_argc-1) { i++; - fs_modified = true; - FS_AddGameHierarchy (com_argv[i]); - // update the com_modname - strlcpy (com_modname, com_argv[i], sizeof (com_modname)); + if (FS_CheckNastyPath(com_argv[i], true)) + Sys_Error("-game %s%s/ is a dangerous/non-portable path\n", fs_basedir, com_argv[i]); + if (!FS_CheckGameDir(com_argv[i])) + Sys_Error("-game %s%s/ not found!\n", fs_basedir, com_argv[i]); + // add the gamedir to the list of active gamedirs + strlcpy (fs_gamedirs[fs_numgamedirs], com_argv[i], sizeof(fs_gamedirs[fs_numgamedirs])); + fs_numgamedirs++; } } - // If "-condebug" is in the command line, remove the previous log file - if (COM_CheckParm ("-condebug") != 0) - unlink (va("%s/qconsole.log", fs_gamedir)); + // generate the searchpath + FS_Rescan(); } void FS_Init_Commands(void) { Cvar_RegisterVariable (&scr_screenshot_name); + 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"); Cmd_AddCommand ("path", FS_Path_f, "print searchpath (game directories and archives)"); Cmd_AddCommand ("dir", FS_Dir_f, "list files in searchpath matching an * filename pattern, one per line"); Cmd_AddCommand ("ls", FS_Ls_f, "list files in searchpath matching an * filename pattern, multiple per line"); - - // set the default screenshot name to either the mod name or the - // gamemode screenshot name - if (fs_modified) - Cvar_SetQuick (&scr_screenshot_name, com_modname); - else - Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); } /* @@ -1188,7 +1505,7 @@ qfile_t *FS_OpenPackedFile (pack_t* pack, int pack_ind) if (lseek (pack->handle, pfile->offset, SEEK_SET) == -1) { Con_Printf ("FS_OpenPackedFile: can't lseek to %s in %s (offset: %d)\n", - pfile->name, pack->filename, pfile->offset); + pfile->name, pack->filename, (int) pfile->offset); return NULL; } @@ -1259,8 +1576,12 @@ Return true if the path should be rejected due to one of the following: or are just not a good idea for a mod to be using. ==================== */ -int FS_CheckNastyPath (const char *path) +int FS_CheckNastyPath (const char *path, qboolean isgamedir) { + // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless + if (!path[0]) + return 2; + // Windows: don't allow \ in filenames (windows-only), period. // (on Windows \ is a directory separator, but / is also supported) if (strstr(path, "\\")) @@ -1277,14 +1598,34 @@ int FS_CheckNastyPath (const char *path) if (strstr(path, "//")) return 1; // non-portable attempt to go to parent directory - // all: don't allow going to current directory (./) or parent directory (../ or /../) - if (strstr(path, "./")) + // all: don't allow going to parent directory (../ or /../) + if (strstr(path, "..")) return 2; // attempt to go outside the game directory // Windows and UNIXes: don't allow absolute paths if (path[0] == '/') return 2; // attempt to go outside the game directory + // all: don't allow . characters before the last slash (it should only be used in filenames, not path elements), this catches all imaginable cases of ./, ../, .../, etc + if (strchr(path, '.')) + { + if (isgamedir) + { + // gamedir is entirely path elements, so simply forbid . entirely + return 2; + } + if (strchr(path, '.') < strrchr(path, '/')) + return 2; // possible attempt to go outside the game directory + } + + // all: forbid trailing slash on gamedir + if (isgamedir && path[strlen(path)-1] == '/') + return 2; + + // all: forbid leading dot on any filename for any reason + if (strstr(path, "/.")) + return 2; // attempt to go outside the game directory + // after all these checks we're pretty sure it's a / separated filename // and won't do much if any harm return false; @@ -1331,8 +1672,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) // Found it if (!diff) { - if (!quiet) - Con_DPrintf("FS_FindFile: %s in %s\n", + if (!quiet && developer.integer >= 10) + Con_Printf("FS_FindFile: %s in %s\n", pak->files[middle].name, pak->filename); if (index != NULL) @@ -1353,8 +1694,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) dpsnprintf(netpath, sizeof(netpath), "%s%s", search->filename, name); if (FS_SysFileExists (netpath)) { - if (!quiet) - Con_DPrintf("FS_FindFile: %s\n", netpath); + if (!quiet && developer.integer >= 10) + Con_Printf("FS_FindFile: %s\n", netpath); if (index != NULL) *index = -1; @@ -1363,8 +1704,8 @@ static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) } } - if (!quiet) - Con_DPrintf("FS_FindFile: can't find %s\n", name); + if (!quiet && developer.integer >= 10) + Con_Printf("FS_FindFile: can't find %s\n", name); if (index != NULL) *index = -1; @@ -1420,7 +1761,7 @@ Open a file. The syntax is the same as fopen */ qfile_t* FS_Open (const char* filepath, const char* mode, qboolean quiet, qboolean nonblocking) { - if (FS_CheckNastyPath(filepath)) + if (FS_CheckNastyPath(filepath, false)) { Con_Printf("FS_Open(\"%s\", \"%s\", %s): nasty filename rejected\n", filepath, mode, quiet ? "true" : "false"); return NULL; @@ -1880,6 +2221,19 @@ fs_offset_t FS_Tell (qfile_t* file) } +/* +==================== +FS_FileSize + +Give the total size of a file +==================== +*/ +fs_offset_t FS_FileSize (qfile_t* file) +{ + return file->real_length; +} + + /* ==================== FS_Purge @@ -1966,17 +2320,19 @@ FS_StripExtension void FS_StripExtension (const char *in, char *out, size_t size_out) { char *last = NULL; + char currentchar; if (size_out == 0) return; - while (*in && size_out > 1) + while ((currentchar = *in) && size_out > 1) { - if (*in == '.') + if (currentchar == '.') last = out; - else if (*in == '/' || *in == '\\' || *in == ':') + else if (currentchar == '/' || currentchar == '\\' || currentchar == ':') last = NULL; - *out++ = *in++; + *out++ = currentchar; + in++; size_out--; } if (last) @@ -2073,8 +2429,9 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) 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]; @@ -2089,10 +2446,9 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) return NULL; } + stringlistinit(&resultlist); + stringlistinit(&dirlist); search = NULL; - liststart = NULL; - listcurrent = NULL; - listtemp = NULL; slash = strrchr(pattern, '/'); backslash = strrchr(pattern, '\\'); colon = strrchr(pattern, ':'); @@ -2114,19 +2470,17 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) pak = searchpath->pack; for (i = 0;i < pak->numfiles;i++) { - strcpy(temp, pak->files[i].name); + strlcpy(temp, pak->files[i].name, sizeof(temp)); while (temp[0]) { 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); } @@ -2151,57 +2505,52 @@ fssearch_t *FS_Search(const char *pattern, int caseinsensitive, int quiet) { // 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; - strcpy(search->filenames[numfiles], listtemp->text); + textlen = strlen(resultlist.strings[resultlistindex]) + 1; + memcpy(search->filenames[numfiles], resultlist.strings[resultlistindex], textlen); numfiles++; - numchars += (int)strlen(listtemp->text) + 1; + numchars += (int)textlen; } - if (liststart) - stringlistfree(liststart); } + stringlistfreecontents(&resultlist); Mem_Free(basepath); return search; @@ -2307,3 +2656,87 @@ void FS_Ls_f(void) FS_ListDirectoryCmd("ls", false); } +const char *FS_WhichPack(const char *filename) +{ + int index; + searchpath_t *sp = FS_FindFile(filename, &index, true); + if(sp && sp->pack) + return sp->pack->filename; + else + return 0; +} + +/* +==================== +FS_IsRegisteredQuakePack + +Look for a proof of purchase file file in the requested package + +If it is found, this file should NOT be downloaded. +==================== +*/ +qboolean FS_IsRegisteredQuakePack(const char *name) +{ + searchpath_t *search; + pack_t *pak; + + // search through the path, one element at a time + for (search = fs_searchpaths;search;search = search->next) + { + if (search->pack && !strcasecmp(FS_FileWithoutPath(search->filename), name)) + { + int (*strcmp_funct) (const char* str1, const char* str2); + int left, right, middle; + + pak = search->pack; + strcmp_funct = pak->ignorecase ? strcasecmp : strcmp; + + // Look for the file (binary search) + left = 0; + right = pak->numfiles - 1; + while (left <= right) + { + int diff; + + middle = (left + right) / 2; + diff = !strcmp_funct (pak->files[middle].name, "gfx/pop.lmp"); + + // Found it + if (!diff) + return true; + + // If we're too far in the list + if (diff > 0) + right = middle - 1; + else + left = middle + 1; + } + + // we found the requested pack but it is not registered quake + return false; + } + } + + return false; +} + +int FS_CRCFile(const char *filename, size_t *filesizepointer) +{ + int crc = -1; + unsigned char *filedata; + fs_offset_t filesize; + if (filesizepointer) + *filesizepointer = 0; + if (!filename || !*filename) + return crc; + filedata = FS_LoadFile(filename, tempmempool, true, &filesize); + if (filedata) + { + if (filesizepointer) + *filesizepointer = filesize; + crc = CRC_Block(filedata, filesize); + Mem_Free(filedata); + } + return crc; +} +