filesystem: take code from DarkPlaces to properly read compressed files
This commit is contained in:
parent
2065ef143a
commit
f29c588e0f
3 changed files with 187 additions and 7 deletions
|
@ -2224,6 +2224,12 @@ int FS_Close( file_t *file )
|
||||||
return EOF;
|
return EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( file->ztk )
|
||||||
|
{
|
||||||
|
inflateEnd( &file->ztk->zstream );
|
||||||
|
Mem_Free( file->ztk );
|
||||||
|
}
|
||||||
|
|
||||||
Mem_Free( file );
|
Mem_Free( file );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -2329,7 +2335,93 @@ 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 );
|
FS_EnsureOpenFile( file ); // FIXME: broken XASH_REDUCE_FD in case of compressed files!
|
||||||
|
|
||||||
|
if( FBitSet( file->flags, FILE_DEFLATED ))
|
||||||
|
{
|
||||||
|
// If the file is compressed, it's more complicated...
|
||||||
|
// We cycle through a few operations until we have read enough data
|
||||||
|
while( buffersize > 0 )
|
||||||
|
{
|
||||||
|
ztoolkit_t *ztk = file->ztk;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
// NOTE: at this point, the read buffer is always empty
|
||||||
|
|
||||||
|
// If "input" is also empty, we need to refill it
|
||||||
|
if( ztk->in_ind == ztk->in_len )
|
||||||
|
{
|
||||||
|
// If we are at the end of the file
|
||||||
|
if( file->position == file->real_length )
|
||||||
|
return done;
|
||||||
|
|
||||||
|
count = (fs_offset_t)( ztk->comp_length - ztk->in_position );
|
||||||
|
if( count > (fs_offset_t)sizeof( ztk->input ))
|
||||||
|
count = (fs_offset_t)sizeof( ztk->input );
|
||||||
|
lseek( file->handle, file->offset + (fs_offset_t)ztk->in_position, SEEK_SET );
|
||||||
|
if( read( file->handle, ztk->input, count ) != count )
|
||||||
|
{
|
||||||
|
Con_Printf( "%s: unexpected end of file\n", __func__ );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ztk->in_ind = 0;
|
||||||
|
ztk->in_len = count;
|
||||||
|
ztk->in_position += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
ztk->zstream.next_in = &ztk->input[ztk->in_ind];
|
||||||
|
ztk->zstream.avail_in = (unsigned int)( ztk->in_len - ztk->in_ind );
|
||||||
|
|
||||||
|
// Now that we are sure we have compressed data available, we need to determine
|
||||||
|
// if it's better to inflate it in "file->buff" or directly in "buffer"
|
||||||
|
|
||||||
|
// Inflate the data in "file->buff"
|
||||||
|
if( buffersize < sizeof( file->buff ) / 2 )
|
||||||
|
{
|
||||||
|
ztk->zstream.next_out = file->buff;
|
||||||
|
ztk->zstream.avail_out = sizeof( file->buff );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ztk->zstream.next_out = &((unsigned char*)buffer)[done];
|
||||||
|
ztk->zstream.avail_out = (unsigned int)buffersize;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = inflate( &ztk->zstream, Z_SYNC_FLUSH );
|
||||||
|
if( error != Z_OK && error != Z_STREAM_END )
|
||||||
|
{
|
||||||
|
Con_Printf( "%s: Can't inflate file (%d)\n", __func__, error );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ztk->in_ind = ztk->in_len - ztk->zstream.avail_in;
|
||||||
|
|
||||||
|
if( buffersize < sizeof( file->buff ) / 2 )
|
||||||
|
{
|
||||||
|
file->buff_len = (fs_offset_t)sizeof( file->buff ) - ztk->zstream.avail_out;
|
||||||
|
file->position += file->buff_len;
|
||||||
|
|
||||||
|
// Copy the requested data in "buffer" (as much as we can)
|
||||||
|
count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
|
||||||
|
memcpy( &((unsigned char*)buffer)[done], file->buff, count );
|
||||||
|
file->buff_ind = count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
count = (fs_offset_t)( buffersize - ztk->zstream.avail_out );
|
||||||
|
file->position += count;
|
||||||
|
|
||||||
|
// Purge cached data
|
||||||
|
FS_Purge( file );
|
||||||
|
}
|
||||||
|
|
||||||
|
done += count;
|
||||||
|
buffersize -= count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return done;
|
||||||
|
}
|
||||||
|
|
||||||
// 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;
|
||||||
|
|
||||||
|
@ -2545,11 +2637,59 @@ int FS_Seek( file_t *file, fs_offset_t offset, int whence )
|
||||||
// Purge cached data
|
// Purge cached data
|
||||||
FS_Purge( file );
|
FS_Purge( file );
|
||||||
|
|
||||||
|
if( FBitSet( file->flags, FILE_DEFLATED ))
|
||||||
|
{
|
||||||
|
// Seeking in compressed files is more a hack than anything else,
|
||||||
|
// but we need to support it, so here we go.
|
||||||
|
ztoolkit_t *ztk = file->ztk;
|
||||||
|
unsigned char *buffer;
|
||||||
|
fs_offset_t buffersize;
|
||||||
|
|
||||||
|
// If we have to go back in the file, we need to restart from the beginning
|
||||||
|
if( offset <= file->position )
|
||||||
|
{
|
||||||
|
ztk->in_ind = 0;
|
||||||
|
ztk->in_len = 0;
|
||||||
|
ztk->in_position = 0;
|
||||||
|
file->position = 0;
|
||||||
|
if( lseek( file->handle, file->offset, SEEK_SET ) == -1 )
|
||||||
|
Con_Printf("IMPOSSIBLE: couldn't seek in already opened pk3 file.\n");
|
||||||
|
|
||||||
|
// Reset the Zlib stream
|
||||||
|
ztk->zstream.next_in = ztk->input;
|
||||||
|
ztk->zstream.avail_in = 0;
|
||||||
|
inflateReset( &ztk->zstream );
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a big buffer to force inflating into it directly
|
||||||
|
buffersize = 2 * sizeof( file->buff );
|
||||||
|
buffer = (unsigned char *)Mem_Malloc( fs_mempool, buffersize );
|
||||||
|
|
||||||
|
// Skip all data until we reach the requested offset
|
||||||
|
while( offset > ( file->position - file->buff_len + file->buff_ind ))
|
||||||
|
{
|
||||||
|
fs_offset_t diff = offset - ( file->position - file->buff_len + file->buff_ind );
|
||||||
|
fs_offset_t count;
|
||||||
|
|
||||||
|
count = ( diff > buffersize ) ? buffersize : diff;
|
||||||
|
if( FS_Read( file, buffer, count ) != count )
|
||||||
|
{
|
||||||
|
Mem_Free( buffer );
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Mem_Free( buffer );
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if( lseek( file->handle, file->offset + offset, SEEK_SET ) == -1 )
|
if( lseek( file->handle, file->offset + offset, SEEK_SET ) == -1 )
|
||||||
return -1;
|
return -1;
|
||||||
file->position = offset;
|
file->position = offset;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -22,6 +22,7 @@ GNU General Public License for more details.
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "xash3d_types.h"
|
#include "xash3d_types.h"
|
||||||
#include "filesystem.h"
|
#include "filesystem.h"
|
||||||
|
#include "miniz.h"
|
||||||
|
|
||||||
#if XASH_ANDROID
|
#if XASH_ANDROID
|
||||||
#include <android/asset_manager.h>
|
#include <android/asset_manager.h>
|
||||||
|
@ -40,6 +41,16 @@ typedef struct wfile_s wfile_t;
|
||||||
typedef struct android_assets_s android_assets_t;
|
typedef struct android_assets_s android_assets_t;
|
||||||
|
|
||||||
#define FILE_BUFF_SIZE (2048)
|
#define FILE_BUFF_SIZE (2048)
|
||||||
|
#define FILE_DEFLATED BIT( 0 )
|
||||||
|
|
||||||
|
typedef struct ztoolkit_s
|
||||||
|
{
|
||||||
|
z_stream zstream;
|
||||||
|
size_t comp_length;
|
||||||
|
size_t in_ind, in_len;
|
||||||
|
size_t in_position;
|
||||||
|
byte input[FILE_BUFF_SIZE];
|
||||||
|
} ztoolkit_t;
|
||||||
|
|
||||||
struct file_s
|
struct file_s
|
||||||
{
|
{
|
||||||
|
@ -50,7 +61,9 @@ struct file_s
|
||||||
fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode)
|
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 position; // current position in the file
|
||||||
fs_offset_t offset; // offset into the package (0 if external file)
|
fs_offset_t offset; // offset into the package (0 if external file)
|
||||||
|
uint32_t flags;
|
||||||
|
ztoolkit_t *ztk; // if not NULL, all read functions must go through decompression
|
||||||
|
|
||||||
// contents buffer
|
// contents buffer
|
||||||
fs_offset_t buff_ind; // buffer current index
|
fs_offset_t buff_ind; // buffer current index
|
||||||
fs_offset_t buff_len; // buffer current length
|
fs_offset_t buff_len; // buffer current length
|
||||||
|
|
|
@ -27,7 +27,6 @@ GNU General Public License for more details.
|
||||||
#include "filesystem_internal.h"
|
#include "filesystem_internal.h"
|
||||||
#include "crtlib.h"
|
#include "crtlib.h"
|
||||||
#include "common/com_strings.h"
|
#include "common/com_strings.h"
|
||||||
#include "miniz.h"
|
|
||||||
|
|
||||||
#define ZIP_HEADER_LF (('K'<<8)+('P')+(0x03<<16)+(0x04<<24))
|
#define ZIP_HEADER_LF (('K'<<8)+('P')+(0x03<<16)+(0x04<<24))
|
||||||
#define ZIP_HEADER_SPANNED ((0x08<<24)+(0x07<<16)+('K'<<8)+'P')
|
#define ZIP_HEADER_SPANNED ((0x08<<24)+(0x07<<16)+('K'<<8)+'P')
|
||||||
|
@ -389,15 +388,43 @@ Open a packed file using its package file descriptor
|
||||||
static file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char *mode, int pack_ind )
|
static file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char *mode, int pack_ind )
|
||||||
{
|
{
|
||||||
zipfile_t *pfile = &search->zip->files[pack_ind];
|
zipfile_t *pfile = &search->zip->files[pack_ind];
|
||||||
|
file_t *f = FS_OpenHandle( search, search->zip->handle->handle, pfile->offset, pfile->size );
|
||||||
|
|
||||||
// compressed files handled in Zip_LoadFile
|
if( !f )
|
||||||
if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION )
|
return NULL;
|
||||||
|
|
||||||
|
if( pfile->flags == ZIP_COMPRESSION_DEFLATED )
|
||||||
{
|
{
|
||||||
Con_Printf( S_ERROR "%s: can't open compressed file %s\n", __func__, pfile->name );
|
ztoolkit_t *ztk;
|
||||||
|
|
||||||
|
SetBits( f->flags, FILE_DEFLATED );
|
||||||
|
|
||||||
|
ztk = Mem_Calloc( fs_mempool, sizeof( *ztk ));
|
||||||
|
ztk->comp_length = pfile->compressed_size;
|
||||||
|
ztk->zstream.next_in = ztk->input;
|
||||||
|
ztk->zstream.avail_in = 0;
|
||||||
|
|
||||||
|
if( inflateInit2( &ztk->zstream, -MAX_WBITS ) != Z_OK )
|
||||||
|
{
|
||||||
|
Con_Printf( "%s: inflate init error (file: %s)\n", __func__, filename );
|
||||||
|
FS_Close( f );
|
||||||
|
Mem_Free( ztk );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ztk->zstream.next_out = f->buff;
|
||||||
|
ztk->zstream.avail_out = sizeof( f->buff );
|
||||||
|
|
||||||
|
f->ztk = ztk;
|
||||||
|
}
|
||||||
|
else if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION )
|
||||||
|
{
|
||||||
|
Con_Reportf( S_ERROR "%s: %s: file compressed with unknown algorithm.\n", __func__, filename );
|
||||||
|
FS_Close( f );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FS_OpenHandle( search, search->zip->handle->handle, pfile->offset, pfile->size );
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Reference in a new issue