filesystem: refactor zip/pak loading, partially fix reading files from zip, add option to reduce FD usage, remove dup() dependency

This commit is contained in:
mittorn 2020-02-08 22:50:30 +07:00
parent 70cddcb9ca
commit 22c148a39e

View File

@ -20,6 +20,9 @@ GNU General Public License for more details.
#if XASH_WIN32 #if XASH_WIN32
#include <direct.h> #include <direct.h>
#include <io.h> #include <io.h>
#elif XASH_DOS4GW
#include <direct.h>
#include <errno.h>
#else #else
#include <dirent.h> #include <dirent.h>
#include <errno.h> #include <errno.h>
@ -61,6 +64,8 @@ GNU General Public License for more details.
#define ZIP_LOAD_NO_FILES 5 #define ZIP_LOAD_NO_FILES 5
#define ZIP_LOAD_CORRUPTED 6 #define ZIP_LOAD_CORRUPTED 6
#define XASH_REDUCE_FD
typedef struct stringlist_s typedef struct stringlist_s
{ {
// maxstrings changes as needed, causing reallocation of strings[] array // maxstrings changes as needed, causing reallocation of strings[] array
@ -86,6 +91,11 @@ struct file_s
// contents buffer // 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
byte buff[FILE_BUFF_SIZE]; // intermediate buffer byte buff[FILE_BUFF_SIZE]; // intermediate buffer
#ifdef XASH_REDUCE_FD
const char *backup_path;
fs_offset_t *backup_position;
uint backup_options;
#endif
}; };
struct wfile_s struct wfile_s
@ -114,13 +124,13 @@ typedef struct zipfile_s
fs_offset_t offset; // offset of local file header fs_offset_t offset; // offset of local file header
fs_offset_t size; //original file size fs_offset_t size; //original file size
fs_offset_t compressed_size; // compressed file size fs_offset_t compressed_size; // compressed file size
unsigned short flags;
} zipfile_t; } zipfile_t;
typedef struct zip_s typedef struct zip_s
{ {
string filename; string filename;
byte *mempool; int handle;
file_t *handle;
int numfiles; int numfiles;
time_t filetime; time_t filetime;
zipfile_t *files; zipfile_t *files;
@ -136,18 +146,82 @@ typedef struct searchpath_s
struct searchpath_s *next; struct searchpath_s *next;
} searchpath_t; } searchpath_t;
byte *fs_mempool; static byte *fs_mempool;
searchpath_t *fs_searchpaths = NULL; // chain static searchpath_t *fs_searchpaths = NULL; // chain
searchpath_t fs_directpath; // static direct path static searchpath_t fs_directpath; // static direct path
char fs_basedir[MAX_SYSPATH]; // base game directory static char fs_basedir[MAX_SYSPATH]; // base game directory
char fs_gamedir[MAX_SYSPATH]; // game current directory static char fs_gamedir[MAX_SYSPATH]; // game current directory
char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) static char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course)
qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes static qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes
#if !XASH_WIN32 #if !XASH_WIN32
qboolean fs_caseinsensitive = true; // try to search missing files static qboolean fs_caseinsensitive = true; // try to search missing files
#endif #endif
#ifdef XASH_REDUCE_FD
static file_t *fs_last_readfile;
static zip_t *fs_last_zip;
static pack_t *fs_last_pak;
static void FS_EnsureOpenFile( file_t *file )
{
if( fs_last_readfile == file )
return;
if( !file->backup_path )
return;
if( fs_last_readfile && (fs_last_readfile->handle != -1) )
{
fs_last_readfile->backup_position = lseek( fs_last_readfile->handle, 0, SEEK_CUR );
close( fs_last_readfile->handle );
fs_last_readfile->handle = -1;
}
fs_last_readfile = file;
if( file && (file->handle == -1) )
{
file->handle = open( file->backup_path, file->backup_options );
lseek( file->handle, file->backup_position, SEEK_SET );
}
}
static void FS_EnsureOpenZip( zip_t *zip )
{
if( fs_last_zip == zip )
return;
if( fs_last_zip && (fs_last_zip->handle != -1) )
{
close( fs_last_zip->handle );
fs_last_zip->handle = -1;
}
fs_last_zip = zip;
if( zip && (zip->handle == -1) )
zip->handle = open( zip->filename, O_RDONLY|O_BINARY );
}
static void FS_BackupFileName( file_t *file, const char *path, uint options )
{
if( path == NULL )
{
if( file->backup_path )
Mem_Free( file->backup_path );
if( file == fs_last_readfile )
FS_EnsureOpenFile( NULL );
}
else if( options == O_RDONLY || options == (O_RDONLY|O_BINARY) )
{
file->backup_path = copystring( path );
file->backup_options = options;
}
}
#else
static void FS_EnsureOpenFile( file_t *file ) {}
static void FS_EnsureOpenZip( zip_t *zip ) {}
static void FS_BackupFileName( file_t *file, const char *path, uint options ) {}
#endif
static void FS_InitMemory( void ); static void FS_InitMemory( void );
static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly );
@ -281,10 +355,8 @@ static void listdirectory( stringlist_t *list, const char *path, qboolean lowerc
closedir( dir ); closedir( dir );
#endif #endif
// seems not needed anymore #if XASH_DOS4GW
#if 0 // convert names to lowercase because 8.3 always in CAPS
// convert names to lowercase because windows doesn't care, but pattern matching code often does
if( lowercase )
listlowercase( list ); listlowercase( list );
#endif #endif
} }
@ -296,6 +368,7 @@ OTHER PRIVATE FUNCTIONS
============================================================================= =============================================================================
*/ */
/* /*
================== ==================
FS_FixFileCase FS_FixFileCase
@ -305,7 +378,36 @@ emulate WIN32 FS behaviour when opening local file
*/ */
static const char *FS_FixFileCase( const char *path ) static const char *FS_FixFileCase( const char *path )
{ {
#if !XASH_WIN32 && !XASH_IOS // assume case insensitive #if defined __DOS__ & !defined __WATCOM_LFN__
// not fix, but convert to 8.3 CAPS and rotate slashes
// it is still recommended to package game data
static char out[PATH_MAX];
int i = 0;
int last = 0;
while(*path)
{
char c = *path++;
if(c == '/') c = '\\';
else c = toupper(c);
out[i++] = c;
if(c == '\\' || c == '.')
{
if( i - last > 8 )
{
char *l = &out[last];
l[6] = '~';
l[7] = '1';
l[8] = c;
i = last + 9;
}
last = i;
}
}
out[i] = 0;
return out;
#elif !XASH_WIN32 && !XASH_IOS // assume case insensitive
DIR *dir; struct dirent *entry; DIR *dir; struct dirent *entry;
char path2[PATH_MAX], *fname; char path2[PATH_MAX], *fname;
@ -567,52 +669,41 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error )
for( i = 0; i < numpackfiles; i++ ) for( i = 0; i < numpackfiles; i++ )
FS_AddFileToPack( info[i].name, pack, info[i].filepos, info[i].filelen ); FS_AddFileToPack( info[i].name, pack, info[i].filepos, info[i].filelen );
#ifdef XASH_REDUCE_FD
// will reopen when needed
close(pack->handle);
pack->handle = -1;
#endif
if( error ) *error = PAK_LOAD_OK; if( error ) *error = PAK_LOAD_OK;
Mem_Free( info ); Mem_Free( info );
return pack; return pack;
} }
static zipfile_t *FS_AddFileToZip( const char *name, zip_t *zip, fs_offset_t offset, fs_offset_t size, fs_offset_t compressed_size)
{
zipfile_t *zipfile = NULL;
zipfile = &zip->files[zip->numfiles];
Q_strncpy( zipfile->name, name, MAX_SYSPATH );
zipfile->size = size;
zipfile->offset = offset;
zipfile->compressed_size = compressed_size;
zip->numfiles++;
return zipfile;
}
static zip_t *FS_LoadZip( const char *zipfile, int *error ) static zip_t *FS_LoadZip( const char *zipfile, int *error )
{ {
int numpackfiles = 0, i; int numpackfiles = 0, i;
zip_cdf_header_t header_cdf; zip_cdf_header_t header_cdf;
zip_header_eocd_t header_eocd; zip_header_eocd_t header_eocd;
uint signature; uint signature;
fs_offset_t filepos = 0; fs_offset_t filepos = 0, length;
zipfile_t *info = NULL; zipfile_t *info = NULL;
char filename_buffer[MAX_SYSPATH]; char filename_buffer[MAX_SYSPATH];
zip_t *zip = (zip_t *)Mem_Calloc( fs_mempool, sizeof( zip_t ) ); zip_t *zip = (zip_t *)Mem_Calloc( fs_mempool, sizeof( zip_t ) );
zip->handle = FS_Open( zipfile, "rb", true ); zip->handle = open( zipfile, O_RDONLY|O_BINARY );
#if !XASH_WIN32 #if !XASH_WIN32
if( !zip->handle ) if( zip->handle < 0 )
{ {
const char *fzipfile = FS_FixFileCase( zipfile ); const char *fzipfile = FS_FixFileCase( zipfile );
if( fzipfile != zipfile ) if( fzipfile != zipfile )
zip->handle = FS_Open( fzipfile, "rb", true ); zip->handle = open( fzipfile, O_RDONLY|O_BINARY );
} }
#endif #endif
if( !zip->handle ) if( zip->handle < 0 )
{ {
Con_Reportf( S_ERROR "%s couldn't open\n", zipfile ); Con_Reportf( S_ERROR "%s couldn't open\n", zipfile );
@ -623,7 +714,9 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
return NULL; return NULL;
} }
if( FS_FileLength( zip->handle ) > UINT_MAX ) length = lseek( zip->handle, 0, SEEK_END );
if( length > UINT_MAX )
{ {
Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile ); Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile );
@ -634,7 +727,9 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
return NULL; return NULL;
} }
FS_Read( zip->handle, (void *)&signature, sizeof( uint ) ); lseek( zip->handle, 0, SEEK_SET );
read( zip->handle, &signature, sizeof( signature ) );
if( signature == ZIP_HEADER_EOCD ) if( signature == ZIP_HEADER_EOCD )
{ {
@ -659,13 +754,13 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
} }
// Find oecd // Find oecd
FS_Seek( zip->handle, 0, SEEK_SET ); lseek( zip->handle, 0, SEEK_SET );
filepos = zip->handle->real_length; filepos = length;
while ( filepos > 0 ) while ( filepos > 0 )
{ {
FS_Seek( zip->handle, filepos, SEEK_SET ); lseek( zip->handle, filepos, SEEK_SET );
FS_Read( zip->handle, (void *)&signature, sizeof( signature ) ); read( zip->handle, &signature, sizeof( signature ) );
if( signature == ZIP_HEADER_EOCD ) if( signature == ZIP_HEADER_EOCD )
break; break;
@ -675,7 +770,7 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
if( ZIP_HEADER_EOCD != signature ) if( ZIP_HEADER_EOCD != signature )
{ {
Con_Reportf( S_ERROR "Cannot find EOCD in %s. Zip file corrupted.\n", zipfile ); Con_Reportf( S_ERROR "cannot find EOCD in %s. Zip file corrupted.\n", zipfile );
if( error ) if( error )
*error = ZIP_LOAD_BAD_HEADER; *error = ZIP_LOAD_BAD_HEADER;
@ -684,19 +779,17 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
return NULL; return NULL;
} }
FS_Read( zip->handle, (void *)&header_eocd, sizeof( zip_header_eocd_t ) ); read( zip->handle, &header_eocd, sizeof( zip_header_eocd_t ) );
// Move to CDF start // Move to CDF start
lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET );
FS_Seek( zip->handle, header_eocd.central_directory_offset, SEEK_SET );
// Calc count of files in archive // Calc count of files in archive
info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( zipfile_t ) * header_eocd.total_central_directory_record ); info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( zipfile_t ) * header_eocd.total_central_directory_record );
for( i = 0; i < header_eocd.total_central_directory_record; i++ ) for( i = 0; i < header_eocd.total_central_directory_record; i++ )
{ {
FS_Read( zip->handle, (void *)&header_cdf, sizeof( header_cdf ) ); read( zip->handle, &header_cdf, sizeof( header_cdf ) );
if( header_cdf.signature != ZIP_HEADER_CDF ) if( header_cdf.signature != ZIP_HEADER_CDF )
{ {
@ -710,16 +803,10 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
return NULL; return NULL;
} }
if( header_cdf.uncompressed_size && header_cdf.filename_len ) if( header_cdf.uncompressed_size && header_cdf.filename_len && ( header_cdf.filename_len < MAX_SYSPATH ) )
{ {
if(header_cdf.filename_len > MAX_SYSPATH) // ignore files with bigger than 1024 bytes
{
Con_Reportf( S_WARN "File name bigger than buffer. Ignored.\n" );
continue;
}
memset( &filename_buffer, '\0', MAX_SYSPATH ); memset( &filename_buffer, '\0', MAX_SYSPATH );
FS_Read( zip->handle, &filename_buffer, header_cdf.filename_len ); read( zip->handle, &filename_buffer, header_cdf.filename_len );
Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH ); Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH );
info[numpackfiles].size = header_cdf.uncompressed_size; info[numpackfiles].size = header_cdf.uncompressed_size;
@ -728,29 +815,40 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
numpackfiles++; numpackfiles++;
} }
else else
FS_Seek( zip->handle, header_cdf.filename_len, SEEK_CUR ); lseek( zip->handle, header_cdf.filename_len, SEEK_CUR );
if( header_cdf.extrafield_len ) if( header_cdf.extrafield_len )
FS_Seek( zip->handle, header_cdf.extrafield_len, SEEK_CUR ); lseek( zip->handle, header_cdf.extrafield_len, SEEK_CUR );
if( header_cdf.file_commentary_len ) if( header_cdf.file_commentary_len )
FS_Seek( zip->handle, header_cdf.file_commentary_len, SEEK_CUR ); lseek( zip->handle, header_cdf.file_commentary_len, SEEK_CUR );
}
// recalculate offsets
for( i = 0; i < numpackfiles; i++ )
{
zip_header_t header;
lseek( zip->handle, info[i].offset, SEEK_SET );
read( zip->handle, &header, sizeof( header ) );
info[i].flags = header.compression_flags;
info[i].offset = info[i].offset + header.filename_len + header.extrafield_len + sizeof( header );
} }
Q_strncpy( zip->filename, zipfile, sizeof( zip->filename ) ); Q_strncpy( zip->filename, zipfile, sizeof( zip->filename ) );
zip->mempool = Mem_AllocPool( zipfile );
zip->filetime = FS_SysFileTime( zipfile ); zip->filetime = FS_SysFileTime( zipfile );
zip->numfiles = 0; zip->numfiles = numpackfiles;
zip->files = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( zipfile_t ) * numpackfiles ); zip->files = info;
for( i = 0; i < numpackfiles; i++ ) #ifdef XASH_REDUCE_FD
FS_AddFileToZip( info[i].name, zip, info[i].offset, info[i].size, info[i].compressed_size ); // will reopen when needed
close(zip->handle);
zip->handle = -1;
#endif
if( error ) if( error )
*error = ZIP_LOAD_OK; *error = ZIP_LOAD_OK;
Mem_Free( info );
return zip; return zip;
} }
@ -759,10 +857,12 @@ void Zip_Close( zip_t *zip )
if( !zip ) if( !zip )
return; return;
Mem_FreePool( &zip->mempool ); Mem_Free( zip->files );
if( zip->handle != NULL ) FS_EnsureOpenZip( NULL );
FS_Close( zip->handle );
if( zip->handle >= 0 )
close( zip->handle );
Mem_Free( zip ); Mem_Free( zip );
} }
@ -771,7 +871,6 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
{ {
searchpath_t *search; searchpath_t *search;
int index; int index;
zip_header_t header;
zipfile_t *file = NULL; zipfile_t *file = NULL;
byte *compressed_buffer = NULL, *decompressed_buffer = NULL; byte *compressed_buffer = NULL, *decompressed_buffer = NULL;
int zlib_result = 0; int zlib_result = 0;
@ -787,57 +886,51 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
file = &search->zip->files[index]; file = &search->zip->files[index];
FS_Seek( search->zip->handle, file->offset, SEEK_SET ); FS_EnsureOpenZip( search->zip );
FS_Read( search->zip->handle, (void*)&header, sizeof( header ) );
if( lseek( search->zip->handle, file->offset, SEEK_SET ) == -1 )
return NULL;
/*if( read( search->zip->handle, &header, sizeof( header ) ) < 0 )
return NULL;
if( header.signature != ZIP_HEADER_LF ) if( header.signature != ZIP_HEADER_LF )
{ {
Con_Reportf( S_ERROR "Zip_LoadFile: %s signature error\n", file->name ); Con_Reportf( S_ERROR "Zip_LoadFile: %s signature error\n", file->name );
return NULL; return NULL;
} }*/
if( header.compression_flags == ZIP_COMPRESSION_NO_COMPRESSION ) if( file->flags == ZIP_COMPRESSION_NO_COMPRESSION )
{ {
if( header.filename_len ) decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 );
FS_Seek( search->zip->handle, header.filename_len, SEEK_CUR );
if( header.extrafield_len )
FS_Seek( search->zip->handle, header.extrafield_len, SEEK_CUR );
decompressed_buffer = Mem_Malloc( search->zip->mempool, file->size + 1 );
decompressed_buffer[file->size] = '\0'; decompressed_buffer[file->size] = '\0';
FS_Read( search->zip->handle, decompressed_buffer, file->size ); read( search->zip->handle, decompressed_buffer, file->size );
#if 0
CRC32_Init( &test_crc ); CRC32_Init( &test_crc );
CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size );
final_crc = CRC32_Final( test_crc ); final_crc = CRC32_Final( test_crc );
if( final_crc != header.crc32 ) if( final_crc != file->crc32 )
{ {
Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name );
Mem_Free( decompressed_buffer ); Mem_Free( decompressed_buffer );
return NULL; return NULL;
} }
#endif
if( sizeptr ) *sizeptr = file->size; if( sizeptr ) *sizeptr = file->size;
FS_EnsureOpenZip( NULL );
return decompressed_buffer; return decompressed_buffer;
} }
else if( header.compression_flags == ZIP_COMPRESSION_DEFLATED ) else if( file->flags == ZIP_COMPRESSION_DEFLATED )
{ {
if( header.filename_len ) compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 );
FS_Seek( search->zip->handle, header.filename_len, SEEK_CUR ); decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 );
if( header.extrafield_len )
FS_Seek( search->zip->handle, header.extrafield_len, SEEK_CUR );
compressed_buffer = Mem_Malloc( search->zip->mempool, file->compressed_size + 1 );
decompressed_buffer = Mem_Malloc( search->zip->mempool, file->size + 1 );
decompressed_buffer[file->size] = '\0'; decompressed_buffer[file->size] = '\0';
FS_Read( search->zip->handle, compressed_buffer, file->compressed_size ); read( search->zip->handle, compressed_buffer, file->compressed_size );
memset( &decompress_stream, 0, sizeof( decompress_stream ) ); memset( &decompress_stream, 0, sizeof( decompress_stream ) );
@ -864,21 +957,22 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
if( zlib_result == Z_OK || zlib_result == Z_STREAM_END ) if( zlib_result == Z_OK || zlib_result == Z_STREAM_END )
{ {
Mem_Free( compressed_buffer ); // finaly free compressed buffer Mem_Free( compressed_buffer ); // finaly free compressed buffer
#if 0
CRC32_Init( &test_crc ); CRC32_Init( &test_crc );
CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size );
final_crc = CRC32_Final( test_crc ); final_crc = CRC32_Final( test_crc );
if( final_crc != header.crc32 ) if( final_crc != file->crc32 )
{ {
Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name );
Mem_Free( decompressed_buffer ); Mem_Free( decompressed_buffer );
return NULL; return NULL;
} }
#endif
if( sizeptr ) *sizeptr = file->size; if( sizeptr ) *sizeptr = file->size;
FS_EnsureOpenZip( NULL );
return decompressed_buffer; return decompressed_buffer;
} }
else else
@ -896,6 +990,7 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
return NULL; return NULL;
} }
FS_EnsureOpenZip( NULL );
return NULL; return NULL;
} }
@ -998,7 +1093,7 @@ static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loade
{ {
if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" )) if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" ))
{ {
Q_sprintf( fullpath, "%s/%s", pakfile, pak->files[i].name ); Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name );
FS_AddWad_Fullpath( fullpath, NULL, flags ); FS_AddWad_Fullpath( fullpath, NULL, flags );
} }
} }
@ -1036,6 +1131,9 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int
if( zip ) if( zip )
{ {
string fullpath;
int i;
search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) ); search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) );
search->zip = zip; search->zip = zip;
search->next = fs_searchpaths; search->next = fs_searchpaths;
@ -1043,6 +1141,16 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int
fs_searchpaths = search; fs_searchpaths = search;
Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles ); Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles );
// time to add in search list all the wads that contains in current pakfile (if do)
for( i = 0; i < zip->numfiles; i++ )
{
if( !Q_stricmp( COM_FileExtension( zip->files[i].name ), "wad" ))
{
Q_snprintf( fullpath, MAX_STRING, "%s/%s", zipfile, zip->files[i].name );
FS_AddWad_Fullpath( fullpath, NULL, flags );
}
}
return true; return true;
} }
else else
@ -1070,7 +1178,6 @@ void FS_AddGameDirectory( const char *dir, uint flags )
if( !FBitSet( flags, FS_NOWRITE_PATH )) if( !FBitSet( flags, FS_NOWRITE_PATH ))
Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); Q_strncpy( fs_writedir, dir, sizeof( fs_writedir ));
stringlistinit( &list ); stringlistinit( &list );
listdirectory( &list, dir, false ); listdirectory( &list, dir, false );
stringlistsort( &list ); stringlistsort( &list );
@ -1085,6 +1192,16 @@ void FS_AddGameDirectory( const char *dir, uint flags )
} }
} }
// add any Zip package in the directory
for( i = 0; i < list.numstrings; i++ )
{
if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "zip" ) || !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" ))
{
Q_sprintf( fullpath, "%s%s", dir, list.strings[i] );
FS_AddZip_Fullpath( fullpath, NULL, flags );
}
}
FS_AllowDirectPaths( true ); FS_AllowDirectPaths( true );
// add any WAD package in the directory // add any WAD package in the directory
@ -1097,16 +1214,6 @@ void FS_AddGameDirectory( const char *dir, uint flags )
} }
} }
// add any Zip package in the directory
for( i = 0; i < list.numstrings; i++ )
{
if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "zip" ) || !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" ))
{
Q_sprintf( fullpath, "%s%s", dir, list.strings[i] );
FS_AddZip_Fullpath( fullpath, NULL, flags );
}
}
stringlistfreecontents( &list ); stringlistfreecontents( &list );
FS_AllowDirectPaths( false ); FS_AllowDirectPaths( false );
@ -1197,6 +1304,8 @@ void FS_ClearSearchPath( void )
{ {
if( search->pack->files ) if( search->pack->files )
Mem_Free( search->pack->files ); Mem_Free( search->pack->files );
if( search->pack->handle >= 0 )
close( search->pack->handle );
Mem_Free( search->pack ); Mem_Free( search->pack );
} }
@ -2118,7 +2227,11 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode )
const char *ffilepath = FS_FixFileCase( filepath ); const char *ffilepath = FS_FixFileCase( filepath );
if( ffilepath != filepath ) if( ffilepath != filepath )
file->handle = open( ffilepath, mod|opt, 0666 ); file->handle = open( ffilepath, mod|opt, 0666 );
if( file->handle >= 0 )
FS_BackupFileName( file, ffilepath, mod|opt );
} }
else
FS_BackupFileName( file, filepath, mod|opt );
#endif #endif
if( file->handle < 0 ) if( file->handle < 0 )
@ -2127,14 +2240,62 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode )
return NULL; return NULL;
} }
file->real_length = lseek( file->handle, 0, SEEK_END ); file->real_length = lseek( file->handle, 0, SEEK_END );
// uncomment do disable write
//if( opt & O_CREAT )
// return NULL;
// For files opened in append mode, we start at the end of the file // For files opened in append mode, we start at the end of the file
if( mod & O_APPEND ) file->position = file->real_length; if( opt & O_APPEND ) file->position = file->real_length;
else lseek( file->handle, 0, SEEK_SET ); else lseek( file->handle, 0, SEEK_SET );
return file; return file;
} }
/*
static int FS_DuplicateHandle( const char *filename, int handle, fs_offset_t pos )
{
#ifdef HAVE_DUP
return dup( handle );
#else
int newhandle = open( filename, O_RDONLY|O_BINARY );
lseek( newhandle, pos, SEEK_SET );
return newhandle;
#endif
}
*/
static int FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len )
{
file_t *file = (file_t *)Mem_Calloc( fs_mempool, sizeof( file_t ));
#ifndef XASH_REDUCE_FD
#ifdef HAVE_DUP
file->handle = dup( handle );
#else
file->handle = open( syspath, O_RDONLY|O_BINARY );
#endif
if( lseek( file->handle, offset, SEEK_SET ) == -1 )
{
Mem_Free( file );
return NULL;
}
#else
file->backup_position = offset;
file->backup_path = copystring( syspath );
file->backup_options = O_RDONLY|O_BINARY;
file->handle = -1;
#endif
file->real_length = len;
file->offset = offset;
file->position = 0;
file->ungetc = EOF;
return file;
}
/* /*
=========== ===========
@ -2146,27 +2307,29 @@ Open a packed file using its package file descriptor
file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ) file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind )
{ {
dpackfile_t *pfile; dpackfile_t *pfile;
int dup_handle;
file_t *file;
pfile = &pack->files[pack_ind]; pfile = &pack->files[pack_ind];
if( lseek( pack->handle, pfile->filepos, SEEK_SET ) == -1 ) return FS_OpenHandle( pack->filename, pack->handle, pfile->filepos, pfile->filelen );
}
/*
===========
FS_OpenZipFile
Open a packed file using its package file descriptor
===========
*/
file_t *FS_OpenZipFile( zip_t *zip, int pack_ind )
{
zipfile_t *pfile;
pfile = &zip->files[pack_ind];
// compressed files handled in Zip_LoadFile
if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION )
return NULL; return NULL;
dup_handle = dup( pack->handle ); return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size );
if( dup_handle < 0 )
return NULL;
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file ));
file->handle = dup_handle;
file->real_length = pfile->filelen;
file->offset = pfile->filepos;
file->position = 0;
file->ungetc = EOF;
return file;
} }
/* /*
@ -2431,6 +2594,8 @@ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedi
return FS_OpenPackedFile( search->pack, pack_ind ); return FS_OpenPackedFile( search->pack, pack_ind );
else if( search->wad ) else if( search->wad )
return NULL; // let W_LoadFile get lump correctly return NULL; // let W_LoadFile get lump correctly
else if( search->zip )
return FS_OpenZipFile( search->zip, pack_ind );
else if( pack_ind < 0 ) else if( pack_ind < 0 )
{ {
char path [MAX_SYSPATH]; char path [MAX_SYSPATH];
@ -2495,6 +2660,9 @@ int FS_Close( file_t *file )
{ {
if( !file ) return 0; if( !file ) return 0;
FS_BackupFileName( file, NULL, 0 );
if( file->handle >= 0 )
if( close( file->handle )) if( close( file->handle ))
return EOF; return EOF;
@ -2575,6 +2743,7 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
// NOTE: at this point, the read buffer is always empty // NOTE: at this point, the read buffer is always empty
FS_EnsureOpenFile( file );
// we must take care to not read after the end of the file // we must take care to not read after the end of the file
count = file->real_length - file->position; count = file->real_length - file->position;
@ -2784,6 +2953,7 @@ int FS_Seek( file_t *file, fs_offset_t offset, int whence )
return 0; return 0;
} }
FS_EnsureOpenFile( file );
// Purge cached data // Purge cached data
FS_Purge( file ); FS_Purge( file );
@ -2853,6 +3023,7 @@ byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamediro
if( file ) if( file )
{ {
filesize = file->real_length; filesize = file->real_length;
buf = (byte *)Mem_Malloc( fs_mempool, filesize + 1 ); buf = (byte *)Mem_Malloc( fs_mempool, filesize + 1 );
buf[filesize] = '\0'; buf[filesize] = '\0';
FS_Read( file, buf, filesize ); FS_Read( file, buf, filesize );