Skip to content

refactor(pathutil): Add appendPathSeparator for character buffers - #3444

Draft
bobtista wants to merge 30 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/refactor/append-path-separator
Draft

bobtista wants to merge 30 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/refactor/append-path-separator

Conversation

@bobtista

@bobtista bobtista commented Oct 8, 2026

Copy link
Copy Markdown

Follow up to #3362. Merge that first.

Adds appendPathSeparator to PathUtil.h. It appends the native separator when the path does not already end with one. Replaces the hand-written trailing separator checks in ImagePacker, W3DView, Autorun, WWLib, WW3D2 and WorldBuilder.

TODO: Replicate in Generals.

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: acbbee24-a5bc-4724-a356-849fb31fd890

📥 Commits

Reviewing files that changed from the base of the PR and between b64c8c6 and 382efc8.


📒 Files selected for processing (17)
  • Core/GameEngine/Source/Common/CommandLine.cpp
  • Core/GameEngine/Source/Common/INI/INI.cpp
  • Core/GameEngine/Source/Common/ReplaySimulation.cpp
  • Core/GameEngine/Source/GameClient/GUI/GameWindowManagerScript.cpp
  • Core/GameEngineDevice/Source/StdDevice/Common/StdBIGFileSystem.cpp
  • Core/GameEngineDevice/Source/Win32Device/Common/Win32BIGFileSystem.cpp
  • Core/Libraries/Include/Lib/PathUtil.h
  • Core/Libraries/Source/WWVegas/WWAudio/Utils.h
  • Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp
  • Core/Tools/Launcher/findpatch.cpp
  • Core/Tools/Launcher/patch.cpp
  • Generals/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
  • Generals/Code/Tools/WorldBuilder/src/TerrainMaterial.cpp
  • Generals/Code/Tools/WorldBuilder/src/TerrainModal.cpp
  • GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogic.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/TerrainMaterial.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/TerrainModal.cpp

💤 Files with no reviewable changes (1)
  • Core/Libraries/Source/WWVegas/WWAudio/Utils.h

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



Walkthrough

The pull request extends shared path utilities and updates engine, library, and tool code to use them. The changes replace backslash-specific parsing in path operations and filename extraction. They also remove two file-transfer helpers and one library filename helper.

Changes

Shared path handling

Layer / File(s) Summary
Path utility and file-transfer APIs
Core/Libraries/Include/Lib/PathUtil.h, Core/GameEngine/Include/GameNetwork/FileTransfer.h, Core/GameEngine/Source/GameNetwork/FileTransfer.cpp
PathUtil.h adds wide-character and mutable-pointer overloads, a first-separator lookup, and appendPathSeparator. File-transfer code uses shared path and extension helpers and removes GetFileFromPath and GetExtensionFromFile.
Engine path operations and map names
Core/GameEngine/Source/Common/*, Core/GameEngine/Source/GameClient/*, Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp, Core/GameEngine/Source/GameNetwork/*, Core/GameEngineDevice/Source/*
Engine code uses shared helpers to locate separators, extract filenames, append separators, and parse archive paths. Map-name fallbacks, path matching, INI loading, and preview-image naming also use the helpers.
Library assets, audio, and diagnostics
Core/Libraries/Source/WWVegas/*, Core/Libraries/Source/debug/*
Library code uses shared helpers for asset and sound filenames, download directory scanning, file-factory paths, and debug output. The inline Get_Filename_From_Path helper is removed.
Core tools and launcher paths
Core/Tools/Autorun/*, Core/Tools/ImagePacker/Source/*, Core/Tools/Launcher/*, Core/Tools/W3DView/*, Core/Tools/WW3D/max2w3d/w3dutil.cpp
Tool code uses shared helpers for filename extraction, executable-directory lookup, separator checks, and path delimiting.
Generals game-engine paths
Generals/Code/GameEngine/Source/*, Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp, GeneralsMD/Code/GameEngine/Source/*, GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp
Both game variants use shared helpers for replay names, map names, save-game labels, and map-directory paths.
Generals and GeneralsMD tool paths
Generals/Code/Tools/*, GeneralsMD/Code/Tools/*
WorldBuilder and GUI tools use shared helpers for path delimiters, executable paths, and filename extraction. GeneralsMD wdump also uses the filename helper.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Suggested reviewers: xezon

Merge Risk: 🔵 Low · up to 382ef

The path-handling refactor appears behavior-preserving for archive loading. One minor open concern remains in the W3DView Delimit_Path helper: it can mishandle an empty path. It should be addressed or explicitly accepted.

Pre-merge checks | Passed 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check Passed The title clearly identifies the main change: adding appendPathSeparator to PathUtil for character buffers.
Description check Passed The description accurately describes the new helper, its behavior, affected areas, dependency on #3362, and the remaining Generals TODO.
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.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@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: 2


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1a9d0ce7-d8fc-4873-a0c0-c8cd27bd0843
📥 Commits

Reviewing files that changed from the base of the PR and between 9176130 and 6700aad.

📒 Files selected for processing (80)
  • Core/GameEngine/Include/GameNetwork/FileTransfer.h
  • Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp
  • Core/GameEngine/Source/Common/CRCDebug.cpp
  • Core/GameEngine/Source/Common/INI/INIMapCache.cpp
  • Core/GameEngine/Source/Common/MiniLog.cpp
  • Core/GameEngine/Source/Common/System/Debug.cpp
  • Core/GameEngine/Source/Common/System/FileSystem.cpp
  • Core/GameEngine/Source/Common/System/GameMemoryInit.cpp
  • Core/GameEngine/Source/Common/WorkingDirectory.cpp
  • Core/GameEngine/Source/GameClient/MapUtil.cpp
  • Core/GameEngine/Source/GameLogic/Map/TerrainLogic.cpp
  • Core/GameEngine/Source/GameNetwork/FileTransfer.cpp
  • Core/GameEngine/Source/GameNetwork/GameInfo.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
  • Core/Libraries/Include/Lib/PathUtil.h
  • Core/Libraries/Source/WWVegas/WW3D2/agg_def.cpp
  • Core/Libraries/Source/WWVegas/WW3D2/ringobj.cpp
  • Core/Libraries/Source/WWVegas/WW3D2/sphereobj.cpp
  • Core/Libraries/Source/WWVegas/WW3D2/w3d_dep.cpp
  • Core/Libraries/Source/WWVegas/WWAudio/AudibleSound.cpp
  • Core/Libraries/Source/WWVegas/WWAudio/Utils.h
  • Core/Libraries/Source/WWVegas/WWDownload/FTP.cpp
  • Core/Libraries/Source/WWVegas/WWLib/WWCommon.h
  • Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp
  • Core/Libraries/Source/debug/debug_debug.cpp
  • Core/Libraries/Source/debug/debug_io_flat.cpp
  • Core/Libraries/Source/debug/debug_stack.cpp
  • Core/Tools/Autorun/Utils.cpp
  • Core/Tools/Autorun/Wnd_file.cpp
  • Core/Tools/ImagePacker/Source/ImagePacker.cpp
  • Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp
  • Core/Tools/Launcher/DatGen/DatGen.cpp
  • Core/Tools/Launcher/findpatch.cpp
  • Core/Tools/Launcher/main.cpp
  • Core/Tools/Launcher/patch.cpp
  • Core/Tools/W3DView/GraphicView.cpp
  • Core/Tools/W3DView/MainFrm.cpp
  • Core/Tools/W3DView/SaveSettingsDialog.cpp
  • Core/Tools/W3DView/Utils.cpp
  • Core/Tools/W3DView/Utils.h
  • Core/Tools/W3DView/W3DViewDoc.cpp
  • Core/Tools/WW3D/max2w3d/w3dutil.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
  • Generals/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp
  • Generals/Code/Tools/GUIEdit/Source/GUIEdit.cpp
  • Generals/Code/Tools/WorldBuilder/src/BuildList.cpp
  • Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp
  • Generals/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
  • Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
  • Generals/Code/Tools/WorldBuilder/src/WorldBuilderDoc.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
  • GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/part_ldr.cpp
  • GeneralsMD/Code/Tools/GUIEdit/Source/GUIEdit.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/BuildList.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/MeshMoldOptions.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/ScriptDialog.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp
  • GeneralsMD/Code/Tools/WorldBuilder/src/WorldBuilderDoc.cpp
  • GeneralsMD/Code/Tools/wdump/wdump.cpp
💤 Files with no reviewable changes (2)
  • Core/GameEngine/Include/GameNetwork/FileTransfer.h
  • Core/GameEngine/Source/Common/System/FileSystem.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.

Comment on lines +106 to +114
inline void appendPathSeparator(char* path, size_t size)
{
const size_t len = strlen(path);
if (len > 0 && len + 1 < size && !isPathSeparator(path[len - 1]))
{
path[len] = getNativePathSeparator();
path[len + 1] = '\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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

set -eu
printf '%s\n' '--- diff ---'
git diff --unified=40 ba7a17a684d6466a49f42652838f81ff90d9bd1b 6700aad51d9f5e930cf4a9f306171a02352d2437 -- Core/Libraries/Include/Lib/PathUtil.h
printf '%s\n' '--- call sites ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'appendPathSeparator(' . || test "$?" -eq 1
printf '%s\n' '--- related identifiers ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'ffactory' . || test "$?" -eq 1
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'W3DView' . || test "$?" -eq 1
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'WorldBuilder' . || test "$?" -eq 1

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 40757


🏁 Script executed:

set -eu
for spec in \
  'Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp:110,215' \
  'GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp:390,445' \
  'GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp:95,155' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:100,145' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:235,280' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:330,370' \
  'Core/Tools/Autorun/Wnd_file.cpp:245,295'; do
  file=${spec%%:*}; range=${spec##*:}
  printf '\\n--- %s:%s ---\\n' "$file" "$range"
  sed -n "${range}p" "$file"
done

printf '\\n--- ffactory declarations and uses ---\\n'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'Get_File' Core/Libraries/Source/WWVegas Generals GeneralsMD Core/Tools/W3DView || test "$?" -eq 1
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'temp_sub_dir' Core/Libraries/Source/WWVegas Generals GeneralsMD Core/Tools/W3DView || test "$?" -eq 1
printf '\\n--- path construction near asset loading ---\\n'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'strcat' Generals/Code/Libraries/Source/WWVegas/WW3D2 GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2 Generals/Code/GameEngineDevice GeneralsMD/Code/GameEngineDevice Core/Tools/W3DView || test "$?" -eq 1

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 25713


🏁 Script executed:

set -eu
for spec in \
  'Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp:110,215' \
  'GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp:390,445' \
  'GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp:95,155' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:100,145' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:235,280' \
  'Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp:330,370' \
  'Core/Tools/Autorun/Wnd_file.cpp:245,295'; do
  file=${spec%%:*}; range=${spec##*:}
  printf '\n--- %s:%s ---\n' "$file" "$range"
  sed -n "${range}p" "$file"
done

printf '\n--- ffactory declarations and uses ---\n'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'Get_File' Core/Libraries/Source/WWVegas Generals GeneralsMD Core/Tools/W3DView || test "$?" -eq 1
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'temp_sub_dir' Core/Libraries/Source/WWVegas Generals GeneralsMD Core/Tools/W3DView || test "$?" -eq 1
printf '\n--- path construction near asset loading ---\n'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'strcat' Generals/Code/Libraries/Source/WWVegas/WW3D2 GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2 Generals/Code/GameEngineDevice GeneralsMD/Code/GameEngineDevice Core/Tools/W3DView || test "$?" -eq 1

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 25695


🏁 Script executed:

set -eu
printf '%s\n' '--- SaveMap and WorldHeightMap callers ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'loadDirectoryOfImages(' GeneralsMD/Code/Tools/WorldBuilder Generals/Code/Tools/WorldBuilder || test "$?" -eq 1
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'getFileListInDirectory' GeneralsMD/Code Generals/Code Core/GameEngine GeneralsMD/Code/Tools/WorldBuilder Generals/Code/Tools/WorldBuilder || test "$?" -eq 1
printf '%s\n' '--- getFileListInDirectory definitions ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'FileSystem::getFileListInDirectory' Core Generals GeneralsMD || test "$?" -eq 1
printf '%s\n' '--- SaveMap path source declarations ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'getPath_UserData' Core Generals GeneralsMD || test "$?" -eq 1
printf '%s\n' '--- Autorun DebugFile declarations and uses ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'DebugFile' Core/Tools/Autorun || test "$?" -eq 1
printf '%s\n' '--- ImagePacker path construction ---'
sed -n '140,225p' Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp
sed -n '280,335p' Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp
printf '%s\n' '--- W3D/asset path buffers and helper-adjacent code ---'
sed -n '430,475p' GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DFileSystem.cpp
sed -n '760,815p' GeneralsMD/Code/Libraries/Source/WWVegas/WW3D2/assetmgr.cpp
sed -n '760,810p' GeneralsMD/Code/GameEngineDevice/Source/W3DDevice/GameClient/W3DAssetManager.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 30791


🏁 Script executed:

set -eu
printf '%s\n' '--- GlobalData user-data initialization and declarations ---'
sed -n '540,605p' Generals/Code/GameEngine/Include/Common/GlobalData.h
sed -n '1025,1085p' Generals/Code/GameEngine/Source/Common/GlobalData.cpp
sed -n '550,600p' GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'm_userDataDir' Generals/Code GeneralsMD/Code Core/GameEngine || test "$?" -eq 1
printf '%s\n' '--- SaveMap/OpenMap complete path blocks ---'
sed -n '55,145p' Generals/Code/Tools/WorldBuilder/src/OpenMap.cpp
sed -n '105,155p' Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp
sed -n '340,395p' Generals/Code/Tools/WorldBuilder/src/WorldBuilder.cpp
printf '%s\n' '--- local filesystem directory implementation ---'
sed -n '110,175p' Core/GameEngineDevice/Source/Win32Device/Common/Win32LocalFileSystem.cpp
sed -n '205,270p' Core/GameEngineDevice/Source/StdDevice/Common/StdLocalFileSystem.cpp
printf '%s\n' '--- Autorun constants and API call context ---'
sed -n '45,75p' Core/Tools/Autorun/Wnd_file.cpp
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'DEBUG_FILE' Core/Tools/Autorun

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 19823


🏁 Script executed:

set -eu
printf '%s\n' '--- caller diffs against merge base ---'
git diff --unified=20 ba7a17a684d6466a49f42652838f81ff90d9bd1b 6700aad51d9f5e930cf4a9f306171a02352d2437 -- \
  Core/Libraries/Source/WWVegas/WWLib/ffactory.cpp \
  Generals/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp \
  GeneralsMD/Code/Tools/WorldBuilder/src/WHeightMapEdit.cpp \
  Generals/Code/Tools/WorldBuilder/src/SaveMap.cpp \
  GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp \
  Core/Tools/Autorun/Wnd_file.cpp \
  Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp
printf '%s\n' '--- user-data path builders ---'
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'BuildUserDataPathFromIni' Generals/Code GeneralsMD/Code
rg -n -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'BuildUserDataPathFromRegistry' Generals/Code GeneralsMD/Code
sed -n '1160,1210p' Generals/Code/GameEngine/Source/Common/GlobalData.cpp
sed -n '1030,1070p' GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
printf '%s\n' '--- exact helper call inventory with surrounding declarations ---'
rg -n -C 8 -F --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'appendPathSeparator(' Core Generals GeneralsMD

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 41794


🏁 Script executed:

set -eu
printf '%s\n' '--- locate user-data builders ---'
rg -n -F -- 'GlobalData::BuildUserDataPath' .
rg -n -F -- 'BuildUserDataPathFromIni' .
rg -n -F -- 'BuildUserDataPathFromRegistry' .
printf '%s\n' '--- complete GeneralsMD SaveMap diff ---'
git diff --unified=30 ba7a17a684d6466a49f42652838f81ff90d9bd1b 6700aad51d9f5e930cf4a9f306171a02352d2437 -- GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp
printf '%s\n' '--- repository constants and path-setting documentation ---'
rg -n -i -F --glob '*.{h,hpp,c,cc,cpp,cxx,ini,txt,md}' -- 'userdata' GeneralsMD/Code/GameEngine Generals/Code/GameEngine Core | head -120

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 23355


🏁 Script executed:

set -eu
printf '%s\n' '--- Generals user-data builder ---'
sed -n '1325,1375p' Generals/Code/GameEngine/Source/Common/GlobalData.cpp
printf '%s\n' '--- GeneralsMD user-data builder ---'
sed -n '1330,1385p' GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
printf '%s\n' '--- GeneralsMD SaveMap changed block with line numbers ---'
nl -ba GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp | sed -n '118,150p'
printf '%s\n' '--- path source APIs around builder inputs ---'
rg -n -C 6 -i --glob '*.{h,hpp,c,cc,cpp,cxx}' -- 'SHGetFolderPath|GetEnvironmentVariable|Reg(Open|Query)|USERPROFILE|APPDATA|UserData' GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp Generals/Code/GameEngine/Source/Common/GlobalData.cpp

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 16863


🏁 Script executed:

set -eu
printf '%s\n' '--- exact ImagePacker path construction ---'
nl -ba Core/Tools/ImagePacker/Source/WindowProcedures/DirectorySelect.cpp | sed -n '180,205p'
printf '%s\n' '--- exact helper and affected SaveMap ranges ---'
nl -ba Core/Libraries/Include/Lib/PathUtil.h | sed -n '104,116p'
nl -ba GeneralsMD/Code/Tools/WorldBuilder/src/SaveMap.cpp | sed -n '122,149p'
printf '%s\n' '--- exact user-data builder tail ---'
nl -ba GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp | sed -n '1375,1398p'

Repository: TheSuperHackers/GeneralsGameCode

Length of output: 3775


Handle separator-appending failure before building a child path.

appendPathSeparator can leave a non-empty buffer unchanged when it has no room for the separator. SaveMap.cpp can truncate the configurable user-data path into dirBuf, then concatenate search and map names without checking the result. ImagePacker also inserts text directly after buffer; its final hard-coded \\ does not repair a missing separator.

Return a status and reject the path before concatenation.

Suggested fix
-inline void appendPathSeparator(char* path, size_t size)
+inline bool appendPathSeparator(char* path, size_t size)
 {
 	const size_t len = strlen(path);
-	if (len > 0 && len + 1 < size && !isPathSeparator(path[len - 1]))
+	if (len == 0 || isPathSeparator(path[len - 1]))
+		return true;
+	if (len + 1 < size)
 	{
 		path[len] = getNativePathSeparator();
 		path[len + 1] = '\0';
+		return true;
 	}
+	return false;
 }
-	appendPathSeparator(dirBuf, ARRAY_SIZE(dirBuf));
+	if (!appendPathSeparator(dirBuf, ARRAY_SIZE(dirBuf)))
+		return;

Apply the same check at ImagePacker and other callers before they concatenate another path component.

__inline void Delimit_Path (CString &path)
{
if (path[::lstrlen (path) - 1] != '\\') {
if (!isPathSeparator (path[::lstrlen (path) - 1])) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against an empty CString in Delimit_Path.

For an empty string, path[::lstrlen(path) - 1] reads index -1. The LPTSTR overload checks lstrlen > 0 first. Add the same check here.

Proposed fix
--- "a/Core/Tools/W3DView/Utils.h"
+++ "b/Core/Tools/W3DView/Utils.h"
@@ -69,7 +69,7 @@
 
 __inline void Delimit_Path (CString &path)
 {
-	if (!isPathSeparator (path[::lstrlen (path) - 1])) {
+	if (path.GetLength () > 0 && !isPathSeparator (path[path.GetLength () - 1])) {
 		path += CString ("\\");
 	}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!isPathSeparator (path[::lstrlen (path) - 1])) {
if (path.GetLength () > 0 && !isPathSeparator (path[path.GetLength () - 1])) {

@bobtista
bobtista force-pushed the bobtista/refactor/append-path-separator branch from 6700aad to b64c8c6 Compare October 8, 2026 19:11
@bobtista
bobtista force-pushed the bobtista/refactor/append-path-separator branch from b64c8c6 to abff68e Compare October 11, 2026 06:48
@bobtista
bobtista force-pushed the bobtista/refactor/append-path-separator branch from abff68e to 4e0b258 Compare October 11, 2026 15:52
@bobtista
bobtista force-pushed the bobtista/refactor/append-path-separator branch from 4e0b258 to 382efc8 Compare October 11, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant