X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=3aab0537b24f27f7cb3d4ed84befff07453de7d0;hb=6badc60e47deb6a26e2e318d7f3813ffaef14c52;hp=9fd764153457d1f5a049ec1a69626c8100e6dd3f;hpb=6e433c1776f48ca417d7a855b336151ae77443ac;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index 9fd76415..3aab0537 100644 --- a/fs.c +++ b/fs.c @@ -1,8 +1,7 @@ /* DarkPlaces file system - Copyright (C) 2003-2005 Mathieu Olivier - Copyright (C) 1999,2000 contributors of the QuakeForge project + 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 @@ -44,6 +43,11 @@ # define O_BINARY 0 #endif +// In case the system doesn't support the O_NONBLOCK flag +#ifndef O_NONBLOCK +# define O_NONBLOCK 0 +#endif + /* @@ -90,7 +94,11 @@ CONSTANTS #define MAX_WBITS 15 #define Z_OK 0 #define Z_STREAM_END 1 -#define ZLIB_VERSION "1.1.4" +#define ZLIB_VERSION "1.2.3" + +// Uncomment the following line if the zlib DLL you have still uses +// the 1.1.x series calling convention on Win32 (WINAPI) +//#define ZLIB_USES_WINAPI /* @@ -106,11 +114,11 @@ TYPES // been cast to "void*" for a matter of simplicity typedef struct { - qbyte *next_in; // next input byte + unsigned char *next_in; // next input byte unsigned int avail_in; // number of bytes available at next_in unsigned long total_in; // total nb of input bytes read so far - qbyte *next_out; // next output byte should be put there + unsigned char *next_out; // next output byte should be put there unsigned int avail_out; // remaining free space at next_out unsigned long total_out; // total nb of bytes output so far @@ -127,12 +135,10 @@ typedef struct } z_stream; -typedef enum -{ - QFILE_FLAG_NONE = 0, - QFILE_FLAG_PACKED = (1 << 0), // inside a package (PAK or PK3) - QFILE_FLAG_DEFLATED = (1 << 1) // file is compressed using the deflate algorithm (PK3 only) -} qfile_flags_t; +// inside a package (PAK or PK3) +#define QFILE_FLAG_PACKED (1 << 0) +// file is compressed using the deflate algorithm (PK3 only) +#define QFILE_FLAG_DEFLATED (1 << 1) #define FILE_BUFF_SIZE 2048 typedef struct @@ -141,12 +147,12 @@ typedef struct size_t comp_length; // length of the compressed file size_t in_ind, in_len; // input buffer current index and length size_t in_position; // position in the compressed file - qbyte input [FILE_BUFF_SIZE]; + unsigned char input [FILE_BUFF_SIZE]; } ztoolkit_t; struct qfile_s { - qfile_flags_t flags; + int flags; int handle; // file descriptor fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode) fs_offset_t position; // current position in the file @@ -154,8 +160,8 @@ struct qfile_s int ungetc; // single stored character from ungetc, cleared to EOF when read // Contents buffer - size_t buff_ind, buff_len; // buffer current index and length - qbyte buff [FILE_BUFF_SIZE]; + fs_offset_t buff_ind, buff_len; // buffer current index and length + unsigned char buff [FILE_BUFF_SIZE]; // For zipped files ztoolkit_t* ztk; @@ -166,7 +172,7 @@ struct qfile_s // You can get the complete ZIP format description from PKWARE website -typedef struct +typedef struct pk3_endOfCentralDir_s { unsigned int signature; unsigned short disknum; @@ -180,13 +186,13 @@ typedef struct // ------ PAK files on disk ------ // -typedef struct +typedef struct dpackfile_s { char name[56]; int filepos, filelen; } dpackfile_t; -typedef struct +typedef struct dpackheader_s { char id[4]; int dirofs; @@ -195,17 +201,15 @@ typedef struct // Packages in memory -typedef enum -{ - PACKFILE_FLAG_NONE = 0, - PACKFILE_FLAG_TRUEOFFS = (1 << 0), // the offset in packfile_t is the true contents offset - PACKFILE_FLAG_DEFLATED = (1 << 1) // file compressed using the deflate algorithm -} packfile_flags_t; +// the offset in packfile_t is the true contents offset +#define PACKFILE_FLAG_TRUEOFFS (1 << 0) +// file compressed using the deflate algorithm +#define PACKFILE_FLAG_DEFLATED (1 << 1) -typedef struct +typedef struct packfile_s { char name [MAX_QPATH]; - packfile_flags_t flags; + int flags; fs_offset_t offset; fs_offset_t packsize; // size in the package fs_offset_t realsize; // real file size (uncompressed) @@ -243,9 +247,11 @@ FUNCTION PROTOTYPES void FS_Dir_f(void); void FS_Ls_f(void); +static const char *FS_FileExtension (const char *in); +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, packfile_flags_t flags); + fs_offset_t realsize, int flags); /* @@ -258,8 +264,6 @@ VARIABLES mempool_t *fs_mempool; -fs_offset_t fs_filesize; - pack_t *packlist = NULL; searchpath_t *fs_searchpaths = NULL; @@ -271,6 +275,8 @@ char fs_basedir[MAX_OSPATH]; qboolean fs_modified; // set true if using non-id files +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)"}; + /* ============================================================================= @@ -281,7 +287,7 @@ PRIVATE FUNCTIONS - PK3 HANDLING */ // Functions exported from zlib -#ifdef WIN32 +#if defined(WIN32) && defined(ZLIB_USES_WINAPI) # define ZEXPORT WINAPI #else # define ZEXPORT @@ -332,8 +338,15 @@ qboolean PK3_OpenLibrary (void) { const char* dllnames [] = { -#ifdef WIN32 +#if defined(WIN64) + "zlib64.dll", +#elif defined(WIN32) +# ifdef ZLIB_USES_WINAPI + "zlibwapi.dll", "zlib.dll", +# else + "zlib1.dll", +# endif #elif defined(MACOSX) "libz.dylib", #else @@ -368,8 +381,8 @@ Extract the end of the central directory from a PK3 package */ qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOfCentralDir_t *eocd) { - long filesize, maxsize; - qbyte *buffer, *ptr; + fs_offset_t filesize, maxsize; + unsigned char *buffer, *ptr; int ind; // Get the package size @@ -382,7 +395,7 @@ qboolean PK3_GetEndOfCentralDir (const char *packfile, int packhandle, pk3_endOf maxsize = filesize; else maxsize = ZIP_MAX_COMMENTS_SIZE + ZIP_END_CDIR_SIZE; - buffer = Mem_Alloc (tempmempool, maxsize); + buffer = (unsigned char *)Mem_Alloc (tempmempool, maxsize); lseek (packhandle, filesize - maxsize, SEEK_SET); if (read (packhandle, buffer, maxsize) != (fs_offset_t) maxsize) { @@ -431,12 +444,12 @@ Extract the file list from a PK3 file */ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) { - qbyte *central_dir, *ptr; + unsigned char *central_dir, *ptr; unsigned int ind; fs_offset_t remaining; // Load the central directory in memory - central_dir = Mem_Alloc (tempmempool, eocd->cdir_size); + central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size); lseek (pack->handle, eocd->cdir_offset, SEEK_SET); read (pack->handle, central_dir, eocd->cdir_size); @@ -486,7 +499,7 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) { char filename [sizeof (pack->files[0].name)]; fs_offset_t offset, packsize, realsize; - packfile_flags_t flags; + int flags; // Extract the name (strip it if necessary) namesize = min(namesize, (int)sizeof (filename) - 1); @@ -539,7 +552,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile) if (! PK3_GetEndOfCentralDir (packfile, packhandle, &eocd)) { - Con_Printf ("%s is not a PK3 file", packfile); + Con_Printf ("%s is not a PK3 file\n", packfile); close(packhandle); return NULL; } @@ -547,7 +560,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile) // Multi-volume ZIP archives are NOT allowed if (eocd.disknum != 0 || eocd.cdir_disknum != 0) { - Con_Printf ("%s is a multi-volume ZIP archive", packfile); + Con_Printf ("%s is a multi-volume ZIP archive\n", packfile); close(packhandle); return NULL; } @@ -557,26 +570,26 @@ pack_t *FS_LoadPackPK3 (const char *packfile) #if MAX_FILES_IN_PACK < 65535 if (eocd.nbentries > MAX_FILES_IN_PACK) { - Con_Printf ("%s contains too many files (%hu)", packfile, eocd.nbentries); + Con_Printf ("%s contains too many files (%hu)\n", packfile, eocd.nbentries); close(packhandle); return NULL; } #endif // Create a package structure in memory - pack = Mem_Alloc(fs_mempool, sizeof (pack_t)); + pack = (pack_t *)Mem_Alloc(fs_mempool, sizeof (pack_t)); pack->ignorecase = true; // PK3 ignores case strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; pack->numfiles = eocd.nbentries; - pack->files = Mem_Alloc(fs_mempool, eocd.nbentries * sizeof(packfile_t)); + 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) { - Con_Printf ("%s is not a valid PK3 file", packfile); + Con_Printf ("%s is not a valid PK3 file\n", packfile); close(pack->handle); Mem_Free(pack); return NULL; @@ -596,7 +609,7 @@ Find where the true file data offset is */ qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack) { - qbyte buffer [ZIP_LOCAL_CHUNK_BASE_SIZE]; + unsigned char buffer [ZIP_LOCAL_CHUNK_BASE_SIZE]; fs_offset_t count; // Already found? @@ -608,7 +621,7 @@ qboolean PK3_GetTrueFileOffset (packfile_t *pfile, pack_t *pack) count = read (pack->handle, buffer, ZIP_LOCAL_CHUNK_BASE_SIZE); if (count != ZIP_LOCAL_CHUNK_BASE_SIZE || BuffBigLong (buffer) != ZIP_DATA_HEADER) { - Con_Printf ("Can't retrieve file %s in package %s", pfile->name, pack->filename); + Con_Printf ("Can't retrieve file %s in package %s\n", pfile->name, pack->filename); return false; } @@ -638,7 +651,7 @@ Add a file to the list of files contained into a package */ static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, fs_offset_t offset, fs_offset_t packsize, - fs_offset_t realsize, packfile_flags_t flags) + fs_offset_t realsize, int flags) { int (*strcmp_funct) (const char* str1, const char* str2); int left, right, middle; @@ -752,7 +765,7 @@ pack_t *FS_LoadPackPAK (const char *packfile) read (packhandle, (void *)&header, sizeof(header)); if (memcmp(header.id, "PACK", 4)) { - Con_Printf ("%s is not a packfile", packfile); + Con_Printf ("%s is not a packfile\n", packfile); close(packhandle); return NULL; } @@ -761,7 +774,7 @@ pack_t *FS_LoadPackPAK (const char *packfile) if (header.dirlen % sizeof(dpackfile_t)) { - Con_Printf ("%s has an invalid directory size", packfile); + Con_Printf ("%s has an invalid directory size\n", packfile); close(packhandle); return NULL; } @@ -770,24 +783,30 @@ pack_t *FS_LoadPackPAK (const char *packfile) if (numpackfiles > MAX_FILES_IN_PACK) { - Con_Printf ("%s has %i files", packfile, numpackfiles); + Con_Printf ("%s has %i files\n", packfile, numpackfiles); + close(packhandle); + 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 = Mem_Alloc(fs_mempool, sizeof (pack_t)); + 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 = Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t)); + pack->files = (packfile_t *)Mem_Alloc(fs_mempool, numpackfiles * sizeof(packfile_t)); pack->next = packlist; packlist = pack; - info = 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++) { @@ -803,6 +822,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); +} + /* ================ @@ -816,7 +965,6 @@ void FS_AddGameDirectory (const char *dir) { stringlist_t *list, *current; searchpath_t *search; - pack_t *pak; char pakfile[MAX_OSPATH]; strlcpy (fs_gamedir, dir, sizeof (fs_gamedir)); @@ -826,45 +974,27 @@ void FS_AddGameDirectory (const char *dir) // add any PAK package in the directory for (current = list;current;current = current->next) { - if (matchpattern(current->text, "*.pak", true)) + if (!strcasecmp(FS_FileExtension(current->text), "pak")) { dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text); - pak = FS_LoadPackPAK (pakfile); - if (pak) - { - search = 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); + FS_AddPack_Fullpath(pakfile, NULL, false); } } // add any PK3 package in the director for (current = list;current;current = current->next) { - if (matchpattern(current->text, "*.pk3", true)) + if (!strcasecmp(FS_FileExtension(current->text), "pk3")) { dpsnprintf (pakfile, sizeof (pakfile), "%s%s", dir, current->text); - pak = FS_LoadPackPK3 (pakfile); - if (pak) - { - search = 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); + FS_AddPack_Fullpath(pakfile, NULL, false); } } freedirectory(list); // Add the directory to the search path // (unpacked files have the priority over packed files) - search = Mem_Alloc(fs_mempool, sizeof(searchpath_t)); + search = (searchpath_t *)Mem_Alloc(fs_mempool, sizeof(searchpath_t)); strlcpy (search->filename, dir, sizeof (search->filename)); search->next = fs_searchpaths; fs_searchpaths = search; @@ -883,7 +1013,7 @@ void FS_AddGameHierarchy (const char *dir) #endif // Add the common game directory - FS_AddGameDirectory (va("%s/%s/", fs_basedir, dir)); + FS_AddGameDirectory (va("%s%s/", fs_basedir, dir)); #ifndef WIN32 // Add the personal game directory @@ -905,14 +1035,14 @@ static const char *FS_FileExtension (const char *in) 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; @@ -931,21 +1061,27 @@ void FS_Init (void) fs_mempool = Mem_AllocPool("file management", 0, NULL); - strcpy(fs_basedir, "."); strcpy(fs_gamedir, ""); +// If the base directory is explicitly defined by the compilation process +#ifdef DP_FS_BASEDIR + strcpy(fs_basedir, DP_FS_BASEDIR); +#else + strcpy(fs_basedir, ""); + #ifdef MACOSX // FIXME: is there a better way to find the directory outside the .app? if (strstr(com_argv[0], ".app/")) { char *split; - char temp[4096]; + split = strstr(com_argv[0], ".app/"); while (split > com_argv[0] && *split != '/') split--; strlcpy(fs_basedir, com_argv[0], sizeof(fs_basedir)); fs_basedir[split - com_argv[0]] = 0; } +#endif #endif PK3_OpenLibrary (); @@ -962,6 +1098,10 @@ void FS_Init (void) fs_basedir[i-1] = 0; } + // add a path separator to the end of the basedir if it lacks one + if (fs_basedir[0] && fs_basedir[strlen(fs_basedir) - 1] != '/' && fs_basedir[strlen(fs_basedir) - 1] != '\\') + strlcat(fs_basedir, "/", sizeof(fs_basedir)); + // -path