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
4 changes: 4 additions & 0 deletions Core/GameEngine/Include/Common/FileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ class FileSystem : public SubsystemInterface
static bool removeExtension(AsciiString& path);
static bool removeExtension(UnicodeString& path);

/// 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
1 change: 1 addition & 0 deletions Core/GameEngine/Include/GameClient/MapUtil.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ class MapCache : public std::map<AsciiString, MapMetaData>
void addShippingMap(AsciiString mapName) { mapName.toLower(); m_allowedMaps.insert(mapName); }

private:
static AsciiString getCachePath(const AsciiString &mapDir);
void prepareUnseenMaps(const AsciiString &mapDir);
Bool clearUnseenMaps(const AsciiString &mapDir);
void loadMapsFromMapCacheINI(const AsciiString &mapDir);
Expand Down
5 changes: 3 additions & 2 deletions Core/GameEngine/Source/Common/INI/INIMapCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine

#include "Lib/BaseType.h"
#include "Lib/PathUtil.h"
#include "Common/INI.h"
#include "GameClient/MapUtil.h"
#include "GameClient/GameText.h"
Expand Down Expand Up @@ -148,8 +149,8 @@ void INI::parseMapCacheDefinition( INI* ini )
if (md.m_nameLookupTag.isEmpty())
{
// maps without localized name tags
AsciiString tempdisplayname;
tempdisplayname = name.reverseFind('\\') + 1;
// TheSuperHackers @bugfix bobtista 14/09/2026 Handle map filenames with either separator or no separator.
AsciiString tempdisplayname = getFileName(name.str());
md.m_displayName.translate(tempdisplayname);
if (md.m_numPlayers >= 2)
{
Expand Down
10 changes: 4 additions & 6 deletions Core/GameEngine/Source/Common/System/ArchiveFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "Common/ArchiveFile.h"
#include "Common/ArchiveFileSystem.h"
#include "Common/FileSystem.h"
#include "Common/file.h"
#include "Common/PerfTimer.h"

Expand Down Expand Up @@ -157,9 +158,8 @@ void ArchiveFile::getFileListInDirectory(const DetailedArchivedDirectoryInfo *di
const DetailedArchivedDirectoryInfo *tempDirInfo = &(diriter->second);
AsciiString tempdirname;
tempdirname = currentDirectory;
if ((!tempdirname.isEmpty()) && (!tempdirname.endsWith("\\"))) {
tempdirname.concat('\\');
}
// TheSuperHackers @fix bobtista 14/08/2026 Avoid adding a separator when either separator is already present.
FileSystem::appendPathSeparator(tempdirname);
tempdirname.concat(tempDirInfo->m_directoryName);
getFileListInDirectory(tempDirInfo, tempdirname, searchName, filenameList, searchSubdirectories);
diriter++;
Expand All @@ -170,9 +170,7 @@ void ArchiveFile::getFileListInDirectory(const DetailedArchivedDirectoryInfo *di
if (SearchStringMatches(fileiter->second.m_filename, searchName)) {
AsciiString tempfilename;
tempfilename = currentDirectory;
if ((!tempfilename.isEmpty()) && (!tempfilename.endsWith("\\"))) {
tempfilename.concat('\\');
}
FileSystem::appendPathSeparator(tempfilename);
tempfilename.concat(fileiter->second.m_filename);
if (filenameList.find(tempfilename) == filenameList.end()) {
// only insert into the list if its not already in there.
Expand Down
19 changes: 13 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,14 @@ bool FileSystem::removeExtension(UnicodeString& path)

return false;
}

//============================================================================
// FileSystem::appendPathSeparator
//============================================================================
void FileSystem::appendPathSeparator(AsciiString& path)
{
if (path.isNotEmpty() && !isPathSeparator(path.getCharAt(path.getLength() - 1)))
{
path.concat(getNativePathSeparator());
}
}
36 changes: 21 additions & 15 deletions Core/GameEngine/Source/GameClient/MapUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
#include "GameLogic/FPUControl.h"
#include "GameNetwork/GameInfo.h"
#include "GameNetwork/NetworkDefs.h"
#include "Lib/PathUtil.h"


//-------------------------------------------------------------------------------
Expand Down Expand Up @@ -329,14 +330,20 @@ AsciiString MapCache::getMapExtension() const
return "map";
}

AsciiString MapCache::getCachePath( const AsciiString &mapDir )
{
AsciiString path = mapDir;
FileSystem::appendPathSeparator(path);
path.concat(m_mapCacheName);
return path;
}

void MapCache::writeCacheINI( const AsciiString &mapDir )
{
AsciiString filepath = mapDir;
filepath.concat('\\');
AsciiString filepath = getCachePath(mapDir);

TheFileSystem->createDirectory(mapDir);

filepath.concat(m_mapCacheName);
FILE *fp = fopen(filepath.str(), "w");
DEBUG_ASSERTCRASH(fp != nullptr, ("Failed to create %s", filepath.str()));
if (fp == nullptr) {
Expand Down Expand Up @@ -499,8 +506,7 @@ Bool MapCache::clearUnseenMaps( const AsciiString &mapDir )
void MapCache::loadMapsFromMapCacheINI( const AsciiString &mapDir )
{
INI ini;
AsciiString fname;
fname.format("%s\\%s", mapDir.str(), m_mapCacheName);
AsciiString fname = getCachePath(mapDir);

if (TheFileSystem->doesFileExist(fname.str()))
{
Expand All @@ -514,8 +520,8 @@ Bool MapCache::loadMapsFromDisk( const AsciiString &mapDir, Bool isOfficial, Boo

FilenameList filepathList;
FilenameListIter filepathIt;
AsciiString toplevelPattern;
toplevelPattern.format("%s\\", mapDir.str());
AsciiString toplevelPattern = mapDir;
FileSystem::appendPathSeparator(toplevelPattern);
Bool mapListChanged = FALSE;
AsciiString filenamepattern;
filenamepattern.format("*.%s", getMapExtension().str());
Expand All @@ -530,14 +536,13 @@ Bool MapCache::loadMapsFromDisk( const AsciiString &mapDir, Bool isOfficial, Boo
AsciiString filepathLower = *filepathIt;
filepathLower.toLower();

const char *szFilenameLower = filepathLower.reverseFind('\\');
const char *szFilenameLower = getLastPathSeparator(filepathLower.str());
if (!szFilenameLower)
{
DEBUG_CRASH(("Couldn't find \\ in map name!"));
DEBUG_CRASH(("Couldn't find path separator in map name!"));
continue;
}

AsciiString endingStr;
AsciiString filenameLower = szFilenameLower+1;
filenameLower.truncateBy(strlen(mapExtension));

Expand All @@ -547,7 +552,9 @@ Bool MapCache::loadMapsFromDisk( const AsciiString &mapDir, Bool isOfficial, Boo
continue;
}

endingStr.format("%s\\%s%s", filenameLower.str(), filenameLower.str(), mapExtension);
// Match against the separator this listing actually used, which is not necessarily the platform one
AsciiString endingStr;
endingStr.format("%s%c%s%s", filenameLower.str(), *szFilenameLower, filenameLower.str(), mapExtension);

if (!filepathLower.endsWithNoCase(endingStr.str()))
{
Expand Down Expand Up @@ -591,8 +598,8 @@ Bool MapCache::addMap(
if (md.m_nameLookupTag.isEmpty())
{
// unofficial maps or maps without names
AsciiString tempdisplayname;
tempdisplayname = fname.reverseFind('\\') + 1;
// TheSuperHackers @bugfix bobtista 14/09/2026 Handle map filenames with either separator or no separator.
AsciiString tempdisplayname = getFileName(fname.str());
(*this)[lowerFname].m_displayName.translate(tempdisplayname);
if (md.m_numPlayers >= 2)
{
Expand Down Expand Up @@ -653,8 +660,7 @@ Bool MapCache::addMap(
if (!exists || nameLookupTag.isEmpty())
{
DEBUG_LOG(("Missing TheKey_mapName!"));
AsciiString tempdisplayname;
tempdisplayname = fname.reverseFind('\\') + 1;
AsciiString tempdisplayname = getFileName(fname.str());
md.m_displayName.translate(tempdisplayname);
if (md.m_numPlayers >= 2)
{
Expand Down
43 changes: 38 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,32 @@ inline bool isAbsolutePath(const char* path)
return false;
}

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

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 +97,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 +117,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
Loading