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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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