Skip to content

feat(cli): Add -loadsave option - #3001

Open
bobtista wants to merge 3 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feat/loadsave-commandline
Open

feat(cli): Add -loadsave option#3001
bobtista wants to merge 3 commits into
TheSuperHackers:mainfrom
bobtista:bobtista/feat/loadsave-commandline

Conversation

@bobtista

@bobtista bobtista commented Jul 22, 2026

Copy link
Copy Markdown

Fixes #2999

Now -loadsave loads a save from the user Save directory during startup in normal or headless mode.

Snapshots can now disable xfer to be excluded from save game data. The particle, terrain visual and ghost object snapshots disable xfer in headless mode, so their blocks are omitted when saving and are skipped like unknown blocks when loading rendered saves headlessly.

Todo:

  • Test normal save loaded normally
  • Test normal save loaded headlessly
  • Test headless save loaded normally
  • Test headless save loaded headlessly
  • Replicate to Generals

Notes:
If the save file is not found, the game logs the failure and exits after engine initialization. Debug builds show a "File not found" assert first.

@bobtista bobtista self-assigned this Jul 22, 2026
@bobtista bobtista added the Enhancement Is new feature or request label Jul 22, 2026
@greptile-apps

greptile-apps Bot commented Jul 22, 2026

Copy link
Copy Markdown

Greptile Summary

Adds command-line save loading and makes save serialization compatible across rendered and headless modes.

  • Registers -loadsave <filename> and loads the selected user save during engine startup.
  • Omits renderer-owned particle, terrain-visual, and ghost-object blocks from headless saves and skips them when loading rendered saves headlessly.
  • Centralizes Zero Hour save/load feedback and updates the relevant GUI load paths.
  • Suppresses shell, shell-map, and intro startup behavior while command-line save loading is pending.

Confidence Score: 5/5

The PR appears safe to merge because no eligible blocking failure remains from the displayed follow-up review context.

No blocking failure remains.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/Common/GameEngine.cpp Adds the base-game startup path that prepares and loads a command-line-selected save before entering the main loop.
GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp Adds the equivalent Zero Hour startup save-loading path.
Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Excludes renderer-owned snapshot blocks when saving or loading in base-game headless mode.
GeneralsMD/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Updates Zero Hour save result handling and headless snapshot-block serialization.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp Introduces shared UI feedback handling for Zero Hour save and load result codes.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/PopupSaveLoad.cpp Routes save/load menu result handling through the shared feedback implementation.
GeneralsMD/Code/GameEngine/Source/GameClient/GUI/Shell/Shell.cpp Prevents shell and shell-map startup while a command-line save load is pending.

Sequence Diagram

sequenceDiagram
  participant CLI as Command line
  participant Engine as GameEngine
  participant State as GameState
  participant Logic as GameLogic
  participant Shell as Shell

  CLI->>Engine: Set -loadsave filename
  Engine->>State: Read save metadata
  Engine->>Logic: Prepare single-player game
  Engine->>State: Load save
  alt Load succeeds
    Engine->>Shell: Hide shell
    Engine->>Engine: Enter main loop
  else Load fails
    Engine->>Engine: Exit
  end
Loading

Reviews (2): Last reviewed commit: "feat(commandline): Add -loadsave option ..." | Re-trigger Greptile

@xezon

xezon commented Aug 23, 2026

Copy link
Copy Markdown

This needs a rebase

@bobtista
bobtista force-pushed the bobtista/feat/loadsave-commandline branch from 65b9e16 to bc65271 Compare August 23, 2026 14:33
@bobtista

Copy link
Copy Markdown
Author

rebased

@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.

Needs polishing

Comment thread Core/GameEngine/Source/Common/CommandLine.cpp
Comment thread Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/feat/loadsave-commandline branch from bc65271 to a689346 Compare August 23, 2026 17:37
@bobtista

Copy link
Copy Markdown
Author

Did some polishing and updated the description - give it a try :)

@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 better, but can be improved more.

Too many "TheSuperHackers" labeled comments are added in the code. Prefer using authored comments only for crucial comments that describe a bigger change, not small tweaks, refactors and accomodating changes.

Comment thread Generals/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/GameClient/GameClient.cpp Outdated
Comment thread Core/GameEngine/Include/Common/Snapshot.h Outdated
Comment thread Core/GameEngineDevice/Source/W3DDevice/GameClient/W3DTerrainVisual.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/GameEngine.cpp Outdated
Comment thread Generals/Code/GameEngine/Source/Common/System/SaveGame/GameState.cpp Outdated
@bobtista
bobtista force-pushed the bobtista/feat/loadsave-commandline branch from a689346 to dfc6ebd Compare August 24, 2026 19:43

@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.

It's getting better, but it is still hacking its way.


m_intro = NEW Intro;
// The intro leads into the shell, which is not shown when a save game is loaded on startup
if (TheGlobalData->m_loadSaveGame.isEmpty())

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 also looks like a hack.

I expect there is a more elegant way to do this. Before TheShell is shown, TheGameLogic->prepareNewGame(GAME_SINGLE_PLAYER, DIFFICULTY_NORMAL, 0); should have executed, so there likely is some game state info available that allows skipping shell if a game is already running.

However, not starting the shell also sounds like hack. I suggest properly study this and implement a graceful shell launch without loading shell map.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I cleaned this up - now we don't create the intro when a save game is queued, so doPostIntro never suspends rendering and the shell is never pushed. That let me revert the Shell::showShell/showShellMap guards too

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Also, reworked things so startup initializes the shell normally instead of suppressing it.
-loadsave disables intro playback and the shell-map game, but the normal post-intro shell transition still runs. showShell() pushes the Main Menu directly, then the queued save is loaded. prepareNewGame() hides the shell while preserving the Main Menu on its stack, and loadGame() restores the save.
This removes the load-specific intro branch and the direct m_breakTheMovie change. MainMenuInit clears that flag through the normal path. Keeping Main Menu on the stack also restores the expected mission-end flow: game → score screen → Main Menu.
GameEngine.cpp and Shell.cpp are now unchanged. The only post-intro handoff is:

TheShell->showShellMap(TRUE);
TheShell->showShell();

if (TheGlobalData->m_loadSaveGame.isNotEmpty())
{
    TheGameState->loadQueuedSaveGame();
}

I also found two separate issues while testing that are now fixed:

  • loadGame() takes AvailableGameInfo by value again. The copy is required because resetting the engine during a mission-save load frees the caller’s save-list entry.
  • Missing or malformed -loadsave files are caught and logged instead of escaping from getSaveGameInfoFromFile() as an exception.
    Verified with normal and headless save loading in both games, including the complete mission-exit path back through the score screen to a working Main Menu.

@bobtista
bobtista force-pushed the bobtista/feat/loadsave-commandline branch from dfc6ebd to 46df43d Compare August 24, 2026 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Enhancement Is new feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add -loadsave command line option

2 participants