From 5146d14e659de058076587ecde89d8fc5943e151 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Fri, 25 Sep 2026 12:51:13 -0400 Subject: [PATCH 01/22] bugfix(savegame): Handle both separators in fallback map labels --- .../Source/Common/System/SaveGame/GameState.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 17b73805824..1ceceffd78d 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -1666,14 +1666,8 @@ void GameState::xfer( Xfer *xfer ) // if no label was found, we'll use the map name (just filename, no directory info) if (exists == FALSE || saveGameInfo->mapLabel == AsciiString::TheEmptyString) { - const char* p = TheGlobalData->m_mapName.reverseFind('\\'); - if (p == nullptr) - saveGameInfo->mapLabel = TheGlobalData->m_mapName; - else - { - p++; // skip the '\' we're on - saveGameInfo->mapLabel.set(p); - } + const char* p = maxPtr(TheGlobalData->m_mapName.reverseFind('/'), TheGlobalData->m_mapName.reverseFind('\\')); + saveGameInfo->mapLabel = p ? p + 1 : TheGlobalData->m_mapName.str(); } // xfer map label From 32ce7495b6cacc30881042600d9a48b555c50116 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Fri, 25 Sep 2026 12:54:31 -0400 Subject: [PATCH 02/22] bugfix(generals): Handle both separators in fallback map labels --- .../Source/Common/System/SaveGame/GameState.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 3338c981e04..416c34d1be1 100644 --- a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -1666,14 +1666,8 @@ void GameState::xfer( Xfer *xfer ) // if no label was found, we'll use the map name (just filename, no directory info) if (exists == FALSE || saveGameInfo->mapLabel == AsciiString::TheEmptyString) { - const char* p = TheGlobalData->m_mapName.reverseFind('\\'); - if (p == nullptr) - saveGameInfo->mapLabel = TheGlobalData->m_mapName; - else - { - p++; // skip the '\' we're on - saveGameInfo->mapLabel.set(p); - } + const char* p = maxPtr(TheGlobalData->m_mapName.reverseFind('/'), TheGlobalData->m_mapName.reverseFind('\\')); + saveGameInfo->mapLabel = p ? p + 1 : TheGlobalData->m_mapName.str(); } // xfer map label From 62dc0166cb48daa08a563ad122cb5edffd752804 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sun, 27 Sep 2026 15:27:55 -0400 Subject: [PATCH 03/22] refactor(savegame): Reuse the filename helper for fallback labels --- .../GameEngine/Source/Common/System/SaveGame/GameState.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 1ceceffd78d..250d0f191cb 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -1666,8 +1666,7 @@ void GameState::xfer( Xfer *xfer ) // if no label was found, we'll use the map name (just filename, no directory info) if (exists == FALSE || saveGameInfo->mapLabel == AsciiString::TheEmptyString) { - const char* p = maxPtr(TheGlobalData->m_mapName.reverseFind('/'), TheGlobalData->m_mapName.reverseFind('\\')); - saveGameInfo->mapLabel = p ? p + 1 : TheGlobalData->m_mapName.str(); + saveGameInfo->mapLabel = getFileName(TheGlobalData->m_mapName.str()); } // xfer map label From 655853cfa7833ce41e39299008b86b1dbd24a54e Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sun, 27 Sep 2026 15:27:56 -0400 Subject: [PATCH 04/22] refactor(generals): Reuse the filename helper for fallback labels --- .../GameEngine/Source/Common/System/SaveGame/GameState.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 416c34d1be1..a96f7f22a43 100644 --- a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -1666,8 +1666,7 @@ void GameState::xfer( Xfer *xfer ) // if no label was found, we'll use the map name (just filename, no directory info) if (exists == FALSE || saveGameInfo->mapLabel == AsciiString::TheEmptyString) { - const char* p = maxPtr(TheGlobalData->m_mapName.reverseFind('/'), TheGlobalData->m_mapName.reverseFind('\\')); - saveGameInfo->mapLabel = p ? p + 1 : TheGlobalData->m_mapName.str(); + saveGameInfo->mapLabel = getFileName(TheGlobalData->m_mapName.str()); } // xfer map label From 303b9042efaf9e52bd334e3a7f7fe8843ce89eb9 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 29 Sep 2026 16:03:26 -0400 Subject: [PATCH 05/22] bugfix(savegame): Handle both separators wherever a file name is taken from a path --- .../Source/Common/Audio/AudioEventRTS.cpp | 8 ++----- Core/GameEngine/Source/Common/CRCDebug.cpp | 3 ++- .../Source/Common/INI/INIMapCache.cpp | 3 ++- Core/GameEngine/Source/GameClient/MapUtil.cpp | 7 +++--- .../Source/GameLogic/Map/TerrainLogic.cpp | 5 ++-- .../Source/GameNetwork/FileTransfer.cpp | 8 +++---- .../Source/GameNetwork/GameSpy/LobbyUtils.cpp | 23 +++---------------- .../MilesAudioDevice/MilesAudioManager.cpp | 13 ++++++----- .../GameClient/Drawable/Draw/W3DModelDraw.cpp | 5 ++-- .../W3DDevice/GameClient/W3DDisplay.cpp | 7 ++---- .../GameEngine/Source/Common/Recorder.cpp | 5 +--- .../Source/Common/StatsCollector.cpp | 6 ++--- .../Common/System/SaveGame/GameState.cpp | 2 +- .../GUI/GUICallbacks/Menus/DownloadMenu.cpp | 10 ++------ .../GUI/GUICallbacks/Menus/GameInfoWindow.cpp | 12 ++-------- .../GUICallbacks/Menus/LanGameOptionsMenu.cpp | 7 ++---- .../GUI/GUICallbacks/Menus/PopupSaveLoad.cpp | 8 ++----- .../GUI/GUICallbacks/Menus/ReplayMenu.cpp | 6 ++--- .../GUICallbacks/Menus/WOLGameSetupMenu.cpp | 7 ++---- 19 files changed, 48 insertions(+), 97 deletions(-) diff --git a/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp b/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp index f125ccba84d..9db9ef1198c 100644 --- a/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp +++ b/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp @@ -56,6 +56,7 @@ #include "GameClient/Drawable.h" // For getPosition #include "GameClient/GameClient.h" // For getDrawableByID +#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------------------- @@ -796,12 +797,7 @@ AsciiString AudioEventRTS::generateFilenameExtension( AudioType audioTypeToPlay //------------------------------------------------------------------------------------------------- void AudioEventRTS::adjustForLocalization(AsciiString &strToAdjust) { - const char *filename = strToAdjust.reverseFind('\\'); - if (filename) { - filename += 1; - } else { - filename = strToAdjust.str(); - } + const char *filename = getFileName(strToAdjust.str()); // try the localized version first so that we're guaranteed to get it // even if the generic data directory holds a version of the file diff --git a/Core/GameEngine/Source/Common/CRCDebug.cpp b/Core/GameEngine/Source/Common/CRCDebug.cpp index 62abfc358e2..e22a1743f64 100644 --- a/Core/GameEngine/Source/Common/CRCDebug.cpp +++ b/Core/GameEngine/Source/Common/CRCDebug.cpp @@ -31,6 +31,7 @@ #include "Common/LocalFileSystem.h" #include "GameClient/InGameUI.h" #include "GameNetwork/IPEnumeration.h" +#include "Lib/PathUtil.h" #include @@ -183,7 +184,7 @@ void outputCRCDumpLines() static AsciiString getFname(AsciiString path) { - return path.reverseFind('\\') + 1; + return getFileName(path.str()); } static void addCRCDebugLineInternal(bool count, const char *fmt, va_list args) diff --git a/Core/GameEngine/Source/Common/INI/INIMapCache.cpp b/Core/GameEngine/Source/Common/INI/INIMapCache.cpp index 212cf5ccac5..025b23a2d11 100644 --- a/Core/GameEngine/Source/Common/INI/INIMapCache.cpp +++ b/Core/GameEngine/Source/Common/INI/INIMapCache.cpp @@ -37,6 +37,7 @@ #include "Common/NameKeyGenerator.h" #include "Common/WellKnownKeys.h" #include "Common/QuotedPrintable.h" +#include "Lib/PathUtil.h" class MapMetaDataReader @@ -148,7 +149,7 @@ void INI::parseMapCacheDefinition( INI* ini ) { // maps without localized name tags AsciiString tempdisplayname; - tempdisplayname = name.reverseFind('\\') + 1; + tempdisplayname = getFileName(name.str()); md.m_displayName.translate(tempdisplayname); if (md.m_numPlayers >= 2) { diff --git a/Core/GameEngine/Source/GameClient/MapUtil.cpp b/Core/GameEngine/Source/GameClient/MapUtil.cpp index 43a93d1f34b..10fdf14e775 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" //------------------------------------------------------------------------------- @@ -530,7 +531,7 @@ 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!")); @@ -592,7 +593,7 @@ Bool MapCache::addMap( { // unofficial maps or maps without names AsciiString tempdisplayname; - tempdisplayname = fname.reverseFind('\\') + 1; + tempdisplayname = getFileName(fname.str()); (*this)[lowerFname].m_displayName.translate(tempdisplayname); if (md.m_numPlayers >= 2) { @@ -654,7 +655,7 @@ Bool MapCache::addMap( { DEBUG_LOG(("Missing TheKey_mapName!")); AsciiString tempdisplayname; - tempdisplayname = fname.reverseFind('\\') + 1; + tempdisplayname = getFileName(fname.str()); md.m_displayName.translate(tempdisplayname); if (md.m_numPlayers >= 2) { diff --git a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 2225b35618f..a36d3cf110d 100644 --- a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -59,6 +59,7 @@ #include "WWMath/plane.h" #include "WWMath/tri.h" +#include "Lib/PathUtil.h" // GLOBALS //////////////////////////////////////////////////////////////////////////////////////// @@ -1145,14 +1146,14 @@ void TerrainLogic::enableWaterGrid( Bool enable ) const char *c; // create stripped map name - c = strrchr( TheGlobalData->m_mapName.str(), '\\' ); + c = getLastPathSeparator( TheGlobalData->m_mapName.str() ); if( c ) strippedMapNameOnly.set( c ); else strippedMapNameOnly = TheGlobalData->m_mapName; // create stripped compare name - c = strrchr( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str(), '\\' ); + c = getLastPathSeparator( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); if( c ) strippedCompareMapNameOnly.set( c ); else diff --git a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp index 5d9cc7dfe64..1ea32f5a84a 100644 --- a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp +++ b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp @@ -34,6 +34,7 @@ #include "GameClient/Shell.h" #include "GameNetwork/FileTransfer.h" #include "GameNetwork/networkutil.h" +#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- @@ -137,7 +138,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 +154,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 ) diff --git a/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp b/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp index 6a73ace1b46..183529e96b2 100644 --- a/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp +++ b/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp @@ -61,6 +61,7 @@ #include "GameNetwork/GameSpy/GSConfig.h" #include "Common/STLTypedefs.h" +#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// @@ -274,16 +275,7 @@ static void gameTooltip(GameWindow *window, } else { - const char *start = room->getMap().reverseFind('\\'); - if (start) - { - ++start; - } - else - { - start = room->getMap().str(); - } - mapName.translate( start ); + mapName.translate( getFileName(room->getMap().str()) ); } UnicodeString tmp; tooltip.format(TheGameText->fetch("TOOLTIP:GameInfoGameName"), room->getGameName().str()); @@ -584,16 +576,7 @@ static Int insertGame( GameWindow *win, GameSpyStagingRoom *game, Bool showMap ) } else { - const char *start = game->getMap().reverseFind('\\'); - if (start) - { - ++start; - } - else - { - start = game->getMap().str(); - } - mapName.translate( start ); + mapName.translate( getFileName(game->getMap().str()) ); } GadgetListBoxAddEntryText(win, mapName, gameColor, index, COLUMN_MAP); diff --git a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp index 5db80e6f21e..1e23efb04d9 100644 --- a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp +++ b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp @@ -67,6 +67,7 @@ #include #include "MilesLoader.h" +#include "Lib/PathUtil.h" #include @@ -245,7 +246,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; @@ -265,7 +266,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL playing = *it; AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind( '\\' ) + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; @@ -302,7 +303,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; @@ -354,7 +355,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL playing = *it; AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; @@ -378,7 +379,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL playing = *it; AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; @@ -402,7 +403,7 @@ void MilesAudioManager::audioDebugDisplay(DebugDisplayInterface *dd, void *, FIL playing = *it; AudioEventRTS *event = playing->m_audioEventRTS.Peek(); filenameNoSlashes = event->getFilename(); - filenameNoSlashes = filenameNoSlashes.reverseFind('\\') + 1; + filenameNoSlashes = getFileName(filenameNoSlashes.str()); // Calculate Sample volume volume = 100.0f; diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index 6a98dd58398..db3a555972d 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -67,6 +67,7 @@ #include "WW3D2/mesh.h" #include "WW3D2/meshmdl.h" #include "Common/BitFlagsIO.h" +#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------------------- @@ -152,7 +153,7 @@ void LogClass::log(const char *fmt, ...) void LogClass::dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int line) { fname.toLower(); - fname = fname.reverseFind('\\') + 1; + fname = getFileName(fname.str()); const Real *matrix = (const Real *)m; log("dumpMatrix3D() %s:%d %s", fname.str(), line, name.str()); @@ -166,7 +167,7 @@ void LogClass::dumpReal(Real r, AsciiString name, AsciiString fname, Int line) if (!m_fp || !isValidTimeToCalcLogicStuff()) return; fname.toLower(); - fname = fname.reverseFind('\\') + 1; + fname = getFileName(fname.str()); log("dumpReal() %s:%d %s %8.8X (%f)", fname.str(), line, name.str(), AS_INT(r), r); } diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index b7d6a44a5b5..698699fc82f 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -110,6 +110,7 @@ static void drawFramerateBar(); #endif #include "WinMain.h" +#include "Lib/PathUtil.h" // DEFINE AND ENUMS /////////////////////////////////////////////////////////// @@ -3260,11 +3261,7 @@ void W3DDisplay::dumpAssetUsage(const char* mapname) DynamicVectorClass names(8000); m_assetManager->Create_Asset_List(names); - const char* leafname = strrchr(mapname, '\\'); - if (leafname) - ++leafname; // point to first character after the last backslash - else - leafname = mapname; // point to the start of the filename + const char* leafname = getFileName(mapname); char buf[256]; int idx = 1; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 1b1bb00f6d2..38e98d96095 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1665,10 +1665,7 @@ AsciiString RecorderClass::getLastReplayFileName() AsciiString players; AsciiString full; AsciiString fullPlusNum; - AsciiString mapName = game->getMap(); - const char *fname = mapName.reverseFind('\\'); - if (fname) - mapName = fname+1; + AsciiString mapName = getFileName(game->getMap().str()); for (Int i=0; igetSlot(i); diff --git a/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp b/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp index 048051a28b2..871df9c5d08 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp @@ -62,6 +62,7 @@ #include "GameClient/MapUtil.h" #include "GameNetwork/networkutil.h" #include "GameNetwork/LANAPICallbacks.h" +#include "Lib/PathUtil.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- @@ -307,10 +308,7 @@ void StatsCollector::createFileName() curtime = localtime(&longTime); strftime(datestr, 256, "_%b%d_%I%M%p", curtime); // const MapMetaData *m = TheMapCache->findMap(TheGlobalData->m_mapName); - AsciiString name = TheGlobalData->m_mapName; - const char *fname = name.reverseFind('\\'); - if (fname) - name = fname+1; + AsciiString name = getFileName(TheGlobalData->m_mapName.str()); name.truncateBy(4); // ".map" m_statsFileName.clear(); #if defined(RTS_DEBUG) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 250d0f191cb..6c10c8f2a91 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -840,7 +840,7 @@ Bool GameState::isInSaveDirectory(const AsciiString& path) const // ------------------------------------------------------------------------------------------------ AsciiString GameState::getMapLeafName(const AsciiString& in) const { - const char* p = strrchr(in.str(), '\\'); + const char* p = getLastPathSeparator(in.str()); if (p) { // diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp index 9e3c9318796..c5f0178c046 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp @@ -59,6 +59,7 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" +#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonCancelID = NAMEKEY_INVALID; @@ -142,15 +143,8 @@ HRESULT DownloadManagerMunkee::downloadFile( AsciiString server, AsciiString use if (staticTextFile) { - AsciiString bob = file; - // just get the filename, not the pathname - const char *tmp = bob.reverseFind('/'); - if (tmp) - bob = tmp+1; - tmp = bob.reverseFind('\\'); - if (tmp) - bob = tmp+1; + AsciiString bob = getFileName(file.str()); UnicodeString fileString; fileString.translate(bob); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp index d3f68fe3bb9..fb1485f515b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp @@ -43,6 +43,7 @@ #include "Common/PlayerTemplate.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/LANAPI.h" +#include "Lib/PathUtil.h" static GameWindow *parent = nullptr; @@ -118,16 +119,7 @@ void RefreshGameInfoWindow(GameInfo *gameInfo, UnicodeString gameName) else { // can happen if the map will have to be transferred... so use the leaf name (srj) - const char *noPath = gameInfo->getMap().reverseFind('\\'); - if (noPath) - { - ++noPath; - } - else - { - noPath = gameInfo->getMap().str(); - } - map.translate(noPath); + map.translate(getFileName(gameInfo->getMap().str())); } GadgetStaticTextSetText(staticTextMapName,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 d00f0834296..814660dc96b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -59,6 +59,7 @@ #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" +#include "Lib/PathUtil.h" extern char *LANnextScreen; @@ -950,11 +951,7 @@ void updateGameOptions() } else { - AsciiString s = TheLAN->GetMyGame()->getMap(); - if (s.reverseFind('\\')) - { - s = s.reverseFind('\\') + 1; - } + AsciiString s = getFileName(TheLAN->GetMyGame()->getMap().str()); mapDisplayName.format(L"%hs", s.str()); } UnicodeString old = GadgetStaticTextGetText(textEntryMapDisplay); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp index 18b148a08d2..f099a5712ec 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp @@ -60,6 +60,7 @@ #include "GameClient/Shell.h" #include "GameLogic/GameLogic.h" #include "GameClient/GameWindowTransitions.h" +#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonBackKey = NAMEKEY_INVALID; @@ -468,12 +469,7 @@ static void setEditDescription( GameWindow *editControl ) TheCampaignManager->getCurrentMissionNumber() + 1 ); else { - const char *mapName = TheGlobalData->m_mapName.reverseFind( '\\' ); - - if( mapName ) - defaultDesc.format( L"%S", mapName + 1 ); - else - defaultDesc.format( L"%S", TheGlobalData->m_mapName.str() ); + defaultDesc.format( L"%S", getFileName( TheGlobalData->m_mapName.str() ) ); //Keep the extension out of the descriptive name. if( (defaultDesc.getLength() >= 4) && (defaultDesc.getCharAt(defaultDesc.getLength()-4) == '.') ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp index 8ba96a24e0e..bc26bcd94eb 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp @@ -47,6 +47,7 @@ #include "GameClient/Mouse.h" #include "GameClient/GameText.h" #include "GameClient/GameWindowTransitions.h" +#include "Lib/PathUtil.h" typedef UnicodeString ReplayName; typedef UnicodeString TooltipString; @@ -158,8 +159,7 @@ static UnicodeString createMapName(const AsciiString& filename, const ReplayGame { // TheSuperHackers @bugfix helmutbuhler 08/03/2025 Just use the filename. // Displaying a long map path string would break the map list gui. - const char* filename = info.getMap().reverseFind('\\'); - mapName.translate(filename ? filename + 1 : info.getMap()); + mapName.translate(getFileName(info.getMap().str())); } else { @@ -274,7 +274,7 @@ void PopulateReplayFileListbox(GameWindow *listbox) for (it = replayFilenames.begin(); it != replayFilenames.end(); ++it) { // just want the filename - asciistr.set((*it).reverseFind('\\') + 1); + asciistr.set(getFileName((*it).str())); RecorderClass::ReplayHeader header; ReplayGameInfo info; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp index ab14869fcf5..36cb3805703 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp @@ -65,6 +65,7 @@ #include "GameNetwork/NAT.h" #include "GameNetwork/GUIUtil.h" #include "GameNetwork/GameSpy/GSConfig.h" +#include "Lib/PathUtil.h" void WOLDisplaySlotList(); @@ -970,11 +971,7 @@ void WOLDisplayGameOptions() } else { - AsciiString s = TheGameSpyInfo->getCurrentStagingRoom()->getMap(); - if (s.reverseFind('\\')) - { - s = s.reverseFind('\\') + 1; - } + AsciiString s = getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str()); UnicodeString mapDisplay; mapDisplay.translate(s); GadgetStaticTextSetText(textEntryMapDisplay, mapDisplay); From eb6cd71342d7056a7e1ba0f02c38e4c109737fd9 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 29 Sep 2026 16:31:33 -0400 Subject: [PATCH 06/22] bugfix(generals): Handle both separators wherever a file name is taken from a path --- Generals/Code/GameEngine/Source/Common/Recorder.cpp | 5 +---- .../Code/GameEngine/Source/Common/StatsCollector.cpp | 6 ++---- .../Source/Common/System/SaveGame/GameState.cpp | 2 +- .../GUI/GUICallbacks/Menus/DownloadMenu.cpp | 10 ++-------- .../GUI/GUICallbacks/Menus/GameInfoWindow.cpp | 12 ++---------- .../GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp | 7 ++----- .../GUI/GUICallbacks/Menus/PopupSaveLoad.cpp | 8 ++------ .../GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp | 6 +++--- .../GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp | 7 ++----- .../GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 5 +++-- 10 files changed, 20 insertions(+), 48 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index e9c55ee350b..6456b1bebcd 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -1662,10 +1662,7 @@ AsciiString RecorderClass::getLastReplayFileName() AsciiString players; AsciiString full; AsciiString fullPlusNum; - AsciiString mapName = game->getMap(); - const char *fname = mapName.reverseFind('\\'); - if (fname) - mapName = fname+1; + AsciiString mapName = getFileName(game->getMap().str()); for (Int i=0; igetSlot(i); diff --git a/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp b/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp index f8740095b8d..378169548fb 100644 --- a/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp +++ b/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp @@ -62,6 +62,7 @@ #include "GameClient/MapUtil.h" #include "GameNetwork/networkutil.h" #include "GameNetwork/LANAPICallbacks.h" +#include "Lib/PathUtil.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- @@ -307,10 +308,7 @@ void StatsCollector::createFileName() curtime = localtime(&longTime); strftime(datestr, 256, "_%b%d_%I%M%p", curtime); // const MapMetaData *m = TheMapCache->findMap(TheGlobalData->m_mapName); - AsciiString name = TheGlobalData->m_mapName; - const char *fname = name.reverseFind('\\'); - if (fname) - name = fname+1; + AsciiString name = getFileName(TheGlobalData->m_mapName.str()); name.truncateBy(4); // ".map" m_statsFileName.clear(); #if defined(RTS_DEBUG) diff --git a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index a96f7f22a43..b1ba6805d03 100644 --- a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -840,7 +840,7 @@ Bool GameState::isInSaveDirectory(const AsciiString& path) const // ------------------------------------------------------------------------------------------------ AsciiString GameState::getMapLeafName(const AsciiString& in) const { - const char* p = strrchr(in.str(), '\\'); + const char* p = getLastPathSeparator(in.str()); if (p) { // diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp index 497bcd95ede..024701fb397 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp @@ -59,6 +59,7 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" +#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonCancelID = NAMEKEY_INVALID; @@ -142,15 +143,8 @@ HRESULT DownloadManagerMunkee::downloadFile( AsciiString server, AsciiString use if (staticTextFile) { - AsciiString bob = file; - // just get the filename, not the pathname - const char *tmp = bob.reverseFind('/'); - if (tmp) - bob = tmp+1; - tmp = bob.reverseFind('\\'); - if (tmp) - bob = tmp+1; + AsciiString bob = getFileName(file.str()); UnicodeString fileString; fileString.translate(bob); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp index 466b9caebd6..8885fe78bb2 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp @@ -43,6 +43,7 @@ #include "Common/PlayerTemplate.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/LANAPI.h" +#include "Lib/PathUtil.h" static GameWindow *parent = nullptr; @@ -118,16 +119,7 @@ void RefreshGameInfoWindow(GameInfo *gameInfo, UnicodeString gameName) else { // can happen if the map will have to be transferred... so use the leaf name (srj) - const char *noPath = gameInfo->getMap().reverseFind('\\'); - if (noPath) - { - ++noPath; - } - else - { - noPath = gameInfo->getMap().str(); - } - map.translate(noPath); + map.translate(getFileName(gameInfo->getMap().str())); } GadgetStaticTextSetText(staticTextMapName,map); 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 a3057616354..12959eaec46 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -56,6 +56,7 @@ #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" +#include "Lib/PathUtil.h" extern char *LANnextScreen; @@ -870,11 +871,7 @@ void updateGameOptions() } else { - AsciiString s = TheLAN->GetMyGame()->getMap(); - if (s.reverseFind('\\')) - { - s = s.reverseFind('\\') + 1; - } + AsciiString s = getFileName(TheLAN->GetMyGame()->getMap().str()); mapDisplayName.format(L"%hs", s.str()); } UnicodeString old = GadgetStaticTextGetText(textEntryMapDisplay); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp index f3c946ec266..9e4da851fa4 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp @@ -60,6 +60,7 @@ #include "GameClient/Shell.h" #include "GameLogic/GameLogic.h" #include "GameClient/GameWindowTransitions.h" +#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonBackKey = NAMEKEY_INVALID; @@ -458,12 +459,7 @@ static void setEditDescription( GameWindow *editControl ) TheCampaignManager->getCurrentMissionNumber() + 1 ); else { - const char *mapName = TheGlobalData->m_mapName.reverseFind( '\\' ); - - if( mapName ) - defaultDesc.format( L"%S", mapName + 1 ); - else - defaultDesc.format( L"%S", TheGlobalData->m_mapName.str() ); + defaultDesc.format( L"%S", getFileName( TheGlobalData->m_mapName.str() ) ); } diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp index 683d9a242f0..c98820ff21a 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp @@ -47,6 +47,7 @@ #include "GameClient/Mouse.h" #include "GameClient/GameText.h" #include "GameClient/GameWindowTransitions.h" +#include "Lib/PathUtil.h" typedef UnicodeString ReplayName; typedef UnicodeString TooltipString; @@ -158,8 +159,7 @@ static UnicodeString createMapName(const AsciiString& filename, const ReplayGame { // TheSuperHackers @bugfix helmutbuhler 08/03/2025 Just use the filename. // Displaying a long map path string would break the map list gui. - const char* filename = info.getMap().reverseFind('\\'); - mapName.translate(filename ? filename + 1 : info.getMap()); + mapName.translate(getFileName(info.getMap().str())); } else { @@ -274,7 +274,7 @@ void PopulateReplayFileListbox(GameWindow *listbox) for (it = replayFilenames.begin(); it != replayFilenames.end(); ++it) { // just want the filename - asciistr.set((*it).reverseFind('\\') + 1); + asciistr.set(getFileName((*it).str())); RecorderClass::ReplayHeader header; ReplayGameInfo info; diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp index e2a44cd6707..7f718dfa3fd 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp @@ -63,6 +63,7 @@ #include "GameNetwork/NAT.h" #include "GameNetwork/GUIUtil.h" #include "GameNetwork/GameSpy/GSConfig.h" +#include "Lib/PathUtil.h" void WOLDisplaySlotList(); @@ -908,11 +909,7 @@ void WOLDisplayGameOptions() } else { - AsciiString s = TheGameSpyInfo->getCurrentStagingRoom()->getMap(); - if (s.reverseFind('\\')) - { - s = s.reverseFind('\\') + 1; - } + AsciiString s = getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str()); UnicodeString mapDisplay; mapDisplay.translate(s); GadgetStaticTextSetText(textEntryMapDisplay, mapDisplay); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 1b402424162..6816a79f225 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -59,6 +59,7 @@ #include "WWMath/plane.h" #include "WWMath/tri.h" +#include "Lib/PathUtil.h" // GLOBALS //////////////////////////////////////////////////////////////////////////////////////// @@ -1145,14 +1146,14 @@ void TerrainLogic::enableWaterGrid( Bool enable ) const char *c; // create stripped map name - c = strrchr( TheGlobalData->m_mapName.str(), '\\' ); + c = getLastPathSeparator( TheGlobalData->m_mapName.str() ); if( c ) strippedMapNameOnly.set( c ); else strippedMapNameOnly = TheGlobalData->m_mapName; // create stripped compare name - c = strrchr( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str(), '\\' ); + c = getLastPathSeparator( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); if( c ) strippedCompareMapNameOnly.set( c ); else From a1adbbfbaa1795b70894e9574c4e0a71b0be3a53 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 29 Sep 2026 18:14:17 -0400 Subject: [PATCH 07/22] bugfix(terrain): Compare water map filenames without separators --- .../Source/GameLogic/Map/TerrainLogic.cpp | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index a36d3cf110d..bf2ea03f35c 100644 --- a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -1141,23 +1141,9 @@ void TerrainLogic::enableWaterGrid( Bool enable ) // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when // loading the map from a save game file // - AsciiString strippedMapNameOnly; - AsciiString strippedCompareMapNameOnly; - const char *c; - - // create stripped map name - c = getLastPathSeparator( TheGlobalData->m_mapName.str() ); - if( c ) - strippedMapNameOnly.set( c ); - else - strippedMapNameOnly = TheGlobalData->m_mapName; - - // create stripped compare name - c = getLastPathSeparator( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); - if( c ) - strippedCompareMapNameOnly.set( c ); - else - strippedCompareMapNameOnly = TheGlobalData->m_vertexWaterAvailableMaps[ i ]; + // TheSuperHackers @bugfix bobtista 29/09/2026 Match water settings regardless of path separators. + AsciiString strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); + AsciiString strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 ) From 85d83f9c9ad4d177bcfd7afbe56c6e008b4d08a5 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Tue, 29 Sep 2026 18:19:02 -0400 Subject: [PATCH 08/22] bugfix(generals): Compare water map filenames without separators --- .../Source/GameLogic/Map/TerrainLogic.cpp | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 6816a79f225..4c1cbbdb19d 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -1141,23 +1141,9 @@ void TerrainLogic::enableWaterGrid( Bool enable ) // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when // loading the map from a save game file // - AsciiString strippedMapNameOnly; - AsciiString strippedCompareMapNameOnly; - const char *c; - - // create stripped map name - c = getLastPathSeparator( TheGlobalData->m_mapName.str() ); - if( c ) - strippedMapNameOnly.set( c ); - else - strippedMapNameOnly = TheGlobalData->m_mapName; - - // create stripped compare name - c = getLastPathSeparator( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); - if( c ) - strippedCompareMapNameOnly.set( c ); - else - strippedCompareMapNameOnly = TheGlobalData->m_vertexWaterAvailableMaps[ i ]; + // TheSuperHackers @bugfix bobtista 29/09/2026 Match water settings regardless of path separators. + AsciiString strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); + AsciiString strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 ) From 4ced5cff39767afde5bd4836c8aa23b62f2d9627 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 30 Sep 2026 12:58:12 -0400 Subject: [PATCH 09/22] refactor(pathutil): Apply filename review cleanup --- Core/GameEngine/Include/GameNetwork/FileTransfer.h | 1 - .../GameEngine/Source/Common/Audio/AudioEventRTS.cpp | 1 - Core/GameEngine/Source/Common/CRCDebug.cpp | 1 - Core/GameEngine/Source/Common/INI/INIMapCache.cpp | 1 - Core/GameEngine/Source/Common/System/FileSystem.cpp | 1 - Core/GameEngine/Source/GameClient/MapUtil.cpp | 1 - .../GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 8 +++----- Core/GameEngine/Source/GameNetwork/FileTransfer.cpp | 12 +++--------- .../Source/GameNetwork/GameSpy/LobbyUtils.cpp | 1 - .../Source/MilesAudioDevice/MilesAudioManager.cpp | 1 - .../GameClient/Drawable/Draw/W3DModelDraw.cpp | 1 - .../Source/W3DDevice/GameClient/W3DDisplay.cpp | 1 - Core/Libraries/Source/WWVegas/WWLib/WWCommon.h | 1 + .../Code/GameEngine/Source/Common/Recorder.cpp | 1 - .../Code/GameEngine/Source/Common/StatsCollector.cpp | 1 - .../Source/Common/System/SaveGame/GameState.cpp | 1 - .../GUI/GUICallbacks/Menus/DownloadMenu.cpp | 3 +-- .../GUI/GUICallbacks/Menus/GameInfoWindow.cpp | 1 - .../GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp | 4 +--- .../GUI/GUICallbacks/Menus/PopupSaveLoad.cpp | 1 - .../GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp | 1 - .../GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp | 4 +--- 22 files changed, 10 insertions(+), 38 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/FileTransfer.h b/Core/GameEngine/Include/GameNetwork/FileTransfer.h index 474599405f0..f973dc0c6a3 100644 --- a/Core/GameEngine/Include/GameNetwork/FileTransfer.h +++ b/Core/GameEngine/Include/GameNetwork/FileTransfer.h @@ -34,7 +34,6 @@ class GameInfo; // Convenience functions AsciiString GetBasePathFromPath( AsciiString path ); -AsciiString GetFileFromPath( AsciiString path ); AsciiString GetExtensionFromFile( AsciiString fname ); AsciiString GetBaseFileFromFile( AsciiString fname ); AsciiString GetPreviewFromMap( AsciiString path ); diff --git a/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp b/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp index 9db9ef1198c..f9b4b7d58ad 100644 --- a/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp +++ b/Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp @@ -56,7 +56,6 @@ #include "GameClient/Drawable.h" // For getPosition #include "GameClient/GameClient.h" // For getDrawableByID -#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/Common/CRCDebug.cpp b/Core/GameEngine/Source/Common/CRCDebug.cpp index e22a1743f64..2f3aec51ec9 100644 --- a/Core/GameEngine/Source/Common/CRCDebug.cpp +++ b/Core/GameEngine/Source/Common/CRCDebug.cpp @@ -31,7 +31,6 @@ #include "Common/LocalFileSystem.h" #include "GameClient/InGameUI.h" #include "GameNetwork/IPEnumeration.h" -#include "Lib/PathUtil.h" #include diff --git a/Core/GameEngine/Source/Common/INI/INIMapCache.cpp b/Core/GameEngine/Source/Common/INI/INIMapCache.cpp index 025b23a2d11..42998a66c1f 100644 --- a/Core/GameEngine/Source/Common/INI/INIMapCache.cpp +++ b/Core/GameEngine/Source/Common/INI/INIMapCache.cpp @@ -37,7 +37,6 @@ #include "Common/NameKeyGenerator.h" #include "Common/WellKnownKeys.h" #include "Common/QuotedPrintable.h" -#include "Lib/PathUtil.h" class MapMetaDataReader diff --git a/Core/GameEngine/Source/Common/System/FileSystem.cpp b/Core/GameEngine/Source/Common/System/FileSystem.cpp index 0907722dcf1..9c2c1f8701f 100644 --- a/Core/GameEngine/Source/Common/System/FileSystem.cpp +++ b/Core/GameEngine/Source/Common/System/FileSystem.cpp @@ -54,7 +54,6 @@ #include "Common/LocalFileSystem.h" #include "Common/PerfTimer.h" -#include "Lib/PathUtil.h" DECLARE_PERF_TIMER(FileSystem) diff --git a/Core/GameEngine/Source/GameClient/MapUtil.cpp b/Core/GameEngine/Source/GameClient/MapUtil.cpp index 10fdf14e775..4e9abcf82be 100644 --- a/Core/GameEngine/Source/GameClient/MapUtil.cpp +++ b/Core/GameEngine/Source/GameClient/MapUtil.cpp @@ -60,7 +60,6 @@ #include "GameLogic/FPUControl.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/NetworkDefs.h" -#include "Lib/PathUtil.h" //------------------------------------------------------------------------------- diff --git a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index bf2ea03f35c..6d612405225 100644 --- a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -59,7 +59,6 @@ #include "WWMath/plane.h" #include "WWMath/tri.h" -#include "Lib/PathUtil.h" // GLOBALS //////////////////////////////////////////////////////////////////////////////////////// @@ -1141,12 +1140,11 @@ void TerrainLogic::enableWaterGrid( Bool enable ) // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when // loading the map from a save game file // - // TheSuperHackers @bugfix bobtista 29/09/2026 Match water settings regardless of path separators. - AsciiString strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); - AsciiString strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); + const char* strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); + const char* strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare - if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 ) + if( _stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) { waterSettingIndex = i; diff --git a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp index 1ea32f5a84a..7ee22b65476 100644 --- a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp +++ b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp @@ -34,7 +34,6 @@ #include "GameClient/Shell.h" #include "GameNetwork/FileTransfer.h" #include "GameNetwork/networkutil.h" -#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------- @@ -152,14 +151,9 @@ AsciiString GetBasePathFromPath( AsciiString path ) return AsciiString::TheEmptyString; } -AsciiString GetFileFromPath( AsciiString path ) -{ - return getFileName(path.str()); -} - AsciiString GetExtensionFromFile( AsciiString fname ) { - const char *s = fname.reverseFind('.'); + const char *s = getExtension(fname.str()); if (s) return s+1; return fname; @@ -167,7 +161,7 @@ AsciiString GetExtensionFromFile( AsciiString fname ) AsciiString GetBaseFileFromFile( AsciiString fname ) { - const char *s = fname.reverseFind('.'); + const char *s = getExtension(fname.str()); if (s) { Int len = s - fname.str(); @@ -183,7 +177,7 @@ AsciiString GetBaseFileFromFile( AsciiString fname ) AsciiString GetPreviewFromMap( AsciiString path ) { - AsciiString fname = GetBaseFileFromFile(GetFileFromPath(path)); + AsciiString fname = GetBaseFileFromFile(getFileName(path.str())); AsciiString base = GetBasePathFromPath(path); AsciiString out; diff --git a/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp b/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp index 183529e96b2..95ecac94593 100644 --- a/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp +++ b/Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp @@ -61,7 +61,6 @@ #include "GameNetwork/GameSpy/GSConfig.h" #include "Common/STLTypedefs.h" -#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// diff --git a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp index 1e23efb04d9..97be26ca5a4 100644 --- a/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp +++ b/Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp @@ -67,7 +67,6 @@ #include #include "MilesLoader.h" -#include "Lib/PathUtil.h" #include diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index db3a555972d..e5ddf13daaf 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -67,7 +67,6 @@ #include "WW3D2/mesh.h" #include "WW3D2/meshmdl.h" #include "Common/BitFlagsIO.h" -#include "Lib/PathUtil.h" //------------------------------------------------------------------------------------------------- diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index 698699fc82f..a63c14bbbd8 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -110,7 +110,6 @@ static void drawFramerateBar(); #endif #include "WinMain.h" -#include "Lib/PathUtil.h" // DEFINE AND ENUMS /////////////////////////////////////////////////////////// diff --git a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h index e505e9b6bb1..0bf2e7cb21a 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h +++ b/Core/Libraries/Source/WWVegas/WWLib/WWCommon.h @@ -18,6 +18,7 @@ #pragma once +#include "Lib/PathUtil.h" #include "ref_ptr.h" #include "refcount.h" #include "Utility/STLUtils.h" diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 38e98d96095..028a7c220cc 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -49,7 +49,6 @@ #include "Common/CRCDebug.h" #include "Common/OptionPreferences.h" #include "Common/version.h" -#include "Lib/PathUtil.h" constexpr const char s_genrep[] = "GENREP"; constexpr const UnsignedInt replayBufferBytes = 8192; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp b/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp index 871df9c5d08..d6b4c01232e 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp @@ -62,7 +62,6 @@ #include "GameClient/MapUtil.h" #include "GameNetwork/networkutil.h" #include "GameNetwork/LANAPICallbacks.h" -#include "Lib/PathUtil.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index 6c10c8f2a91..e04e1843f13 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -58,7 +58,6 @@ #include "GameLogic/ScriptEngine.h" #include "GameLogic/SidesList.h" #include "GameLogic/TerrainLogic.h" -#include "Lib/PathUtil.h" // PUBLIC DATA //////////////////////////////////////////////////////////////////////////////////// diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp index c5f0178c046..0356334ae10 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp @@ -59,7 +59,6 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" -#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonCancelID = NAMEKEY_INVALID; @@ -144,7 +143,7 @@ HRESULT DownloadManagerMunkee::downloadFile( AsciiString server, AsciiString use if (staticTextFile) { // just get the filename, not the pathname - AsciiString bob = getFileName(file.str()); + const char* bob = getFileName(file.str()); UnicodeString fileString; fileString.translate(bob); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp index fb1485f515b..5bbe6581f14 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp @@ -43,7 +43,6 @@ #include "Common/PlayerTemplate.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/LANAPI.h" -#include "Lib/PathUtil.h" static GameWindow *parent = nullptr; 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 814660dc96b..c2b58991e83 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -59,7 +59,6 @@ #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" -#include "Lib/PathUtil.h" extern char *LANnextScreen; @@ -951,8 +950,7 @@ void updateGameOptions() } else { - AsciiString s = getFileName(TheLAN->GetMyGame()->getMap().str()); - mapDisplayName.format(L"%hs", s.str()); + mapDisplayName.format(L"%hs", getFileName(TheLAN->GetMyGame()->getMap().str())); } UnicodeString old = GadgetStaticTextGetText(textEntryMapDisplay); if(old.compare(mapDisplayName) != 0) diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp index f099a5712ec..215da58a940 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp @@ -60,7 +60,6 @@ #include "GameClient/Shell.h" #include "GameLogic/GameLogic.h" #include "GameClient/GameWindowTransitions.h" -#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonBackKey = NAMEKEY_INVALID; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp index bc26bcd94eb..ac62c5e40dd 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp @@ -47,7 +47,6 @@ #include "GameClient/Mouse.h" #include "GameClient/GameText.h" #include "GameClient/GameWindowTransitions.h" -#include "Lib/PathUtil.h" typedef UnicodeString ReplayName; typedef UnicodeString TooltipString; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp index 36cb3805703..894bb30cf8c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp @@ -65,7 +65,6 @@ #include "GameNetwork/NAT.h" #include "GameNetwork/GUIUtil.h" #include "GameNetwork/GameSpy/GSConfig.h" -#include "Lib/PathUtil.h" void WOLDisplaySlotList(); @@ -971,9 +970,8 @@ void WOLDisplayGameOptions() } else { - AsciiString s = getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str()); UnicodeString mapDisplay; - mapDisplay.translate(s); + mapDisplay.translate(getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str())); GadgetStaticTextSetText(textEntryMapDisplay, mapDisplay); } WOLPositionStartSpots(); From de34e9dec7d1c7305ccda01b6b8614d3c9641af8 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 30 Sep 2026 12:58:13 -0400 Subject: [PATCH 10/22] refactor(generals): Align filename review cleanup --- Generals/Code/GameEngine/Source/Common/Recorder.cpp | 1 - Generals/Code/GameEngine/Source/Common/StatsCollector.cpp | 1 - .../Source/Common/System/SaveGame/GameState.cpp | 1 - .../GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp | 3 +-- .../GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp | 1 - .../GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp | 4 +--- .../GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp | 1 - .../GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp | 1 - .../GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp | 4 +--- .../Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 8 +++----- 10 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index 6456b1bebcd..f35720a02e8 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -49,7 +49,6 @@ #include "Common/CRCDebug.h" #include "Common/OptionPreferences.h" #include "Common/version.h" -#include "Lib/PathUtil.h" constexpr const char s_genrep[] = "GENREP"; constexpr const UnsignedInt replayBufferBytes = 8192; diff --git a/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp b/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp index 378169548fb..763fad9f2bb 100644 --- a/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp +++ b/Generals/Code/GameEngine/Source/Common/StatsCollector.cpp @@ -62,7 +62,6 @@ #include "GameClient/MapUtil.h" #include "GameNetwork/networkutil.h" #include "GameNetwork/LANAPICallbacks.h" -#include "Lib/PathUtil.h" //----------------------------------------------------------------------------- // DEFINES //////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------------- diff --git a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp index b1ba6805d03..874d49b4d5d 100644 --- a/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp +++ b/Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp @@ -58,7 +58,6 @@ #include "GameLogic/ScriptEngine.h" #include "GameLogic/SidesList.h" #include "GameLogic/TerrainLogic.h" -#include "Lib/PathUtil.h" // PUBLIC DATA //////////////////////////////////////////////////////////////////////////////////// diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp index 024701fb397..b27cdbbc2ab 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp @@ -59,7 +59,6 @@ #include "GameNetwork/DownloadManager.h" #include "GameNetwork/GameSpy/MainMenuUtils.h" -#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonCancelID = NAMEKEY_INVALID; @@ -144,7 +143,7 @@ HRESULT DownloadManagerMunkee::downloadFile( AsciiString server, AsciiString use if (staticTextFile) { // just get the filename, not the pathname - AsciiString bob = getFileName(file.str()); + const char* bob = getFileName(file.str()); UnicodeString fileString; fileString.translate(bob); diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp index 8885fe78bb2..98326801074 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp @@ -43,7 +43,6 @@ #include "Common/PlayerTemplate.h" #include "GameNetwork/GameInfo.h" #include "GameNetwork/LANAPI.h" -#include "Lib/PathUtil.h" static GameWindow *parent = nullptr; 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 12959eaec46..64dd03da210 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp @@ -56,7 +56,6 @@ #include "Common/MultiplayerSettings.h" #include "GameClient/GameText.h" #include "GameNetwork/GUIUtil.h" -#include "Lib/PathUtil.h" extern char *LANnextScreen; @@ -871,8 +870,7 @@ void updateGameOptions() } else { - AsciiString s = getFileName(TheLAN->GetMyGame()->getMap().str()); - mapDisplayName.format(L"%hs", s.str()); + mapDisplayName.format(L"%hs", getFileName(TheLAN->GetMyGame()->getMap().str())); } UnicodeString old = GadgetStaticTextGetText(textEntryMapDisplay); if(old.compare(mapDisplayName) != 0) diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp index 9e4da851fa4..f0a9365b0dd 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp @@ -60,7 +60,6 @@ #include "GameClient/Shell.h" #include "GameLogic/GameLogic.h" #include "GameClient/GameWindowTransitions.h" -#include "Lib/PathUtil.h" // PRIVATE DATA /////////////////////////////////////////////////////////////////////////////////// static NameKeyType buttonBackKey = NAMEKEY_INVALID; diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp index c98820ff21a..6e69b9b8ef4 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp @@ -47,7 +47,6 @@ #include "GameClient/Mouse.h" #include "GameClient/GameText.h" #include "GameClient/GameWindowTransitions.h" -#include "Lib/PathUtil.h" typedef UnicodeString ReplayName; typedef UnicodeString TooltipString; diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp index 7f718dfa3fd..61a9b939185 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp @@ -63,7 +63,6 @@ #include "GameNetwork/NAT.h" #include "GameNetwork/GUIUtil.h" #include "GameNetwork/GameSpy/GSConfig.h" -#include "Lib/PathUtil.h" void WOLDisplaySlotList(); @@ -909,9 +908,8 @@ void WOLDisplayGameOptions() } else { - AsciiString s = getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str()); UnicodeString mapDisplay; - mapDisplay.translate(s); + mapDisplay.translate(getFileName(TheGameSpyInfo->getCurrentStagingRoom()->getMap().str())); GadgetStaticTextSetText(textEntryMapDisplay, mapDisplay); } WOLPositionStartSpots(); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 4c1cbbdb19d..3f55ea23f86 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -59,7 +59,6 @@ #include "WWMath/plane.h" #include "WWMath/tri.h" -#include "Lib/PathUtil.h" // GLOBALS //////////////////////////////////////////////////////////////////////////////////////// @@ -1141,12 +1140,11 @@ void TerrainLogic::enableWaterGrid( Bool enable ) // the fact that the map Data\CHI01\CHI01.map will turn into Save\CHI01.map when // loading the map from a save game file // - // TheSuperHackers @bugfix bobtista 29/09/2026 Match water settings regardless of path separators. - AsciiString strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); - AsciiString strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); + const char* strippedMapNameOnly = getFileName( TheGlobalData->m_mapName.str() ); + const char* strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare - if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 ) + if( _stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) { waterSettingIndex = i; From afd4bfc34627ab47313661765d9c19b3ff211953 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 30 Sep 2026 17:57:10 -0400 Subject: [PATCH 11/22] refactor(pathutil): Simplify filename helpers and avoid string copies --- .../Include/GameNetwork/FileTransfer.h | 1 - Core/GameEngine/Source/Common/CRCDebug.cpp | 21 +++++++--------- .../Source/GameLogic/Map/TerrainLogic.cpp | 2 +- .../Source/GameNetwork/FileTransfer.cpp | 24 ++----------------- .../GameEngine/Source/Common/Recorder.cpp | 5 ++-- 5 files changed, 14 insertions(+), 39 deletions(-) diff --git a/Core/GameEngine/Include/GameNetwork/FileTransfer.h b/Core/GameEngine/Include/GameNetwork/FileTransfer.h index f973dc0c6a3..bccd2bba58b 100644 --- a/Core/GameEngine/Include/GameNetwork/FileTransfer.h +++ b/Core/GameEngine/Include/GameNetwork/FileTransfer.h @@ -34,7 +34,6 @@ class GameInfo; // Convenience functions AsciiString GetBasePathFromPath( AsciiString path ); -AsciiString GetExtensionFromFile( AsciiString fname ); AsciiString GetBaseFileFromFile( AsciiString fname ); AsciiString GetPreviewFromMap( AsciiString path ); AsciiString GetINIFromMap( AsciiString path ); diff --git a/Core/GameEngine/Source/Common/CRCDebug.cpp b/Core/GameEngine/Source/Common/CRCDebug.cpp index 2f3aec51ec9..1fedb680f34 100644 --- a/Core/GameEngine/Source/Common/CRCDebug.cpp +++ b/Core/GameEngine/Source/Common/CRCDebug.cpp @@ -181,11 +181,6 @@ void outputCRCDumpLines() */ } -static AsciiString getFname(AsciiString path) -{ - return getFileName(path.str()); -} - static void addCRCDebugLineInternal(bool count, const char *fmt, va_list args) { if (TheGameLogic == nullptr || !(IS_FRAME_OK_TO_LOG)) @@ -278,9 +273,9 @@ void dumpVector3(const Vector3 *v, AsciiString name, AsciiString fname, Int line { if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); - fname = getFname(fname); + const char* fileName = getFileName(fname.str()); addCRCDebugLine("dumpVector3() %s:%d %s %8.8X %8.8X %8.8X", - fname.str(), line, name.str(), + fileName, line, name.str(), AS_INT(v->X), AS_INT(v->Y), AS_INT(v->Z)); } @@ -288,9 +283,9 @@ void dumpCoord3D(const Coord3D *c, AsciiString name, AsciiString fname, Int line { if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); - fname = getFname(fname); + const char* fileName = getFileName(fname.str()); addCRCDebugLine("dumpCoord3D() %s:%d %s %8.8X %8.8X %8.8X", - fname.str(), line, name.str(), + fileName, line, name.str(), AS_INT(c->x), AS_INT(c->y), AS_INT(c->z)); } @@ -298,10 +293,10 @@ void dumpMatrix3D(const Matrix3D *m, AsciiString name, AsciiString fname, Int li { if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); - fname = getFname(fname); + const char* fileName = getFileName(fname.str()); const Real *matrix = (const Real *)m; addCRCDebugLine("dumpMatrix3D() %s:%d %s", - fname.str(), line, name.str()); + fileName, line, name.str()); for (Int i=0; i<3; ++i) addCRCDebugLine(" 0x%08X 0x%08X 0x%08X 0x%08X", AS_INT(matrix[(i<<2)+0]), AS_INT(matrix[(i<<2)+1]), AS_INT(matrix[(i<<2)+2]), AS_INT(matrix[(i<<2)+3])); @@ -311,9 +306,9 @@ void dumpReal(Real r, AsciiString name, AsciiString fname, Int line) { if (!(IS_FRAME_OK_TO_LOG)) return; fname.toLower(); - fname = getFname(fname); + const char* fileName = getFileName(fname.str()); addCRCDebugLine("dumpReal() %s:%d %s %8.8X (%f)", - fname.str(), line, name.str(), AS_INT(r), r); + fileName, line, name.str(), AS_INT(r), r); } #endif // DEBUG_CRC diff --git a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 6d612405225..efc601bfc0e 100644 --- a/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -1144,7 +1144,7 @@ void TerrainLogic::enableWaterGrid( Bool enable ) const char* strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare - if( _stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) + if( stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) { waterSettingIndex = i; diff --git a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp index 7ee22b65476..7ac6f274cc0 100644 --- a/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp +++ b/Core/GameEngine/Source/GameNetwork/FileTransfer.cpp @@ -140,37 +140,17 @@ AsciiString GetBasePathFromPath( AsciiString path ) const char *s = getLastPathSeparator(path.str()); if (s) { - Int len = s - path.str(); - - AsciiString base; - char *buf = base.getBufferForRead(len + 1); - memcpy(buf, path.str(), len); - buf[len] = 0; - return buf; + return AsciiString(path.str(), s - path.str()); } return AsciiString::TheEmptyString; } -AsciiString GetExtensionFromFile( AsciiString fname ) -{ - const char *s = getExtension(fname.str()); - if (s) - return s+1; - return fname; -} - AsciiString GetBaseFileFromFile( AsciiString fname ) { const char *s = getExtension(fname.str()); if (s) { - Int len = s - fname.str(); - - AsciiString base; - char *buf = base.getBufferForRead(len + 1); - memcpy(buf, fname.str(), len); - buf[len] = 0; - return buf; + return AsciiString(fname.str(), s - fname.str()); } return AsciiString::TheEmptyString; } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp index 028a7c220cc..6d44fc87644 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp @@ -1664,7 +1664,8 @@ AsciiString RecorderClass::getLastReplayFileName() AsciiString players; AsciiString full; AsciiString fullPlusNum; - AsciiString mapName = getFileName(game->getMap().str()); + const AsciiString mapPath = game->getMap(); + const char* mapName = getFileName(mapPath.str()); for (Int i=0; igetSlot(i); @@ -1675,7 +1676,7 @@ AsciiString RecorderClass::getLastReplayFileName() players.concat(player); } } - full.format("%s%s_%d_%d", players.str(), mapName.str(), game->getSeed(), game->getLocalSlotNum()); + full.format("%s%s_%d_%d", players.str(), mapName, game->getSeed(), game->getLocalSlotNum()); AsciiString testString; testString.format("%s%s%s", getReplayDir().str(), full.str(), replayExtention); From dd372f0dc2c0403e06329180a0161f8c1d08009b Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 30 Sep 2026 17:57:11 -0400 Subject: [PATCH 12/22] refactor(generals): Align filename helper cleanup --- Generals/Code/GameEngine/Source/Common/Recorder.cpp | 5 +++-- .../Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/Recorder.cpp b/Generals/Code/GameEngine/Source/Common/Recorder.cpp index f35720a02e8..3f4f1e731a7 100644 --- a/Generals/Code/GameEngine/Source/Common/Recorder.cpp +++ b/Generals/Code/GameEngine/Source/Common/Recorder.cpp @@ -1661,7 +1661,8 @@ AsciiString RecorderClass::getLastReplayFileName() AsciiString players; AsciiString full; AsciiString fullPlusNum; - AsciiString mapName = getFileName(game->getMap().str()); + const AsciiString mapPath = game->getMap(); + const char* mapName = getFileName(mapPath.str()); for (Int i=0; igetSlot(i); @@ -1672,7 +1673,7 @@ AsciiString RecorderClass::getLastReplayFileName() players.concat(player); } } - full.format("%s%s_%d_%d", players.str(), mapName.str(), game->getSeed(), game->getLocalSlotNum()); + full.format("%s%s_%d_%d", players.str(), mapName, game->getSeed(), game->getLocalSlotNum()); AsciiString testString; testString.format("%s%s%s", getReplayDir().str(), full.str(), replayExtention); diff --git a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp index 3f55ea23f86..9315507cfe2 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp @@ -1144,7 +1144,7 @@ void TerrainLogic::enableWaterGrid( Bool enable ) const char* strippedCompareMapNameOnly = getFileName( TheGlobalData->m_vertexWaterAvailableMaps[ i ].str() ); // now try this compare - if( _stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) + if( stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 ) { waterSettingIndex = i; From f8aad45c0e1e966f8575ef4e8921d06d73544fa3 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 3 Oct 2026 19:19:07 -0400 Subject: [PATCH 13/22] bugfix(core): Handle both separators in remaining path checks --- .../GameClient/GUI/GameWindowManagerScript.cpp | 12 ++++++------ Core/GameEngine/Source/GameNetwork/GameInfo.cpp | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp b/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp index 2b37cc9c372..6bf4b94070d 100644 --- a/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp @@ -2748,16 +2748,16 @@ GameWindow *GameWindowManager::winCreateFromScript( AsciiString filenameString, resetWindowDefaults(); // - // get the filename from the parameter, if it doesn't contain a '\' it is - // a it is assumed to be a filename only, which we will prefix a "window\" - // directory to, otherwise it is assumed to be an absolute path. When using - // a filename only make sure the current directory is set to the right - // place for the window files subdirectory + // Prefix bare filenames with the window directory. Keep supplied paths unchanged. // - if( strchr( filename, '\\' ) == nullptr ) + if( getLastPathSeparator( filename ) == nullptr ) + { snprintf( filepath, ARRAY_SIZE(filepath), "Window\\%s", filename ); + } else + { strlcpy(filepath, filename, ARRAY_SIZE(filepath)); + } // Open the input file inFile = TheFileSystem->openFile(filepath, File::READ); diff --git a/Core/GameEngine/Source/GameNetwork/GameInfo.cpp b/Core/GameEngine/Source/GameNetwork/GameInfo.cpp index d7ec6355124..0c4c25188e2 100644 --- a/Core/GameEngine/Source/GameNetwork/GameInfo.cpp +++ b/Core/GameEngine/Source/GameNetwork/GameInfo.cpp @@ -539,7 +539,7 @@ void GameInfo::setMap( AsciiString mapName ) // directory name, we can do this since the filename // is just the directory name with the file extention // added onto it. - while (mapName.find('\\') != nullptr) + while (getLastPathSeparator(mapName.str()) != nullptr) { if (!newMapName.isEmpty()) { @@ -973,7 +973,7 @@ static AsciiString buildGameInfoAsciiString(const GameInfo& game, const AsciiStr // directory name, we can do this since the filename // is just the directory name with the file extention // added onto it. - while (mapName.find('\\') != nullptr) + while (getLastPathSeparator(mapName.str()) != nullptr) { if (!newMapName.isEmpty()) { From 012ad70472bf3ee70077e19afa50c5cedd07720f Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Mon, 5 Oct 2026 17:37:23 -0400 Subject: [PATCH 14/22] bugfix(gui): Keep the window prefix for relative layout paths --- .../GameClient/GUI/GameWindowManagerScript.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp b/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp index 6bf4b94070d..2b37cc9c372 100644 --- a/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp +++ b/Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp @@ -2748,16 +2748,16 @@ GameWindow *GameWindowManager::winCreateFromScript( AsciiString filenameString, resetWindowDefaults(); // - // Prefix bare filenames with the window directory. Keep supplied paths unchanged. + // get the filename from the parameter, if it doesn't contain a '\' it is + // a it is assumed to be a filename only, which we will prefix a "window\" + // directory to, otherwise it is assumed to be an absolute path. When using + // a filename only make sure the current directory is set to the right + // place for the window files subdirectory // - if( getLastPathSeparator( filename ) == nullptr ) - { + if( strchr( filename, '\\' ) == nullptr ) snprintf( filepath, ARRAY_SIZE(filepath), "Window\\%s", filename ); - } else - { strlcpy(filepath, filename, ARRAY_SIZE(filepath)); - } // Open the input file inFile = TheFileSystem->openFile(filepath, File::READ); From af57a8ea4c872af7e32ad10ac55e9679e9210df0 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:53:03 -0400 Subject: [PATCH 15/22] bugfix(pathutil): Handle both separators in remaining engine path checks --- Core/GameEngine/Source/GameClient/MapUtil.cpp | 2 +- Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp | 2 +- Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp | 2 +- Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp | 4 ++-- Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp | 2 +- Core/Libraries/Source/WWVegas/WWAudio/Utils.h | 4 +++- Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp | 4 ++-- Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp | 6 +++--- GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp | 2 +- 9 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Core/GameEngine/Source/GameClient/MapUtil.cpp b/Core/GameEngine/Source/GameClient/MapUtil.cpp index 4e9abcf82be..8bf037e780c 100644 --- a/Core/GameEngine/Source/GameClient/MapUtil.cpp +++ b/Core/GameEngine/Source/GameClient/MapUtil.cpp @@ -1156,7 +1156,7 @@ Image *getMapPreviewImage( AsciiString mapName ) for(Int i = 0; i < portableName.getLength(); ++i) { char c = portableName.getCharAt(i); - if (c == '\\' || c == ':') + if (isPathSeparator(c) || c == ':') tempName.concat('_'); else tempName.concat(c); diff --git a/Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp b/Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp index e82c97476ca..26f7ede105c 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp @@ -1221,7 +1221,7 @@ RingPrototypeClass::RingPrototypeClass(RingRenderObjClass *ring) // if (ring->RingTexture != nullptr) { StringClass name = ring->RingTexture->Get_Full_Path(); - const char *filename = ::strrchr (name, '\\'); + const char *filename = getLastPathSeparator (name); if (filename != nullptr) { filename ++; } else { diff --git a/Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp b/Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp index b4d3cde9dad..60c409950db 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp @@ -1176,7 +1176,7 @@ SpherePrototypeClass::SpherePrototypeClass(SphereRenderObjClass *sphere) // if (sphere->SphereTexture != nullptr) { StringClass name = sphere->SphereTexture->Get_Full_Path(); - const char *filename = ::strrchr (name, '\\'); + const char *filename = getLastPathSeparator (name); if (filename != nullptr) { filename ++; } else { diff --git a/Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp b/Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp index e1dba6df305..4ee2758f673 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp @@ -518,9 +518,9 @@ static void Get_W3D_Name(const char* filename, char* w3d_name, size_t w3d_name_s // Figure out the first character of the name of the file // (bypass the path if it was given). - const char *start = strrchr(filename, '\\'); + const char *start = getLastPathSeparator(filename); if (start) - ++start; // point to first character after the last backslash + ++start; // point to first character after the last separator else start = filename; // point to the start of the filename diff --git a/Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp b/Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp index 7e9bdc689d0..7daa74d737f 100644 --- a/Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp +++ b/Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp @@ -1540,7 +1540,7 @@ AudibleSoundDefinitionClass::Create_Sound (int classid_hint) const // the current directory is set correctly. // StringClass real_filename(m_Filename,true); - const char *dir_delimiter = ::strrchr (m_Filename, '\\'); + const char *dir_delimiter = getLastPathSeparator (m_Filename); if (dir_delimiter != nullptr && m_Filename.Get_Length () > 2 && m_Filename[1] != ':') { real_filename = (dir_delimiter + 1); } diff --git a/Core/Libraries/Source/WWVegas/WWAudio/Utils.h b/Core/Libraries/Source/WWVegas/WWAudio/Utils.h index 45c215ccd4d..c5db4d0081a 100644 --- a/Core/Libraries/Source/WWVegas/WWAudio/Utils.h +++ b/Core/Libraries/Source/WWVegas/WWAudio/Utils.h @@ -40,6 +40,8 @@ #include "mss.h" #pragma warning (pop) +#include "Lib/PathUtil.h" + ///////////////////////////////////////////////////////////////////////////// // // Macros @@ -75,7 +77,7 @@ __inline LPCTSTR Get_Filename_From_Path (LPCTSTR path) { // Find the last occurrence of the directory deliminator - LPCTSTR filename = ::strrchr (path, '\\'); + LPCTSTR filename = getLastPathSeparator (path); if (filename != nullptr) { // Increment past the directory deliminator filename ++; diff --git a/Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp b/Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp index 520436548ec..a8cfede9ef4 100644 --- a/Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp +++ b/Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp @@ -1798,7 +1798,7 @@ void Cftp::GetDownloadFilename(const char *localname, char *downloadname, size_t char *s = name; while (*s) { - if (*s == '\\' || *s == '.' || *s == ' ') + if (isPathSeparator(*s) || *s == '.' || *s == ' ') *s = '_'; ++s; } @@ -1826,7 +1826,7 @@ bool Prepare_Directories(const char *rootdir, const char *filename) char newdir[256]; const char *cptr=filename; - while((cptr=strchr(cptr,'\\')) != nullptr) + while((cptr=strpbrk(cptr,"\\/")) != nullptr) { strlcpy(tempstr,filename,cptr-filename + 1); snprintf(newdir, ARRAY_SIZE(newdir), "%s\\%s", rootdir, tempstr); diff --git a/Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp b/Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp index 64f0e90e12d..eac3ae610b6 100644 --- a/Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp +++ b/Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp @@ -147,7 +147,7 @@ void SimpleFileFactoryClass::Prepend_Sub_Directory( const char * sub_directory ) // Ensure sub_directory ends with a slash, and append a semicolon char temp_sub_dir[1024]; strlcpy(temp_sub_dir, sub_directory, ARRAY_SIZE(temp_sub_dir)); - if (temp_sub_dir[sub_len - 1] != '\\') { + if (!isPathSeparator(temp_sub_dir[sub_len - 1])) { temp_sub_dir[sub_len] = '\\'; temp_sub_dir[sub_len + 1] = 0; sub_len++; @@ -182,7 +182,7 @@ void SimpleFileFactoryClass::Append_Sub_Directory( const char * sub_directory ) // Ensure sub_directory ends with a slash char temp_sub_dir[1024]; strlcpy(temp_sub_dir, sub_directory, ARRAY_SIZE(temp_sub_dir)); - if (temp_sub_dir[sub_len - 1] != '\\') { + if (!isPathSeparator(temp_sub_dir[sub_len - 1])) { temp_sub_dir[sub_len] = '\\'; temp_sub_dir[sub_len + 1] = 0; sub_len++; @@ -237,7 +237,7 @@ FileClass * SimpleFileFactoryClass::Get_File( char const *filename ) // concatenated which may not produce reasonable results. StringClass stripped_name(true); if (IsStripPath) { - const char * ptr = ::strrchr( filename, '\\' ); + const char * ptr = getLastPathSeparator( filename ); if (ptr != nullptr) { ptr++; diff --git a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp index 9209bd3c9c4..595e8dba027 100644 --- a/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp +++ b/GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp @@ -309,7 +309,7 @@ ParticleEmitterDefClass::Normalize_Filename () ::lstrcpy (path, m_Info.TextureFilename); // Find the last occurrence of the directory deliminator - LPCTSTR filename = ::strrchr (path, '\\'); + LPCTSTR filename = getLastPathSeparator (path); if (filename != nullptr) { // Increment past the directory deliminator From 252eec1540cdf8bd5ce6882001ee25f274b4e229 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:53:03 -0400 Subject: [PATCH 16/22] bugfix(generals): Handle both separators in remaining engine path checks --- Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp index af907b9a156..c22d4115da3 100644 --- a/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp +++ b/Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp @@ -306,7 +306,7 @@ ParticleEmitterDefClass::Normalize_Filename () ::lstrcpy (path, m_Info.TextureFilename); // Find the last occurrence of the directory deliminator - LPCTSTR filename = ::strrchr (path, '\\'); + LPCTSTR filename = getLastPathSeparator (path); if (filename != nullptr) { // Increment past the directory deliminator From 3816b1602bbc114d0049dc788297c55fb211ffd0 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:53:37 -0400 Subject: [PATCH 17/22] refactor(pathutil): Use separator helpers for Windows API paths --- Core/GameEngine/Source/Common/MiniLog.cpp | 2 +- Core/GameEngine/Source/Common/System/Debug.cpp | 2 +- .../GameEngine/Source/Common/System/GameMemoryInit.cpp | 2 +- Core/GameEngine/Source/Common/WorkingDirectory.cpp | 2 +- .../GameClient/Drawable/Draw/W3DModelDraw.cpp | 2 +- .../Source/W3DDevice/GameClient/W3DDisplay.cpp | 2 +- Core/Libraries/Include/Lib/PathUtil.h | 10 ++++++++++ Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp | 2 +- 8 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Core/GameEngine/Source/Common/MiniLog.cpp b/Core/GameEngine/Source/Common/MiniLog.cpp index d74f485686b..c27df56f5f4 100644 --- a/Core/GameEngine/Source/Common/MiniLog.cpp +++ b/Core/GameEngine/Source/Common/MiniLog.cpp @@ -36,7 +36,7 @@ LogClass::LogClass(const char *fname) { char buffer[ _MAX_PATH ]; GetModuleFileName( nullptr, buffer, sizeof( buffer ) ); - if (char *pEnd = strrchr(buffer, '\\')) + if (char *pEnd = getLastPathSeparator(buffer)) { *pEnd = 0; } diff --git a/Core/GameEngine/Source/Common/System/Debug.cpp b/Core/GameEngine/Source/Common/System/Debug.cpp index 815ec558ea8..533f6703d15 100644 --- a/Core/GameEngine/Source/Common/System/Debug.cpp +++ b/Core/GameEngine/Source/Common/System/Debug.cpp @@ -378,7 +378,7 @@ void DebugInit(int flags) char dirbuf[ _MAX_PATH ]; ::GetModuleFileName( nullptr, dirbuf, sizeof( dirbuf ) ); - if (char *pEnd = strrchr(dirbuf, '\\')) + if (char *pEnd = getLastPathSeparator(dirbuf)) { *(pEnd + 1) = 0; } diff --git a/Core/GameEngine/Source/Common/System/GameMemoryInit.cpp b/Core/GameEngine/Source/Common/System/GameMemoryInit.cpp index ece7906ed0e..0e0afcb686b 100644 --- a/Core/GameEngine/Source/Common/System/GameMemoryInit.cpp +++ b/Core/GameEngine/Source/Common/System/GameMemoryInit.cpp @@ -112,7 +112,7 @@ void userMemoryManagerInitPools() // we expect. so do it the hard way. char buf[_MAX_PATH]; ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char* pEnd = strrchr(buf, '\\')) + if (char* pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } diff --git a/Core/GameEngine/Source/Common/WorkingDirectory.cpp b/Core/GameEngine/Source/Common/WorkingDirectory.cpp index 7c927bf9bbe..bf19b7c95d2 100644 --- a/Core/GameEngine/Source/Common/WorkingDirectory.cpp +++ b/Core/GameEngine/Source/Common/WorkingDirectory.cpp @@ -90,7 +90,7 @@ Bool WorkingDirectory::setExecutableWorkingDirectory() return FALSE; } - Char *pEnd = strrchr(buffer, '\\'); + Char *pEnd = getLastPathSeparator(buffer); if (pEnd == nullptr) { DEBUG_LOG(("Executable path has no directory: '%s'", buffer)); diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp index e5ddf13daaf..d56e1c796f8 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp @@ -99,7 +99,7 @@ LogClass::LogClass(const char *fname) { char buffer[ _MAX_PATH ]; GetModuleFileName( nullptr, buffer, sizeof( buffer ) ); - if (char *pEnd = strrchr(buffer, '\\')) + if (char *pEnd = getLastPathSeparator(buffer)) { *pEnd = 0; } diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp index a63c14bbbd8..dd511ac555c 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp @@ -149,7 +149,7 @@ StatDumpClass::StatDumpClass( const char *fname ) { char buffer[ _MAX_PATH ]; GetModuleFileName( nullptr, buffer, sizeof( buffer ) ); - if (char *pEnd = strrchr(buffer, '\\')) + if (char *pEnd = getLastPathSeparator(buffer)) { *pEnd = 0; } diff --git a/Core/Libraries/Include/Lib/PathUtil.h b/Core/Libraries/Include/Lib/PathUtil.h index 6fcdc91fadf..0ae117427da 100644 --- a/Core/Libraries/Include/Lib/PathUtil.h +++ b/Core/Libraries/Include/Lib/PathUtil.h @@ -81,6 +81,16 @@ inline const wchar_t* getLastPathSeparator(const wchar_t* path) return path ? maxPtr(wcsrchr(path, L'/'), wcsrchr(path, L'\\')) : nullptr; } +inline char* getLastPathSeparator(char* path) +{ + return const_cast(getLastPathSeparator(static_cast(path))); +} + +inline wchar_t* getLastPathSeparator(wchar_t* path) +{ + return const_cast(getLastPathSeparator(static_cast(path))); +} + // Returns the whole path when it contains no separator inline const char* getFileName(const char* path) { diff --git a/Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp b/Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp index 50b348b8f8a..88aeb6522fa 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp +++ b/Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp @@ -347,7 +347,7 @@ AggregateDefClass::Load_Assets (const char *passet_name) ::GetCurrentDirectory (sizeof (path), path); // Ensure the path is directory delimited - if (path[::lstrlen(path)-1] != '\\') { + if (!isPathSeparator(path[::lstrlen(path)-1])) { ::lstrcat (path, "\\"); } From 88f6efa53835e8db15248fc907451d7e3b4fccb0 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:53:54 -0400 Subject: [PATCH 18/22] refactor(debug): Use separator helpers in the debug library --- Core/Libraries/Source/debug/debug_debug.cpp | 4 ++-- Core/Libraries/Source/debug/debug_io_flat.cpp | 2 +- Core/Libraries/Source/debug/debug_stack.cpp | 11 ++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Core/Libraries/Source/debug/debug_debug.cpp b/Core/Libraries/Source/debug/debug_debug.cpp index 291a06aff27..1fd996c6b36 100644 --- a/Core/Libraries/Source/debug/debug_debug.cpp +++ b/Core/Libraries/Source/debug/debug_debug.cpp @@ -1228,7 +1228,7 @@ Debug::FrameHashEntry* Debug::AddFrameEntry(unsigned addr, unsigned type, else { // no, just add file name (without path though) - e->fileOrGroup=fileOrGroup?strrchr(fileOrGroup,'\\'):nullptr; + e->fileOrGroup=fileOrGroup?getLastPathSeparator(fileOrGroup):nullptr; e->fileOrGroup=e->fileOrGroup?e->fileOrGroup+1:fileOrGroup; } @@ -1265,7 +1265,7 @@ const char *Debug::AddLogGroup(const char *fileOrGroup, const char *descr) char help[200]; // do we need to strip down fileOrGroup? - const char *p=strrchr(fileOrGroup,'\\'); + const char *p=getLastPathSeparator(fileOrGroup); const char *q=strchr(p?p:fileOrGroup,'.'); if (p||q) { diff --git a/Core/Libraries/Source/debug/debug_io_flat.cpp b/Core/Libraries/Source/debug/debug_io_flat.cpp index 115268a608f..6e5f79e1d93 100644 --- a/Core/Libraries/Source/debug/debug_io_flat.cpp +++ b/Core/Libraries/Source/debug/debug_io_flat.cpp @@ -74,7 +74,7 @@ void DebugIOFlat::OutputStream::Delete(const char *path) char *ext=strrchr(m_fileName,'.'); if (!ext) ext=m_fileName+strlen(m_fileName); - char *fileNameOnly=strrchr(m_fileName,'\\'); + char *fileNameOnly=getLastPathSeparator(m_fileName); fileNameOnly=fileNameOnly?fileNameOnly+1:m_fileName; for (;;) diff --git a/Core/Libraries/Source/debug/debug_stack.cpp b/Core/Libraries/Source/debug/debug_stack.cpp index ea21eba807e..9441a862ed2 100644 --- a/Core/Libraries/Source/debug/debug_stack.cpp +++ b/Core/Libraries/Source/debug/debug_stack.cpp @@ -31,6 +31,7 @@ #include "debug_stack.h" #include #include "Utility/stringex.h" +#include "Lib/PathUtil.h" #include // Definitions to allow run-time linking to the dbghelp.dll functions. @@ -74,7 +75,7 @@ static void InitDbghelp() char dbgHelpPath[256]; if (GetModuleFileName(nullptr,dbgHelpPath,sizeof(dbgHelpPath))) { - char *slash=strrchr(dbgHelpPath,'\\'); + char *slash=getLastPathSeparator(dbgHelpPath); if (slash) { strcpy(slash+1,"DBGHELP.DLL"); @@ -170,7 +171,7 @@ void DebugStackwalk::Signature::GetSymbol(unsigned addr, char *buf, unsigned buf char symbolBuffer[512]; GetModuleFileName((HMODULE)modBase,symbolBuffer,sizeof(symbolBuffer)); - char *p=strrchr(symbolBuffer,'\\'); // use filename only, strip off path + char *p=getLastPathSeparator(symbolBuffer); // use filename only, strip off path p=p?p+1:symbolBuffer; *buf++=' '; strcpy(buf,p); @@ -198,7 +199,7 @@ void DebugStackwalk::Signature::GetSymbol(unsigned addr, char *buf, unsigned buf if (!gDbg._SymGetLineFromAddr((HANDLE)GetCurrentProcessId(),addr,&displacement,&line)) return; - p=strrchr(line.FileName,'\\'); // use filename only, strip off path + p=getLastPathSeparator(line.FileName); // use filename only, strip off path p=p?p+1:line.FileName; if ((unsigned int)(bufEnd-buf) Date: Wed, 7 Oct 2026 14:54:09 -0400 Subject: [PATCH 19/22] bugfix(worldbuilder): Handle both separators in WorldBuilder paths --- GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp | 2 +- GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp | 2 +- GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp | 4 ++-- GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp | 4 ++-- GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp | 2 +- GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp index 12b1d28219c..b2bdffeb5a9 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp @@ -739,7 +739,7 @@ void BuildList::OnExport() try { char buffer[_MAX_PATH]; ::GetModuleFileName(nullptr, buffer, sizeof(buffer)); - if (char* pEnd = strrchr(buffer, '\\')) + if (char* pEnd = getLastPathSeparator(buffer)) { *(pEnd + 1) = 0; } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp index d97e2bfc42d..133c747e9bf 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp @@ -103,7 +103,7 @@ BOOL MeshMoldOptions::OnInitDialog() } char *nameStart = fileBuf; for (i=0; igetPath_UserData().str()); int len = strlen(dirBuf); - if (len > 0 && dirBuf[len - 1] != '\\') { + if (len > 0 && !isPathSeparator(dirBuf[len - 1])) { dirBuf[len++] = '\\'; dirBuf[len] = 0; } @@ -171,7 +171,7 @@ void SaveMap::populateMapListbox( Bool systemMaps ) fileBuf[len-4] = 0; } while (len>0) { - if (fileBuf[len] == '\\') { + if (isPathSeparator(fileBuf[len])) { len++; break; } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 13715f4a395..982b5746d50 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1296,7 +1296,7 @@ void ScriptDialog::OnSave() // change it back. char buf[_MAX_PATH]; ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } ::SetCurrentDirectory(buf); @@ -1515,7 +1515,7 @@ void ScriptDialog::OnLoad() // change it back. char buf[_MAX_PATH]; ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc(); diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp index e409b2a42f2..b973987df9b 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp @@ -420,7 +420,7 @@ void WorldHeightMapEdit::loadDirectoryOfImages(const char *pFilePath) strlcpy(dirBuf, pFilePath, ARRAY_SIZE(dirBuf)); int len = strlen(dirBuf); - if (len > 0 && dirBuf[len - 1] != '\\') { + if (len > 0 && !isPathSeparator(dirBuf[len - 1])) { dirBuf[len++] = '\\'; dirBuf[len] = 0; } diff --git a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp index 9de7548202f..4cae83e1a21 100644 --- a/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp +++ b/GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp @@ -1409,7 +1409,7 @@ BOOL CWorldBuilderDoc::OnOpenDocument(LPCTSTR lpszPathName) // clear out map-specific text TheGameText->reset(); AsciiString s = lpszPathName; - const char* lastSep = s.reverseFind('\\'); + const char* lastSep = getLastPathSeparator(s.str()); if (lastSep != nullptr) { s.truncateTo(lastSep - s.str() + 1); @@ -1420,7 +1420,7 @@ BOOL CWorldBuilderDoc::OnOpenDocument(LPCTSTR lpszPathName) WbApp()->setCurrentDirectory(AsciiString(buf)); ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } ::SetCurrentDirectory(buf); @@ -2161,7 +2161,7 @@ void CWorldBuilderDoc::OnDumpDocToText() try { char curbuf[_MAX_PATH]; GetModuleFileName(nullptr, curbuf, sizeof(curbuf)); - if (char *pEnd = strrchr(curbuf, '\\')) + if (char *pEnd = getLastPathSeparator(curbuf)) { *(pEnd + 1) = 0; } From 0792b216464755ccedd3fb9e51e64a9b769ca773 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:54:10 -0400 Subject: [PATCH 20/22] bugfix(generals): Handle both separators in WorldBuilder paths --- Generals/Code/Tools/WorldBuilder/src/BuildList.cpp | 2 +- Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp | 4 ++-- Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp | 4 ++-- Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp | 2 +- Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Generals/Code/Tools/WorldBuilder/src/BuildList.cpp b/Generals/Code/Tools/WorldBuilder/src/BuildList.cpp index 34dc1374e7f..8707f52f562 100644 --- a/Generals/Code/Tools/WorldBuilder/src/BuildList.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/BuildList.cpp @@ -739,7 +739,7 @@ void BuildList::OnExport() try { char buffer[ _MAX_PATH ]; ::GetModuleFileName( nullptr, buffer, sizeof( buffer ) ); - if (char *pEnd = strrchr(buffer, '\\')) + if (char *pEnd = getLastPathSeparator(buffer)) { *(pEnd + 1) = 0; } diff --git a/Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp b/Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp index 9640f4e4483..598ee179b9d 100644 --- a/Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp @@ -131,7 +131,7 @@ void SaveMap::populateMapListbox( Bool systemMaps ) snprintf(dirBuf, ARRAY_SIZE(dirBuf), "%sMaps\\", TheGlobalData->getPath_UserData().str()); int len = strlen(dirBuf); - if (len > 0 && dirBuf[len - 1] != '\\') { + if (len > 0 && !isPathSeparator(dirBuf[len - 1])) { dirBuf[len++] = '\\'; dirBuf[len] = 0; } @@ -171,7 +171,7 @@ void SaveMap::populateMapListbox( Bool systemMaps ) fileBuf[len-4] = 0; } while (len>0) { - if (fileBuf[len] == '\\') { + if (isPathSeparator(fileBuf[len])) { len++; break; } diff --git a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp index 2d88de1aa93..73dcb95bdbb 100644 --- a/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp @@ -1136,7 +1136,7 @@ void ScriptDialog::OnSave() // change it back. char buf[_MAX_PATH]; ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } ::SetCurrentDirectory(buf); @@ -1348,7 +1348,7 @@ void ScriptDialog::OnLoad() // change it back. char buf[_MAX_PATH]; ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } CWorldBuilderDoc* pDoc = CWorldBuilderDoc::GetActiveDoc(); diff --git a/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp b/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp index 09bbaf42c5b..8d501c156ee 100644 --- a/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp @@ -420,7 +420,7 @@ void WorldHeightMapEdit::loadDirectoryOfImages(const char *pFilePath) strlcpy(dirBuf, pFilePath, ARRAY_SIZE(dirBuf)); int len = strlen(dirBuf); - if (len > 0 && dirBuf[len - 1] != '\\') { + if (len > 0 && !isPathSeparator(dirBuf[len - 1])) { dirBuf[len++] = '\\'; dirBuf[len] = 0; } diff --git a/Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp b/Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp index 2b9866d0324..0874973eaa7 100644 --- a/Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp +++ b/Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp @@ -1352,7 +1352,7 @@ BOOL CWorldBuilderDoc::OnOpenDocument(LPCTSTR lpszPathName) // clear out map-specific text TheGameText->reset(); AsciiString s = lpszPathName; - const char* lastSep = s.reverseFind('\\'); + const char* lastSep = getLastPathSeparator(s.str()); if (lastSep != nullptr) { s.truncateTo(lastSep - s.str() + 1); @@ -1363,7 +1363,7 @@ BOOL CWorldBuilderDoc::OnOpenDocument(LPCTSTR lpszPathName) WbApp()->setCurrentDirectory(AsciiString(buf)); ::GetModuleFileName(nullptr, buf, sizeof(buf)); - if (char *pEnd = strrchr(buf, '\\')) { + if (char *pEnd = getLastPathSeparator(buf)) { *pEnd = 0; } ::SetCurrentDirectory(buf); @@ -2104,7 +2104,7 @@ void CWorldBuilderDoc::OnDumpDocToText() try { char curbuf[_MAX_PATH]; GetModuleFileName(nullptr, curbuf, sizeof(curbuf)); - if (char *pEnd = strrchr(curbuf, '\\')) + if (char *pEnd = getLastPathSeparator(curbuf)) { *(pEnd + 1) = 0; } From 8af3d62c1097e4601c9655772811cbcad0f1031b Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:56:20 -0400 Subject: [PATCH 21/22] bugfix(tools): Handle both separators in tool paths --- Core/Libraries/Include/Lib/PathUtil.h | 5 +++++ Core/Tools/Autorun/Utils.cpp | 9 +++++---- Core/Tools/Autorun/Wnd_file.cpp | 7 ++++--- Core/Tools/ImagePacker/Source/ImagePacker.cpp | 2 +- .../Source/WindowProcedures/DirectorySelect.cpp | 6 +++--- Core/Tools/Launcher/DatGen/DatGen.cpp | 3 ++- Core/Tools/Launcher/findpatch.cpp | 2 +- Core/Tools/Launcher/main.cpp | 5 +++-- Core/Tools/Launcher/patch.cpp | 2 +- Core/Tools/W3DView/GraphicView.cpp | 2 +- Core/Tools/W3DView/MainFrm.cpp | 6 +++--- Core/Tools/W3DView/SaveSettingsDialog.cpp | 2 +- Core/Tools/W3DView/Utils.cpp | 6 +++--- Core/Tools/W3DView/Utils.h | 4 ++-- Core/Tools/W3DView/W3DViewDoc.cpp | 10 +++++----- Core/Tools/WW3D/max2w3d/w3dutil.cpp | 3 ++- GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp | 2 +- GeneralsMD/Code/Tools/wdump/wdump.cpp | 4 ++-- 18 files changed, 45 insertions(+), 35 deletions(-) diff --git a/Core/Libraries/Include/Lib/PathUtil.h b/Core/Libraries/Include/Lib/PathUtil.h index 0ae117427da..e6ea4d55bfc 100644 --- a/Core/Libraries/Include/Lib/PathUtil.h +++ b/Core/Libraries/Include/Lib/PathUtil.h @@ -48,6 +48,11 @@ inline bool isPathSeparator(char ch) return ch == '/' || ch == '\\'; } +inline bool isPathSeparator(wchar_t ch) +{ + return ch == L'/' || ch == L'\\'; +} + inline bool isAbsolutePath(const char* path) { if (path == nullptr) diff --git a/Core/Tools/Autorun/Utils.cpp b/Core/Tools/Autorun/Utils.cpp index 565449b0f96..5b22499e904 100644 --- a/Core/Tools/Autorun/Utils.cpp +++ b/Core/Tools/Autorun/Utils.cpp @@ -76,6 +76,7 @@ #include "Utils.h" #include "WinFix.h" #include "Wnd_File.h" +#include "Lib/PathUtil.h" #include #include //#include "resources.h" @@ -393,7 +394,7 @@ wchar_t *Make_Current_Path_To ( const wchar_t *filename, wchar_t *path ) char *Path_Add_Back_Slash ( char *path ) { if ( path != nullptr && *path != '\0' ) { - if ( path[ strlen( path )-1 ] != '\\' ) { + if ( !isPathSeparator( path[ strlen( path )-1 ] ) ) { strcat( path, "\\" ); } } @@ -403,7 +404,7 @@ char *Path_Add_Back_Slash ( char *path ) wchar_t *Path_Add_Back_Slash ( wchar_t *path ) { if ( path != nullptr && *path != '\0' ) { - if ( path[ wcslen( path )-1 ] != '\\' ) { + if ( !isPathSeparator( path[ wcslen( path )-1 ] ) ) { wcscat( path, L"\\" ); } } @@ -427,7 +428,7 @@ wchar_t *Path_Add_Back_Slash ( wchar_t *path ) char *Path_Remove_Back_Slash ( char *path ) { if ( path != nullptr && *path != '\0' ) { - if ( path[ strlen( path )-1 ] == '\\' ) { + if ( isPathSeparator( path[ strlen( path )-1 ] ) ) { path[ strlen( path )-1 ] = '\0'; } } @@ -437,7 +438,7 @@ char *Path_Remove_Back_Slash ( char *path ) wchar_t *Path_Remove_Back_Slash ( wchar_t *path ) { if ( path != nullptr && *path != '\0' ) { - if ( path[ wcslen( path )-1 ] == L'\\' ) { + if ( isPathSeparator( path[ wcslen( path )-1 ] ) ) { path[ wcslen( path )-1 ] = L'\0'; } } diff --git a/Core/Tools/Autorun/Wnd_file.cpp b/Core/Tools/Autorun/Wnd_file.cpp index de15f9f0e9e..0f1d7b5b471 100644 --- a/Core/Tools/Autorun/Wnd_file.cpp +++ b/Core/Tools/Autorun/Wnd_file.cpp @@ -46,6 +46,7 @@ #include #include "Wnd_File.h" #include "WinFix.h" +#include "Lib/PathUtil.h" //#include "autorun.h" @@ -124,7 +125,7 @@ void Msg( int line, const char *filename, const char *fmt, ... ) //---------------------------------------------------------------------- // Make filename. //---------------------------------------------------------------------- - const char *temp = strrchr( filename, '\\' ); + const char *temp = getLastPathSeparator( filename ); if ( temp != nullptr || temp[0] != '\0' ) { temp++; strcpy( szFile, temp ); @@ -206,7 +207,7 @@ void Msg( int line, const char *filename, const wchar_t *fmt, UINT codepage, ... //---------------------------------------------------------------------- // Make filename. //---------------------------------------------------------------------- - const char *temp = strrchr( filename, '\\' ); + const char *temp = getLastPathSeparator( filename ); if ( temp != nullptr || temp[0] != '\0' ) { temp++; length = strlen( temp ); @@ -280,7 +281,7 @@ void Delete_Msg_File () //---------------------------------------------------------------------- // strcat( strcpy( DebugFile, ".\\" ), DEBUG_FILE ); GetWindowsDirectory( DebugFile, MAX_PATH ); - if ( DebugFile[ strlen( DebugFile )-1 ] != '\\' ) { + if ( !isPathSeparator( DebugFile[ strlen( DebugFile )-1 ] ) ) { strcat( DebugFile, "\\" ); } strcat( DebugFile, DEBUG_FILE ); diff --git a/Core/Tools/ImagePacker/Source/ImagePacker.cpp b/Core/Tools/ImagePacker/Source/ImagePacker.cpp index 30f6a43c931..11e814821fa 100644 --- a/Core/Tools/ImagePacker/Source/ImagePacker.cpp +++ b/Core/Tools/ImagePacker/Source/ImagePacker.cpp @@ -831,7 +831,7 @@ void ImagePacker::addImage( char *path ) for( i = len - 1; i >= 0; i-- ) { - if( path[ i ] == '\\' ) + if( isPathSeparator( path[ i ] ) ) { c = &path[ i + 1 ]; break; diff --git a/Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp b/Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp index 184f807a4c9..d1e15a5491c 100644 --- a/Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp +++ b/Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp @@ -126,7 +126,7 @@ BOOL CALLBACK DirectorySelectProc( HWND hWndDialog, UINT message, // set the current directory in the top label GetCurrentDirectory( _MAX_PATH, buffer ); - if( buffer[ strlen( buffer ) - 1 ] != '\\' ) + if( !isPathSeparator( buffer[ strlen( buffer ) - 1 ] ) ) strlcat(buffer, "\\", ARRAY_SIZE(buffer)); SetDlgItemText( hWndDialog, STATIC_CURRENT_DIR, buffer ); @@ -261,7 +261,7 @@ BOOL CALLBACK DirectorySelectProc( HWND hWndDialog, UINT message, // construct new direcotry name and update status text GetCurrentDirectory( _MAX_PATH, buffer ); - if( buffer[ strlen( buffer ) - 1 ] != '\\' ) + if( !isPathSeparator( buffer[ strlen( buffer ) - 1 ] ) ) strlcat(buffer, "\\", ARRAY_SIZE(buffer)); SetDlgItemText( hWndDialog, STATIC_CURRENT_DIR, buffer ); EnableWindow( GetDlgItem( hWndDialog, BUTTON_ADD ), FALSE ); @@ -352,7 +352,7 @@ BOOL CALLBACK DirectorySelectProc( HWND hWndDialog, UINT message, // construct new direcotry name and update status text GetCurrentDirectory( _MAX_PATH, buffer ); - if( buffer[ strlen( buffer ) - 1 ] != '\\' ) + if( !isPathSeparator( buffer[ strlen( buffer ) - 1 ] ) ) strlcat(buffer, "\\", ARRAY_SIZE(buffer)); SetDlgItemText( hWndDialog, STATIC_CURRENT_DIR, buffer ); EnableWindow( GetDlgItem( hWndDialog, BUTTON_ADD ), FALSE ); diff --git a/Core/Tools/Launcher/DatGen/DatGen.cpp b/Core/Tools/Launcher/DatGen/DatGen.cpp index 4ca1f6099e2..b17b1189724 100644 --- a/Core/Tools/Launcher/DatGen/DatGen.cpp +++ b/Core/Tools/Launcher/DatGen/DatGen.cpp @@ -25,6 +25,7 @@ #include #include "BFISH.h" #include +#include "Lib/PathUtil.h" void __cdecl doIt(); @@ -172,7 +173,7 @@ static void doIt() DebugPrint("Install dir = '%s'\n", installPath); - char *lastBackslash = strrchr((char *)installPath, '\\'); + char *lastBackslash = getLastPathSeparator((char *)installPath); if (lastBackslash) *lastBackslash = 0; // strip of \\game.exe from install path diff --git a/Core/Tools/Launcher/findpatch.cpp b/Core/Tools/Launcher/findpatch.cpp index d259d81eb34..11706c665d0 100644 --- a/Core/Tools/Launcher/findpatch.cpp +++ b/Core/Tools/Launcher/findpatch.cpp @@ -145,7 +145,7 @@ bit8 Get_App_Dir(OUT char *filename,int maxlen, ConfigFile &config,int index) // path to a file, you better end the directory with a trailing '\\'!!! char *cptr=gamePath; char *tempPtr; - while( (tempPtr=strchr(cptr,'\\')) !=nullptr) + while( (tempPtr=strpbrk(cptr,"\\/")) !=nullptr) cptr=tempPtr+1; if (cptr) *cptr=0; diff --git a/Core/Tools/Launcher/main.cpp b/Core/Tools/Launcher/main.cpp index a33aa33216c..d0fb3965fd5 100644 --- a/Core/Tools/Launcher/main.cpp +++ b/Core/Tools/Launcher/main.cpp @@ -60,6 +60,7 @@ #include "monod.h" #include "filed.h" #include "configfile.h" +#include "Lib/PathUtil.h" #include #include @@ -163,7 +164,7 @@ int main(int argc, char *argv[]) char debugFile[MAX_PATH + 3]; strcpy(debugFile, configName); strcat(debugFile, ".txt"); - strcpy(debugLogName, strrchr(configName, '\\')); + strcpy(debugLogName, getLastPathSeparator(configName)); strcat(debugLogName, "Log"); FileD outputDevice(debugFile, true); MsgManager::setAllStreams(&outputDevice); @@ -362,7 +363,7 @@ void myChdir(char *path) _splitpath( path, drive, dir, file, ext ); _makepath ( filepath, drive, dir, nullptr, nullptr ); - if ( filepath[ strlen( filepath ) - 1 ] == '\\' ) + if ( isPathSeparator( filepath[ strlen( filepath ) - 1 ] ) ) { filepath[ strlen( filepath ) - 1 ] = '\0'; } diff --git a/Core/Tools/Launcher/patch.cpp b/Core/Tools/Launcher/patch.cpp index e780d37d208..d045c72a86d 100644 --- a/Core/Tools/Launcher/patch.cpp +++ b/Core/Tools/Launcher/patch.cpp @@ -248,7 +248,7 @@ void Apply_Patch(char *patchfile,ConfigFile &config,int skuIndex) char *cptr=patchfile; char *tempPtr; DWORD version; - while( (tempPtr=strchr(cptr,'\\')) !=nullptr) + while( (tempPtr=strpbrk(cptr,"\\/")) !=nullptr) cptr=tempPtr+1; if (cptr) version=atol(cptr); diff --git a/Core/Tools/W3DView/GraphicView.cpp b/Core/Tools/W3DView/GraphicView.cpp index 4569093cd45..40f33b0fc1e 100644 --- a/Core/Tools/W3DView/GraphicView.cpp +++ b/Core/Tools/W3DView/GraphicView.cpp @@ -1291,7 +1291,7 @@ CGraphicView::Load_Default_Dat () ::GetModuleFileName (nullptr, filename, sizeof (filename)); // Strip the filename from the path - LPTSTR ppath = ::strrchr (filename, '\\'); + LPTSTR ppath = getLastPathSeparator (filename); if (ppath != nullptr) { ppath[0] = 0; } diff --git a/Core/Tools/W3DView/MainFrm.cpp b/Core/Tools/W3DView/MainFrm.cpp index e42303f5ee4..ea74cc83f09 100644 --- a/Core/Tools/W3DView/MainFrm.cpp +++ b/Core/Tools/W3DView/MainFrm.cpp @@ -533,7 +533,7 @@ CMainFrame::OnCreateClient TCHAR szFileName[MAX_PATH]; ::GetModuleFileName (nullptr, szFileName, sizeof (szFileName)); - LPTSTR pszPath = ::strrchr (szFileName, '\\'); + LPTSTR pszPath = getLastPathSeparator (szFileName); if (pszPath) { pszPath[0] = 0; ::SetCurrentDirectory (szFileName); @@ -674,7 +674,7 @@ CMainFrame::WindowProc ::GetModuleFileName (nullptr, filename, sizeof (filename)); // Strip the filename from the path - LPTSTR ppath = ::strrchr (filename, '\\'); + LPTSTR ppath = getLastPathSeparator (filename); if (ppath != nullptr) { ppath[0] = 0; } @@ -3090,7 +3090,7 @@ CMainFrame::OnSaveScreenshot () // // Strip the filename from the path // - LPTSTR ppath = ::strrchr (filename, '\\'); + LPTSTR ppath = getLastPathSeparator (filename); if (ppath != nullptr) { ppath[0] = 0; } diff --git a/Core/Tools/W3DView/SaveSettingsDialog.cpp b/Core/Tools/W3DView/SaveSettingsDialog.cpp index cc35fa60188..b1276fd631b 100644 --- a/Core/Tools/W3DView/SaveSettingsDialog.cpp +++ b/Core/Tools/W3DView/SaveSettingsDialog.cpp @@ -99,7 +99,7 @@ CSaveSettingsDialog::OnBrowseButton () { TCHAR szFileName[MAX_PATH]; ::GetModuleFileName (nullptr, szFileName, sizeof (szFileName)); - LPTSTR pszPath = ::strrchr (szFileName, '\\'); + LPTSTR pszPath = getLastPathSeparator (szFileName); if (pszPath) { ::SetCurrentDirectory (pszPath); diff --git a/Core/Tools/W3DView/Utils.cpp b/Core/Tools/W3DView/Utils.cpp index 2c281a27d02..4db473031c4 100644 --- a/Core/Tools/W3DView/Utils.cpp +++ b/Core/Tools/W3DView/Utils.cpp @@ -403,7 +403,7 @@ CString Get_Filename_From_Path (LPCTSTR path) { // Find the last occurrence of the directory deliminator - LPCTSTR filename = ::strrchr (path, '\\'); + LPCTSTR filename = getLastPathSeparator (path); if (filename != nullptr) { // Increment past the directory deliminator filename ++; @@ -428,7 +428,7 @@ Strip_Filename_From_Path (LPCTSTR path) ::lstrcpy (temp_path, path); // Find the last occurrence of the directory deliminator - LPTSTR filename = ::strrchr (temp_path, '\\'); + LPTSTR filename = getLastPathSeparator (temp_path); if (filename != nullptr) { // Strip off the filename filename[0] = 0; @@ -760,7 +760,7 @@ Load_RC_Texture (LPCTSTR resource_name) void Resolve_Path (CString &filename) { - if (filename.Find ('\\') == -1) { + if (getLastPathSeparator (filename) == nullptr) { char path[MAX_PATH]; ::GetCurrentDirectory (MAX_PATH, path); ::Delimit_Path (path); diff --git a/Core/Tools/W3DView/Utils.h b/Core/Tools/W3DView/Utils.h index 82038f23e31..0f9e4695e08 100644 --- a/Core/Tools/W3DView/Utils.h +++ b/Core/Tools/W3DView/Utils.h @@ -62,14 +62,14 @@ class RenderObjClass; __inline void Delimit_Path (LPTSTR path) { - if (::lstrlen (path) > 0 && path[::lstrlen (path) - 1] != '\\') { + if (::lstrlen (path) > 0 && !isPathSeparator (path[::lstrlen (path) - 1])) { ::lstrcat (path, "\\"); } } __inline void Delimit_Path (CString &path) { - if (path[::lstrlen (path) - 1] != '\\') { + if (!isPathSeparator (path[::lstrlen (path) - 1])) { path += CString ("\\"); } } diff --git a/Core/Tools/W3DView/W3DViewDoc.cpp b/Core/Tools/W3DView/W3DViewDoc.cpp index da6d529a0a4..4f7fab7676a 100644 --- a/Core/Tools/W3DView/W3DViewDoc.cpp +++ b/Core/Tools/W3DView/W3DViewDoc.cpp @@ -489,9 +489,9 @@ CW3DViewDoc::LoadAssetsFromFile (LPCTSTR lpszPathName) // HACK HACK -- Force the current directory to be the directory // the file is located in. // - if (::strrchr (lpszPathName, '\\')) { + if (getLastPathSeparator (lpszPathName)) { CString stringTemp = lpszPathName; - stringTemp = stringTemp.Left ((long)::strrchr (lpszPathName, '\\') - (long)lpszPathName); + stringTemp = stringTemp.Left ((long)getLastPathSeparator (lpszPathName) - (long)lpszPathName); ::SetCurrentDirectory (stringTemp); _TheSimpleFileFactory->Append_Sub_Directory(stringTemp); } @@ -1440,13 +1440,13 @@ CW3DViewDoc::SaveSettings CString stringCompleteFilename = pszFilename; // Does this filename contain a path? - if (::strrchr (pszFilename, '\\') == nullptr) + if (getLastPathSeparator (pszFilename) == nullptr) { // Add the current directories path to the filename TCHAR szPath[MAX_PATH] = { 0 }; ::GetCurrentDirectory (sizeof (szPath), szPath); - if (szPath[::lstrlen (szPath)-1] != '\\') + if (!isPathSeparator (szPath[::lstrlen (szPath)-1])) { // Ensure the path is directory delimited strlcat(szPath, "\\", ARRAY_SIZE(szPath)); @@ -2334,7 +2334,7 @@ CW3DViewDoc::Make_Movie () ::GetModuleFileName (nullptr, filename, sizeof (filename)); // Strip the filename from the path - LPTSTR ppath = ::strrchr (filename, '\\'); + LPTSTR ppath = getLastPathSeparator (filename); if (ppath != nullptr) { ppath[0] = 0; } diff --git a/Core/Tools/WW3D/max2w3d/w3dutil.cpp b/Core/Tools/WW3D/max2w3d/w3dutil.cpp index 3d22d0793f8..ab5c7afbcda 100644 --- a/Core/Tools/WW3D/max2w3d/w3dutil.cpp +++ b/Core/Tools/WW3D/max2w3d/w3dutil.cpp @@ -44,6 +44,7 @@ #include "tchar.h" #include "gamemtl.h" #include "notify.h" +#include "Lib/PathUtil.h" #include "gennamesdialog.h" #include "genmtlnamesdialog.h" #include "genlodextensiondialog.h" @@ -1529,7 +1530,7 @@ void SettingsFormClass::Init() // space before or after the equal sign as well. char dllpath[_MAX_PATH]; ::GetModuleFileName(AppInstance,dllpath,sizeof(dllpath)); - char * last_slash = strrchr(dllpath,'\\'); + char * last_slash = getLastPathSeparator(dllpath); last_slash++; strcpy(last_slash,DAZZLE_SETTINGS_FILENAME); diff --git a/GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp b/GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp index 6bd7238c696..f2f3cec84ff 100644 --- a/GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp +++ b/GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp @@ -258,7 +258,7 @@ void GUIEdit::setSaveFile( const char *fullPathAndFilename ) // copy everything after the last '\' from the full path, this will // be just the filename with extension // - ptr = strrchr( fullPathAndFilename, '\\' ) + 1; + ptr = getFileName( fullPathAndFilename ); strlcpy(m_saveFilename, ptr, ARRAY_SIZE(m_saveFilename)); } diff --git a/GeneralsMD/Code/Tools/wdump/wdump.cpp b/GeneralsMD/Code/Tools/wdump/wdump.cpp index 2e6fc758ce3..c9407f82fd6 100644 --- a/GeneralsMD/Code/Tools/wdump/wdump.cpp +++ b/GeneralsMD/Code/Tools/wdump/wdump.cpp @@ -145,10 +145,10 @@ BOOL CWdumpApp::InitInstance() if(NoWindow) { if(cmdInfo.m_nShellCommand == CWDumpCommandLineInfo::FileOpen) { - const char *c = strrchr(cmdInfo.m_strFileName, '\\'); + const char *c = getLastPathSeparator(cmdInfo.m_strFileName); if(c == nullptr) c = (LPCTSTR) cmdInfo.m_strFileName; - if(*c == '\\') + if(isPathSeparator(*c)) c++; Filename = c; From d9cc9232dadd586f2791be22dd0affe72d07561b Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Wed, 7 Oct 2026 14:56:27 -0400 Subject: [PATCH 22/22] bugfix(generals): Handle both separators in tool paths --- Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp b/Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp index 8862a0eae62..490b26933ff 100644 --- a/Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp +++ b/Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp @@ -258,7 +258,7 @@ void GUIEdit::setSaveFile( const char *fullPathAndFilename ) // copy everything after the last '\' from the full path, this will // be just the filename with extension // - ptr = strrchr( fullPathAndFilename, '\\' ) + 1; + ptr = getFileName( fullPathAndFilename ); strlcpy(m_saveFilename, ptr, ARRAY_SIZE(m_saveFilename)); }