Skip to content

Commit 37f1631

Browse files
committed
feat(filesystem): Implement access to shadowed files inside archives
1 parent a4a57a2 commit 37f1631

File tree

12 files changed

+356
-217
lines changed

12 files changed

+356
-217
lines changed

Core/GameEngine/Include/Common/ArchiveFileSystem.h

Lines changed: 33 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,19 @@ class ArchivedDirectoryInfo;
8484
class DetailedArchivedDirectoryInfo;
8585
class ArchivedFileInfo;
8686

87-
typedef std::map<AsciiString, DetailedArchivedDirectoryInfo> DetailedArchivedDirectoryInfoMap;
88-
typedef std::map<AsciiString, ArchivedDirectoryInfo> ArchivedDirectoryInfoMap;
89-
typedef std::map<AsciiString, ArchivedFileInfo> ArchivedFileInfoMap;
90-
typedef std::map<AsciiString, ArchiveFile *> ArchiveFileMap;
91-
typedef std::map<AsciiString, AsciiString> ArchivedFileLocationMap; // first string is the file name, second one is the archive filename.
87+
typedef std::map<AsciiString, DetailedArchivedDirectoryInfo> DetailedArchivedDirectoryInfoMap; // Archived directory name to detailed archived directory info
88+
typedef std::map<AsciiString, ArchivedDirectoryInfo> ArchivedDirectoryInfoMap; // Archived directory name to archived directory info
89+
typedef std::map<AsciiString, ArchivedFileInfo> ArchivedFileInfoMap; // Archived file name to archived file info
90+
typedef std::map<AsciiString, ArchiveFile *> ArchiveFileMap; // Archive file name to archive data
91+
typedef std::multimap<AsciiString, ArchiveFile *> ArchivedFileLocationMap; // Archived file name to archive data
9292

9393
class ArchivedDirectoryInfo
9494
{
9595
public:
96-
AsciiString m_directoryName;
97-
ArchivedDirectoryInfoMap m_directories;
98-
ArchivedFileLocationMap m_files;
99-
100-
void clear()
101-
{
102-
m_directoryName.clear();
103-
m_directories.clear();
104-
m_files.clear();
105-
}
106-
96+
AsciiString m_path; // The full path to this directory
97+
AsciiString m_directoryName; // The current directory
98+
ArchivedDirectoryInfoMap m_directories; // Contained leaf directories
99+
ArchivedFileLocationMap m_files; // Contained files
107100
};
108101

109102
class DetailedArchivedDirectoryInfo
@@ -112,13 +105,6 @@ class DetailedArchivedDirectoryInfo
112105
AsciiString m_directoryName;
113106
DetailedArchivedDirectoryInfoMap m_directories;
114107
ArchivedFileInfoMap m_files;
115-
116-
void clear()
117-
{
118-
m_directoryName.clear();
119-
m_directories.clear();
120-
m_files.clear();
121-
}
122108
};
123109

124110
class ArchivedFileInfo
@@ -130,23 +116,16 @@ class ArchivedFileInfo
130116
UnsignedInt m_size;
131117

132118
ArchivedFileInfo()
119+
: m_offset(0)
120+
, m_size(0)
133121
{
134-
clear();
135-
}
136-
137-
void clear()
138-
{
139-
m_filename.clear();
140-
m_archiveFilename.clear();
141-
m_offset = 0;
142-
m_size = 0;
143122
}
144123
};
145124

146125

147126
class ArchiveFileSystem : public SubsystemInterface
148127
{
149-
public:
128+
public:
150129
ArchiveFileSystem();
151130
virtual ~ArchiveFileSystem();
152131

@@ -158,24 +137,38 @@ class ArchiveFileSystem : public SubsystemInterface
158137
// ArchiveFile operations
159138
virtual ArchiveFile* openArchiveFile( const Char *filename ) = 0; ///< Create new or return existing Archive file from file name
160139
virtual void closeArchiveFile( const Char *filename ) = 0; ///< Close the one specified big file.
161-
virtual void closeAllArchiveFiles( void ) = 0; ///< Close all Archivefiles currently open
140+
virtual void closeAllArchiveFiles( void ) = 0; ///< Close all Archive files currently open
162141

163142
// File operations
164-
virtual File* openFile( const Char *filename, Int access = 0); ///< Search Archive files for specified file name and open it if found
165-
virtual void closeAllFiles( void ) = 0; ///< Close all files associated with ArchiveFiles
166-
virtual Bool doesFileExist(const Char *filename) const; ///< return true if that file exists in an archive file somewhere.
143+
virtual File* openFile( const Char *filename, Int access = 0, UnsignedInt instance = 0); ///< Search Archive files for specified file name and open it if found
144+
virtual void closeAllFiles( void ) = 0; ///< Close all files associated with Archive files
145+
virtual Bool doesFileExist(const Char *filename, UnsignedInt instance = 0) const; ///< return true if that file exists in an archive file somewhere.
167146

168147
void getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const; ///< search the given directory for files matching the searchName (egs. *.ini, *.rep). Possibly search subdirectories. Scans each Archive file.
169-
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const; ///< see FileSystem.h
148+
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo, UnsignedInt instance = 0) const; ///< see FileSystem.h
170149

171150
virtual Bool loadBigFilesFromDirectory(AsciiString dir, AsciiString fileMask, Bool overwrite = FALSE) = 0;
172151

173152
// Unprotected this for copy-protection routines
174-
AsciiString getArchiveFilenameForFile(const AsciiString& filename) const;
153+
ArchiveFile* getArchiveFile(const AsciiString& filename, UnsignedInt instance = 0) const;
154+
175155
void loadMods( void );
176156

157+
ArchivedDirectoryInfo* friend_getArchivedDirectoryInfo(const Char* directory);
158+
177159
protected:
178-
virtual void loadIntoDirectoryTree(const ArchiveFile *archiveFile, const AsciiString& archiveFilename, Bool overwrite = FALSE ); ///< load the archive file's header information and apply it to the global archive directory tree.
160+
struct ArchivedDirectoryInfoResult
161+
{
162+
ArchivedDirectoryInfoResult() : dirInfo(NULL) {}
163+
Bool valid() const { return dirInfo != NULL; }
164+
165+
ArchivedDirectoryInfo* dirInfo;
166+
AsciiString lastToken; ///< Synonymous for file name if the search directory was a file path
167+
};
168+
169+
ArchivedDirectoryInfoResult getArchivedDirectoryInfo(const Char* directory);
170+
171+
virtual void loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite = FALSE); ///< load the archive file's header information and apply it to the global archive directory tree.
179172

180173
ArchiveFileMap m_archiveFileMap;
181174
ArchivedDirectoryInfo m_rootDirectory;

Core/GameEngine/Include/Common/FileSystem.h

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
#include "Common/STLTypedefs.h"
5555
#include "Common/SubsystemInterface.h"
5656

57+
#include <map>
58+
5759
//----------------------------------------------------------------------------
5860
// Forward References
5961
//----------------------------------------------------------------------------
@@ -98,7 +100,16 @@ typedef FilenameList::iterator FilenameListIter;
98100
#define TEST_TGA_DIR_PATH "../TestArt/" ///< .tga texture files live here
99101
#endif
100102

103+
#ifndef ENABLE_FILESYSTEM_LOGGING
104+
#define ENABLE_FILESYSTEM_LOGGING (0)
105+
#endif
106+
107+
101108
struct FileInfo {
109+
110+
Int64 size() const { return (Int64)sizeHigh << 32 | sizeLow; }
111+
Int64 timestamp() const { return (Int64)timestampHigh << 32 | timestampLow; }
112+
102113
Int sizeHigh;
103114
Int sizeLow;
104115
Int timestampHigh;
@@ -111,12 +122,13 @@ struct FileInfo {
111122
/**
112123
* FileSystem is an interface class for creating specific FileSystem objects.
113124
*
114-
* A FileSystem object's implemenation decides what derivative of File object needs to be
125+
* A FileSystem object's implementation decides what derivative of File object needs to be
115126
* created when FileSystem::Open() gets called.
116127
*/
128+
// TheSuperHackers @feature xezon 23/08/2025 Implements file instance access.
129+
// Can be used to access different versions of files in different archives under the same name.
130+
// Instance 0 refers to the top file that shadows all other files under the same name.
117131
//===============================
118-
#include <map>
119-
120132
class FileSystem : public SubsystemInterface
121133
{
122134
FileSystem(const FileSystem&);
@@ -130,22 +142,30 @@ class FileSystem : public SubsystemInterface
130142
void reset();
131143
void update();
132144

133-
File* openFile( const Char *filename, Int access = File::NONE, size_t bufferSize = File::BUFFERSIZE ); ///< opens a File interface to the specified file
134-
Bool doesFileExist(const Char *filename) const; ///< returns TRUE if the file exists. filename should have no directory.
145+
File* openFile( const Char *filename, Int access = File::NONE, size_t bufferSize = File::BUFFERSIZE, UnsignedInt instance = 0 ); ///< opens a File interface to the specified file
146+
Bool doesFileExist(const Char *filename, UnsignedInt instance = 0) const; ///< returns TRUE if the file exists. filename should have no directory.
135147
void getFileListInDirectory(const AsciiString& directory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const; ///< search the given directory for files matching the searchName (egs. *.ini, *.rep). Possibly search subdirectories.
136-
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const; ///< fills in the FileInfo struct for the file given. returns TRUE if successful.
148+
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo, UnsignedInt instance = 0) const; ///< fills in the FileInfo struct for the file given. returns TRUE if successful.
137149

138150
Bool createDirectory(AsciiString directory); ///< create a directory of the given name.
139151

140152
Bool areMusicFilesOnCD();
141153
void loadMusicFilesFromCD();
142154
void unloadMusicFilesFromCD();
143-
AsciiString normalizePath(const AsciiString& path) const; ///< normalizes a file path. The path can refer to a directory. File path must be absolute, but does not need to exist. Returns an empty string on failure.
155+
156+
static AsciiString normalizePath(const AsciiString& path); ///< normalizes a file path. The path can refer to a directory. File path must be absolute, but does not need to exist. Returns an empty string on failure.
144157
static Bool isPathInDirectory(const AsciiString& testPath, const AsciiString& basePath); ///< determines if a file path is within a base path. Both paths must be absolute, but do not need to exist.
145158

146159
protected:
147160
#if ENABLE_FILESYSTEM_EXISTENCE_CACHE
148-
mutable std::map<unsigned,bool> m_fileExist;
161+
struct FileExistData
162+
{
163+
FileExistData() : instanceExists(0), instanceDoesNotExist(~0u) {}
164+
UnsignedInt instanceExists;
165+
UnsignedInt instanceDoesNotExist;
166+
};
167+
typedef std::map<unsigned, FileExistData> FileExistMap; ///< Filename key to data
168+
mutable FileExistMap m_fileExist;
149169
#endif
150170
};
151171

Core/GameEngine/Include/Common/STLUtils.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,44 @@
2121
#include <Utility/CppMacros.h>
2222
#include <utility>
2323
#include <algorithm>
24+
#include <map>
2425

2526
namespace stl
2627
{
2728

29+
// Convenience struct to avoid the std::pair<iterator, iterator>
30+
template <typename Container>
31+
struct range
32+
{
33+
typedef typename Container::iterator iterator;
34+
35+
range(iterator b, iterator e) : begin(b), end(e) {}
36+
37+
iterator get() const { return begin; }
38+
bool valid() const { return begin != end; }
39+
ptrdiff_t distance() const { return std::distance(begin, end); }
40+
41+
iterator begin;
42+
iterator end;
43+
};
44+
45+
template <typename Container>
46+
struct const_range
47+
{
48+
typedef typename Container::const_iterator iterator;
49+
50+
const_range(iterator b, iterator e) : begin(b), end(e) {}
51+
const_range(const range<Container>& other) : begin(other.begin), end(other.end) {}
52+
53+
iterator get() const { return begin; }
54+
bool valid() const { return begin != end; }
55+
ptrdiff_t distance() const { return std::distance(begin, end); }
56+
57+
iterator begin;
58+
iterator end;
59+
};
60+
61+
2862
// Finds first matching element in vector-like container and erases it.
2963
template <typename Container>
3064
bool find_and_erase(Container& container, const typename Container::value_type& value)
@@ -73,4 +107,38 @@ bool push_back_unique(Container& container, const typename Container::value_type
73107
return false;
74108
}
75109

110+
111+
template <typename Iter>
112+
Iter advance_in_range(Iter first, Iter last, std::ptrdiff_t n)
113+
{
114+
if (n <= 0)
115+
return first;
116+
117+
const std::ptrdiff_t count = std::distance(first, last);
118+
119+
if (n >= count)
120+
return last;
121+
122+
std::advance(first, n);
123+
return first;
124+
}
125+
126+
template <typename Key, typename Val>
127+
range<std::multimap<Key, Val> > get_range(std::multimap<Key, Val>& mm, const Key& key, std::ptrdiff_t n = 0)
128+
{
129+
typedef typename std::multimap<Key, Val>::iterator Iter;
130+
const std::pair<Iter, Iter> pair = mm.equal_range(key);
131+
const Iter it = advance_in_range(pair.first, pair.second, n);
132+
return range<std::multimap<Key, Val> >(it, pair.second);
133+
}
134+
135+
template <typename Key, typename Val>
136+
const_range<std::multimap<Key, Val> > get_range(const std::multimap<Key, Val>& mm, const Key& key, std::ptrdiff_t n = 0)
137+
{
138+
typedef typename std::multimap<Key, Val>::const_iterator Iter;
139+
const std::pair<Iter, Iter> pair = mm.equal_range(key);
140+
const Iter it = advance_in_range(pair.first, pair.second, n);
141+
return const_range<std::multimap<Key, Val> >(it, pair.second);
142+
}
143+
76144
} // namespace stl

Core/GameEngine/Source/Common/System/ArchiveFile.cpp

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -90,50 +90,46 @@ ArchiveFile::~ArchiveFile()
9090
}
9191

9292
ArchiveFile::ArchiveFile()
93+
: m_file(NULL)
9394
{
94-
m_rootDirectory.clear();
9595
}
9696

9797
void ArchiveFile::addFile(const AsciiString& path, const ArchivedFileInfo *fileInfo)
9898
{
99-
AsciiString temp;
100-
temp = path;
101-
temp.toLower();
102-
AsciiString token;
103-
AsciiString debugpath;
104-
10599
DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
106100

107-
temp.nextToken(&token, "\\/");
101+
AsciiString token;
102+
AsciiString tokenizer = path;
103+
tokenizer.toLower();
104+
tokenizer.nextToken(&token, "\\/");
108105

109-
while (token.getLength() > 0) {
110-
if (dirInfo->m_directories.find(token) == dirInfo->m_directories.end())
106+
while (token.getLength() > 0)
107+
{
108+
DetailedArchivedDirectoryInfoMap::iterator tempiter = dirInfo->m_directories.find(token);
109+
if (tempiter == dirInfo->m_directories.end())
110+
{
111+
dirInfo = &(dirInfo->m_directories[token]);
112+
dirInfo->m_directoryName = token;
113+
}
114+
else
111115
{
112-
dirInfo->m_directories[token].clear();
113-
dirInfo->m_directories[token].m_directoryName = token;
116+
dirInfo = &tempiter->second;
114117
}
115118

116-
debugpath.concat(token);
117-
debugpath.concat('\\');
118-
dirInfo = &(dirInfo->m_directories[token]);
119-
temp.nextToken(&token, "\\/");
119+
tokenizer.nextToken(&token, "\\/");
120120
}
121121

122122
dirInfo->m_files[fileInfo->m_filename] = *fileInfo;
123-
//path.concat(fileInfo->m_filename);
124123
}
125124

126125
void ArchiveFile::getFileListInDirectory(const AsciiString& currentDirectory, const AsciiString& originalDirectory, const AsciiString& searchName, FilenameList &filenameList, Bool searchSubdirectories) const
127126
{
128-
129-
AsciiString searchDir;
130127
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
131128

132-
searchDir = originalDirectory;
133-
searchDir.toLower();
134129
AsciiString token;
135-
136-
searchDir.nextToken(&token, "\\/");
130+
AsciiString tokenizer = originalDirectory;
131+
tokenizer.toLower();
132+
tokenizer.nextToken(&token, "\\/");
137133

138134
while (token.getLength() > 0) {
139135

@@ -148,7 +144,7 @@ void ArchiveFile::getFileListInDirectory(const AsciiString& currentDirectory, co
148144
return;
149145
}
150146

151-
searchDir.nextToken(&token, "\\/");
147+
tokenizer.nextToken(&token, "\\/");
152148
}
153149

154150
getFileListInDirectory(dirInfo, originalDirectory, searchName, filenameList, searchSubdirectories);
@@ -198,17 +194,15 @@ void ArchiveFile::attachFile(File *file)
198194

199195
const ArchivedFileInfo * ArchiveFile::getArchivedFileInfo(const AsciiString& filename) const
200196
{
201-
AsciiString path;
202-
path = filename;
203-
path.toLower();
204-
AsciiString token;
205-
206197
const DetailedArchivedDirectoryInfo *dirInfo = &m_rootDirectory;
207198

208-
path.nextToken(&token, "\\/");
209-
210-
while ((token.find('.') == NULL) || (path.find('.') != NULL)) {
199+
AsciiString token;
200+
AsciiString tokenizer = filename;
201+
tokenizer.toLower();
202+
tokenizer.nextToken(&token, "\\/");
211203

204+
while (!token.find('.') || tokenizer.find('.'))
205+
{
212206
DetailedArchivedDirectoryInfoMap::const_iterator it = dirInfo->m_directories.find(token);
213207
if (it != dirInfo->m_directories.end())
214208
{
@@ -219,7 +213,7 @@ const ArchivedFileInfo * ArchiveFile::getArchivedFileInfo(const AsciiString& fil
219213
return NULL;
220214
}
221215

222-
path.nextToken(&token, "\\/");
216+
tokenizer.nextToken(&token, "\\/");
223217
}
224218

225219
ArchivedFileInfoMap::const_iterator it = dirInfo->m_files.find(token);

0 commit comments

Comments
 (0)