]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/vfspk3/vfs.cpp
CMake: Make dll bundling optional
[xonotic/netradiant.git] / plugins / vfspk3 / vfs.cpp
index 50bd1e4ea0f2dd0642404e10f5c387057af79364..bb3b5dbf4fc7c8cc237efdf54da6671272aa6872 100644 (file)
@@ -45,9 +45,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
-#include <glib/gslist.h>
-#include <glib/gdir.h>
-#include <glib/gstrfuncs.h>
+#include <glib.h>
 
 #include "qerplugin.h"
 #include "idatastream.h"
@@ -61,6 +59,7 @@ ArchiveModules& FileSystemQ3API_getArchiveModules();
 #include "os/path.h"
 #include "moduleobservers.h"
 #include "filematch.h"
+#include "dpkdeps.h"
 
 
 #define VFS_MAXDIRS 64
@@ -86,6 +85,7 @@ struct archive_entry_t
 };
 
 #include <list>
+#include <map>
 
 typedef std::list<archive_entry_t> archives_t;
 
@@ -127,14 +127,13 @@ static void FixDOSName( char *src ){
        }
 }
 
-
-
 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 ) {
@@ -145,7 +144,11 @@ static void InitPakFile( ArchiveModules& archiveModules, const char *filename ){
                entry.is_pakfile = true;
                g_archives.push_back( entry );
                globalOutputStream() << "  pak file: " << filename << "\n";
+
+               return entry.archive;
        }
+
+       return 0;
 }
 
 inline void pathlist_prepend_unique( GSList*& pathlist, char* path ){
@@ -278,6 +281,148 @@ bool operator()( const CopiedString& self, const CopiedString& other ) const {
 
 typedef std::set<CopiedString, PakLess> Archives;
 
+Archive* AddPk3Dir( const char* fullpath ){
+       if ( g_numDirs == VFS_MAXDIRS ) return 0;
+
+       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 AddPk3Dir( fullpath );
+}
+
+struct pakfile_path_t
+{
+       CopiedString fullpath;  // full pak dir or pk3dir name
+       bool is_pakfile;  // defines is it .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 ){
+       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;
+
+       Archive* arc;
+       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 );
+
+       ArchiveTextFile* 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
 
@@ -339,15 +484,23 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
        }
 
        if ( g_bUsePak ) {
+
                GDir* dir = g_dir_open( path, 0, 0 );
 
                if ( dir != 0 ) {
                        globalOutputStream() << "vfs directory: " << path << "\n";
 
+                       Archives archives;
+                       Archives archivesOverride;
                        const char* ignore_prefix = "";
                        const char* override_prefix = "";
+                       bool is_pk3_vfs, is_pk4_vfs, is_dpk_vfs;
 
-                       {
+                       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();
 
@@ -361,8 +514,6 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
                                }
                        }
 
-                       Archives archives;
-                       Archives archivesOverride;
                        for (;; )
                        {
                                const char* name = g_dir_read_name( dir );
@@ -383,27 +534,31 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
                                }
 
                                const char *ext = strrchr( name, '.' );
-
-                               if ( ext && !string_compare_nocase_upper( ext, ".pk3dir" ) ) {
-                                       if ( g_numDirs == VFS_MAXDIRS ) {
-                                               continue;
+                               char tmppath[PATH_MAX];
+
+                               if ( is_dpk_vfs ) {
+                                       if ( !!ext && !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 );
+                               }
+
+                               if ( is_pk3_vfs || is_pk4_vfs ) {
+                                       if ( !!ext && ( !string_compare_nocase_upper( ext, ".pk3dir" )
+                                               || !string_compare_nocase_upper( ext, ".pk4dir" ) ) ) {
+                                               snprintf( tmppath, PATH_MAX, "%s%s/", path, name );
+                                               tmppath[PATH_MAX] = '\0';
+                                               FixDOSName( tmppath );
+                                               AddSlash( tmppath );
+                                               AddPk3Dir( tmppath );
                                        }
                                }
 
-                               if ( ( ext == 0 ) || *( ++ext ) == '\0' || GetArchiveTable( archiveModules, ext ) == 0 ) {
+                               // GetArchiveTable() needs "pk3" if ext is ".pk3"
+                               if ( ( ext == 0 ) || *( ext + 1 ) == '\0' || GetArchiveTable( archiveModules, ext + 1 ) == 0 ) {
                                        continue;
                                }
 
@@ -412,7 +567,14 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
                                        continue;
                                }
                                if ( !string_empty( override_prefix ) && strncmp( name, override_prefix, strlen( override_prefix ) ) == 0 ) {
-                                       archivesOverride.insert( name );
+                                       if ( !string_compare_nocase_upper( ext, ".dpk" ) ) {
+                                               if ( is_dpk_vfs ) {
+                                                       archives.insert( name );
+                                               }
+                                       }
+                                       else {
+                                               archivesOverride.insert( name );
+                                       }
                                        continue;
                                }
 
@@ -422,19 +584,42 @@ void InitDirectory( const char* directory, ArchiveModules& archiveModules ){
                        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 )
-                       {
-                               char filename[PATH_MAX];
-                               strcpy( filename, path );
-                               strcat( filename, ( *i ).c_str() );
-                               InitPakFile( archiveModules, filename );
+                       if ( is_pk3_vfs || is_pk4_vfs ) {
+                               for ( Archives::iterator i = archivesOverride.begin(); i != archivesOverride.end(); ++i )
+                               {
+                                       const char* name = i->c_str();
+                                       const char* ext = strrchr( name, '.' );
+                                       if ( !string_compare_nocase_upper( ext, ".pk3" )
+                                               || !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 ( !string_compare_nocase_upper( ext, ".pk3" )
+                                               || !string_compare_nocase_upper( ext, ".pk4" ) ) {
+                                               fullpath = string_new_concat( path, i->c_str() );
+                                               InitPakFile( archiveModules, fullpath );
+                                               string_release( fullpath, string_length( fullpath ) );
+                                       }
+                               }
                        }
                }
                else
@@ -456,6 +641,9 @@ void Shutdown(){
 
        g_numDirs = 0;
        g_numForbiddenDirs = 0;
+
+       g_pakfile_paths.clear();
+       g_loaded_dpk_paks.clear();
 }
 
 #define VFS_SEARCH_PAK 0x1
@@ -589,9 +777,50 @@ void initDirectory( const char *path ){
        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();
+
+               pakname = GetLatestDpkPakVersion( "tex-common" );
+               if (pakname != NULL) {
+                       LoadDpkPakWithDeps( pakname );
+               }
+
+               // 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";
@@ -694,14 +923,15 @@ void forEachArchive( const ArchiveNameCallback& callback, bool pakonly, bool rev
 }
 };
 
+
 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(){
 }