diff --git a/Core/GameEngine/Include/Common/FileSystem.h b/Core/GameEngine/Include/Common/FileSystem.h index 2aaa30a61f1..a91c86bb2bd 100644 --- a/Core/GameEngine/Include/Common/FileSystem.h +++ b/Core/GameEngine/Include/Common/FileSystem.h @@ -163,6 +163,8 @@ class FileSystem : public SubsystemInterface static bool removeExtension(AsciiString& path); static bool removeExtension(UnicodeString& path); + static void appendPathSeparator(AsciiString& path); ///< appends the platform separator, unless the path is empty or already ends with either separator. + protected: #if ENABLE_FILESYSTEM_EXISTENCE_CACHE struct FileExistData diff --git a/Core/GameEngine/Source/Common/System/FileSystem.cpp b/Core/GameEngine/Source/Common/System/FileSystem.cpp index b8e4c4695b6..654e7575500 100644 --- a/Core/GameEngine/Source/Common/System/FileSystem.cpp +++ b/Core/GameEngine/Source/Common/System/FileSystem.cpp @@ -395,6 +395,17 @@ bool FileSystem::removeExtension(AsciiString& path) return false; } +//============================================================================ +// FileSystem::appendPathSeparator +//============================================================================ +void FileSystem::appendPathSeparator(AsciiString& path) +{ + if (path.isNotEmpty() && !isPathSeparator(path.getCharAt(path.getLength() - 1))) + { + path.concat(getNativePathSeparator()); + } +} + //============================================================================ // FileSystem::removeExtension - Unicode handling variant //============================================================================ diff --git a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp index 5d9cc7dfe64..3f10ecb5e5b 100644 --- a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp +++ b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp @@ -30,10 +30,12 @@ #include "PreRTS.h" // This must go first in EVERY cpp file in the GameEngine +#include "Common/FileSystem.h" #include "GameClient/LoadScreen.h" #include "GameClient/Shell.h" #include "GameNetwork/FileTransfer.h" #include "GameNetwork/networkutil.h" +#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- @@ -137,7 +139,7 @@ static Bool doFileTransfer( AsciiString filename, MapTransferLoadScreen *ls, Int AsciiString GetBasePathFromPath( AsciiString path ) { - const char *s = path.reverseFind('\\'); + const char *s = getLastPathSeparator(path.str()); if (s) { Int len = s - path.str(); @@ -153,10 +155,7 @@ AsciiString GetBasePathFromPath( AsciiString path ) AsciiString GetFileFromPath( AsciiString path ) { - const char *s = path.reverseFind('\\'); - if (s) - return s+1; - return path; + return getFileName(path.str()); } AsciiString GetExtensionFromFile( AsciiString fname ) @@ -183,59 +182,45 @@ AsciiString GetBaseFileFromFile( AsciiString fname ) return AsciiString::TheEmptyString; } -AsciiString GetPreviewFromMap( AsciiString path ) +static AsciiString GetFileInMapDirectory( const AsciiString &mapPath, const AsciiString &filename ) { - AsciiString fname = GetBaseFileFromFile(GetFileFromPath(path)); - AsciiString base = GetBasePathFromPath(path); + const char *file = getFileName(mapPath.str()); + AsciiString path; + path.set(mapPath.str(), file - mapPath.str()); + path.concat(filename); + return path; +} - AsciiString out; - out.format("%s\\%s.tga", base.str(), fname.str()); - return out; +AsciiString GetPreviewFromMap( AsciiString path ) +{ + FileSystem::removeExtension(path); + path.concat(".tga"); + return path; } AsciiString GetINIFromMap( AsciiString path ) { - AsciiString base = GetBasePathFromPath(path); - - AsciiString out; - out.format("%s\\map.ini", base.str()); - return out; + return GetFileInMapDirectory(path, "map.ini"); } AsciiString GetStrFileFromMap( AsciiString path ) { - AsciiString base = GetBasePathFromPath(path); - - AsciiString out; - out.format("%s\\map.str", base.str()); - return out; + return GetFileInMapDirectory(path, "map.str"); } AsciiString GetSoloINIFromMap( AsciiString path ) { - AsciiString base = GetBasePathFromPath(path); - - AsciiString out; - out.format("%s\\solo.ini", base.str()); - return out; + return GetFileInMapDirectory(path, "solo.ini"); } AsciiString GetAssetUsageFromMap( AsciiString path ) { - AsciiString base = GetBasePathFromPath(path); - - AsciiString out; - out.format("%s\\assetusage.txt", base.str()); - return out; + return GetFileInMapDirectory(path, "assetusage.txt"); } AsciiString GetReadmeFromMap( AsciiString path ) { - AsciiString base = GetBasePathFromPath(path); - - AsciiString out; - out.format("%s\\readme.txt", base.str()); - return out; + return GetFileInMapDirectory(path, "readme.txt"); } //------------------------------------------------------------------------------------- diff --git a/Core/Libraries/Include/Lib/PathUtil.h b/Core/Libraries/Include/Lib/PathUtil.h index 683f6fee130..ad6c00d6283 100644 --- a/Core/Libraries/Include/Lib/PathUtil.h +++ b/Core/Libraries/Include/Lib/PathUtil.h @@ -23,7 +23,8 @@ #include "BaseType.h" #include -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 == '/'; @@ -32,6 +33,12 @@ 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) @@ -39,14 +46,14 @@ inline bool isAbsolutePath(const char* path) 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; } @@ -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, '.'); @@ -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) @@ -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)