diff --git a/Core/GameEngine/Source/GameClient/MapUtil.cpp b/Core/GameEngine/Source/GameClient/MapUtil.cpp index 1806c50741a..d56bfee2ad9 100644 --- a/Core/GameEngine/Source/GameClient/MapUtil.cpp +++ b/Core/GameEngine/Source/GameClient/MapUtil.cpp @@ -60,6 +60,7 @@ #include "GameLogic/FPUControl.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/NetworkDefs.h" +#include "Lib/PathUtil.h" //------------------------------------------------------------------------------- @@ -1078,9 +1079,41 @@ Bool isOfficialMap( AsciiString mapName ) } +static AsciiString normalizePathSeparators( const AsciiString &path ) +{ + const char nativeSeparator = getNativePathSeparator(); + const char *src = path.str(); + + for (; *src != 0; ++src) + { + if (isPathSeparator(*src) && *src != nativeSeparator) + { + break; + } + } + + if (*src == 0) + { + return path; + } + + AsciiString normalized; + for (src = path.str(); *src != 0; ++src) + { + normalized.concat(isPathSeparator(*src) ? nativeSeparator : *src); + } + + return normalized; +} + const MapMetaData *MapCache::findMap(AsciiString mapName) { mapName.toLower(); + + // TheSuperHackers @bugfix bobtista 28/08/2026 Match whichever separator the cache keys were + // built with, so a name stored with the other separator still resolves + mapName = normalizePathSeparators(mapName); + MapCache::iterator it = find(mapName); if (it == end()) return nullptr; diff --git a/Core/Libraries/Include/Lib/PathUtil.h b/Core/Libraries/Include/Lib/PathUtil.h index cf1ce769d91..33c9b5a3c2b 100644 --- a/Core/Libraries/Include/Lib/PathUtil.h +++ b/Core/Libraries/Include/Lib/PathUtil.h @@ -23,6 +23,38 @@ #include "BaseType.h" #include +inline char getNativePathSeparator() +{ +#ifdef _WIN32 + return '\\'; +#else + return '/'; +#endif +} + +inline Bool isPathSeparator(char c) +{ + return c == '/' || c == '\\'; +} + +inline const char* getLastPathSeparator(const char* path) +{ + return maxPtr(strrchr(path, '/'), strrchr(path, '\\')); +} + +inline const wchar_t* getLastPathSeparator(const wchar_t* path) +{ + return maxPtr(wcsrchr(path, L'/'), wcsrchr(path, L'\\')); +} + +// Returns the whole path when it contains no separator +inline const char* getFileName(const char* path) +{ + const char* lastSeparator = getLastPathSeparator(path); + + return lastSeparator ? lastSeparator + 1 : path; +} + inline const char* getExtension(const char* path) { const char* lastDot = strrchr(path, '.'); @@ -32,7 +64,7 @@ inline const char* getExtension(const char* path) return nullptr; } - const char* lastSeparator = maxPtr(strrchr(path, '/'), strrchr(path, '\\')); + const char* lastSeparator = getLastPathSeparator(path); // Check if the dot is contained in the filename if (lastSeparator && lastDot < lastSeparator) @@ -52,7 +84,7 @@ inline const wchar_t* getExtension(const wchar_t* path) return nullptr; } - const wchar_t* lastSeparator = maxPtr(wcsrchr(path, L'/'), wcsrchr(path, L'\\')); + const wchar_t* lastSeparator = getLastPathSeparator(path); // Check if the dot is contained in the filename if (lastSeparator && lastDot < lastSeparator) diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp index 0520332611d..232c71d563f 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -780,15 +780,18 @@ void LanGameOptionsMenuInit( WindowLayout *layout, void *userData ) slot->setColor( pref.getPreferredColor() ); slot->setPlayerTemplate( pref.getPreferredFaction() ); slot->setNATBehavior(FirewallHelperClass::FIREWALL_TYPE_SIMPLE); - game->setMap( pref.getPreferredMap() ); - AsciiString lowerMap = pref.getPreferredMap(); - lowerMap.toLower(); - std::map::iterator it = TheMapCache->find(lowerMap); - if (it != TheMapCache->end()) + AsciiString mapName = pref.getPreferredMap(); + const MapMetaData *mapData = TheMapCache->findMap(mapName); + if (mapData != nullptr) + { + mapName = mapData->m_fileName; + } + game->setMap(mapName); + if (mapData != nullptr) { TheLAN->GetMyGame()->getSlot(0)->setMapAvailability(true); - TheLAN->GetMyGame()->setMapCRC( it->second.m_CRC ); - TheLAN->GetMyGame()->setMapSize( it->second.m_filesize ); + TheLAN->GetMyGame()->setMapCRC( mapData->m_CRC ); + TheLAN->GetMyGame()->setMapSize( mapData->m_filesize ); TheLAN->GetMyGame()->adjustSlotsForMap(); // BGC- adjust the slots for the selected map. } diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp index 609a0f772cd..9ed2aae7e9c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -856,17 +856,20 @@ void LanGameOptionsMenuInit( WindowLayout *layout, void *userData ) slot->setColor( pref.getPreferredColor() ); slot->setPlayerTemplate( pref.getPreferredFaction() ); slot->setNATBehavior(FirewallHelperClass::FIREWALL_TYPE_SIMPLE); - game->setMap( pref.getPreferredMap() ); + AsciiString mapName = pref.getPreferredMap(); + const MapMetaData *mapData = TheMapCache->findMap(mapName); + if (mapData != nullptr) + { + mapName = mapData->m_fileName; + } + game->setMap(mapName); game->setStartingCash( pref.getStartingCash() ); game->setSuperweaponRestriction( pref.getSuperweaponRestricted() ? 1 : 0 ); - AsciiString lowerMap = pref.getPreferredMap(); - lowerMap.toLower(); - std::map::iterator it = TheMapCache->find(lowerMap); - if (it != TheMapCache->end()) + if (mapData != nullptr) { TheLAN->GetMyGame()->getSlot(0)->setMapAvailability(true); - TheLAN->GetMyGame()->setMapCRC( it->second.m_CRC ); - TheLAN->GetMyGame()->setMapSize( it->second.m_filesize ); + TheLAN->GetMyGame()->setMapCRC( mapData->m_CRC ); + TheLAN->GetMyGame()->setMapSize( mapData->m_filesize ); TheLAN->GetMyGame()->adjustSlotsForMap(); // BGC- adjust the slots for the selected map. }