Skip to content

bugfix(pathutil): Handle both separators when extracting filenames - #3362

Open
bobtista wants to merge 12 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/bugfix/save-map-label-separators
Open

bobtista wants to merge 12 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/bugfix/save-map-label-separators

Conversation

@bobtista

@bobtista bobtista commented Sep 25, 2026 •

Copy link
Copy Markdown

Several filename extractions search only for a backslash. Paths using forward slashes can therefore retain directory names, and some callers use an invalid pointer when no backslash is found.

Use getFileName and getLastPathSeparator from PathUtil.h to handle both separators. For example, the save list fallback label now uses foo.map for Maps/foo/foo.map. Existing map labels remain unchanged.

The changes cover save labels and default descriptions, map cache names, lobby and game setup map names, replay lists, generated stats and replay filenames, audio localization, file transfer helpers, and diagnostic output. Applied to Generals and Zero Hour.

getMapLeafAndDirName, which constructs the portable save path, and Windows-only tools such as WorldBuilder are unchanged. The vertex water map comparison continues to include the separator in the compared names.

Verification:

  • Focused checks for forward-slash, backslash, mixed-separator and bare filenames
  • Check that existing map labels are preserved
  • Verify the fallback label in the in-game save list
  • Build Generals and Zero Hour on Windows

In-game verification covers the save list fallback label. The other changed call sites have not been tested in game.

@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

The changes replace manual path-separator searches with shared PathUtil functions across map handling, menus, networking, audio, and diagnostic output in both game variants.

Changes

Shared path filename extraction

Layer / File(s) Summary
Map path handling
Generals*/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp, Core/GameEngine/Source/GameClient/MapUtil.cpp, Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp, Core/GameEngine/Source/GameNetwork/FileTransfer.cpp
Map-state, map-loading, water-grid, and file-transfer code uses getFileName or getLastPathSeparator instead of local separator searches.
Map and file name display
Core/GameEngine/Source/Common/INI/INIMapCache.cpp, Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp, Generals*/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/*
Fallback map labels, replay-list names, and download filenames use getFileName.
Diagnostic and output filenames
Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp, Core/GameEngine/Source/Common/CRCDebug.cpp, Core/GameEngineDevice/Source/*, Generals*/Code/GameEngine/Source/Common/{Recorder.cpp,StatsCollector.cpp}
Audio, CRC, asset-usage, replay, and statistics code uses getFileName for path-derived names.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: xezon

Merge Risk: 🔵 Low · up to 10a87

Maps with mixed separator styles can miss their water-grid settings. This is a narrow, pre-existing limitation, so the change is mergeable with owner awareness.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 10a87

The change lets forward-slash map paths resolve to the intended filenames during transfers. Receiving clients retain destination and content checks, and no new security bypass was established. Some uncertainty remains about where all map paths originate.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The relevant exposure is a game host sending map files to peers that lack the map, rather than a new service or deployment privilege. Forward-slash paths can now identify auxiliary files beside that map.

Trust Boundaries and Controls

  • observed — The receiver rejects failed destination normalization, disallowed extensions, and invalid content before opening the destination for writing. Destination conversion checks containment in the applicable map or save directory.
  • observed — Auxiliary transfers use fixed sibling filenames, while the original map path was already sent directly. The inspected change does not give the extracted filename control over an arbitrary auxiliary destination.

Resilience and Maintainability Implications

  • observed — The sequential transfer loop and timeout remain outside the changed extraction helpers; rejected receiver paths return before a write. No new retry or cleanup transition was identified.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly states the main change: path filename extraction now handles both separators. It is concise and specific.
Description check ✅ Passed The description directly explains the path separator bug, the utility changes, affected areas, scope, and verification performed.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bobtista bobtista self-assigned this Sep 25, 2026
@bobtista bobtista added Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker labels Sep 25, 2026
@bobtista
bobtista marked this pull request as ready for review September 26, 2026 16:58
@greptile-apps

greptile-apps Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Refactors path parsing to use centralized utility functions.

The PR appears safe to merge; no new actionable failure was identified.

Summary

The PR replaces backslash-only filename extraction with path helpers that recognize both separators across shared engine code and both game variants. The changes since the previous review simplify file-transfer helpers and diagnostic formatting, retain an owned map path while generating replay filenames, and adjust the terrain name comparison. No new actionable issue was established.

Reviews (6) · Last reviewed commit: "refactor(generals): Align filename helpe..."

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it can be simplified

Comment thread Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/bugfix/save-map-label-separators branch from ffcf008 to 312cf98 Compare September 27, 2026 19:28

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks plausible. Can you make a full audit over the code if there are any other places like that and fix them too?

@bobtista

Copy link
Copy Markdown
Author

Done. Audited all places that take a file name from a path with only a backslash and switched them to getFileName or getLastPathSeparator. I left the windows-only tools stuff eg WorldBuilder and getMapLeafAndDirName alone

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 822dca59-ce18-4fcc-ae4a-c01b2145c2d2

📥 Commits

Reviewing files that changed from the base of the PR and between 312cf98 and 10a872c.

📒 Files selected for processing (29)
  • Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
  • Core/GameEngine/Source/Common/CRCDebug.cpp
  • Core/GameEngine/Source/Common/INI/INIMapCache.cpp
  • Core/GameEngine/Source/GameClient/MapUtil.cpp
  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
  • Core/GameEngine/Source/GameNetwork/FileTransfer.cpp
  • Core/GameEngine/Source/GameNetwork/GameSpy/LobbyUtils.cpp
  • Core/GameEngineDevice/Source/MilesAudioDevice/MilesAudioManager.cpp
  • Core/GameEngineDevice/Source/W3DDevice/GameClient/Drawable/Draw/W3DModelDraw.cpp
  • Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DDisplay.cpp
  • Generals/Code/GameEngine/Source/Common/Recorder.cpp
  • Generals/Code/GameEngine/Source/Common/StatsCollector.cpp
  • Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp
  • Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp
  • Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
  • GeneralsMD/Code/GameEngine/Source/Common/Recorder.cpp
  • GeneralsMD/Code/GameEngine/Source/Common/StatsCollector.cpp
  • GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/DownloadMenu.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/GameInfoWindow.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/LanGameOptionsMenu.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ReplayMenu.cpp
  • GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/WOLGameSetupMenu.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


// create stripped map name
c = strrchr( TheGlobalData->m_mapName.str(), '\\' );
c = getLastPathSeparator( TheGlobalData->m_mapName.str() );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Compare map filenames without their separators.

When the map path and configured path use different separators, the fallback compares strings such as /CHI01.map and \CHI01.map. The comparison fails, so the water-grid settings are not applied. Advance each separator pointer before setting the stripped name.

  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1149-L1149: Set strippedMapNameOnly from c + 1.
  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1156-L1156: Set strippedCompareMapNameOnly from c + 1.
  • Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1149-L1149: Set strippedMapNameOnly from c + 1.
  • Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1156-L1156: Set strippedCompareMapNameOnly from c + 1.
📍 Affects 2 files
  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1149-L1149 (this comment)
  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1156-L1156
  • Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1149-L1149
  • Generals/Code/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp#L1156-L1156

@bobtista bobtista changed the title bugfix(savegame): Handle both separators in fallback map labels bugfix(pathutil): Handle both separators when extracting filenames Sep 29, 2026
@bobtista
bobtista force-pushed the bobtista/bugfix/save-map-label-separators branch from 10a872c to a426ff8 Compare September 29, 2026 22:21

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can see further polishing

Comment thread Core/GameEngine/Source/Common/INI/INIMapCache.cpp Outdated
Comment thread Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp Outdated
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() );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we also make most of these const char* then?

Comment thread Core/GameEngine/Source/GameNetwork/FileTransfer.cpp Outdated
Comment thread Core/GameEngine/Source/GameNetwork/FileTransfer.cpp Outdated
return path;
}

AsciiString GetExtensionFromFile( AsciiString fname )

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function appears to be uncalled. Remove?


// now try this compare
if( strippedMapNameOnly.compareNoCase( strippedCompareMapNameOnly.str() ) == 0 )
if( _stricmp( strippedMapNameOnly, strippedCompareMapNameOnly ) == 0 )

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stricmp is used dominantly

const char *fname = mapName.reverseFind('\\');
if (fname)
mapName = fname+1;
AsciiString mapName = getFileName(game->getMap().str());

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const char* ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While at it, this mumbo jumbo can be simplified by using constructor AsciiString(const char* s, int len)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same mumbo jumbo here.

@@ -183,7 +183,7 @@ void outputCRCDumpLines()

static AsciiString getFname(AsciiString path)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe remove this function too

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants