X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;f=fs.c;h=9eb46843f796fff86a8c3d1cf32e9bfdb3d78c97;hb=f3e79d752a76a9d6329759a83ec9800a5e4cc92b;hp=8518c93db5045f6ac99260ba88624e016587fb17;hpb=f4d146d977657c9569797e278df183234509553b;p=xonotic%2Fdarkplaces.git diff --git a/fs.c b/fs.c index 8518c93d..9eb46843 100644 --- a/fs.c +++ b/fs.c @@ -25,8 +25,6 @@ #include "quakedef.h" -#include -#include #include #include @@ -231,6 +229,22 @@ typedef struct searchpath_s } searchpath_t; +/* +============================================================================= + +FUNCTION PROTOTYPES + +============================================================================= +*/ + +void FS_Dir_f(void); +void FS_Ls_f(void); + +static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, + size_t offset, size_t packsize, + size_t realsize, file_flags_t flags); + + /* ============================================================================= @@ -246,7 +260,7 @@ int fs_filesize; pack_t *packlist = NULL; -searchpath_t *fs_searchpaths; +searchpath_t *fs_searchpaths = NULL; #define MAX_FILES_IN_PACK 65536 @@ -301,11 +315,7 @@ Unload the Zlib DLL */ void PK3_CloseLibrary (void) { - if (!zlib_dll) - return; - - Sys_UnloadLibrary (zlib_dll); - zlib_dll = NULL; + Sys_UnloadLibrary (&zlib_dll); } @@ -319,7 +329,6 @@ Try to load the Zlib DLL qboolean PK3_OpenLibrary (void) { const char* dllname; - const dllfunction_t *func; // Already loaded? if (zlib_dll) @@ -328,30 +337,17 @@ qboolean PK3_OpenLibrary (void) #ifdef WIN32 dllname = "zlib.dll"; #else - dllname = "libz.so.1"; + dllname = "libz.so"; #endif - // Initializations - for (func = zlibfuncs; func && func->name != NULL; func++) - *func->funcvariable = NULL; - // Load the DLL - if (! (zlib_dll = Sys_LoadLibrary (dllname))) + if (! Sys_LoadLibrary (dllname, &zlib_dll, zlibfuncs)) { - Con_Printf("Can't find %s. Compressed files support disabled\n", dllname); + Con_Printf ("Compressed files support disabled\n"); return false; } - // Get the function adresses - for (func = zlibfuncs; func && func->name != NULL; func++) - if (!(*func->funcvariable = (void *) Sys_GetProcAddress (zlib_dll, func->name))) - { - Con_Printf("missing function \"%s\" - broken Zlib library!\n", func->name); - PK3_CloseLibrary (); - return false; - } - - Con_Printf("%s loaded. Compressed files support enabled\n", dllname); + Con_Printf ("Compressed files support enabled\n"); return true; } @@ -447,7 +443,6 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) for (ind = 0; ind < eocd->nbentries; ind++) { size_t namesize, count; - packfile_t *file; // Checking the remaining size if (remaining < ZIP_CDIR_CHUNK_BASE_SIZE) @@ -483,19 +478,24 @@ int PK3_BuildFileList (pack_t *pack, const pk3_endOfCentralDir_t *eocd) // WinZip doesn't use the "directory" attribute, so we need to check the name directly if (ptr[ZIP_CDIR_CHUNK_BASE_SIZE + namesize - 1] != '/') { - // Extract the name - file = &pack->files[pack->numfiles]; - memcpy (file->name, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize); - file->name[namesize] = '\0'; + char filename [sizeof (pack->files[0].name)]; + size_t offset, packsize, realsize; + file_flags_t flags; - // Compression, sizes and offset - if (BuffLittleShort (&ptr[10])) - file->flags = FILE_FLAG_DEFLATED; - file->packsize = BuffLittleLong (&ptr[20]); - file->realsize = BuffLittleLong (&ptr[24]); - file->offset = BuffLittleLong (&ptr[42]); + // Extract the name (strip it if necessary) + if (namesize >= sizeof (filename)) + namesize = sizeof (filename) - 1; + memcpy (filename, &ptr[ZIP_CDIR_CHUNK_BASE_SIZE], namesize); + filename[namesize] = '\0'; - pack->numfiles++; + if (BuffLittleShort (&ptr[10])) + flags = FILE_FLAG_DEFLATED; + else + flags = 0; + offset = BuffLittleLong (&ptr[42]); + packsize = BuffLittleLong (&ptr[20]); + realsize = BuffLittleLong (&ptr[24]); + FS_AddFileToPack (filename, pack, offset, packsize, realsize, flags); } } @@ -559,7 +559,7 @@ pack_t *FS_LoadPackPK3 (const char *packfile) if (real_nb_files <= 0) Sys_Error ("%s is not a valid PK3 file", packfile); - Con_Printf ("Added packfile %s (%i files)\n", packfile, real_nb_files); + Con_Printf("Added packfile %s (%i files)\n", packfile, real_nb_files); return pack; } @@ -602,11 +602,64 @@ OTHER PRIVATE FUNCTIONS */ +/* +==================== +FS_AddFileToPack + +Add a file to the list of files contained into a package +==================== +*/ +static packfile_t* FS_AddFileToPack (const char* name, pack_t* pack, + size_t offset, size_t packsize, + size_t realsize, file_flags_t flags) +{ + int (*strcmp_funct) (const char* str1, const char* str2); + size_t left, right, middle; + int diff; + packfile_t *file; + + strcmp_funct = pack->ignorecase ? strcasecmp : strcmp; + + // Look for the slot we should put that file into (binary search) + left = 0; + right = pack->numfiles; + while (left != right) + { + middle = (left + right - 1) / 2; + diff = strcmp_funct (pack->files[middle].name, name); + + // If we found the file, there's a problem + if (!diff) + Sys_Error ("Package %s contains several time the file %s\n", + pack->filename, name); + + // If we're too far in the list + if (diff > 0) + right = middle; + else + left = middle + 1; + } + + // We have to move the right of the list by one slot to free the one we need + file = &pack->files[left]; + memmove (file + 1, file, (pack->numfiles - left) * sizeof (*file)); + pack->numfiles++; + + strlcpy (file->name, name, sizeof (file->name)); + file->offset = offset; + file->packsize = packsize; + file->realsize = realsize; + file->flags = flags; + + return file; +} + + /* ============ FS_CreatePath -Only used for FS_WriteFile. +Only used for FS_Open. ============ */ void FS_CreatePath (char *path) @@ -637,15 +690,15 @@ void FS_Path_f (void) { searchpath_t *s; - Con_Printf ("Current search path:\n"); + Con_Print("Current search path:\n"); for (s=fs_searchpaths ; s ; s=s->next) { if (s->pack) { - Con_Printf ("%s (%i files)\n", s->pack->filename, s->pack->numfiles); + Con_Printf("%s (%i files)\n", s->pack->filename, s->pack->numfiles); } else - Con_Printf ("%s\n", s->filename); + Con_Printf("%s\n", s->filename); } } @@ -690,7 +743,7 @@ pack_t *FS_LoadPackPAK (const char *packfile) pack->ignorecase = false; // PAK is case sensitive strlcpy (pack->filename, packfile, sizeof (pack->filename)); pack->handle = packhandle; - pack->numfiles = numpackfiles; + pack->numfiles = 0; pack->mempool = Mem_AllocPool(packfile); pack->files = Mem_Alloc(pack->mempool, numpackfiles * sizeof(packfile_t)); pack->next = packlist; @@ -703,20 +756,15 @@ pack_t *FS_LoadPackPAK (const char *packfile) // parse the directory for (i = 0;i < numpackfiles;i++) { - size_t size; - packfile_t *file = &pack->files[i]; - - strlcpy (file->name, info[i].name, sizeof (file->name)); - file->offset = LittleLong(info[i].filepos); - size = LittleLong (info[i].filelen); - file->packsize = size; - file->realsize = size; - file->flags = FILE_FLAG_TRUEOFFS; + size_t offset = LittleLong (info[i].filepos); + size_t size = LittleLong (info[i].filelen); + + FS_AddFileToPack (info[i].name, pack, offset, size, size, FILE_FLAG_TRUEOFFS); } Mem_Free(info); - Con_Printf ("Added packfile %s (%i files)\n", packfile, numpackfiles); + Con_Printf("Added packfile %s (%i files)\n", packfile, numpackfiles); return pack; } @@ -738,11 +786,13 @@ void FS_AddGameDirectory (char *dir) strlcpy (fs_gamedir, dir, sizeof (fs_gamedir)); +#ifndef AKVERSION // add the directory to the search path search = Mem_Alloc(pak_mempool, sizeof(searchpath_t)); strlcpy (search->filename, dir, sizeof (search->filename)); search->next = fs_searchpaths; fs_searchpaths = search; +#endif list = listdirectory(dir); @@ -784,6 +834,15 @@ void FS_AddGameDirectory (char *dir) } } freedirectory(list); + +// Unpacked files have the priority over packed files if AKVERSION is defined +#ifdef AKVERSION + // add the directory to the search path + search = Mem_Alloc(pak_mempool, sizeof(searchpath_t)); + strlcpy (search->filename, dir, sizeof (search->filename)); + search->next = fs_searchpaths; + fs_searchpaths = search; +#endif } @@ -807,7 +866,7 @@ char *FS_FileExtension (const char *in) separator = backslash; if (separator < colon) separator = colon; - if (dot < separator) + if (dot == NULL || dot < separator) return ""; dot++; for (i = 0;i < 7 && dot[i];i++) @@ -830,9 +889,14 @@ void FS_Init (void) fs_mempool = Mem_AllocPool("file management"); pak_mempool = Mem_AllocPool("paks"); + Cvar_RegisterVariable (&scr_screenshot_name); + Cmd_AddCommand ("path", FS_Path_f); + Cmd_AddCommand ("dir", FS_Dir_f); + Cmd_AddCommand ("ls", FS_Ls_f); strcpy(fs_basedir, "."); + strcpy(fs_gamedir, "."); PK3_OpenLibrary (); @@ -840,30 +904,11 @@ void FS_Init (void) // Overrides the system supplied base directory (under GAMENAME) i = COM_CheckParm ("-basedir"); if (i && i < com_argc-1) - strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir)); - - i = strlen (fs_basedir); - if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/')) - fs_basedir[i-1] = 0; - - // start up with GAMENAME by default (id1) - strlcpy (com_modname, GAMENAME, sizeof (com_modname)); - FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir)); - if (gamedirname[0]) { - fs_modified = true; - strlcpy (com_modname, gamedirname, sizeof (com_modname)); - FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname)); - } - - // -game - // Adds basedir/gamedir as an override game - i = COM_CheckParm ("-game"); - if (i && i < com_argc-1) - { - fs_modified = true; - strlcpy (com_modname, com_argv[i+1], sizeof (com_modname)); - FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i+1])); + strlcpy (fs_basedir, com_argv[i+1], sizeof (fs_basedir)); + i = strlen (fs_basedir); + if (i > 0 && (fs_basedir[i-1] == '\\' || fs_basedir[i-1] == '/')) + fs_basedir[i-1] = 0; } // -path [] ... @@ -872,7 +917,6 @@ void FS_Init (void) if (i) { fs_modified = true; - fs_searchpaths = NULL; while (++i < com_argc) { if (!com_argv[i] || com_argv[i][0] == '+' || com_argv[i][0] == '-') @@ -896,6 +940,37 @@ void FS_Init (void) search->next = fs_searchpaths; fs_searchpaths = search; } + return; + } + + // start up with GAMENAME by default (id1) + strlcpy (com_modname, GAMENAME, sizeof (com_modname)); + FS_AddGameDirectory (va("%s/"GAMENAME, fs_basedir)); + Cvar_SetQuick (&scr_screenshot_name, gamescreenshotname); + + // add the game-specific path, if any + if (gamedirname[0]) + { + fs_modified = true; + strlcpy (com_modname, gamedirname, sizeof (com_modname)); + FS_AddGameDirectory (va("%s/%s", fs_basedir, gamedirname)); + } + + // -game + // Adds basedir/gamedir as an override game + // LordHavoc: now supports multiple -game directories + for (i = 1;i < com_argc;i++) + { + if (!com_argv[i]) + continue; + if (!strcmp (com_argv[i], "-game") && i < com_argc-1) + { + i++; + fs_modified = true; + strlcpy (com_modname, com_argv[i], sizeof (com_modname)); + FS_AddGameDirectory (va("%s/%s", fs_basedir, com_argv[i])); + Cvar_SetQuick (&scr_screenshot_name, com_modname); + } } } @@ -963,213 +1038,177 @@ qfile_t *FS_OpenRead (const char *path, int offs, int len) return file; } + /* -=========== -FS_FOpenFile +==================== +FS_FindFile -If the requested file is inside a packfile, a new qfile_t* will be opened -into the file. +Look for a file in the packages and in the filesystem -Sets fs_filesize -=========== +Return the searchpath where the file was found (or NULL) +and the file index in the package if relevant +==================== */ -qfile_t *FS_FOpenFile (const char *filename, qboolean quiet) +static searchpath_t *FS_FindFile (const char *name, int* index, qboolean quiet) { searchpath_t *search; - char netpath[MAX_OSPATH]; pack_t *pak; - int i, filenamelen, matched; + int (*strcmp_funct) (const char* str1, const char* str2); - filenamelen = strlen (filename); - -#ifdef AKVERSION // search through the path, one element at a time - search = fs_searchpaths; - - for( ; search ; search = search->next) - if(!search->pack) - { - snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename); - - if (!FS_SysFileExists (netpath)) - continue; - - if (!quiet) - Sys_Printf ("FindFile: %s\n",netpath); - return FS_OpenRead (netpath, -1, -1); - } - - search = fs_searchpaths; - for ( ; search ; search = search->next) + for (search = fs_searchpaths;search;search = search->next) + { // is the element a pak file? if (search->pack) { - // look through all the pak file elements + size_t left, right, middle; + pak = search->pack; - for (i=0 ; inumfiles ; i++) - { - if (pak->ignorecase) - matched = !strcasecmp (pak->files[i].name, filename); - else - matched = !strcmp (pak->files[i].name, filename); - if (matched) // found it? - { - qfile_t *file; + strcmp_funct = pak->ignorecase ? strcasecmp : strcmp; - if (!quiet) - Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name); + // Look for the file (binary search) + left = 0; + right = pak->numfiles; + while (left != right) + { + int diff; - // If we don't have the true offset, get it now - if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS)) - PK3_GetTrueFileOffset (&pak->files[i], pak); + middle = (left + right - 1) / 2; + diff = strcmp_funct (pak->files[middle].name, name); - // No Zlib DLL = no compressed files - if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED)) - { - Con_Printf ("WARNING: can't open the compressed file %s\n" - "You need the Zlib DLL to use compressed files\n", filename); - fs_filesize = -1; - return NULL; - } + // Found it + if (!diff) + { + if (!quiet) + Sys_Printf("FS_FindFile: %s in %s\n", + pak->files[middle].name, pak->filename); - // open a new file in the pakfile - file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize); - fs_filesize = pak->files[i].realsize; + if (index != NULL) + *index = middle; + return search; + } - if (pak->files[i].flags & FILE_FLAG_DEFLATED) - { - ztoolkit_t *ztk; + // If we're too far in the list + if (diff > 0) + right = middle; + else + left = middle + 1; + } + } + else + { + char netpath[MAX_OSPATH]; + snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, name); + if (FS_SysFileExists (netpath)) + { + if (!quiet) + Sys_Printf("FS_FindFile: %s\n", netpath); - file->flags |= FS_FLAG_DEFLATED; + if (index != NULL) + *index = -1; + return search; + } + } + } - // We need some more variables - ztk = Mem_Alloc (fs_mempool, sizeof (*file->z)); + if (!quiet) + Sys_Printf("FS_FindFile: can't find %s\n", name); - ztk->real_length = pak->files[i].realsize; + if (index != NULL) + *index = -1; + return NULL; +} - // Initialize zlib stream - ztk->zstream.next_in = ztk->input; - ztk->zstream.avail_in = 0; - /* From Zlib's "unzip.c": - * - * windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK) - Sys_Error ("inflate init error (file: %s)", filename); +/* +=========== +FS_FOpenFile - ztk->zstream.next_out = ztk->output; - ztk->zstream.avail_out = sizeof (ztk->output); +If the requested file is inside a packfile, a new qfile_t* will be opened +into the file. - file->z = ztk; - } +Sets fs_filesize +=========== +*/ +qfile_t *FS_FOpenFile (const char *filename, qboolean quiet) +{ + searchpath_t *search; + packfile_t *packfile; + int i; + qfile_t *file; - return file; - } - } - } -#else - // search through the path, one element at a time - search = fs_searchpaths; + search = FS_FindFile (filename, &i, quiet); - for ( ; search ; search = search->next) + // Not found? + if (search == NULL) { - // is the element a pak file? - if (search->pack) - { - // look through all the pak file elements - pak = search->pack; - for (i=0 ; inumfiles ; i++) - { - if (pak->ignorecase) - matched = !strcasecmp (pak->files[i].name, filename); - else - matched = !strcmp (pak->files[i].name, filename); - if (matched) // found it? - { - qfile_t *file; - - if (!quiet) - Sys_Printf ("PackFile: %s : %s\n",pak->filename, pak->files[i].name); - - // If we don't have the true offset, get it now - if (! (pak->files[i].flags & FILE_FLAG_TRUEOFFS)) - PK3_GetTrueFileOffset (&pak->files[i], pak); + fs_filesize = -1; + return NULL; + } - // No Zlib DLL = no compressed files - if (!zlib_dll && (pak->files[i].flags & FILE_FLAG_DEFLATED)) - { - Con_Printf ("WARNING: can't open the compressed file %s\n" - "You need the Zlib DLL to use compressed files\n", filename); - fs_filesize = -1; - return NULL; - } + // Found in the filesystem? + if (i < 0) + { + char netpath[MAX_OSPATH]; + snprintf(netpath, sizeof(netpath), "%s/%s", search->filename, filename); + return FS_OpenRead(netpath, -1, -1); + } - // open a new file in the pakfile - file = FS_OpenRead (pak->filename, pak->files[i].offset, pak->files[i].packsize); - fs_filesize = pak->files[i].realsize; + // So, we found it in a package... + packfile = &search->pack->files[i]; - if (pak->files[i].flags & FILE_FLAG_DEFLATED) - { - ztoolkit_t *ztk; + // If we don't have the true offset, get it now + if (! (packfile->flags & FILE_FLAG_TRUEOFFS)) + PK3_GetTrueFileOffset (packfile, search->pack); - file->flags |= FS_FLAG_DEFLATED; + // No Zlib DLL = no compressed files + if (!zlib_dll && (packfile->flags & FILE_FLAG_DEFLATED)) + { + Con_Printf("WARNING: can't open the compressed file %s\n" + "You need the Zlib DLL to use compressed files\n", + filename); + fs_filesize = -1; + return NULL; + } - // We need some more variables - ztk = Mem_Alloc (fs_mempool, sizeof (*file->z)); + // open a new file in the pakfile + file = FS_OpenRead (search->pack->filename, packfile->offset, packfile->packsize); + fs_filesize = packfile->realsize; - ztk->real_length = pak->files[i].realsize; + if (packfile->flags & FILE_FLAG_DEFLATED) + { + ztoolkit_t *ztk; - // Initialize zlib stream - ztk->zstream.next_in = ztk->input; - ztk->zstream.avail_in = 0; + file->flags |= FS_FLAG_DEFLATED; - /* From Zlib's "unzip.c": - * - * windowBits is passed < 0 to tell that there is no zlib header. - * Note that in this case inflate *requires* an extra "dummy" byte - * after the compressed stream in order to complete decompression and - * return Z_STREAM_END. - * In unzip, i don't wait absolutely Z_STREAM_END because I known the - * size of both compressed and uncompressed data - */ - if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK) - Sys_Error ("inflate init error (file: %s)", filename); + // We need some more variables + ztk = Mem_Alloc (fs_mempool, sizeof (*file->z)); - ztk->zstream.next_out = ztk->output; - ztk->zstream.avail_out = sizeof (ztk->output); + ztk->real_length = packfile->realsize; - file->z = ztk; - } + // Initialize zlib stream + ztk->zstream.next_in = ztk->input; + ztk->zstream.avail_in = 0; - return file; - } - } - } - else - { - snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename); + /* From Zlib's "unzip.c": + * + * windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. + * In unzip, i don't wait absolutely Z_STREAM_END because I known the + * size of both compressed and uncompressed data + */ + if (qz_inflateInit2 (&ztk->zstream, -MAX_WBITS) != Z_OK) + Sys_Error ("inflate init error (file: %s)", filename); - if (!FS_SysFileExists (netpath)) - continue; + ztk->zstream.next_out = ztk->output; + ztk->zstream.avail_out = sizeof (ztk->output); - if (!quiet) - Sys_Printf ("FindFile: %s\n",netpath); - return FS_OpenRead (netpath, -1, -1); - } + file->z = ztk; } -#endif - - if (!quiet) - Sys_Printf ("FindFile: can't find %s\n", filename); - fs_filesize = -1; - return NULL; + return file; } @@ -1299,12 +1338,13 @@ size_t FS_Read (qfile_t* file, void* buffer, size_t buffersize) // If "input" is also empty, we need to fill it if (ztk->in_ind == ztk->in_max) { - size_t remain = file->length - ztk->in_position; + size_t remain; // If we are at the end of the file - if (!remain) + if (ztk->out_position == ztk->real_length) return nb; + remain = file->length - ztk->in_position; count = (remain > sizeof (ztk->input)) ? sizeof (ztk->input) : remain; fread (ztk->input, 1, count, file->stream); @@ -1385,6 +1425,18 @@ int FS_Flush (qfile_t* file) } +/* +==================== +FS_Print + +Print a string into a file +==================== +*/ +int FS_Print(qfile_t* file, const char *msg) +{ + return FS_Write(file, msg, strlen(msg)); +} + /* ==================== FS_Printf @@ -1392,7 +1444,7 @@ FS_Printf Print a string into a file ==================== */ -int FS_Printf (qfile_t* file, const char* format, ...) +int FS_Printf(qfile_t* file, const char* format, ...) { int result; va_list args; @@ -1405,6 +1457,19 @@ int FS_Printf (qfile_t* file, const char* format, ...) } +/* +==================== +FS_VPrintf + +Print a string into a file +==================== +*/ +int FS_VPrintf(qfile_t* file, const char* format, va_list ap) +{ + return vfprintf (file->stream, format, ap); +} + + /* ==================== FS_Getc @@ -1661,7 +1726,7 @@ Filename are relative to the quake directory. Always appends a 0 byte. ============ */ -qbyte *FS_LoadFile (const char *path, qboolean quiet) +qbyte *FS_LoadFile (const char *path, mempool_t *pool, qboolean quiet) { qfile_t *h; qbyte *buf; @@ -1671,7 +1736,7 @@ qbyte *FS_LoadFile (const char *path, qboolean quiet) if (!h) return NULL; - buf = Mem_Alloc(tempmempool, fs_filesize+1); + buf = Mem_Alloc(pool, fs_filesize+1); if (!buf) Sys_Error ("FS_LoadFile: not enough available memory for %s (size %i)", path, fs_filesize); @@ -1693,24 +1758,18 @@ The filename will be prefixed by the current game directory */ qboolean FS_WriteFile (const char *filename, void *data, int len) { - FILE *handle; - char name[MAX_OSPATH]; + qfile_t *handle; - snprintf (name, sizeof (name), "%s/%s", fs_gamedir, filename); - - // Create directories up to the file - FS_CreatePath (name); - - handle = fopen (name, "wb"); + handle = FS_Open (filename, "wb", false); if (!handle) { - Con_Printf ("FS_WriteFile: failed on %s\n", name); + Con_Printf("FS_WriteFile: failed on %s\n", filename); return false; } - Con_DPrintf ("FS_WriteFile: %s\n", name); - fwrite (data, 1, len, handle); - fclose (handle); + Con_DPrintf("FS_WriteFile: %s\n", filename); + FS_Write (handle, data, len); + FS_Close (handle); return true; } @@ -1775,34 +1834,26 @@ void FS_DefaultExtension (char *path, const char *extension, size_t size_path) } +/* +================== +FS_FileExists + +Look for a file in the packages and in the filesystem +================== +*/ qboolean FS_FileExists (const char *filename) { - searchpath_t *search; - char netpath[MAX_OSPATH]; - pack_t *pak; - int i; - - for (search = fs_searchpaths;search;search = search->next) - { - if (search->pack) - { - pak = search->pack; - for (i = 0;i < pak->numfiles;i++) - if (!strcmp (pak->files[i].name, filename)) - return true; - } - else - { - snprintf (netpath, sizeof (netpath), "%s/%s",search->filename, filename); - if (FS_SysFileExists (netpath)) - return true; - } - } - - return false; + return (FS_FindFile (filename, NULL, true) != NULL); } +/* +================== +FS_SysFileExists + +Look for a file in the filesystem only +================== +*/ qboolean FS_SysFileExists (const char *path) { #if WIN32 @@ -1834,3 +1885,251 @@ void FS_mkdir (const char *path) mkdir (path, 0777); #endif } + +/* +=========== +FS_Search + +Allocate and fill a search structure with information on matching filenames. +=========== +*/ +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; + const char *slash, *backslash, *colon, *separator; + char *basepath; + char netpath[MAX_OSPATH]; + char temp[MAX_OSPATH]; + + while(!strncmp(pattern, "./", 2)) + pattern += 2; + while(!strncmp(pattern, ".\\", 2)) + pattern += 2; + + search = NULL; + liststart = NULL; + listcurrent = NULL; + listtemp = NULL; + slash = strrchr(pattern, '/'); + backslash = strrchr(pattern, '\\'); + colon = strrchr(pattern, ':'); + separator = pattern; + if (separator < slash) + separator = slash; + if (separator < backslash) + separator = backslash; + if (separator < colon) + separator = colon; + basepathlength = separator - pattern; + basepath = Mem_Alloc (tempmempool, basepathlength + 1); + if (basepathlength) + memcpy(basepath, pattern, basepathlength); + basepath[basepathlength] = 0; + + // search through the path, one element at a time + for (searchpath = fs_searchpaths;searchpath;searchpath = searchpath->next) + { + // is the element a pak file? + if (searchpath->pack) + { + // look through all the pak file elements + pak = searchpath->pack; + for (i = 0;i < pak->numfiles;i++) + { + strcpy(temp, pak->files[i].name); + while (temp[0]) + { + if (matchpattern(temp, (char *)pattern, true)) + { + 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) + Sys_Printf("SearchPackFile: %s : %s\n", pak->filename, temp); + } + } + // strip off one path element at a time until empty + // this way directories are added to the listing if they match the pattern + slash = strrchr(temp, '/'); + backslash = strrchr(temp, '\\'); + colon = strrchr(temp, ':'); + separator = temp; + if (separator < slash) + separator = slash; + if (separator < backslash) + separator = backslash; + if (separator < colon) + separator = colon; + *((char *)separator) = 0; + } + } + } + else + { + // get a directory listing and look at each name + snprintf(netpath, sizeof (netpath), "%s/%s", searchpath->filename, basepath); + if ((dir = listdirectory(netpath))) + { + for (dirfile = dir;dirfile;dirfile = dirfile->next) + { + snprintf(temp, sizeof(temp), "%s/%s", basepath, dirfile->text); + if (matchpattern(temp, (char *)pattern, true)) + { + 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) + Sys_Printf("SearchDirFile: %s\n", temp); + } + } + } + freedirectory(dir); + } + } + } + + if (liststart) + { + liststart = stringlistsort(liststart); + numfiles = 0; + numchars = 0; + for (listtemp = liststart;listtemp;listtemp = listtemp->next) + { + numfiles++; + numchars += strlen(listtemp->text) + 1; + } + search = 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 = numfiles; + numfiles = 0; + numchars = 0; + for (listtemp = liststart;listtemp;listtemp = listtemp->next) + { + search->filenames[numfiles] = search->filenamesbuffer + numchars; + strcpy(search->filenames[numfiles], listtemp->text); + numfiles++; + numchars += strlen(listtemp->text) + 1; + } + if (liststart) + stringlistfree(liststart); + } + + Mem_Free(basepath); + return search; +} + +void FS_FreeSearch(fssearch_t *search) +{ + Z_Free(search); +} + +extern int con_linewidth; +int FS_ListDirectory(const char *pattern, int oneperline) +{ + int numfiles; + int numcolumns; + int numlines; + int columnwidth; + int linebufpos; + int i, j, k, l; + const char *name; + char linebuf[4096]; + fssearch_t *search; + search = FS_Search(pattern, true, true); + if (!search) + return 0; + numfiles = search->numfilenames; + if (!oneperline) + { + // FIXME: the names could be added to one column list and then + // gradually shifted into the next column if they fit, and then the + // next to make a compact variable width listing but it's a lot more + // complicated... + // find width for columns + columnwidth = 0; + for (i = 0;i < numfiles;i++) + { + l = strlen(search->filenames[i]); + if (columnwidth < l) + columnwidth = l; + } + // count the spacing character + columnwidth++; + // calculate number of columns + numcolumns = con_linewidth / columnwidth; + // don't bother with the column printing if it's only one column + if (numcolumns >= 2) + { + numlines = (numfiles + numcolumns - 1) / numcolumns; + for (i = 0;i < numlines;i++) + { + linebufpos = 0; + for (k = 0;k < numcolumns;k++) + { + l = i * numcolumns + k; + if (l < numfiles) + { + name = search->filenames[l]; + for (j = 0;name[j] && j < (int)sizeof(linebuf) - 1;j++) + linebuf[linebufpos++] = name[j]; + // space out name unless it's the last on the line + if (k < (numcolumns - 1) && l < (numfiles - 1)) + for (;j < columnwidth && j < (int)sizeof(linebuf) - 1;j++) + linebuf[linebufpos++] = ' '; + } + } + linebuf[linebufpos] = 0; + Con_Printf("%s\n", linebuf); + } + } + else + oneperline = true; + } + if (oneperline) + for (i = 0;i < numfiles;i++) + Con_Printf("%s\n", search->filenames[i]); + FS_FreeSearch(search); + return numfiles; +} + +static void FS_ListDirectoryCmd (const char* cmdname, int oneperline) +{ + const char *pattern; + if (Cmd_Argc() > 3) + { + Con_Printf("usage:\n%s [path/pattern]\n", cmdname); + return; + } + if (Cmd_Argc() == 2) + pattern = Cmd_Argv(1); + else + pattern = "*"; + if (!FS_ListDirectory(pattern, oneperline)) + Con_Print("No files found.\n"); +} + +void FS_Dir_f(void) +{ + FS_ListDirectoryCmd("dir", true); +} + +void FS_Ls_f(void) +{ + FS_ListDirectoryCmd("ls", false); +} +