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
1 changed files with 302 additions and 131 deletions

View File

@ -20,6 +20,9 @@ GNU General Public License for more details.
#if XASH_WIN32
#include <direct.h>
#include <io.h>
#elif XASH_DOS4GW
#include <direct.h>
#include <errno.h>
#else
#include <dirent.h>
#include <errno.h>
@ -61,6 +64,8 @@ GNU General Public License for more details.
#define ZIP_LOAD_NO_FILES 5
#define ZIP_LOAD_CORRUPTED 6
#define XASH_REDUCE_FD
typedef struct stringlist_s
{
// maxstrings changes as needed, causing reallocation of strings[] array
@ -86,6 +91,11 @@ struct file_s
// contents buffer
fs_offset_t buff_ind, buff_len; // buffer current index and length
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
@ -114,13 +124,13 @@ typedef struct zipfile_s
fs_offset_t offset; // offset of local file header
fs_offset_t size; //original file size
fs_offset_t compressed_size; // compressed file size
unsigned short flags;
} zipfile_t;
typedef struct zip_s
{
string filename;
byte *mempool;
file_t *handle;
int handle;
int numfiles;
time_t filetime;
zipfile_t *files;
@ -136,18 +146,82 @@ typedef struct searchpath_s
struct searchpath_s *next;
} searchpath_t;
byte *fs_mempool;
searchpath_t *fs_searchpaths = NULL; // chain
searchpath_t fs_directpath; // static direct path
char fs_basedir[MAX_SYSPATH]; // base game directory
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 byte *fs_mempool;
static searchpath_t *fs_searchpaths = NULL; // chain
static searchpath_t fs_directpath; // static direct path
static char fs_basedir[MAX_SYSPATH]; // base game directory
static char fs_gamedir[MAX_SYSPATH]; // game current directory
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
qboolean fs_caseinsensitive = true; // try to search missing files
static qboolean fs_caseinsensitive = true; // try to search missing files
#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 searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly );
@ -281,11 +355,9 @@ static void listdirectory( stringlist_t *list, const char *path, qboolean lowerc
closedir( dir );
#endif
// seems not needed anymore
#if 0
// convert names to lowercase because windows doesn't care, but pattern matching code often does
if( lowercase )
listlowercase( list );
#if XASH_DOS4GW
// convert names to lowercase because 8.3 always in CAPS
listlowercase( list );
#endif
}
@ -296,6 +368,7 @@ OTHER PRIVATE FUNCTIONS
=============================================================================
*/
/*
==================
FS_FixFileCase
@ -305,7 +378,36 @@ emulate WIN32 FS behaviour when opening local file
*/
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;
char path2[PATH_MAX], *fname;
@ -567,52 +669,41 @@ pack_t *FS_LoadPackPAK( const char *packfile, int *error )
for( i = 0; i < numpackfiles; i++ )
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;
Mem_Free( info );
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 )
{
int numpackfiles = 0, i;
zip_cdf_header_t header_cdf;
zip_header_eocd_t header_eocd;
uint signature;
fs_offset_t filepos = 0;
fs_offset_t filepos = 0, length;
zipfile_t *info = NULL;
char filename_buffer[MAX_SYSPATH];
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( !zip->handle )
if( zip->handle < 0 )
{
const char *fzipfile = FS_FixFileCase( zipfile );
if( fzipfile != zipfile )
zip->handle = FS_Open( fzipfile, "rb", true );
zip->handle = open( fzipfile, O_RDONLY|O_BINARY );
}
#endif
if( !zip->handle )
if( zip->handle < 0 )
{
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;
}
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 );
@ -634,7 +727,9 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
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 )
{
@ -659,13 +754,13 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
}
// Find oecd
FS_Seek( zip->handle, 0, SEEK_SET );
filepos = zip->handle->real_length;
lseek( zip->handle, 0, SEEK_SET );
filepos = length;
while ( filepos > 0 )
{
FS_Seek( zip->handle, filepos, SEEK_SET );
FS_Read( zip->handle, (void *)&signature, sizeof( signature ) );
lseek( zip->handle, filepos, SEEK_SET );
read( zip->handle, &signature, sizeof( signature ) );
if( signature == ZIP_HEADER_EOCD )
break;
@ -675,7 +770,7 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
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 )
*error = ZIP_LOAD_BAD_HEADER;
@ -684,19 +779,17 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
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
FS_Seek( zip->handle, header_eocd.central_directory_offset, SEEK_SET );
lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET );
// Calc count of files in archive
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++ )
{
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 )
{
@ -710,16 +803,10 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
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 );
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 );
info[numpackfiles].size = header_cdf.uncompressed_size;
@ -728,29 +815,40 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error )
numpackfiles++;
}
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 )
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 )
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 ) );
zip->mempool = Mem_AllocPool( zipfile );
zip->filetime = FS_SysFileTime( zipfile );
zip->numfiles = 0;
zip->files = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( zipfile_t ) * numpackfiles );
zip->numfiles = numpackfiles;
zip->files = info;
for( i = 0; i < numpackfiles; i++ )
FS_AddFileToZip( info[i].name, zip, info[i].offset, info[i].size, info[i].compressed_size );
#ifdef XASH_REDUCE_FD
// will reopen when needed
close(zip->handle);
zip->handle = -1;
#endif
if( error )
*error = ZIP_LOAD_OK;
Mem_Free( info );
return zip;
}
@ -759,10 +857,12 @@ void Zip_Close( zip_t *zip )
if( !zip )
return;
Mem_FreePool( &zip->mempool );
Mem_Free( zip->files );
if( zip->handle != NULL )
FS_Close( zip->handle );
FS_EnsureOpenZip( NULL );
if( zip->handle >= 0 )
close( zip->handle );
Mem_Free( zip );
}
@ -771,7 +871,6 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
{
searchpath_t *search;
int index;
zip_header_t header;
zipfile_t *file = NULL;
byte *compressed_buffer = NULL, *decompressed_buffer = NULL;
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];
FS_Seek( search->zip->handle, file->offset, SEEK_SET );
FS_Read( search->zip->handle, (void*)&header, sizeof( header ) );
FS_EnsureOpenZip( search->zip );
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 )
{
Con_Reportf( S_ERROR "Zip_LoadFile: %s signature error\n", file->name );
return NULL;
}
}*/
if( header.compression_flags == ZIP_COMPRESSION_NO_COMPRESSION )
if( file->flags == ZIP_COMPRESSION_NO_COMPRESSION )
{
if( header.filename_len )
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 = Mem_Malloc( fs_mempool, file->size + 1 );
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_ProcessBuffer( &test_crc, decompressed_buffer, file->size );
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 );
Mem_Free( decompressed_buffer );
return NULL;
}
#endif
if( sizeptr ) *sizeptr = file->size;
FS_EnsureOpenZip( NULL );
return decompressed_buffer;
}
else if( header.compression_flags == ZIP_COMPRESSION_DEFLATED )
else if( file->flags == ZIP_COMPRESSION_DEFLATED )
{
if( header.filename_len )
FS_Seek( search->zip->handle, header.filename_len, SEEK_CUR );
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 );
compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 );
decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 );
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 ) );
@ -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 )
{
Mem_Free( compressed_buffer ); // finaly free compressed buffer
#if 0
CRC32_Init( &test_crc );
CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size );
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 );
Mem_Free( decompressed_buffer );
return NULL;
}
#endif
if( sizeptr ) *sizeptr = file->size;
FS_EnsureOpenZip( NULL );
return decompressed_buffer;
}
else
@ -896,6 +990,7 @@ static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean game
return NULL;
}
FS_EnsureOpenZip( 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" ))
{
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 );
}
}
@ -1036,6 +1131,9 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int
if( zip )
{
string fullpath;
int i;
search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) );
search->zip = zip;
search->next = fs_searchpaths;
@ -1043,6 +1141,16 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int
fs_searchpaths = search;
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;
}
else
@ -1070,7 +1178,6 @@ void FS_AddGameDirectory( const char *dir, uint flags )
if( !FBitSet( flags, FS_NOWRITE_PATH ))
Q_strncpy( fs_writedir, dir, sizeof( fs_writedir ));
stringlistinit( &list );
listdirectory( &list, dir, false );
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 );
// 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 );
FS_AllowDirectPaths( false );
@ -1197,6 +1304,8 @@ void FS_ClearSearchPath( void )
{
if( search->pack->files )
Mem_Free( search->pack->files );
if( search->pack->handle >= 0 )
close( search->pack->handle );
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 );
if( ffilepath != filepath )
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
if( file->handle < 0 )
@ -2127,14 +2240,62 @@ static file_t *FS_SysOpen( const char *filepath, const char *mode )
return NULL;
}
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
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 );
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 )
{
dpackfile_t *pfile;
int dup_handle;
file_t *file;
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;
dup_handle = dup( pack->handle );
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;
return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size );
}
/*
@ -2431,6 +2594,8 @@ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedi
return FS_OpenPackedFile( search->pack, pack_ind );
else if( search->wad )
return NULL; // let W_LoadFile get lump correctly
else if( search->zip )
return FS_OpenZipFile( search->zip, pack_ind );
else if( pack_ind < 0 )
{
char path [MAX_SYSPATH];
@ -2495,8 +2660,11 @@ int FS_Close( file_t *file )
{
if( !file ) return 0;
if( close( file->handle ))
return EOF;
FS_BackupFileName( file, NULL, 0 );
if( file->handle >= 0 )
if( close( file->handle ))
return EOF;
Mem_Free( file );
return 0;
@ -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
FS_EnsureOpenFile( file );
// we must take care to not read after the end of the file
count = file->real_length - file->position;
@ -2784,6 +2953,7 @@ int FS_Seek( file_t *file, fs_offset_t offset, int whence )
return 0;
}
FS_EnsureOpenFile( file );
// Purge cached data
FS_Purge( file );
@ -2853,6 +3023,7 @@ byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamediro
if( file )
{
filesize = file->real_length;
buf = (byte *)Mem_Malloc( fs_mempool, filesize + 1 );
buf[filesize] = '\0';
FS_Read( file, buf, filesize );