X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=b5262f636ea30a4a6ae03d181d07cc97df451fa6;hb=e36b2f6c58b8a8c736c10e0133108acd9bc3a630;hp=c83273e4e746a02ed80326333bbbe4f15a5668b6;hpb=c4d580d4e1aba16739caa49c7910b54b06e7ab92;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index c83273e4..b5262f63 100644 --- a/fs.c +++ b/fs.c @@ -66,7 +66,7 @@ # define dup _dup #endif -/* +/** \page fs File System All of Quake's data access is through a hierchal file system, but the contents of the file system can be transparently merged from several sources. @@ -111,8 +111,23 @@ CONSTANTS #define MAX_WBITS 15 #define Z_OK 0 #define Z_STREAM_END 1 +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) #define ZLIB_VERSION "1.2.3" +#define Z_BINARY 0 +#define Z_DEFLATED 8 +#define Z_MEMLEVEL_DEFAULT 8 + +#define Z_NULL 0 +#define Z_DEFAULT_COMPRESSION (-1) +#define Z_NO_FLUSH 0 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 + // 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 @@ -126,62 +141,66 @@ TYPES ============================================================================= */ -// Zlib stream (from zlib.h) -// Warning: some pointers we don't use directly have -// been cast to "void*" for a matter of simplicity +/*! Zlib stream (from zlib.h) + * \warning: some pointers we don't use directly have + * been cast to "void*" for a matter of simplicity + */ typedef struct { - 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 + 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 - 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 + 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 - char *msg; // last error message, NULL if no error - void *state; // not visible by applications + char *msg; ///< last error message, NULL if no error + void *state; ///< not visible by applications - void *zalloc; // used to allocate the internal state - void *zfree; // used to free the internal state - void *opaque; // private data object passed to zalloc and zfree + void *zalloc; ///< used to allocate the internal state + void *zfree; ///< used to free the internal state + void *opaque; ///< private data object passed to zalloc and zfree - int data_type; // best guess about the data type: ascii or binary - unsigned long adler; // adler32 value of the uncompressed data - unsigned long reserved; // reserved for future use + int data_type; ///< best guess about the data type: ascii or binary + unsigned long adler; ///< adler32 value of the uncompressed data + unsigned long reserved; ///< reserved for future use } z_stream; -// inside a package (PAK or PK3) +/// inside a package (PAK or PK3) #define QFILE_FLAG_PACKED (1 << 0) -// file is compressed using the deflate algorithm (PK3 only) +/// file is compressed using the deflate algorithm (PK3 only) #define QFILE_FLAG_DEFLATED (1 << 1) +/// file is actually already loaded data +#define QFILE_FLAG_DATA (1 << 2) #define FILE_BUFF_SIZE 2048 typedef struct { z_stream zstream; - 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 + 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 unsigned char input [FILE_BUFF_SIZE]; } ztoolkit_t; struct qfile_s { 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 - fs_offset_t offset; // offset into the package (0 if external file) - int ungetc; // single stored character from ungetc, cleared to EOF when read + 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 + fs_offset_t offset; ///< offset into the package (0 if external file) + int ungetc; ///< single stored character from ungetc, cleared to EOF when read // Contents buffer - fs_offset_t buff_ind, buff_len; // buffer current index and length + fs_offset_t buff_ind, buff_len; ///< buffer current index and length unsigned char buff [FILE_BUFF_SIZE]; - // For zipped files - ztoolkit_t* ztk; + ztoolkit_t* ztk; ///< For zipped files. + + const unsigned char *data; ///< For data files. }; @@ -193,11 +212,11 @@ typedef struct pk3_endOfCentralDir_s { unsigned int signature; unsigned short disknum; - unsigned short cdir_disknum; // number of the disk with the start of the central directory - unsigned short localentries; // number of entries in the central directory on this disk - unsigned short nbentries; // total number of entries in the central directory on this disk - unsigned int cdir_size; // size of the central directory - unsigned int cdir_offset; // with respect to the starting disk number + unsigned short cdir_disknum; ///< number of the disk with the start of the central directory + unsigned short localentries; ///< number of entries in the central directory on this disk + unsigned short nbentries; ///< total number of entries in the central directory on this disk + unsigned int cdir_size; ///< size of the central directory + unsigned int cdir_offset; ///< with respect to the starting disk number unsigned short comment_size; } pk3_endOfCentralDir_t; @@ -217,12 +236,14 @@ typedef struct dpackheader_s } dpackheader_t; -// Packages in memory -// the offset in packfile_t is the true contents offset +/*! \name Packages in memory + * @{ + */ +/// the offset in packfile_t is the true contents offset #define PACKFILE_FLAG_TRUEOFFS (1 << 0) -// file compressed using the deflate algorithm +/// file compressed using the deflate algorithm #define PACKFILE_FLAG_DEFLATED (1 << 1) -// file is a symbolic link +/// file is a symbolic link #define PACKFILE_FLAG_SYMLINK (1 << 2) typedef struct packfile_s @@ -230,8 +251,8 @@ typedef struct packfile_s char name [MAX_QPATH]; int flags; fs_offset_t offset; - fs_offset_t packsize; // size in the package - fs_offset_t realsize; // real file size (uncompressed) + fs_offset_t packsize; ///< size in the package + fs_offset_t realsize; ///< real file size (uncompressed) } packfile_t; typedef struct pack_s @@ -239,13 +260,13 @@ typedef struct pack_s char filename [MAX_OSPATH]; char shortname [MAX_QPATH]; int handle; - int ignorecase; // PK3 ignores case + int ignorecase; ///< PK3 ignores case int numfiles; packfile_t *files; } pack_t; +//@} - -// Search paths for files (including packages) +/// Search paths for files (including packages) typedef struct searchpath_s { // only one of filename / pack will be used @@ -265,6 +286,7 @@ FUNCTION PROTOTYPES void FS_Dir_f(void); void FS_Ls_f(void); +void FS_Which_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, @@ -293,7 +315,7 @@ char fs_basedir[MAX_OSPATH]; 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 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; the date is encoded using strftime escapes)"}; 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"}; @@ -316,9 +338,16 @@ static int (ZEXPORT *qz_inflate) (z_stream* strm, int flush); static int (ZEXPORT *qz_inflateEnd) (z_stream* strm); static int (ZEXPORT *qz_inflateInit2_) (z_stream* strm, int windowBits, const char *version, int stream_size); static int (ZEXPORT *qz_inflateReset) (z_stream* strm); +static int (ZEXPORT *qz_deflateInit2_) (z_stream* strm, int level, int method, int windowBits, int memLevel, int strategy, const char *version, int stream_size); +static int (ZEXPORT *qz_deflateEnd) (z_stream* strm); +static int (ZEXPORT *qz_deflate) (z_stream* strm, int flush); #define qz_inflateInit2(strm, windowBits) \ qz_inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define qz_deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + qz_deflateInit2_((strm), (level), (method), (windowBits), (memLevel), (strategy), ZLIB_VERSION, sizeof(z_stream)) + +// qz_deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) static dllfunction_t zlibfuncs[] = { @@ -326,10 +355,13 @@ static dllfunction_t zlibfuncs[] = {"inflateEnd", (void **) &qz_inflateEnd}, {"inflateInit2_", (void **) &qz_inflateInit2_}, {"inflateReset", (void **) &qz_inflateReset}, + {"deflateInit2_", (void **) &qz_deflateInit2_}, + {"deflateEnd", (void **) &qz_deflateEnd}, + {"deflate", (void **) &qz_deflate}, {NULL, NULL} }; -// Handle for Zlib DLL +/// Handle for Zlib DLL static dllhandle_t zlib_dll = NULL; #ifdef WIN32 @@ -392,6 +424,18 @@ qboolean PK3_OpenLibrary (void) return Sys_LoadLibrary (dllnames, &zlib_dll, zlibfuncs); } +/* +==================== +FS_HasZlib + +See if zlib is available +==================== +*/ +qboolean FS_HasZlib(void) +{ + PK3_OpenLibrary(); // to be safe + return (zlib_dll != 0); +} /* ==================== @@ -472,7 +516,7 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) // Load the central directory in memory central_dir = (unsigned char *)Mem_Alloc (tempmempool, eocd->cdir_size); lseek (pack->handle, eocd->cdir_offset, SEEK_SET); - if(read (pack->handle, central_dir, eocd->cdir_size) != eocd->cdir_size) + if(read (pack->handle, central_dir, eocd->cdir_size) != (ssize_t) eocd->cdir_size) { Mem_Free (central_dir); return -1; @@ -790,13 +834,12 @@ void FS_Path_f (void) /* ================= FS_LoadPackPAK - -Takes an explicit (not game tree related) path to a pak file. - -Loads the header and directory, adding the files at the beginning -of the list so they override previous pack files. ================= */ +/*! Takes an explicit (not game tree related) path to a pak file. + *Loads the header and directory, adding the files at the beginning + *of the list so they override previous pack files. + */ pack_t *FS_LoadPackPAK (const char *packfile) { dpackheader_t header; @@ -878,17 +921,18 @@ pack_t *FS_LoadPackPAK (const char *packfile) /* ================ 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. ================ */ +/*! 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, const char *shortname, qboolean *already_loaded, qboolean keep_plain_dirs) { searchpath_t *search; @@ -974,17 +1018,17 @@ static qboolean FS_AddPack_Fullpath(const char *pakfile, const char *shortname, /* ================ 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. ================ */ +/*! 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]; @@ -1525,6 +1569,7 @@ void FS_Init_Commands(void) 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"); + Cmd_AddCommand ("which", FS_Which_f, "accepts a file name as argument and reports where the file is taken from"); } /* @@ -2036,6 +2081,25 @@ qfile_t* FS_OpenVirtualFile (const char* filepath, qboolean quiet) } +/* +==================== +FS_FileFromData + +Open a file. The syntax is the same as fopen +==================== +*/ +qfile_t* FS_FileFromData (const unsigned char *data, const size_t size, qboolean quiet) +{ + qfile_t* file; + file = (qfile_t *)Mem_Alloc (fs_mempool, sizeof (*file)); + memset (file, 0, sizeof (*file)); + file->flags = QFILE_FLAG_DATA; + file->ungetc = EOF; + file->real_length = size; + file->data = data; + return file; +} + /* ==================== FS_Close @@ -2045,6 +2109,12 @@ Close a file */ int FS_Close (qfile_t* file) { + if(file->flags & QFILE_FLAG_DATA) + { + Mem_Free(file); + return 0; + } + if (close (file->handle)) return EOF; @@ -2115,6 +2185,16 @@ fs_offset_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) else done = 0; + if(file->flags & QFILE_FLAG_DATA) + { + size_t left = file->real_length - file->position; + if(buffersize > left) + buffersize = left; + memcpy(buffer, file->data + file->position, buffersize); + file->position += buffersize; + return buffersize; + } + // First, we copy as many bytes as we can from "buff" if (file->buff_ind < file->buff_len) { @@ -2396,6 +2476,12 @@ int FS_Seek (qfile_t* file, fs_offset_t offset, int whence) if (offset < 0 || offset > file->real_length) return -1; + if(file->flags & QFILE_FLAG_DATA) + { + file->position = offset; + return 0; + } + // If we have the data in our read buffer, we don't need to actually seek if (file->position - file->buff_len <= offset && offset <= file->position) { @@ -2517,6 +2603,13 @@ unsigned char *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet, f if (file) { filesize = file->real_length; + if(filesize < 0) + { + Con_Printf("FS_LoadFile(\"%s\", pool, %s, filesizepointer): trying to open a non-regular file\n", path, quiet ? "true" : "false"); + FS_Close(file); + return NULL; + } + buf = (unsigned char *)Mem_Alloc (pool, filesize + 1); buf[filesize] = '\0'; FS_Read (file, buf, filesize); @@ -3011,6 +3104,29 @@ void FS_Ls_f(void) FS_ListDirectoryCmd("ls", false); } +void FS_Which_f(void) +{ + const char *filename; + int index; + searchpath_t *sp; + if (Cmd_Argc() != 2) + { + Con_Printf("usage:\n%s \n", Cmd_Argv(0)); + return; + } + filename = Cmd_Argv(1); + sp = FS_FindFile(filename, &index, true); + if (!sp) { + Con_Printf("%s isn't anywhere\n", filename); + return; + } + if (sp->pack) + Con_Printf("%s is in package %s\n", filename, sp->pack->shortname); + else + Con_Printf("%s is file %s%s\n", filename, sp->filename, filename); +} + + const char *FS_WhichPack(const char *filename) { int index; @@ -3095,3 +3211,181 @@ int FS_CRCFile(const char *filename, size_t *filesizepointer) return crc; } +unsigned char *FS_Deflate(const unsigned char *data, size_t size, size_t *deflated_size, int level, mempool_t *mempool) +{ + z_stream strm; + unsigned char *out = NULL; + unsigned char *tmp; + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + if(level < 0) + level = Z_DEFAULT_COMPRESSION; + + if(qz_deflateInit2(&strm, level, Z_DEFLATED, -MAX_WBITS, Z_MEMLEVEL_DEFAULT, Z_BINARY) != Z_OK) + { + Con_Printf("FS_Deflate: deflate init error!\n"); + return NULL; + } + + strm.next_in = (unsigned char*)data; + strm.avail_in = size; + + tmp = (unsigned char *) Mem_Alloc(tempmempool, size); + if(!tmp) + { + Con_Printf("FS_Deflate: not enough memory in tempmempool!\n"); + qz_deflateEnd(&strm); + return NULL; + } + + strm.next_out = tmp; + strm.avail_out = size; + + if(qz_deflate(&strm, Z_FINISH) != Z_STREAM_END) + { + Con_Printf("FS_Deflate: deflate failed!\n"); + qz_deflateEnd(&strm); + Mem_Free(tmp); + return NULL; + } + + if(qz_deflateEnd(&strm) != Z_OK) + { + Con_Printf("FS_Deflate: deflateEnd failed\n"); + Mem_Free(tmp); + return NULL; + } + + if(strm.total_out >= size) + { + Con_Printf("FS_Deflate: deflate is useless on this data!\n"); + Mem_Free(tmp); + return NULL; + } + + out = (unsigned char *) Mem_Alloc(mempool, strm.total_out); + if(!out) + { + Con_Printf("FS_Deflate: not enough memory in target mempool!\n"); + Mem_Free(tmp); + return NULL; + } + + if(deflated_size) + *deflated_size = (size_t)strm.total_out; + + memcpy(out, tmp, strm.total_out); + Mem_Free(tmp); + + return out; +} + +static void AssertBufsize(sizebuf_t *buf, int length) +{ + if(buf->cursize + length > buf->maxsize) + { + int oldsize = buf->maxsize; + unsigned char *olddata; + olddata = buf->data; + buf->maxsize += length; + buf->data = (unsigned char *) Mem_Alloc(tempmempool, buf->maxsize); + if(olddata) + { + memcpy(buf->data, olddata, oldsize); + Mem_Free(olddata); + } + } +} + +unsigned char *FS_Inflate(const unsigned char *data, size_t size, size_t *inflated_size, mempool_t *mempool) +{ + int ret; + z_stream strm; + unsigned char *out = NULL; + unsigned char tmp[2048]; + unsigned int have; + sizebuf_t outbuf; + + memset(&outbuf, 0, sizeof(outbuf)); + outbuf.data = (unsigned char *) Mem_Alloc(tempmempool, sizeof(tmp)); + outbuf.maxsize = sizeof(tmp); + + memset(&strm, 0, sizeof(strm)); + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + if(qz_inflateInit2(&strm, -MAX_WBITS) != Z_OK) + { + Con_Printf("FS_Inflate: inflate init error!\n"); + Mem_Free(outbuf.data); + return NULL; + } + + strm.next_in = (unsigned char*)data; + strm.avail_in = size; + + do + { + strm.next_out = tmp; + strm.avail_out = sizeof(tmp); + ret = qz_inflate(&strm, Z_NO_FLUSH); + // it either returns Z_OK on progress, Z_STREAM_END on end + // or an error code + switch(ret) + { + case Z_STREAM_END: + case Z_OK: + break; + + case Z_STREAM_ERROR: + Con_Print("FS_Inflate: stream error!\n"); + break; + case Z_DATA_ERROR: + Con_Print("FS_Inflate: data error!\n"); + break; + case Z_MEM_ERROR: + Con_Print("FS_Inflate: mem error!\n"); + break; + case Z_BUF_ERROR: + Con_Print("FS_Inflate: buf error!\n"); + break; + default: + Con_Print("FS_Inflate: unknown error!\n"); + break; + + } + if(ret != Z_OK && ret != Z_STREAM_END) + { + Con_Printf("Error after inflating %u bytes\n", (unsigned)strm.total_in); + Mem_Free(outbuf.data); + qz_inflateEnd(&strm); + return NULL; + } + have = sizeof(tmp) - strm.avail_out; + AssertBufsize(&outbuf, max(have, sizeof(tmp))); + SZ_Write(&outbuf, tmp, have); + } while(ret != Z_STREAM_END); + + qz_inflateEnd(&strm); + + out = (unsigned char *) Mem_Alloc(mempool, outbuf.cursize); + if(!out) + { + Con_Printf("FS_Inflate: not enough memory in target mempool!\n"); + Mem_Free(outbuf.data); + return NULL; + } + + memcpy(out, outbuf.data, outbuf.cursize); + Mem_Free(outbuf.data); + + if(inflated_size) + *inflated_size = (size_t)outbuf.cursize; + + return out; +}