This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/filesystem.c

3960 lines
100 KiB
C
Raw Normal View History

2007-06-21 22:00:00 +02:00
//=======================================================================
// Copyright XashXT Group 2007 <20>
// filesystem.c - game filesystem based on DP fs
//=======================================================================
2007-11-10 22:00:00 +01:00
#include "launch.h"
2010-09-10 22:00:00 +02:00
#include "wadfile.h"
2008-07-23 22:00:00 +02:00
#include "filesystem.h"
2010-03-27 22:00:00 +01:00
#include "library.h"
2009-09-24 22:00:00 +02:00
#include "mathlib.h"
2007-06-21 22:00:00 +02:00
#define FILE_BUFF_SIZE 2048
2007-11-10 22:00:00 +01:00
2008-08-14 22:00:00 +02:00
typedef struct stringlist_s
{
// maxstrings changes as needed, causing reallocation of strings[] array
2010-11-19 22:00:00 +01:00
int maxstrings;
int numstrings;
char **strings;
2008-08-14 22:00:00 +02:00
} stringlist_t;
typedef struct wadtype_s
{
2010-11-19 22:00:00 +01:00
char *ext;
char type;
2008-08-14 22:00:00 +02:00
} wadtype_t;
2010-03-27 22:00:00 +01:00
typedef struct file_s
2007-06-21 22:00:00 +02:00
{
int handle; // file descriptor
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)
int ungetc; // single stored character from ungetc, cleared to EOF when read
2010-03-27 22:00:00 +01:00
time_t filetime; // pak, wad or real filetime
2007-06-21 22:00:00 +02:00
// Contents buffer
fs_offset_t buff_ind, buff_len; // buffer current index and length
2010-11-19 22:00:00 +01:00
byte buff[FILE_BUFF_SIZE]; // intermediate buffer
2007-06-21 22:00:00 +02:00
};
typedef struct vfile_s
{
2007-07-21 22:00:00 +02:00
byte *buff;
2007-11-18 22:00:00 +01:00
file_t *handle;
int mode;
2007-07-21 22:00:00 +02:00
fs_offset_t buffsize;
fs_offset_t length;
fs_offset_t offset;
};
2007-06-21 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
typedef struct wfile_s
{
char filename [MAX_SYSPATH];
int infotableofs;
2008-08-15 22:00:00 +02:00
byte *mempool; // W_ReadLump temp buffers
2008-08-14 22:00:00 +02:00
int numlumps;
int mode;
2010-10-26 22:00:00 +02:00
int handle;
2008-08-14 22:00:00 +02:00
dlumpinfo_t *lumps;
2010-03-27 22:00:00 +01:00
time_t filetime;
2008-08-14 22:00:00 +02:00
};
2007-06-21 22:00:00 +02:00
typedef struct packfile_s
{
2007-11-25 22:00:00 +01:00
char name[128];
2007-06-21 22:00:00 +02:00
fs_offset_t offset;
fs_offset_t realsize; // real file size (uncompressed)
} packfile_t;
typedef struct pack_s
{
2010-11-19 22:00:00 +01:00
char filename[MAX_SYSPATH];
2007-06-21 22:00:00 +02:00
int handle;
int numfiles;
2010-03-27 22:00:00 +01:00
time_t filetime; // common for all packed files
2010-11-19 22:00:00 +01:00
packfile_t *files;
2007-06-21 22:00:00 +02:00
} pack_t;
typedef struct searchpath_s
{
char filename[MAX_SYSPATH];
pack_t *pack;
2008-08-14 22:00:00 +02:00
wfile_t *wad;
2008-06-05 22:00:00 +02:00
int flags;
2007-06-21 22:00:00 +02:00
struct searchpath_s *next;
} searchpath_t;
byte *fs_mempool;
searchpath_t *fs_searchpaths = NULL;
2009-01-21 22:00:00 +01:00
searchpath_t fs_directpath; // static direct path
2007-06-21 22:00:00 +02:00
static void FS_InitMemory( void );
2010-03-27 22:00:00 +01:00
const char *FS_FileExtension( const char *in );
2010-10-26 22:00:00 +02:00
static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly );
2008-08-14 22:00:00 +02:00
static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype );
2010-11-19 22:00:00 +01:00
static packfile_t* FS_AddFileToPack( const char* name, pack_t *pack, fs_offset_t offset, fs_offset_t size );
2008-08-14 22:00:00 +02:00
static byte *W_LoadFile( const char *path, fs_offset_t *filesizeptr );
2010-10-26 22:00:00 +02:00
static qboolean FS_SysFileExists( const char *path );
2010-03-27 22:00:00 +01:00
static long FS_SysFileTime( const char *filename );
2008-08-14 22:00:00 +02:00
static char W_TypeFromExt( const char *lumpname );
static const char *W_ExtFromType( char lumptype );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
char sys_rootdir[MAX_SYSPATH];// system root
2010-02-21 22:00:00 +01:00
char fs_rootdir[MAX_SYSPATH]; // engine root directory
char fs_basedir[MAX_SYSPATH]; // base directory of game
char fs_gamedir[MAX_SYSPATH]; // game current directory
char gs_basedir[MAX_SYSPATH]; // initial dir before loading gameinfo.txt (used for compilers too)
2007-06-21 22:00:00 +02:00
2007-11-10 22:00:00 +01:00
// command ilne parms
2007-06-21 22:00:00 +02:00
int fs_argc;
2007-11-10 22:00:00 +01:00
char *fs_argv[MAX_NUM_ARGVS];
2010-10-26 22:00:00 +02:00
qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes
2010-10-22 22:00:00 +02:00
convar_t *fs_defaultdir;
2009-09-10 22:00:00 +02:00
sysinfo_t SI;
2007-06-21 22:00:00 +02:00
/*
=============================================================================
FILEMATCH COMMON SYSTEM
=============================================================================
*/
2010-11-19 22:00:00 +01:00
int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive )
2007-06-21 22:00:00 +02:00
{
2010-03-24 22:00:00 +01:00
int c1, c2;
while( *pattern )
2007-06-21 22:00:00 +02:00
{
2010-03-24 22:00:00 +01:00
switch( *pattern )
2007-06-21 22:00:00 +02:00
{
case 0: return 1; // end of pattern
case '?': // match any single character
2010-03-24 22:00:00 +01:00
if( *in == 0 || *in == '/' || *in == '\\' || *in == ':' )
return 0; // no match
2007-06-21 22:00:00 +02:00
in++;
pattern++;
break;
case '*': // match anything until following string
2010-03-24 22:00:00 +01:00
if( !*in ) return 1; // match
2007-06-21 22:00:00 +02:00
pattern++;
2010-03-24 22:00:00 +01:00
while( *in )
2007-06-21 22:00:00 +02:00
{
2010-03-24 22:00:00 +01:00
if( *in == '/' || *in == '\\' || *in == ':' )
break;
2007-06-21 22:00:00 +02:00
// see if pattern matches at this offset
2010-03-24 22:00:00 +01:00
if( matchpattern( in, pattern, caseinsensitive ))
return 1;
2007-06-21 22:00:00 +02:00
// nope, advance to next offset
in++;
}
break;
default:
2010-03-24 22:00:00 +01:00
if( *in != *pattern )
2007-06-21 22:00:00 +02:00
{
2010-03-24 22:00:00 +01:00
if( !caseinsensitive )
return 0; // no match
2007-06-21 22:00:00 +02:00
c1 = *in;
2010-03-24 22:00:00 +01:00
if( c1 >= 'A' && c1 <= 'Z' )
c1 += 'a' - 'A';
2007-06-21 22:00:00 +02:00
c2 = *pattern;
2010-03-24 22:00:00 +01:00
if( c2 >= 'A' && c2 <= 'Z' )
c2 += 'a' - 'A';
if( c1 != c2) return 0; // no match
2007-06-21 22:00:00 +02:00
}
in++;
pattern++;
break;
}
}
2010-03-24 22:00:00 +01:00
if( *in ) return 0; // reached end of pattern but not end of input
2007-06-21 22:00:00 +02:00
return 1; // success
}
2008-08-13 22:00:00 +02:00
void stringlistinit( stringlist_t *list )
2007-06-21 22:00:00 +02:00
{
2008-10-27 22:00:00 +01:00
Mem_Set( list, 0, sizeof( *list ));
2007-08-13 22:00:00 +02:00
}
2007-06-27 22:00:00 +02:00
2007-08-13 22:00:00 +02:00
void stringlistfreecontents(stringlist_t *list)
{
2010-11-19 22:00:00 +01:00
int i;
for( i = 0; i < list->numstrings; i++ )
2007-08-13 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
if( list->strings[i] )
Mem_Free( list->strings[i] );
2007-08-13 22:00:00 +02:00
list->strings[i] = NULL;
}
2010-11-19 22:00:00 +01:00
2007-08-13 22:00:00 +02:00
list->numstrings = 0;
list->maxstrings = 0;
2010-11-19 22:00:00 +01:00
if( list->strings ) Mem_Free( list->strings );
2007-06-21 22:00:00 +02:00
}
2009-01-22 22:00:00 +01:00
void stringlistappend( stringlist_t *list, char *text )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
size_t textlen;
char **oldstrings;
2007-08-13 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
if( list->numstrings >= list->maxstrings )
2007-06-21 22:00:00 +02:00
{
2007-08-13 22:00:00 +02:00
oldstrings = list->strings;
list->maxstrings += 4096;
2010-11-19 22:00:00 +01:00
list->strings = Mem_Alloc( fs_mempool, list->maxstrings * sizeof( *list->strings ));
if( list->numstrings ) Mem_Copy( list->strings, oldstrings, list->numstrings * sizeof( *list->strings ));
if( oldstrings ) Mem_Free( oldstrings );
2007-06-21 22:00:00 +02:00
}
2010-11-19 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
textlen = com.strlen( text ) + 1;
list->strings[list->numstrings] = Mem_Alloc( fs_mempool, textlen );
Mem_Copy( list->strings[list->numstrings], text, textlen );
2007-08-13 22:00:00 +02:00
list->numstrings++;
2007-06-21 22:00:00 +02:00
}
2009-01-22 22:00:00 +01:00
void stringlistsort( stringlist_t *list )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
int i, j;
char *temp;
2007-08-13 22:00:00 +02:00
// this is a selection sort (finds the best entry for each slot)
2009-01-22 22:00:00 +01:00
for( i = 0; i < list->numstrings - 1; i++ )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
for( j = i + 1; j < list->numstrings; j++ )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
if( com.strcmp( list->strings[i], list->strings[j] ) > 0 )
2007-06-21 22:00:00 +02:00
{
2007-08-13 22:00:00 +02:00
temp = list->strings[i];
list->strings[i] = list->strings[j];
list->strings[j] = temp;
2007-06-21 22:00:00 +02:00
}
}
}
}
2009-01-22 22:00:00 +01:00
void listdirectory( stringlist_t *list, const char *path )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
int i;
char pattern[4096], *c;
struct _finddata_t n_file;
long hFile;
com.strncpy( pattern, path, sizeof( pattern ));
com.strncat( pattern, "*", sizeof( pattern ));
2007-06-21 22:00:00 +02:00
// ask for the directory listing handle
2009-01-22 22:00:00 +01:00
hFile = _findfirst( pattern, &n_file );
if( hFile == -1 ) return;
2007-06-21 22:00:00 +02:00
// start a new chain with the the first name
2009-01-22 22:00:00 +01:00
stringlistappend( list, n_file.name );
2007-06-21 22:00:00 +02:00
// iterate through the directory
2009-01-22 22:00:00 +01:00
while( _findnext( hFile, &n_file ) == 0 )
stringlistappend( list, n_file.name );
_findclose( hFile );
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
// convert names to lowercase because windows doesn't care, but pattern matching code often does
for( i = 0; i < list->numstrings; i++ )
{
for( c = list->strings[i]; *c; c++ )
{
if( *c >= 'A' && *c <= 'Z' )
2007-06-21 22:00:00 +02:00
*c += 'a' - 'A';
2009-01-22 22:00:00 +01:00
}
}
2007-06-21 22:00:00 +02:00
}
/*
=============================================================================
OTHER PRIVATE FUNCTIONS
=============================================================================
*/
/*
====================
FS_AddFileToPack
Add a file to the list of files contained into a package
====================
*/
2010-11-19 22:00:00 +01:00
static packfile_t* FS_AddFileToPack( const char* name, pack_t* pack, fs_offset_t offset, fs_offset_t size )
2007-06-21 22:00:00 +02:00
{
2007-11-25 22:00:00 +01:00
int left, right, middle;
packfile_t *pfile;
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
// look for the slot we should put that file into (binary search)
2007-06-21 22:00:00 +02:00
left = 0;
right = pack->numfiles - 1;
2009-01-22 22:00:00 +01:00
while( left <= right )
2007-06-21 22:00:00 +02:00
{
int diff;
middle = (left + right) / 2;
2009-01-21 22:00:00 +01:00
diff = com.stricmp( pack->files[middle].name, name );
2007-06-21 22:00:00 +02:00
// If we found the file, there's a problem
2009-01-22 22:00:00 +01:00
if( !diff ) MsgDev( D_NOTE, "Package %s contains the file %s several times\n", pack->filename, name );
2007-06-21 22:00:00 +02:00
// If we're too far in the list
2009-01-22 22:00:00 +01:00
if( diff > 0 ) right = middle - 1;
2007-06-21 22:00:00 +02:00
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];
2009-01-22 22:00:00 +01:00
memmove( pfile + 1, pfile, (pack->numfiles - left) * sizeof( *pfile ));
2007-06-21 22:00:00 +02:00
pack->numfiles++;
2009-01-22 22:00:00 +01:00
com.strncpy( pfile->name, name, sizeof( pfile->name ));
2007-06-21 22:00:00 +02:00
pfile->offset = offset;
2010-11-19 22:00:00 +01:00
pfile->realsize = size;
2007-06-21 22:00:00 +02:00
return pfile;
}
/*
============
FS_CreatePath
Only used for FS_Open.
============
*/
2008-08-14 22:00:00 +02:00
void FS_CreatePath( char *path )
2007-06-21 22:00:00 +02:00
{
char *ofs, save;
2008-08-14 22:00:00 +02:00
for( ofs = path+1; *ofs; ofs++ )
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( *ofs == '/' || *ofs == '\\' )
2007-06-21 22:00:00 +02:00
{
// create the directory
save = *ofs;
*ofs = 0;
2010-09-10 22:00:00 +02:00
_mkdir( path );
2007-06-21 22:00:00 +02:00
*ofs = save;
}
}
}
/*
============
2008-06-05 22:00:00 +02:00
FS_Path_f
2007-06-21 22:00:00 +02:00
debug info
============
*/
2008-01-28 22:00:00 +01:00
void FS_Path_f( void )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
searchpath_t *s;
2007-06-21 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
Msg( "Current search path:\n" );
2010-11-19 22:00:00 +01:00
2008-08-14 22:00:00 +02:00
for( s = fs_searchpaths; s; s = s->next )
2007-06-21 22:00:00 +02:00
{
2008-08-14 22:00:00 +02:00
if( s->pack ) Msg( "%s (%i files)\n", s->pack->filename, s->pack->numfiles );
else if( s->wad ) Msg( "%s (%i files)\n", s->wad->filename, s->wad->numlumps );
2008-01-28 22:00:00 +01:00
else Msg( "%s\n", s->filename );
2007-06-21 22:00:00 +02:00
}
}
2008-06-05 22:00:00 +02:00
/*
============
2008-08-14 22:00:00 +02:00
FS_ClearPath_f
2008-06-05 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
only for debug targets
2008-06-05 22:00:00 +02:00
============
*/
void FS_ClearPaths_f( void )
{
FS_ClearSearchPath();
}
2007-06-21 22:00:00 +02:00
/*
============
FS_FileBase
Extracts the base name of a file (no path, no extension, assumes '/' as path separator)
============
*/
2010-10-26 22:00:00 +02:00
void _FS_FileBase( const char *in, char *out, qboolean kill_backwardslash )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
int len, start, end;
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
len = com.strlen( in );
2010-03-06 22:00:00 +01:00
if( !len ) return;
2007-06-21 22:00:00 +02:00
// scan backward for '.'
end = len - 1;
2010-03-06 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
while( end && in[end] != '.' && in[end] != '/' && in[end] != '\\' )
2007-06-21 22:00:00 +02:00
end--;
2009-01-22 22:00:00 +01:00
if( in[end] != '.' )
end = len-1; // no '.', copy to end
else end--; // found ',', copy to left of '.'
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
// scan backward for '/'
2007-06-21 22:00:00 +02:00
start = len - 1;
2008-08-08 22:00:00 +02:00
if( kill_backwardslash )
{
2009-01-22 22:00:00 +01:00
while( start >= 0 && in[start] != '/' && in[start] != '\\' )
2008-08-08 22:00:00 +02:00
start--;
2009-01-22 22:00:00 +01:00
if( start < 0 || ( in[start] != '/' && in[start] != '\\' ))
2008-08-08 22:00:00 +02:00
start = 0;
else start++;
}
else
{
// NOTE: some doomwads using backward slash as part of animation name
// e.g. vile\1, so ignore backward slash for wads
while ( start >= 0 && in[start] != '/' )
start--;
if ( start < 0 || in[start] != '/' )
start = 0;
else start++;
}
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
// length of new sting
2007-06-21 22:00:00 +02:00
len = end - start + 1;
// Copy partial string
2009-01-22 22:00:00 +01:00
com.strncpy( out, &in[start], len + 1 );
2007-06-21 22:00:00 +02:00
out[len] = 0;
}
2008-08-08 22:00:00 +02:00
void FS_FileBase( const char *in, char *out )
{
_FS_FileBase( in, out, true );
}
2008-08-14 22:00:00 +02:00
void W_FileBase( const char *in, char *out )
{
_FS_FileBase( in, out, false );
}
2007-06-21 22:00:00 +02:00
/*
=================
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.
=================
*/
2010-03-27 22:00:00 +01:00
pack_t *FS_LoadPackPAK( const char *packfile )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
dpackheader_t header;
int i, numpackfiles;
int packhandle;
pack_t *pack;
dpackfile_t *info;
2007-06-21 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
packhandle = open( packfile, O_RDONLY|O_BINARY );
if( packhandle < 0 ) return NULL;
read( packhandle, (void *)&header, sizeof( header ));
if( header.ident != IDPACKV1HEADER )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
MsgDev( D_NOTE, "%s is not a packfile. Ignored.\n", packfile );
close( packhandle );
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-11-19 22:00:00 +01:00
2010-03-27 22:00:00 +01:00
if( header.dirlen % sizeof( dpackfile_t ))
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
MsgDev( D_ERROR, "%s has an invalid directory size. Ignored.\n", packfile );
close( packhandle );
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-03-27 22:00:00 +01:00
numpackfiles = header.dirlen / sizeof( dpackfile_t );
2007-06-21 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
if( numpackfiles > MAX_FILES_IN_PACK )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
MsgDev( D_ERROR, "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles );
close( packhandle );
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-11-18 22:00:00 +01:00
if( numpackfiles <= 0 )
{
MsgDev( D_ERROR, "%s has no files. Ignored.\n", packfile );
close( packhandle );
return NULL;
}
2010-03-27 22:00:00 +01:00
info = (dpackfile_t *)Mem_Alloc( fs_mempool, sizeof( *info ) * numpackfiles );
lseek( packhandle, header.dirofs, SEEK_SET );
if( header.dirlen != read( packhandle, (void *)info, header.dirlen ))
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
MsgDev( D_NOTE, "%s is an incomplete PAK, not loading\n", packfile );
Mem_Free( info );
close( packhandle );
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-03-27 22:00:00 +01:00
pack = (pack_t *)Mem_Alloc( fs_mempool, sizeof( pack_t ));
2009-01-22 22:00:00 +01:00
com.strncpy( pack->filename, packfile, sizeof( pack->filename ));
2007-06-21 22:00:00 +02:00
pack->handle = packhandle;
pack->numfiles = 0;
2009-01-22 22:00:00 +01:00
pack->files = (packfile_t *)Mem_Alloc( fs_mempool, numpackfiles * sizeof( packfile_t ));
2010-03-27 22:00:00 +01:00
pack->filetime = FS_SysFileTime( packfile );
2007-06-21 22:00:00 +02:00
// parse the directory
2010-03-27 22:00:00 +01:00
for( i = 0; i < numpackfiles; i++ )
2007-06-21 22:00:00 +02:00
{
2010-11-21 22:00:00 +01:00
FS_AddFileToPack( info[i].name, pack, info[i].filepos, info[i].filelen );
2007-06-21 22:00:00 +02:00
}
2008-11-21 22:00:00 +01:00
Mem_Free( info );
2010-09-09 22:00:00 +02:00
MsgDev( D_NOTE, "Adding packfile: %s (%i files)\n", packfile, numpackfiles );
2007-06-21 22:00:00 +02:00
return pack;
}
/*
================
FS_AddPack_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.
================
*/
2010-10-26 22:00:00 +02:00
static qboolean FS_AddPack_Fullpath( const char *pakfile, qboolean *already_loaded, qboolean keep_plain_dirs, int flags )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
searchpath_t *search;
pack_t *pak = NULL;
const char *ext = FS_FileExtension( pakfile );
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
for( search = fs_searchpaths; search; search = search->next )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
if( search->pack && !com.stricmp( search->pack->filename, pakfile ))
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
if( already_loaded ) *already_loaded = true;
2007-06-21 22:00:00 +02:00
return true; // already loaded
}
}
2009-01-22 22:00:00 +01:00
if( already_loaded ) *already_loaded = false;
2007-06-21 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
if( !com.stricmp( ext, "pak" )) pak = FS_LoadPackPAK( pakfile );
2009-01-22 22:00:00 +01:00
else MsgDev( D_ERROR, "\"%s\" does not have a pack extension\n", pakfile );
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
if( pak )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
if( keep_plain_dirs )
2007-06-21 22:00:00 +02:00
{
// find the first item whose next one is a pack or NULL
searchpath_t *insertion_point = 0;
2010-03-27 22:00:00 +01:00
if( fs_searchpaths && !fs_searchpaths->pack )
2007-06-21 22:00:00 +02:00
{
insertion_point = fs_searchpaths;
2010-03-27 22:00:00 +01:00
while( 1 )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
if( !insertion_point->next ) break;
if( insertion_point->next->pack ) break;
2007-06-21 22:00:00 +02:00
insertion_point = insertion_point->next;
}
}
2010-03-27 22:00:00 +01:00
// if insertion_point is NULL, this means that either there is no
2007-06-21 22:00:00 +02:00
// item in the list yet, or that the very first item is a pack. In
// that case, we want to insert at the beginning...
2010-03-27 22:00:00 +01:00
if( !insertion_point )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2007-06-21 22:00:00 +02:00
search->pack = pak;
search->next = fs_searchpaths;
2010-04-12 22:00:00 +02:00
search->flags |= flags;
2007-06-21 22:00:00 +02:00
fs_searchpaths = search;
}
else // otherwise we want to append directly after insertion_point.
{
2010-03-27 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2007-06-21 22:00:00 +02:00
search->pack = pak;
search->next = insertion_point->next;
2010-04-12 22:00:00 +02:00
search->flags |= flags;
2007-06-21 22:00:00 +02:00
insertion_point->next = search;
}
}
else
{
2010-03-27 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2007-06-21 22:00:00 +02:00
search->pack = pak;
search->next = fs_searchpaths;
2010-04-12 22:00:00 +02:00
search->flags |= flags;
2007-06-21 22:00:00 +02:00
fs_searchpaths = search;
}
return true;
}
else
{
2010-03-27 22:00:00 +01:00
MsgDev( D_ERROR, "FS_AddPack_Fullpath: unable to load pak \"%s\"\n", pakfile );
2007-06-21 22:00:00 +02:00
return false;
}
}
2008-08-14 22:00:00 +02:00
/*
====================
FS_AddWad_Fullpath
====================
*/
2010-10-26 22:00:00 +02:00
static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, qboolean keep_plain_dirs )
2008-08-14 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
searchpath_t *search;
wfile_t *wad = NULL;
const char *ext = FS_FileExtension( wadfile );
2008-08-14 22:00:00 +02:00
for( search = fs_searchpaths; search; search = search->next )
{
2009-01-22 22:00:00 +01:00
if( search->wad && !com.stricmp( search->wad->filename, wadfile ))
2008-08-14 22:00:00 +02:00
{
if( already_loaded ) *already_loaded = true;
return true; // already loaded
}
}
if( already_loaded ) *already_loaded = false;
2009-01-22 22:00:00 +01:00
if( !com.stricmp( ext, "wad" )) wad = W_Open( wadfile, "rb" );
else MsgDev( D_ERROR, "\"%s\" doesn't have a wad extension\n", wadfile );
2008-08-14 22:00:00 +02:00
if( wad )
{
if( keep_plain_dirs )
{
// find the first item whose next one is a wad or NULL
searchpath_t *insertion_point = NULL;
if( fs_searchpaths && !fs_searchpaths->wad )
{
insertion_point = fs_searchpaths;
while( 1 )
{
if( !insertion_point->next ) break;
if( insertion_point->next->wad ) break;
insertion_point = insertion_point->next;
}
}
// if insertion_point is NULL, this means that either there is no
// item in the list yet, or that the very first item is a wad. In
// that case, we want to insert at the beginning...
if( !insertion_point )
{
2009-01-22 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2008-08-14 22:00:00 +02:00
search->wad = wad;
search->next = fs_searchpaths;
fs_searchpaths = search;
}
else // otherwise we want to append directly after insertion_point.
{
2009-01-22 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2008-08-14 22:00:00 +02:00
search->wad = wad;
search->next = insertion_point->next;
insertion_point->next = search;
}
}
else
{
2009-01-22 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2008-08-14 22:00:00 +02:00
search->wad = wad;
search->next = fs_searchpaths;
fs_searchpaths = search;
}
2010-10-26 22:00:00 +02:00
2010-09-09 22:00:00 +02:00
MsgDev( D_NOTE, "Adding wadfile %s (%i files)\n", wadfile, wad->numlumps );
2008-08-14 22:00:00 +02:00
return true;
}
else
{
MsgDev( D_ERROR, "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile );
return false;
}
}
2007-06-21 22:00:00 +02:00
/*
================
FS_AddGameDirectory
Sets fs_gamedir, adds the directory to the head of the path,
then loads and adds pak1.pak pak2.pak ...
================
*/
2008-06-05 22:00:00 +02:00
void FS_AddGameDirectory( const char *dir, int flags )
2007-06-21 22:00:00 +02:00
{
2008-07-23 22:00:00 +02:00
stringlist_t list;
searchpath_t *search;
2010-10-26 22:00:00 +02:00
string fullpath;
2008-07-23 22:00:00 +02:00
int i;
2007-06-21 22:00:00 +02:00
2009-08-29 22:00:00 +02:00
if(!( flags & FS_NOWRITE_PATH ))
com.strncpy( fs_gamedir, dir, sizeof( fs_gamedir ));
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
stringlistinit( &list );
listdirectory( &list, dir );
stringlistsort( &list );
2007-08-13 22:00:00 +02:00
2007-06-21 22:00:00 +02:00
// add any PAK package in the directory
2009-01-22 22:00:00 +01:00
for( i = 0; i < list.numstrings; i++ )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
if( !com.stricmp( FS_FileExtension( list.strings[i] ), "pak" ))
2007-06-21 22:00:00 +02:00
{
2010-10-26 22:00:00 +02:00
com.sprintf( fullpath, "%s%s", dir, list.strings[i] );
FS_AddPack_Fullpath( fullpath, NULL, false, flags );
2007-06-21 22:00:00 +02:00
}
2007-08-13 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
// add any WAD package in the directory
for( i = 0; i < list.numstrings; i++ )
{
if( !com.stricmp( FS_FileExtension( list.strings[i] ), "wad" ))
{
com.sprintf( fullpath, "%s%s", dir, list.strings[i] );
FS_AddWad_Fullpath( fullpath, NULL, false );
}
}
stringlistfreecontents( &list );
2009-01-22 22:00:00 +01:00
// add the directory to the search path
2007-06-21 22:00:00 +02:00
// (unpacked files have the priority over packed files)
2009-01-22 22:00:00 +01:00
search = (searchpath_t *)Mem_Alloc( fs_mempool, sizeof( searchpath_t ));
2008-10-25 22:00:00 +02:00
com.strncpy( search->filename, dir, sizeof ( search->filename ));
2007-06-21 22:00:00 +02:00
search->next = fs_searchpaths;
2008-06-05 22:00:00 +02:00
search->flags = flags;
2007-06-21 22:00:00 +02:00
fs_searchpaths = search;
2008-08-14 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
2007-06-21 22:00:00 +02:00
}
/*
================
FS_AddGameHierarchy
================
*/
2009-08-29 22:00:00 +02:00
void FS_AddGameHierarchy( const char *dir, int flags )
2007-06-21 22:00:00 +02:00
{
// Add the common game directory
2010-03-27 22:00:00 +01:00
if( dir && *dir ) FS_AddGameDirectory( va( "%s%s/", fs_basedir, dir ), flags );
2007-06-21 22:00:00 +02:00
}
/*
============
FS_FileExtension
============
*/
2008-08-04 22:00:00 +02:00
const char *FS_FileExtension( const char *in )
2007-06-21 22:00:00 +02:00
{
const char *separator, *backslash, *colon, *dot;
2010-05-28 22:00:00 +02:00
separator = com.strrchr( in, '/' );
backslash = com.strrchr( in, '\\' );
2009-01-22 22:00:00 +01:00
if( !separator || separator < backslash ) separator = backslash;
colon = com.strrchr( in, ':' );
if( !separator || separator < colon ) separator = colon;
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
dot = com.strrchr( in, '.' );
2010-11-19 22:00:00 +01:00
if( dot == NULL || ( separator && ( dot < separator )))
2007-06-21 22:00:00 +02:00
return "";
return dot + 1;
}
/*
============
FS_FileWithoutPath
============
*/
2009-01-22 22:00:00 +01:00
const char *FS_FileWithoutPath( const char *in )
2007-06-21 22:00:00 +02:00
{
const char *separator, *backslash, *colon;
2009-01-22 22:00:00 +01:00
separator = com.strrchr( in, '/' );
backslash = com.strrchr( in, '\\' );
if( !separator || separator < backslash ) separator = backslash;
colon = com.strrchr( in, ':' );
if( !separator || separator < colon ) separator = colon;
2007-06-21 22:00:00 +02:00
return separator ? separator + 1 : in;
}
2007-06-29 22:00:00 +02:00
/*
============
FS_ExtractFilePath
============
*/
2008-08-08 22:00:00 +02:00
void FS_ExtractFilePath( const char* const path, char* dest )
2007-06-29 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
const char *src;
src = path + com.strlen( path ) - 1;
2007-06-29 22:00:00 +02:00
// back up until a \ or the start
2009-01-22 22:00:00 +01:00
while( src != path && !(*(src - 1) == '\\' || *(src - 1) == '/' ))
2007-06-29 22:00:00 +02:00
src--;
2008-08-08 22:00:00 +02:00
if( src != path )
{
2009-01-22 22:00:00 +01:00
Mem_Copy( dest, path, src - path );
2008-08-08 22:00:00 +02:00
dest[src - path - 1] = 0; // cutoff backslash
}
2009-01-22 22:00:00 +01:00
else com.strcpy( dest, "" ); // file without path
2007-06-29 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
/*
================
FS_ClearSearchPath
================
*/
2008-06-05 22:00:00 +02:00
void FS_ClearSearchPath( void )
2007-06-21 22:00:00 +02:00
{
2008-06-05 22:00:00 +02:00
while( fs_searchpaths )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
searchpath_t *search = fs_searchpaths;
2008-06-05 22:00:00 +02:00
2009-08-29 22:00:00 +02:00
if( search->flags & FS_STATIC_PATH )
2007-06-21 22:00:00 +02:00
{
2010-11-15 22:00:00 +01:00
// skip read-only pathes
2009-01-22 22:00:00 +01:00
if( search->next )
fs_searchpaths = search->next->next;
2008-06-05 22:00:00 +02:00
else break;
}
else fs_searchpaths = search->next;
if( search->pack )
{
if( search->pack->files )
2008-08-14 22:00:00 +02:00
Mem_Free( search->pack->files );
Mem_Free( search->pack );
}
if( search->wad )
{
W_Close( search->wad );
2007-06-21 22:00:00 +02:00
}
2008-06-05 22:00:00 +02:00
Mem_Free( search );
2007-06-21 22:00:00 +02:00
}
}
/*
====================
FS_CheckNastyPath
Return true if the path should be rejected due to one of the following:
1: path elements that are non-portable
2: path elements that would allow access to files outside the game directory,
or are just not a good idea for a mod to be using.
====================
*/
2010-10-26 22:00:00 +02:00
int FS_CheckNastyPath( const char *path, qboolean isgamedir )
2007-06-21 22:00:00 +02:00
{
// all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless
2009-01-21 22:00:00 +01:00
if( !path[0] ) return 2;
2007-06-21 22:00:00 +02:00
// Mac: don't allow Mac-only filenames - : is a directory separator
// instead of /, but we rely on / working already, so there's no reason to
// support a Mac-only path
// Amiga and Windows: : tries to go to root of drive
2009-01-21 22:00:00 +01:00
if( com.strstr( path, ":" ) && !fs_ext_path ) return 1; // non-portable attempt to go to root of drive
2007-06-21 22:00:00 +02:00
// Amiga: // is parent directory
2009-01-21 22:00:00 +01:00
if( com.strstr( path, "//" ) && !fs_ext_path ) return 1; // non-portable attempt to go to parent directory
2007-06-21 22:00:00 +02:00
// all: don't allow going to parent directory (../ or /../)
2009-01-21 22:00:00 +01:00
if( com.strstr( path, ".." ) && !fs_ext_path ) return 2; // attempt to go outside the game directory
2007-06-21 22:00:00 +02:00
// Windows and UNIXes: don't allow absolute paths
2009-01-21 22:00:00 +01:00
if( path[0] == '/' && !fs_ext_path ) return 2; // attempt to go outside the game directory
2007-06-21 22:00:00 +02:00
2009-01-21 22:00:00 +01:00
// all: don't allow . characters before the last slash (it should only be used in filenames, not path elements),
// this catches all imaginable cases of ./, ../, .../, etc
2009-01-22 22:00:00 +01:00
if( com.strchr( path, '.' ) && !fs_ext_path )
2007-06-21 22:00:00 +02:00
{
2009-01-21 22:00:00 +01:00
if( isgamedir ) return 2; // gamedir is entirely path elements, so simply forbid . entirely
if( com.strchr( path, '.' ) < com.strrchr( path, '/' )) return 2; // possible attempt to go outside the game directory
2007-06-21 22:00:00 +02:00
}
// all: forbid trailing slash on gamedir
2009-01-21 22:00:00 +01:00
if( isgamedir && !fs_ext_path && path[com.strlen( path )-1] == '/' ) return 2;
2007-06-21 22:00:00 +02:00
// all: forbid leading dot on any filename for any reason
2009-01-21 22:00:00 +01:00
if( com.strstr( path, "/." ) && !fs_ext_path ) return 2; // attempt to go outside the game directory
2007-06-21 22:00:00 +02:00
// after all these checks we're pretty sure it's a / separated filename
// and won't do much if any harm
return false;
}
/*
================
FS_Rescan
================
*/
2009-01-18 22:00:00 +01:00
void FS_Rescan( void )
2007-06-21 22:00:00 +02:00
{
2009-09-10 22:00:00 +02:00
MsgDev( D_NOTE, "FS_Rescan( %s )\n", SI.GameInfo->title );
2007-06-21 22:00:00 +02:00
FS_ClearSearchPath();
2009-09-10 22:00:00 +02:00
FS_AddGameHierarchy( SI.GameInfo->basedir, 0 );
2010-04-12 22:00:00 +02:00
FS_AddGameHierarchy( SI.GameInfo->gamedir, FS_GAMEDIR_PATH );
2009-01-18 22:00:00 +01:00
}
void FS_Rescan_f( void )
{
FS_Rescan();
2007-06-21 22:00:00 +02:00
}
2009-09-10 22:00:00 +02:00
void FS_UpdateSysInfo( void )
2007-06-21 22:00:00 +02:00
{
2009-09-10 22:00:00 +02:00
com.strcpy( SI.username, Sys_GetCurrentUser());
2010-07-21 22:00:00 +02:00
SI.developer = Sys.developer;
2009-09-10 22:00:00 +02:00
SI.version = XASH_VERSION;
2007-06-21 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
static qboolean FS_ParseVector( script_t *script, float *v, size_t size )
2009-11-10 22:00:00 +01:00
{
uint i;
token_t token;
2010-10-26 22:00:00 +02:00
qboolean bracket = false;
2009-11-10 22:00:00 +01:00
if( v == NULL || size == 0 )
return false;
Mem_Set( v, 0, sizeof( *v ) * size );
if( size == 1 )
return PS_GetFloat( script, 0, v );
if( !PS_ReadToken( script, false, &token ))
return false;
if( token.type == TT_PUNCTUATION && !com.stricmp( token.string, "(" ))
bracket = true;
else PS_SaveToken( script, &token ); // save token to right get it again
for( i = 0; i < size; i++ )
{
if( !PS_GetFloat( script, false, &v[i] ))
v[i] = 0; // because Com_ReadFloat may return 0 if parsing expression it's not a number
}
if( !bracket ) return true; // done
if( !PS_ReadToken( script, false, &token ))
return false;
if( token.type == TT_PUNCTUATION && !com.stricmp( token.string, ")" ))
return true;
return false;
}
2010-07-22 22:00:00 +02:00
/*
================
FS_WriteGameInfo
assume GameInfo is valid
================
*/
2010-10-26 22:00:00 +02:00
static qboolean FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
2007-08-13 22:00:00 +02:00
{
2010-07-22 22:00:00 +02:00
file_t *f;
int i;
2007-08-13 22:00:00 +02:00
2010-07-22 22:00:00 +02:00
if( !GameInfo ) return false;
2010-09-10 22:00:00 +02:00
f = FS_Open( filepath, "w", false ); // we in binary-mode
2010-07-22 22:00:00 +02:00
if( !f ) return false;
FS_Print( f, "// generated by Xash3D\r\r\n" );
if( com.strlen( GameInfo->basedir ))
FS_Printf( f, "basedir\t\t\"%s\"\n", GameInfo->basedir );
if( com.strlen( GameInfo->gamedir ))
FS_Printf( f, "gamedir\t\t\"%s\"\n", GameInfo->gamedir );
if( com.strlen( GameInfo->title ))
FS_Printf( f, "title\t\t\"%s\"\n", GameInfo->title );
if( com.strlen( GameInfo->startmap ))
FS_Printf( f, "startmap\t\t\"%s\"\n", GameInfo->startmap );
if( com.strlen( GameInfo->trainmap ))
FS_Printf( f, "trainmap\t\t\"%s\"\n", GameInfo->trainmap );
if( com.strlen( GameInfo->gameHint ))
FS_Printf( f, "gameHint\t\t\"%s\"\n", GameInfo->gameHint );
if( GameInfo->version != 0.0f )
FS_Printf( f, "version\t\t%g\n", GameInfo->version );
if( GameInfo->size != 0 )
FS_Printf( f, "size\t\t%i\n", GameInfo->size );
if( com.strlen( GameInfo->game_url ))
FS_Printf( f, "url_info\t\t\"%s\"\n", GameInfo->game_url );
if( com.strlen( GameInfo->update_url ))
FS_Printf( f, "url_update\t\t\"%s\"\n", GameInfo->update_url );
if( com.strlen( GameInfo->type ))
FS_Printf( f, "type\t\t\"%s\"\n", GameInfo->type );
if( com.strlen( GameInfo->date ))
FS_Printf( f, "date\t\t\"%s\"\n", GameInfo->date );
2010-10-21 22:00:00 +02:00
if( com.strlen( GameInfo->dll_path ))
FS_Printf( f, "dllpath\t\t\"%s\"\n", GameInfo->dll_path );
if( com.strlen( GameInfo->game_dll ))
FS_Printf( f, "gamedll\t\t\"%s\"\n", GameInfo->game_dll );
2010-07-22 22:00:00 +02:00
switch( GameInfo->gamemode )
{
case 1: FS_Print( f, "gamemode\t\t\"singleplayer_only\"\n" ); break;
case 2: FS_Print( f, "gamemode\t\t\"multiplayer_only\"\n" ); break;
}
if( com.strlen( GameInfo->sp_entity ))
2010-10-21 22:00:00 +02:00
FS_Printf( f, "sp_entity\t\t\"%s\"\n", GameInfo->sp_entity );
if( com.strlen( GameInfo->mp_entity ))
FS_Printf( f, "mp_entity\t\t\"%s\"\n", GameInfo->mp_entity );
2010-07-22 22:00:00 +02:00
2010-08-15 22:00:00 +02:00
for( i = 0; i < 4; i++ )
2010-07-22 22:00:00 +02:00
{
float *min, *max;
if( i && ( VectorIsNull( GameInfo->client_mins[i] ) || VectorIsNull( GameInfo->client_maxs[i] )))
continue;
min = GameInfo->client_mins[i];
max = GameInfo->client_maxs[i];
FS_Printf( f, "hull%i\t\t( %g %g %g ) ( %g %g %g )\n", i, min[0], min[1], min[2], max[0], max[1], max[2] );
}
if( GameInfo->max_edicts > 0 )
FS_Printf( f, "max_edicts\t%i\n", GameInfo->max_edicts );
2010-10-26 22:00:00 +02:00
if( GameInfo->max_tents > 0 )
FS_Printf( f, "max_tempents\t%i\n", GameInfo->max_tents );
if( GameInfo->max_beams > 0 )
2010-11-19 22:00:00 +01:00
FS_Printf( f, "max_beams\t\t%i\n", GameInfo->max_beams );
2010-10-26 22:00:00 +02:00
if( GameInfo->max_particles > 0 )
FS_Printf( f, "max_particles\t%i\n", GameInfo->max_particles );
2010-07-22 22:00:00 +02:00
FS_Print( f, "\r\r\n" );
FS_Close( f ); // all done
return true;
}
/*
================
FS_CreateDefaultGameInfo
================
*/
void FS_CreateDefaultGameInfo( const char *filename )
{
gameinfo_t defGI;
Mem_Set( &defGI, 0, sizeof( defGI ));
// setup default values
2010-10-26 22:00:00 +02:00
defGI.max_edicts = 900; // default value if not specified
defGI.max_tents = 500;
2010-11-19 22:00:00 +01:00
defGI.max_beams = 128;
defGI.max_particles = 4096;
2010-07-22 22:00:00 +02:00
defGI.version = 1.0;
2010-09-09 22:00:00 +02:00
com.strncpy( defGI.gameHint, "Half-Life", sizeof( defGI.gameHint ));
com.strncpy( defGI.title, "New Game", sizeof( defGI.title ));
com.strncpy( defGI.gamedir, gs_basedir, sizeof( defGI.gamedir ));
com.strncpy( defGI.basedir, fs_defaultdir->string, sizeof( defGI.basedir ));
com.strncpy( defGI.sp_entity, "info_player_start", sizeof( defGI.sp_entity ));
2010-10-21 22:00:00 +02:00
com.strncpy( defGI.mp_entity, "info_player_deathmatch", sizeof( defGI.mp_entity ));
2010-11-15 22:00:00 +01:00
com.strncpy( defGI.dll_path, "cl_dlls", sizeof( defGI.dll_path ));
com.strncpy( defGI.game_dll, "dlls/hl.dll", sizeof( defGI.game_dll ));
2010-09-09 22:00:00 +02:00
com.strncpy( defGI.startmap, "newmap", sizeof( defGI.startmap ));
2010-07-22 22:00:00 +02:00
VectorSet( defGI.client_mins[0], 0, 0, 0 );
VectorSet( defGI.client_maxs[0], 0, 0, 0 );
VectorSet( defGI.client_mins[1], -16, -16, -36 );
VectorSet( defGI.client_maxs[1], 16, 16, 36 );
VectorSet( defGI.client_mins[2], -32, -32, -32 );
VectorSet( defGI.client_maxs[2], 32, 32, 32 );
VectorSet( defGI.client_mins[3], -16, -16, -18 );
VectorSet( defGI.client_maxs[3], 16, 16, 18 );
// make simple gameinfo.txt
FS_WriteGameInfo( filename, &defGI );
2007-08-13 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo )
2010-07-22 22:00:00 +02:00
{
script_t *script = NULL;
token_t token;
if( !GameInfo ) return false;
script = PS_LoadScript( filename, NULL, 0 );
if( !script ) return false;
// setup default values
2010-10-26 22:00:00 +02:00
GameInfo->max_edicts = 900; // default value if not specified
GameInfo->max_tents = 500;
2010-11-19 22:00:00 +01:00
GameInfo->max_beams = 128;
GameInfo->max_particles = 4096;
2010-09-09 22:00:00 +02:00
GameInfo->version = 1.0f;
2010-07-22 22:00:00 +02:00
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->gameHint, "Half-Life", sizeof( GameInfo->gameHint ));
com.strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
com.strncpy( GameInfo->gamedir, gamedir, sizeof( GameInfo->gamedir ));
com.strncpy( GameInfo->basedir, "valve", sizeof( GameInfo->basedir )); // all liblist.gam have 'valve' as basedir
com.strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
2010-10-21 22:00:00 +02:00
com.strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
com.strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
2010-11-15 22:00:00 +01:00
com.strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
2010-10-21 22:00:00 +02:00
2010-07-22 22:00:00 +02:00
VectorSet( GameInfo->client_mins[0], 0, 0, 0 );
VectorSet( GameInfo->client_maxs[0], 0, 0, 0 );
VectorSet( GameInfo->client_mins[1], -16, -16, -36 );
VectorSet( GameInfo->client_maxs[1], 16, 16, 36 );
VectorSet( GameInfo->client_mins[2], -32, -32, -32 );
VectorSet( GameInfo->client_maxs[2], 32, 32, 32 );
VectorSet( GameInfo->client_mins[3], -16, -16, -18 );
VectorSet( GameInfo->client_maxs[3], 16, 16, 18 );
while( script )
{
if( !PS_ReadToken( script, SC_ALLOW_NEWLINES, &token ))
break;
if( !com.stricmp( token.string, "game" ))
{
PS_GetString( script, false, GameInfo->title, sizeof( GameInfo->title ));
}
if( !com.stricmp( token.string, "gamedir" ))
{
PS_GetString( script, false, GameInfo->gamedir, sizeof( GameInfo->gamedir ));
}
else if( !com.stricmp( token.string, "startmap" ))
{
PS_GetString( script, false, GameInfo->startmap, sizeof( GameInfo->startmap ));
2010-10-21 22:00:00 +02:00
FS_StripExtension( GameInfo->startmap ); // HQ2:Amen has extension .bsp
2010-07-22 22:00:00 +02:00
}
else if( !com.stricmp( token.string, "trainmap" ))
{
PS_GetString( script, false, GameInfo->trainmap, sizeof( GameInfo->trainmap ));
2010-10-21 22:00:00 +02:00
FS_StripExtension( GameInfo->trainmap ); // HQ2:Amen has extension .bsp
2010-07-22 22:00:00 +02:00
}
else if( !com.stricmp( token.string, "url_info" ))
{
PS_GetString( script, false, GameInfo->game_url, sizeof( GameInfo->game_url ));
}
else if( !com.stricmp( token.string, "url_dl" ))
{
PS_GetString( script, false, GameInfo->update_url, sizeof( GameInfo->update_url ));
}
2010-10-21 22:00:00 +02:00
else if( !com.stricmp( token.string, "gamedll" ))
{
PS_GetString( script, false, GameInfo->game_dll, sizeof( GameInfo->game_dll ));
}
2010-07-22 22:00:00 +02:00
else if( !com.stricmp( token.string, "type" ))
{
PS_ReadToken( script, 0, &token );
if( !com.stricmp( token.string, "singleplayer_only" ))
{
GameInfo->gamemode = 1;
com.strncpy( GameInfo->type, "Single", sizeof( GameInfo->type ));
}
else if( !com.stricmp( token.string, "multiplayer_only" ))
{
GameInfo->gamemode = 2;
com.strncpy( GameInfo->type, "Multiplayer", sizeof( GameInfo->type ));
}
else
{
// pass type without changes
GameInfo->gamemode = 0;
com.strncpy( GameInfo->type, token.string, sizeof( GameInfo->type ));
}
}
else if( !com.stricmp( token.string, "version" ))
{
PS_ReadToken( script, false, &token );
GameInfo->version = com.atof( token.string );
}
else if( !com.stricmp( token.string, "size" ))
{
PS_ReadToken( script, false, &token );
GameInfo->size = com.atoi( token.string );
}
else if( !com.stricmp( token.string, "mpentity" ))
{
2010-10-21 22:00:00 +02:00
PS_GetString( script, false, GameInfo->mp_entity, sizeof( GameInfo->mp_entity ));
2010-07-22 22:00:00 +02:00
}
}
PS_FreeScript( script );
return true;
}
/*
================
FS_ConvertGameInfo
================
*/
void FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path )
{
gameinfo_t GameInfo;
Mem_Set( &GameInfo, 0, sizeof( GameInfo ));
if( FS_ParseLiblistGam( liblist_path, gamedir, &GameInfo ))
{
if( FS_WriteGameInfo( gameinfo_path, &GameInfo ))
MsgDev( D_INFO, "Convert %s to %s\n", liblist_path, gameinfo_path );
}
}
2009-09-10 22:00:00 +02:00
/*
================
FS_ParseGameInfo
================
*/
2010-10-26 22:00:00 +02:00
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo )
2007-06-21 22:00:00 +02:00
{
2008-11-01 22:00:00 +01:00
script_t *script = NULL;
2009-09-24 22:00:00 +02:00
string fs_path, filepath;
2010-07-22 22:00:00 +02:00
string liblist;
2008-11-01 22:00:00 +01:00
token_t token;
2007-10-05 22:00:00 +02:00
2010-07-22 22:00:00 +02:00
com.snprintf( filepath, sizeof( filepath ), "%s/gameinfo.txt", gamedir );
com.snprintf( liblist, sizeof( liblist ), "%s/liblist.gam", gamedir );
2010-09-10 22:00:00 +02:00
if( !FS_FileExists( filepath, false ) && FS_FileExists( liblist, false ))
2010-07-22 22:00:00 +02:00
FS_ConvertGameInfo( gamedir, filepath, liblist );
2009-09-10 22:00:00 +02:00
2009-09-24 22:00:00 +02:00
// force to create gameinfo for specified game if missing
2010-09-10 22:00:00 +02:00
if( !com.stricmp( gs_basedir, gamedir ) && !FS_FileExists( filepath, false ))
2010-07-22 22:00:00 +02:00
FS_CreateDefaultGameInfo( filepath );
2009-09-24 22:00:00 +02:00
2009-10-23 22:00:00 +02:00
if( !GameInfo ) return false; // no dest
2009-09-24 22:00:00 +02:00
script = PS_LoadScript( filepath, NULL, 0 );
2009-09-10 22:00:00 +02:00
if( !script ) return false;
2009-09-24 22:00:00 +02:00
// setup default values
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
2010-10-26 22:00:00 +02:00
GameInfo->max_edicts = 900; // default value if not specified
GameInfo->max_tents = 500;
2010-11-10 22:00:00 +01:00
GameInfo->max_beams = 128;
2010-11-19 22:00:00 +01:00
GameInfo->max_particles = 4096;
2010-09-09 22:00:00 +02:00
GameInfo->version = 1.0f;
2009-11-10 22:00:00 +01:00
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->gameHint, "Half-Life", sizeof( GameInfo->gameHint ));
com.strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
com.strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
2010-10-21 22:00:00 +02:00
com.strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
2010-11-15 22:00:00 +01:00
com.strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
com.strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->startmap, "", sizeof( GameInfo->startmap ));
2009-09-24 22:00:00 +02:00
2009-11-23 22:00:00 +01:00
VectorSet( GameInfo->client_mins[0], 0, 0, 0 );
VectorSet( GameInfo->client_maxs[0], 0, 0, 0 );
VectorSet( GameInfo->client_mins[1], -16, -16, -36 );
VectorSet( GameInfo->client_maxs[1], 16, 16, 36 );
VectorSet( GameInfo->client_mins[2], -32, -32, -32 );
VectorSet( GameInfo->client_maxs[2], 32, 32, 32 );
VectorSet( GameInfo->client_mins[3], -16, -16, -18 );
VectorSet( GameInfo->client_maxs[3], 16, 16, 18 );
2007-11-25 22:00:00 +01:00
2008-11-01 22:00:00 +01:00
while( script )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
if( !PS_ReadToken( script, SC_ALLOW_NEWLINES, &token ))
2008-11-01 22:00:00 +01:00
break;
2007-06-21 22:00:00 +02:00
2008-11-01 22:00:00 +01:00
if( !com.stricmp( token.string, "basedir" ))
{
2010-09-09 22:00:00 +02:00
PS_GetString( script, false, fs_path, sizeof( fs_path ));
2009-09-10 22:00:00 +02:00
if( com.stricmp( fs_path, GameInfo->basedir ) || com.stricmp( fs_path, GameInfo->gamedir ))
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->basedir, fs_path, sizeof( GameInfo->basedir ));
2007-06-21 22:00:00 +02:00
}
2008-11-01 22:00:00 +01:00
else if( !com.stricmp( token.string, "gamedir" ))
2007-06-21 22:00:00 +02:00
{
2010-09-09 22:00:00 +02:00
PS_GetString( script, false, fs_path, sizeof( fs_path ));
2009-09-10 22:00:00 +02:00
if( com.stricmp( fs_path, GameInfo->basedir ) || com.stricmp( fs_path, GameInfo->gamedir ))
2010-09-09 22:00:00 +02:00
com.strncpy( GameInfo->gamedir, fs_path, sizeof( GameInfo->gamedir ));
2007-06-21 22:00:00 +02:00
}
2008-11-01 22:00:00 +01:00
else if( !com.stricmp( token.string, "title" ))
2007-11-25 22:00:00 +01:00
{
2009-09-10 22:00:00 +02:00
PS_GetString( script, false, GameInfo->title, sizeof( GameInfo->title ));
2007-11-25 22:00:00 +01:00
}
2010-04-20 22:00:00 +02:00
else if( !com.stricmp( token.string, "gameHint" ))
2009-10-16 22:00:00 +02:00
{
2010-04-20 22:00:00 +02:00
PS_GetString( script, false, GameInfo->gameHint, sizeof( GameInfo->gameHint ));
2009-10-16 22:00:00 +02:00
}
2010-10-21 22:00:00 +02:00
else if( !com.stricmp( token.string, "sp_entity" ))
2009-07-12 22:00:00 +02:00
{
2009-09-10 22:00:00 +02:00
PS_GetString( script, false, GameInfo->sp_entity, sizeof( GameInfo->sp_entity ));
2009-07-12 22:00:00 +02:00
}
2010-10-21 22:00:00 +02:00
else if( !com.stricmp( token.string, "mp_entity" ))
2009-07-12 22:00:00 +02:00
{
2010-10-21 22:00:00 +02:00
PS_GetString( script, false, GameInfo->mp_entity, sizeof( GameInfo->mp_entity ));
2009-09-10 22:00:00 +02:00
}
2010-10-21 22:00:00 +02:00
else if( !com.stricmp( token.string, "gamedll" ))
2009-09-28 22:00:00 +02:00
{
2010-10-21 22:00:00 +02:00
PS_GetString( script, false, GameInfo->game_dll, sizeof( GameInfo->game_dll ));
2009-09-28 22:00:00 +02:00
}
2010-10-21 22:00:00 +02:00
else if( !com.stricmp( token.string, "dllpath" ))
2009-07-12 22:00:00 +02:00
{
2010-10-21 22:00:00 +02:00
PS_GetString( script, false, GameInfo->dll_path, sizeof( GameInfo->dll_path ));
2009-07-12 22:00:00 +02:00
}
2008-11-01 22:00:00 +01:00
else if( !com.stricmp( token.string, "startmap" ))
2008-01-20 22:00:00 +01:00
{
2009-09-10 22:00:00 +02:00
PS_GetString( script, false, GameInfo->startmap, sizeof( GameInfo->startmap ));
2010-10-21 22:00:00 +02:00
FS_StripExtension( GameInfo->startmap ); // HQ2:Amen has extension .bsp
2008-01-20 22:00:00 +01:00
}
2009-12-11 22:00:00 +01:00
else if( !com.stricmp( token.string, "trainmap" ))
{
PS_GetString( script, false, GameInfo->trainmap, sizeof( GameInfo->trainmap ));
2010-10-21 22:00:00 +02:00
FS_StripExtension( GameInfo->trainmap ); // HQ2:Amen has extension .bsp
2009-12-11 22:00:00 +01:00
}
2009-12-09 22:00:00 +01:00
else if( !com.stricmp( token.string, "url_info" ))
{
PS_GetString( script, false, GameInfo->game_url, sizeof( GameInfo->game_url ));
}
2010-01-05 22:00:00 +01:00
else if( !com.stricmp( token.string, "url_update" ))
{
PS_GetString( script, false, GameInfo->update_url, sizeof( GameInfo->update_url ));
}
2010-06-10 22:00:00 +02:00
else if( !com.stricmp( token.string, "textures_path" ))
{
PS_GetString( script, false, GameInfo->texpath, sizeof( GameInfo->texpath ));
}
2009-12-09 22:00:00 +01:00
else if( !com.stricmp( token.string, "date" ))
{
PS_GetString( script, false, GameInfo->date, sizeof( GameInfo->date ));
}
else if( !com.stricmp( token.string, "type" ))
{
PS_GetString( script, false, GameInfo->type, sizeof( GameInfo->type ));
}
2008-11-01 22:00:00 +01:00
else if( !com.stricmp( token.string, "version" ))
2007-11-25 22:00:00 +01:00
{
2009-09-10 22:00:00 +02:00
PS_GetFloat( script, false, &GameInfo->version );
2007-11-25 22:00:00 +01:00
}
2009-12-09 22:00:00 +01:00
else if( !com.stricmp( token.string, "size" ))
{
PS_ReadToken( script, 0, &token );
GameInfo->size = com.atoi( token.string );
}
2009-09-24 22:00:00 +02:00
else if( !com.stricmp( token.string, "max_edicts" ))
{
PS_GetInteger( script, false, &GameInfo->max_edicts );
2010-10-26 22:00:00 +02:00
GameInfo->max_edicts = bound( 600, GameInfo->max_edicts, 4096 );
}
else if( !com.stricmp( token.string, "max_tempents" ))
{
PS_GetInteger( script, false, &GameInfo->max_tents );
GameInfo->max_tents = bound( 300, GameInfo->max_tents, 2048 );
}
else if( !com.stricmp( token.string, "max_beams" ))
{
PS_GetInteger( script, false, &GameInfo->max_beams );
GameInfo->max_beams = bound( 64, GameInfo->max_beams, 512 );
}
else if( !com.stricmp( token.string, "max_particles" ))
{
PS_GetInteger( script, false, &GameInfo->max_particles );
GameInfo->max_particles = bound( 1024, GameInfo->max_particles, 8192 );
2009-09-24 22:00:00 +02:00
}
2009-09-10 22:00:00 +02:00
else if( !com.stricmp( token.string, "gamemode" ))
2007-06-21 22:00:00 +02:00
{
2008-11-01 22:00:00 +01:00
PS_ReadToken( script, 0, &token );
2010-01-07 22:00:00 +01:00
if( !com.stricmp( token.string, "singleplayer_only" ))
2009-09-10 22:00:00 +02:00
GameInfo->gamemode = 1;
2010-01-07 22:00:00 +01:00
else if( !com.stricmp( token.string, "multiplayer_only" ))
2009-09-10 22:00:00 +02:00
GameInfo->gamemode = 2;
2008-05-23 22:00:00 +02:00
}
2009-11-10 22:00:00 +01:00
else if( !com.strnicmp( token.string, "hull", 4 ))
2009-09-24 22:00:00 +02:00
{
2009-11-10 22:00:00 +01:00
int hullNum = com.atoi( token.string + 4 );
2010-08-15 22:00:00 +02:00
if( hullNum < 0 || hullNum > 3 )
2009-11-10 22:00:00 +01:00
{
MsgDev( D_ERROR, "FS_ParseGameInfo: Invalid hull number %i. Ignored.\n", hullNum );
PS_SkipRestOfLine( script );
}
else
{
FS_ParseVector( script, GameInfo->client_mins[hullNum], 3 );
FS_ParseVector( script, GameInfo->client_maxs[hullNum], 3 );
}
2009-09-24 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
}
2009-09-10 22:00:00 +02:00
2008-11-01 22:00:00 +01:00
PS_FreeScript( script );
2009-09-10 22:00:00 +02:00
return true;
}
/*
================
FS_LoadGameInfo
2009-09-13 22:00:00 +02:00
can be passed null arg
2009-09-10 22:00:00 +02:00
================
*/
2009-09-13 22:00:00 +02:00
void FS_LoadGameInfo( const char *rootfolder )
2009-09-10 22:00:00 +02:00
{
int i;
// lock uplevel of gamedir for read\write
fs_ext_path = false;
2009-09-13 22:00:00 +02:00
if( rootfolder ) com.strcpy( gs_basedir, rootfolder );
MsgDev( D_NOTE, "FS_LoadGameInfo( %s )\n", gs_basedir );
2009-09-10 22:00:00 +02:00
// clear any old pathes
FS_ClearSearchPath();
// validate gamedir
for( i = 0; i < SI.numgames; i++ )
{
if( !com.stricmp( SI.games[i]->gamefolder, gs_basedir ))
break;
}
2010-02-16 22:00:00 +01:00
if( i == SI.numgames )
Sys_Break( "Couldn't find game directory '%s'\n", gs_basedir );
2009-09-10 22:00:00 +02:00
SI.GameInfo = SI.games[i];
FS_Rescan(); // create new filesystem
2007-06-21 22:00:00 +02:00
}
/*
================
FS_Init
================
*/
2007-11-10 22:00:00 +01:00
void FS_Init( void )
2007-06-21 22:00:00 +02:00
{
2007-08-13 22:00:00 +02:00
stringlist_t dirs;
2010-10-26 22:00:00 +02:00
qboolean hasDefaultDir = false;
2007-08-13 22:00:00 +02:00
int i;
2007-06-21 22:00:00 +02:00
FS_InitMemory();
2007-08-13 22:00:00 +02:00
2010-11-15 22:00:00 +01:00
FS_AddGameDirectory( "./", FS_STATIC_PATH ); // execute system config
2008-06-05 22:00:00 +02:00
2009-01-18 22:00:00 +01:00
Cmd_AddCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
2008-01-28 22:00:00 +01:00
Cmd_AddCommand( "fs_path", FS_Path_f, "show filesystem search pathes" );
2008-06-05 22:00:00 +02:00
Cmd_AddCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
2010-07-29 22:00:00 +02:00
fs_defaultdir = Cvar_Get( "fs_defaultdir", "valve", CVAR_INIT, "game default directory" );
2008-01-28 22:00:00 +01:00
2007-11-25 22:00:00 +01:00
// ignore commandlineoption "-game" for other stuff
2008-08-06 22:00:00 +02:00
if( Sys.app_name == HOST_NORMAL || Sys.app_name == HOST_DEDICATED || Sys.app_name == HOST_BSPLIB )
2007-06-21 22:00:00 +02:00
{
2009-09-10 22:00:00 +02:00
stringlistinit( &dirs );
listdirectory( &dirs, "./" );
stringlistsort( &dirs );
SI.numgames = 0;
2008-08-04 22:00:00 +02:00
2008-11-22 22:00:00 +01:00
if( !FS_GetParmFromCmdLine( "-game", gs_basedir, sizeof( gs_basedir )))
2007-11-25 22:00:00 +01:00
{
2008-08-06 22:00:00 +02:00
if( Sys.app_name == HOST_BSPLIB )
2009-01-22 22:00:00 +01:00
com.strcpy( gs_basedir, fs_defaultdir->string );
2008-11-21 22:00:00 +01:00
else if( Sys_GetModuleName( gs_basedir, MAX_SYSPATH ));
else com.strcpy( gs_basedir, fs_defaultdir->string ); // default dir
2007-11-25 22:00:00 +01:00
}
2010-02-16 22:00:00 +01:00
2010-11-15 22:00:00 +01:00
if( FS_CheckNastyPath( gs_basedir, true ))
2007-11-25 22:00:00 +01:00
{
2008-11-21 22:00:00 +01:00
MsgDev( D_ERROR, "FS_Init: invalid game directory \"%s\"\n", gs_basedir );
com.strcpy( gs_basedir, fs_defaultdir->string ); // default dir
2007-11-25 22:00:00 +01:00
}
// validate directories
2009-01-22 22:00:00 +01:00
for( i = 0; i < dirs.numstrings; i++ )
2007-11-25 22:00:00 +01:00
{
2010-02-16 22:00:00 +01:00
if( !com.stricmp( fs_defaultdir->string, dirs.strings[i] ))
hasDefaultDir = true;
2010-07-29 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
if( !com.stricmp( gs_basedir, dirs.strings[i] ))
2007-11-25 22:00:00 +01:00
break;
}
2007-10-19 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
if( i == dirs.numstrings )
2007-11-25 22:00:00 +01:00
{
2008-06-29 22:00:00 +02:00
MsgDev( D_INFO, "FS_Init: game directory \"%s\" not exist\n", gs_basedir );
2010-02-16 22:00:00 +01:00
if( hasDefaultDir ) com.strcpy( gs_basedir, fs_defaultdir->string ); // default dir
2007-11-25 22:00:00 +01:00
}
2008-08-04 22:00:00 +02:00
2008-11-21 22:00:00 +01:00
// build list of game directories here
2008-08-04 22:00:00 +02:00
FS_AddGameDirectory( "./", 0 );
for( i = 0; i < dirs.numstrings; i++ )
{
2010-07-22 22:00:00 +02:00
const char *ext = FS_FileExtension( dirs.strings[i] );
2009-09-10 22:00:00 +02:00
2010-07-22 22:00:00 +02:00
if( com.stricmp( ext, "" ) || (!com.stricmp( dirs.strings[i], ".." ) && !fs_ext_path ))
2009-09-10 22:00:00 +02:00
continue;
if( !SI.games[SI.numgames] )
SI.games[SI.numgames] = (gameinfo_t *)Mem_Alloc( fs_mempool, sizeof( gameinfo_t ));
2009-09-24 22:00:00 +02:00
if( FS_ParseGameInfo( dirs.strings[i], SI.games[SI.numgames] ))
2009-09-10 22:00:00 +02:00
SI.numgames++; // added
2008-08-04 22:00:00 +02:00
}
2008-11-21 22:00:00 +01:00
stringlistfreecontents( &dirs );
2007-11-25 22:00:00 +01:00
}
2007-11-27 22:00:00 +01:00
2009-09-10 22:00:00 +02:00
FS_UpdateSysInfo();
2010-07-29 22:00:00 +02:00
MsgDev( D_NOTE, "FS_Root: %s\n", sys_rootdir );
2008-11-21 22:00:00 +01:00
MsgDev( D_NOTE, "FS_Init: done\n" );
2007-06-21 22:00:00 +02:00
}
2007-06-27 22:00:00 +02:00
void FS_InitRootDir( char *path )
2007-06-21 22:00:00 +02:00
{
char szTemp[4096];
2008-08-04 22:00:00 +02:00
FS_InitMemory();
2007-10-05 22:00:00 +02:00
// just set cwd
2007-06-21 22:00:00 +02:00
GetModuleFileName( NULL, szTemp, MAX_SYSPATH );
2007-06-29 22:00:00 +02:00
FS_ExtractFilePath( szTemp, szTemp );
2010-11-17 22:00:00 +01:00
SetCurrentDirectory( szTemp );
2007-06-21 22:00:00 +02:00
2007-10-05 22:00:00 +02:00
// use extended pathname
fs_ext_path = true;
2007-06-21 22:00:00 +02:00
FS_ClearSearchPath();
2009-08-29 22:00:00 +02:00
FS_AddGameHierarchy( path, FS_STATIC_PATH );
2007-06-21 22:00:00 +02:00
}
2010-10-26 22:00:00 +02:00
qboolean FS_GetParmFromCmdLine( char *parm, char *out, size_t size )
2007-06-21 22:00:00 +02:00
{
int argc = FS_CheckParm( parm );
if(!argc) return false;
if(!out) return false;
2007-11-08 22:00:00 +01:00
if(!fs_argv[argc + 1]) return false;
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
com.strncpy( out, fs_argv[argc+1], size );
2007-06-21 22:00:00 +02:00
return true;
}
2010-10-26 22:00:00 +02:00
void FS_AllowDirectPaths( qboolean enable )
2009-01-21 22:00:00 +01:00
{
fs_ext_path = enable;
}
2008-08-07 22:00:00 +02:00
/*
================
FS_Shutdown
================
*/
void FS_Shutdown( void )
{
2009-09-13 22:00:00 +02:00
int i;
// release gamedirs
for( i = 0; i < SI.numgames; i++ )
if( SI.games[i] ) Mem_Free( SI.games[i] );
Mem_Set( &SI, 0, sizeof( SI ));
2008-08-07 22:00:00 +02:00
FS_ClearSearchPath(); // release all wad files too
FS_UpdateEnvironmentVariables(); // merge working directory
2008-08-04 22:00:00 +02:00
Mem_FreePool( &fs_mempool );
2007-06-21 22:00:00 +02:00
}
2010-03-27 22:00:00 +01:00
/*
====================
FS_SysFileTime
Internal function used to determine filetime
====================
*/
static long FS_SysFileTime( const char *filename )
{
struct stat buf;
if( stat( filename, &buf ) == -1 )
return -1;
return buf.st_mtime;
}
2007-06-21 22:00:00 +02:00
/*
====================
FS_SysOpen
Internal function used to create a file_t and open the relevant non-packed file on disk
====================
*/
2008-08-13 22:00:00 +02:00
static file_t* FS_SysOpen( const char* filepath, const char* mode )
2007-06-21 22:00:00 +02:00
{
2009-01-21 22:00:00 +01:00
file_t *file;
int mod, opt;
uint ind;
2007-06-21 22:00:00 +02:00
// Parse the mode string
2009-01-21 22:00:00 +01:00
switch( mode[0] )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
case 'r':
mod = O_RDONLY;
opt = 0;
break;
case 'w':
mod = O_WRONLY;
opt = O_CREAT | O_TRUNC;
break;
case 'a':
mod = O_WRONLY;
opt = O_CREAT | O_APPEND;
break;
default:
MsgDev( D_ERROR, "FS_SysOpen(%s, %s): invalid mode\n", filepath, mode );
return NULL;
2007-06-21 22:00:00 +02:00
}
2010-11-19 22:00:00 +01:00
2008-08-02 22:00:00 +02:00
for( ind = 1; mode[ind] != '\0'; ind++ )
2007-06-21 22:00:00 +02:00
{
2008-08-02 22:00:00 +02:00
switch( mode[ind] )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
case '+':
mod = O_RDWR;
break;
case 'b':
opt |= O_BINARY;
break;
default:
MsgDev( D_ERROR, "FS_SysOpen: %s: unknown char in mode (%c)\n", filepath, mode, mode[ind] );
break;
2007-06-21 22:00:00 +02:00
}
}
2008-10-27 22:00:00 +01:00
file = (file_t *)Mem_Alloc( fs_mempool, sizeof( *file ));
2010-03-27 22:00:00 +01:00
file->filetime = FS_SysFileTime( filepath );
2007-06-21 22:00:00 +02:00
file->ungetc = EOF;
2010-03-27 22:00:00 +01:00
file->handle = open( filepath, mod|opt, 0666 );
if( file->handle < 0 )
2007-06-21 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
Mem_Free( file );
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-11-19 22:00:00 +01:00
2010-03-27 22:00:00 +01:00
file->real_length = lseek( file->handle, 0, SEEK_END );
2007-06-21 22:00:00 +02:00
// For files opened in append mode, we start at the end of the file
2010-03-27 22:00:00 +01:00
if( mod & O_APPEND ) file->position = file->real_length;
else lseek( file->handle, 0, SEEK_SET );
2007-06-21 22:00:00 +02:00
return file;
}
/*
===========
FS_OpenPackedFile
Open a packed file using its package file descriptor
===========
*/
2010-02-12 22:00:00 +01:00
file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind )
2007-06-21 22:00:00 +02:00
{
2010-02-12 22:00:00 +01:00
packfile_t *pfile;
int dup_handle;
file_t *file;
2007-06-21 22:00:00 +02:00
pfile = &pack->files[pack_ind];
2010-02-12 22:00:00 +01:00
if( lseek( pack->handle, pfile->offset, SEEK_SET ) == -1 )
2007-06-21 22:00:00 +02:00
return NULL;
2010-02-12 22:00:00 +01:00
dup_handle = dup( pack->handle );
if( dup_handle < 0 )
2007-06-21 22:00:00 +02:00
return NULL;
2010-02-12 22:00:00 +01:00
file = (file_t *)Mem_Alloc( fs_mempool, sizeof( *file ));
2008-10-27 22:00:00 +01:00
Mem_Set( file, 0, sizeof( *file ));
2007-06-21 22:00:00 +02:00
file->handle = dup_handle;
file->real_length = pfile->realsize;
file->offset = pfile->offset;
file->position = 0;
file->ungetc = EOF;
return file;
}
/*
==================
FS_SysFileExists
Look for a file in the filesystem only
==================
*/
2010-10-26 22:00:00 +02:00
qboolean FS_SysFileExists( const char *path )
2007-06-21 22:00:00 +02:00
{
int desc;
2009-01-21 22:00:00 +01:00
desc = open( path, O_RDONLY|O_BINARY );
2007-06-21 22:00:00 +02:00
2009-01-21 22:00:00 +01:00
if( desc < 0 ) return false;
close( desc );
2007-06-21 22:00:00 +02:00
return true;
}
/*
====================
FS_FindFile
Look for a file in the packages and in the filesystem
Return the searchpath where the file was found (or NULL)
and the file index in the package if relevant
====================
*/
2010-10-26 22:00:00 +02:00
static searchpath_t *FS_FindFile( const char *name, int* index, qboolean gamedironly )
2007-06-21 22:00:00 +02:00
{
2009-01-21 22:00:00 +01:00
searchpath_t *search;
char *pEnvPath;
pack_t *pak;
2007-06-21 22:00:00 +02:00
// search through the path, one element at a time
2008-08-14 22:00:00 +02:00
for( search = fs_searchpaths; search; search = search->next )
2007-06-21 22:00:00 +02:00
{
2010-04-12 22:00:00 +02:00
if( gamedironly & !( search->flags & FS_GAMEDIR_PATH ))
continue;
2007-06-21 22:00:00 +02:00
// is the element a pak file?
2008-08-14 22:00:00 +02:00
if( search->pack )
2007-06-21 22:00:00 +02:00
{
int left, right, middle;
pak = search->pack;
2008-08-14 22:00:00 +02:00
// look for the file (binary search)
2007-06-21 22:00:00 +02:00
left = 0;
right = pak->numfiles - 1;
2008-08-14 22:00:00 +02:00
while( left <= right )
2007-06-21 22:00:00 +02:00
{
int diff;
middle = (left + right) / 2;
2009-01-21 22:00:00 +01:00
diff = com.stricmp( pak->files[middle].name, name );
2007-06-21 22:00:00 +02:00
// Found it
2008-08-14 22:00:00 +02:00
if( !diff )
2007-06-21 22:00:00 +02:00
{
2008-08-14 22:00:00 +02:00
if( index ) *index = middle;
2007-06-21 22:00:00 +02:00
return search;
}
2008-08-14 22:00:00 +02:00
// if we're too far in the list
2009-12-17 22:00:00 +01:00
if( diff > 0 )
right = middle - 1;
2007-06-21 22:00:00 +02:00
else left = middle + 1;
}
}
2008-08-14 22:00:00 +02:00
else if( search->wad )
{
2010-07-13 22:00:00 +02:00
dlumpinfo_t *lump;
char type = W_TypeFromExt( name );
2010-10-26 22:00:00 +02:00
qboolean anywadname = true;
2010-07-13 22:00:00 +02:00
string wadname, wadfolder;
string shortname;
// quick reject by filetype
2010-09-10 22:00:00 +02:00
if( type == TYP_NONE ) continue;
2010-07-13 22:00:00 +02:00
FS_ExtractFilePath( name, wadname );
wadfolder[0] = '\0';
if( com.strlen( wadname ))
{
FS_FileBase( wadname, wadname );
com.strncpy( wadfolder, wadname, sizeof( wadfolder ));
FS_DefaultExtension( wadname, ".wad" );
anywadname = false;
}
2010-10-26 22:00:00 +02:00
// make wadname from wad fullpath
FS_FileBase( search->wad->filename, shortname );
FS_DefaultExtension( shortname, ".wad" );
2010-07-13 22:00:00 +02:00
// quick reject by wadname
2010-10-26 22:00:00 +02:00
if( !anywadname && com.stricmp( wadname, shortname ))
2010-07-13 22:00:00 +02:00
continue;
2008-08-14 22:00:00 +02:00
// NOTE: we can't using long names for wad,
// because we using original wad names[16];
W_FileBase( name, shortname );
2010-07-13 22:00:00 +02:00
lump = W_FindLump( search->wad, shortname, type );
2008-08-14 22:00:00 +02:00
if( lump )
{
2010-11-19 22:00:00 +01:00
if( index )
*index = lump - search->wad->lumps;
return search;
2008-08-14 22:00:00 +02:00
}
}
2007-06-21 22:00:00 +02:00
else
{
2009-01-21 22:00:00 +01:00
char netpath[MAX_SYSPATH];
com.sprintf( netpath, "%s%s", search->filename, name );
if( FS_SysFileExists( netpath ))
{
if( index != NULL ) *index = -1;
return search;
}
}
}
2010-03-27 22:00:00 +01:00
if( fs_ext_path && ( pEnvPath = getenv( "Path" )))
2009-01-21 22:00:00 +01:00
{
char netpath[MAX_SYSPATH];
// clear searchpath
search = &fs_directpath;
Mem_Set( search, 0, sizeof( searchpath_t ));
// search for environment path
while( pEnvPath )
{
char *end = com.strchr( pEnvPath, ';' );
if( !end ) break;
com.strncpy( search->filename, pEnvPath, (end - pEnvPath) + 1 );
com.strcat( search->filename, "\\" );
com.snprintf( netpath, MAX_SYSPATH, "%s%s", search->filename, name );
if( FS_SysFileExists( netpath ))
2007-06-21 22:00:00 +02:00
{
2009-01-21 22:00:00 +01:00
if( index != NULL ) *index = -1;
2007-06-21 22:00:00 +02:00
return search;
}
2009-01-21 22:00:00 +01:00
pEnvPath += (end - pEnvPath) + 1; // move pointer
2007-06-21 22:00:00 +02:00
}
}
2008-08-14 22:00:00 +02:00
if( index != NULL ) *index = -1;
2007-06-21 22:00:00 +02:00
return NULL;
}
/*
===========
FS_OpenReadFile
Look for a file in the search paths and open it in read-only mode
===========
*/
2010-10-26 22:00:00 +02:00
file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly )
2007-06-21 22:00:00 +02:00
{
2010-04-12 22:00:00 +02:00
searchpath_t *search;
int pack_ind;
2007-06-21 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
search = FS_FindFile( filename, &pack_ind, gamedironly );
2007-06-21 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
// not found?
if( search == NULL )
return NULL;
2007-06-21 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
if( search->pack )
return FS_OpenPackedFile( search->pack, pack_ind );
2009-01-22 22:00:00 +01:00
else if( search->wad )
return NULL; // let W_LoadFile get lump correctly
2008-08-14 22:00:00 +02:00
else if( pack_ind < 0 )
2007-06-21 22:00:00 +02:00
{
2008-08-14 22:00:00 +02:00
// found in the filesystem?
2009-01-22 22:00:00 +01:00
char path [MAX_SYSPATH];
com.sprintf( path, "%s%s", search->filename, filename );
2008-07-23 22:00:00 +02:00
return FS_SysOpen( path, mode );
2008-08-14 22:00:00 +02:00
}
return NULL;
2007-06-21 22:00:00 +02:00
}
2007-12-20 22:00:00 +01:00
/*
=============================================================================
2007-12-17 22:00:00 +01:00
2007-06-21 22:00:00 +02:00
MAIN PUBLIC FUNCTIONS
=============================================================================
*/
/*
====================
FS_Open
Open a file. The syntax is the same as fopen
====================
*/
2010-10-26 22:00:00 +02:00
file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly )
2007-06-21 22:00:00 +02:00
{
2010-07-28 22:00:00 +02:00
if( Sys.app_name == HOST_NORMAL || Sys.app_name == HOST_DEDICATED || Sys.app_name == HOST_BSPLIB )
{
2010-11-19 22:00:00 +01:00
// some stupid mappers used leading '/' or '\' in path to models or sounds
if( filepath[0] == '/' || filepath[0] == '\\' ) filepath++;
2010-07-28 22:00:00 +02:00
}
2009-01-21 22:00:00 +01:00
if( FS_CheckNastyPath( filepath, false ))
2007-06-21 22:00:00 +02:00
{
2008-08-08 22:00:00 +02:00
MsgDev( D_NOTE, "FS_Open: (\"%s\", \"%s\"): nasty filename rejected\n", filepath, mode );
2007-06-21 22:00:00 +02:00
return NULL;
}
2009-01-21 22:00:00 +01:00
// if the file is opened in "write", "append", or "read/write" mode
if( mode[0] == 'w' || mode[0] == 'a' || com.strchr( mode, '+' ))
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
char real_path[MAX_SYSPATH];
2007-06-21 22:00:00 +02:00
2009-01-21 22:00:00 +01:00
// open the file on disk directly
com.sprintf( real_path, "%s/%s", fs_gamedir, filepath );
FS_CreatePath( real_path );// Create directories up to the file
return FS_SysOpen( real_path, mode );
2007-06-21 22:00:00 +02:00
}
2008-08-14 22:00:00 +02:00
// else, we look at the various search paths and open the file in read-only mode
2010-09-10 22:00:00 +02:00
return FS_OpenReadFile( filepath, mode, gamedironly );
2007-06-29 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
/*
====================
FS_Close
Close a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_Close( file_t *file )
2007-06-21 22:00:00 +02:00
{
2010-02-12 22:00:00 +01:00
if( close( file->handle ))
return EOF;
2007-06-21 22:00:00 +02:00
2010-02-12 22:00:00 +01:00
Mem_Free( file );
2007-06-21 22:00:00 +02:00
return 0;
}
/*
====================
FS_Write
Write "datasize" bytes into a file
====================
*/
2010-11-19 22:00:00 +01:00
fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
fs_offset_t result;
2007-06-21 22:00:00 +02:00
2008-10-25 22:00:00 +02:00
if( !file ) return 0;
// if necessary, seek to the exact file position we're supposed to be
if( file->buff_ind != file->buff_len )
2010-11-19 22:00:00 +01:00
lseek( file->handle, file->buff_ind - file->buff_len, SEEK_CUR );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
// purge cached data
FS_Purge( file );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
// write the buffer and update the position
result = write( file->handle, data, (fs_offset_t)datasize );
file->position = lseek( file->handle, 0, SEEK_CUR );
if( file->real_length < file->position )
2007-06-21 22:00:00 +02:00
file->real_length = file->position;
2010-11-19 22:00:00 +01:00
if( result < 0 )
return 0;
2007-06-21 22:00:00 +02:00
return result;
}
/*
====================
FS_Read
Read up to "buffersize" bytes from a file
====================
*/
2010-11-19 22:00:00 +01:00
fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
fs_offset_t count, done;
fs_offset_t nb;
2007-06-21 22:00:00 +02:00
2007-10-31 22:00:00 +01:00
// nothing to copy
2010-11-19 22:00:00 +01:00
if( buffersize == 0 ) return 1;
2007-06-21 22:00:00 +02:00
// Get rid of the ungetc character
2010-11-19 22:00:00 +01:00
if( file->ungetc != EOF )
2007-06-21 22:00:00 +02:00
{
((char*)buffer)[0] = file->ungetc;
buffersize--;
file->ungetc = EOF;
done = 1;
}
else done = 0;
2010-11-19 22:00:00 +01:00
// first, we copy as many bytes as we can from "buff"
if( file->buff_ind < file->buff_len )
2007-06-21 22:00:00 +02:00
{
count = file->buff_len - file->buff_ind;
2010-11-19 22:00:00 +01:00
done += ((fs_offset_t)buffersize > count ) ? count : (fs_offset_t)buffersize;
Mem_Copy( buffer, &file->buff[file->buff_ind], done );
2007-06-21 22:00:00 +02:00
file->buff_ind += done;
buffersize -= done;
2010-11-19 22:00:00 +01:00
if( buffersize == 0 )
return done;
2007-06-21 22:00:00 +02:00
}
// NOTE: at this point, the read buffer is always empty
2010-11-19 22:00:00 +01:00
// we must take care to not read after the end of the file
count = file->real_length - file->position;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
// if we have a lot of data to get, put them directly into "buffer"
if( buffersize > sizeof( file->buff ) / 2 )
{
if( count > (fs_offset_t)buffersize )
count = (fs_offset_t)buffersize;
lseek( file->handle, file->offset + file->position, SEEK_SET );
nb = read (file->handle, &((byte *)buffer)[done], count );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
if( nb > 0 )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
done += nb;
file->position += nb;
// Purge cached data
FS_Purge (file);
2007-06-21 22:00:00 +02:00
}
}
2010-11-19 22:00:00 +01:00
else
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
if( count > (fs_offset_t)sizeof( file->buff ))
count = (fs_offset_t)sizeof( file->buff );
lseek( file->handle, file->offset + file->position, SEEK_SET );
nb = read( file->handle, file->buff, count );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
if( nb > 0 )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
file->buff_len = nb;
file->position += nb;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
// copy the requested data in "buffer" (as much as we can)
2007-06-21 22:00:00 +02:00
count = (fs_offset_t)buffersize > file->buff_len ? file->buff_len : (fs_offset_t)buffersize;
2010-11-19 22:00:00 +01:00
Mem_Copy (&((byte *)buffer)[done], file->buff, count );
2007-06-21 22:00:00 +02:00
file->buff_ind = count;
2010-11-19 22:00:00 +01:00
done += count;
2007-06-21 22:00:00 +02:00
}
}
return done;
}
/*
====================
FS_Print
Print a string into a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_Print( file_t *file, const char *msg )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
return FS_Write( file, msg, com.strlen( msg ));
2007-06-21 22:00:00 +02:00
}
/*
====================
FS_Printf
Print a string into a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_Printf( file_t *file, const char* format, ... )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
int result;
va_list args;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
va_start( args, format );
result = FS_VPrintf( file, format, args );
va_end( args );
2007-06-21 22:00:00 +02:00
return result;
}
/*
====================
FS_VPrintf
Print a string into a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_VPrintf( file_t *file, const char* format, va_list ap )
2007-06-21 22:00:00 +02:00
{
2008-10-25 22:00:00 +02:00
int len;
2010-07-26 22:00:00 +02:00
fs_offset_t buff_size = MAX_SYSPATH;
2008-10-25 22:00:00 +02:00
char *tempbuff;
2007-06-21 22:00:00 +02:00
2008-10-25 22:00:00 +02:00
if( !file ) return 0;
while( 1 )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
tempbuff = (char *)Mem_Alloc( fs_mempool, buff_size );
len = com.vsprintf( tempbuff, format, ap );
if( len >= 0 && len < buff_size ) break;
Mem_Free( tempbuff );
2007-06-21 22:00:00 +02:00
buff_size *= 2;
}
2009-01-22 22:00:00 +01:00
len = write( file->handle, tempbuff, len );
Mem_Free( tempbuff );
2007-06-21 22:00:00 +02:00
return len;
}
/*
====================
FS_Getc
Get the next character of a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_Getc( file_t *file )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
char c;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
if( FS_Read( file, &c, 1) != 1 )
2007-06-21 22:00:00 +02:00
return EOF;
return c;
}
/*
====================
FS_UnGetc
Put a character back into the read buffer (only supports one character!)
====================
*/
2010-11-19 22:00:00 +01:00
int FS_UnGetc( file_t *file, byte c )
2007-06-21 22:00:00 +02:00
{
// If there's already a character waiting to be read
2010-11-19 22:00:00 +01:00
if( file->ungetc != EOF )
2007-06-21 22:00:00 +02:00
return EOF;
file->ungetc = c;
return c;
}
2010-11-19 22:00:00 +01:00
int FS_Gets( file_t *file, byte *string, size_t bufsize )
2007-06-27 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
int c, end = 0;
2007-06-27 22:00:00 +02:00
while( 1 )
{
c = FS_Getc( file );
2009-08-30 22:00:00 +02:00
if( c == '\r' || c == '\n' || c < 0 )
2007-06-27 22:00:00 +02:00
break;
2009-08-30 22:00:00 +02:00
if( end < bufsize - 1 )
2007-06-27 22:00:00 +02:00
string[end++] = c;
}
string[end] = 0;
// remove \n following \r
2009-08-30 22:00:00 +02:00
if( c == '\r' )
2007-06-27 22:00:00 +02:00
{
2009-08-30 22:00:00 +02:00
c = FS_Getc( file );
if( c != '\n' ) FS_UnGetc( file, (byte)c );
2007-06-27 22:00:00 +02:00
}
return c;
}
2007-06-21 22:00:00 +02:00
/*
====================
FS_Seek
Move the position index in a file
====================
*/
2010-11-19 22:00:00 +01:00
int FS_Seek( file_t *file, fs_offset_t offset, int whence )
2007-06-21 22:00:00 +02:00
{
2010-11-19 22:00:00 +01:00
// compute the file offset
switch( whence )
2007-06-21 22:00:00 +02:00
{
case SEEK_CUR:
offset += file->position - file->buff_len + file->buff_ind;
break;
case SEEK_SET:
break;
case SEEK_END:
offset += file->real_length;
break;
default:
return -1;
}
2010-11-19 22:00:00 +01:00
if( offset < 0 || offset > (long)file->real_length )
return -1;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
// if we have the data in our read buffer, we don't need to actually seek
if( file->position - file->buff_len <= offset && offset <= file->position )
2007-06-21 22:00:00 +02:00
{
file->buff_ind = offset + file->buff_len - file->position;
return 0;
}
// Purge cached data
2010-11-19 22:00:00 +01:00
FS_Purge( file );
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
if( lseek( file->handle, file->offset + offset, SEEK_SET ) == -1 )
return -1;
file->position = offset;
2007-06-21 22:00:00 +02:00
return 0;
}
/*
====================
FS_Tell
Give the current position in a file
====================
*/
2009-07-12 22:00:00 +02:00
fs_offset_t FS_Tell( file_t* file )
2007-06-21 22:00:00 +02:00
{
2008-07-04 22:00:00 +02:00
if( !file ) return 0;
2007-06-21 22:00:00 +02:00
return file->position - file->buff_len + file->buff_ind;
}
2009-07-12 22:00:00 +02:00
/*
====================
FS_Eof
indicates at reached end of file
====================
*/
2010-10-26 22:00:00 +02:00
qboolean FS_Eof( file_t* file )
2007-06-21 22:00:00 +02:00
{
2008-07-04 22:00:00 +02:00
if( !file ) return true;
2007-06-21 22:00:00 +02:00
return (file->position == file->real_length) ? true : false;
}
/*
====================
FS_Purge
Erases any buffered input or output data
====================
*/
2008-07-04 22:00:00 +02:00
void FS_Purge( file_t* file )
2007-06-21 22:00:00 +02:00
{
file->buff_len = 0;
file->buff_ind = 0;
file->ungetc = EOF;
}
/*
============
FS_LoadFile
Filename are relative to the xash directory.
Always appends a 0 byte.
============
*/
2009-01-21 22:00:00 +01:00
byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr )
2007-06-21 22:00:00 +02:00
{
2007-12-17 22:00:00 +01:00
file_t *file;
byte *buf = NULL;
2007-06-21 22:00:00 +02:00
fs_offset_t filesize = 0;
2007-12-17 22:00:00 +01:00
const char *ext = FS_FileExtension( path );
2007-06-21 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
file = FS_Open( path, "rb", false );
2008-08-06 22:00:00 +02:00
if( file )
2007-06-21 22:00:00 +02:00
{
filesize = file->real_length;
2008-06-06 22:00:00 +02:00
buf = (byte *)Mem_Alloc( fs_mempool, filesize + 1 );
2007-06-21 22:00:00 +02:00
buf[filesize] = '\0';
2008-08-14 22:00:00 +02:00
FS_Read( file, buf, filesize );
FS_Close( file );
2007-06-21 22:00:00 +02:00
}
2010-11-19 22:00:00 +01:00
else
{
buf = W_LoadFile( path, &filesize );
}
2007-12-20 22:00:00 +01:00
2008-08-08 22:00:00 +02:00
if( filesizeptr ) *filesizeptr = filesize;
2007-06-21 22:00:00 +02:00
return buf;
}
/*
============
FS_WriteFile
The filename will be prefixed by the current game directory
============
*/
2010-10-26 22:00:00 +02:00
qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len )
2007-06-21 22:00:00 +02:00
{
file_t *file;
2010-09-10 22:00:00 +02:00
file = FS_Open( filename, "wb", false );
if( !file )
2007-06-21 22:00:00 +02:00
{
2008-06-29 22:00:00 +02:00
MsgDev( D_ERROR, "FS_WriteFile: failed on %s\n", filename);
2007-06-21 22:00:00 +02:00
return false;
}
FS_Write (file, data, len);
FS_Close (file);
return true;
}
/*
=============================================================================
OTHERS PUBLIC FUNCTIONS
=============================================================================
*/
/*
============
FS_StripExtension
============
*/
2010-10-28 22:00:00 +02:00
void FS_StripExtension( char *path )
2007-06-21 22:00:00 +02:00
{
2008-10-27 22:00:00 +01:00
size_t length;
2007-06-21 22:00:00 +02:00
2008-10-27 22:00:00 +01:00
length = com.strlen( path ) - 1;
while( length > 0 && path[length] != '.' )
2007-06-21 22:00:00 +02:00
{
length--;
2008-10-27 22:00:00 +01:00
if( path[length] == '/' || path[length] == '\\' || path[length] == ':' )
2007-06-21 22:00:00 +02:00
return; // no extension
}
2008-10-27 22:00:00 +01:00
if( length ) path[length] = 0;
2007-06-21 22:00:00 +02:00
}
/*
==================
FS_DefaultExtension
==================
*/
2009-01-22 22:00:00 +01:00
void FS_DefaultExtension( char *path, const char *extension )
2007-06-21 22:00:00 +02:00
{
const char *src;
// if path doesn't have a .EXT, append extension
// (extension should include the .)
2009-01-22 22:00:00 +01:00
src = path + com.strlen( path ) - 1;
2007-06-21 22:00:00 +02:00
2010-11-19 22:00:00 +01:00
while( *src != '/' && src != path )
2007-06-21 22:00:00 +02:00
{
// it has an extension
2010-11-19 22:00:00 +01:00
if( *src == '.' ) return;
2007-06-21 22:00:00 +02:00
src--;
}
2009-01-22 22:00:00 +01:00
com.strcat( path, extension );
2007-06-21 22:00:00 +02:00
}
/*
==================
FS_FileExists
Look for a file in the packages and in the filesystem
==================
*/
2010-10-26 22:00:00 +02:00
qboolean FS_FileExists( const char *filename, qboolean gamedironly )
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( FS_FindFile( filename, NULL, gamedironly ))
2008-01-03 22:00:00 +01:00
return true;
return false;
2007-06-21 22:00:00 +02:00
}
2010-11-19 22:00:00 +01:00
/*
==================
FS_GetDiskPath
Build direct path for file in the filesystem
return NULL for file in pack
==================
*/
2010-10-26 22:00:00 +02:00
const char *FS_GetDiskPath( const char *name, qboolean gamedironly )
2010-09-30 22:00:00 +02:00
{
int index;
searchpath_t *search;
search = FS_FindFile( name, &index, gamedironly );
if( search )
{
if( index != -1 )
{
// file in pack or wad
return NULL;
}
return va( "%s%s", search->filename, name );
}
return NULL;
}
2010-11-20 22:00:00 +01:00
/*
==================
FS_CheckForCrypt
return true is library is crypted
==================
*/
qboolean FS_CheckForCrypt( const char *dllname )
{
file_t *f;
int key;
f = FS_Open( dllname, "rb", false );
if( !f ) return false;
FS_Seek( f, 64, SEEK_SET ); // skip first 64 bytes
FS_Read( f, &key, sizeof( key ));
FS_Close( f );
return ( key == 0x12345678 ) ? true : false;
}
2010-03-27 22:00:00 +01:00
/*
==================
FS_FindLibrary
search for library, assume index is valid
only for internal use
==================
*/
2010-10-26 22:00:00 +02:00
dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath )
2010-03-27 22:00:00 +01:00
{
string dllpath;
searchpath_t *search;
dll_user_t *hInst;
2010-10-23 22:00:00 +02:00
int i, index;
2010-03-27 22:00:00 +01:00
// check for bad exports
if( !dllname || !*dllname )
return NULL;
2010-10-21 22:00:00 +02:00
// some mods used direct path to dlls
if( com.strstr( dllname, ".." ))
directpath = true;
2010-03-27 22:00:00 +01:00
fs_ext_path = directpath;
2010-10-23 22:00:00 +02:00
// replace all backward slashes
for( i = 0; i < com.strlen( dllname ); i++ )
{
if( dllname[i] == '\\' ) dllpath[i] = '/';
else dllpath[i] = com.tolower( dllname[i] );
}
dllpath[i] = '\0';
2010-03-27 22:00:00 +01:00
FS_DefaultExtension( dllpath, ".dll" ); // trying to apply if forget
2010-09-10 22:00:00 +02:00
search = FS_FindFile( dllpath, &index, false );
2010-03-27 22:00:00 +01:00
if( !search )
{
fs_ext_path = false;
if( directpath ) return NULL; // direct paths fails here
// trying check also 'bin' folder for indirect paths
com.strncpy( dllpath, dllname, sizeof( dllpath ));
2010-09-10 22:00:00 +02:00
search = FS_FindFile( dllpath, &index, false );
2010-03-27 22:00:00 +01:00
if( !search ) return NULL; // unable to find
}
// all done, create dll_user_t struct
hInst = Mem_Alloc( Sys.basepool, sizeof( dll_user_t ));
// save dllname for debug purposes
com.strncpy( hInst->dllName, dllname, sizeof( hInst->dllName ));
// shortPath is used for LibraryLoadSymbols only
com.strncpy( hInst->shortPath, dllpath, sizeof( hInst->shortPath ));
2010-11-20 22:00:00 +01:00
hInst->encrypted = FS_CheckForCrypt( dllpath );
if( index < 0 && !hInst->encrypted )
2010-03-27 22:00:00 +01:00
{
com.snprintf( hInst->fullPath, sizeof( hInst->fullPath ), "%s%s", search->filename, dllpath );
hInst->custom_loader = false; // we can loading from disk and use normal debugging
}
else
{
com.strncpy( hInst->fullPath, dllpath, sizeof( hInst->fullPath ));
hInst->custom_loader = true; // loading from pack or wad - for release, debug don't working
}
fs_ext_path = false; // always reset direct paths
return hInst;
}
2007-06-21 22:00:00 +02:00
/*
==================
FS_FileSize
return size of file in bytes
==================
*/
2010-10-26 22:00:00 +02:00
fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly )
2007-06-21 22:00:00 +02:00
{
file_t *fp;
int length = 0;
2010-09-10 22:00:00 +02:00
fp = FS_Open( filename, "rb", gamedironly );
2007-06-21 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
if( fp )
2007-06-21 22:00:00 +02:00
{
// it exists
2010-03-27 22:00:00 +01:00
FS_Seek( fp, 0, SEEK_END );
length = FS_Tell( fp );
FS_Close( fp );
2007-06-21 22:00:00 +02:00
}
return length;
}
2010-10-28 22:00:00 +02:00
/*
==================
FS_FileLength
return size of file in bytes
==================
*/
fs_offset_t FS_FileLength( file_t *f )
{
if( !f ) return 0;
return f->real_length;
}
2008-06-07 22:00:00 +02:00
/*
==================
FS_FileTime
return time of creation file in seconds
==================
*/
2010-10-26 22:00:00 +02:00
fs_offset_t FS_FileTime( const char *filename, qboolean gamedironly )
2008-06-07 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
searchpath_t *search;
int pack_ind;
2008-06-07 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
search = FS_FindFile( filename, &pack_ind, gamedironly );
2010-03-27 22:00:00 +01:00
if( !search ) return -1; // doesn't exist
if( search->pack ) // grab pack filetime
return search->pack->filetime;
else if( search->wad ) // grab wad filetime
return search->wad->filetime;
else if( pack_ind < 0 )
{
// found in the filesystem?
char path [MAX_SYSPATH];
com.sprintf( path, "%s%s", search->filename, filename );
return FS_SysFileTime( path );
}
return -1; // doesn't exist
2008-06-07 22:00:00 +02:00
}
2007-06-21 22:00:00 +02:00
2010-03-27 22:00:00 +01:00
/*
==================
FS_Rename
rename specified file from gamefolder
==================
*/
2010-10-26 22:00:00 +02:00
qboolean FS_Rename( const char *oldname, const char *newname )
2008-09-10 22:00:00 +02:00
{
2010-03-27 22:00:00 +01:00
char oldpath[MAX_SYSPATH], newpath[MAX_SYSPATH];
2010-10-26 22:00:00 +02:00
qboolean iRet;
2010-03-27 22:00:00 +01:00
if( !oldname || !newname || !*oldname || !*newname )
return false;
2010-09-10 22:00:00 +02:00
com.snprintf( oldpath, sizeof( oldpath ), "%s/%s", fs_gamedir, oldname );
com.snprintf( newpath, sizeof( newpath ), "%s/%s", fs_gamedir, newname );
2010-03-27 22:00:00 +01:00
iRet = rename( oldpath, newpath );
return iRet;
2008-09-10 22:00:00 +02:00
}
2010-02-21 22:00:00 +01:00
/*
==================
2010-03-27 22:00:00 +01:00
FS_Delete
2010-02-21 22:00:00 +01:00
2010-03-27 22:00:00 +01:00
delete specified file from gamefolder
2010-02-21 22:00:00 +01:00
==================
*/
2010-10-26 22:00:00 +02:00
qboolean FS_Delete( const char *path )
2010-02-21 22:00:00 +01:00
{
2010-03-27 22:00:00 +01:00
char real_path[MAX_SYSPATH];
2010-10-26 22:00:00 +02:00
qboolean iRet;
2010-03-27 22:00:00 +01:00
if( !path || !*path )
return false;
2010-09-10 22:00:00 +02:00
com.snprintf( real_path, sizeof( real_path ), "%s/%s", fs_gamedir, path );
2010-03-27 22:00:00 +01:00
iRet = remove( real_path );
return iRet;
2010-02-21 22:00:00 +01:00
}
2007-06-21 22:00:00 +02:00
/*
===========
FS_Search
Allocate and fill a search structure with information on matching filenames.
===========
*/
2010-09-10 22:00:00 +02:00
search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly )
2007-06-21 22:00:00 +02:00
{
2008-07-23 22:00:00 +02:00
search_t *search = NULL;
searchpath_t *searchpath;
pack_t *pak;
2008-08-14 22:00:00 +02:00
wfile_t *wad;
2008-08-13 22:00:00 +02:00
int i, basepathlength, numfiles, numchars;
int resultlistindex, dirlistindex;
const char *slash, *backslash, *colon, *separator;
string netpath, temp;
2008-07-23 22:00:00 +02:00
stringlist_t resultlist;
stringlist_t dirlist;
char *basepath;
2007-06-21 22:00:00 +02:00
2008-06-14 22:00:00 +02:00
for( i = 0; pattern[i] == '.' || pattern[i] == ':' || pattern[i] == '/' || pattern[i] == '\\'; i++ );
2007-06-21 22:00:00 +02:00
2008-06-14 22:00:00 +02:00
if( i > 0 )
2007-06-21 22:00:00 +02:00
{
2008-06-14 22:00:00 +02:00
MsgDev( D_INFO, "FS_Search: don't use punctuation at the beginning of a search pattern!\n");
2007-06-21 22:00:00 +02:00
return NULL;
}
2010-04-12 22:00:00 +02:00
stringlistinit( &resultlist );
stringlistinit( &dirlist );
2009-01-22 22:00:00 +01:00
slash = com.strrchr( pattern, '/' );
backslash = com.strrchr( pattern, '\\' );
colon = com.strrchr( pattern, ':' );
separator = max( slash, backslash );
separator = max( separator, colon );
2007-06-21 22:00:00 +02:00
basepathlength = separator ? (separator + 1 - pattern) : 0;
2008-08-04 22:00:00 +02:00
basepath = Mem_Alloc( fs_mempool, basepathlength + 1 );
2009-01-22 22:00:00 +01:00
if( basepathlength ) Mem_Copy( basepath, pattern, basepathlength );
2007-06-21 22:00:00 +02:00
basepath[basepathlength] = 0;
// search through the path, one element at a time
2008-06-14 22:00:00 +02:00
for( searchpath = fs_searchpaths; searchpath; searchpath = searchpath->next )
2007-06-21 22:00:00 +02:00
{
2010-04-12 22:00:00 +02:00
if( gamedironly && !( searchpath->flags & FS_GAMEDIR_PATH ))
continue;
2007-06-21 22:00:00 +02:00
// is the element a pak file?
2008-08-14 22:00:00 +02:00
if( searchpath->pack )
2007-06-21 22:00:00 +02:00
{
// look through all the pak file elements
pak = searchpath->pack;
2008-08-14 22:00:00 +02:00
for( i = 0; i < pak->numfiles; i++ )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
com.strncpy( temp, pak->files[i].name, sizeof( temp ));
while( temp[0] )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
if( matchpattern( temp, (char *)pattern, true ))
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ )
{
if( !com.strcmp( resultlist.strings[resultlistindex], temp ))
2007-06-21 22:00:00 +02:00
break;
2009-01-22 22:00:00 +01:00
}
2010-09-10 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
if( resultlistindex == resultlist.numstrings )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
stringlistappend( &resultlist, temp );
2007-06-21 22:00:00 +02:00
}
}
// strip off one path element at a time until empty
// this way directories are added to the listing if they match the pattern
2009-01-22 22:00:00 +01:00
slash = com.strrchr( temp, '/' );
backslash = com.strrchr( temp, '\\' );
colon = com.strrchr( temp, ':' );
2007-06-21 22:00:00 +02:00
separator = temp;
2009-01-22 22:00:00 +01:00
if( separator < slash )
2007-08-13 22:00:00 +02:00
separator = slash;
2009-01-22 22:00:00 +01:00
if( separator < backslash )
2007-08-13 22:00:00 +02:00
separator = backslash;
2009-01-22 22:00:00 +01:00
if( separator < colon )
2007-08-13 22:00:00 +02:00
separator = colon;
2007-06-21 22:00:00 +02:00
*((char *)separator) = 0;
}
}
}
2008-08-14 22:00:00 +02:00
else if( searchpath->wad )
{
string wadpattern, wadname, temp2;
2009-01-22 22:00:00 +01:00
char type = W_TypeFromExt( pattern );
2010-10-26 22:00:00 +02:00
qboolean anywadname = true;
2008-08-14 22:00:00 +02:00
string wadfolder;
// quick reject by filetype
2010-09-10 22:00:00 +02:00
if( type == TYP_NONE ) continue;
2008-08-14 22:00:00 +02:00
FS_ExtractFilePath( pattern, wadname );
FS_FileBase( pattern, wadpattern );
wadfolder[0] = '\0';
2009-01-22 22:00:00 +01:00
if( com.strlen( wadname ))
2008-08-14 22:00:00 +02:00
{
FS_FileBase( wadname, wadname );
2009-01-22 22:00:00 +01:00
com.strncpy( wadfolder, wadname, sizeof( wadfolder ));
2008-08-14 22:00:00 +02:00
FS_DefaultExtension( wadname, ".wad" );
anywadname = false;
}
2010-10-26 22:00:00 +02:00
// make wadname from wad fullpath
FS_FileBase( searchpath->wad->filename, temp2 );
FS_DefaultExtension( temp2, ".wad" );
2008-08-14 22:00:00 +02:00
// quick reject by wadname
2010-10-26 22:00:00 +02:00
if( !anywadname && com.stricmp( wadname, temp2 ))
2008-08-14 22:00:00 +02:00
continue;
// look through all the wad file elements
wad = searchpath->wad;
for( i = 0; i < wad->numlumps; i++ )
{
// if type not matching, we already no chance ...
2010-09-10 22:00:00 +02:00
if( type != TYP_ANY && wad->lumps[i].type != type )
2008-08-14 22:00:00 +02:00
continue;
2010-11-19 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
com.strncpy( temp, wad->lumps[i].name, sizeof( temp ));
2008-08-14 22:00:00 +02:00
while( temp[0] )
{
if( matchpattern( temp, wadpattern, true ))
{
for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ )
2009-01-22 22:00:00 +01:00
{
if( !com.strcmp( resultlist.strings[resultlistindex], temp ))
2008-08-14 22:00:00 +02:00
break;
2009-01-22 22:00:00 +01:00
}
2010-11-19 22:00:00 +01:00
2008-08-14 22:00:00 +02:00
if( resultlistindex == resultlist.numstrings )
{
// build path: wadname/lumpname.ext
2009-01-22 22:00:00 +01:00
com.snprintf( temp2, sizeof(temp2), "%s/%s", wadfolder, temp );
2008-08-14 22:00:00 +02:00
FS_DefaultExtension( temp2, va(".%s", W_ExtFromType( wad->lumps[i].type ))); // make ext
stringlistappend( &resultlist, temp2 );
}
}
2010-11-19 22:00:00 +01:00
2008-08-14 22:00:00 +02:00
// strip off one path element at a time until empty
// this way directories are added to the listing if they match the pattern
2009-01-22 22:00:00 +01:00
slash = com.strrchr( temp, '/' );
backslash = com.strrchr( temp, '\\' );
colon = com.strrchr( temp, ':' );
2008-08-14 22:00:00 +02:00
separator = temp;
2009-01-22 22:00:00 +01:00
if( separator < slash )
2008-08-14 22:00:00 +02:00
separator = slash;
2009-01-22 22:00:00 +01:00
if( separator < backslash )
2008-08-14 22:00:00 +02:00
separator = backslash;
2009-01-22 22:00:00 +01:00
if( separator < colon )
2008-08-14 22:00:00 +02:00
separator = colon;
*((char *)separator) = 0;
}
}
}
2007-06-21 22:00:00 +02:00
else
{
// get a directory listing and look at each name
2009-01-22 22:00:00 +01:00
com.sprintf( netpath, "%s%s", searchpath->filename, basepath );
stringlistinit( &dirlist );
listdirectory( &dirlist, netpath );
for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
com.sprintf( temp, "%s%s", basepath, dirlist.strings[dirlistindex] );
if( matchpattern( temp, (char *)pattern, true ))
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ )
{
if( !com.strcmp( resultlist.strings[resultlistindex], temp ))
2007-08-13 22:00:00 +02:00
break;
2009-01-22 22:00:00 +01:00
}
2010-09-10 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
if( resultlistindex == resultlist.numstrings )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
stringlistappend( &resultlist, temp );
2007-06-21 22:00:00 +02:00
}
}
}
2009-01-22 22:00:00 +01:00
stringlistfreecontents( &dirlist );
2007-06-21 22:00:00 +02:00
}
}
2008-08-13 22:00:00 +02:00
if( resultlist.numstrings )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
stringlistsort( &resultlist );
2007-08-13 22:00:00 +02:00
numfiles = resultlist.numstrings;
2007-06-21 22:00:00 +02:00
numchars = 0;
2010-11-19 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ )
numchars += (int)com.strlen( resultlist.strings[resultlistindex]) + 1;
2010-11-19 22:00:00 +01:00
search = Mem_Alloc( fs_mempool, sizeof(search_t) + numchars + numfiles * sizeof( char* ));
search->filenames = (char **)((char *)search + sizeof( search_t ));
search->filenamesbuffer = (char *)((char *)search + sizeof( search_t ) + numfiles * sizeof( char* ));
2007-06-21 22:00:00 +02:00
search->numfilenames = (int)numfiles;
2010-11-19 22:00:00 +01:00
numfiles = numchars = 0;
2009-01-22 22:00:00 +01:00
for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
size_t textlen;
2007-06-21 22:00:00 +02:00
search->filenames[numfiles] = search->filenamesbuffer + numchars;
2009-01-22 22:00:00 +01:00
textlen = com.strlen(resultlist.strings[resultlistindex]) + 1;
Mem_Copy( search->filenames[numfiles], resultlist.strings[resultlistindex], textlen );
2007-06-21 22:00:00 +02:00
numfiles++;
numchars += (int)textlen;
}
}
2009-01-22 22:00:00 +01:00
stringlistfreecontents( &resultlist );
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
Mem_Free( basepath );
2007-06-21 22:00:00 +02:00
return search;
}
void FS_InitMemory( void )
{
2007-12-17 22:00:00 +01:00
fs_mempool = Mem_AllocPool( "Filesystem Pool" );
2007-06-21 22:00:00 +02:00
// add a path separator to the end of the basedir if it lacks one
2009-01-22 22:00:00 +01:00
if( fs_basedir[0] && fs_basedir[com.strlen(fs_basedir) - 1] != '/' && fs_basedir[com.strlen(fs_basedir) - 1] != '\\' )
com.strncat( fs_basedir, "/", sizeof( fs_basedir ));
2008-08-04 22:00:00 +02:00
fs_searchpaths = NULL;
2007-06-21 22:00:00 +02:00
}
/*
================
FS_CheckParm
Returns the position (1 to argc-1) in the program's argument list
where the given parameter apears, or 0 if not present
================
*/
2009-01-22 22:00:00 +01:00
int FS_CheckParm( const char *parm )
2007-06-21 22:00:00 +02:00
{
2009-01-22 22:00:00 +01:00
int i;
2007-06-21 22:00:00 +02:00
2009-01-22 22:00:00 +01:00
for( i = 1; i < fs_argc; i++ )
2007-06-21 22:00:00 +02:00
{
// NEXTSTEP sometimes clears appkit vars.
2009-01-22 22:00:00 +01:00
if( !fs_argv[i] ) continue;
if( !com.stricmp( parm, fs_argv[i] )) return i;
2007-06-21 22:00:00 +02:00
}
return 0;
}
2007-11-10 22:00:00 +01:00
void FS_GetBaseDir( char *pszBuffer, char *out )
{
2010-07-22 22:00:00 +02:00
char basedir[MAX_SYSPATH];
char szBuffer[MAX_SYSPATH];
2007-11-10 22:00:00 +01:00
char *pBuffer = NULL;
2010-07-22 22:00:00 +02:00
int j;
2007-11-10 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
com.strcpy( szBuffer, pszBuffer );
2007-11-10 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
pBuffer = com.strrchr( szBuffer,'\\' );
2010-07-22 22:00:00 +02:00
if ( pBuffer ) *(pBuffer + 1) = '\0';
2007-11-10 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
com.strcpy( basedir, szBuffer );
2007-11-10 22:00:00 +01:00
2009-01-22 22:00:00 +01:00
j = com.strlen( basedir );
if( j > 0 )
2007-11-10 22:00:00 +01:00
{
2010-07-22 22:00:00 +02:00
if(( basedir[j-1] == '\\' ) || ( basedir[j-1] == '/' ))
basedir[j-1] = 0;
2007-11-10 22:00:00 +01:00
}
2009-01-22 22:00:00 +01:00
com.strcpy( out, basedir );
2007-11-10 22:00:00 +01:00
}
void FS_ReadEnvironmentVariables( char *pPath )
{
// get basepath from registry
2009-01-22 22:00:00 +01:00
REG_GetValue( HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Session Manager\\Environment", "Xash3D", pPath );
2007-11-10 22:00:00 +01:00
}
void FS_SaveEnvironmentVariables( char *pPath )
{
// save new path
2009-01-22 22:00:00 +01:00
REG_SetValue( HKEY_LOCAL_MACHINE, "System\\CurrentControlSet\\Control\\Session Manager\\Environment", "Xash3D", pPath );
2010-07-22 22:00:00 +02:00
SendMessageTimeout( HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_NORMAL, 10, NULL ); // system update message
2007-11-10 22:00:00 +01:00
}
static void FS_BuildPath( char *pPath, char *pOut )
{
// set working directory
SetCurrentDirectory ( pPath );
2010-11-15 22:00:00 +01:00
com.sprintf( pOut, "%s\\launch.dll", pPath );
2007-11-10 22:00:00 +01:00
}
void FS_UpdateEnvironmentVariables( void )
{
2009-01-22 22:00:00 +01:00
char szTemp[4096];
char szPath[MAX_SYSPATH]; // test path
2010-07-22 22:00:00 +02:00
if( Sys.app_name == HOST_NORMAL || Sys.app_name == HOST_DEDICATED )
{
// just update environment path
FS_SaveEnvironmentVariables( sys_rootdir );
return;
}
2007-11-10 22:00:00 +01:00
// get variable from registry and current directory
FS_ReadEnvironmentVariables( szTemp );
// if both values is math - no run additional tests
2009-01-22 22:00:00 +01:00
if( com.stricmp( sys_rootdir, szTemp ))
2007-11-10 22:00:00 +01:00
{
// Step1: path from registry have higher priority than current working directory
// because user can execute launcher from random place or from a bat-file
// so, set current working directory as path from registry and test it
FS_BuildPath( szTemp, szPath );
2010-02-18 22:00:00 +01:00
if( !FS_SysFileExists( szPath )) // Step2: engine root dir has been moved to other place?
2007-11-10 22:00:00 +01:00
{
FS_BuildPath( sys_rootdir, szPath );
2010-02-18 22:00:00 +01:00
if( !FS_SysFileExists( szPath )) // Step3: directly execute from bin directory?
2007-11-10 22:00:00 +01:00
{
// Step4: create last test for bin directory
FS_GetBaseDir( sys_rootdir, szTemp );
FS_BuildPath( szTemp, szPath );
2010-02-18 22:00:00 +01:00
if( !FS_SysFileExists( szPath ))
2007-11-10 22:00:00 +01:00
{
2010-02-16 22:00:00 +01:00
// big bada-boom: engine was moved and launcher was running from other place
// step5: so, path form registry is invalid, current path is no valid
Sys_Break( "Invalid root directory!\n\rPlease re-install Xash3D\n" );
2007-11-10 22:00:00 +01:00
}
2010-02-16 22:00:00 +01:00
else FS_SaveEnvironmentVariables( szTemp ); // update registry
2007-11-10 22:00:00 +01:00
}
else FS_SaveEnvironmentVariables( sys_rootdir );
}
}
}
2007-06-21 22:00:00 +02:00
/*
=============================================================================
VIRTUAL FILE SYSTEM - WRITE DATA INTO MEMORY
=============================================================================
*/
2008-08-06 22:00:00 +02:00
vfile_t *VFS_Create( const byte *buffer, size_t buffsize)
2007-07-22 22:00:00 +02:00
{
2008-08-06 22:00:00 +02:00
vfile_t *file = (vfile_t *)Mem_Alloc( fs_mempool, sizeof (*file));
2007-07-22 22:00:00 +02:00
file->length = file->buffsize = buffsize;
file->buff = Mem_Alloc(fs_mempool, (file->buffsize));
file->offset = 0;
2007-11-18 22:00:00 +01:00
file->mode = O_RDONLY;
2008-08-06 22:00:00 +02:00
Mem_Copy( file->buff, buffer, buffsize );
2007-07-22 22:00:00 +02:00
return file;
}
2010-09-10 22:00:00 +02:00
vfile_t *VFS_Open( file_t *handle, const char *mode )
2007-06-21 22:00:00 +02:00
{
2007-11-18 22:00:00 +01:00
vfile_t *file = (vfile_t *)Mem_Alloc (fs_mempool, sizeof (vfile_t));
2007-08-28 22:00:00 +02:00
2007-07-21 22:00:00 +02:00
// If the file is opened in "write", "append", or "read/write" mode
2010-09-10 22:00:00 +02:00
if( mode[0] == 'w' )
2007-06-21 22:00:00 +02:00
{
2007-11-18 22:00:00 +01:00
file->handle = handle;
2007-07-21 22:00:00 +02:00
file->buffsize = (64 * 1024); // will be resized if need
2010-09-10 22:00:00 +02:00
file->buff = Mem_Alloc( fs_mempool, file->buffsize );
2007-07-21 22:00:00 +02:00
file->length = 0;
file->offset = 0;
2007-11-18 22:00:00 +01:00
file->mode = O_WRONLY;
2007-06-21 22:00:00 +02:00
}
2010-09-10 22:00:00 +02:00
else if( mode[0] == 'r' )
2007-07-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
int curpos, endpos;
2007-06-21 22:00:00 +02:00
2007-11-18 22:00:00 +01:00
file->handle = handle;
2010-09-10 22:00:00 +02:00
curpos = FS_Tell( file->handle );
FS_Seek( file->handle, 0, SEEK_END );
endpos = FS_Tell( file->handle );
FS_Seek( file->handle, curpos, SEEK_SET );
2007-07-21 22:00:00 +02:00
file->buffsize = endpos - curpos;
2010-09-10 22:00:00 +02:00
file->buff = Mem_Alloc( fs_mempool, file->buffsize );
2007-07-21 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
FS_Read( file->handle, file->buff, file->buffsize );
2007-07-21 22:00:00 +02:00
file->length = file->buffsize;
file->offset = 0;
2007-11-18 22:00:00 +01:00
file->mode = O_RDONLY;
2007-07-21 22:00:00 +02:00
}
else
{
2007-11-10 22:00:00 +01:00
Mem_Free( file );
2008-06-29 22:00:00 +02:00
MsgDev( D_ERROR, "VFS_Open: unsupported mode %s\n", mode );
2007-07-21 22:00:00 +02:00
return NULL;
}
return file;
2007-06-21 22:00:00 +02:00
}
2007-07-21 22:00:00 +02:00
fs_offset_t VFS_Read( vfile_t* file, void* buffer, size_t buffersize)
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
fs_offset_t read_size = 0;
2008-01-05 22:00:00 +01:00
2010-09-10 22:00:00 +02:00
if( buffersize == 0 ) return 1;
if( !file ) return 0;
2007-06-21 22:00:00 +02:00
2007-12-13 22:00:00 +01:00
// check for enough room
2010-09-10 22:00:00 +02:00
if( file->offset >= file->length )
2007-07-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
return 0; // hit EOF
2007-07-21 22:00:00 +02:00
}
2010-09-10 22:00:00 +02:00
if( file->offset + buffersize <= file->length )
2007-07-21 22:00:00 +02:00
{
Mem_Copy( buffer, file->buff + file->offset, buffersize );
file->offset += buffersize;
2008-01-05 22:00:00 +01:00
read_size = buffersize;
2007-07-21 22:00:00 +02:00
}
else
{
int reduced_size = file->length - file->offset;
Mem_Copy( buffer, file->buff + file->offset, reduced_size );
file->offset += reduced_size;
2008-01-05 22:00:00 +01:00
read_size = reduced_size;
2008-06-29 22:00:00 +02:00
MsgDev( D_NOTE, "VFS_Read: vfs buffer is out\n");
2007-07-21 22:00:00 +02:00
}
2010-09-10 22:00:00 +02:00
2008-01-05 22:00:00 +01:00
return read_size;
2007-06-21 22:00:00 +02:00
}
2007-07-21 22:00:00 +02:00
fs_offset_t VFS_Write( vfile_t *file, const void *buf, size_t size )
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( !file ) return -1;
2007-07-21 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
if( file->offset + size >= file->buffsize )
2007-07-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
int newsize = file->offset + size + (64 * 1024);
2007-07-21 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
if( file->buffsize < newsize )
2007-07-22 22:00:00 +02:00
{
2008-06-09 22:00:00 +02:00
// reallocate buffer now
2010-11-20 22:00:00 +01:00
file->buff = Mem_Realloc( fs_mempool, file->buff, newsize );
2008-06-09 22:00:00 +02:00
file->buffsize = newsize; // merge buffsize
2007-07-22 22:00:00 +02:00
}
2007-07-21 22:00:00 +02:00
}
// write into buffer
2008-07-17 22:00:00 +02:00
Mem_Copy( file->buff + file->offset, (byte *)buf, size );
2007-07-21 22:00:00 +02:00
file->offset += size;
2008-07-17 22:00:00 +02:00
if( file->offset > file->length )
2007-07-21 22:00:00 +02:00
file->length = file->offset;
2007-06-21 22:00:00 +02:00
2007-07-21 22:00:00 +02:00
return file->length;
2007-06-21 22:00:00 +02:00
}
2008-08-15 22:00:00 +02:00
byte *VFS_GetBuffer( vfile_t *file )
{
if( !file ) return NULL;
return file->buff;
}
2007-11-18 22:00:00 +01:00
/*
====================
2008-06-09 22:00:00 +02:00
VFS_Print
2007-11-18 22:00:00 +01:00
Print a string into a file
====================
*/
2009-01-22 22:00:00 +01:00
int VFS_Print( vfile_t* file, const char *msg )
2007-11-18 22:00:00 +01:00
{
2009-01-22 22:00:00 +01:00
return (int)VFS_Write( file, msg, com.strlen( msg ));
2007-11-18 22:00:00 +01:00
}
/*
====================
VFS_VPrintf
Print a string into a buffer
====================
*/
2010-09-10 22:00:00 +02:00
int VFS_VPrintf( vfile_t* file, const char* format, va_list ap )
2007-11-18 22:00:00 +01:00
{
int len;
2010-07-26 22:00:00 +02:00
fs_offset_t buff_size = MAX_SYSPATH;
2007-11-18 22:00:00 +01:00
char *tempbuff;
2008-08-04 22:00:00 +02:00
while( 1 )
2007-11-18 22:00:00 +01:00
{
2008-08-04 22:00:00 +02:00
tempbuff = (char *)Mem_Alloc( fs_mempool, buff_size );
2009-01-22 22:00:00 +01:00
len = com.vsprintf( tempbuff, format, ap );
2008-08-04 22:00:00 +02:00
if( len >= 0 && len < buff_size ) break;
Mem_Free( tempbuff );
2007-11-18 22:00:00 +01:00
buff_size *= 2;
}
2009-01-22 22:00:00 +01:00
len = VFS_Write( file, tempbuff, len );
2007-11-18 22:00:00 +01:00
Mem_Free( tempbuff );
return len;
}
/*
====================
VFS_Printf
Print a string into a buffer
====================
*/
2010-09-10 22:00:00 +02:00
int VFS_Printf( vfile_t* file, const char* format, ... )
2007-11-18 22:00:00 +01:00
{
2010-09-10 22:00:00 +02:00
int result;
va_list args;
2007-11-18 22:00:00 +01:00
2010-09-10 22:00:00 +02:00
va_start( args, format );
result = VFS_VPrintf( file, format, args );
va_end( args );
2007-11-18 22:00:00 +01:00
return result;
}
2010-09-10 22:00:00 +02:00
fs_offset_t VFS_Tell( vfile_t *file )
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( !file ) return -1;
2007-07-21 22:00:00 +02:00
return file->offset;
}
2010-10-26 22:00:00 +02:00
qboolean VFS_Eof( vfile_t *file )
2008-01-05 22:00:00 +01:00
{
2010-09-10 22:00:00 +02:00
if( !file ) return true;
2008-01-05 22:00:00 +01:00
return (file->offset == file->length) ? true : false;
}
2007-11-21 22:00:00 +01:00
/*
====================
2009-07-12 22:00:00 +02:00
VFS_Getc
2007-11-21 22:00:00 +01:00
Get the next character of a file
====================
*/
2009-07-12 22:00:00 +02:00
int VFS_Getc( vfile_t *file )
2007-11-21 22:00:00 +01:00
{
char c;
2009-07-12 22:00:00 +02:00
if(!VFS_Read( file, &c, 1 ))
2007-11-21 22:00:00 +01:00
return EOF;
return c;
}
2009-07-12 22:00:00 +02:00
int VFS_Gets( vfile_t* file, byte *string, size_t bufsize )
2007-11-21 22:00:00 +01:00
{
int c, end = 0;
while( 1 )
{
c = VFS_Getc( file );
2010-09-10 22:00:00 +02:00
if( c == '\r' || c == '\n' || c < 0 )
2007-11-21 22:00:00 +01:00
break;
2010-09-10 22:00:00 +02:00
if( end < bufsize - 1 )
string[end++] = c;
2007-11-21 22:00:00 +01:00
}
string[end] = 0;
// remove \n following \r
2010-09-10 22:00:00 +02:00
if( c == '\r' )
2007-11-21 22:00:00 +01:00
{
c = VFS_Getc( file );
2010-09-10 22:00:00 +02:00
if( c != '\n' ) VFS_Seek( file, -1, SEEK_CUR ); // rewind
2007-11-21 22:00:00 +01:00
}
return c;
}
2007-07-21 22:00:00 +02:00
int VFS_Seek( vfile_t *file, fs_offset_t offset, int whence )
{
2010-09-10 22:00:00 +02:00
if( !file ) return -1;
2007-08-28 22:00:00 +02:00
2007-07-21 22:00:00 +02:00
// Compute the file offset
2010-09-10 22:00:00 +02:00
switch( whence )
2007-06-21 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
case SEEK_CUR:
offset += file->offset;
break;
case SEEK_SET:
break;
case SEEK_END:
offset += file->length;
break;
default:
return -1;
2007-06-21 22:00:00 +02:00
}
2007-07-21 22:00:00 +02:00
2010-11-20 22:00:00 +01:00
if( offset < 0 ) return -1;
if( offset > (long)file->length )
{
if( file->mode == O_WRONLY )
{
int newsize = offset + (64 * 1024);
if( file->buffsize < newsize )
{
// reallocate buffer now
file->buff = Mem_Realloc( fs_mempool, file->buff, newsize );
file->buffsize = newsize; // merge buffsize
}
}
else
{
return -1;
}
}
2007-07-21 22:00:00 +02:00
file->offset = offset;
return 0;
2007-06-21 22:00:00 +02:00
}
2007-11-18 22:00:00 +01:00
file_t *VFS_Close( vfile_t *file )
2007-06-21 22:00:00 +02:00
{
2007-11-20 22:00:00 +01:00
file_t *handle;
2007-11-18 22:00:00 +01:00
2010-09-10 22:00:00 +02:00
if( !file ) return NULL;
2007-07-22 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
if( file->mode == O_WRONLY )
2007-07-22 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( file->handle )
2008-10-19 22:00:00 +02:00
FS_Write(file->handle, file->buff, (file->length + 3) & ~3); // align
2007-07-22 22:00:00 +02:00
}
2010-09-10 22:00:00 +02:00
2007-11-18 22:00:00 +01:00
handle = file->handle; // keep real handle
2007-07-21 22:00:00 +02:00
2009-07-12 22:00:00 +02:00
if( file->buff ) Mem_Free( file->buff );
2007-11-18 22:00:00 +01:00
Mem_Free( file ); // himself
2007-07-21 22:00:00 +02:00
2007-11-18 22:00:00 +01:00
return handle;
2007-07-21 22:00:00 +02:00
}
2008-08-14 22:00:00 +02:00
/*
=============================================================================
WADSYSTEM PRIVATE COMMON FUNCTIONS
=============================================================================
*/
2010-07-13 22:00:00 +02:00
// associate extension with wad type
static const wadtype_t wad_types[] =
{
2010-09-10 22:00:00 +02:00
{ "flp", TYP_FLMP }, // doom1 menu picture
{ "snd", TYP_SND }, // doom1 sound
{ "mus", TYP_MUS }, // doom1 .mus format
{ "skn", TYP_SKIN }, // doom1 sprite model
{ "flt", TYP_FLAT }, // doom1 wall texture
{ "pal", TYP_QPAL }, // palette
{ "lmp", TYP_QPIC }, // quake1, hl pic
{ "fnt", TYP_QFONT }, // hl qfonts
{ "mip", TYP_MIPTEX }, // hl/q1 mip
{ "raw", TYP_RAW }, // signed raw data
{ NULL, TYP_NONE }
2008-08-14 22:00:00 +02:00
};
static char W_TypeFromExt( const char *lumpname )
{
const char *ext = FS_FileExtension( lumpname );
2010-07-13 22:00:00 +02:00
const wadtype_t *type;
2008-08-14 22:00:00 +02:00
2010-07-13 22:00:00 +02:00
// we not known about filetype, so match only by filename
2009-01-22 22:00:00 +01:00
if( !com.strcmp( ext, "*" ) || !com.strcmp( ext, "" ))
2010-09-10 22:00:00 +02:00
return TYP_ANY;
2008-08-14 22:00:00 +02:00
for( type = wad_types; type->ext; type++ )
{
2009-01-22 22:00:00 +01:00
if(!com.stricmp( ext, type->ext ))
2008-08-14 22:00:00 +02:00
return type->type;
}
2010-09-10 22:00:00 +02:00
return TYP_NONE;
2008-08-14 22:00:00 +02:00
}
static const char *W_ExtFromType( char lumptype )
{
2010-07-13 22:00:00 +02:00
const wadtype_t *type;
2008-08-14 22:00:00 +02:00
2008-10-19 22:00:00 +02:00
// we not known aboyt filetype, so match only by filename
2010-09-10 22:00:00 +02:00
if( lumptype == TYP_NONE || lumptype == TYP_ANY )
2008-08-14 22:00:00 +02:00
return "";
for( type = wad_types; type->ext; type++ )
{
if( lumptype == type->type )
return type->ext;
}
return "";
}
static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype )
{
2010-07-23 22:00:00 +02:00
int left, right, middle;
2008-10-25 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
if( !wad || !wad->lumps || matchtype == TYP_NONE )
2008-08-14 22:00:00 +02:00
return NULL;
2010-07-23 22:00:00 +02:00
// look for the file (binary search)
left = 0;
right = wad->numlumps - 1;
while( left <= right )
2008-08-14 22:00:00 +02:00
{
2010-07-23 22:00:00 +02:00
int diff;
middle = (left + right) / 2;
diff = com.stricmp( wad->lumps[middle].name, name );
// Found it
if( !diff )
2008-08-14 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
if( matchtype == TYP_ANY || matchtype == wad->lumps[middle].type )
2010-07-23 22:00:00 +02:00
return &wad->lumps[middle]; // found
else break;
2008-08-14 22:00:00 +02:00
}
2010-07-23 22:00:00 +02:00
// if we're too far in the list
if( diff > 0 )
right = middle - 1;
else left = middle + 1;
2008-08-14 22:00:00 +02:00
}
return NULL;
}
static void W_CleanupName( const char *dirtyname, char *cleanname )
{
string tempname;
if( !dirtyname || !cleanname ) return;
cleanname[0] = '\0'; // clear output
FS_FileBase( dirtyname, tempname );
tempname[16] = '\0'; // cutoff all other ...
2009-01-22 22:00:00 +01:00
com.strncpy( cleanname, tempname, 16 );
2008-08-14 22:00:00 +02:00
// .. and turn big letters
com.strupr( cleanname, cleanname );
}
2010-07-23 22:00:00 +02:00
/*
====================
FS_AddFileToWad
Add a file to the list of files contained into a package
====================
*/
static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, int filepos, int packsize, int realsize, char type, char compression )
{
int left, right, middle;
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 diff;
middle = ( left + right ) / 2;
diff = com.stricmp( wad->lumps[middle].name, name );
// If we found the file, there's a problem
if( !diff ) MsgDev( D_NOTE, "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++;
Mem_Copy( plump->name, name, sizeof( plump->name ));
plump->filepos = filepos;
plump->disksize = realsize;
plump->size = packsize;
plump->compression = compression;
// convert all qmip types to miptex
2010-09-10 22:00:00 +02:00
if( type == TYP_QMIP )
plump->type = TYP_MIPTEX;
2010-07-23 22:00:00 +02:00
else plump->type = type;
// check for Quake 'conchars' issues (only lmp loader supposed to read this lame pic)
2010-09-10 22:00:00 +02:00
if( !com.stricmp( plump->name, "conchars" ) && plump->type == TYP_QMIP )
plump->type = TYP_QPIC;
2010-07-23 22:00:00 +02:00
return plump;
}
2010-10-26 22:00:00 +02:00
static qboolean W_ConvertIWADLumps( wfile_t *wad )
2008-08-14 22:00:00 +02:00
{
dlumpfile_t *doomlumps;
2010-10-26 22:00:00 +02:00
qboolean flat_images = false; // doom1 wall texture marker
qboolean skin_images = false; // doom1 skin image ( sprite model ) marker
qboolean flmp_images = false; // doom1 menu image marker
2008-08-14 22:00:00 +02:00
size_t lat_size; // LAT - LumpAllocationTable
2010-07-23 22:00:00 +02:00
int i, k, numlumps;
2008-08-14 22:00:00 +02:00
// nothing to convert ?
if( !wad ) return false;
lat_size = wad->numlumps * sizeof( dlumpfile_t );
2008-08-15 22:00:00 +02:00
doomlumps = (dlumpfile_t *)Mem_Alloc( wad->mempool, lat_size );
2010-07-23 22:00:00 +02:00
numlumps = wad->numlumps;
wad->numlumps = 0; // reset it
2008-08-14 22:00:00 +02:00
2010-10-26 22:00:00 +02:00
if( read( wad->handle, doomlumps, lat_size ) != lat_size )
2008-08-14 22:00:00 +02:00
{
MsgDev( D_ERROR, "W_ConvertIWADLumps: %s has corrupted lump allocation table\n", wad->filename );
Mem_Free( doomlumps );
W_Close( wad );
return false;
}
// convert doom1 format into WAD3 lump format
2010-07-23 22:00:00 +02:00
for( i = 0; i < numlumps; i++ )
2008-08-14 22:00:00 +02:00
{
// W_Open will be swap lump later
2010-07-23 22:00:00 +02:00
int filepos = doomlumps[i].filepos;
int size = doomlumps[i].size;
2010-09-10 22:00:00 +02:00
char type = TYP_NONE;
2010-07-23 22:00:00 +02:00
char name[16];
com.strnlwr( doomlumps[i].name, name, 9 );
2008-08-14 22:00:00 +02:00
// check for backslash issues
2010-09-10 22:00:00 +02:00
k = com.strlen( com.strchr( name, '\\' ));
2010-07-23 22:00:00 +02:00
if( k ) name[com.strlen( name )-k] = '#'; // vile1.spr issues
2008-08-14 22:00:00 +02:00
// textures begin
2010-07-23 22:00:00 +02:00
if( !com.stricmp( "S_START", name ))
2008-08-14 22:00:00 +02:00
{
skin_images = true;
continue; // skip identifier
}
2010-07-23 22:00:00 +02:00
else if( !com.stricmp( "P_START", name ))
2008-08-14 22:00:00 +02:00
{
flat_images = true;
continue; // skip identifier
}
2010-07-23 22:00:00 +02:00
else if( !com.stricmp( "P1_START", name ))
2008-08-14 22:00:00 +02:00
{
flat_images = true;
continue; // skip identifier
}
2010-07-23 22:00:00 +02:00
else if( !com.stricmp( "P2_START", name ))
2008-08-14 22:00:00 +02:00
{
flat_images = true;
continue; // skip identifier
}
2010-07-23 22:00:00 +02:00
else if( !com.stricmp( "P3_START", name ))
2008-08-14 22:00:00 +02:00
{
// only doom2 uses this name
flat_images = true;
continue; // skip identifier
}
2010-07-23 22:00:00 +02:00
else if( !com.strnicmp( "WI", name, 2 )) flmp_images = true;
else if( !com.strnicmp( "ST", name, 2 )) flmp_images = true;
else if( !com.strnicmp( "M_", name, 2 )) flmp_images = true;
else if( !com.strnicmp( "END", name, 3 )) flmp_images = true;
else if( !com.strnicmp( "HELP", name, 4 )) flmp_images = true;
else if( !com.strnicmp( "CREDIT", name, 6 )) flmp_images = true;
else if( !com.strnicmp( "TITLEPIC", name, 8 )) flmp_images = true;
else if( !com.strnicmp( "VICTORY", name, 7 )) flmp_images = true;
else if( !com.strnicmp( "PFUB", name, 4 )) flmp_images = true;
else if( !com.stricmp( "P_END", name )) flat_images = false;
else if( !com.stricmp( "P1_END", name )) flat_images = false;
else if( !com.stricmp( "P2_END", name )) flat_images = false;
else if( !com.stricmp("P3_END", name )) flat_images = false;
else if( !com.stricmp( "S_END", name )) skin_images = false;
2008-08-14 22:00:00 +02:00
else flmp_images = false;
// setup lumptypes for doomwads
2010-09-10 22:00:00 +02:00
if( flmp_images ) type = TYP_FLMP; // mark as menu pic
if( flat_images ) type = TYP_FLAT; // mark as texture
if( skin_images ) type = TYP_SKIN; // mark as skin (sprite model)
if(!com.strnicmp( name, "D_", 2 )) wad->lumps[i].type = TYP_MUS;
if(!com.strnicmp( name, "DS", 2 )) wad->lumps[i].type = TYP_SND;
2008-10-27 22:00:00 +01:00
// remove invalid resources
2010-09-10 22:00:00 +02:00
if( !com.strnicmp( name, "ENDOOM", 6 )) type = TYP_NONE;
if( !com.strnicmp( name, "STEP1", 5 )) type = TYP_NONE;
if( !com.strnicmp( name, "STEP2", 5 )) type = TYP_NONE;
2010-07-23 22:00:00 +02:00
2010-09-10 22:00:00 +02:00
if( type == TYP_NONE ) continue; // invalid resource
2010-07-23 22:00:00 +02:00
// add wad file
W_AddFileToWad( name, wad, filepos, size, size, type, CMP_NONE );
2008-08-14 22:00:00 +02:00
}
Mem_Free( doomlumps ); // no need anymore
return true;
}
2010-10-26 22:00:00 +02:00
static qboolean W_ReadLumpTable( wfile_t *wad )
2008-08-14 22:00:00 +02:00
{
2010-07-23 22:00:00 +02:00
size_t lat_size;
dlumpinfo_t *srclumps;
int i, k, numlumps;
2008-08-14 22:00:00 +02:00
// nothing to convert ?
2010-11-20 22:00:00 +01:00
if( !wad || !wad->numlumps )
return false;
2008-08-14 22:00:00 +02:00
lat_size = wad->numlumps * sizeof( dlumpinfo_t );
2010-07-23 22:00:00 +02:00
srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size );
numlumps = wad->numlumps;
wad->numlumps = 0; // reset it
2010-10-26 22:00:00 +02:00
if( read( wad->handle, srclumps, lat_size ) != lat_size )
2008-08-14 22:00:00 +02:00
{
MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
W_Close( wad );
return false;
}
// swap everything
2010-07-23 22:00:00 +02:00
for( i = 0; i < numlumps; i++ )
2008-08-14 22:00:00 +02:00
{
2010-07-23 22:00:00 +02:00
char name[16];
2008-08-14 22:00:00 +02:00
// cleanup lumpname
2010-07-23 22:00:00 +02:00
com.strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name ));
2008-08-14 22:00:00 +02:00
// check for '*' symbol issues
2010-07-23 22:00:00 +02:00
k = com.strlen( com.strrchr( name, '*' ));
if( k ) name[com.strlen( name ) - k] = '!'; // quake1 issues (can't save images that contain '*' symbol)
2008-11-14 22:00:00 +01:00
2010-11-21 22:00:00 +01:00
W_AddFileToWad( name, wad, srclumps[i].filepos, srclumps[i].size, srclumps[i].disksize, srclumps[i].type, srclumps[i].compression );
2008-08-14 22:00:00 +02:00
}
2010-07-23 22:00:00 +02:00
// release source lumps
Mem_Free( srclumps );
2008-08-14 22:00:00 +02:00
return true;
}
byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, size_t *lumpsizeptr )
{
byte *buf, *cbuf;
size_t size = 0;
2008-11-22 22:00:00 +01:00
// assume error
if( lumpsizeptr ) *lumpsizeptr = 0;
2008-08-14 22:00:00 +02:00
// no wads loaded
if( !wad || !lump ) return NULL;
2010-10-26 22:00:00 +02:00
if( lseek( wad->handle, lump->filepos, SEEK_SET ) == -1 )
2008-08-14 22:00:00 +02:00
{
MsgDev( D_ERROR, "W_ReadLump: %s is corrupted\n", lump->name );
2008-11-22 22:00:00 +01:00
return NULL;
2008-08-14 22:00:00 +02:00
}
switch( lump->compression )
{
2010-09-10 22:00:00 +02:00
case CMP_LZSS:
cbuf = (byte *)Mem_Alloc( wad->mempool, lump->disksize );
2010-10-26 22:00:00 +02:00
size = read( wad->handle, cbuf, lump->disksize );
2010-09-10 22:00:00 +02:00
buf = (byte *)Mem_Alloc( wad->mempool, lump->size );
2008-08-14 22:00:00 +02:00
if( size < lump->disksize )
{
MsgDev( D_WARN, "W_ReadLump: %s is probably corrupted\n", lump->name );
2010-09-10 22:00:00 +02:00
Mem_Free( cbuf );
2008-08-14 22:00:00 +02:00
Mem_Free( buf );
return NULL;
}
2010-09-10 22:00:00 +02:00
if( lzss_decompress( cbuf, cbuf + lump->disksize, buf, buf + lump->size ))
2008-08-14 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
MsgDev( D_WARN, "W_ReadLump: can't unpack %s\n", lump->name );
2008-08-14 22:00:00 +02:00
Mem_Free( cbuf );
Mem_Free( buf );
return NULL;
}
2010-09-10 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
Mem_Free( cbuf ); // no reason to keep this data
break;
2010-09-10 22:00:00 +02:00
default:
buf = (byte *)Mem_Alloc( wad->mempool, lump->disksize );
2010-10-26 22:00:00 +02:00
size = read( wad->handle, buf, lump->disksize );
2010-09-10 22:00:00 +02:00
if( size < lump->disksize )
{
MsgDev( D_WARN, "W_ReadLump: %s is probably corrupted\n", lump->name );
Mem_Free( buf );
return NULL;
}
break;
2008-08-14 22:00:00 +02:00
}
if( lumpsizeptr ) *lumpsizeptr = lump->size;
return buf;
}
2010-10-26 22:00:00 +02:00
qboolean W_WriteLump( wfile_t *wad, dlumpinfo_t *lump, const void* data, size_t datasize, char cmp )
2008-08-14 22:00:00 +02:00
{
2010-09-10 22:00:00 +02:00
byte *inbuf, *outbuf;
2008-08-14 22:00:00 +02:00
2008-08-15 22:00:00 +02:00
if( !wad || !lump ) return false;
2010-09-10 22:00:00 +02:00
2008-08-15 22:00:00 +02:00
if( !data || !datasize )
{
MsgDev( D_WARN, "W_WriteLump: ignore blank lump %s - nothing to save\n", lump->name );
2008-08-14 22:00:00 +02:00
return false;
2008-08-15 22:00:00 +02:00
}
2010-09-10 22:00:00 +02:00
switch( cmp )
2008-08-14 22:00:00 +02:00
{
case CMP_LZSS:
2010-09-10 22:00:00 +02:00
inbuf = (byte *)data;
// NOTE: abort compression if it matches or exceeds the original file's size
outbuf = Mem_Alloc( fs_mempool, datasize - 1 );
lump->disksize = lzss_compress( inbuf, inbuf + datasize, outbuf, outbuf + datasize - 1 );
2008-08-14 22:00:00 +02:00
lump->size = datasize; // realsize
2010-10-26 22:00:00 +02:00
lump->compression = CMP_LZSS;
2010-09-10 22:00:00 +02:00
if( lump->disksize > 0 )
{
2010-10-26 22:00:00 +02:00
write( wad->handle, outbuf, lump->disksize ); // write compressed file
2010-09-10 22:00:00 +02:00
}
Mem_Free( outbuf );
return ( lump->disksize != 0 ) ? true : false;
2008-08-14 22:00:00 +02:00
default: // CMP_NONE method
2010-11-21 22:00:00 +01:00
lump->size = lump->disksize = datasize;
2010-10-26 22:00:00 +02:00
lump->compression = CMP_NONE;
write( wad->handle, data, datasize ); // just write file
2008-08-14 22:00:00 +02:00
return true;
}
}
/*
=============================================================================
WADSYSTEM PUBLIC BASE FUNCTIONS
=============================================================================
*/
2008-08-15 22:00:00 +02:00
int W_Check( const char *filename )
{
file_t *testwad;
dwadinfo_t header;
2010-09-10 22:00:00 +02:00
testwad = FS_Open( filename, "rb", false );
2008-08-15 22:00:00 +02:00
if( !testwad ) return 0; // just not exist
2010-10-26 22:00:00 +02:00
if( FS_Read( testwad, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t ))
2008-08-15 22:00:00 +02:00
{
// corrupted or not wad
FS_Close( testwad );
return -1; // too small file
}
switch( header.ident )
{
case IDIWADHEADER:
case IDPWADHEADER:
case IDWAD2HEADER:
case IDWAD3HEADER: break;
default:
FS_Close( testwad );
return -2; // invalid id
}
2010-11-21 22:00:00 +01:00
if( header.numlumps < 0 || header.numlumps > MAX_FILES_IN_WAD )
2008-08-15 22:00:00 +02:00
{
// invalid lump number
FS_Close( testwad );
return -3; // invalid lumpcount
}
2010-11-21 22:00:00 +01:00
if( FS_Seek( testwad, header.infotableofs, SEEK_SET ))
2008-08-15 22:00:00 +02:00
{
// corrupted or not wad
FS_Close( testwad );
return -4; // invalid lumptable
}
// all check is done
FS_Close( testwad );
return 1; // valid
}
2008-08-14 22:00:00 +02:00
wfile_t *W_Open( const char *filename, const char *mode )
{
dwadinfo_t header;
wfile_t *wad = (wfile_t *)Mem_Alloc( fs_mempool, sizeof( wfile_t ));
2010-10-26 22:00:00 +02:00
const char *comment = "Generated by Xash WadLib. ";
2008-08-14 22:00:00 +02:00
2010-10-26 22:00:00 +02:00
if( mode[0] == 'a' ) wad->handle = open( filename, O_RDWR|O_BINARY, 0x666 );
else if( mode[0] == 'w' ) wad->handle = open( filename, O_CREAT|O_TRUNC|O_WRONLY|O_BINARY, 0x666 );
else if( mode[0] == 'r' ) wad->handle = open( filename, O_RDONLY|O_BINARY, 0x666 );
2008-11-14 22:00:00 +01:00
2010-10-26 22:00:00 +02:00
if( wad->handle < 0 )
2008-08-14 22:00:00 +02:00
{
W_Close( wad );
2008-11-22 22:00:00 +01:00
MsgDev( D_ERROR, "W_Open: couldn't open %s\n", filename );
2008-08-14 22:00:00 +02:00
return NULL;
}
2008-08-15 22:00:00 +02:00
// copy wad name
2009-01-22 22:00:00 +01:00
com.strncpy( wad->filename, filename, sizeof( wad->filename ));
2008-08-15 22:00:00 +02:00
wad->mempool = Mem_AllocPool( filename );
2010-10-26 22:00:00 +02:00
wad->filetime = FS_SysFileTime( filename );
2008-08-15 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
// if the file is opened in "write", "append", or "read/write" mode
if( mode[0] == 'w' )
{
dwadinfo_t hdr;
wad->numlumps = 0; // blank wad
wad->lumps = NULL; //
wad->mode = O_WRONLY;
// save space for header
hdr.ident = IDWAD3HEADER;
2010-11-21 22:00:00 +01:00
hdr.numlumps = wad->numlumps;
hdr.infotableofs = sizeof( dwadinfo_t );
2010-10-26 22:00:00 +02:00
write( wad->handle, &hdr, sizeof( hdr ));
write( wad->handle, comment, com.strlen( comment ) + 1 );
wad->infotableofs = tell( wad->handle );
2008-08-14 22:00:00 +02:00
}
else if( mode[0] == 'r' || mode[0] == 'a' )
{
if( mode[0] == 'a' )
{
2010-10-26 22:00:00 +02:00
lseek( wad->handle, 0, SEEK_SET );
2008-08-14 22:00:00 +02:00
wad->mode = O_APPEND;
}
2010-10-26 22:00:00 +02:00
if( read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t ))
2008-08-14 22:00:00 +02:00
{
MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename );
W_Close( wad );
return NULL;
}
switch( header.ident )
{
case IDIWADHEADER:
case IDPWADHEADER:
case IDWAD2HEADER:
if( wad->mode == O_APPEND )
{
MsgDev( D_WARN, "W_Open: %s is readonly\n", filename, mode );
wad->mode = O_RDONLY; // set read-only mode
}
break;
2010-06-16 22:00:00 +02:00
case IDWAD3HEADER:
break; // WAD3 allow r\w mode
2008-08-14 22:00:00 +02:00
default:
MsgDev( D_ERROR, "W_Open: %s unknown wadtype\n", filename );
W_Close( wad );
return NULL;
}
2010-11-21 22:00:00 +01:00
wad->numlumps = header.numlumps;
2008-08-14 22:00:00 +02:00
if( wad->numlumps >= MAX_FILES_IN_WAD && wad->mode == O_APPEND )
{
MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", wad->numlumps );
wad->mode = O_RDONLY; // set read-only mode
}
2010-11-21 22:00:00 +01:00
wad->infotableofs = header.infotableofs; // save infotableofs position
2010-10-26 22:00:00 +02:00
if( lseek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 )
2008-08-14 22:00:00 +02:00
{
MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename );
W_Close( wad );
return NULL;
}
2010-03-27 22:00:00 +01:00
2008-08-14 22:00:00 +02:00
// NOTE: lumps table can be reallocated for O_APPEND mode
2008-08-15 22:00:00 +02:00
wad->lumps = Mem_Alloc( wad->mempool, wad->numlumps * sizeof( dlumpinfo_t ));
2008-08-14 22:00:00 +02:00
2008-11-14 22:00:00 +01:00
if( wad->mode == O_APPEND )
{
size_t lat_size = wad->numlumps * sizeof( dlumpinfo_t );
2010-10-26 22:00:00 +02:00
if( read( wad->handle, wad->lumps, lat_size ) != lat_size )
2008-11-14 22:00:00 +01:00
{
MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename );
W_Close( wad );
2008-08-14 22:00:00 +02:00
return NULL;
2008-11-14 22:00:00 +01:00
}
2008-08-14 22:00:00 +02:00
2008-11-14 22:00:00 +01:00
// if we are in append mode - we need started from infotableofs poisition
// overwrite lumptable as well, we have her copy in wad->lumps
2010-10-26 22:00:00 +02:00
lseek( wad->handle, wad->infotableofs, SEEK_SET );
2008-11-14 22:00:00 +01:00
}
else
{
// setup lump allocation table
switch( header.ident )
{
case IDIWADHEADER:
case IDPWADHEADER:
if(!W_ConvertIWADLumps( wad ))
return NULL;
break;
case IDWAD2HEADER:
case IDWAD3HEADER:
if(!W_ReadLumpTable( wad ))
return NULL;
break;
}
}
2008-08-14 22:00:00 +02:00
}
// and leaves the file open
return wad;
}
void W_Close( wfile_t *wad )
{
fs_offset_t ofs;
if( !wad ) return;
2010-10-26 22:00:00 +02:00
if( wad->handle >= 0 && ( wad->mode == O_APPEND || wad->mode == O_WRONLY ))
2008-08-14 22:00:00 +02:00
{
dwadinfo_t hdr;
// write the lumpingo
2010-10-26 22:00:00 +02:00
ofs = tell( wad->handle );
write( wad->handle, wad->lumps, wad->numlumps * sizeof( dlumpinfo_t ));
2008-08-14 22:00:00 +02:00
// write the header
hdr.ident = IDWAD3HEADER;
2010-11-21 22:00:00 +01:00
hdr.numlumps = wad->numlumps;
hdr.infotableofs = ofs;
2008-11-14 22:00:00 +01:00
2010-10-26 22:00:00 +02:00
lseek( wad->handle, 0, SEEK_SET );
write( wad->handle, &hdr, sizeof( hdr ));
2008-08-14 22:00:00 +02:00
}
2008-08-15 22:00:00 +02:00
Mem_FreePool( &wad->mempool );
2010-10-26 22:00:00 +02:00
if( wad->handle >= 0 ) close( wad->handle );
2008-08-14 22:00:00 +02:00
Mem_Free( wad ); // free himself
}
fs_offset_t W_SaveLump( wfile_t *wad, const char *lump, const void* data, size_t datasize, char type, char cmp )
{
size_t lat_size;
dlumpinfo_t *info;
2010-09-10 22:00:00 +02:00
int i;
2008-08-14 22:00:00 +02:00
2008-08-15 22:00:00 +02:00
if( !wad || !lump ) return -1;
2010-09-10 22:00:00 +02:00
2008-08-15 22:00:00 +02:00
if( !data || !datasize )
{
MsgDev( D_WARN, "W_SaveLump: ignore blank lump %s - nothing to save\n", lump );
return -1;
}
2010-09-10 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
if( wad->mode == O_RDONLY )
{
MsgDev( D_ERROR, "W_SaveLump: %s opened in readonly mode\n", wad->filename );
return -1;
}
2010-09-10 22:00:00 +02:00
2008-08-14 22:00:00 +02:00
if( wad->numlumps >= MAX_FILES_IN_WAD )
{
MsgDev( D_ERROR, "W_SaveLump: %s is full\n", wad->filename );
return -1;
}
2010-09-10 22:00:00 +02:00
2008-11-04 22:00:00 +01:00
if( W_FindLump( wad, lump, type ))
{
// don't make mirror lumps
MsgDev( D_ERROR, "W_SaveLump: %s already exist\n", lump );
return -1;
}
2008-08-14 22:00:00 +02:00
2008-08-15 22:00:00 +02:00
lat_size = sizeof( dlumpinfo_t ) * (wad->numlumps + 1);
2008-08-14 22:00:00 +02:00
// reallocate lumptable
2008-08-15 22:00:00 +02:00
wad->lumps = Mem_Realloc( wad->mempool, wad->lumps, lat_size );
2008-08-14 22:00:00 +02:00
info = wad->lumps + wad->numlumps;
// write header
W_CleanupName( lump, info->name );
2010-11-21 22:00:00 +01:00
info->filepos = tell( wad->handle );
2008-08-14 22:00:00 +02:00
info->compression = cmp;
info->type = type;
2010-10-26 22:00:00 +02:00
i = tell( wad->handle );
2010-09-10 22:00:00 +02:00
if( !W_WriteLump( wad, info, data, datasize, cmp ))
2010-10-26 22:00:00 +02:00
{
if( cmp == CMP_LZSS )
{
MsgDev( D_NOTE, "W_SaveLump: %s failed to compress\n", info->name );
// in case we fail compress this lump, store it as non-compressed
if( !W_WriteLump( wad, info, data, datasize, CMP_NONE ))
return -1;
}
else return -1;
}
2008-08-14 22:00:00 +02:00
MsgDev( D_NOTE, "W_SaveLump: %s, size %d\n", info->name, info->disksize );
2008-08-15 22:00:00 +02:00
return wad->numlumps++;
2008-08-14 22:00:00 +02:00
}
byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type )
{
dlumpinfo_t *lump;
2008-11-22 22:00:00 +01:00
// assume error
if( lumpsizeptr ) *lumpsizeptr = 0;
2008-08-14 22:00:00 +02:00
if( !wad ) return NULL;
lump = W_FindLump( wad, lumpname, type );
return W_ReadLump( wad, lump, lumpsizeptr );
}
/*
=============================================================================
FILESYSTEM IMPLEMENTATION
=============================================================================
*/
static byte *W_LoadFile( const char *path, fs_offset_t *lumpsizeptr )
{
searchpath_t *search;
int index;
2010-09-10 22:00:00 +02:00
search = FS_FindFile( path, &index, false );
2008-08-14 22:00:00 +02:00
if( search && search->wad )
return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr );
return NULL;
}