Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Core/GameEngine/Include/Common/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,12 @@ class FileSystem : public SubsystemInterface
static bool removeExtension(AsciiString& path);
static bool removeExtension(UnicodeString& path);

static AsciiString normalizePathSeparators(const AsciiString& path); ///< converts game-data path separators to the native separator.

/// Appends the native separator to nonempty paths unless either separator is already at the end.
/// Existing separators are not converted.
static void appendPathSeparator(AsciiString& path);

protected:
#if ENABLE_FILESYSTEM_EXISTENCE_CACHE
struct FileExistData
Expand Down
3 changes: 2 additions & 1 deletion Core/GameEngine/Source/Common/INI/INIMapCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include "Lib/BaseType.h"
#include "Common/INI.h"
#include "Common/FileSystem.h"
#include "GameClient/MapUtil.h"
#include "GameClient/GameText.h"
#include "GameNetwork/NetworkDefs.h"
Expand Down Expand Up @@ -194,7 +195,7 @@ void INI::parseMapCacheDefinition( INI* ini )

if(TheMapCache && !md.m_displayName.isEmpty())
{
AsciiString lowerName = name;
AsciiString lowerName = FileSystem::normalizePathSeparators(name);
lowerName.toLower();
md.m_fileName = lowerName;
// DEBUG_LOG(("INI::parseMapCacheDefinition - adding %s to map cache", lowerName.str()));
Expand Down
35 changes: 29 additions & 6 deletions Core/GameEngine/Source/Common/System/FileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,9 @@ Bool FileSystem::isPathInDirectory(const AsciiString& testPath, const AsciiStrin
return false;
}

#ifdef _WIN32
const char* pathSep = "\\";
#else
const char* pathSep = "/";
#endif
const char pathSep = getNativePathSeparator();

if (!basePathNormalized.endsWith(pathSep))
if (basePathNormalized.getCharAt(basePathNormalized.getLength() - 1) != pathSep)
{
basePathNormalized.concat(pathSep);
}
Expand Down Expand Up @@ -408,3 +404,30 @@ bool FileSystem::removeExtension(UnicodeString& path)

return false;
}

//============================================================================
// FileSystem::normalizePathSeparators
//============================================================================
AsciiString FileSystem::normalizePathSeparators(const AsciiString& path)
{
const char otherSeparator = getNativePathSeparator() == '/' ? '\\' : '/';
if (path.find(otherSeparator) == nullptr)
{
return path;
}

AsciiString normalized;
::normalizePathSeparators(normalized.getBufferForRead(path.getLength()), path.str());
return normalized;
}

//============================================================================
// FileSystem::appendPathSeparator
//============================================================================
void FileSystem::appendPathSeparator(AsciiString& path)
{
if (path.isNotEmpty() && !isPathSeparator(path.getCharAt(path.getLength() - 1)))
{
path.concat(getNativePathSeparator());
}
}
3 changes: 2 additions & 1 deletion Core/GameEngine/Source/Common/UserPreferences.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
//-----------------------------------------------------------------------------
#include "Common/GameSpyMiscPreferences.h"
#include "Common/UserPreferences.h"
#include "Common/FileSystem.h"
#include "Common/LadderPreferences.h"
#include "Common/Player.h"
#include "Common/PlayerTemplate.h"
Expand Down Expand Up @@ -668,7 +669,7 @@ AsciiString CustomMatchPreferences::getPreferredMap()
return ret;
}

ret = QuotedPrintableToAsciiString(it->second);
ret = FileSystem::normalizePathSeparators(QuotedPrintableToAsciiString(it->second));
ret.trim();
if (ret.isEmpty() || !isValidMap(ret, TRUE))
{ //map is invalid, use default instead
Expand Down
26 changes: 9 additions & 17 deletions Core/GameEngine/Source/GameClient/MapUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ Bool MapCache::loadMapsFromDisk( const AsciiString &mapDir, Bool isOfficial, Boo
continue;
}

mapListChanged |= addMap(mapDir, *filepathIt, filepathLower, fileInfo, isOfficial);
mapListChanged |= addMap(mapDir, *filepathIt, FileSystem::normalizePathSeparators(filepathLower), fileInfo, isOfficial);
}

if (clearUnseenMaps(mapDir))
Expand Down Expand Up @@ -1012,17 +1012,8 @@ Bool isValidMap( AsciiString mapName, Bool isMultiplayer )
return FALSE;
TheMapCache->updateCache();

mapName.toLower();
MapCache::iterator it = TheMapCache->find(mapName);
if (it != TheMapCache->end())
{
if (isMultiplayer == it->second.m_isMultiplayer)
{
return TRUE;
}
}

return FALSE;
const MapMetaData *mapData = TheMapCache->findMap(mapName);
return mapData != nullptr && isMultiplayer == mapData->m_isMultiplayer;
}

//-------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -1070,17 +1061,18 @@ Bool isOfficialMap( AsciiString mapName )
if(!TheMapCache || mapName.isEmpty())
return FALSE;
TheMapCache->updateCache();
mapName.toLower();
MapCache::iterator it = TheMapCache->find(mapName);
if (it != TheMapCache->end())
return it->second.m_isOfficial;
return FALSE;
const MapMetaData *mapData = TheMapCache->findMap(mapName);
return mapData != nullptr && mapData->m_isOfficial;
}


const MapMetaData *MapCache::findMap(AsciiString mapName)
{
mapName.toLower();

// TheSuperHackers @bugfix bobtista 14/09/2026 Use the same separators for cache keys and lookups.
mapName = FileSystem::normalizePathSeparators(mapName);

MapCache::iterator it = find(mapName);
if (it == end())
return nullptr;
Expand Down
55 changes: 50 additions & 5 deletions Core/Libraries/Include/Lib/PathUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
#include "BaseType.h"
#include <string.h>

inline bool isPathSeparator(char ch)
// Returns true for a separator the host platform uses to open files.
inline bool isNativePathSeparator(char ch)
{
#ifdef _WIN32
return ch == '\\' || ch == '/';
Expand All @@ -32,21 +33,27 @@ inline bool isPathSeparator(char ch)
#endif
}

// Returns true for either separator. Game data paths carry '\\' on every platform.
inline bool isPathSeparator(char ch)
{
return ch == '/' || ch == '\\';
}

inline bool isAbsolutePath(const char* path)
{
if (path == nullptr)
{
return false;
}

if (isPathSeparator(path[0]))
if (isNativePathSeparator(path[0]))
{
return true;
}

#ifdef _WIN32
const bool hasDriveLetter = (path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z');
if (hasDriveLetter && path[1] == ':' && isPathSeparator(path[2]))
if (hasDriveLetter && path[1] == ':' && isNativePathSeparator(path[2]))
{
return true;
}
Expand All @@ -55,6 +62,44 @@ inline bool isAbsolutePath(const char* path)
return false;
}

inline char getNativePathSeparator()
{
#ifdef _WIN32
return '\\';
#else
return '/';
#endif
}

// Copies a game-data path using native separators. Output needs strlen(path) + 1 bytes.
inline void normalizePathSeparators(char* output, const char* path)
{
const char separator = getNativePathSeparator();
while (*path)
{
*output++ = isPathSeparator(*path) ? separator : *path;
++path;
}
*output = 0;
}

inline const char* getLastPathSeparator(const char* path)
{
return path ? maxPtr(strrchr(path, '/'), strrchr(path, '\\')) : nullptr;
}

inline const wchar_t* getLastPathSeparator(const wchar_t* path)
{
return path ? maxPtr(wcsrchr(path, L'/'), wcsrchr(path, L'\\')) : nullptr;
}

// 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, '.');
Expand All @@ -64,7 +109,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)
Expand All @@ -84,7 +129,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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AsciiString, MapMetaData>::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.
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<AsciiString, MapMetaData>::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.
}
Expand Down
Loading