//
#include "vfs.h"
+#include "globaldefs.h"
#include <stdio.h>
#include <stdlib.h>
#include "os/path.h"
#include "moduleobservers.h"
#include "filematch.h"
+#include "dpkdeps.h"
-#define VFS_MAXDIRS 64
+const int VFS_MAXDIRS = 64;
-#if defined( WIN32 )
+#if GDEF_OS_WINDOWS
#define PATH_MAX 260
#endif
};
#include <list>
+#include <map>
typedef std::list<archive_entry_t> archives_t;
}
}
-
-
const _QERArchiveTable* GetArchiveTable( ArchiveModules& archiveModules, const char* ext ){
StringOutputStream tmp( 16 );
tmp << LowerCase( ext );
return archiveModules.findModule( tmp.c_str() );
}
-static void InitPakFile( ArchiveModules& archiveModules, const char *filename ){
+
+static Archive* InitPakFile( ArchiveModules& archiveModules, const char *filename ){
const _QERArchiveTable* table = GetArchiveTable( archiveModules, path_get_extension( filename ) );
if ( table != 0 ) {
entry.archive = table->m_pfnOpenArchive( filename );
entry.is_pakfile = true;
g_archives.push_back( entry );
- globalOutputStream() << " pak file: " << filename << "\n";
+ globalOutputStream() << "pak file: " << filename << "\n";
+
+ return entry.archive;
}
+
+ return 0;
}
inline void pathlist_prepend_unique( GSList*& pathlist, char* path ){
typedef std::set<CopiedString, PakLess> Archives;
+Archive* AddPakDir( const char* fullpath ){
+ if ( g_numDirs == VFS_MAXDIRS ) return 0;
+
+ globalOutputStream() << "pak directory: " << fullpath << "\n";
+ strncpy( g_strDirs[g_numDirs], fullpath, PATH_MAX );
+ g_strDirs[g_numDirs][PATH_MAX] = '\0';
+ g_numDirs++;
+
+ {
+ archive_entry_t entry;
+ entry.name = fullpath;
+ entry.archive = OpenArchive( fullpath );
+ entry.is_pakfile = false;
+ g_archives.push_back( entry );
+
+ return entry.archive;
+ }
+}
+
+// for Daemon DPK VFS
+
+Archive* AddDpkDir( const char* fullpath ){
+ return AddPakDir( fullpath );
+}
+
+struct pakfile_path_t
+{
+ CopiedString fullpath; // full pak dir or pk3dir name
+ bool is_pakfile; // tells it is .pk3dir or .pk3 file
+};
+
+typedef std::pair<CopiedString, pakfile_path_t> PakfilePathsKV;
+typedef std::map<CopiedString, pakfile_path_t> PakfilePaths; // key must have no extension, only name
+
+static PakfilePaths g_pakfile_paths;
+
+void AddDpkPak( const char* name, const char* fullpath, bool is_pakfile ){
+ pakfile_path_t pakfile_path;
+ pakfile_path.fullpath = fullpath;
+ pakfile_path.is_pakfile = is_pakfile;
+ g_pakfile_paths.insert( PakfilePathsKV( name, pakfile_path ) );
+}
+
+// takes name without ext, returns without ext
+static const char* GetLatestDpkPakVersion( const char* name ){
+ const char* maxversion = 0;
+ const char* result = 0;
+ const char* pakname;
+ const char* pakversion;
+ int namelen = string_length( name );
+
+ for ( PakfilePaths::iterator i = g_pakfile_paths.begin(); i != g_pakfile_paths.end(); ++i )
+ {
+ pakname = i->first.c_str();
+ if ( strncmp( pakname, name, namelen ) != 0 || pakname[namelen] != '_' ) continue;
+ pakversion = pakname + (namelen + 1);
+ if ( maxversion == 0 || DpkPakVersionCmp( pakversion, maxversion ) > 0 ){
+ maxversion = pakversion;
+ result = pakname;
+ }
+ }
+ return result;
+}
+
+// release string after using
+static char* GetCurrentMapDpkPakName(){
+ char* mapdir;
+ char* mapname;
+ int mapnamelen;
+ char* result = 0;
+
+ mapname = string_clone( GlobalRadiant().getMapName() );
+ mapnamelen = string_length( mapname );
+
+ mapdir = strrchr( mapname, '/' );
+ if ( mapdir ) {
+ mapdir -= 12;
+ if ( strncmp( mapdir, ".dpkdir/maps/", 13 ) == 0 ) {
+ *mapdir = '\0';
+ mapdir = strrchr( mapname, '/' );
+ if ( mapdir ) mapdir++;
+ else mapdir = mapname;
+ result = string_clone( mapdir );
+ }
+ }
+
+ string_release( mapname, mapnamelen );
+ return result;
+
+}
+
+// prevent loading duplicates or circular references
+static Archives g_loaded_dpk_paks;
+
+// actual pak adding on initialise, deferred from InitDirectory
+// Daemon DPK filesystem doesn't need load all paks it finds
+static void LoadDpkPakWithDeps( const char* pakname ){
+ Archive* arc;
+ ArchiveTextFile* depsFile;
+
+ if (pakname == NULL) {
+ // load DEPS from game pack
+ StringOutputStream baseDirectory( 256 );
+ const char* basegame = GlobalRadiant().getRequiredGameDescriptionKeyValue( "basegame" );
+ baseDirectory << GlobalRadiant().getGameToolsPath() << basegame << '/';
+ arc = AddDpkDir( baseDirectory.c_str() );
+ depsFile = arc->openTextFile( "DEPS" );
+ } else {
+ const char* und = strrchr( pakname, '_' );
+ if ( !und ) {
+ pakname = GetLatestDpkPakVersion( pakname );
+ }
+ if ( !pakname || g_loaded_dpk_paks.find( pakname ) != g_loaded_dpk_paks.end() ) {
+ return;
+ }
+
+ PakfilePaths::iterator i = g_pakfile_paths.find( pakname );
+ if ( i == g_pakfile_paths.end() ) {
+ return;
+ }
+
+ if ( i->second.is_pakfile ){
+ arc = InitPakFile( FileSystemQ3API_getArchiveModules(), i->second.fullpath.c_str() );
+ } else {
+ arc = AddDpkDir( i->second.fullpath.c_str() );
+ }
+ g_loaded_dpk_paks.insert( pakname );
+
+ depsFile = arc->openTextFile( "DEPS" );
+ }
+
+ if ( !depsFile ) {
+ return;
+ }
+
+ {
+ TextLinesInputStream<TextInputStream> istream = depsFile->getInputStream();
+
+ CopiedString line;
+ char *p_name;
+ char *p_version;
+ while ( line = istream.readLine(), string_length( line.c_str() ) ) {
+ if ( !DpkReadDepsLine( line.c_str(), &p_name, &p_version ) ) continue;
+ if ( !p_version ) {
+ const char* p_latest = GetLatestDpkPakVersion( p_name );
+ if ( p_latest ) LoadDpkPakWithDeps( p_latest );
+ } else {
+ int len = string_length( p_name ) + string_length( p_version ) + 1;
+ char* p_pakname = string_new( len );
+ sprintf( p_pakname, "%s_%s", p_name, p_version );
+ LoadDpkPakWithDeps( p_pakname );
+ string_release( p_pakname, len );
+ }
+ string_release( p_name, string_length( p_name ) );
+ if ( p_version ) string_release( p_version, string_length( p_version ) );
+ }
+ }
+
+ depsFile->release();
+}
+
+// end for Daemon DPK vfs
+
// =============================================================================
// Global functions
g_numForbiddenDirs = 0;
StringTokeniser st( GlobalRadiant().getGameDescriptionKeyValue( "forbidden_paths" ), " " );
+
for ( j = 0; j < VFS_MAXDIRS; ++j )
{
const char *t = st.getToken();
}
g_free( dbuf );
}
+
if ( j < g_numForbiddenDirs ) {
printf( "Directory %s matched by forbidden dirs, removed\n", directory );
return;
}
if ( g_bUsePak ) {
+
GDir* dir = g_dir_open( path, 0, 0 );
- if ( dir != 0 ) {
+ if ( dir != NULL ) {
globalOutputStream() << "vfs directory: " << path << "\n";
+ Archives archives;
+ Archives archivesOverride;
const char* ignore_prefix = "";
const char* override_prefix = "";
+ bool is_wad_vfs, is_pak_vfs, is_pk3_vfs, is_pk4_vfs, is_dpk_vfs;
- {
+ is_wad_vfs = !!GetArchiveTable( archiveModules, "wad" );
+ is_pak_vfs = !!GetArchiveTable( archiveModules, "pak" );
+ is_pk3_vfs = !!GetArchiveTable( archiveModules, "pk3" );
+ is_pk4_vfs = !!GetArchiveTable( archiveModules, "pk4" );
+ is_dpk_vfs = !!GetArchiveTable( archiveModules, "dpk" );
+
+ if ( is_dpk_vfs ) {
// See if we are in "sp" or "mp" mapping mode
const char* gamemode = gamemode_get();
}
}
- Archives archives;
- Archives archivesOverride;
- for (;; )
+ while ( true )
{
const char* name = g_dir_read_name( dir );
- if ( name == 0 ) {
+
+ if ( name == nullptr ) {
break;
}
break;
}
}
+
if ( j < g_numForbiddenDirs ) {
continue;
}
const char *ext = strrchr( name, '.' );
-
- if ( ext && !string_compare_nocase_upper( ext, ".pk3dir" ) ) {
- if ( g_numDirs == VFS_MAXDIRS ) {
- continue;
+ char tmppath[PATH_MAX + 1];
+
+ if ( ext != nullptr ) {
+ if ( is_dpk_vfs && !string_compare_nocase_upper( ext, ".dpkdir" ) ) {
+ snprintf( tmppath, PATH_MAX, "%s%s/", path, name );
+ tmppath[PATH_MAX] = '\0';
+ FixDOSName( tmppath );
+ AddSlash( tmppath );
+ AddDpkPak( CopiedString( StringRange( name, ext ) ).c_str(), tmppath, false );
}
- snprintf( g_strDirs[g_numDirs], PATH_MAX, "%s%s/", path, name );
- g_strDirs[g_numDirs][PATH_MAX] = '\0';
- FixDOSName( g_strDirs[g_numDirs] );
- AddSlash( g_strDirs[g_numDirs] );
- g_numDirs++;
- {
- archive_entry_t entry;
- entry.name = g_strDirs[g_numDirs - 1];
- entry.archive = OpenArchive( g_strDirs[g_numDirs - 1] );
- entry.is_pakfile = false;
- g_archives.push_back( entry );
+ else if ( ( is_wad_vfs && !string_compare_nocase_upper( ext, ".pakdir" ) )
+ || ( is_pk3_vfs && !string_compare_nocase_upper( ext, ".pk3dir" ) )
+ || ( is_pk4_vfs && !string_compare_nocase_upper( ext, ".pk4dir" ) ) ) {
+ snprintf( tmppath, PATH_MAX, "%s%s/", path, name );
+ tmppath[PATH_MAX] = '\0';
+ FixDOSName( tmppath );
+ AddSlash( tmppath );
+ AddPakDir( tmppath );
}
}
- if ( ( ext == 0 ) || *( ++ext ) == '\0' || GetArchiveTable( archiveModules, ext ) == 0 ) {
+ // GetArchiveTable() needs "pk3" if ext is ".pk3"
+ if ( ( ext == nullptr ) || *( ext + 1 ) == '\0' || GetArchiveTable( archiveModules, ext + 1 ) == 0 ) {
continue;
}
if ( !string_empty( ignore_prefix ) && strncmp( name, ignore_prefix, strlen( ignore_prefix ) ) == 0 ) {
continue;
}
+
if ( !string_empty( override_prefix ) && strncmp( name, override_prefix, strlen( override_prefix ) ) == 0 ) {
- archivesOverride.insert( name );
- continue;
+ if ( !string_compare_nocase_upper( ext, ".dpk" ) ) {
+ if ( is_dpk_vfs ) {
+ archives.insert( name );
+ continue;
+ }
+ }
+ else {
+ archivesOverride.insert( name );
+ continue;
+ }
}
archives.insert( name );
g_dir_close( dir );
// add the entries to the vfs
- for ( Archives::iterator i = archivesOverride.begin(); i != archivesOverride.end(); ++i )
- {
- char filename[PATH_MAX];
- strcpy( filename, path );
- strcat( filename, ( *i ).c_str() );
- InitPakFile( archiveModules, filename );
+ char* fullpath;
+ if ( is_dpk_vfs ) {
+ for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i ) {
+ const char* name = i->c_str();
+ const char* ext = strrchr( name, '.' );
+ if ( !string_compare_nocase_upper( ext, ".dpk" ) )
+ {
+ CopiedString name_final = CopiedString( StringRange( name, ext ) );
+ fullpath = string_new_concat( path, name );
+ AddDpkPak( name_final.c_str(), fullpath, true );
+ string_release( fullpath, string_length( fullpath ) );
+ }
+ }
}
- for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i )
+ else
{
- char filename[PATH_MAX];
- strcpy( filename, path );
- strcat( filename, ( *i ).c_str() );
- InitPakFile( archiveModules, filename );
+ for ( Archives::iterator i = archivesOverride.begin(); i != archivesOverride.end(); ++i )
+ {
+ const char* name = i->c_str();
+ const char* ext = strrchr( name, '.' );
+ if ( ( is_wad_vfs && !string_compare_nocase_upper( ext, ".wad" ) )
+ || ( is_pak_vfs && !string_compare_nocase_upper( ext, ".pak" ) )
+ || ( is_pk3_vfs && !string_compare_nocase_upper( ext, ".pk3" ) )
+ || ( is_pk4_vfs && !string_compare_nocase_upper( ext, ".pk4" ) ) ) {
+ fullpath = string_new_concat( path, i->c_str() );
+ InitPakFile( archiveModules, fullpath );
+ string_release( fullpath, string_length( fullpath ) );
+ }
+ }
+
+ for ( Archives::iterator i = archives.begin(); i != archives.end(); ++i )
+ {
+ const char* name = i->c_str();
+ const char* ext = strrchr( name, '.' );
+ if ( ( is_wad_vfs && !string_compare_nocase_upper( ext, ".wad" ) )
+ || ( is_pak_vfs && !string_compare_nocase_upper( ext, ".pak" ) )
+ || ( is_pk3_vfs && !string_compare_nocase_upper( ext, ".pk3" ) )
+ || ( is_pk4_vfs && !string_compare_nocase_upper( ext, ".pk4" ) ) ) {
+ fullpath = string_new_concat( path, i->c_str() );
+ InitPakFile( archiveModules, fullpath );
+ string_release( fullpath, string_length( fullpath ) );
+ }
+ }
}
}
else
g_numDirs = 0;
g_numForbiddenDirs = 0;
+
+ g_pakfile_paths.clear();
+ g_loaded_dpk_paks.clear();
}
-#define VFS_SEARCH_PAK 0x1
-#define VFS_SEARCH_DIR 0x2
+const int VFS_SEARCH_PAK = 0x1;
+const int VFS_SEARCH_DIR = 0x2;
int GetFileCount( const char *filename, int flag ){
int count = 0;
for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i )
{
- if ( ( *i ).is_pakfile && ( flag & VFS_SEARCH_PAK ) != 0
- || !( *i ).is_pakfile && ( flag & VFS_SEARCH_DIR ) != 0 ) {
+ if ( (( *i ).is_pakfile && ( flag & VFS_SEARCH_PAK ) != 0)
+ || (!( *i ).is_pakfile && ( flag & VFS_SEARCH_DIR ) != 0) ) {
if ( ( *i ).archive->containsFile( fixed ) ) {
++count;
}
InitDirectory( path, FileSystemQ3API_getArchiveModules() );
}
void initialise(){
+ load();
globalOutputStream() << "filesystem initialised\n";
g_observers.realise();
}
+
+void load(){
+ ArchiveModules& archiveModules = FileSystemQ3API_getArchiveModules();
+ bool is_dpk_vfs = !!GetArchiveTable( archiveModules, "dpk" );
+
+ if ( is_dpk_vfs ) {
+ const char* pakname;
+ g_loaded_dpk_paks.clear();
+
+ // Load DEPS from game pack
+ LoadDpkPakWithDeps( NULL );
+
+ // prevent VFS double start, for MapName="" and MapName="unnamed.map"
+ if ( string_length( GlobalRadiant().getMapName() ) ){
+ // load map's paks from DEPS
+ char* mappakname = GetCurrentMapDpkPakName();
+ if ( mappakname != NULL ) {
+ LoadDpkPakWithDeps( mappakname );
+ string_release( mappakname, string_length( mappakname ) );
+ }
+ }
+
+ g_pakfile_paths.clear();
+ g_loaded_dpk_paks.clear();
+ }
+}
+
+void clear() {
+ // like shutdown() but does not unrealise (keep map etc.)
+ Shutdown();
+}
+
+void refresh(){
+ // like initialise() but does not realise (keep map etc.)
+ load();
+ globalOutputStream() << "filesystem refreshed\n";
+}
+
void shutdown(){
g_observers.unrealise();
globalOutputStream() << "filesystem shutdown\n";
if ( path_equal( ( *i ).name.c_str(), archiveName ) ) {
return ( *i ).archive;
}
+ else if ( path_equal( path_get_filename_start( ( *i ).name.c_str() ), archiveName ) ) {
+ return ( *i ).archive;
+ }
}
return 0;
}
}
};
+
Quake3FileSystem g_Quake3FileSystem;
-void FileSystem_Init(){
+VirtualFileSystem& GetFileSystem(){
+ return g_Quake3FileSystem;
}
-void FileSystem_Shutdown(){
+void FileSystem_Init(){
}
-VirtualFileSystem& GetFileSystem(){
- return g_Quake3FileSystem;
+void FileSystem_Shutdown(){
}