Skip to content

Commit 6748e68

Browse files
authored
feat(filesystem): Implement access to shadowed files inside archives (#1483)
1 parent 08e59cb commit 6748e68

File tree

12 files changed

+362
-218
lines changed

12 files changed

+362
-218
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, FileInstance 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, FileInstance 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, FileInstance 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, FileInstance 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: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666

6767
typedef std::set<AsciiString, rts::less_than_nocase<AsciiString> > FilenameList;
6868
typedef FilenameList::iterator FilenameListIter;
69+
typedef UnsignedByte FileInstance;
6970

7071
//----------------------------------------------------------------------------
7172
// Type Defines
@@ -80,7 +81,7 @@ typedef FilenameList::iterator FilenameListIter;
8081
#define USER_W3D_DIR_PATH "%sW3D/" ///< .w3d files live here
8182
#define USER_TGA_DIR_PATH "%sTextures/" ///< User .tga texture files live here
8283

83-
// the following defines are only to be used while maintaining legacy compatability
84+
// the following defines are only to be used while maintaining legacy compatibility
8485
// with old files until they are completely gone and in the regular art set
8586
#ifdef MAINTAIN_LEGACY_FILES
8687
#define LEGACY_W3D_DIR_PATH "../LegacyArt/W3D/" ///< .w3d files live here
@@ -102,7 +103,16 @@ typedef FilenameList::iterator FilenameListIter;
102103
#define TEST_TGA_DIR_PATH "../TestArt/" ///< .tga texture files live here
103104
#endif
104105

106+
#ifndef ENABLE_FILESYSTEM_LOGGING
107+
#define ENABLE_FILESYSTEM_LOGGING (0)
108+
#endif
109+
110+
105111
struct FileInfo {
112+
113+
Int64 size() const { return (Int64)sizeHigh << 32 | sizeLow; }
114+
Int64 timestamp() const { return (Int64)timestampHigh << 32 | timestampLow; }
115+
106116
Int sizeHigh;
107117
Int sizeLow;
108118
Int timestampHigh;
@@ -115,11 +125,13 @@ struct FileInfo {
115125
/**
116126
* FileSystem is an interface class for creating specific FileSystem objects.
117127
*
118-
* A FileSystem object's implemenation decides what derivative of File object needs to be
128+
* A FileSystem object's implementation decides what derivative of File object needs to be
119129
* created when FileSystem::Open() gets called.
120130
*/
131+
// TheSuperHackers @feature xezon 23/08/2025 Implements file instance access.
132+
// Can be used to access different versions of files in different archives under the same name.
133+
// Instance 0 refers to the top file that shadows all other files under the same name.
121134
//===============================
122-
123135
class FileSystem : public SubsystemInterface
124136
{
125137
FileSystem(const FileSystem&);
@@ -133,23 +145,30 @@ class FileSystem : public SubsystemInterface
133145
void reset();
134146
void update();
135147

136-
File* openFile( const Char *filename, Int access = File::NONE, size_t bufferSize = File::BUFFERSIZE ); ///< opens a File interface to the specified file
137-
Bool doesFileExist(const Char *filename) const; ///< returns TRUE if the file exists. filename should have no directory.
148+
File* openFile( const Char *filename, Int access = File::NONE, size_t bufferSize = File::BUFFERSIZE, FileInstance instance = 0 ); ///< opens a File interface to the specified file
149+
Bool doesFileExist(const Char *filename, FileInstance instance = 0) const; ///< returns TRUE if the file exists. filename should have no directory.
138150
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.
139-
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo) const; ///< fills in the FileInfo struct for the file given. returns TRUE if successful.
151+
Bool getFileInfo(const AsciiString& filename, FileInfo *fileInfo, FileInstance instance = 0) const; ///< fills in the FileInfo struct for the file given. returns TRUE if successful.
140152

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

143155
Bool areMusicFilesOnCD();
144156
void loadMusicFilesFromCD();
145157
void unloadMusicFilesFromCD();
146-
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.
158+
159+
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.
147160
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.
148161

149162
protected:
150163
#if ENABLE_FILESYSTEM_EXISTENCE_CACHE
164+
struct FileExistData
165+
{
166+
FileExistData() : instanceExists(0), instanceDoesNotExist(~FileInstance(0)) {}
167+
FileInstance instanceExists;
168+
FileInstance instanceDoesNotExist;
169+
};
151170
typedef std::hash_map<
152-
rts::string_key<AsciiString>, bool,
171+
rts::string_key<AsciiString>, FileExistData,
153172
rts::string_key_hash<AsciiString>,
154173
rts::string_key_equal<AsciiString> > FileExistMap;
155174
mutable FileExistMap m_fileExist;

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)