2018-04-13 19:23:45 +03:00
/*
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 .
*/
# include "common.h"
2022-07-01 19:37:21 +03:00
# include "hpak.h"
2018-04-13 19:23:45 +03:00
# define HPAK_MAX_ENTRIES 0x8000
typedef struct hash_pack_queue_s
{
char * name ;
resource_t resource ;
size_t size ;
void * data ;
struct hash_pack_queue_s * next ;
} hash_pack_queue_t ;
2024-06-15 16:53:57 +03:00
static CVAR_DEFINE ( hpk_maxsize , " hpk_max_size " , " 64 " , FCVAR_ARCHIVE | FCVAR_PRIVILEGED , " set limit by size for all HPK-files in megabytes ( 0 - unlimited ) " ) ;
2024-06-15 17:05:52 +03:00
CVAR_DEFINE_AUTO ( hpk_custom_file , " custom.hpk " , FCVAR_ARCHIVE | FCVAR_PRIVILEGED , " set custom path for players customizations cache file " ) ;
2023-04-03 06:22:49 +03:00
static hash_pack_queue_t * gp_hpak_queue = NULL ;
static hpak_header_t hash_pack_header ;
static hpak_info_t hash_pack_info ;
2018-04-13 19:23:45 +03:00
2024-06-15 16:53:57 +03:00
static void HPAK_MaxSize_f ( void )
{
Con_Printf ( S_ERROR " hpk_maxsize is deprecated, use hpk_max_size \n " ) ;
}
2025-01-23 13:57:15 +03:00
const char * COM_ResourceTypeFromIndex ( int type )
2018-04-13 19:23:45 +03:00
{
switch ( type )
{
case t_sound : return " decal " ;
case t_skin : return " skin " ;
case t_model : return " model " ;
case t_decal : return " decal " ;
case t_generic : return " generic " ;
case t_eventscript : return " event " ;
2021-01-03 01:28:45 +00:00
case t_world : return " map " ;
2018-04-13 19:23:45 +03:00
}
return " ? " ;
}
2022-09-20 21:57:13 +03:00
static inline void HPAK_ResourceToCompat ( dresource_t * dest , resource_t * src )
{
memcpy ( dest , src , sizeof ( * dest ) ) ;
dest - > pNext = dest - > pPrev = 0xDEADBEEF ;
}
static inline void HPAK_ResourceFromCompat ( resource_t * dest , dresource_t * src )
{
memcpy ( dest , src , sizeof ( * src ) ) ;
dest - > pNext = dest - > pPrev = ( void * ) 0xDEADBEEF ;
}
2018-04-13 19:23:45 +03:00
static void HPAK_AddToQueue ( const char * name , resource_t * pResource , void * data , file_t * f )
{
hash_pack_queue_t * p ;
p = Z_Malloc ( sizeof ( hash_pack_queue_t ) ) ;
p - > name = copystring ( name ) ;
p - > resource = * pResource ;
p - > size = pResource - > nDownloadSize ;
p - > data = Z_Malloc ( p - > size ) ;
if ( data ! = NULL ) memcpy ( p - > data , data , p - > size ) ;
else if ( f ! = NULL ) FS_Read ( f , p - > data , p - > size ) ;
2024-06-19 06:46:08 +03:00
else Host_Error ( " %s: data == NULL. \n " , __func__ ) ;
2018-04-13 19:23:45 +03:00
p - > next = gp_hpak_queue ;
gp_hpak_queue = p ;
}
void HPAK_FlushHostQueue ( void )
{
hash_pack_queue_t * p ;
for ( p = gp_hpak_queue ; p ! = NULL ; p = gp_hpak_queue )
{
gp_hpak_queue = p - > next ;
HPAK_AddLump ( false , p - > name , & p - > resource , p - > data , NULL ) ;
freestring ( p - > name ) ;
Mem_Free ( p - > data ) ;
Mem_Free ( p ) ;
}
gp_hpak_queue = NULL ;
}
2024-01-27 19:48:17 +03:00
static void HPAK_CreatePak ( const char * filename , resource_t * pResource , byte * pData , file_t * fin )
2018-04-13 19:23:45 +03:00
{
int filelocation ;
string pakname ;
2019-07-13 23:25:03 +03:00
byte md5 [ 16 ] ;
2018-04-13 19:23:45 +03:00
file_t * fout ;
2025-01-14 12:11:10 +03:00
MD5Context_t ctx = { 0 } ;
2018-04-13 19:23:45 +03:00
if ( ! COM_CheckString ( filename ) )
return ;
if ( ( fin ! = NULL & & pData ! = NULL ) | | ( fin = = NULL & & pData = = NULL ) )
return ;
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2018-10-27 23:31:55 +03:00
Con_Printf ( " creating HPAK %s. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
fout = FS_Open ( pakname , " wb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! fout )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: can't write %s. \n " , __func__ , pakname ) ;
2018-04-13 19:23:45 +03:00
return ;
}
// let's hash it.
MD5Init ( & ctx ) ;
if ( pData = = NULL )
{
2019-07-13 23:25:03 +03:00
byte * temp ;
2018-04-13 19:23:45 +03:00
// there are better ways
filelocation = FS_Tell ( fin ) ;
temp = Z_Malloc ( pResource - > nDownloadSize ) ;
FS_Read ( fin , temp , pResource - > nDownloadSize ) ;
FS_Seek ( fin , filelocation , SEEK_SET ) ;
MD5Update ( & ctx , temp , pResource - > nDownloadSize ) ;
Mem_Free ( temp ) ;
}
else
{
MD5Update ( & ctx , pData , pResource - > nDownloadSize ) ;
}
MD5Final ( md5 , & ctx ) ;
if ( memcmp ( md5 , pResource - > rgucMD5_hash , 16 ) )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: bad checksum for %s. Ignored \n " , __func__ , pakname ) ;
2018-04-13 19:23:45 +03:00
return ;
}
hash_pack_header . ident = IDHPAKHEADER ;
hash_pack_header . version = IDHPAK_VERSION ;
hash_pack_header . infotableofs = 0 ;
FS_Write ( fout , & hash_pack_header , sizeof ( hash_pack_header ) ) ;
hash_pack_info . count = 1 ;
hash_pack_info . entries = Z_Malloc ( sizeof ( hpak_lump_t ) ) ;
2022-09-20 21:57:13 +03:00
HPAK_ResourceToCompat ( & hash_pack_info . entries [ 0 ] . resource , pResource ) ;
2018-04-13 19:23:45 +03:00
hash_pack_info . entries [ 0 ] . filepos = FS_Tell ( fout ) ;
hash_pack_info . entries [ 0 ] . disksize = pResource - > nDownloadSize ;
if ( pData = = NULL )
{
FS_FileCopy ( fout , fin , hash_pack_info . entries [ 0 ] . disksize ) ;
}
else
{
FS_Write ( fout , pData , hash_pack_info . entries [ 0 ] . disksize ) ;
}
filelocation = FS_Tell ( fout ) ;
FS_Write ( fout , & hash_pack_info . count , sizeof ( hash_pack_info . count ) ) ;
FS_Write ( fout , & hash_pack_info . entries [ 0 ] , sizeof ( hpak_lump_t ) ) ;
if ( hash_pack_info . entries )
Mem_Free ( hash_pack_info . entries ) ;
memset ( & hash_pack_info , 0 , sizeof ( hpak_info_t ) ) ;
hash_pack_header . infotableofs = filelocation ;
FS_Seek ( fout , 0 , SEEK_SET ) ;
FS_Write ( fout , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
FS_Close ( fout ) ;
}
static qboolean HPAK_FindResource ( hpak_info_t * hpk , byte * hash , resource_t * pResource )
{
int i ;
for ( i = 0 ; i < hpk - > count ; i + + )
{
if ( ! memcmp ( hpk - > entries [ i ] . resource . rgucMD5_hash , hash , 16 ) )
{
if ( pResource )
2022-09-20 21:57:13 +03:00
HPAK_ResourceFromCompat ( pResource , & hpk - > entries [ i ] . resource ) ;
2018-04-13 19:23:45 +03:00
return true ;
}
}
return false ;
}
void HPAK_AddLump ( qboolean bUseQueue , const char * name , resource_t * pResource , byte * pData , file_t * pFile )
{
int i , j , position , length ;
hpak_lump_t * pCurrentEntry = NULL ;
string srcname , dstname ;
hpak_info_t srcpak , dstpak ;
file_t * file_src ;
file_t * file_dst ;
2019-07-13 23:25:03 +03:00
byte md5 [ 16 ] ;
2025-01-14 12:11:10 +03:00
MD5Context_t ctx = { 0 } ;
2018-04-13 19:23:45 +03:00
if ( pData = = NULL & & pFile = = NULL )
return ;
2022-11-25 20:56:11 +04:00
if ( pResource - > nDownloadSize < HPAK_ENTRY_MIN_SIZE | | pResource - > nDownloadSize > HPAK_ENTRY_MAX_SIZE )
2018-04-13 19:23:45 +03:00
{
Con_Printf ( S_ERROR " %s: invalid size %s \n " , name , Q_pretifymem ( pResource - > nDownloadSize , 2 ) ) ;
return ;
}
// hash it
MD5Init ( & ctx ) ;
2022-11-26 05:34:05 +04:00
if ( ! pData )
2018-04-13 19:23:45 +03:00
{
2019-07-13 23:25:03 +03:00
byte * temp ;
2018-04-13 19:23:45 +03:00
// there are better ways
position = FS_Tell ( pFile ) ;
temp = Z_Malloc ( pResource - > nDownloadSize ) ;
FS_Read ( pFile , temp , pResource - > nDownloadSize ) ;
FS_Seek ( pFile , position , SEEK_SET ) ;
MD5Update ( & ctx , temp , pResource - > nDownloadSize ) ;
Mem_Free ( temp ) ;
}
else
{
MD5Update ( & ctx , pData , pResource - > nDownloadSize ) ;
}
MD5Final ( md5 , & ctx ) ;
if ( memcmp ( md5 , pResource - > rgucMD5_hash , 16 ) )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: bad checksum for %s. Ignored \n " , __func__ , pResource - > szFileName ) ;
2018-04-13 19:23:45 +03:00
return ;
}
if ( bUseQueue )
{
HPAK_AddToQueue ( name , pResource , pData , pFile ) ;
return ;
}
Q_strncpy ( srcname , name , sizeof ( srcname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( srcname , " .hpk " , sizeof ( srcname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
file_src = FS_Open ( srcname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! file_src )
{
// just create new pack
HPAK_CreatePak ( name , pResource , pData , pFile ) ;
return ;
}
Q_strncpy ( dstname , srcname , sizeof ( dstname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( dstname , " .hp2 " , sizeof ( dstname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
file_dst = FS_Open ( dstname , " wb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! file_dst )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: couldn't open %s. \n " , __func__ , srcname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
return ;
}
// load headers
FS_Read ( file_src , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
if ( hash_pack_header . version ! = IDHPAK_VERSION )
{
// we don't check the HPAK bit for some reason.
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s does not have a valid header. \n " , __func__ , srcname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
2019-07-12 16:13:03 +01:00
return ;
2018-04-13 19:23:45 +03:00
}
length = FS_FileLength ( file_src ) ;
FS_Seek ( file_src , 0 , SEEK_SET ) ; // rewind to start of file
FS_FileCopy ( file_dst , file_src , length ) ;
FS_Seek ( file_src , hash_pack_header . infotableofs , SEEK_SET ) ;
FS_Read ( file_src , & srcpak . count , sizeof ( srcpak . count ) ) ;
if ( srcpak . count < 1 | | srcpak . count > HPAK_MAX_ENTRIES )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s contain too many lumps. \n " , __func__ , srcname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
return ;
}
// load the data
srcpak . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * srcpak . count ) ;
FS_Read ( file_src , srcpak . entries , sizeof ( hpak_lump_t ) * srcpak . count ) ;
// check if already exists
if ( HPAK_FindResource ( & srcpak , pResource - > rgucMD5_hash , NULL ) )
{
Z_Free ( srcpak . entries ) ;
2019-07-12 16:13:03 +01:00
FS_Close ( file_src ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_dst ) ;
FS_Delete ( dstname ) ;
return ;
}
// make a new container
dstpak . count = srcpak . count + 1 ;
dstpak . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * dstpak . count ) ;
2022-11-25 20:56:11 +04:00
memcpy ( dstpak . entries , srcpak . entries , sizeof ( hpak_lump_t ) * srcpak . count ) ;
2018-04-13 19:23:45 +03:00
2022-11-26 05:34:05 +04:00
// check is there are entry with same hash
2018-04-13 19:23:45 +03:00
for ( i = 0 ; i < srcpak . count ; i + + )
{
2022-11-26 05:34:05 +04:00
if ( memcmp ( md5 , srcpak . entries [ i ] . resource . rgucMD5_hash , 16 ) = = 0 )
2018-04-13 19:23:45 +03:00
{
pCurrentEntry = & dstpak . entries [ i ] ;
for ( j = i ; j < srcpak . count ; j + + )
dstpak . entries [ j + 1 ] = srcpak . entries [ j ] ;
}
}
if ( ! pCurrentEntry )
pCurrentEntry = & dstpak . entries [ dstpak . count - 1 ] ;
memset ( pCurrentEntry , 0 , sizeof ( hpak_lump_t ) ) ;
FS_Seek ( file_dst , hash_pack_header . infotableofs , SEEK_SET ) ;
2022-09-20 21:57:13 +03:00
HPAK_ResourceToCompat ( & pCurrentEntry - > resource , pResource ) ;
2018-04-13 19:23:45 +03:00
pCurrentEntry - > filepos = FS_Tell ( file_dst ) ;
pCurrentEntry - > disksize = pResource - > nDownloadSize ;
2022-11-26 05:34:05 +04:00
if ( ! pData )
FS_FileCopy ( file_dst , pFile , pCurrentEntry - > disksize ) ;
else
FS_Write ( file_dst , pData , pCurrentEntry - > disksize ) ;
2018-04-13 19:23:45 +03:00
hash_pack_header . infotableofs = FS_Tell ( file_dst ) ;
FS_Write ( file_dst , & dstpak . count , sizeof ( dstpak . count ) ) ;
for ( i = 0 ; i < dstpak . count ; i + + )
{
FS_Write ( file_dst , & dstpak . entries [ i ] , sizeof ( hpak_lump_t ) ) ;
}
// finalize
if ( srcpak . entries )
Mem_Free ( srcpak . entries ) ;
if ( dstpak . entries )
Mem_Free ( dstpak . entries ) ;
FS_Seek ( file_dst , 0 , SEEK_SET ) ;
FS_Write ( file_dst , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
2019-07-12 16:13:03 +01:00
FS_Close ( file_src ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_dst ) ;
FS_Delete ( srcname ) ;
FS_Rename ( dstname , srcname ) ;
}
2023-04-03 06:04:18 +03:00
static qboolean HPAK_Validate ( const char * filename , qboolean quiet , qboolean delete )
2018-04-13 19:23:45 +03:00
{
file_t * f ;
hpak_lump_t * dataDir ;
hpak_header_t hdr ;
byte * dataPak ;
int i , num_lumps ;
MD5Context_t MD5_Hash ;
string pakname ;
2022-09-20 21:57:13 +03:00
dresource_t * pRes ;
2019-07-13 23:25:03 +03:00
byte md5 [ 16 ] ;
2018-04-13 19:23:45 +03:00
if ( quiet ) HPAK_FlushHostQueue ( ) ;
// not an error - just flush queue
2020-11-22 11:49:39 +05:00
if ( ! COM_CheckString ( filename ) )
2018-04-13 19:23:45 +03:00
return true ;
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f )
{
2024-04-21 20:03:25 +03:00
if ( ! quiet )
Con_DPrintf ( S_ERROR " Couldn't find %s. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
return true ;
}
2018-10-27 23:31:55 +03:00
if ( ! quiet ) Con_Printf ( " Validating %s \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Read ( f , & hdr , sizeof ( hdr ) ) ;
if ( hdr . ident ! = IDHPAKHEADER | | hdr . version ! = IDHPAK_VERSION )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s does not have a valid HPAK header. \n " , __func__ , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
2023-04-03 06:04:18 +03:00
if ( delete ) FS_Delete ( pakname ) ;
2018-04-13 19:23:45 +03:00
return false ;
}
FS_Seek ( f , hdr . infotableofs , SEEK_SET ) ;
FS_Read ( f , & num_lumps , sizeof ( num_lumps ) ) ;
2022-07-01 19:37:21 +03:00
if ( num_lumps < 1 | | num_lumps > HPAK_MAX_ENTRIES )
2018-04-13 19:23:45 +03:00
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s has too many lumps %u. \n " , __func__ , pakname , num_lumps ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
2023-04-03 06:04:18 +03:00
if ( delete ) FS_Delete ( pakname ) ;
2018-04-13 19:23:45 +03:00
return false ;
}
2018-10-27 23:31:55 +03:00
if ( ! quiet ) Con_Printf ( " # of Entries: %i \n " , num_lumps ) ;
2018-04-13 19:23:45 +03:00
dataDir = Z_Malloc ( sizeof ( hpak_lump_t ) * num_lumps ) ;
FS_Read ( f , dataDir , sizeof ( hpak_lump_t ) * num_lumps ) ;
2018-10-27 23:31:55 +03:00
if ( ! quiet ) Con_Printf ( " # Type Size FileName : MD5 Hash \n " ) ;
2018-04-13 19:23:45 +03:00
for ( i = 0 ; i < num_lumps ; i + + )
{
2022-11-25 20:56:11 +04:00
if ( dataDir [ i ] . disksize < HPAK_ENTRY_MIN_SIZE | | dataDir [ i ] . disksize > HPAK_ENTRY_MAX_SIZE )
2018-04-13 19:23:45 +03:00
{
// odd max size
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: lump %i has invalid size %s \n " , __func__ , i , Q_pretifymem ( dataDir [ i ] . disksize , 2 ) ) ;
2018-04-13 19:23:45 +03:00
Mem_Free ( dataDir ) ;
2023-04-03 06:04:18 +03:00
FS_Close ( f ) ;
if ( delete ) FS_Delete ( pakname ) ;
2018-04-13 19:23:45 +03:00
return false ;
}
dataPak = Z_Malloc ( dataDir [ i ] . disksize ) ;
FS_Seek ( f , dataDir [ i ] . filepos , SEEK_SET ) ;
FS_Read ( f , dataPak , dataDir [ i ] . disksize ) ;
memset ( & MD5_Hash , 0 , sizeof ( MD5Context_t ) ) ;
MD5Init ( & MD5_Hash ) ;
MD5Update ( & MD5_Hash , dataPak , dataDir [ i ] . disksize ) ;
MD5Final ( md5 , & MD5_Hash ) ;
pRes = & dataDir [ i ] . resource ;
2023-04-17 04:39:21 +03:00
if ( ! quiet )
{
2025-01-23 13:57:15 +03:00
Con_Printf ( " %i: %s %s %s: " , i , COM_ResourceTypeFromIndex ( pRes - > type ) ,
2023-04-17 04:39:21 +03:00
Q_pretifymem ( pRes - > nDownloadSize , 2 ) , pRes - > szFileName ) ;
}
2018-04-13 19:23:45 +03:00
if ( memcmp ( md5 , pRes - > rgucMD5_hash , 0x10 ) )
{
if ( quiet )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s has invalid checksum. \n " , __func__ , pakname ) ;
2018-04-13 19:23:45 +03:00
Mem_Free ( dataPak ) ;
Mem_Free ( dataDir ) ;
FS_Close ( f ) ;
2023-04-03 06:04:18 +03:00
if ( delete ) FS_Delete ( pakname ) ;
2018-04-13 19:23:45 +03:00
return false ;
}
2018-10-27 23:31:55 +03:00
else Con_DPrintf ( S_ERROR " failed \n " ) ;
2018-04-13 19:23:45 +03:00
}
else
{
2018-10-27 23:31:55 +03:00
if ( ! quiet ) Con_Printf ( " OK \n " ) ;
2018-04-13 19:23:45 +03:00
}
// at this point, it's passed our checks.
Mem_Free ( dataPak ) ;
}
Mem_Free ( dataDir ) ;
FS_Close ( f ) ;
return true ;
}
void HPAK_CheckIntegrity ( const char * filename )
{
string pakname ;
2020-11-22 11:49:39 +05:00
if ( ! COM_CheckString ( filename ) )
2018-04-13 19:23:45 +03:00
return ;
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2023-04-03 06:04:18 +03:00
HPAK_Validate ( pakname , true , true ) ;
2018-04-13 19:23:45 +03:00
}
void HPAK_CheckSize ( const char * filename )
{
string pakname ;
int maxsize ;
2023-04-03 06:22:49 +03:00
maxsize = hpk_maxsize . value ;
2018-04-13 19:23:45 +03:00
if ( maxsize < = 0 ) return ;
2020-11-22 11:49:39 +05:00
if ( ! COM_CheckString ( filename ) )
2018-04-13 19:23:45 +03:00
return ;
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2024-06-11 07:47:01 +03:00
if ( FS_FileSize ( pakname , false ) > ( maxsize * 1024 * 1024 ) )
2018-04-13 19:23:45 +03:00
{
2023-04-03 06:22:49 +03:00
Con_Printf ( " Server: Size of %s > %f MB, deleting. \n " , filename , hpk_maxsize . value ) ;
Log_Printf ( " Server: Size of %s > %f MB, deleting. \n " , filename , hpk_maxsize . value ) ;
2018-04-13 19:23:45 +03:00
FS_Delete ( filename ) ;
}
}
qboolean HPAK_ResourceForHash ( const char * filename , byte * hash , resource_t * pResource )
{
hpak_info_t directory ;
hpak_header_t header ;
string pakname ;
qboolean bFound ;
file_t * f ;
hash_pack_queue_t * p ;
if ( ! COM_CheckString ( filename ) )
return false ;
2021-01-03 01:28:45 +00:00
2018-04-13 19:23:45 +03:00
for ( p = gp_hpak_queue ; p ! = NULL ; p = p - > next )
{
if ( ! Q_stricmp ( p - > name , filename ) & & ! memcmp ( p - > resource . rgucMD5_hash , hash , 16 ) )
{
if ( pResource ! = NULL )
* pResource = p - > resource ;
return true ;
}
}
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f ) return false ;
FS_Read ( f , & header , sizeof ( header ) ) ;
if ( header . ident ! = IDHPAKHEADER )
{
FS_Close ( f ) ;
return false ;
}
if ( header . version ! = IDHPAK_VERSION )
{
FS_Close ( f ) ;
return false ;
}
FS_Seek ( f , header . infotableofs , SEEK_SET ) ;
FS_Read ( f , & directory . count , sizeof ( directory . count ) ) ;
if ( directory . count < 1 | | directory . count > HPAK_MAX_ENTRIES )
{
FS_Close ( f ) ;
return false ;
}
directory . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * directory . count ) ;
FS_Read ( f , directory . entries , sizeof ( hpak_lump_t ) * directory . count ) ;
bFound = HPAK_FindResource ( & directory , hash , pResource ) ;
Mem_Free ( directory . entries ) ;
FS_Close ( f ) ;
return bFound ;
}
static qboolean HPAK_ResourceForIndex ( const char * filename , int index , resource_t * pResource )
{
hpak_header_t header ;
hpak_info_t directory ;
string pakname ;
file_t * f ;
2020-11-22 11:49:39 +05:00
if ( ! COM_CheckString ( filename ) )
2018-04-13 19:23:45 +03:00
return false ;
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " couldn't open %s. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
return false ;
}
FS_Read ( f , & header , sizeof ( header ) ) ;
if ( header . ident ! = IDHPAKHEADER )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s is not an HPAK file \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
if ( header . version ! = IDHPAK_VERSION )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid version (%i should be %i). \n " , pakname , header . version , IDHPAK_VERSION ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
FS_Seek ( f , header . infotableofs , SEEK_SET ) ;
FS_Read ( f , & directory . count , sizeof ( directory . count ) ) ;
if ( directory . count < 1 | | directory . count > HPAK_MAX_ENTRIES )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has too many lumps %u. \n " , pakname , directory . count ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
if ( index < 1 | | index > directory . count )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s, lump with index %i doesn't exist. \n " , pakname , index ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
directory . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * directory . count ) ;
FS_Read ( f , directory . entries , sizeof ( hpak_lump_t ) * directory . count ) ;
2022-09-20 21:57:13 +03:00
HPAK_ResourceFromCompat ( pResource , & directory . entries [ index - 1 ] . resource ) ;
2018-04-13 19:23:45 +03:00
Z_Free ( directory . entries ) ;
FS_Close ( f ) ;
return true ;
}
qboolean HPAK_GetDataPointer ( const char * filename , resource_t * pResource , byte * * buffer , int * bufsize )
{
byte * tmpbuf ;
string pakname ;
hpak_header_t header ;
hpak_info_t directory ;
hpak_lump_t * entry ;
hash_pack_queue_t * p ;
file_t * f ;
int i ;
if ( ! COM_CheckString ( filename ) )
return false ;
if ( buffer ) * buffer = NULL ;
if ( bufsize ) * bufsize = 0 ;
for ( p = gp_hpak_queue ; p ! = NULL ; p = p - > next )
{
2022-09-19 06:02:41 +03:00
if ( ! Q_stricmp ( p - > name , filename ) & & ! memcmp ( p - > resource . rgucMD5_hash , pResource - > rgucMD5_hash , 16 ) )
2018-04-13 19:23:45 +03:00
{
if ( buffer )
{
tmpbuf = Z_Malloc ( p - > size ) ;
memcpy ( tmpbuf , p - > data , p - > size ) ;
* buffer = tmpbuf ;
}
if ( bufsize )
* bufsize = p - > size ;
return true ;
}
}
Q_strncpy ( pakname , filename , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f ) return false ;
FS_Read ( f , & header , sizeof ( header ) ) ;
if ( header . ident ! = IDHPAKHEADER )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s it's not a HPK file. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
if ( header . version ! = IDHPAK_VERSION )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid version (%i should be %i). \n " , pakname , header . version , IDHPAK_VERSION ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
FS_Seek ( f , header . infotableofs , SEEK_SET ) ;
FS_Read ( f , & directory . count , sizeof ( directory . count ) ) ;
if ( directory . count < 1 | | directory . count > HPAK_MAX_ENTRIES )
{
2024-06-19 06:46:08 +03:00
Con_DPrintf ( S_ERROR " %s: %s has too many lumps %u. \n " , __func__ , filename , directory . count ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return false ;
}
directory . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * directory . count ) ;
FS_Read ( f , directory . entries , sizeof ( hpak_lump_t ) * directory . count ) ;
for ( i = 0 ; i < directory . count ; i + + )
{
entry = & directory . entries [ i ] ;
2022-09-19 06:02:41 +03:00
if ( entry - > filepos > 0 & &
entry - > disksize > 0 & &
! memcmp ( entry - > resource . rgucMD5_hash , pResource - > rgucMD5_hash , 16 ) )
2018-04-13 19:23:45 +03:00
{
FS_Seek ( f , entry - > filepos , SEEK_SET ) ;
2022-09-19 06:02:41 +03:00
if ( buffer )
2018-04-13 19:23:45 +03:00
{
tmpbuf = Z_Malloc ( entry - > disksize ) ;
FS_Read ( f , tmpbuf , entry - > disksize ) ;
* buffer = tmpbuf ;
}
if ( bufsize )
* bufsize = entry - > disksize ;
Mem_Free ( directory . entries ) ;
FS_Close ( f ) ;
return true ;
}
}
Mem_Free ( directory . entries ) ;
FS_Close ( f ) ;
return false ;
}
void HPAK_RemoveLump ( const char * name , resource_t * pResource )
{
string read_path ;
string save_path ;
file_t * file_src ;
file_t * file_dst ;
hpak_info_t hpak_read ;
hpak_info_t hpak_save ;
int i , j ;
if ( ! COM_CheckString ( name ) | | ! pResource )
return ;
HPAK_FlushHostQueue ( ) ;
Q_strncpy ( read_path , name , sizeof ( read_path ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( read_path , " .hpk " , sizeof ( read_path ) ) ;
2018-04-13 19:23:45 +03:00
2022-11-22 18:54:33 +04:00
file_src = FS_Open ( read_path , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! file_src )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s couldn't open. \n " , read_path ) ;
2018-04-13 19:23:45 +03:00
return ;
}
Q_strncpy ( save_path , read_path , sizeof ( save_path ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( save_path , " .hp2 " , sizeof ( save_path ) ) ;
2022-11-22 18:54:33 +04:00
file_dst = FS_Open ( save_path , " wb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! file_dst )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s couldn't open. \n " , save_path ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
return ;
}
FS_Seek ( file_src , 0 , SEEK_SET ) ;
FS_Seek ( file_dst , 0 , SEEK_SET ) ;
// header copy
FS_Read ( file_src , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
FS_Write ( file_dst , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
if ( hash_pack_header . ident ! = IDHPAKHEADER | | hash_pack_header . version ! = IDHPAK_VERSION )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid header. \n " , read_path ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
FS_Delete ( save_path ) ; // delete temp file
return ;
}
FS_Seek ( file_src , hash_pack_header . infotableofs , SEEK_SET ) ;
FS_Read ( file_src , & hpak_read . count , sizeof ( hpak_read . count ) ) ;
if ( hpak_read . count < 1 | | hpak_read . count > HPAK_MAX_ENTRIES )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid number of lumps. \n " , read_path ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
FS_Delete ( save_path ) ; // delete temp file
return ;
}
if ( hpak_read . count = = 1 )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_WARN " %s only has one element, so HPAK will be removed \n " , read_path ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
FS_Delete ( read_path ) ;
FS_Delete ( save_path ) ;
return ;
}
hpak_save . count = hpak_read . count - 1 ;
hpak_read . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * hpak_read . count ) ;
hpak_save . entries = Z_Malloc ( sizeof ( hpak_lump_t ) * hpak_save . count ) ;
FS_Read ( file_src , hpak_read . entries , sizeof ( hpak_lump_t ) * hpak_read . count ) ;
if ( ! HPAK_FindResource ( & hpak_read , pResource - > rgucMD5_hash , NULL ) )
{
2018-11-16 15:25:04 +03:00
Con_DPrintf ( S_ERROR " HPAK %s doesn't contain specified lump: %s \n " , read_path , pResource - > szFileName ) ;
2018-04-13 19:23:45 +03:00
Mem_Free ( hpak_read . entries ) ;
Mem_Free ( hpak_save . entries ) ;
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
FS_Delete ( save_path ) ;
return ;
}
2018-10-27 23:31:55 +03:00
Con_Printf ( " Removing %s from HPAK %s. \n " , pResource - > szFileName , read_path ) ;
2018-04-13 19:23:45 +03:00
// If there's a collision, we've just corrupted this hpak.
for ( i = 0 , j = 0 ; i < hpak_read . count ; i + + )
{
if ( ! memcmp ( hpak_read . entries [ i ] . resource . rgucMD5_hash , pResource - > rgucMD5_hash , 16 ) )
continue ;
hpak_save . entries [ j ] = hpak_read . entries [ i ] ;
hpak_save . entries [ j ] . filepos = FS_Tell ( file_dst ) ;
FS_Seek ( file_src , hpak_read . entries [ j ] . filepos , SEEK_SET ) ;
FS_FileCopy ( file_dst , file_src , hpak_save . entries [ j ] . disksize ) ;
j + + ;
}
hash_pack_header . infotableofs = FS_Tell ( file_dst ) ;
FS_Write ( file_dst , & hpak_save . count , sizeof ( hpak_save . count ) ) ;
for ( i = 0 ; i < hpak_save . count ; i + + )
FS_Write ( file_dst , & hpak_save . entries [ i ] , sizeof ( hpak_lump_t ) ) ;
FS_Seek ( file_dst , 0 , SEEK_SET ) ;
FS_Write ( file_dst , & hash_pack_header , sizeof ( hpak_header_t ) ) ;
Mem_Free ( hpak_read . entries ) ;
Mem_Free ( hpak_save . entries ) ;
FS_Close ( file_src ) ;
FS_Close ( file_dst ) ;
FS_Delete ( read_path ) ;
FS_Rename ( save_path , read_path ) ;
}
2024-01-27 19:48:17 +03:00
static void HPAK_List_f ( void )
2018-04-13 19:23:45 +03:00
{
int nCurrent ;
hpak_header_t header ;
hpak_info_t directory ;
hpak_lump_t * entry ;
string lumpname ;
string pakname ;
const char * type ;
const char * size ;
file_t * f ;
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( S_USAGE " hpklist <hpk> \n " ) ;
return ;
}
HPAK_FlushHostQueue ( ) ;
Q_strncpy ( pakname , Cmd_Argv ( 1 ) , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
Con_Printf ( " Contents for %s. \n " , pakname ) ;
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " couldn't open %s. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
return ;
}
FS_Read ( f , & header , sizeof ( hpak_header_t ) ) ;
if ( header . ident ! = IDHPAKHEADER )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s is not an HPAK file \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
if ( header . version ! = IDHPAK_VERSION )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid version (%i should be %i). \n " , pakname , header . version , IDHPAK_VERSION ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
FS_Seek ( f , header . infotableofs , SEEK_SET ) ;
FS_Read ( f , & directory . count , sizeof ( directory . count ) ) ;
if ( directory . count < 1 | | directory . count > HPAK_MAX_ENTRIES )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has too many lumps %u. \n " , pakname , directory . count ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
Con_Printf ( " # of Entries: %i \n " , directory . count ) ;
Con_Printf ( " # Type Size FileName : MD5 Hash \n " ) ;
directory . entries = Z_Malloc ( directory . count * sizeof ( hpak_lump_t ) ) ;
FS_Read ( f , directory . entries , directory . count * sizeof ( hpak_lump_t ) ) ;
for ( nCurrent = 0 ; nCurrent < directory . count ; nCurrent + + )
{
entry = & directory . entries [ nCurrent ] ;
2023-04-26 04:04:48 +03:00
COM_FileBase ( entry - > resource . szFileName , lumpname , sizeof ( lumpname ) ) ;
2025-01-23 13:57:15 +03:00
type = COM_ResourceTypeFromIndex ( entry - > resource . type ) ;
2018-04-13 19:23:45 +03:00
size = Q_memprint ( entry - > resource . nDownloadSize ) ;
Con_Printf ( " %i: %10s %s %s \n : %s \n " , nCurrent + 1 , type , size , lumpname , MD5_Print ( entry - > resource . rgucMD5_hash ) ) ;
}
if ( directory . entries )
Mem_Free ( directory . entries ) ;
FS_Close ( f ) ;
}
2024-01-27 19:48:17 +03:00
static void HPAK_Extract_f ( void )
2018-04-13 19:23:45 +03:00
{
int nCurrent ;
hpak_header_t header ;
hpak_info_t directory ;
hpak_lump_t * entry ;
string lumpname ;
string pakname ;
string szFileOut ;
int nIndex ;
byte * pData ;
int nDataSize ;
const char * type ;
const char * size ;
file_t * f ;
if ( Cmd_Argc ( ) ! = 3 )
{
Con_Printf ( S_USAGE " hpkextract hpkname [all | single index] \n " ) ;
return ;
}
if ( ! Q_stricmp ( Cmd_Argv ( 2 ) , " all " ) )
{
nIndex = - 1 ;
}
else
{
nIndex = Q_atoi ( Cmd_Argv ( 2 ) ) ;
}
HPAK_FlushHostQueue ( ) ;
Q_strncpy ( pakname , Cmd_Argv ( 1 ) , sizeof ( pakname ) ) ;
2023-04-26 04:57:28 +03:00
COM_ReplaceExtension ( pakname , " .hpk " , sizeof ( pakname ) ) ;
2018-04-13 19:23:45 +03:00
Con_Printf ( " Contents for %s. \n " , pakname ) ;
2022-11-22 18:54:33 +04:00
f = FS_Open ( pakname , " rb " , true ) ;
2018-04-13 19:23:45 +03:00
if ( ! f )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " couldn't open %s. \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
return ;
}
FS_Read ( f , & header , sizeof ( hpak_header_t ) ) ;
if ( header . ident ! = IDHPAKHEADER )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s is not an HPAK file \n " , pakname ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
if ( header . version ! = IDHPAK_VERSION )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has invalid version (%i should be %i). \n " , pakname , header . version , IDHPAK_VERSION ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
FS_Seek ( f , header . infotableofs , SEEK_SET ) ;
FS_Read ( f , & directory . count , sizeof ( directory . count ) ) ;
if ( directory . count < 1 | | directory . count > HPAK_MAX_ENTRIES )
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " %s has too many lumps %u. \n " , pakname , directory . count ) ;
2018-04-13 19:23:45 +03:00
FS_Close ( f ) ;
return ;
}
if ( nIndex = = - 1 ) Con_Printf ( " Extracting all lumps from %s. \n " , pakname ) ;
else Con_Printf ( " Extracting lump %i from %s \n " , nIndex , pakname ) ;
directory . entries = Z_Malloc ( directory . count * sizeof ( hpak_lump_t ) ) ;
FS_Read ( f , directory . entries , directory . count * sizeof ( hpak_lump_t ) ) ;
for ( nCurrent = 0 ; nCurrent < directory . count ; nCurrent + + )
{
entry = & directory . entries [ nCurrent ] ;
if ( nIndex ! = - 1 & & nIndex ! = nCurrent )
continue ;
2023-04-26 04:04:48 +03:00
COM_FileBase ( entry - > resource . szFileName , lumpname , sizeof ( lumpname ) ) ;
2025-01-23 13:57:15 +03:00
type = COM_ResourceTypeFromIndex ( entry - > resource . type ) ;
2018-04-13 19:23:45 +03:00
size = Q_memprint ( entry - > resource . nDownloadSize ) ;
Con_Printf ( " Extracting %i: %10s %s %s \n " , nCurrent + 1 , type , size , lumpname ) ;
2022-11-25 20:56:11 +04:00
if ( entry - > disksize < HPAK_ENTRY_MIN_SIZE | | entry - > disksize > HPAK_ENTRY_MAX_SIZE )
2018-04-13 19:23:45 +03:00
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_WARN " Unable to extract data, size invalid: %s \n " , Q_memprint ( entry - > disksize ) ) ;
2018-04-13 19:23:45 +03:00
continue ;
}
nDataSize = entry - > disksize ;
pData = Z_Malloc ( nDataSize + 1 ) ;
FS_Seek ( f , entry - > filepos , SEEK_SET ) ;
FS_Read ( f , pData , nDataSize ) ;
2024-10-26 05:15:09 +03:00
Q_snprintf ( szFileOut , sizeof ( szFileOut ) , " hpklmps/lmp%04i.bmp " , nCurrent ) ;
2018-04-13 19:23:45 +03:00
FS_WriteFile ( szFileOut , pData , nDataSize ) ;
if ( pData ) Mem_Free ( pData ) ;
}
if ( directory . entries )
Mem_Free ( directory . entries ) ;
FS_Close ( f ) ;
}
2024-01-27 19:48:17 +03:00
static void HPAK_Remove_f ( void )
2018-04-13 19:23:45 +03:00
{
resource_t resource ;
HPAK_FlushHostQueue ( ) ;
if ( Cmd_Argc ( ) ! = 3 )
{
Con_Printf ( S_USAGE " hpkremove <hpk> <index> \n " ) ;
return ;
}
if ( HPAK_ResourceForIndex ( Cmd_Argv ( 1 ) , Q_atoi ( Cmd_Argv ( 2 ) ) , & resource ) )
{
HPAK_RemoveLump ( Cmd_Argv ( 1 ) , & resource ) ;
}
else
{
2018-10-27 23:31:55 +03:00
Con_DPrintf ( S_ERROR " Could not locate resource %i in %s \n " , Q_atoi ( Cmd_Argv ( 2 ) ) , Cmd_Argv ( 1 ) ) ;
2018-04-13 19:23:45 +03:00
}
}
2024-01-27 19:48:17 +03:00
static void HPAK_Validate_f ( void )
2018-04-13 19:23:45 +03:00
{
if ( Cmd_Argc ( ) ! = 2 )
{
Con_Printf ( S_USAGE " hpkval <filename> \n " ) ;
return ;
}
2023-04-03 06:04:18 +03:00
HPAK_Validate ( Cmd_Argv ( 1 ) , false , false ) ;
2018-04-13 19:23:45 +03:00
}
void HPAK_Init ( void )
{
2021-11-02 12:51:47 +06:00
Cmd_AddRestrictedCommand ( " hpklist " , HPAK_List_f , " list all files in specified HPK-file " ) ;
Cmd_AddRestrictedCommand ( " hpkremove " , HPAK_Remove_f , " remove specified file from HPK-file " ) ;
Cmd_AddRestrictedCommand ( " hpkval " , HPAK_Validate_f , " validate specified HPK-file " ) ;
Cmd_AddRestrictedCommand ( " hpkextract " , HPAK_Extract_f , " extract all lumps from specified HPK-file " ) ;
2024-06-15 16:53:57 +03:00
Cmd_AddRestrictedCommand ( " hpk_maxsize " , HPAK_MaxSize_f , " deprecation notice for hpk_maxsize " ) ;
2023-04-03 06:22:49 +03:00
Cvar_RegisterVariable ( & hpk_maxsize ) ;
2024-06-15 17:05:52 +03:00
Cvar_RegisterVariable ( & hpk_custom_file ) ;
2018-04-13 19:23:45 +03:00
gp_hpak_queue = NULL ;
2018-04-23 21:49:37 +03:00
}