X-Git-Url: http://git.xonotic.org/?a=blobdiff_plain;ds=sidebyside;f=plugins%2Fvfspk3%2Fvfs.cpp;h=0829073f78b1be186fb2c90e8dd1aa7a162fc099;hb=9dfae1c9b270ee369c6362903a9205b30751b95f;hp=e92bad76c016694bda7f4391d1fa2c0b6f478050;hpb=e0b3d0ac5abc0cde0a74fadbd2aa4739b43b24a3;p=xonotic%2Fnetradiant.git diff --git a/plugins/vfspk3/vfs.cpp b/plugins/vfspk3/vfs.cpp index e92bad76..0829073f 100644 --- a/plugins/vfspk3/vfs.cpp +++ b/plugins/vfspk3/vfs.cpp @@ -42,6 +42,7 @@ // #include "vfs.h" +#include "globaldefs.h" #include #include @@ -50,7 +51,9 @@ #include "qerplugin.h" #include "idatastream.h" #include "iarchive.h" -ArchiveModules& FileSystemQ3API_getArchiveModules(); + +ArchiveModules &FileSystemQ3API_getArchiveModules(); + #include "ifilesystem.h" #include "generic/callback.h" @@ -59,11 +62,12 @@ ArchiveModules& FileSystemQ3API_getArchiveModules(); #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 @@ -74,13 +78,12 @@ ArchiveModules& FileSystemQ3API_getArchiveModules(); // ============================================================================= // Global variables -Archive* OpenArchive( const char* name ); +Archive *OpenArchive(const char *name); -struct archive_entry_t -{ - CopiedString name; - Archive* archive; - bool is_pakfile; +struct archive_entry_t { + CopiedString name; + Archive *archive; + bool is_pakfile; }; #include @@ -100,143 +103,145 @@ ModuleObservers g_observers; // ============================================================================= // Static functions -static void AddSlash( char *str ){ - std::size_t n = strlen( str ); - if ( n > 0 ) { - if ( str[n - 1] != '\\' && str[n - 1] != '/' ) { - globalErrorStream() << "WARNING: directory path does not end with separator: " << str << "\n"; - strcat( str, "/" ); - } - } +static void AddSlash(char *str) +{ + std::size_t n = strlen(str); + if (n > 0) { + if (str[n - 1] != '\\' && str[n - 1] != '/') { + globalErrorStream() << "WARNING: directory path does not end with separator: " << str << "\n"; + strcat(str, "/"); + } + } } -static void FixDOSName( char *src ){ - if ( src == 0 || strchr( src, '\\' ) == 0 ) { - return; - } +static void FixDOSName(char *src) +{ + if (src == 0 || strchr(src, '\\') == 0) { + return; + } - globalErrorStream() << "WARNING: invalid path separator '\\': " << src << "\n"; + globalErrorStream() << "WARNING: invalid path separator '\\': " << src << "\n"; - while ( *src ) - { - if ( *src == '\\' ) { - *src = '/'; - } - src++; - } + while (*src) { + if (*src == '\\') { + *src = '/'; + } + src++; + } } -const _QERArchiveTable* GetArchiveTable( ArchiveModules& archiveModules, const char* ext ){ - StringOutputStream tmp( 16 ); - tmp << LowerCase( ext ); - return archiveModules.findModule( tmp.c_str() ); +const _QERArchiveTable *GetArchiveTable(ArchiveModules &archiveModules, const char *ext) +{ + StringOutputStream tmp(16); + tmp << LowerCase(ext); + return archiveModules.findModule(tmp.c_str()); } -static Archive* InitPakFile( ArchiveModules& archiveModules, const char *filename ){ - const _QERArchiveTable* table = GetArchiveTable( archiveModules, path_get_extension( filename ) ); +static Archive *InitPakFile(ArchiveModules &archiveModules, const char *filename) +{ + const _QERArchiveTable *table = GetArchiveTable(archiveModules, path_get_extension(filename)); - if ( table != 0 ) { - archive_entry_t entry; - entry.name = filename; + if (table != 0) { + archive_entry_t entry; + entry.name = filename; - entry.archive = table->m_pfnOpenArchive( filename ); - entry.is_pakfile = true; - g_archives.push_back( entry ); - globalOutputStream() << " pak file: " << filename << "\n"; + entry.archive = table->m_pfnOpenArchive(filename); + entry.is_pakfile = true; + g_archives.push_back(entry); + globalOutputStream() << " pak file: " << filename << "\n"; - return entry.archive; - } + return entry.archive; + } - return 0; + return 0; } -inline void pathlist_prepend_unique( GSList*& pathlist, char* path ){ - if ( g_slist_find_custom( pathlist, path, (GCompareFunc)path_compare ) == 0 ) { - pathlist = g_slist_prepend( pathlist, path ); - } - else - { - g_free( path ); - } +inline void pathlist_prepend_unique(GSList *&pathlist, char *path) +{ + if (g_slist_find_custom(pathlist, path, (GCompareFunc) path_compare) == 0) { + pathlist = g_slist_prepend(pathlist, path); + } else { + g_free(path); + } } -class DirectoryListVisitor : public Archive::Visitor -{ -GSList*& m_matches; -const char* m_directory; +class DirectoryListVisitor : public Archive::Visitor { + GSList *&m_matches; + const char *m_directory; public: -DirectoryListVisitor( GSList*& matches, const char* directory ) - : m_matches( matches ), m_directory( directory ) -{} -void visit( const char* name ){ - const char* subname = path_make_relative( name, m_directory ); - if ( subname != name ) { - if ( subname[0] == '/' ) { - ++subname; - } - char* dir = g_strdup( subname ); - char* last_char = dir + strlen( dir ); - if ( last_char != dir && *( --last_char ) == '/' ) { - *last_char = '\0'; - } - pathlist_prepend_unique( m_matches, dir ); - } -} + DirectoryListVisitor(GSList *&matches, const char *directory) + : m_matches(matches), m_directory(directory) + {} + + void visit(const char *name) + { + const char *subname = path_make_relative(name, m_directory); + if (subname != name) { + if (subname[0] == '/') { + ++subname; + } + char *dir = g_strdup(subname); + char *last_char = dir + strlen(dir); + if (last_char != dir && *(--last_char) == '/') { + *last_char = '\0'; + } + pathlist_prepend_unique(m_matches, dir); + } + } }; -class FileListVisitor : public Archive::Visitor -{ -GSList*& m_matches; -const char* m_directory; -const char* m_extension; +class FileListVisitor : public Archive::Visitor { + GSList *&m_matches; + const char *m_directory; + const char *m_extension; public: -FileListVisitor( GSList*& matches, const char* directory, const char* extension ) - : m_matches( matches ), m_directory( directory ), m_extension( extension ) -{} -void visit( const char* name ){ - const char* subname = path_make_relative( name, m_directory ); - if ( subname != name ) { - if ( subname[0] == '/' ) { - ++subname; - } - if ( m_extension[0] == '*' || extension_equal( path_get_extension( subname ), m_extension ) ) { - pathlist_prepend_unique( m_matches, g_strdup( subname ) ); - } - } -} + FileListVisitor(GSList *&matches, const char *directory, const char *extension) + : m_matches(matches), m_directory(directory), m_extension(extension) + {} + + void visit(const char *name) + { + const char *subname = path_make_relative(name, m_directory); + if (subname != name) { + if (subname[0] == '/') { + ++subname; + } + if (m_extension[0] == '*' || extension_equal(path_get_extension(subname), m_extension)) { + pathlist_prepend_unique(m_matches, g_strdup(subname)); + } + } + } }; -static GSList* GetListInternal( const char *refdir, const char *ext, bool directories, std::size_t depth ){ - GSList* files = 0; - - ASSERT_MESSAGE( refdir[strlen( refdir ) - 1] == '/', "search path does not end in '/'" ); - - if ( directories ) { - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - DirectoryListVisitor visitor( files, refdir ); - ( *i ).archive->forEachFile( Archive::VisitorFunc( visitor, Archive::eDirectories, depth ), refdir ); - } - } - else - { - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - FileListVisitor visitor( files, refdir, ext ); - ( *i ).archive->forEachFile( Archive::VisitorFunc( visitor, Archive::eFiles, depth ), refdir ); - } - } - - files = g_slist_reverse( files ); - - return files; +static GSList *GetListInternal(const char *refdir, const char *ext, bool directories, std::size_t depth) +{ + GSList *files = 0; + + ASSERT_MESSAGE(refdir[strlen(refdir) - 1] == '/', "search path does not end in '/'"); + + if (directories) { + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + DirectoryListVisitor visitor(files, refdir); + (*i).archive->forEachFile(Archive::VisitorFunc(visitor, Archive::eDirectories, depth), refdir); + } + } else { + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + FileListVisitor visitor(files, refdir, ext); + (*i).archive->forEachFile(Archive::VisitorFunc(visitor, Archive::eFiles, depth), refdir); + } + } + + files = g_slist_reverse(files); + + return files; } -inline int ascii_to_upper( int c ){ - if ( c >= 'a' && c <= 'z' ) { - return c - ( 'a' - 'A' ); - } - return c; +inline int ascii_to_upper(int c) +{ + if (c >= 'a' && c <= 'z') { + return c - ('a' - 'A'); + } + return c; } /*! @@ -245,22 +250,22 @@ inline int ascii_to_upper( int c ){ it converts all alphabet chars to uppercase before comparison, while stricmp converts them to lowercase. */ -static int string_compare_nocase_upper( const char* a, const char* b ){ - for (;; ) - { - int c1 = ascii_to_upper( *a++ ); - int c2 = ascii_to_upper( *b++ ); - - if ( c1 < c2 ) { - return -1; // a < b - } - if ( c1 > c2 ) { - return 1; // a > b - } - if ( c1 == 0 ) { - return 0; // a == b - } - } +static int string_compare_nocase_upper(const char *a, const char *b) +{ + for (;;) { + int c1 = ascii_to_upper(*a++); + int c2 = ascii_to_upper(*b++); + + if (c1 < c2) { + return -1; // a < b + } + if (c1 > c2) { + return 1; // a > b + } + if (c1 == 0) { + return 0; // a == b + } + } } // Arnout: note - sort pakfiles in reverse order. This ensures that @@ -270,44 +275,45 @@ static int string_compare_nocase_upper( const char* a, const char* b ){ // last one in the list that is). //!\todo Analyse the code in rtcw/q3 to see which order it sorts pak files. -class PakLess -{ +class PakLess { public: -bool operator()( const CopiedString& self, const CopiedString& other ) const { - return string_compare_nocase_upper( self.c_str(), other.c_str() ) > 0; -} + bool operator()(const CopiedString &self, const CopiedString &other) const + { + return string_compare_nocase_upper(self.c_str(), other.c_str()) > 0; + } }; typedef std::set Archives; -Archive* AddPk3Dir( const char* fullpath ){ - if ( g_numDirs == VFS_MAXDIRS ) return 0; +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++; + 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 ); + { + archive_entry_t entry; + entry.name = fullpath; + entry.archive = OpenArchive(fullpath); + entry.is_pakfile = false; + g_archives.push_back(entry); - return entry.archive; - } + return entry.archive; + } } // for Daemon DPK vfs -Archive* AddDpkDir( const char* fullpath ){ - return AddPk3Dir( fullpath ); +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 +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 PakfilePathsKV; @@ -315,112 +321,60 @@ typedef std::map PakfilePaths; // key must have n 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 ) ); -} - -// Comparaison function for version numbers -// Implementation is based on dpkg's version comparison code (verrevcmp() and order()) -// http://anonscm.debian.org/gitweb/?p=dpkg/dpkg.git;a=blob;f=lib/dpkg/version.c;hb=74946af470550a3295e00cf57eca1747215b9311 -static int char_weight(char c){ - if (std::isdigit(c)) - return 0; - else if (std::isalpha(c)) - return c; - else if (c == '~') - return -1; - else if (c) - return c + 256; - else - return 0; -} - -static int VersionCmp(const char* a, const char* b){ - while (*a || *b) { - int firstDiff = 0; - - while ((*a && !std::isdigit(*a)) || (*b && !std::isdigit(*b))) { - int ac = char_weight(*a); - int bc = char_weight(*b); - - if (ac != bc) - return ac - bc; - - a++; - b++; - } - - while (*a == '0') - a++; - while (*b == '0') - b++; - - while (std::isdigit(*a) && std::isdigit(*b)) { - if (firstDiff == 0) - firstDiff = *a - *b; - a++; - b++; - } - - if (std::isdigit(*a)) - return 1; - if (std::isdigit(*b)) - return -1; - if (firstDiff) - return firstDiff; - } - - return false; +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 || VersionCmp( pakversion, maxversion ) > 0 ){ - maxversion = pakversion; - result = pakname; - } - } - return result; +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; +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; } @@ -429,67 +383,70 @@ 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 istream = depsFile->getInputStream(); - - CopiedString line; - const char* c; - const char* p_name; - const char* p_name_end; - const char* p_version; - const char* p_version_end; - while ( line = istream.readLine(), string_length( line.c_str() ) ) { - c = line.c_str(); - while ( std::isspace( *c ) && *c != '\0' ) ++c; - p_name = c; - while ( !std::isspace( *c ) && *c != '\0' ) ++c; - p_name_end = c; - while ( std::isspace( *c ) && *c != '\0' ) ++c; - p_version = c; - while ( !std::isspace( *c ) && *c != '\0' ) ++c; - p_version_end = c; - - if ( p_name_end - p_name == 0 ) continue; - if ( p_version_end - p_version == 0 ) { - const char* p_pakname; - CopiedString name_final = CopiedString( StringRange( p_name, p_name_end ) ); - p_pakname = GetLatestDpkPakVersion( name_final.c_str() ); - if ( p_pakname != NULL ) { - LoadDpkPakWithDeps( p_pakname ); - } - } else { - int len = ( p_name_end - p_name ) + ( p_version_end - p_version ) + 1; - char* p_pakname = string_new( len ); - strncpy( p_pakname, p_name, p_name_end - p_name ); - p_pakname[ p_name_end - p_name ] = '\0'; - strcat( p_pakname, "_" ); - strncat( p_pakname, p_version, p_version_end - p_version ); - LoadDpkPakWithDeps( p_pakname ); - string_release( p_pakname, len ); - } - } - } - - depsFile->release(); +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 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 @@ -498,496 +455,536 @@ static void LoadDpkPakWithDeps( const char* pakname ){ // Global functions // reads all pak files from a dir -void InitDirectory( const char* directory, ArchiveModules& archiveModules ){ - int j; - - g_numForbiddenDirs = 0; - StringTokeniser st( GlobalRadiant().getGameDescriptionKeyValue( "forbidden_paths" ), " " ); - for ( j = 0; j < VFS_MAXDIRS; ++j ) - { - const char *t = st.getToken(); - if ( string_empty( t ) ) { - break; - } - strncpy( g_strForbiddenDirs[g_numForbiddenDirs], t, PATH_MAX ); - g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = '\0'; - ++g_numForbiddenDirs; - } - - for ( j = 0; j < g_numForbiddenDirs; ++j ) - { - char* dbuf = g_strdup( directory ); - if ( *dbuf && dbuf[strlen( dbuf ) - 1] == '/' ) { - dbuf[strlen( dbuf ) - 1] = 0; - } - const char *p = strrchr( dbuf, '/' ); - p = ( p ? ( p + 1 ) : dbuf ); - if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) { - g_free( dbuf ); - break; - } - g_free( dbuf ); - } - if ( j < g_numForbiddenDirs ) { - printf( "Directory %s matched by forbidden dirs, removed\n", directory ); - return; - } - - if ( g_numDirs == VFS_MAXDIRS ) { - return; - } - - strncpy( g_strDirs[g_numDirs], directory, PATH_MAX ); - g_strDirs[g_numDirs][PATH_MAX] = '\0'; - FixDOSName( g_strDirs[g_numDirs] ); - AddSlash( g_strDirs[g_numDirs] ); - - const char* path = g_strDirs[g_numDirs]; - - g_numDirs++; - - { - archive_entry_t entry; - entry.name = path; - entry.archive = OpenArchive( path ); - entry.is_pakfile = false; - g_archives.push_back( entry ); - } - - 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(); - - if ( strcmp( gamemode, "sp" ) == 0 ) { - ignore_prefix = "mp_"; - override_prefix = "sp_"; - } - else if ( strcmp( gamemode, "mp" ) == 0 ) { - ignore_prefix = "sp_"; - override_prefix = "mp_"; - } - } - - for (;; ) - { - const char* name = g_dir_read_name( dir ); - if ( name == 0 ) { - break; - } - - for ( j = 0; j < g_numForbiddenDirs; ++j ) - { - const char *p = strrchr( name, '/' ); - p = ( p ? ( p + 1 ) : name ); - if ( matchpattern( p, g_strForbiddenDirs[j], TRUE ) ) { - break; - } - } - if ( j < g_numForbiddenDirs ) { - continue; - } - - const char *ext = strrchr( name, '.' ); - 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 ); - } - } - - 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 ); - } - } - - // GetArchiveTable() needs "pk3" if ext is ".pk3" - if ( ( ext == 0 ) || *( ext + 1 ) == '\0' || GetArchiveTable( archiveModules, ext + 1 ) == 0 ) { - continue; - } - - // using the same kludge as in engine to ensure consistency - 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 ) { - if ( !string_compare_nocase_upper( ext, ".dpk" ) ) { - if ( is_dpk_vfs ) { - archives.insert( name ); - } - } - else { - archivesOverride.insert( name ); - } - continue; - } - - archives.insert( name ); - } - - g_dir_close( dir ); - - // add the entries to the vfs - 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 ) ); - } - } - } - 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 - { - globalErrorStream() << "vfs directory not found: " << path << "\n"; - } - } +void InitDirectory(const char *directory, ArchiveModules &archiveModules) +{ + int j; + + g_numForbiddenDirs = 0; + StringTokeniser st(GlobalRadiant().getGameDescriptionKeyValue("forbidden_paths"), " "); + for (j = 0; j < VFS_MAXDIRS; ++j) { + const char *t = st.getToken(); + if (string_empty(t)) { + break; + } + strncpy(g_strForbiddenDirs[g_numForbiddenDirs], t, PATH_MAX); + g_strForbiddenDirs[g_numForbiddenDirs][PATH_MAX] = '\0'; + ++g_numForbiddenDirs; + } + + for (j = 0; j < g_numForbiddenDirs; ++j) { + char *dbuf = g_strdup(directory); + if (*dbuf && dbuf[strlen(dbuf) - 1] == '/') { + dbuf[strlen(dbuf) - 1] = 0; + } + const char *p = strrchr(dbuf, '/'); + p = (p ? (p + 1) : dbuf); + if (matchpattern(p, g_strForbiddenDirs[j], TRUE)) { + g_free(dbuf); + break; + } + g_free(dbuf); + } + if (j < g_numForbiddenDirs) { + printf("Directory %s matched by forbidden dirs, removed\n", directory); + return; + } + + if (g_numDirs == VFS_MAXDIRS) { + return; + } + + strncpy(g_strDirs[g_numDirs], directory, PATH_MAX); + g_strDirs[g_numDirs][PATH_MAX] = '\0'; + FixDOSName(g_strDirs[g_numDirs]); + AddSlash(g_strDirs[g_numDirs]); + + const char *path = g_strDirs[g_numDirs]; + + g_numDirs++; + + { + archive_entry_t entry; + entry.name = path; + entry.archive = OpenArchive(path); + entry.is_pakfile = false; + g_archives.push_back(entry); + } + + 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(); + + if (strcmp(gamemode, "sp") == 0) { + ignore_prefix = "mp_"; + override_prefix = "sp_"; + } else if (strcmp(gamemode, "mp") == 0) { + ignore_prefix = "sp_"; + override_prefix = "mp_"; + } + } + + for (;;) { + const char *name = g_dir_read_name(dir); + if (name == 0) { + break; + } + + for (j = 0; j < g_numForbiddenDirs; ++j) { + const char *p = strrchr(name, '/'); + p = (p ? (p + 1) : name); + if (matchpattern(p, g_strForbiddenDirs[j], TRUE)) { + break; + } + } + if (j < g_numForbiddenDirs) { + continue; + } + + const char *ext = strrchr(name, '.'); + 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); + } + } + + 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); + } + } + + // GetArchiveTable() needs "pk3" if ext is ".pk3" + if ((ext == 0) || *(ext + 1) == '\0' || GetArchiveTable(archiveModules, ext + 1) == 0) { + continue; + } + + // using the same kludge as in engine to ensure consistency + 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) { + if (!string_compare_nocase_upper(ext, ".dpk")) { + if (is_dpk_vfs) { + archives.insert(name); + } + } else { + archivesOverride.insert(name); + } + continue; + } + + archives.insert(name); + } + + g_dir_close(dir); + + // add the entries to the vfs + 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)); + } + } + } + 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 { + globalErrorStream() << "vfs directory not found: " << path << "\n"; + } + } } // frees all memory that we allocated // FIXME TTimo this should be improved so that we can shutdown and restart the VFS without exiting Radiant? // (for instance when modifying the project settings) -void Shutdown(){ - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - ( *i ).archive->release(); - } - g_archives.clear(); - - g_numDirs = 0; - g_numForbiddenDirs = 0; - - g_pakfile_paths.clear(); - g_loaded_dpk_paks.clear(); +void Shutdown() +{ + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + (*i).archive->release(); + } + g_archives.clear(); + + 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; - char fixed[PATH_MAX + 1]; +int GetFileCount(const char *filename, int flag) +{ + int count = 0; + char fixed[PATH_MAX + 1]; - strncpy( fixed, filename, PATH_MAX ); - fixed[PATH_MAX] = '\0'; - FixDOSName( fixed ); + strncpy(fixed, filename, PATH_MAX); + fixed[PATH_MAX] = '\0'; + FixDOSName(fixed); - if ( !flag ) { - flag = VFS_SEARCH_PAK | VFS_SEARCH_DIR; - } + if (!flag) { + flag = VFS_SEARCH_PAK | VFS_SEARCH_DIR; + } - 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 ).archive->containsFile( fixed ) ) { - ++count; - } - } - } + 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).archive->containsFile(fixed)) { + ++count; + } + } + } - return count; + return count; } -ArchiveFile* OpenFile( const char* filename ){ - ASSERT_MESSAGE( strchr( filename, '\\' ) == 0, "path contains invalid separator '\\': \"" << filename << "\"" ); - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - ArchiveFile* file = ( *i ).archive->openFile( filename ); - if ( file != 0 ) { - return file; - } - } - - return 0; -} +ArchiveFile *OpenFile(const char *filename) +{ + ASSERT_MESSAGE(strchr(filename, '\\') == 0, "path contains invalid separator '\\': \"" << filename << "\""); + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + ArchiveFile *file = (*i).archive->openFile(filename); + if (file != 0) { + return file; + } + } -ArchiveTextFile* OpenTextFile( const char* filename ){ - ASSERT_MESSAGE( strchr( filename, '\\' ) == 0, "path contains invalid separator '\\': \"" << filename << "\"" ); - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - ArchiveTextFile* file = ( *i ).archive->openTextFile( filename ); - if ( file != 0 ) { - return file; - } - } - - return 0; + return 0; } -// NOTE: when loading a file, you have to allocate one extra byte and set it to \0 -std::size_t LoadFile( const char *filename, void **bufferptr, int index ){ - char fixed[PATH_MAX + 1]; +ArchiveTextFile *OpenTextFile(const char *filename) +{ + ASSERT_MESSAGE(strchr(filename, '\\') == 0, "path contains invalid separator '\\': \"" << filename << "\""); + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + ArchiveTextFile *file = (*i).archive->openTextFile(filename); + if (file != 0) { + return file; + } + } - strncpy( fixed, filename, PATH_MAX ); - fixed[PATH_MAX] = '\0'; - FixDOSName( fixed ); + return 0; +} - ArchiveFile* file = OpenFile( fixed ); +// NOTE: when loading a file, you have to allocate one extra byte and set it to \0 +std::size_t LoadFile(const char *filename, void **bufferptr, int index) +{ + char fixed[PATH_MAX + 1]; - if ( file != 0 ) { - *bufferptr = malloc( file->size() + 1 ); - // we need to end the buffer with a 0 - ( (char*) ( *bufferptr ) )[file->size()] = 0; + strncpy(fixed, filename, PATH_MAX); + fixed[PATH_MAX] = '\0'; + FixDOSName(fixed); - std::size_t length = file->getInputStream().read( (InputStream::byte_type*)*bufferptr, file->size() ); - file->release(); - return length; - } + ArchiveFile *file = OpenFile(fixed); - *bufferptr = 0; - return 0; -} + if (file != 0) { + *bufferptr = malloc(file->size() + 1); + // we need to end the buffer with a 0 + ((char *) (*bufferptr))[file->size()] = 0; -void FreeFile( void *p ){ - free( p ); -} + std::size_t length = file->getInputStream().read((InputStream::byte_type *) *bufferptr, file->size()); + file->release(); + return length; + } -GSList* GetFileList( const char *dir, const char *ext, std::size_t depth ){ - return GetListInternal( dir, ext, false, depth ); + *bufferptr = 0; + return 0; } -GSList* GetDirList( const char *dir, std::size_t depth ){ - return GetListInternal( dir, 0, true, depth ); +void FreeFile(void *p) +{ + free(p); } -void ClearFileDirList( GSList **lst ){ - while ( *lst ) - { - g_free( ( *lst )->data ); - *lst = g_slist_remove( *lst, ( *lst )->data ); - } +GSList *GetFileList(const char *dir, const char *ext, std::size_t depth) +{ + return GetListInternal(dir, ext, false, depth); } -const char* FindFile( const char* relative ){ - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - if ( ( *i ).archive->containsFile( relative ) ) { - return ( *i ).name.c_str(); - } - } - - return ""; +GSList *GetDirList(const char *dir, std::size_t depth) +{ + return GetListInternal(dir, 0, true, depth); } -const char* FindPath( const char* absolute ){ - const char *best = ""; - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - if ( string_length( ( *i ).name.c_str() ) > string_length( best ) ) { - if ( path_equal_n( absolute, ( *i ).name.c_str(), string_length( ( *i ).name.c_str() ) ) ) { - best = ( *i ).name.c_str(); - } - } - } - - return best; +void ClearFileDirList(GSList **lst) +{ + while (*lst) { + g_free((*lst)->data); + *lst = g_slist_remove(*lst, (*lst)->data); + } } - -class Quake3FileSystem : public VirtualFileSystem +const char *FindFile(const char *relative) { -public: -void initDirectory( const char *path ){ - InitDirectory( path, FileSystemQ3API_getArchiveModules() ); -} -void initialise(){ - 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(); - } - - globalOutputStream() << "filesystem initialised\n"; - g_observers.realise(); -} -void shutdown(){ - g_observers.unrealise(); - globalOutputStream() << "filesystem shutdown\n"; - Shutdown(); -} + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + if ((*i).archive->containsFile(relative)) { + return (*i).name.c_str(); + } + } -int getFileCount( const char *filename, int flags ){ - return GetFileCount( filename, flags ); -} -ArchiveFile* openFile( const char* filename ){ - return OpenFile( filename ); + return ""; } -ArchiveTextFile* openTextFile( const char* filename ){ - return OpenTextFile( filename ); -} -std::size_t loadFile( const char *filename, void **buffer ){ - return LoadFile( filename, buffer, 0 ); -} -void freeFile( void *p ){ - FreeFile( p ); -} - -void forEachDirectory( const char* basedir, const FileNameCallback& callback, std::size_t depth ){ - GSList* list = GetDirList( basedir, depth ); - - for ( GSList* i = list; i != 0; i = g_slist_next( i ) ) - { - callback( reinterpret_cast( ( *i ).data ) ); - } - ClearFileDirList( &list ); -} -void forEachFile( const char* basedir, const char* extension, const FileNameCallback& callback, std::size_t depth ){ - GSList* list = GetFileList( basedir, extension, depth ); - - for ( GSList* i = list; i != 0; i = g_slist_next( i ) ) - { - const char* name = reinterpret_cast( ( *i ).data ); - if ( extension_equal( path_get_extension( name ), extension ) ) { - callback( name ); - } - } - - ClearFileDirList( &list ); -} -GSList* getDirList( const char *basedir ){ - return GetDirList( basedir, 1 ); -} -GSList* getFileList( const char *basedir, const char *extension ){ - return GetFileList( basedir, extension, 1 ); -} -void clearFileDirList( GSList **lst ){ - ClearFileDirList( lst ); -} +const char *FindPath(const char *absolute) +{ + const char *best = ""; + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + if (string_length((*i).name.c_str()) > string_length(best)) { + if (path_equal_n(absolute, (*i).name.c_str(), string_length((*i).name.c_str()))) { + best = (*i).name.c_str(); + } + } + } -const char* findFile( const char *name ){ - return FindFile( name ); -} -const char* findRoot( const char *name ){ - return FindPath( name ); + return best; } -void attach( ModuleObserver& observer ){ - g_observers.attach( observer ); -} -void detach( ModuleObserver& observer ){ - g_observers.detach( observer ); -} -Archive* getArchive( const char* archiveName, bool pakonly ){ - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - if ( pakonly && !( *i ).is_pakfile ) { - continue; - } - - if ( path_equal( ( *i ).name.c_str(), archiveName ) ) { - return ( *i ).archive; - } - } - return 0; -} -void forEachArchive( const ArchiveNameCallback& callback, bool pakonly, bool reverse ){ - if ( reverse ) { - g_archives.reverse(); - } - - for ( archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i ) - { - if ( pakonly && !( *i ).is_pakfile ) { - continue; - } - - callback( ( *i ).name.c_str() ); - } - - if ( reverse ) { - g_archives.reverse(); - } -} +class Quake3FileSystem : public VirtualFileSystem { +public: + 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(); + + // 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"; + Shutdown(); + } + + int getFileCount(const char *filename, int flags) + { + return GetFileCount(filename, flags); + } + + ArchiveFile *openFile(const char *filename) + { + return OpenFile(filename); + } + + ArchiveTextFile *openTextFile(const char *filename) + { + return OpenTextFile(filename); + } + + std::size_t loadFile(const char *filename, void **buffer) + { + return LoadFile(filename, buffer, 0); + } + + void freeFile(void *p) + { + FreeFile(p); + } + + void forEachDirectory(const char *basedir, const FileNameCallback &callback, std::size_t depth) + { + GSList *list = GetDirList(basedir, depth); + + for (GSList *i = list; i != 0; i = g_slist_next(i)) { + callback(reinterpret_cast((*i).data )); + } + + ClearFileDirList(&list); + } + + void forEachFile(const char *basedir, const char *extension, const FileNameCallback &callback, std::size_t depth) + { + GSList *list = GetFileList(basedir, extension, depth); + + for (GSList *i = list; i != 0; i = g_slist_next(i)) { + const char *name = reinterpret_cast((*i).data ); + if (extension_equal(path_get_extension(name), extension)) { + callback(name); + } + } + + ClearFileDirList(&list); + } + + GSList *getDirList(const char *basedir) + { + return GetDirList(basedir, 1); + } + + GSList *getFileList(const char *basedir, const char *extension) + { + return GetFileList(basedir, extension, 1); + } + + void clearFileDirList(GSList **lst) + { + ClearFileDirList(lst); + } + + const char *findFile(const char *name) + { + return FindFile(name); + } + + const char *findRoot(const char *name) + { + return FindPath(name); + } + + void attach(ModuleObserver &observer) + { + g_observers.attach(observer); + } + + void detach(ModuleObserver &observer) + { + g_observers.detach(observer); + } + + Archive *getArchive(const char *archiveName, bool pakonly) + { + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + if (pakonly && !(*i).is_pakfile) { + continue; + } + + if (path_equal((*i).name.c_str(), archiveName)) { + return (*i).archive; + } + } + return 0; + } + + void forEachArchive(const ArchiveNameCallback &callback, bool pakonly, bool reverse) + { + if (reverse) { + g_archives.reverse(); + } + + for (archives_t::iterator i = g_archives.begin(); i != g_archives.end(); ++i) { + if (pakonly && !(*i).is_pakfile) { + continue; + } + + callback((*i).name.c_str()); + } + + if (reverse) { + g_archives.reverse(); + } + } }; Quake3FileSystem g_Quake3FileSystem; -VirtualFileSystem& GetFileSystem(){ - return g_Quake3FileSystem; +VirtualFileSystem &GetFileSystem() +{ + return g_Quake3FileSystem; } -void FileSystem_Init(){ +void FileSystem_Init() +{ } -void FileSystem_Shutdown(){ +void FileSystem_Shutdown() +{ }