jedi-academy/codemp/qcommon/files_console.cpp

1032 lines
19 KiB
C++

#include "../game/q_shared.h"
#include "qcommon.h"
#include "files.h"
#include "../win32/win_file.h"
#include "../zlib/zlib.h"
static cvar_t *fs_openorder;
// Zlib Tech Ref says decompression should use about 44kb. I'll
// go with 64kb as a safety factor...
#define ZI_STACKSIZE (64*1024)
static char* zi_stackTop = NULL;
static char* zi_stackBase = NULL;
//GOB stuff
//===========================================================================
struct gi_handleTable
{
wfhandle_t file;
bool used;
};
static gi_handleTable *gi_handles = NULL;
static int gi_cacheHandle = 0;
static GOBFSHandle gi_open(GOBChar* name, GOBAccessType type)
{
if (type != GOBACCESS_READ) return (GOBFSHandle)0xFFFFFFFF;
int f;
for (f = 0; f < MAX_FILE_HANDLES; ++f)
{
if (!gi_handles[f].used) break;
}
if (f == MAX_FILE_HANDLES) return (GOBFSHandle)0xFFFFFFFF;
gi_handles[f].file = WF_Open(name, true, strstr(name, "assets.gob") ? true : false);
if (gi_handles[f].file < 0) return (GOBFSHandle)0xFFFFFFFF;
gi_handles[f].used = true;
return (GOBFSHandle)f;
}
static GOBBool gi_close(GOBFSHandle* handle)
{
WF_Close(gi_handles[(int)*handle].file);
gi_handles[(int)*handle].used = false;
return GOB_TRUE;
}
static GOBInt32 gi_read(GOBFSHandle handle, GOBVoid* buffer, GOBInt32 size)
{
return WF_Read(buffer, size, gi_handles[(int)handle].file);
}
static GOBInt32 gi_seek(GOBFSHandle handle, GOBInt32 offset, GOBSeekType type)
{
int _type;
switch (type) {
case GOBSEEK_START: _type = SEEK_SET; break;
case GOBSEEK_CURRENT: _type = SEEK_CUR; break;
case GOBSEEK_END: _type = SEEK_END; break;
default: assert(0); _type = SEEK_SET; break;
}
return WF_Seek(offset, _type, gi_handles[(int)handle].file);
}
static GOBVoid* gi_alloc(GOBUInt32 size)
{
return Z_Malloc(size, TAG_FILESYS, qfalse, 32);
}
static GOBVoid gi_free(GOBVoid* ptr)
{
Z_Free(ptr);
}
static GOBBool cache_open(GOBUInt32 size)
{
for (gi_cacheHandle = 0; gi_cacheHandle < MAX_FILE_HANDLES; ++gi_cacheHandle)
{
if (!gi_handles[gi_cacheHandle].used) break;
}
if (gi_cacheHandle == MAX_FILE_HANDLES) return GOB_FALSE;
gi_handles[gi_cacheHandle].file = WF_Open("z:\\jedi.swap", false, true);
if (gi_handles[gi_cacheHandle].file < 0) return GOB_FALSE;
if (!WF_Resize(size, gi_handles[gi_cacheHandle].file))
{
WF_Close(gi_handles[gi_cacheHandle].file);
return GOB_FALSE;
}
gi_handles[gi_cacheHandle].used = true;
return GOB_TRUE;
}
static GOBBool cache_close(GOBVoid)
{
WF_Close(gi_handles[gi_cacheHandle].file);
gi_handles[gi_cacheHandle].used = false;
return GOB_TRUE;
}
static GOBInt32 cache_read(GOBVoid* buffer, GOBInt32 size)
{
return WF_Read(buffer, size, gi_handles[gi_cacheHandle].file);
}
static GOBInt32 cache_write(GOBVoid* buffer, GOBInt32 size)
{
return WF_Write(buffer, size, gi_handles[gi_cacheHandle].file);
}
static GOBInt32 cache_seek(GOBInt32 offset)
{
return WF_Seek(offset, SEEK_SET, gi_handles[gi_cacheHandle].file);
}
static voidpf zi_alloc(voidpf opaque, uInt items, uInt size)
{
voidpf ret = zi_stackTop;
zi_stackTop += items * size;
assert(zi_stackTop < zi_stackBase + ZI_STACKSIZE);
return ret;
}
static void zi_free(voidpf opaque, voidpf address)
{
}
static GOBInt32 gi_decompress_zlib(GOBVoid* source, GOBUInt32 sourceLen,
GOBVoid* dest, GOBUInt32* destLen)
{
// Copied and modified version of zlib's uncompress()...
z_stream stream;
int err;
stream.next_in = (Bytef*)source;
stream.avail_in = (uInt)sourceLen;
stream.next_out = (Bytef*)dest;
stream.avail_out = (uInt)*destLen;
if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR;
stream.zalloc = zi_alloc;
stream.zfree = zi_free;
zi_stackTop = zi_stackBase;
err = inflateInit(&stream);
if (err != Z_OK) return err;
err = inflate(&stream, Z_FINISH);
if (err != Z_STREAM_END) {
inflateEnd(&stream);
return err == Z_OK ? Z_BUF_ERROR : err;
}
*destLen = stream.total_out;
err = inflateEnd(&stream);
return err;
}
GOBInt32 gi_decompress_null(GOBVoid* source, GOBUInt32 sourceLen,
GOBVoid* dest, GOBUInt32* destLen)
{
if (sourceLen > *destLen) return -1;
*destLen = sourceLen;
memcpy(dest, source, sourceLen);
return 0;
}
#ifdef GOB_PROFILE
static GOBVoid gi_profileread(GOBUInt32 code)
{
code = LittleLong(code);
Sys_Log("gob-prof.dat", &code, sizeof(code), true);
}
#endif
//===========================================================================
static void FS_CheckUsed(fileHandle_t f)
{
if (!fsh[f].used)
{
Com_Error( ERR_FATAL, "Filesystem call attempting to use invalid handle\n" );
}
}
int FS_filelength( fileHandle_t f )
{
FS_CheckInit();
FS_CheckUsed(f);
if (fsh[f].gob)
{
GOBUInt32 cur, end, crap;
GOBSeek(fsh[f].ghandle, 0, GOBSEEK_CURRENT, &cur);
GOBSeek(fsh[f].ghandle, 0, GOBSEEK_END, &end);
GOBSeek(fsh[f].ghandle, cur, GOBSEEK_START, &crap);
return end;
}
else
{
int pos = WF_Tell(fsh[f].whandle);
WF_Seek(0, SEEK_END, fsh[f].whandle);
int end = WF_Tell(fsh[f].whandle);
WF_Seek(pos, SEEK_SET, fsh[f].whandle);
return end;
}
}
void FS_FCloseFile( fileHandle_t f )
{
FS_CheckInit();
FS_CheckUsed(f);
if (fsh[f].gob)
GOBClose(fsh[f].ghandle);
else
WF_Close(fsh[f].whandle);
fsh[f].used = qfalse;
}
fileHandle_t FS_FOpenFileWrite( const char *filename )
{
FS_CheckInit();
fileHandle_t f = FS_HandleForFile();
char* osname = FS_BuildOSPath( filename );
fsh[f].whandle = WF_Open(osname, false, false);
if (fsh[f].whandle >= 0)
{
fsh[f].used = qtrue;
fsh[f].gob = qfalse;
return f;
}
return 0;
}
/*
===========
FS_FOpenFileRead
Finds the file in the search path.
Returns filesize and an open FILE pointer.
Used for streaming data out of either a
separate file or a ZIP file.
===========
*/
static int FS_FOpenFileReadOS( const char *filename, fileHandle_t f )
{
if (Sys_GetFileCode(filename) != -1)
{
char* osname = FS_BuildOSPath( filename );
fsh[f].whandle = WF_Open(osname, true, false);
if (fsh[f].whandle >= 0)
{
fsh[f].used = qtrue;
fsh[f].gob = qfalse;
return FS_filelength(f);
}
}
return -1;
}
/*
===================
FS_BuildGOBPath
Qpath may have either forward or backwards slashes
===================
*/
static char *FS_BuildGOBPath(const char *qpath )
{
static char path[2][MAX_OSPATH];
static int toggle;
toggle ^= 1; // flip-flop to allow two returns without clash
if (qpath[0] == '\\' || qpath[0] == '/')
{
Com_sprintf( path[toggle], sizeof( path[0] ), ".%s", qpath );
}
else
{
Com_sprintf( path[toggle], sizeof( path[0] ), ".\\%s", qpath );
}
// FS_ReplaceSeparators( path[toggle], '\\' );
FS_ReplaceSeparators( path[toggle] );
return path[toggle];
}
static int FS_FOpenFileReadGOB( const char *filename, fileHandle_t f )
{
char* gobname = FS_BuildGOBPath( filename );
if (GOBOpen(gobname, &fsh[f].ghandle) == GOBERR_OK)
{
fsh[f].used = qtrue;
fsh[f].gob = qtrue;
return FS_filelength(f);
}
return -1;
}
/*
===========
FS_FOpenFileRead
Finds the file in the search path.
Returns filesize and an open FILE pointer.
Used for streaming data out of either a
separate file or a ZIP file.
===========
*/
int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE )
{
FS_CheckInit();
if ( file == NULL ) {
Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'file' parameter passed\n" );
}
if ( !filename ) {
Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" );
}
*file = FS_HandleForFile();
int len;
if (fs_openorder->integer == 0)
{
// Release mode -- read from GOB first
len = FS_FOpenFileReadGOB(filename, *file);
if (len < 0) len = FS_FOpenFileReadOS(filename, *file);
}
else
{
// Debug mode -- external files override GOB
len = FS_FOpenFileReadOS(filename, *file);
if (len < 0) len = FS_FOpenFileReadGOB(filename, *file);
}
if (len >= 0) return len;
Com_DPrintf ("Can't find %s\n", filename);
*file = 0;
return -1;
}
/*
=================
FS_Read
Properly handles partial reads
=================
*/
int FS_Read( void *buffer, int len, fileHandle_t f )
{
FS_CheckInit();
FS_CheckUsed(f);
if ( !f )
{
return 0;
}
if ( f <= 0 || f >= MAX_FILE_HANDLES )
{
Com_Error( ERR_FATAL, "FS_Read: Invalid handle %d\n", f );
}
if (fsh[f].gob)
{
GOBUInt32 size = GOBRead(buffer, len, fsh[f].ghandle);
if (size == GOB_INVALID_SIZE)
{
#if defined(FINAL_BUILD)
extern void ERR_DiscFail(bool);
ERR_DiscFail(false);
#else
Com_Error( ERR_FATAL, "Failed to read from GOB" );
#endif
}
return size;
}
else
{
return WF_Read(buffer, len, fsh[f].whandle);
}
}
/*
MP has FS_Read2 which is supposed to do some extra logic.
We don't care, and just call FS_Read()
*/
int FS_Read2( void *buffer, int len, fileHandle_t f )
{
return FS_Read(buffer, len, f);
}
/*
=================
FS_Write
=================
*/
int FS_Write( const void *buffer, int len, fileHandle_t f )
{
FS_CheckInit();
FS_CheckUsed(f);
if ( !f )
{
return 0;
}
if ( f <= 0 || f >= MAX_FILE_HANDLES )
{
Com_Error( ERR_FATAL, "FS_Read: Invalid handle %d\n", f );
}
if (fsh[f].gob)
{
Com_Error( ERR_FATAL, "FS_Write: Cannot write to GOB files %d\n", f );
}
else
{
return WF_Write(buffer, len, fsh[f].whandle);
}
return 0;
}
/*
=================
FS_Seek
=================
*/
int FS_Seek( fileHandle_t f, long offset, int origin )
{
FS_CheckInit();
FS_CheckUsed(f);
if (fsh[f].gob)
{
int _origin;
switch( origin ) {
case FS_SEEK_CUR: _origin = GOBSEEK_CURRENT; break;
case FS_SEEK_END: _origin = GOBSEEK_END; break;
case FS_SEEK_SET: _origin = GOBSEEK_START; break;
default:
_origin = GOBSEEK_CURRENT;
Com_Error( ERR_FATAL, "Bad origin in FS_Seek\n" );
break;
}
GOBUInt32 pos;
GOBSeek(fsh[f].ghandle, offset, _origin, &pos);
return pos;
}
else
{
int _origin;
switch( origin ) {
case FS_SEEK_CUR: _origin = SEEK_CUR; break;
case FS_SEEK_END: _origin = SEEK_END; break;
case FS_SEEK_SET: _origin = SEEK_SET; break;
default:
_origin = SEEK_CUR;
Com_Error( ERR_FATAL, "Bad origin in FS_Seek\n" );
break;
}
return WF_Seek(offset, _origin, fsh[f].whandle);
}
}
/*
=================
FS_Access
=================
*/
qboolean FS_Access( const char *filename )
{
GOBBool status;
FS_CheckInit();
char* gobname = FS_BuildGOBPath( filename );
if (GOBAccess(gobname, &status) != GOBERR_OK || status != GOB_TRUE)
{
return Sys_GetFileCode( filename ) != -1;
}
return qtrue;
}
/*
======================================================================================
CONVENIENCE FUNCTIONS FOR ENTIRE FILES
======================================================================================
*/
#ifdef _JK2MP
int FS_FileIsInPAK(const char *filename, int *pChecksum)
#else
int FS_FileIsInPAK(const char *filename)
#endif
{
FS_CheckInit();
if ( !filename ) {
Com_Error( ERR_FATAL, "FS_FOpenFileRead: NULL 'filename' parameter passed\n" );
}
GOBBool exists;
GOBAccess(const_cast<GOBChar*>(filename), &exists);
#ifdef _JK2MP
*pChecksum = 0;
#endif
return exists ? 1 : -1;
}
/*
============
FS_ReadFile
Filename are relative to the quake search path
a null buffer will just return the file length without loading
============
*/
int FS_ReadFile( const char *qpath, void **buffer )
{
FS_CheckInit();
if ( !qpath || !qpath[0] ) {
Com_Error( ERR_FATAL, "FS_ReadFile with empty name\n" );
}
// stop sounds from repeating
S_ClearSoundBuffer();
fileHandle_t h;
int len = FS_FOpenFileRead( qpath, &h, qfalse );
if ( h == 0 )
{
if ( buffer ) *buffer = NULL;
return -1;
}
if ( !buffer )
{
FS_FCloseFile(h);
return len;
}
// assume temporary....
byte* buf = (byte*)Z_Malloc( len+1, TAG_TEMP_WORKSPACE, qfalse, 32);
buf[len]='\0';
// Z_Label(buf, qpath);
FS_Read(buf, len, h);
// guarantee that it will have a trailing 0 for string operations
buf[len] = 0;
FS_FCloseFile( h );
*buffer = buf;
return len;
}
/*
=============
FS_FreeFile
=============
*/
void FS_FreeFile( void *buffer )
{
FS_CheckInit();
if ( !buffer ) {
Com_Error( ERR_FATAL, "FS_FreeFile( NULL )" );
}
Z_Free( buffer );
}
int FS_FOpenFileByMode( const char *qpath, fileHandle_t *f, fsMode_t mode )
{
FS_CheckInit();
if (mode != FS_READ)
{
Com_Error( ERR_FATAL, "FSH_FOpenFile: bad mode" );
return -1;
}
return FS_FOpenFileRead( qpath, f, qtrue );
}
int FS_FTell( fileHandle_t f )
{
FS_CheckInit();
FS_CheckUsed(f);
if (fsh[f].gob)
{
GOBUInt32 pos;
GOBSeek(fsh[f].ghandle, 0, GOBSEEK_CURRENT, &pos);
return pos;
}
else
{
return WF_Tell(fsh[f].whandle);
}
}
/*
================
FS_Startup
================
*/
void FS_Startup( const char *gameName )
{
Com_Printf( "----- FS_Startup -----\n" );
fs_openorder = Cvar_Get( "fs_openorder", "0", 0 );
fs_debug = Cvar_Get( "fs_debug", "0", 0 );
fs_copyfiles = Cvar_Get( "fs_copyfiles", "0", CVAR_INIT );
fs_cdpath = Cvar_Get ("fs_cdpath", Sys_DefaultCDPath(), CVAR_INIT );
fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultBasePath(), CVAR_INIT );
fs_gamedirvar = Cvar_Get ("fs_game", "base", CVAR_INIT|CVAR_SERVERINFO );
fs_restrict = Cvar_Get ("fs_restrict", "", CVAR_INIT );
gi_handles = new gi_handleTable[MAX_FILE_HANDLES];
for (int f = 0; f < MAX_FILE_HANDLES; ++f)
{
fsh[f].used = false;
gi_handles[f].used = false;
}
zi_stackBase = (char*)Z_Malloc(ZI_STACKSIZE, TAG_FILESYS, qfalse);
GOBMemoryFuncSet mem;
mem.alloc = gi_alloc;
mem.free = gi_free;
GOBFileSysFuncSet file;
file.close = gi_close;
file.open = gi_open;
file.read = gi_read;
file.seek = gi_seek;
file.write = NULL;
GOBCacheFileFuncSet cache;
cache.close = cache_close;
cache.open = cache_open;
cache.read = cache_read;
cache.seek = cache_seek;
cache.write = cache_write;
GOBCodecFuncSet codec = {
2, // codecs
{
{ // Codec 0 - zlib
'z', GOB_INFINITE_RATIO, // tag, ratio (ratio is meaningless for decomp)
NULL,
gi_decompress_zlib,
},
{ // Codec 1 - null
'0', GOB_INFINITE_RATIO, // tag, ratio (ratio is meaningless for decomp)
NULL,
gi_decompress_null,
},
}
};
if (
#ifdef _XBOX
GOBInit(&mem, &file, &codec, &cache)
#else
GOBInit(&mem, &file, &codec, NULL)
#endif
!= GOBERR_OK)
{
Com_Error( ERR_FATAL, "Could not initialize GOB" );
}
char* archive = FS_BuildOSPath( "assets" );
if (GOBArchiveOpen(archive, GOBACCESS_READ, GOB_FALSE, GOB_TRUE) != GOBERR_OK)
{
#if defined(FINAL_BUILD)
/*
extern void ERR_DiscFail(bool);
ERR_DiscFail(false);
*/
#else
//Com_Error( ERR_FATAL, "Could not initialize GOB" );
Cvar_Set("fs_openorder", "1");
#endif
}
GOBSetCacheSize(1);
GOBSetReadBufferSize(32 * 1024);
#ifdef GOB_PROFILE
GOBProfileFuncSet profile = {
gi_profileread
};
GOBSetProfileFuncs(&profile);
GOBStartProfile();
#endif
Com_Printf( "----------------------\n" );
}
/*
============================
DIRECTORY SCANNING FUCNTIONS
============================
*/
#define MAX_FOUND_FILES 0x1000
/*
==================
FS_AddFileToList
==================
*/
static int FS_AddFileToList( char *name, char *list[MAX_FOUND_FILES], int nfiles ) {
int i;
if ( nfiles == MAX_FOUND_FILES - 1 ) {
return nfiles;
}
for ( i = 0 ; i < nfiles ; i++ ) {
if ( !Q_stricmp( name, list[i] ) ) {
return nfiles; // allready in list
}
}
// list[nfiles] = CopyString( name );
list[nfiles] = (char *) Z_Malloc( strlen(name) + 1, TAG_LISTFILES, qfalse );
strcpy(list[nfiles], name);
nfiles++;
return nfiles;
}
/*
===============
FS_ListFiles
Returns a uniqued list of files that match the given criteria
from all search paths
===============
*/
char **FS_ListFiles( const char *path, const char *extension, int *numfiles )
{
char *netpath;
int numSysFiles;
char **sysFiles;
char *name;
int nfiles = 0;
char **listCopy;
char *list[MAX_FOUND_FILES];
int i;
FS_CheckInit();
if ( !path ) {
*numfiles = 0;
return NULL;
}
// We don't do any fancy searchpath magic here, it's all in the meta-file
// that Sys_ListFiles will return
netpath = FS_BuildOSPath( path );
#ifdef _JK2MP
sysFiles = Sys_ListFiles( netpath, extension, NULL, &numSysFiles, qfalse );
#else
sysFiles = Sys_ListFiles( netpath, extension, &numSysFiles, qfalse );
#endif
for ( i = 0 ; i < numSysFiles ; i++ ) {
// unique the match
name = sysFiles[i];
nfiles = FS_AddFileToList( name, list, nfiles );
}
Sys_FreeFileList( sysFiles );
// return a copy of the list
*numfiles = nfiles;
if ( !nfiles ) {
return NULL;
}
listCopy = (char**)Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ), TAG_LISTFILES, qfalse);
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
return listCopy;
}
/*
=================
FS_FreeFileList
=================
*/
void FS_FreeFileList( char **filelist )
{
int i;
FS_CheckInit();
if ( !filelist ) {
return;
}
for ( i = 0 ; filelist[i] ; i++ ) {
Z_Free( filelist[i] );
}
Z_Free( filelist );
}
/*
===============
FS_AddFileToListBuf
===============
*/
static int FS_AddFileToListBuf( char *name, char *listbuf, int bufsize, int nfiles )
{
char *p;
if ( nfiles == MAX_FOUND_FILES - 1 ) {
return nfiles;
}
if (name[0] == '/' || name[0] == '\\') {
name++;
}
p = listbuf;
while ( *p ) {
if ( !Q_stricmp( name, p ) ) {
return nfiles; // already in list
}
p += strlen( p ) + 1;
}
if ( ( p + strlen( name ) + 2 - listbuf ) > bufsize ) {
return nfiles; // list is full
}
strcpy( p, name );
p += strlen( p ) + 1;
*p = 0;
return nfiles + 1;
}
/*
================
FS_GetFileList
Returns a uniqued list of files that match the given criteria
from all search paths
================
*/
int FS_GetFileList( const char *path, const char *extension, char *listbuf, int bufsize )
{
int nfiles = 0;
int i;
char *netpath;
int numSysFiles;
char **sysFiles;
char *name;
FS_CheckInit();
if ( !path ) {
return 0;
}
if ( !extension ) {
extension = "";
}
// Prime the file list buffer
listbuf[0] = '\0';
netpath = FS_BuildOSPath( path );
#ifdef _JK2MP
sysFiles = Sys_ListFiles( netpath, extension, NULL, &numSysFiles, qfalse );
#else
sysFiles = Sys_ListFiles( netpath, extension, &numSysFiles, qfalse );
#endif
for ( i = 0 ; i < numSysFiles ; i++ ) {
// unique the match
name = sysFiles[i];
nfiles = FS_AddFileToListBuf( name, listbuf, bufsize, nfiles );
}
Sys_FreeFileList( sysFiles );
return nfiles;
}
/*
=================
Filesytem STUBS
=================
*/
qboolean FS_ConditionalRestart(int checksumFeed)
{
return qfalse;
}
void FS_ClearPakReferences(int flags)
{
return;
}
const char *FS_LoadedPakNames(void)
{
return "";
}
const char *FS_ReferencedPakNames(void)
{
return "";
}
void FS_SetRestrictions(void)
{
return;
}
#ifdef _JK2MP
void FS_Restart(int checksumFeed)
#else
void FS_Restart(void)
#endif
{
return;
}
qboolean FS_FileExists(const char *file)
{
assert(!"FS_FileExists not implemented on Xbox");
return qfalse;
}
void FS_UpdateGamedir(void)
{
return;
}
void FS_PureServerSetReferencedPaks(const char *pakSums, const char *pakNames)
{
return;
}
void FS_PureServerSetLoadedPaks(const char *pakSums, const char *pakNames)
{
return;
}
const char *FS_ReferencedPakChecksums(void)
{
return "";
}
const char *FS_LoadedPakChecksums(void)
{
return "";
}