mirror of
https://github.com/FWGS/xash3d-fwgs
synced 2024-12-22 17:01:14 +01:00
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
This commit is contained in:
parent
12ea6dcfd7
commit
5e4fc64430
@ -980,7 +980,7 @@ pfnGetGamesList
|
||||
*/
|
||||
static GAMEINFO ** GAME_EXPORT pfnGetGamesList( int *numGames )
|
||||
{
|
||||
if( numGames ) *numGames = SI.numgames;
|
||||
if( numGames ) *numGames = FI->numgames;
|
||||
return gameui.modsInfo;
|
||||
}
|
||||
|
||||
@ -1117,6 +1117,30 @@ static char *pfnParseFile( char *buf, char *token )
|
||||
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
|
||||
static ui_enginefuncs_t gEngfuncs =
|
||||
{
|
||||
@ -1163,7 +1187,7 @@ static ui_enginefuncs_t gEngfuncs =
|
||||
pfnRenderScene,
|
||||
pfnAddEntity,
|
||||
Host_Error,
|
||||
FS_FileExists,
|
||||
pfnFileExists,
|
||||
pfnGetGameDir,
|
||||
Cmd_CheckMapsList,
|
||||
CL_Active,
|
||||
@ -1202,7 +1226,7 @@ static ui_enginefuncs_t gEngfuncs =
|
||||
COM_CompareFileTime,
|
||||
VID_GetModeString,
|
||||
(void*)COM_SaveFile,
|
||||
(void*)FS_Delete
|
||||
pfnDelete
|
||||
};
|
||||
|
||||
static void pfnEnableTextInput( int enable )
|
||||
@ -1358,13 +1382,13 @@ qboolean UI_LoadProgs( void )
|
||||
Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY );
|
||||
|
||||
// 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 ));
|
||||
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
|
||||
gameui.globals->developer = host.allow_console;
|
||||
|
@ -334,10 +334,6 @@ static ref_api_t gEngfuncs =
|
||||
COM_FreeLibrary,
|
||||
COM_GetProcAddress,
|
||||
|
||||
FS_LoadFile,
|
||||
FS_FileExists,
|
||||
FS_AllowDirectPaths,
|
||||
|
||||
R_Init_Video_,
|
||||
R_Free_Video,
|
||||
|
||||
@ -383,7 +379,9 @@ static ref_api_t gEngfuncs =
|
||||
|
||||
pfnDrawNormalTriangles,
|
||||
pfnDrawTransparentTriangles,
|
||||
&clgame.drawFuncs
|
||||
&clgame.drawFuncs,
|
||||
|
||||
&g_fsapi,
|
||||
};
|
||||
|
||||
static void R_UnloadProgs( void )
|
||||
|
@ -585,23 +585,6 @@ void COM_TrimSpace( const char *source, char *dest )
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int numfilenames;
|
||||
char **filenames;
|
||||
char *filenamesbuffer;
|
||||
} search_t;
|
||||
|
||||
enum
|
||||
{
|
||||
DEV_NONE = 0,
|
||||
@ -118,6 +111,8 @@ typedef enum
|
||||
#include "cvar.h"
|
||||
#include "con_nprint.h"
|
||||
#include "crclib.h"
|
||||
#include "ref_api.h"
|
||||
#include "fscallback.h"
|
||||
|
||||
// PERFORMANCE INFO
|
||||
#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
|
||||
#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 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)
|
||||
========================================================================
|
||||
*/
|
||||
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
|
||||
{
|
||||
string exeName; // exe.filename
|
||||
@ -261,9 +189,6 @@ typedef struct sysinfo_s
|
||||
string basedirName; // name of base directory
|
||||
string gamedll;
|
||||
string clientlib;
|
||||
gameinfo_t *GameInfo; // current GameInfo
|
||||
gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start)
|
||||
int numgames;
|
||||
} sysinfo_t;
|
||||
|
||||
typedef enum
|
||||
@ -470,6 +395,14 @@ extern sysinfo_t SI;
|
||||
|
||||
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
|
||||
//
|
||||
@ -529,56 +462,6 @@ void Mem_PrintStats( void );
|
||||
#define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem )
|
||||
#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
|
||||
//
|
||||
@ -942,6 +825,11 @@ void UI_SetActiveMenu( qboolean fActive );
|
||||
void UI_ShowConnectionWarning( void );
|
||||
void Cmd_Null_f( void );
|
||||
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
|
||||
qboolean S_Init( void );
|
||||
|
@ -761,10 +761,10 @@ qboolean Cmd_GetGamesList( const char *s, char *completedname, int length )
|
||||
// compare gamelist with current keyword
|
||||
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))
|
||||
Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder );
|
||||
if(( *s == '*' ) || !Q_strnicmp( FI->games[i]->gamefolder, s, len))
|
||||
Q_strcpy( gamedirs[numgamedirs++], FI->games[i]->gamefolder );
|
||||
}
|
||||
|
||||
if( !numgamedirs ) return false;
|
||||
|
@ -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
Normal file
146
engine/common/filesystem_engine.c
Normal file
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -328,13 +328,13 @@ void Host_ChangeGame_f( void )
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if( i == SI.numgames )
|
||||
if( i == FI->numgames )
|
||||
{
|
||||
Con_Printf( "%s not exist\n", Cmd_Argv( 1 ));
|
||||
}
|
||||
@ -345,7 +345,7 @@ void Host_ChangeGame_f( void )
|
||||
else
|
||||
{
|
||||
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 );
|
||||
}
|
||||
@ -1027,18 +1027,27 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
|
||||
if( len && host.rodir[len - 1] == '/' )
|
||||
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 );
|
||||
else
|
||||
Sys_Error( "Changing working directory to %s failed.\n", host.rootdir );
|
||||
|
||||
FS_Init();
|
||||
|
||||
Sys_InitLog();
|
||||
|
||||
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
|
||||
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
|
||||
Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" );
|
||||
|
||||
FS_Init();
|
||||
Image_Init();
|
||||
Sound_Init();
|
||||
|
||||
@ -1048,8 +1057,16 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha
|
||||
#endif
|
||||
|
||||
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 ));
|
||||
|
||||
Image_CheckPaletteQ1 ();
|
||||
Host_InitDecals (); // reload decals
|
||||
|
||||
// DEPRECATED: by FWGS fork
|
||||
#if 0
|
||||
if( GI->secure )
|
||||
|
@ -14,7 +14,7 @@ GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "filesystem.h"
|
||||
#include "hpak.h"
|
||||
|
||||
#define HPAK_MAX_ENTRIES 0x8000
|
||||
#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_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 );
|
||||
FS_Close( f );
|
||||
|
60
engine/common/hpak.h
Normal file
60
engine/common/hpak.h
Normal file
@ -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
|
@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.h"
|
||||
#include "imagelib.h"
|
||||
#include "xash3d_mathlib.h"
|
||||
|
@ -86,6 +86,31 @@ const char *COM_OffsetNameForFunction( void *function )
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
|
||||
|
@ -391,7 +391,7 @@ qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname
|
||||
dll_user_t *hInst;
|
||||
qboolean ret = FALSE;
|
||||
|
||||
hInst = FS_FindLibrary( name, directpath );
|
||||
hInst = COM_FindLibrary( name, directpath );
|
||||
if ( !hInst ) return 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();
|
||||
|
||||
hInst = FS_FindLibrary( dllname, directpath );
|
||||
hInst = COM_FindLibrary( dllname, directpath );
|
||||
if( !hInst )
|
||||
{
|
||||
COM_PushLibraryError( va( "Failed to find library %s", dllname ) );
|
||||
|
@ -27,8 +27,12 @@ GNU General Public License for more details.
|
||||
#include "studio.h"
|
||||
#include "r_efx.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)
|
||||
@ -367,13 +371,6 @@ typedef struct ref_api_s
|
||||
void (*COM_FreeLibrary)( void *handle );
|
||||
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
|
||||
// try to create window
|
||||
// will call GL_SetupAttributes in case of REF_GL
|
||||
@ -430,6 +427,9 @@ typedef struct ref_api_s
|
||||
void (*pfnDrawNormalTriangles)( void );
|
||||
void (*pfnDrawTransparentTriangles)( void );
|
||||
render_interface_t *drawFuncs;
|
||||
|
||||
// filesystem exports
|
||||
fs_api_t *fsapi;
|
||||
} ref_api_t;
|
||||
|
||||
struct mip_s;
|
||||
|
@ -166,7 +166,7 @@ def build(bld):
|
||||
'client/vgui/*.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:
|
||||
install_path = bld.env.BINDIR
|
||||
|
File diff suppressed because it is too large
Load Diff
202
filesystem/filesystem.h
Normal file
202
filesystem/filesystem.h
Normal file
@ -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
Normal file
204
filesystem/filesystem_internal.h
Normal file
@ -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
Normal file
80
filesystem/fscallback.h
Normal file
@ -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
Normal file
394
filesystem/pak.c
Normal file
@ -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
Normal file
634
filesystem/wad.c
Normal file
@ -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
Normal file
17
filesystem/wscript
Normal file
@ -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
Normal file
678
filesystem/zip.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -95,6 +95,7 @@ void COM_ExtractFilePath( const char *path, char *dest );
|
||||
const char *COM_FileWithoutPath( const char *in );
|
||||
void COM_StripExtension( char *path );
|
||||
void COM_RemoveLineFeed( char *str );
|
||||
void COM_FixSlashes( char *pname );
|
||||
void COM_PathSlashFix( char *path );
|
||||
char COM_Hex2Char( uint8_t hex );
|
||||
void COM_Hex2String( uint8_t hex, char *str );
|
||||
|
@ -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( lumaname, sizeof( lumaname ), "%s:luma%i", 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 );
|
||||
|
||||
m_pAliasHeader->gl_texturenum[skinnum][0] =
|
||||
|
@ -478,7 +478,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type )
|
||||
case VID_SCREENSHOT:
|
||||
break;
|
||||
case VID_SNAPSHOT:
|
||||
gEngfuncs.FS_AllowDirectPaths( true );
|
||||
gEngfuncs.fsapi->AllowDirectPaths( true );
|
||||
break;
|
||||
case VID_LEVELSHOT:
|
||||
flags |= IMAGE_RESAMPLE;
|
||||
@ -509,7 +509,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type )
|
||||
|
||||
// write image
|
||||
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 );
|
||||
|
||||
return result;
|
||||
|
@ -29,7 +29,7 @@ static void R_ParseDetailTextures( const char *filename )
|
||||
texture_t *tex;
|
||||
int i;
|
||||
|
||||
afile = gEngfuncs.COM_LoadFile( filename, NULL, false );
|
||||
afile = gEngfuncs.fsapi->LoadFile( filename, NULL, false );
|
||||
if( !afile ) return;
|
||||
|
||||
pfile = (char *)afile;
|
||||
|
@ -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 );
|
||||
|
||||
if( gEngfuncs.FS_FileExists( state->modelname, false ))
|
||||
if( gEngfuncs.fsapi->FileExists( state->modelname, false ))
|
||||
state->model = gEngfuncs.Mod_ForName( state->modelname, false, true );
|
||||
else state->model = NULL;
|
||||
|
||||
|
@ -78,7 +78,7 @@ static int CheckSkybox( const char *name )
|
||||
{
|
||||
// build side name
|
||||
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++;
|
||||
|
||||
}
|
||||
@ -90,7 +90,7 @@ static int CheckSkybox( const char *name )
|
||||
{
|
||||
// build side name
|
||||
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++;
|
||||
}
|
||||
|
||||
|
@ -59,6 +59,7 @@ def build(bld):
|
||||
source = bld.path.ant_glob(['*.c'])
|
||||
|
||||
includes = ['.',
|
||||
'../filesystem',
|
||||
'../engine',
|
||||
'../engine/common',
|
||||
'../engine/server',
|
||||
|
@ -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 );
|
||||
|
||||
if( gEngfuncs.FS_FileExists( state->modelname, false ))
|
||||
if( gEngfuncs.fsapi->FileExists( state->modelname, false ))
|
||||
state->model = gEngfuncs.Mod_ForName( state->modelname, false, true );
|
||||
else state->model = NULL;
|
||||
|
||||
|
@ -30,6 +30,7 @@ def build(bld):
|
||||
source = bld.path.ant_glob(['*.c'])
|
||||
|
||||
includes = ['.',
|
||||
'../filesystem',
|
||||
'../engine',
|
||||
'../engine/common',
|
||||
'../engine/server',
|
||||
|
Loading…
Reference in New Issue
Block a user