From a4fa456b8af1847453a3dcf12212429fa319b277 Mon Sep 17 00:00:00 2001 From: tintinhamans <5984296+tintinhamans@users.noreply.github.com> Date: Fri, 7 Aug 2026 09:56:05 +0200 Subject: [PATCH] Enhance ArchiveFileSystem: Add sortedByName option to loadIntoDirectoryTree and refactor community patch loading logic Signed-off-by: tintinhamans <5984296+tintinhamans@users.noreply.github.com> --- .../Include/Common/ArchiveFileSystem.h | 2 +- .../Common/System/ArchiveFileSystem.cpp | 40 ++++++++++++++++--- .../GameEngine/Source/Common/GameEngine.cpp | 6 +-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Core/GameEngine/Include/Common/ArchiveFileSystem.h b/Core/GameEngine/Include/Common/ArchiveFileSystem.h index af2321d4e25..48d144484c6 100644 --- a/Core/GameEngine/Include/Common/ArchiveFileSystem.h +++ b/Core/GameEngine/Include/Common/ArchiveFileSystem.h @@ -162,7 +162,7 @@ class ArchiveFileSystem : public SubsystemInterface ArchivedDirectoryInfoResult getArchivedDirectoryInfo(const Char* directory); - virtual void loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite = FALSE); ///< load the archive file's header information and apply it to the global archive directory tree. + virtual void loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite = FALSE, Bool sortedByName = FALSE); ///< load the archive file's header information and apply it to the global archive directory tree. sortedByName inserts by archive filename order (addon number convention), ignoring overwrite. ArchiveFileMap m_archiveFileMap; ArchivedDirectoryInfo m_rootDirectory; diff --git a/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp b/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp index f8b6aecbf0c..5d88922554c 100644 --- a/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp +++ b/Core/GameEngine/Source/Common/System/ArchiveFileSystem.cpp @@ -49,6 +49,7 @@ #include "Common/ArchiveFile.h" #include "Common/ArchiveFileSystem.h" #include "Common/AsciiString.h" +#include "Common/LocalFileSystem.h" #include "Common/PerfTimer.h" #include "../NGMP_include.h" #include "../OnlineServices_Init.h" @@ -94,6 +95,15 @@ ArchiveFileSystem *TheArchiveFileSystem = nullptr; // Private Functions //---------------------------------------------------------------------------- +static AsciiString getBaseFilename(const AsciiString& path) +{ + const char* str = path.str(); + const char* p1 = strrchr(str, '\\'); + const char* p2 = strrchr(str, '/'); + const char* sep = (p1 == nullptr) ? p2 : ((p2 == nullptr) ? p1 : ((p1 > p2) ? p1 : p2)); + return sep ? AsciiString(sep + 1) : path; +} + //---------------------------------------------------------------------------- @@ -117,7 +127,7 @@ ArchiveFileSystem::~ArchiveFileSystem() } } -void ArchiveFileSystem::loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite) +void ArchiveFileSystem::loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool overwrite, Bool sortedByName) { FilenameList filenameList; @@ -157,7 +167,16 @@ void ArchiveFileSystem::loadIntoDirectoryTree(ArchiveFile *archiveFile, Bool ove } ArchivedFileLocationMap::iterator fileIt; - if (overwrite) + if (sortedByName) + { + // Insert by case-insensitive archive filename, matching game folder load order where the alphabetically first archive wins. + const AsciiString baseName = getBaseFilename(archiveFile->getName()); + std::pair range = dirInfo->m_files.equal_range(token); + fileIt = range.first; + while (fileIt != range.second && getBaseFilename(fileIt->second->getName()).compareNoCase(baseName) <= 0) + ++fileIt; + } + else if (overwrite) { // When overwriting, try place the new value at the beginning of the key list. fileIt = dirInfo->m_files.find(token); @@ -218,9 +237,20 @@ void ArchiveFileSystem::loadMods() // load community data patch BIG if (NGMP_OnlineServicesManager::Settings.DataPacks_UseCommunityPatch()) { - std::string strSettingsFileDir = std::format("{}/GeneralsOnlineGameData/", TheGlobalData->getPath_UserData().str()); - bool bLoaded = TheArchiveFileSystem->loadBigFilesFromDirectory(strSettingsFileDir.c_str(), "500_900_CommunityPatch_CoreINI.big", TRUE); - NetworkLog(ELogVerbosity::LOG_RELEASE, "Loaded community patch: %d", bLoaded); + std::string strBigPath = std::format("{}GeneralsOnlineGameData/500_900_CommunityPatch_CoreINI.big", TheGlobalData->getPath_UserData().str()); + bool bLoaded = false; + if (TheLocalFileSystem->doesFileExist(strBigPath.c_str())) + { + ArchiveFile* archiveFile = openArchiveFile(strBigPath.c_str()); + if (archiveFile != nullptr) + { + // Sorted by filename so the patch respects the addon number order of BIGs in the game folder. + loadIntoDirectoryTree(archiveFile, FALSE, TRUE); + m_archiveFileMap[AsciiString(strBigPath.c_str())] = archiveFile; + bLoaded = true; + } + } + NetworkLog(ELogVerbosity::LOG_RELEASE, "Loaded community patch (%s): %d", strBigPath.c_str(), bLoaded); } #endif diff --git a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp index 8b2f1914f77..2c2e19c65a0 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp @@ -528,6 +528,9 @@ void GameEngine::init() // special-case: parse command-line parameters after loading global data CommandLine::parseCommandLineForEngineInit(); + // NGMP_CHANGE: Init our settings before loadMods, which reads DataPacks_UseCommunityPatch. Needs TheGlobalData for the user data path. + NGMP_OnlineServicesManager::Settings.Initialize(); + TheArchiveFileSystem->loadMods(); // doesn't require resets so just create a single instance here. @@ -826,9 +829,6 @@ void GameEngine::init() HideControlBar(); - // NGMP_CHANGE: Init our settings - NGMP_OnlineServicesManager::Settings.Initialize(); - m_discordRichPresence = new GeneralsOnlineDiscordRPC(); m_discordRichPresence->Initialize(); }