* sort pk3s, so content of later (zzz) overrides earlier, like in radiant and engine

* fix strong performance penalty with large amount of files in pk3s
store pak file path once per pk3, not per each file inside
This commit is contained in:
Garux 2021-08-13 03:20:07 +03:00
parent 4beae3d362
commit bb1931b745
3 changed files with 90 additions and 103 deletions

View File

@ -129,6 +129,27 @@ inline bool string_greater_nocase( const char* string, const char* other ){
return string_compare_nocase( string, other ) > 0;
}
/*!
This behaves identically to stricmp(a,b), except that ASCII chars
[\]^`_ come AFTER alphabet chars instead of before. This is because
it converts all alphabet chars to uppercase before comparison,
while stricmp converts them to lowercase.
*/
inline int string_compare_nocase_upper( const char* a, const char* b ){
for (;; )
{
const int c1 = std::toupper( *a++ );
const int c2 = std::toupper( *b++ );
if ( c1 < c2 )
return -1; // a < b
if ( c1 > c2 )
return 1; // a > b
if ( c1 == 0 )
return 0; // a == b
}
}
/// \brief Returns the number of non-null characters in \p string.
/// O(n)
inline std::size_t string_length( const char* string ){

View File

@ -92,7 +92,7 @@ static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
static int g_numDirs;
static char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
static int g_numForbiddenDirs = 0;
static bool g_bUsePak = true;
static constexpr bool g_bUsePak = true;
ModuleObservers g_observers;
@ -228,37 +228,6 @@ static GSList* GetListInternal( const char *refdir, const char *ext, bool direct
return files;
}
inline int ascii_to_upper( int c ){
if ( c >= 'a' && c <= 'z' ) {
return c - ( 'a' - 'A' );
}
return c;
}
/*!
This behaves identically to stricmp(a,b), except that ASCII chars
[\]^`_ come AFTER alphabet chars instead of before. This is because
it converts all alphabet chars to uppercase before comparison,
while stricmp converts them to lowercase.
*/
static int string_compare_nocase_upper( const char* a, const char* b ){
for (;; )
{
int c1 = ascii_to_upper( *a++ );
int c2 = ascii_to_upper( *b++ );
if ( c1 < c2 ) {
return -1; // a < b
}
if ( c1 > c2 ) {
return 1; // a > b
}
if ( c1 == 0 ) {
return 0; // a == b
}
}
}
// Arnout: note - sort pakfiles in reverse order. This ensures that
// later pakfiles override earlier ones. This because the vfs module
// returns a filehandle to the first file it can find (while it should

View File

@ -54,26 +54,36 @@
#include "stream/stringstream.h"
#include "stream/textstream.h"
#include <forward_list>
struct VFS_PAK
{
unzFile zipfile;
const CopiedString unzFilePath;
VFS_PAK( unzFile zipfile, const char *unzFilePath ) : zipfile( zipfile ), unzFilePath( unzFilePath ) {};
~VFS_PAK(){
unzClose( zipfile );
}
};
struct VFS_PAKFILE
{
char* unzFilePath;
char* name;
unz_s zipinfo;
unzFile zipfile;
guint32 size;
const CopiedString name;
const unz_s zipinfo;
VFS_PAK& pak;
const guint32 size;
};
// =============================================================================
// Global variables
static GSList* g_unzFiles;
static GSList* g_pakFiles;
static std::forward_list<VFS_PAK> g_paks;
static std::forward_list<VFS_PAKFILE> g_pakFiles;
static char g_strDirs[VFS_MAXDIRS][PATH_MAX + 1];
static int g_numDirs;
char g_strForbiddenDirs[VFS_MAXDIRS][PATH_MAX + 1];
int g_numForbiddenDirs = 0;
static gboolean g_bUsePak = TRUE;
static constexpr bool g_bUsePak = true;
char g_strLoadedFileLocation[1024];
// =============================================================================
@ -93,7 +103,7 @@ static void vfsInitPakFile( const char *filename ){
return;
}
g_unzFiles = g_slist_append( g_unzFiles, uf );
VFS_PAK& pak = g_paks.emplace_front( uf, filename );
err = unzGetGlobalInfo( uf,&gi );
if ( err != UNZ_OK ) {
@ -101,8 +111,6 @@ static void vfsInitPakFile( const char *filename ){
}
unzGoToFirstFile( uf );
char* unzFilePath = strdup( filename );
for ( i = 0; i < gi.number_entry; i++ )
{
char filename_inzip[NAME_MAX];
@ -113,17 +121,15 @@ static void vfsInitPakFile( const char *filename ){
break;
}
VFS_PAKFILE* file = safe_malloc( sizeof( VFS_PAKFILE ) );
g_pakFiles = g_slist_append( g_pakFiles, file );
FixDOSName( filename_inzip );
strLower( filename_inzip );
file->name = strdup( filename_inzip );
file->size = file_info.uncompressed_size;
file->zipfile = uf;
file->unzFilePath = unzFilePath;
memcpy( &file->zipinfo, uf, sizeof( unz_s ) );
g_pakFiles.emplace_front( VFS_PAKFILE{
filename_inzip,
*(unz_s*)uf,
pak,
file_info.uncompressed_size
} );
if ( ( i + 1 ) < gi.number_entry ) {
err = unzGoToNextFile( uf );
@ -139,7 +145,6 @@ static void vfsInitPakFile( const char *filename ){
// reads all pak files from a dir
void vfsInitDirectory( const char *path ){
char filename[PATH_MAX];
GDir *dir;
int j;
@ -171,6 +176,7 @@ void vfsInitDirectory( const char *path ){
dir = g_dir_open( path, 0, NULL );
if ( dir != NULL ) {
std::vector<StringOutputStream> paks;
const char* name;
while ( ( name = g_dir_read_name( dir ) ) )
{
@ -187,8 +193,7 @@ void vfsInitDirectory( const char *path ){
const char *ext = path_get_filename_base_end( name );
if ( striEqual( ext, ".pk3" ) ) {
sprintf( filename, "%s/%s", path, name );
vfsInitPakFile( filename );
paks.push_back( StringOutputStream( 256 )( path, '/', name ) );
}
else if ( striEqual( ext, ".pk3dir" ) ) {
if ( g_numDirs == VFS_MAXDIRS ) {
@ -201,6 +206,17 @@ void vfsInitDirectory( const char *path ){
}
}
g_dir_close( dir );
// sort paks in ascending order
// pakFiles are then prepended to the list, reversing the order
// thus later (zzz) pak content have priority over earlier, just like in engine
std::sort( paks.begin(), paks.end(),
[]( const char* a, const char* b ){
return string_compare_nocase_upper( a, b ) < 0;
} );
for( const char* pak : paks ){
vfsInitPakFile( pak );
}
}
}
}
@ -231,12 +247,12 @@ std::vector<CopiedString> vfsListShaderFiles( const char *shaderPath ){
}
}
/* search in packs */
for ( GSList *lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
for ( const VFS_PAKFILE& file : g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if ( striEqual( path_get_filename_base_end( file->name ), ".shader" )
&& strniEqual( file->name, shaderPath, path_get_last_separator( file->name ) - file->name ) ) {
insert( path_get_filename_start( file->name ) );
const char *name = file.name.c_str();
if ( striEqual( path_get_filename_base_end( name ), ".shader" )
&& strniEqual( name, shaderPath, path_get_last_separator( name ) - name ) ) {
insert( path_get_filename_start( name ) );
}
}
@ -245,37 +261,22 @@ std::vector<CopiedString> vfsListShaderFiles( const char *shaderPath ){
// frees all memory that we allocated
void vfsShutdown(){
while ( g_unzFiles )
{
unzClose( (unzFile)g_unzFiles->data );
g_unzFiles = g_slist_remove( g_unzFiles, g_unzFiles->data );
}
while ( g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)g_pakFiles->data;
free( file->unzFilePath );
free( file->name );
free( file );
g_pakFiles = g_slist_remove( g_pakFiles, file );
}
g_paks.clear();
g_pakFiles.clear();
}
// return the number of files that match
int vfsGetFileCount( const char *filename ){
int i, count = 0;
char fixed[NAME_MAX], tmp[NAME_MAX];
GSList *lst;
strcpy( fixed, filename );
FixDOSName( fixed );
strLower( fixed );
for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
for ( const VFS_PAKFILE& file : g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if ( strEqual( file->name, fixed ) ) {
if ( strEqual( file.name.c_str(), fixed ) ) {
count++;
}
}
@ -296,7 +297,6 @@ int vfsGetFileCount( const char *filename ){
int vfsLoadFile( const char *filename, void **bufferptr, int index ){
int i, count = 0;
char tmp[NAME_MAX], fixed[NAME_MAX];
GSList *lst;
// filename is a full path
if ( index == -1 ) {
@ -365,34 +365,33 @@ int vfsLoadFile( const char *filename, void **bufferptr, int index ){
}
}
for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
for ( const VFS_PAKFILE& file : g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if ( !strEqual( file->name, fixed ) ) {
if ( !strEqual( file.name.c_str(), fixed ) ) {
continue;
}
if ( count == index ) {
snprintf( g_strLoadedFileLocation, sizeof( g_strLoadedFileLocation ), "%s :: %s", file->unzFilePath, filename );
snprintf( g_strLoadedFileLocation, sizeof( g_strLoadedFileLocation ), "%s :: %s", file.pak.unzFilePath.c_str(), filename );
memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
unzFile zipfile = file.pak.zipfile;
*(unz_s*)zipfile = file.zipinfo;
if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
if ( unzOpenCurrentFile( zipfile ) != UNZ_OK ) {
return -1;
}
*bufferptr = safe_malloc( file->size + 1 );
*bufferptr = safe_malloc( file.size + 1 );
// we need to end the buffer with a 0
( (char*) ( *bufferptr ) )[file->size] = 0;
( (char*) ( *bufferptr ) )[file.size] = 0;
i = unzReadCurrentFile( file->zipfile, *bufferptr, file->size );
unzCloseCurrentFile( file->zipfile );
i = unzReadCurrentFile( zipfile, *bufferptr, file.size );
unzCloseCurrentFile( zipfile );
if ( i < 0 ) {
return -1;
}
else{
return file->size;
return file.size;
}
}
@ -408,7 +407,6 @@ int vfsLoadFile( const char *filename, void **bufferptr, int index ){
bool vfsPackFile( const char *filename, const char *packname, const int compLevel ){
int i;
char tmp[NAME_MAX], fixed[NAME_MAX];
GSList *lst;
byte *bufferptr = NULL;
strcpy( fixed, filename );
@ -452,32 +450,31 @@ bool vfsPackFile( const char *filename, const char *packname, const int compLeve
}
}
for ( lst = g_pakFiles; lst != NULL; lst = g_slist_next( lst ) )
for ( const VFS_PAKFILE& file : g_pakFiles )
{
VFS_PAKFILE* file = (VFS_PAKFILE*)lst->data;
if ( !strEqual( file->name, fixed ) ) {
if ( !strEqual( file.name.c_str(), fixed ) ) {
continue;
}
memcpy( file->zipfile, &file->zipinfo, sizeof( unz_s ) );
unzFile zipfile = file.pak.zipfile;
*(unz_s*)zipfile = file.zipinfo;
if ( unzOpenCurrentFile( file->zipfile ) != UNZ_OK ) {
if ( unzOpenCurrentFile( zipfile ) != UNZ_OK ) {
return false;
}
bufferptr = safe_malloc( file->size + 1 );
bufferptr = safe_malloc( file.size + 1 );
// we need to end the buffer with a 0
bufferptr[file->size] = 0;
bufferptr[file.size] = 0;
i = unzReadCurrentFile( file->zipfile, bufferptr, file->size );
unzCloseCurrentFile( file->zipfile );
i = unzReadCurrentFile( zipfile, bufferptr, file.size );
unzCloseCurrentFile( zipfile );
if ( i < 0 ) {
return false;
}
else{
mz_bool success = MZ_TRUE;
success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, compLevel, file->zipinfo.cur_file_info.dosDate );
success &= mz_zip_add_mem_to_archive_file_in_place_with_time( packname, filename, bufferptr, i, 0, 0, compLevel, file.zipinfo.cur_file_info.dosDate );
if ( !success ){
Error( "Failed creating zip archive \"%s\"!\n", packname );
}