Browse Source

filesystem: introduce new module, based on engine filesystem.

The goal is to share filesystem code between engine and
utilities and provide C++ VFileSystem interface in the future
pull/2/head
Alibek Omarov 2 years ago
parent
commit
5e4fc64430
  1. 36
      engine/client/cl_gameui.c
  2. 8
      engine/client/ref_common.c
  3. 17
      engine/common/common.c
  4. 142
      engine/common/common.h
  5. 6
      engine/common/con_utils.c
  6. 200
      engine/common/filesystem.h
  7. 146
      engine/common/filesystem_engine.c
  8. 29
      engine/common/host.c
  9. 4
      engine/common/hpak.c
  10. 60
      engine/common/hpak.h
  11. 1
      engine/common/imagelib/img_png.c
  12. 25
      engine/common/lib_common.c
  13. 4
      engine/platform/win32/lib_win.c
  14. 16
      engine/ref_api.h
  15. 2
      engine/wscript
  16. 2759
      filesystem/filesystem.c
  17. 202
      filesystem/filesystem.h
  18. 204
      filesystem/filesystem_internal.h
  19. 80
      filesystem/fscallback.h
  20. 394
      filesystem/pak.c
  21. 634
      filesystem/wad.c
  22. 17
      filesystem/wscript
  23. 678
      filesystem/zip.c
  24. 17
      public/crtlib.c
  25. 1
      public/crtlib.h
  26. 0
      public/miniz.h
  27. 2
      ref_gl/gl_alias.c
  28. 4
      ref_gl/gl_backend.c
  29. 2
      ref_gl/gl_rmisc.c
  30. 2
      ref_gl/gl_studio.c
  31. 4
      ref_gl/gl_warp.c
  32. 1
      ref_gl/wscript
  33. 2
      ref_soft/r_studio.c
  34. 1
      ref_soft/wscript
  35. 1
      wscript

36
engine/client/cl_gameui.c

@ -980,7 +980,7 @@ pfnGetGamesList
*/ */
static GAMEINFO ** GAME_EXPORT pfnGetGamesList( int *numGames ) static GAMEINFO ** GAME_EXPORT pfnGetGamesList( int *numGames )
{ {
if( numGames ) *numGames = SI.numgames; if( numGames ) *numGames = FI->numgames;
return gameui.modsInfo; return gameui.modsInfo;
} }
@ -1117,6 +1117,30 @@ static char *pfnParseFile( char *buf, char *token )
return COM_ParseFile( buf, token, INT_MAX ); return COM_ParseFile( buf, token, INT_MAX );
} }
/*
=============
pfnFileExists
legacy wrapper
=============
*/
static int pfnFileExists( const char *path, int gamedironly )
{
return FS_FileExists( path, gamedironly );
}
/*
=============
pfnDelete
legacy wrapper
=============
*/
static int pfnDelete( const char *path )
{
return FS_Delete( path );
}
// engine callbacks // engine callbacks
static ui_enginefuncs_t gEngfuncs = static ui_enginefuncs_t gEngfuncs =
{ {
@ -1163,7 +1187,7 @@ static ui_enginefuncs_t gEngfuncs =
pfnRenderScene, pfnRenderScene,
pfnAddEntity, pfnAddEntity,
Host_Error, Host_Error,
FS_FileExists, pfnFileExists,
pfnGetGameDir, pfnGetGameDir,
Cmd_CheckMapsList, Cmd_CheckMapsList,
CL_Active, CL_Active,
@ -1202,7 +1226,7 @@ static ui_enginefuncs_t gEngfuncs =
COM_CompareFileTime, COM_CompareFileTime,
VID_GetModeString, VID_GetModeString,
(void*)COM_SaveFile, (void*)COM_SaveFile,
(void*)FS_Delete pfnDelete
}; };
static void pfnEnableTextInput( int enable ) static void pfnEnableTextInput( int enable )
@ -1358,13 +1382,13 @@ qboolean UI_LoadProgs( void )
Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY ); Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY );
// setup gameinfo // setup gameinfo
for( i = 0; i < SI.numgames; i++ ) for( i = 0; i < FI->numgames; i++ )
{ {
gameui.modsInfo[i] = Mem_Calloc( gameui.mempool, sizeof( GAMEINFO )); gameui.modsInfo[i] = Mem_Calloc( gameui.mempool, sizeof( GAMEINFO ));
UI_ConvertGameInfo( gameui.modsInfo[i], SI.games[i] ); UI_ConvertGameInfo( gameui.modsInfo[i], FI->games[i] );
} }
UI_ConvertGameInfo( &gameui.gameInfo, SI.GameInfo ); // current gameinfo UI_ConvertGameInfo( &gameui.gameInfo, FI->GameInfo ); // current gameinfo
// setup globals // setup globals
gameui.globals->developer = host.allow_console; gameui.globals->developer = host.allow_console;

8
engine/client/ref_common.c

@ -334,10 +334,6 @@ static ref_api_t gEngfuncs =
COM_FreeLibrary, COM_FreeLibrary,
COM_GetProcAddress, COM_GetProcAddress,
FS_LoadFile,
FS_FileExists,
FS_AllowDirectPaths,
R_Init_Video_, R_Init_Video_,
R_Free_Video, R_Free_Video,
@ -383,7 +379,9 @@ static ref_api_t gEngfuncs =
pfnDrawNormalTriangles, pfnDrawNormalTriangles,
pfnDrawTransparentTriangles, pfnDrawTransparentTriangles,
&clgame.drawFuncs &clgame.drawFuncs,
&g_fsapi,
}; };
static void R_UnloadProgs( void ) static void R_UnloadProgs( void )

17
engine/common/common.c

@ -585,23 +585,6 @@ void COM_TrimSpace( const char *source, char *dest )
dest[length] = 0; dest[length] = 0;
} }
/*
============
COM_FixSlashes
Changes all '/' characters into '\' characters, in place.
============
*/
void COM_FixSlashes( char *pname )
{
while( *pname )
{
if( *pname == '\\' )
*pname = '/';
pname++;
}
}
/* /*
================== ==================
COM_Nibble COM_Nibble

142
engine/common/common.h

@ -76,13 +76,6 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc
#define HACKS_RELATED_HLMODS // some HL-mods works differently under Xash and can't be fixed without some hacks at least at current time #define HACKS_RELATED_HLMODS // some HL-mods works differently under Xash and can't be fixed without some hacks at least at current time
typedef struct
{
int numfilenames;
char **filenames;
char *filenamesbuffer;
} search_t;
enum enum
{ {
DEV_NONE = 0, DEV_NONE = 0,
@ -118,6 +111,8 @@ typedef enum
#include "cvar.h" #include "cvar.h"
#include "con_nprint.h" #include "con_nprint.h"
#include "crclib.h" #include "crclib.h"
#include "ref_api.h"
#include "fscallback.h"
// PERFORMANCE INFO // PERFORMANCE INFO
#define MIN_FPS 20.0f // host minimum fps value for maxfps. #define MIN_FPS 20.0f // host minimum fps value for maxfps.
@ -147,18 +142,6 @@ typedef enum
#define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn #define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn
#endif #endif
// filesystem flags
#define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path
#define FS_NOWRITE_PATH ( 1U << 1 ) // default behavior - last added gamedir set as writedir. This flag disables it
#define FS_GAMEDIR_PATH ( 1U << 2 ) // just a marker for gamedir path
#define FS_CUSTOM_PATH ( 1U << 3 ) // custom directory
#define FS_GAMERODIR_PATH ( 1U << 4 ) // caseinsensitive
#define FS_GAMEDIRONLY_SEARCH_FLAGS ( FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH )
#define GI SI.GameInfo
#define FS_Gamedir() SI.GameInfo->gamefolder
#define FS_Title() SI.GameInfo->title
#define GameState (&host.game) #define GameState (&host.game)
#define FORCE_DRAW_VERSION_TIME 5.0f // draw version for 5 seconds #define FORCE_DRAW_VERSION_TIME 5.0f // draw version for 5 seconds
@ -199,61 +182,6 @@ GAMEINFO stuff
internal shared gameinfo structure (readonly for engine parts) internal shared gameinfo structure (readonly for engine parts)
======================================================================== ========================================================================
*/ */
typedef struct gameinfo_s
{
// filesystem info
char gamefolder[MAX_QPATH]; // used for change game '-game x'
char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life)
char falldir[MAX_QPATH]; // used as second basedir
char startmap[MAX_QPATH];// map to start singleplayer game
char trainmap[MAX_QPATH];// map to start hazard course (if specified)
char title[64]; // Game Main Title
float version; // game version (optional)
// .dll pathes
char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls"
char game_dll[MAX_QPATH]; // custom path for game.dll
// .ico path
char iconpath[MAX_QPATH]; // "game.ico" by default
// about mod info
string game_url; // link to a developer's site
string update_url; // link to updates page
char type[MAX_QPATH]; // single, toolkit, multiplayer etc
char date[MAX_QPATH];
size_t size;
int gamemode;
qboolean secure; // prevent to console acess
qboolean nomodels; // don't let player to choose model (use player.mdl always)
qboolean noskills; // disable skill menu selection
qboolean render_picbutton_text; // use font renderer to render WON buttons
char sp_entity[32]; // e.g. info_player_start
char mp_entity[32]; // e.g. info_player_deathmatch
char mp_filter[32]; // filtering multiplayer-maps
char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds
int max_edicts; // min edicts is 600, max edicts is 8196
int max_tents; // min temp ents is 300, max is 2048
int max_beams; // min beams is 64, max beams is 512
int max_particles; // min particles is 4096, max particles is 32768
char game_dll_linux[64]; // custom path for game.dll
char game_dll_osx[64]; // custom path for game.dll
qboolean added;
} gameinfo_t;
typedef enum
{
GAME_NORMAL,
GAME_SINGLEPLAYER_ONLY,
GAME_MULTIPLAYER_ONLY
} gametype_t;
typedef struct sysinfo_s typedef struct sysinfo_s
{ {
string exeName; // exe.filename string exeName; // exe.filename
@ -261,9 +189,6 @@ typedef struct sysinfo_s
string basedirName; // name of base directory string basedirName; // name of base directory
string gamedll; string gamedll;
string clientlib; string clientlib;
gameinfo_t *GameInfo; // current GameInfo
gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start)
int numgames;
} sysinfo_t; } sysinfo_t;
typedef enum typedef enum
@ -470,6 +395,14 @@ extern sysinfo_t SI;
typedef void (*xcommand_t)( void ); typedef void (*xcommand_t)( void );
//
// filesystem_engine.c
//
#define FILESYSTEM_STDIO_DLL "filesystem_stdio." OS_LIB_EXT
qboolean FS_LoadProgs( const char *name );
void FS_Init( void );
void FS_Shutdown( void );
// //
// cmd.c // cmd.c
// //
@ -529,56 +462,6 @@ void Mem_PrintStats( void );
#define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem ) #define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem )
#define Mem_Check() _Mem_Check( __FILE__, __LINE__ ) #define Mem_Check() _Mem_Check( __FILE__, __LINE__ )
//
// filesystem.c
//
void FS_Init( void );
void FS_Path( void );
void FS_Rescan( void );
void FS_Shutdown( void );
void FS_ClearSearchPath( void );
void FS_AllowDirectPaths( qboolean enable );
void FS_AddGameDirectory( const char *dir, uint flags );
void FS_AddGameHierarchy( const char *dir, uint flags );
void FS_LoadGameInfo( const char *rootfolder );
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type );
void W_Close( wfile_t *wad );
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
qboolean CRC32_File( dword *crcvalue, const char *filename );
qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] );
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr );
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
qboolean COM_ParseVector( char **pfile, float *v, size_t size );
void COM_NormalizeAngles( vec3_t angles );
int COM_FileSize( const char *filename );
void COM_FixSlashes( char *pname );
void COM_FreeFile( void *buffer );
int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare );
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly );
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly );
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
int FS_VPrintf( file_t *file, const char *format, va_list ap );
int FS_Seek( file_t *file, fs_offset_t offset, int whence );
int FS_Gets( file_t *file, byte *string, size_t bufsize );
int FS_Printf( file_t *file, const char *format, ... ) _format( 2 );
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
int FS_FileTime( const char *filename, qboolean gamedironly );
int FS_Print( file_t *file, const char *msg );
qboolean FS_Rename( const char *oldname, const char *newname );
int FS_FileExists( const char *filename, int gamedironly );
int FS_SetCurrentDirectory( const char *path );
qboolean FS_SysFileExists( const char *path, qboolean casesensitive );
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
qboolean FS_Delete( const char *path );
int FS_UnGetc( file_t *file, byte c );
fs_offset_t FS_Tell( file_t *file );
qboolean FS_Eof( file_t *file );
int FS_Close( file_t *file );
int FS_Getc( file_t *file );
fs_offset_t FS_FileLength( file_t *f );
// //
// imagelib // imagelib
// //
@ -942,6 +825,11 @@ void UI_SetActiveMenu( qboolean fActive );
void UI_ShowConnectionWarning( void ); void UI_ShowConnectionWarning( void );
void Cmd_Null_f( void ); void Cmd_Null_f( void );
void Rcon_Print( const char *pMsg ); void Rcon_Print( const char *pMsg );
qboolean COM_ParseVector( char **pfile, float *v, size_t size );
void COM_NormalizeAngles( vec3_t angles );
int COM_FileSize( const char *filename );
void COM_FreeFile( void *buffer );
int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare );
// soundlib shared exports // soundlib shared exports
qboolean S_Init( void ); qboolean S_Init( void );

6
engine/common/con_utils.c

@ -761,10 +761,10 @@ qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
// compare gamelist with current keyword // compare gamelist with current keyword
len = Q_strlen( s ); len = Q_strlen( s );
for( i = 0, numgamedirs = 0; i < SI.numgames; i++ ) for( i = 0, numgamedirs = 0; i < FI->numgames; i++ )
{ {
if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, len)) if(( *s == '*' ) || !Q_strnicmp( FI->games[i]->gamefolder, s, len))
Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder ); Q_strcpy( gamedirs[numgamedirs++], FI->games[i]->gamefolder );
} }
if( !numgamedirs ) return false; if( !numgamedirs ) return false;

200
engine/common/filesystem.h

@ -1,200 +0,0 @@
/*
filesystem.h - engine FS
Copyright (C) 2007 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
/*
========================================================================
PAK FILES
The .pak files are just a linear collapse of a directory tree
========================================================================
*/
// header
#define IDPACKV1HEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') // little-endian "PACK"
#define MAX_FILES_IN_PACK 65536 // pak
typedef struct
{
int ident;
int dirofs;
int dirlen;
} dpackheader_t;
typedef struct
{
char name[56]; // total 64 bytes
int filepos;
int filelen;
} dpackfile_t;
/*
========================================================================
.WAD archive format (WhereAllData - WAD)
List of compressed files, that can be identify only by TYPE_*
<format>
header: dwadinfo_t[dwadinfo_t]
file_1: byte[dwadinfo_t[num]->disksize]
file_2: byte[dwadinfo_t[num]->disksize]
file_3: byte[dwadinfo_t[num]->disksize]
...
file_n: byte[dwadinfo_t[num]->disksize]
infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
#define WAD3_NAMELEN 16
#define HINT_NAMELEN 5 // e.g. _mask, _norm
#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount
#include "const.h"
typedef struct
{
int ident; // should be WAD3
int numlumps; // num files
int infotableofs; // LUT offset
} dwadinfo_t;
typedef struct
{
int filepos; // file offset in WAD
int disksize; // compressed or uncompressed
int size; // uncompressed
signed char type; // TYP_*
signed char attribs; // file attribs
signed char pad0;
signed char pad1;
char name[WAD3_NAMELEN]; // must be null terminated
} dlumpinfo_t;
#include "custom.h"
/*
========================================================================
.HPK archive format (Hash PAK - HPK)
List of compressed files, that can be identify only by TYPE_*
<format>
header: dwadinfo_t[dwadinfo_t]
file_1: byte[dwadinfo_t[num]->disksize]
file_2: byte[dwadinfo_t[num]->disksize]
file_3: byte[dwadinfo_t[num]->disksize]
...
file_n: byte[dwadinfo_t[num]->disksize]
infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK"
#define IDHPAK_VERSION 1
typedef struct
{
int ident; // should be equal HPAK
int version;
int infotableofs;
} hpak_header_t;
typedef struct
{
resource_t resource;
int filepos;
int disksize;
} hpak_lump_t;
typedef struct
{
int count;
hpak_lump_t *entries; // variable sized.
} hpak_info_t;
#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_CDF ((0x02<<24)+(0x01<<16)+('K'<<8)+'P')
#define ZIP_HEADER_EOCD ((0x06<<24)+(0x05<<16)+('K'<<8)+'P')
#define ZIP_COMPRESSION_NO_COMPRESSION 0
#define ZIP_COMPRESSION_DEFLATED 8
#define ZIP_ZIP64 0xffffffff
#pragma pack( push, 1 )
typedef struct zip_header_s
{
unsigned int signature; // little endian ZIP_HEADER
unsigned short version; // version of pkzip need to unpack
unsigned short flags; // flags (16 bits == 16 flags)
unsigned short compression_flags; // compression flags (bits)
unsigned int dos_date; // file modification time and file modification date
unsigned int crc32; //crc32
unsigned int compressed_size;
unsigned int uncompressed_size;
unsigned short filename_len;
unsigned short extrafield_len;
} zip_header_t;
/*
in zip64 comp and uncompr size == 0xffffffff remeber this
compressed and uncompress filesize stored in extra field
*/
typedef struct zip_header_extra_s
{
unsigned int signature; // ZIP_HEADER_SPANNED
unsigned int crc32;
unsigned int compressed_size;
unsigned int uncompressed_size;
} zip_header_extra_t;
typedef struct zip_cdf_header_s
{
unsigned int signature;
unsigned short version;
unsigned short version_need;
unsigned short generalPurposeBitFlag;
unsigned short flags;
unsigned short modification_time;
unsigned short modification_date;
unsigned int crc32;
unsigned int compressed_size;
unsigned int uncompressed_size;
unsigned short filename_len;
unsigned short extrafield_len;
unsigned short file_commentary_len;
unsigned short disk_start;
unsigned short internal_attr;
unsigned int external_attr;
unsigned int local_header_offset;
} zip_cdf_header_t;
typedef struct zip_header_eocd_s
{
unsigned short disk_number;
unsigned short start_disk_number;
unsigned short number_central_directory_record;
unsigned short total_central_directory_record;
unsigned int size_of_central_directory;
unsigned int central_directory_offset;
unsigned short commentary_len;
} zip_header_eocd_t;
#pragma pack( pop )
#endif//FILESYSTEM_H

146
engine/common/filesystem_engine.c

@ -0,0 +1,146 @@
/*
filesystem.c - game filesystem based on DP fs
Copyright (C) 2007 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "library.h"
fs_api_t g_fsapi;
fs_globals_t *FI;
static HINSTANCE fs_hInstance;
static void FS_Rescan_f( void )
{
FS_Rescan();
}
static void FS_ClearPaths_f( void )
{
FS_ClearSearchPath();
}
static void FS_Path_f_( void )
{
FS_Path_f();
}
static fs_interface_t fs_memfuncs =
{
Con_Printf,
Con_DPrintf,
Con_Reportf,
Sys_Error,
_Mem_AllocPool,
_Mem_FreePool,
_Mem_Alloc,
_Mem_Realloc,
_Mem_Free,
};
static void FS_UnloadProgs( void )
{
COM_FreeLibrary( fs_hInstance );
fs_hInstance = 0;
}
qboolean FS_LoadProgs( const char *name )
{
FSAPI GetFSAPI;
fs_hInstance = COM_LoadLibrary( name, false, true );
if( !fs_hInstance )
{
Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() );
return false;
}
if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API )))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name );
return false;
}
if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ))
{
FS_UnloadProgs();
Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" );
return false;
}
Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" );
return true;
}
/*
================
FS_Init
================
*/
void FS_Init( void )
{
qboolean hasBaseDir = false;
qboolean hasGameDir = false;
qboolean caseinsensitive = true;
int i;
string gamedir;
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
#if !XASH_WIN32
if( Sys_CheckParm( "-casesensitive" ) )
caseinsensitive = false;
#endif
if( !Sys_GetParmFromCmdLine( "-game", gamedir ))
Q_strncpy( gamedir, SI.basedirName, sizeof( gamedir )); // gamedir == basedir
if( !FS_InitStdio( caseinsensitive, host.rootdir, SI.basedirName, gamedir, host.rodir ))
{
Host_Error( "Can't init filesystem_stdio!\n" );
return;
}
if( !Sys_GetParmFromCmdLine( "-dll", SI.gamedll ))
SI.gamedll[0] = 0;
if( !Sys_GetParmFromCmdLine( "-clientlib", SI.clientlib ))
SI.clientlib[0] = 0;
}
/*
================
FS_Shutdown
================
*/
void FS_Shutdown( void )
{
int i;
FS_ShutdownStdio();
memset( &SI, 0, sizeof( sysinfo_t ));
FS_UnloadProgs();
}

29
engine/common/host.c

@ -328,13 +328,13 @@ void Host_ChangeGame_f( void )
} }
// validate gamedir // validate gamedir
for( i = 0; i < SI.numgames; i++ ) for( i = 0; i < FI->numgames; i++ )
{ {
if( !Q_stricmp( SI.games[i]->gamefolder, Cmd_Argv( 1 ))) if( !Q_stricmp( FI->games[i]->gamefolder, Cmd_Argv( 1 )))
break; break;
} }
if( i == SI.numgames ) if( i == FI->numgames )
{ {
Con_Printf( "%s not exist\n", Cmd_Argv( 1 )); Con_Printf( "%s not exist\n", Cmd_Argv( 1 ));
} }
@ -345,7 +345,7 @@ void Host_ChangeGame_f( void )
else else
{ {
const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 )); const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 ));
const char *arg2 = va( "change game to '%s'", SI.games[i]->title ); const char *arg2 = va( "change game to '%s'", FI->games[i]->title );
Host_NewInstance( arg1, arg2 ); Host_NewInstance( arg1, arg2 );
} }
@ -1027,18 +1027,27 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
if( len && host.rodir[len - 1] == '/' ) if( len && host.rodir[len - 1] == '/' )
host.rodir[len - 1] = 0; host.rodir[len - 1] = 0;
if( !COM_CheckStringEmpty( host.rootdir ) || FS_SetCurrentDirectory( host.rootdir ) != 0 ) if( !COM_CheckStringEmpty( host.rootdir ))
{
Sys_Error( "Changing working directory failed (empty working directory)\n" );
return;
}
FS_LoadProgs( FILESYSTEM_STDIO_DLL );
if( FS_SetCurrentDirectory( host.rootdir ) != 0 )
Con_Reportf( "%s is working directory now\n", host.rootdir ); Con_Reportf( "%s is working directory now\n", host.rootdir );
else else
Sys_Error( "Changing working directory to %s failed.\n", host.rootdir ); Sys_Error( "Changing working directory to %s failed.\n", host.rootdir );
FS_Init();
Sys_InitLog(); Sys_InitLog();
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" ); Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" ); Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" ); Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" );
FS_Init();
Image_Init(); Image_Init();
Sound_Init(); Sound_Init();
@ -1048,8 +1057,16 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
#endif #endif
FS_LoadGameInfo( NULL ); FS_LoadGameInfo( NULL );
if( FS_FileExists( va( "%s.rc", SI.basedirName ), false ))
Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc
else Q_strncpy( SI.rcName, SI.exeName, sizeof( SI.rcName )); // e.g. quake.rc
Q_strncpy( host.gamefolder, GI->gamefolder, sizeof( host.gamefolder )); Q_strncpy( host.gamefolder, GI->gamefolder, sizeof( host.gamefolder ));
Image_CheckPaletteQ1 ();
Host_InitDecals (); // reload decals
// DEPRECATED: by FWGS fork // DEPRECATED: by FWGS fork
#if 0 #if 0
if( GI->secure ) if( GI->secure )

4
engine/common/hpak.c

@ -14,7 +14,7 @@ GNU General Public License for more details.
*/ */
#include "common.h" #include "common.h"
#include "filesystem.h" #include "hpak.h"
#define HPAK_MAX_ENTRIES 0x8000 #define HPAK_MAX_ENTRIES 0x8000
#define HPAK_MIN_SIZE (1 * 1024) #define HPAK_MIN_SIZE (1 * 1024)
@ -402,7 +402,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet )
FS_Seek( f, hdr.infotableofs, SEEK_SET ); FS_Seek( f, hdr.infotableofs, SEEK_SET );
FS_Read( f, &num_lumps, sizeof( num_lumps )); FS_Read( f, &num_lumps, sizeof( num_lumps ));
if( num_lumps < 1 || num_lumps > MAX_FILES_IN_WAD ) if( num_lumps < 1 || num_lumps > HPAK_MAX_ENTRIES )
{ {
Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps ); Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps );
FS_Close( f ); FS_Close( f );

60
engine/common/hpak.h

@ -0,0 +1,60 @@
/*
hpak.c - custom user package to send other clients
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef HPAK_H
#define HPAK_H
#include "custom.h"
/*
========================================================================
.HPK archive format (Hash PAK - HPK)
List of compressed files, that can be identify only by TYPE_*
<format>
header: dwadinfo_t[dwadinfo_t]
file_1: byte[dwadinfo_t[num]->disksize]
file_2: byte[dwadinfo_t[num]->disksize]
file_3: byte[dwadinfo_t[num]->disksize]
...
file_n: byte[dwadinfo_t[num]->disksize]
infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK"
#define IDHPAK_VERSION 1
typedef struct
{
int ident; // should be equal HPAK
int version;
int infotableofs;
} hpak_header_t;
typedef struct
{
resource_t resource;
int filepos;
int disksize;
} hpak_lump_t;
typedef struct
{
int count;
hpak_lump_t *entries; // variable sized.
} hpak_info_t;
#endif // HPAK_H

1
engine/common/imagelib/img_png.c

@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
*/ */
#define MINIZ_HEADER_FILE_ONLY
#include "miniz.h" #include "miniz.h"
#include "imagelib.h" #include "imagelib.h"
#include "xash3d_mathlib.h" #include "xash3d_mathlib.h"

25
engine/common/lib_common.c

@ -86,6 +86,31 @@ const char *COM_OffsetNameForFunction( void *function )
return sname; return sname;
} }
dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath )
{
dll_user_t *p;
fs_dllinfo_t dllInfo;
// no fs loaded, can't search
if( !g_fsapi.FindLibrary )
return NULL;
// fs can't find library
if( !g_fsapi.FindLibrary( dllname, directpath, &dllInfo ))
return NULL;
// NOTE: for libraries we not fail even if search is NULL
// let the OS find library himself
p = Mem_Calloc( host.mempool, sizeof( dll_user_t ));
Q_strncpy( p->shortPath, dllInfo.shortPath, sizeof( p->shortPath ));
Q_strncpy( p->fullPath, dllInfo.fullPath, sizeof( p->fullPath ));
Q_strncpy( p->dllName, dllname, sizeof( p->dllName ));
p->custom_loader = dllInfo.custom_loader;
p->encrypted = dllInfo.encrypted;
return p;
}
/* /*
============================================================================= =============================================================================

4
engine/platform/win32/lib_win.c

@ -391,7 +391,7 @@ qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname
dll_user_t *hInst; dll_user_t *hInst;
qboolean ret = FALSE; qboolean ret = FALSE;
hInst = FS_FindLibrary( name, directpath ); hInst = COM_FindLibrary( name, directpath );
if ( !hInst ) return FALSE; if ( !hInst ) return FALSE;
data = FS_LoadFile( name, NULL, false ); data = FS_LoadFile( name, NULL, false );
@ -439,7 +439,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d
COM_ResetLibraryError(); COM_ResetLibraryError();
hInst = FS_FindLibrary( dllname, directpath ); hInst = COM_FindLibrary( dllname, directpath );
if( !hInst ) if( !hInst )
{ {
COM_PushLibraryError( va( "Failed to find library %s", dllname ) ); COM_PushLibraryError( va( "Failed to find library %s", dllname ) );

16
engine/ref_api.h

@ -27,8 +27,12 @@ GNU General Public License for more details.
#include "studio.h" #include "studio.h"
#include "r_efx.h" #include "r_efx.h"
#include "com_image.h" #include "com_image.h"
#include "filesystem.h"
#define REF_API_VERSION 1 // RefAPI changelog:
// 1. Initial release
// 2. FS functions are removed, instead we have full fs_api_t
#define REF_API_VERSION 2
#define TF_SKY (TF_SKYSIDE|TF_NOMIPMAP) #define TF_SKY (TF_SKYSIDE|TF_NOMIPMAP)
@ -367,13 +371,6 @@ typedef struct ref_api_s
void (*COM_FreeLibrary)( void *handle ); void (*COM_FreeLibrary)( void *handle );
void *(*COM_GetProcAddress)( void *handle, const char *name ); void *(*COM_GetProcAddress)( void *handle, const char *name );
// filesystem
byte* (*COM_LoadFile)( const char *path, fs_offset_t *pLength, qboolean gamedironly );
// use Mem_Free instead
// void (*COM_FreeFile)( void *buffer );
int (*FS_FileExists)( const char *filename, int gamedironly );
void (*FS_AllowDirectPaths)( qboolean enable );
// video init // video init
// try to create window // try to create window
// will call GL_SetupAttributes in case of REF_GL // will call GL_SetupAttributes in case of REF_GL
@ -430,6 +427,9 @@ typedef struct ref_api_s
void (*pfnDrawNormalTriangles)( void ); void (*pfnDrawNormalTriangles)( void );
void (*pfnDrawTransparentTriangles)( void ); void (*pfnDrawTransparentTriangles)( void );
render_interface_t *drawFuncs; render_interface_t *drawFuncs;
// filesystem exports
fs_api_t *fsapi;
} ref_api_t; } ref_api_t;
struct mip_s; struct mip_s;

2
engine/wscript

@ -166,7 +166,7 @@ def build(bld):
'client/vgui/*.c', 'client/vgui/*.c',
'client/avi/*.c']) 'client/avi/*.c'])
includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../pm_shared' ] includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ]
if bld.env.SINGLE_BINARY: if bld.env.SINGLE_BINARY:
install_path = bld.env.BINDIR install_path = bld.env.BINDIR

2759
engine/common/filesystem.c → filesystem/filesystem.c

File diff suppressed because it is too large Load Diff

202
filesystem/filesystem.h

@ -0,0 +1,202 @@
/*
filesystem.h - engine FS
Copyright (C) 2007 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef FILESYSTEM_H
#define FILESYSTEM_H
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include "xash3d_types.h"
#include "const.h"
#include "com_model.h"
#ifdef __cplusplus
extern "C"
{
#endif // __cplusplus
#define FS_API_VERSION 1 // not stable yet!
// search path flags
enum
{
FS_STATIC_PATH = BIT( 0 ), // FS_ClearSearchPath will be ignore this path
FS_NOWRITE_PATH = BIT( 1 ), // default behavior - last added gamedir set as writedir. This flag disables it
FS_GAMEDIR_PATH = BIT( 2 ), // just a marker for gamedir path
FS_CUSTOM_PATH = BIT( 3 ), // gamedir but with custom/mod data
FS_GAMERODIR_PATH = BIT( 4 ), // gamedir but read-only
FS_GAMEDIRONLY_SEARCH_FLAGS = FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH
};
typedef struct
{
int numfilenames;
char **filenames;
char *filenamesbuffer;
} search_t;
typedef struct gameinfo_s
{
// filesystem info
char gamefolder[MAX_QPATH]; // used for change game '-game x'
char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life)
char falldir[MAX_QPATH]; // used as second basedir
char startmap[MAX_QPATH];// map to start singleplayer game
char trainmap[MAX_QPATH];// map to start hazard course (if specified)
char title[64]; // Game Main Title
float version; // game version (optional)
// .dll pathes
char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls"
char game_dll[MAX_QPATH]; // custom path for game.dll
// .ico path
char iconpath[MAX_QPATH]; // "game.ico" by default
// about mod info
string game_url; // link to a developer's site
string update_url; // link to updates page
char type[MAX_QPATH]; // single, toolkit, multiplayer etc
char date[MAX_QPATH];
size_t size;
int gamemode;
qboolean secure; // prevent to console acess
qboolean nomodels; // don't let player to choose model (use player.mdl always)
qboolean noskills; // disable skill menu selection
qboolean render_picbutton_text; // use font renderer to render WON buttons
char sp_entity[32]; // e.g. info_player_start
char mp_entity[32]; // e.g. info_player_deathmatch
char mp_filter[32]; // filtering multiplayer-maps
char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds
int max_edicts; // min edicts is 600, max edicts is 8196
int max_tents; // min temp ents is 300, max is 2048
int max_beams; // min beams is 64, max beams is 512
int max_particles; // min particles is 4096, max particles is 32768
char game_dll_linux[64]; // custom path for game.dll
char game_dll_osx[64]; // custom path for game.dll
qboolean added;
} gameinfo_t;
typedef enum
{
GAME_NORMAL,
GAME_SINGLEPLAYER_ONLY,
GAME_MULTIPLAYER_ONLY
} gametype_t;
typedef struct fs_dllinfo_t
{
string fullPath;
string shortPath;
qboolean encrypted;
qboolean custom_loader;
} fs_dllinfo_t;
typedef struct fs_globals_t
{
gameinfo_t *GameInfo; // current GameInfo
gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start)
int numgames;
} fs_globals_t;
typedef struct fs_api_t
{
qboolean (*InitStdio)( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir );
void (*ShutdownStdio)( void );
// search path utils
void (*Rescan)( void );
void (*ClearSearchPath)( void );
void (*AllowDirectPaths)( qboolean enable );
void (*AddGameDirectory)( const char *dir, uint flags );
void (*AddGameHierarchy)( const char *dir, uint flags );
search_t *(*Search)( const char *pattern, int caseinsensitive, int gamedironly );
int (*SetCurrentDirectory)( const char *path );
qboolean (*FindLibrary)( const char *dllname, qboolean directpath, fs_dllinfo_t *dllinfo );
void (*Path_f)( void );
// gameinfo utils
void (*LoadGameInfo)( const char *rootfolder );
// file ops
file_t *(*Open)( const char *filepath, const char *mode, qboolean gamedironly );
fs_offset_t (*Write)( file_t *file, const void *data, size_t datasize );
fs_offset_t (*Read)( file_t *file, void *buffer, size_t buffersize );
int (*Seek)( file_t *file, fs_offset_t offset, int whence );
fs_offset_t (*Tell)( file_t *file );
qboolean (*Eof)( file_t *file );
int (*Flush)( file_t *file );
int (*Close)( file_t *file );
int (*Gets)( file_t *file, byte *string, size_t bufsize );
int (*UnGetc)( file_t *file, byte c );
int (*Getc)( file_t *file );
int (*VPrintf)( file_t *file, const char *format, va_list ap );
int (*Printf)( file_t *file, const char *format, ... ) _format( 2 );
int (*Print)( file_t *file, const char *msg );
fs_offset_t (*FileLength)( file_t *f );
qboolean (*FileCopy)( file_t *pOutput, file_t *pInput, int fileSize );
// file buffer ops
byte *(*LoadFile)( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
byte *(*LoadDirectFile)( const char *path, fs_offset_t *filesizeptr );
qboolean (*WriteFile)( const char *filename, const void *data, fs_offset_t len );
// file hashing
qboolean (*CRC32_File)( dword *crcvalue, const char *filename );
qboolean (*MD5_HashFile)( byte digest[16], const char *pszFileName, uint seed[4] );
// filesystem ops
int (*FileExists)( const char *filename, int gamedironly );
int (*FileTime)( const char *filename, qboolean gamedironly );
fs_offset_t (*FileSize)( const char *filename, qboolean gamedironly );
qboolean (*Rename)( const char *oldname, const char *newname );
qboolean (*Delete)( const char *path );
qboolean (*SysFileExists)( const char *path, qboolean casesensitive );
const char *(*GetDiskPath)( const char *name, qboolean gamedironly );
} fs_api_t;
typedef struct fs_interface_t
{
// logging
void (*_Con_Printf)( const char *fmt, ... ) _format( 1 ); // typical console allowed messages
void (*_Con_DPrintf)( const char *fmt, ... ) _format( 1 ); // -dev 1
void (*_Con_Reportf)( const char *fmt, ... ) _format( 1 ); // -dev 2
void (*_Sys_Error)( const char *fmt, ... ) _format( 1 );
// memory
poolhandle_t (*_Mem_AllocPool)( const char *name, const char *filename, int fileline );
void (*_Mem_FreePool)( poolhandle_t *poolptr, const char *filename, int fileline );
void *(*_Mem_Alloc)( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline );
void *(*_Mem_Realloc)( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline );
void (*_Mem_Free)( void *data, const char *filename, int fileline );
} fs_interface_t;
typedef int (*FSAPI)( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *interface );
#define GET_FS_API "GetFSAPI"
#ifdef __cplusplus
}
#endif // __cplusplus
#endif//FILESYSTEM_H

204
filesystem/filesystem_internal.h

@ -0,0 +1,204 @@
/*
filesystem.h - engine FS
Copyright (C) 2007 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef FILESYSTEM_INTERNAL_H
#define FILESYSTEM_INTERNAL_H
#include "xash3d_types.h"
#include "filesystem.h"
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct zip_s zip_t;
typedef struct pack_s pack_t;
typedef struct wfile_s wfile_t;
#define FILE_BUFF_SIZE (2048)
struct file_s
{
int handle; // file descriptor
int ungetc; // single stored character from ungetc, cleared to EOF when read
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 offset; // offset into the package (0 if external file)
time_t filetime; // pak, wad or real filetime
// 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
};
enum
{
SEARCHPATH_PLAIN = 0,
SEARCHPATH_PAK,
SEARCHPATH_WAD,
SEARCHPATH_ZIP
};
typedef struct stringlist_s
{
// maxstrings changes as needed, causing reallocation of strings[] array
int maxstrings;
int numstrings;
char **strings;
} stringlist_t;
typedef struct searchpath_s
{
string filename;
int type;
int flags;
union
{
pack_t *pack;
wfile_t *wad;
zip_t *zip;
};
struct searchpath_s *next;
} searchpath_t;
extern searchpath_t *fs_searchpaths;
extern poolhandle_t fs_mempool;
extern fs_interface_t g_engfuncs;
extern qboolean fs_ext_path;
extern char fs_rodir[MAX_SYSPATH];
extern char fs_rootdir[MAX_SYSPATH];
#define Mem_Malloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, false, __FILE__, __LINE__ )
#define Mem_Calloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, true, __FILE__, __LINE__ )
#define Mem_Realloc( pool, ptr, size ) g_engfuncs._Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ )
#define Mem_Free( mem ) g_engfuncs._Mem_Free( mem, __FILE__, __LINE__ )
#define Mem_AllocPool( name ) g_engfuncs._Mem_AllocPool( name, __FILE__, __LINE__ )
#define Mem_FreePool( pool ) g_engfuncs._Mem_FreePool( pool, __FILE__, __LINE__ )
#define Con_Printf (*g_engfuncs._Con_Printf)
#define Con_DPrintf (*g_engfuncs._Con_DPrintf)
#define Con_Reportf (*g_engfuncs._Con_Reportf)
#define Sys_Error (*g_engfuncs._Sys_Error)
//
// filesystem.c
//
qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir );
void FS_ShutdownStdio( void );
// search path utils
void FS_Rescan( void );
void FS_ClearSearchPath( void );
void FS_AllowDirectPaths( qboolean enable );
void FS_AddGameDirectory( const char *dir, uint flags );
void FS_AddGameHierarchy( const char *dir, uint flags );
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly );
int FS_SetCurrentDirectory( const char *path );
void FS_Path_f( void );
// gameinfo utils
void FS_LoadGameInfo( const char *rootfolder );
// file ops
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly );
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize );
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize );
int FS_Seek( file_t *file, fs_offset_t offset, int whence );
fs_offset_t FS_Tell( file_t *file );
qboolean FS_Eof( file_t *file );
int FS_Flush( file_t *file );
int FS_Close( file_t *file );
int FS_Gets( file_t *file, byte *string, size_t bufsize );
int FS_UnGetc( file_t *file, byte c );
int FS_Getc( file_t *file );
int FS_VPrintf( file_t *file, const char *format, va_list ap );
int FS_Printf( file_t *file, const char *format, ... ) _format( 2 );
int FS_Print( file_t *file, const char *msg );
fs_offset_t FS_FileLength( file_t *f );
qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize );
// file buffer ops
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly );
byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr );
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len );
// file hashing
qboolean CRC32_File( dword *crcvalue, const char *filename );
qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] );
// filesystem ops
int FS_FileExists( const char *filename, int gamedironly );
int FS_FileTime( const char *filename, qboolean gamedironly );
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly );
qboolean FS_Rename( const char *oldname, const char *newname );
qboolean FS_Delete( const char *path );
qboolean FS_SysFileExists( const char *path, qboolean casesensitive );
const char *FS_GetDiskPath( const char *name, qboolean gamedironly );
void stringlistappend( stringlist_t *list, char *text );
void FS_CreatePath( char *path );
qboolean FS_SysFolderExists( const char *path );
file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly );
int FS_SysFileTime( const char *filename );
file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len );
file_t *FS_SysOpen( const char *filepath, const char *mode );
const char *FS_FixFileCase( const char *path );
searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly );
//
// pak.c
//
int FS_FileTimePAK( pack_t *pack );
int FS_FindFilePAK( pack_t *pack, const char *name );
void FS_PrintPAKInfo( char *dst, size_t size, pack_t *pack );
void FS_ClosePAK( pack_t *pack );
void FS_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern );
file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind );
qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags );
//
// wad.c
//
int FS_FileTimeWAD( wfile_t *wad );
int FS_FindFileWAD( wfile_t *wad, const char *name );
void FS_PrintWADInfo( char *dst, size_t size, wfile_t *wad );
void FS_CloseWAD( wfile_t *wad );
void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern );
byte *FS_LoadWADFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly );
qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags );
//
// zip.c
//
int FS_FileTimeZIP( zip_t *zip );
int FS_FindFileZIP( zip_t *zip, const char *name );
void FS_PrintZIPInfo( char *dst, size_t size, zip_t *zip );
void FS_CloseZIP( zip_t *zip );
void FS_SearchZIP( stringlist_t *list, zip_t *zip, const char *pattern );
byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly );
file_t *FS_OpenZipFile( zip_t *zip, int pack_ind );
qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags );
#ifdef __cplusplus
}
#endif
#endif // FILESYSTEM_INTERNAL_H

80
filesystem/fscallback.h

@ -0,0 +1,80 @@
/*
fscallback.h - common filesystem callbacks
Copyright (C) 2022 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef FSCALLBACK_H
#define FSCALLBACK_H
#include "filesystem.h"
extern fs_api_t g_fsapi;
extern fs_globals_t *FI;
#define GI FI->GameInfo
#define FS_Gamedir() GI->gamefolder
#define FS_Title() GI->title
#define FS_InitStdio (*g_fsapi.InitStdio)
#define FS_ShutdownStdio (*g_fsapi.ShutdownStdio)
// search path utils
#define FS_Rescan (*g_fsapi.Rescan)
#define FS_ClearSearchPath (*g_fsapi.ClearSearchPath)
#define FS_AllowDirectPaths (*g_fsapi.AllowDirectPaths)
#define FS_AddGameDirectory (*g_fsapi.AddGameDirectory)
#define FS_AddGameHierarchy (*g_fsapi.AddGameHierarchy)
#define FS_Search (*g_fsapi.Search)
#define FS_SetCurrentDirectory (*g_fsapi.SetCurrentDirectory)
#define FS_Path_f (*g_fsapi.Path_f)
// gameinfo utils
#define FS_LoadGameInfo (*g_fsapi.LoadGameInfo)
// file ops
#define FS_Open (*g_fsapi.Open)
#define FS_Write (*g_fsapi.Write)
#define FS_Read (*g_fsapi.Read)
#define FS_Seek (*g_fsapi.Seek)
#define FS_Tell (*g_fsapi.Tell)
#define FS_Eof (*g_fsapi.Eof)
#define FS_Flush (*g_fsapi.Flush)
#define FS_Close (*g_fsapi.Close)
#define FS_Gets (*g_fsapi.Gets)
#define FS_UnGetc (*g_fsapi.UnGetc)
#define FS_Getc (*g_fsapi.Getc)
#define FS_VPrintf (*g_fsapi.VPrintf)
#define FS_Printf (*g_fsapi.Printf)
#define FS_Print (*g_fsapi.Print)
#define FS_FileLength (*g_fsapi.FileLength)
#define FS_FileCopy (*g_fsapi.FileCopy)
// file buffer ops
#define FS_LoadFile (*g_fsapi.LoadFile)
#define FS_LoadDirectFile (*g_fsapi.LoadDirectFile)
#define FS_WriteFile (*g_fsapi.WriteFile)
// file hashing
#define CRC32_File (*g_fsapi.CRC32_File)
#define MD5_HashFile (*g_fsapi.MD5_HashFile)
// filesystem ops
#define FS_FileExists (*g_fsapi.FileExists)
#define FS_FileTime (*g_fsapi.FileTime)
#define FS_FileSize (*g_fsapi.FileSize)
#define FS_Rename (*g_fsapi.Rename)
#define FS_Delete (*g_fsapi.Delete)
#define FS_SysFileExists (*g_fsapi.SysFileExists)
#define FS_GetDiskPath (*g_fsapi.GetDiskPath)
#endif // FSCALLBACK_H

394
filesystem/pak.c

@ -0,0 +1,394 @@
/*
pak.c - PAK support for filesystem
Copyright (C) 2007 Uncle Mike
Copyright (C) 2022 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include "port.h"
#include "filesystem_internal.h"
#include "crtlib.h"
#include "common/com_strings.h"
/*
========================================================================
PAK FILES
The .pak files are just a linear collapse of a directory tree
========================================================================
*/
// header
#define IDPACKV1HEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') // little-endian "PACK"
#define MAX_FILES_IN_PACK 65536 // pak
typedef struct
{
int ident;
int dirofs;
int dirlen;
} dpackheader_t;
typedef struct
{
char name[56]; // total 64 bytes
int filepos;
int filelen;
} dpackfile_t;
// PAK errors
#define PAK_LOAD_OK 0
#define PAK_LOAD_COULDNT_OPEN 1
#define PAK_LOAD_BAD_HEADER 2
#define PAK_LOAD_BAD_FOLDERS 3
#define PAK_LOAD_TOO_MANY_FILES 4
#define PAK_LOAD_NO_FILES 5
#define PAK_LOAD_CORRUPTED 6
typedef struct pack_s
{
string filename;
int handle;
int numfiles;
time_t filetime; // common for all packed files
dpackfile_t *files;
} pack_t;
/*
====================
FS_AddFileToPack
Add a file to the list of files contained into a package
====================
*/
static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, fs_offset_t offset, fs_offset_t size )
{
int left, right, middle;
dpackfile_t *pfile;
// look for the slot we should put that file into (binary search)
left = 0;
right = pack->numfiles - 1;
while( left <= right )
{
int diff;
middle = (left + right) / 2;
diff = Q_stricmp( pack->files[middle].name, name );
// If we found the file, there's a problem
if( !diff ) Con_Reportf( S_WARN "package %s contains the file %s several times\n", pack->filename, name );
// If we're too far in the list
if( diff > 0 ) right = middle - 1;
else left = middle + 1;
}
// We have to move the right of the list by one slot to free the one we need
pfile = &pack->files[left];
memmove( pfile + 1, pfile, (pack->numfiles - left) * sizeof( *pfile ));
pack->numfiles++;
Q_strncpy( pfile->name, name, sizeof( pfile->name ));
pfile->filepos = offset;
pfile->filelen = size;
return pfile;
}
/*
=================
FS_LoadPackPAK
Takes an explicit (not game tree related) path to a pak file.
Loads the header and directory, adding the files at the beginning
of the list so they override previous pack files.
=================
*/
static pack_t *FS_LoadPackPAK( const char *packfile, int *error )
{
dpackheader_t header;
int packhandle;
int i, numpackfiles;
pack_t *pack;
dpackfile_t *info;
fs_size_t c;
packhandle = open( packfile, O_RDONLY|O_BINARY );
#if !XASH_WIN32
if( packhandle < 0 )
{
const char *fpackfile = FS_FixFileCase( packfile );
if( fpackfile != packfile )
packhandle = open( fpackfile, O_RDONLY|O_BINARY );
}
#endif
if( packhandle < 0 )
{
Con_Reportf( "%s couldn't open: %s\n", packfile, strerror( errno ));
if( error ) *error = PAK_LOAD_COULDNT_OPEN;
return NULL;
}
c = read( packhandle, (void *)&header, sizeof( header ));
if( c != sizeof( header ) || header.ident != IDPACKV1HEADER )
{
Con_Reportf( "%s is not a packfile. Ignored.\n", packfile );
if( error ) *error = PAK_LOAD_BAD_HEADER;
close( packhandle );
return NULL;
}
if( header.dirlen % sizeof( dpackfile_t ))
{
Con_Reportf( S_ERROR "%s has an invalid directory size. Ignored.\n", packfile );
if( error ) *error = PAK_LOAD_BAD_FOLDERS;
close( packhandle );
return NULL;
}
numpackfiles = header.dirlen / sizeof( dpackfile_t );
if( numpackfiles > MAX_FILES_IN_PACK )
{
Con_Reportf( S_ERROR "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles );
if( error ) *error = PAK_LOAD_TOO_MANY_FILES;
close( packhandle );
return NULL;
}
if( numpackfiles <= 0 )
{
Con_Reportf( "%s has no files. Ignored.\n", packfile );
if( error ) *error = PAK_LOAD_NO_FILES;
close( packhandle );
return NULL;
}
info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles );
lseek( packhandle, header.dirofs, SEEK_SET );
if( header.dirlen != read( packhandle, (void *)info, header.dirlen ))
{
Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile );
if( error ) *error = PAK_LOAD_CORRUPTED;
close( packhandle );
Mem_Free( info );
return NULL;
}
pack = (pack_t *)Mem_Calloc( fs_mempool, sizeof( pack_t ));
Q_strncpy( pack->filename, packfile, sizeof( pack->filename ));
pack->files = (dpackfile_t *)Mem_Calloc( fs_mempool, numpackfiles * sizeof( dpackfile_t ));
pack->filetime = FS_SysFileTime( packfile );
pack->handle = packhandle;
pack->numfiles = 0;
// parse the directory
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;
}
/*
===========
FS_OpenPackedFile
Open a packed file using its package file descriptor
===========
*/
file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind )
{
dpackfile_t *pfile;
pfile = &pack->files[pack_ind];
return FS_OpenHandle( pack->filename, pack->handle, pfile->filepos, pfile->filelen );
}
/*
================
FS_AddPak_Fullpath
Adds the given pack to the search path.
The pack type is autodetected by the file extension.
Returns true if the file was successfully added to the
search path or if it was already included.
If keep_plain_dirs is set, the pack will be added AFTER the first sequence of
plain directories.
================
*/
qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags )
{
searchpath_t *search;
pack_t *pak = NULL;
const char *ext = COM_FileExtension( pakfile );
int i, errorcode = PAK_LOAD_COULDNT_OPEN;
for( search = fs_searchpaths; search; search = search->next )
{
if( search->type == SEARCHPATH_PAK && !Q_stricmp( search->pack->filename, pakfile ))
{
if( already_loaded ) *already_loaded = true;
return true; // already loaded
}
}
if( already_loaded )
*already_loaded = false;
if( !Q_stricmp( ext, "pak" ))
pak = FS_LoadPackPAK( pakfile, &errorcode );
if( pak )
{
string fullpath;
search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ));
search->pack = pak;
search->type = SEARCHPATH_PAK;
search->next = fs_searchpaths;
search->flags |= flags;
fs_searchpaths = search;
Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles );
// time to add in search list all the wads that contains in current pakfile (if do)
for( i = 0; i < pak->numfiles; i++ )
{
if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" ))
{
Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name );
FS_AddWad_Fullpath( fullpath, NULL, flags );
}
}
return true;
}
else
{
if( errorcode != PAK_LOAD_NO_FILES )
Con_Reportf( S_ERROR "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile );
return false;
}
}
int FS_FindFilePAK( pack_t *pack, const char *name )
{
int left, right, middle;
// look for the file (binary search)
left = 0;
right = pack->numfiles - 1;
while( left <= right )
{
int diff;
middle = (left + right) / 2;
diff = Q_stricmp( pack->files[middle].name, name );
// Found it
if( !diff )
{
return middle;
}
// if we're too far in the list
if( diff > 0 )
right = middle - 1;
else left = middle + 1;
}
return -1;
}
void FS_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern )
{
string temp;
const char *slash, *backslash, *colon, *separator;
int j, i;
for( i = 0; i < pack->numfiles; i++ )
{
Q_strncpy( temp, pack->files[i].name, sizeof( temp ));
while( temp[0] )
{
if( matchpattern( temp, pattern, true ))
{
for( j = 0; j < list->numstrings; j++ )
{
if( !Q_strcmp( list->strings[j], temp ))
break;
}
if( j == list->numstrings )
stringlistappend( list, 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 = Q_strrchr( temp, '/' );
backslash = Q_strrchr( temp, '\\' );
colon = Q_strrchr( temp, ':' );
separator = temp;
if( separator < slash )
separator = slash;
if( separator < backslash )
separator = backslash;
if( separator < colon )
separator = colon;
*((char *)separator) = 0;
}
}
}
int FS_FileTimePAK( pack_t *pack )
{
return pack->filetime;
}
void FS_PrintPAKInfo( char *dst, size_t size, pack_t *pack )
{
Q_snprintf( dst, size, "%s (%i files)", pack->filename, pack->numfiles );
}
void FS_ClosePAK( pack_t *pack )
{
if( pack->files )
Mem_Free( pack->files );
if( pack->handle >= 0 )
close( pack->handle );
Mem_Free( pack );
}

634
filesystem/wad.c

@ -0,0 +1,634 @@
/*
wad.c - WAD support for filesystem
Copyright (C) 2007 Uncle Mike
Copyright (C) 2022 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include "port.h"
#include "filesystem_internal.h"
#include "crtlib.h"
#include "common/com_strings.h"
#include "wadfile.h"
/*
========================================================================
.WAD archive format (WhereAllData - WAD)
List of compressed files, that can be identify only by TYPE_*
<format>
header: dwadinfo_t[dwadinfo_t]
file_1: byte[dwadinfo_t[num]->disksize]
file_2: byte[dwadinfo_t[num]->disksize]
file_3: byte[dwadinfo_t[num]->disksize]
...
file_n: byte[dwadinfo_t[num]->disksize]
infotable dlumpinfo_t[dwadinfo_t->numlumps]
========================================================================
*/
#define WAD3_NAMELEN 16
#define HINT_NAMELEN 5 // e.g. _mask, _norm
#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount
#include "const.h"
typedef struct
{
int ident; // should be WAD3
int numlumps; // num files
int infotableofs; // LUT offset
} dwadinfo_t;
typedef struct
{
int filepos; // file offset in WAD
int disksize; // compressed or uncompressed
int size; // uncompressed
signed char type; // TYP_*
signed char attribs; // file attribs
signed char pad0;
signed char pad1;
char name[WAD3_NAMELEN]; // must be null terminated
} dlumpinfo_t;
typedef struct wfile_s
{
string filename;
int infotableofs;
int numlumps;
poolhandle_t mempool; // W_ReadLump temp buffers
file_t *handle;
dlumpinfo_t *lumps;
time_t filetime;
} wfile_t;
// WAD errors
#define WAD_LOAD_OK 0
#define WAD_LOAD_COULDNT_OPEN 1
#define WAD_LOAD_BAD_HEADER 2
#define WAD_LOAD_BAD_FOLDERS 3
#define WAD_LOAD_TOO_MANY_FILES 4
#define WAD_LOAD_NO_FILES 5
#define WAD_LOAD_CORRUPTED 6
typedef struct wadtype_s
{
const char *ext;
signed char type;
} wadtype_t;
// associate extension with wad type
static const wadtype_t wad_types[7] =
{
{ "pal", TYP_PALETTE }, // palette
{ "dds", TYP_DDSTEX }, // DDS image
{ "lmp", TYP_GFXPIC }, // quake1, hl pic
{ "fnt", TYP_QFONT }, // hl qfonts
{ "mip", TYP_MIPTEX }, // hl/q1 mip
{ "txt", TYP_SCRIPT }, // scripts
{ NULL, TYP_NONE }
};
/*
===========
W_TypeFromExt
Extracts file type from extension
===========
*/
static signed char W_TypeFromExt( const char *lumpname )
{
const char *ext = COM_FileExtension( lumpname );
const wadtype_t *type;
// we not known about filetype, so match only by filename
if( !Q_strcmp( ext, "*" ) || !Q_strcmp( ext, "" ))
return TYP_ANY;
for( type = wad_types; type->ext; type++ )
{
if( !Q_stricmp( ext, type->ext ))
return type->type;
}
return TYP_NONE;
}
/*
===========
W_ExtFromType
Convert type to extension
===========
*/
static const char *W_ExtFromType( signed char lumptype )
{
const wadtype_t *type;
// we not known aboyt filetype, so match only by filename
if( lumptype == TYP_NONE || lumptype == TYP_ANY )
return "";
for( type = wad_types; type->ext; type++ )
{
if( lumptype == type->type )
return type->ext;
}
return "";
}
/*
====================
W_AddFileToWad
Add a file to the list of files contained into a package
and sort LAT in alpha-bethical order
====================
*/
static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t *newlump )
{
int left, right;
dlumpinfo_t *plump;
// look for the slot we should put that file into (binary search)
left = 0;
right = wad->numlumps - 1;
while( left <= right )
{
int middle = ( left + right ) / 2;
int diff = Q_stricmp( wad->lumps[middle].name, name );
if( !diff )
{
if( wad->lumps[middle].type < newlump->type )
diff = 1;
else if( wad->lumps[middle].type > newlump->type )
diff = -1;
else Con_Reportf( S_WARN "Wad %s contains the file %s several times\n", wad->filename, name );
}
// If we're too far in the list
if( diff > 0 ) right = middle - 1;
else left = middle + 1;
}
// we have to move the right of the list by one slot to free the one we need
plump = &wad->lumps[left];
memmove( plump + 1, plump, ( wad->numlumps - left ) * sizeof( *plump ));
wad->numlumps++;
*plump = *newlump;
memcpy( plump->name, name, sizeof( plump->name ));
return plump;
}
/*
===========
FS_CloseWAD
finalize wad or just close
===========
*/
void FS_CloseWAD( wfile_t *wad )
{
Mem_FreePool( &wad->mempool );
if( wad->handle != NULL )
FS_Close( wad->handle );
Mem_Free( wad ); // free himself
}
/*
===========
W_Open
open the wad for reading & writing
===========
*/
static wfile_t *W_Open( const char *filename, int *error )
{
wfile_t *wad = (wfile_t *)Mem_Calloc( fs_mempool, sizeof( wfile_t ));
const char *basename;
int i, lumpcount;
dlumpinfo_t *srclumps;
size_t lat_size;
dwadinfo_t header;
// NOTE: FS_Open is load wad file from the first pak in the list (while fs_ext_path is false)
if( fs_ext_path ) basename = filename;
else basename = COM_FileWithoutPath( filename );
wad->handle = FS_Open( basename, "rb", false );
// HACKHACK: try to open WAD by full path for RoDir, when searchpaths are not ready
if( COM_CheckStringEmpty( fs_rodir ) && fs_ext_path && wad->handle == NULL )
wad->handle = FS_SysOpen( filename, "rb" );
if( wad->handle == NULL )
{
Con_Reportf( S_ERROR "W_Open: couldn't open %s\n", filename );
if( error ) *error = WAD_LOAD_COULDNT_OPEN;
FS_CloseWAD( wad );
return NULL;
}
// copy wad name
Q_strncpy( wad->filename, filename, sizeof( wad->filename ));
wad->filetime = FS_SysFileTime( filename );
wad->mempool = Mem_AllocPool( filename );
if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t ))
{
Con_Reportf( S_ERROR "W_Open: %s can't read header\n", filename );
if( error ) *error = WAD_LOAD_BAD_HEADER;
FS_CloseWAD( wad );
return NULL;
}
if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER )
{
Con_Reportf( S_ERROR "W_Open: %s is not a WAD2 or WAD3 file\n", filename );
if( error ) *error = WAD_LOAD_BAD_HEADER;
FS_CloseWAD( wad );
return NULL;
}
lumpcount = header.numlumps;
if( lumpcount >= MAX_FILES_IN_WAD )
{
Con_Reportf( S_WARN "W_Open: %s is full (%i lumps)\n", filename, lumpcount );
if( error ) *error = WAD_LOAD_TOO_MANY_FILES;
}
else if( lumpcount <= 0 )
{
Con_Reportf( S_ERROR "W_Open: %s has no lumps\n", filename );
if( error ) *error = WAD_LOAD_NO_FILES;
FS_CloseWAD( wad );
return NULL;
}
else if( error ) *error = WAD_LOAD_OK;
wad->infotableofs = header.infotableofs; // save infotableofs position
if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 )
{
Con_Reportf( S_ERROR "W_Open: %s can't find lump allocation table\n", filename );
if( error ) *error = WAD_LOAD_BAD_FOLDERS;
FS_CloseWAD( wad );
return NULL;
}
lat_size = lumpcount * sizeof( dlumpinfo_t );
// NOTE: lumps table can be reallocated for O_APPEND mode
srclumps = (dlumpinfo_t *)Mem_Malloc( wad->mempool, lat_size );
if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size )
{
Con_Reportf( S_ERROR "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
if( error ) *error = WAD_LOAD_CORRUPTED;
Mem_Free( srclumps );
FS_CloseWAD( wad );
return NULL;
}
// starting to add lumps
wad->lumps = (dlumpinfo_t *)Mem_Calloc( wad->mempool, lat_size );
wad->numlumps = 0;
// sort lumps for binary search
for( i = 0; i < lumpcount; i++ )
{
char name[16];
int k;
// cleanup lumpname
Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name ));
// check for '*' symbol issues (quake1)
k = Q_strlen( Q_strrchr( name, '*' ));
if( k ) name[Q_strlen( name ) - k] = '!';
// check for Quake 'conchars' issues (only lmp loader really allows to read this lame pic)
if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" ))
srclumps[i].type = TYP_GFXPIC;
W_AddFileToWad( name, wad, &srclumps[i] );
}
// release source lumps
Mem_Free( srclumps );
// and leave the file open
return wad;
}
/*
====================
FS_AddWad_Fullpath
====================
*/
qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags )
{
searchpath_t *search;
wfile_t *wad = NULL;
const char *ext = COM_FileExtension( wadfile );
int errorcode = WAD_LOAD_COULDNT_OPEN;
for( search = fs_searchpaths; search; search = search->next )
{
if( search->type == SEARCHPATH_WAD && !Q_stricmp( search->wad->filename, wadfile ))
{
if( already_loaded ) *already_loaded = true;
return true; // already loaded
}
}
if( already_loaded )
*already_loaded = false;
if( !Q_stricmp( ext, "wad" ))
wad = W_Open( wadfile, &errorcode );
if( wad )
{
search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ));
search->wad = wad;
search->type = SEARCHPATH_WAD;
search->next = fs_searchpaths;
search->flags |= flags;
fs_searchpaths = search;
Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps );
return true;
}
else
{
if( errorcode != WAD_LOAD_NO_FILES )
Con_Reportf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile );
return false;
}
}
/*
=============================================================================
WADSYSTEM PRIVATE ROUTINES
=============================================================================
*/
/*
===========
W_FindLump
Serach for already existed lump
===========
*/
static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const signed char matchtype )
{
int left, right;
if( !wad || !wad->lumps || matchtype == TYP_NONE )
return NULL;
// look for the file (binary search)
left = 0;
right = wad->numlumps - 1;
while( left <= right )
{
int middle = (left + right) / 2;
int diff = Q_stricmp( wad->lumps[middle].name, name );
if( !diff )
{
if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type ))
return &wad->lumps[middle]; // found
else if( wad->lumps[middle].type < matchtype )
diff = 1;
else if( wad->lumps[middle].type > matchtype )
diff = -1;
else break; // not found
}
// if we're too far in the list
if( diff > 0 ) right = middle - 1;
else left = middle + 1;
}
return NULL;
}
/*
===========
W_ReadLump
reading lump into temp buffer
===========
*/
static byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, fs_offset_t *lumpsizeptr )
{
size_t oldpos, size = 0;
byte *buf;
// assume error
if( lumpsizeptr ) *lumpsizeptr = 0;
// no wads loaded
if( !wad || !lump ) return NULL;
oldpos = FS_Tell( wad->handle ); // don't forget restore original position
if( FS_Seek( wad->handle, lump->filepos, SEEK_SET ) == -1 )
{
Con_Reportf( S_ERROR "W_ReadLump: %s is corrupted\n", lump->name );
FS_Seek( wad->handle, oldpos, SEEK_SET );
return NULL;
}
buf = (byte *)Mem_Malloc( wad->mempool, lump->disksize );
size = FS_Read( wad->handle, buf, lump->disksize );
if( size < lump->disksize )
{
Con_Reportf( S_WARN "W_ReadLump: %s is probably corrupted\n", lump->name );
FS_Seek( wad->handle, oldpos, SEEK_SET );
Mem_Free( buf );
return NULL;
}
if( lumpsizeptr ) *lumpsizeptr = lump->disksize;
FS_Seek( wad->handle, oldpos, SEEK_SET );
return buf;
}
/*
===========
FS_LoadWADFile
loading lump into the tmp buffer
===========
*/
byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly )
{
searchpath_t *search;
int index;
search = FS_FindFile( path, &index, gamedironly );
if( search && search->type == SEARCHPATH_WAD )
return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr );
return NULL;
}
int FS_FileTimeWAD( wfile_t *wad )
{
return wad->filetime;
}
void FS_PrintWADInfo( char *dst, size_t size, wfile_t *wad )
{
Q_snprintf( dst, size, "%s (%i files)", wad->filename, wad->numlumps );
}
int FS_FindFileWAD( wfile_t *wad, const char *name )
{
dlumpinfo_t *lump;
signed char type = W_TypeFromExt( name );
qboolean anywadname = true;
string wadname, wadfolder;
string shortname;
// quick reject by filetype
if( type == TYP_NONE )
return -1;
COM_ExtractFilePath( name, wadname );
wadfolder[0] = '\0';
if( COM_CheckStringEmpty( wadname ) )
{
COM_FileBase( wadname, wadname );
Q_strncpy( wadfolder, wadname, sizeof( wadfolder ));
COM_DefaultExtension( wadname, ".wad" );
anywadname = false;
}
// make wadname from wad fullpath
COM_FileBase( wad->filename, shortname );
COM_DefaultExtension( shortname, ".wad" );
// quick reject by wadname
if( !anywadname && Q_stricmp( wadname, shortname ))
return -1;
// NOTE: we can't using long names for wad,
// because we using original wad names[16];
COM_FileBase( name, shortname );
lump = W_FindLump( wad, shortname, type );
if( lump )
{
return lump - wad->lumps;
}
return -1;
}
void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern )
{
string wadpattern, wadname, temp2;
signed char type = W_TypeFromExt( pattern );
qboolean anywadname = true;
string wadfolder, temp;
int j, i;
const char *slash, *backslash, *colon, *separator;
// quick reject by filetype
if( type == TYP_NONE )
return;
COM_ExtractFilePath( pattern, wadname );
COM_FileBase( pattern, wadpattern );
wadfolder[0] = '\0';
if( COM_CheckStringEmpty( wadname ))
{
COM_FileBase( wadname, wadname );
Q_strncpy( wadfolder, wadname, sizeof( wadfolder ));
COM_DefaultExtension( wadname, ".wad" );
anywadname = false;
}
// make wadname from wad fullpath
COM_FileBase( wad->filename, temp2 );
COM_DefaultExtension( temp2, ".wad" );
// quick reject by wadname
if( !anywadname && Q_stricmp( wadname, temp2 ))
return;
for( i = 0; i < wad->numlumps; i++ )
{
// if type not matching, we already have no chance ...
if( type != TYP_ANY && wad->lumps[i].type != type )
continue;
// build the lumpname with image suffix (if present)
Q_strncpy( temp, wad->lumps[i].name, sizeof( temp ));
while( temp[0] )
{
if( matchpattern( temp, wadpattern, true ))
{
for( j = 0; j < list->numstrings; j++ )
{
if( !Q_strcmp( list->strings[j], temp ))
break;
}
if( j == list->numstrings )
{
// build path: wadname/lumpname.ext
Q_snprintf( temp2, sizeof(temp2), "%s/%s", wadfolder, temp );
COM_DefaultExtension( temp2, va(".%s", W_ExtFromType( wad->lumps[i].type )));
stringlistappend( list, temp2 );
}
}
// strip off one path element at a time until empty
// this way directories are added to the listing if they match the pattern
slash = Q_strrchr( temp, '/' );
backslash = Q_strrchr( temp, '\\' );
colon = Q_strrchr( temp, ':' );
separator = temp;
if( separator < slash )
separator = slash;
if( separator < backslash )
separator = backslash;
if( separator < colon )
separator = colon;
*((char *)separator) = 0;
}
}
}

17
filesystem/wscript

@ -0,0 +1,17 @@
#!/usr/bin/env python
def options(opt):
pass
def configure(conf):
if conf.env.cxxshlib_PATTERN.startswith('lib'):
conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:]
def build(bld):
bld.shlib(target = 'filesystem_stdio',
features = 'c',
source = bld.path.ant_glob(['*.c']),
includes = ['.', '../common', '../public', '../engine'],
use = ['public'],
install_path = bld.env.LIBDIR,
subsystem = bld.env.MSVC_SUBSYSTEM)

678
filesystem/zip.c

@ -0,0 +1,678 @@
/*
zip.c - ZIP support for filesystem
Copyright (C) 2019 Mr0maks
Copyright (C) 2022 Alibek Omarov
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stddef.h>
#include STDINT_H
#include "port.h"
#include "filesystem_internal.h"
#include "crtlib.h"
#include "common/com_strings.h"
#include "miniz.h"
#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_CDF ((0x02<<24)+(0x01<<16)+('K'<<8)+'P')
#define ZIP_HEADER_EOCD ((0x06<<24)+(0x05<<16)+('K'<<8)+'P')
#define ZIP_COMPRESSION_NO_COMPRESSION 0
#define ZIP_COMPRESSION_DEFLATED 8
#define ZIP_ZIP64 0xffffffff
#pragma pack( push, 1 )
typedef struct zip_header_s
{
unsigned int signature; // little endian ZIP_HEADER
unsigned short version; // version of pkzip need to unpack
unsigned short flags; // flags (16 bits == 16 flags)
unsigned short compression_flags; // compression flags (bits)
unsigned int dos_date; // file modification time and file modification date
unsigned int crc32; //crc32
unsigned int compressed_size;
unsigned int uncompressed_size;
unsigned short filename_len;
unsigned short extrafield_len;
} zip_header_t;
/*
in zip64 comp and uncompr size == 0xffffffff remeber this
compressed and uncompress filesize stored in extra field
*/
typedef struct zip_header_extra_s
{
unsigned int signature; // ZIP_HEADER_SPANNED
unsigned int crc32;
unsigned int compressed_size;
unsigned int uncompressed_size;
} zip_header_extra_t;
typedef struct zip_cdf_header_s
{
unsigned int signature;
unsigned short version;
unsigned short version_need;
unsigned short generalPurposeBitFlag;
unsigned short flags;
unsigned short modification_time;
unsigned short modification_date;
unsigned int crc32;
unsigned int compressed_size;
unsigned int uncompressed_size;
unsigned short filename_len;
unsigned short extrafield_len;
unsigned short file_commentary_len;
unsigned short disk_start;
unsigned short internal_attr;
unsigned int external_attr;
unsigned int local_header_offset;
} zip_cdf_header_t;
typedef struct zip_header_eocd_s
{
unsigned short disk_number;
unsigned short start_disk_number;
unsigned short number_central_directory_record;
unsigned short total_central_directory_record;
unsigned int size_of_central_directory;
unsigned int central_directory_offset;
unsigned short commentary_len;
} zip_header_eocd_t;
#pragma pack( pop )
// ZIP errors
#define ZIP_LOAD_OK 0
#define ZIP_LOAD_COULDNT_OPEN 1
#define ZIP_LOAD_BAD_HEADER 2
#define ZIP_LOAD_BAD_FOLDERS 3
#define ZIP_LOAD_NO_FILES 5
#define ZIP_LOAD_CORRUPTED 6
typedef struct zipfile_s
{
char name[MAX_SYSPATH];
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;
int handle;
int numfiles;
time_t filetime;
zipfile_t *files;
} zip_t;
#ifdef XASH_REDUCE_FD
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 );
}
#else
static void FS_EnsureOpenZip( zip_t *zip ) {}
#endif
void FS_CloseZIP( zip_t *zip )
{
if( zip->files )
Mem_Free( zip->files );
FS_EnsureOpenZip( NULL );
if( zip->handle >= 0 )
close( zip->handle );
Mem_Free( zip );
}
/*
============
FS_SortZip
============
*/
static int FS_SortZip( const void *a, const void *b )
{
return Q_stricmp( ( ( zipfile_t* )a )->name, ( ( zipfile_t* )b )->name );
}
/*
============
FS_LoadZip
============
*/
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;
uint32_t signature;
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 ));
fs_size_t c;
zip->handle = open( zipfile, O_RDONLY|O_BINARY );
#if !XASH_WIN32
if( zip->handle < 0 )
{
const char *fzipfile = FS_FixFileCase( zipfile );
if( fzipfile != zipfile )
zip->handle = open( fzipfile, O_RDONLY|O_BINARY );
}
#endif
if( zip->handle < 0 )
{
Con_Reportf( S_ERROR "%s couldn't open\n", zipfile );
if( error )
*error = ZIP_LOAD_COULDNT_OPEN;
FS_CloseZIP( zip );
return NULL;
}
length = lseek( zip->handle, 0, SEEK_END );
if( length > UINT_MAX )
{
Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile );
if( error )
*error = ZIP_LOAD_COULDNT_OPEN;
FS_CloseZIP( zip );
return NULL;
}
lseek( zip->handle, 0, SEEK_SET );
c = read( zip->handle, &signature, sizeof( signature ) );
if( c != sizeof( signature ) || signature == ZIP_HEADER_EOCD )
{
Con_Reportf( S_WARN "%s has no files. Ignored.\n", zipfile );
if( error )
*error = ZIP_LOAD_NO_FILES;
FS_CloseZIP( zip );
return NULL;
}
if( signature != ZIP_HEADER_LF )
{
Con_Reportf( S_ERROR "%s is not a zip file. Ignored.\n", zipfile );
if( error )
*error = ZIP_LOAD_BAD_HEADER;
FS_CloseZIP( zip );
return NULL;
}
// Find oecd
lseek( zip->handle, 0, SEEK_SET );
filepos = length;
while ( filepos > 0 )
{
lseek( zip->handle, filepos, SEEK_SET );
c = read( zip->handle, &signature, sizeof( signature ) );
if( c == sizeof( signature ) && signature == ZIP_HEADER_EOCD )
break;
filepos -= sizeof( char ); // step back one byte
}
if( ZIP_HEADER_EOCD != signature )
{
Con_Reportf( S_ERROR "cannot find EOCD in %s. Zip file corrupted.\n", zipfile );
if( error )
*error = ZIP_LOAD_BAD_HEADER;
FS_CloseZIP( zip );
return NULL;
}
c = read( zip->handle, &header_eocd, sizeof( header_eocd ) );
if( c != sizeof( header_eocd ))
{
Con_Reportf( S_ERROR "invalid EOCD header in %s. Zip file corrupted.\n", zipfile );
if( error )
*error = ZIP_LOAD_BAD_HEADER;
FS_CloseZIP( zip );
return NULL;
}
// Move to CDF start
lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET );
// Calc count of files in archive
info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( *info ) * header_eocd.total_central_directory_record );
for( i = 0; i < header_eocd.total_central_directory_record; i++ )
{
c = read( zip->handle, &header_cdf, sizeof( header_cdf ) );
if( c != sizeof( header_cdf ) || header_cdf.signature != ZIP_HEADER_CDF )
{
Con_Reportf( S_ERROR "CDF signature mismatch in %s. Zip file corrupted.\n", zipfile );
if( error )
*error = ZIP_LOAD_BAD_HEADER;
Mem_Free( info );
FS_CloseZIP( zip );
return NULL;
}
if( header_cdf.uncompressed_size && header_cdf.filename_len && ( header_cdf.filename_len < MAX_SYSPATH ) )
{
memset( &filename_buffer, '\0', MAX_SYSPATH );
c = read( zip->handle, &filename_buffer, header_cdf.filename_len );
if( c != header_cdf.filename_len )
{
Con_Reportf( S_ERROR "filename length mismatch in %s. Zip file corrupted.\n", zipfile );
if( error )
*error = ZIP_LOAD_CORRUPTED;
Mem_Free( info );
FS_CloseZIP( zip );
return NULL;
}
Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH );
info[numpackfiles].size = header_cdf.uncompressed_size;
info[numpackfiles].compressed_size = header_cdf.compressed_size;
info[numpackfiles].offset = header_cdf.local_header_offset;
numpackfiles++;
}
else
lseek( zip->handle, header_cdf.filename_len, SEEK_CUR );
if( header_cdf.extrafield_len )
lseek( zip->handle, header_cdf.extrafield_len, SEEK_CUR );
if( header_cdf.file_commentary_len )
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 );
c = read( zip->handle, &header, sizeof( header ) );
if( c != sizeof( header ))
{
Con_Reportf( S_ERROR "header length mismatch in %s. Zip file corrupted.\n", zipfile );
if( error )
*error = ZIP_LOAD_CORRUPTED;
Mem_Free( info );
FS_CloseZIP( zip );
return NULL;
}
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->filetime = FS_SysFileTime( zipfile );
zip->numfiles = numpackfiles;
zip->files = info;
qsort( zip->files, zip->numfiles, sizeof( *zip->files ), FS_SortZip );
#ifdef XASH_REDUCE_FD
// will reopen when needed
close(zip->handle);
zip->handle = -1;
#endif
if( error )
*error = ZIP_LOAD_OK;
return zip;
}
/*
===========
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 )
{
Con_Printf( S_ERROR "%s: can't open compressed file %s\n", __FUNCTION__, pfile->name );
return NULL;
}
return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size );
}
byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly )
{
searchpath_t *search;
int index;
zipfile_t *file = NULL;
byte *compressed_buffer = NULL, *decompressed_buffer = NULL;
int zlib_result = 0;
dword test_crc, final_crc;
z_stream decompress_stream;
size_t c;
if( sizeptr ) *sizeptr = 0;
search = FS_FindFile( path, &index, gamedironly );
if( !search || search->type != SEARCHPATH_ZIP )
return NULL;
file = &search->zip->files[index];
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( file->flags == ZIP_COMPRESSION_NO_COMPRESSION )
{
decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 );
decompressed_buffer[file->size] = '\0';
c = read( search->zip->handle, decompressed_buffer, file->size );
if( c != file->size )
{
Con_Reportf( S_ERROR "Zip_LoadFile: %s size doesn't match\n", file->name );
return NULL;
}
#if 0
CRC32_Init( &test_crc );
CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size );
final_crc = CRC32_Final( test_crc );
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( file->flags == ZIP_COMPRESSION_DEFLATED )
{
compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 );
decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 );
decompressed_buffer[file->size] = '\0';
c = read( search->zip->handle, compressed_buffer, file->compressed_size );
if( c != file->compressed_size )
{
Con_Reportf( S_ERROR "Zip_LoadFile: %s compressed size doesn't match\n", file->name );
return NULL;
}
memset( &decompress_stream, 0, sizeof( decompress_stream ) );
decompress_stream.total_in = decompress_stream.avail_in = file->compressed_size;
decompress_stream.next_in = (Bytef *)compressed_buffer;
decompress_stream.total_out = decompress_stream.avail_out = file->size;
decompress_stream.next_out = (Bytef *)decompressed_buffer;
decompress_stream.zalloc = Z_NULL;
decompress_stream.zfree = Z_NULL;
decompress_stream.opaque = Z_NULL;
if( inflateInit2( &decompress_stream, -MAX_WBITS ) != Z_OK )
{
Con_Printf( S_ERROR "Zip_LoadFile: inflateInit2 failed\n" );
Mem_Free( compressed_buffer );
Mem_Free( decompressed_buffer );
return NULL;
}
zlib_result = inflate( &decompress_stream, Z_NO_FLUSH );
inflateEnd( &decompress_stream );
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 != 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
{
Con_Reportf( S_ERROR "Zip_LoadFile: %s : error while file decompressing. Zlib return code %d.\n", file->name, zlib_result );
Mem_Free( compressed_buffer );
Mem_Free( decompressed_buffer );
return NULL;
}
}
else
{
Con_Reportf( S_ERROR "Zip_LoadFile: %s : file compressed with unknown algorithm.\n", file->name );
return NULL;
}
FS_EnsureOpenZip( NULL );
return NULL;
}
qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags )
{
searchpath_t *search;
zip_t *zip = NULL;
const char *ext = COM_FileExtension( zipfile );
int errorcode = ZIP_LOAD_COULDNT_OPEN;
for( search = fs_searchpaths; search; search = search->next )
{
if( search->type == SEARCHPATH_ZIP && !Q_stricmp( search->zip->filename, zipfile ))
{
if( already_loaded ) *already_loaded = true;
return true; // already loaded
}
}
if( already_loaded ) *already_loaded = false;
if( !Q_stricmp( ext, "pk3" ) )
zip = FS_LoadZip( zipfile, &errorcode );
if( zip )
{
string fullpath;
int i;
search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) );
search->zip = zip;
search->type = SEARCHPATH_ZIP;
search->next = fs_searchpaths;
search->flags |= flags;
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
{
if( errorcode != ZIP_LOAD_NO_FILES )
Con_Reportf( S_ERROR "FS_AddZip_Fullpath: unable to load zip \"%s\"\n", zipfile );
return false;
}
}
int FS_FileTimeZIP( zip_t *zip )
{
return zip->filetime;
}
void FS_PrintZIPInfo( char *dst, size_t size, zip_t *zip )
{
Q_snprintf( dst, size, "%s (%i files)", zip->filename, zip->numfiles );
}
int FS_FindFileZIP( zip_t *zip, const char *name )
{
int left, right, middle;
// look for the file (binary search)
left = 0;
right = zip->numfiles - 1;
while( left <= right )
{
int diff;
middle = (left + right) / 2;
diff = Q_stricmp( zip->files[middle].name, name );
// Found it
if( !diff )
return middle;
// if we're too far in the list
if( diff > 0 )
right = middle - 1;
else left = middle + 1;
}
return -1;
}
void FS_SearchZIP( stringlist_t *list, zip_t *zip, const char *pattern )
{
string temp;
const char *slash, *backslash, *colon, *separator;
int j, i;
for( i = 0; i < zip->numfiles; i++ )
{
Q_strncpy( temp, zip->files[i].name, sizeof( temp ));
while( temp[0] )
{
if( matchpattern( temp, pattern, true ))
{
for( j = 0; j < list->numstrings; j++ )
{
if( !Q_strcmp( list->strings[j], temp ))
break;
}
if( j == list->numstrings )
stringlistappend( list, 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 = Q_strrchr( temp, '/' );
backslash = Q_strrchr( temp, '\\' );
colon = Q_strrchr( temp, ':' );
separator = temp;
if( separator < slash )
separator = slash;
if( separator < backslash )
separator = backslash;
if( separator < colon )
separator = colon;
*((char *)separator) = 0;
}
}
}

17
public/crtlib.c

@ -819,6 +819,23 @@ void COM_RemoveLineFeed( char *str )
} }
} }
/*
============
COM_FixSlashes
Changes all '/' characters into '\' characters, in place.
============
*/
void COM_FixSlashes( char *pname )
{
while( *pname )
{
if( *pname == '\\' )
*pname = '/';
pname++;
}
}
/* /*
============ ============
COM_PathSlashFix COM_PathSlashFix

1
public/crtlib.h

@ -95,6 +95,7 @@ void COM_ExtractFilePath( const char *path, char *dest );
const char *COM_FileWithoutPath( const char *in ); const char *COM_FileWithoutPath( const char *in );
void COM_StripExtension( char *path ); void COM_StripExtension( char *path );
void COM_RemoveLineFeed( char *str ); void COM_RemoveLineFeed( char *str );
void COM_FixSlashes( char *pname );
void COM_PathSlashFix( char *path ); void COM_PathSlashFix( char *path );
char COM_Hex2Char( uint8_t hex ); char COM_Hex2Char( uint8_t hex );
void COM_Hex2String( uint8_t hex, char *str ); void COM_Hex2String( uint8_t hex, char *str );

0
engine/common/miniz.h → public/miniz.h

2
ref_gl/gl_alias.c

@ -486,7 +486,7 @@ void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size )
Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum ); Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum );
Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum ); Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum );
Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum ); Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum );
if( !gEngfuncs.FS_FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL ) if( !gEngfuncs.fsapi->FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL )
pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight );
m_pAliasHeader->gl_texturenum[skinnum][0] = m_pAliasHeader->gl_texturenum[skinnum][0] =

4
ref_gl/gl_backend.c

@ -478,7 +478,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type )
case VID_SCREENSHOT: case VID_SCREENSHOT:
break; break;
case VID_SNAPSHOT: case VID_SNAPSHOT:
gEngfuncs.FS_AllowDirectPaths( true ); gEngfuncs.fsapi->AllowDirectPaths( true );
break; break;
case VID_LEVELSHOT: case VID_LEVELSHOT:
flags |= IMAGE_RESAMPLE; flags |= IMAGE_RESAMPLE;
@ -509,7 +509,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type )
// write image // write image
result = gEngfuncs.FS_SaveImage( filename, r_shot ); result = gEngfuncs.FS_SaveImage( filename, r_shot );
gEngfuncs.FS_AllowDirectPaths( false ); // always reset after store screenshot gEngfuncs.fsapi->AllowDirectPaths( false ); // always reset after store screenshot
gEngfuncs.FS_FreeImage( r_shot ); gEngfuncs.FS_FreeImage( r_shot );
return result; return result;

2
ref_gl/gl_rmisc.c

@ -29,7 +29,7 @@ static void R_ParseDetailTextures( const char *filename )
texture_t *tex; texture_t *tex;
int i; int i;
afile = gEngfuncs.COM_LoadFile( filename, NULL, false ); afile = gEngfuncs.fsapi->LoadFile( filename, NULL, false );
if( !afile ) return; if( !afile ) return;
pfile = (char *)afile; pfile = (char *)afile;

2
ref_gl/gl_studio.c

@ -2699,7 +2699,7 @@ static model_t *R_StudioSetupPlayerModel( int index )
Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model );
if( gEngfuncs.FS_FileExists( state->modelname, false )) if( gEngfuncs.fsapi->FileExists( state->modelname, false ))
state->model = gEngfuncs.Mod_ForName( state->modelname, false, true ); state->model = gEngfuncs.Mod_ForName( state->modelname, false, true );
else state->model = NULL; else state->model = NULL;

4
ref_gl/gl_warp.c

@ -78,7 +78,7 @@ static int CheckSkybox( const char *name )
{ {
// build side name // build side name
sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] );
if( gEngfuncs.FS_FileExists( sidename, false )) if( gEngfuncs.fsapi->FileExists( sidename, false ))
num_checked_sides++; num_checked_sides++;
} }
@ -90,7 +90,7 @@ static int CheckSkybox( const char *name )
{ {
// build side name // build side name
sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] );
if( gEngfuncs.FS_FileExists( sidename, false )) if( gEngfuncs.fsapi->FileExists( sidename, false ))
num_checked_sides++; num_checked_sides++;
} }

1
ref_gl/wscript

@ -59,6 +59,7 @@ def build(bld):
source = bld.path.ant_glob(['*.c']) source = bld.path.ant_glob(['*.c'])
includes = ['.', includes = ['.',
'../filesystem',
'../engine', '../engine',
'../engine/common', '../engine/common',
'../engine/server', '../engine/server',

2
ref_soft/r_studio.c

@ -2462,7 +2462,7 @@ static model_t *R_StudioSetupPlayerModel( int index )
Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model );
if( gEngfuncs.FS_FileExists( state->modelname, false )) if( gEngfuncs.fsapi->FileExists( state->modelname, false ))
state->model = gEngfuncs.Mod_ForName( state->modelname, false, true ); state->model = gEngfuncs.Mod_ForName( state->modelname, false, true );
else state->model = NULL; else state->model = NULL;

1
ref_soft/wscript

@ -30,6 +30,7 @@ def build(bld):
source = bld.path.ant_glob(['*.c']) source = bld.path.ant_glob(['*.c'])
includes = ['.', includes = ['.',
'../filesystem',
'../engine', '../engine',
'../engine/common', '../engine/common',
'../engine/server', '../engine/server',

1
wscript

@ -55,6 +55,7 @@ class Subproject:
SUBDIRS = [ SUBDIRS = [
Subproject('public', dedicated=False, mandatory = True), Subproject('public', dedicated=False, mandatory = True),
Subproject('filesystem', dedicated=False, mandatory = True),
Subproject('game_launch', singlebin=True), Subproject('game_launch', singlebin=True),
Subproject('ref_gl',), Subproject('ref_gl',),
Subproject('ref_soft'), Subproject('ref_soft'),

Loading…
Cancel
Save