Browse Source
The goal is to share filesystem code between engine and utilities and provide C++ VFileSystem interface in the futurepull/2/head
Alibek Omarov
2 years ago
35 changed files with 3262 additions and 2439 deletions
@ -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
|
|
@ -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(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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
|
@ -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
|
@ -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 ); |
||||||
|
} |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -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) |
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue