diff --git a/Core/GameEngine/Include/Common/ArchiveFileSystem.h b/Core/GameEngine/Include/Common/ArchiveFileSystem.h index f323b0f829..011e63bc43 100644 --- a/Core/GameEngine/Include/Common/ArchiveFileSystem.h +++ b/Core/GameEngine/Include/Common/ArchiveFileSystem.h @@ -53,6 +53,7 @@ #include "Common/AsciiString.h" #include "Common/FileSystem.h" // for typedefs, etc. #include "Common/STLTypedefs.h" +#include "mutex.h" //---------------------------------------------------------------------------- // Forward References @@ -169,6 +170,7 @@ class ArchiveFileSystem : public SubsystemInterface ArchiveFileMap m_archiveFileMap; ArchivedDirectoryInfo m_rootDirectory; + mutable FastCriticalSectionClass m_archiveDirectoryMutex; ///< Protects access to m_rootDirectory and all nested directory/file structures }; diff --git a/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp b/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp index 77e9fde8df..c51ff8c51e 100644 --- a/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp +++ b/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp @@ -117,6 +117,7 @@ ArchiveFileSystem::~ArchiveFileSystem() void ArchiveFileSystem::loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite) { + FastCriticalSectionClass::LockClass lock(m_archiveDirectoryMutex); FilenameList filenameList; @@ -240,6 +241,8 @@ void ArchiveFileSystem::loadMods() Bool ArchiveFileSystem::doesFileExist(const Char *filename, FileInstance instance) const { + FastCriticalSectionClass::LockClass lock(m_archiveDirectoryMutex); + ArchivedDirectoryInfoResult result = const_cast(this)->getArchivedDirectoryInfo(filename); if (!result.valid()) @@ -252,6 +255,8 @@ Bool ArchiveFileSystem::doesFileExist(const Char *filename, FileInstance instanc ArchivedDirectoryInfo* ArchiveFileSystem::friend_getArchivedDirectoryInfo(const Char* directory) { + FastCriticalSectionClass::LockClass lock(m_archiveDirectoryMutex); + ArchivedDirectoryInfoResult result = getArchivedDirectoryInfo(directory); return result.dirInfo; @@ -319,6 +324,8 @@ Bool ArchiveFileSystem::getFileInfo(const AsciiString& filename, FileInfo *fileI ArchiveFile* ArchiveFileSystem::getArchiveFile(const AsciiString& filename, FileInstance instance) const { + FastCriticalSectionClass::LockClass lock(m_archiveDirectoryMutex); + ArchivedDirectoryInfoResult result = const_cast(this)->getArchivedDirectoryInfo(filename.str()); if (!result.valid()) diff --git a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp index 0b4ab05e73..c6d82ee7d1 100644 --- a/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp +++ b/Generals/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp @@ -462,9 +462,13 @@ void W3DFileSystem::reprioritizeTexturesBySize() // // Catered to specific game archives only. This ensures that user created archives are not included // for the re-prioritization of textures. +// +// TheSuperHackers @bugfix 02/02/2025 Adds mutex locking to protect multimap access from race conditions +// during concurrent file existence checks and texture reprioritization. //------------------------------------------------------------------------------------------------- void W3DFileSystem::reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo) { + // Note: This function is called with m_archiveDirectoryMutex already locked by friend_getArchivedDirectoryInfo const char* const superiorArchive = "Textures.big"; const char* const inferiorArchive = "TexturesZH.big"; diff --git a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp index ad713be228..b913f5b2d7 100644 --- a/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp +++ b/GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp @@ -489,9 +489,13 @@ void W3DFileSystem::reprioritizeTexturesBySize() // // Catered to specific game archives only. This ensures that user created archives are not included // for the re-prioritization of textures. +// +// TheSuperHackers @bugfix 02/02/2025 Adds mutex locking to protect multimap access from race conditions +// during concurrent file existence checks and texture reprioritization. //------------------------------------------------------------------------------------------------- void W3DFileSystem::reprioritizeTexturesBySize(ArchivedDirectoryInfo& dirInfo) { + // Note: This function is called with m_archiveDirectoryMutex already locked by friend_getArchivedDirectoryInfo const char* const superiorArchive = "Textures.big"; const char* const inferiorArchive = "TexturesZH.big";