Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ class GlobalData : public SubsystemInterface
Bool m_buildMapCache;
AsciiString m_initialFile; ///< If this is specified, load a specific map from the command-line
AsciiString m_pendingFile; ///< If this is specified, use this map at the next game start
AsciiString m_loadSaveGame; ///< If this is specified, load a save game file from the command-line

std::vector<AsciiString> m_simulateReplays; ///< If not empty, simulate this list of replays and exit.
Int m_simulateReplayJobs; ///< Maximum number of processes to use for simulation, or SIMULATE_REPLAYS_SEQUENTIAL for sequential simulation
Expand Down
14 changes: 14 additions & 0 deletions Generals/Code/GameEngine/Source/Common/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,19 @@ Int parseVTune ( char *args[], int num )

#endif // defined(RTS_DEBUG)

// TheSuperHackers @feature bobtista 22/07/2026 Load a save game file from the command line.
Int parseLoadSave(char *args[], int num)
{
if (num > 1)
{
TheWritableGlobalData->m_loadSaveGame = args[1];
TheWritableGlobalData->m_shellMapOn = FALSE;
TheWritableGlobalData->m_playIntro = FALSE;
TheWritableGlobalData->m_playSizzle = FALSE;
}
return 2;
}

//=============================================================================
//=============================================================================

Expand Down Expand Up @@ -1159,6 +1172,7 @@ static CommandLineParam paramsForEngineInit[] =
{ "-noshaders", parseNoShaders },
{ "-quickstart", parseQuickStart },
{ "-useWaveEditor", parseUseWaveEditor },
{ "-loadsave", parseLoadSave },

// TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it.
{ "-forcefullviewport", parseFullViewport },
Expand Down
29 changes: 29 additions & 0 deletions Generals/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,6 +774,35 @@ void GameEngine::execute()
DWORD startTime = timeGetTime() / 1000;
#endif

// TheSuperHackers @feature bobtista 22/07/2026 Load a save game directly from the command line.
if (TheGlobalData->m_loadSaveGame.isNotEmpty())
{
AvailableGameInfo gameInfo;
gameInfo.filename = TheGlobalData->m_loadSaveGame;
gameInfo.next = nullptr;
gameInfo.prev = nullptr;

AsciiString fullPath = TheGameState->getFilePathInSaveDirectory(gameInfo.filename);
TheGameState->getSaveGameInfoFromFile(fullPath, &gameInfo.saveGameInfo);
TheGameLogic->prepareNewGame(GAME_SINGLE_PLAYER, DIFFICULTY_NORMAL, 0);

AsciiString filename = gameInfo.filename;
SaveCode result = TheGameState->loadGame(gameInfo);
TheWritableGlobalData->m_loadSaveGame.clear();
if (result == SC_OK)
{
if (TheShell)
{
TheShell->hideShell();
}
}
else
{
DEBUG_LOG(("Failed to load save game '%s'", filename.str()));
m_quitting = TRUE;
}
}

// pretty basic for now
while( !m_quitting )
{
Expand Down
1 change: 1 addition & 0 deletions Generals/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,7 @@ GlobalData::GlobalData()
m_buildMapCache = FALSE;
m_initialFile.clear();
m_pendingFile.clear();
m_loadSaveGame.clear();

m_simulateReplays.clear();
m_simulateReplayJobs = SIMULATE_REPLAYS_SEQUENTIAL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ static const Int MAX_SAVE_FILE_NUMBER = 99999999;
#define GAME_STATE_BLOCK_STRING "CHUNK_GameState" // block of save game data with game info data
#define CAMPAIGN_BLOCK_STRING "CHUNK_Campaign" // block of game data that has campaign info

static Bool isHeadlessOmittedBlock( const AsciiString &blockName )
{
return blockName.compareNoCase( "CHUNK_ParticleSystem" ) == 0 ||
blockName.compareNoCase( "CHUNK_TerrainVisual" ) == 0 ||
blockName.compareNoCase( "CHUNK_GhostObject" ) == 0;
}

// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
SaveGameInfo::SaveGameInfo()
Expand Down Expand Up @@ -1351,6 +1358,10 @@ void GameState::xferSaveData( Xfer *xfer, SnapshotType which )
blockName = blockInfo->blockName;

DEBUG_LOG(("Looking at block '%s'", blockName.str()));
if( TheGlobalData->m_headless && isHeadlessOmittedBlock( blockName ) )
{
continue;
}

//
// for mission save files, we only save the game state block and campaign manager
Expand Down Expand Up @@ -1448,8 +1459,15 @@ void GameState::xferSaveData( Xfer *xfer, SnapshotType which )
// read block start
blockSize = xfer->beginBlock();

// parse this data
xfer->xferSnapshot( blockInfo->snapshot );
if( TheGlobalData->m_headless && isHeadlessOmittedBlock( token ) )
{
xfer->skip( blockSize );
}
else
{
// parse this data
xfer->xferSnapshot( blockInfo->snapshot );
}

// read block end
xfer->endBlock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,8 @@ void Shell::showShell( Bool runInit )
{
DEBUG_LOG(("Shell:showShell() - %s (%s)", TheGlobalData->m_initialFile.str(), (top())?top()->getFilename().str():"no top screen"));

if(!TheGlobalData->m_initialFile.isEmpty() || !TheGlobalData->m_simulateReplays.empty())
if(!TheGlobalData->m_initialFile.isEmpty() || !TheGlobalData->m_simulateReplays.empty() ||
TheGlobalData->m_loadSaveGame.isNotEmpty())
{
return;
}
Expand Down Expand Up @@ -523,7 +524,8 @@ void Shell::showShell( Bool runInit )
void Shell::showShellMap(Bool useShellMap )
{
// we don't want any of this to show if we're loading straight into a file
if (TheGlobalData->m_initialFile.isNotEmpty() || !TheGameLogic || !TheGlobalData->m_simulateReplays.empty())
if (TheGlobalData->m_initialFile.isNotEmpty() || !TheGameLogic || !TheGlobalData->m_simulateReplays.empty() ||
TheGlobalData->m_loadSaveGame.isNotEmpty())
return;
if(useShellMap && TheGlobalData->m_shellMapOn)
{
Expand Down
2 changes: 2 additions & 0 deletions GeneralsMD/Code/GameEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ set(GAMEENGINE_SRC
# Include/GameClient/LookAtXlat.h
# Include/GameClient/MapUtil.h
Include/GameClient/MessageBox.h
Include/GameClient/SaveLoadFeedback.h
# Include/GameClient/MetaEvent.h
# Include/GameClient/Module/AnimatedParticleSysBoneClientUpdate.h
# Include/GameClient/Module/BeaconClientUpdate.h
Expand Down Expand Up @@ -779,6 +780,7 @@ set(GAMEENGINE_SRC
Source/GameClient/GUI/GUICallbacks/Menus/WOLWelcomeMenu.cpp
Source/GameClient/GUI/GUICallbacks/MessageBox.cpp
Source/GameClient/GUI/GUICallbacks/ReplayControls.cpp
Source/GameClient/GUI/GUICallbacks/SaveLoadFeedback.cpp
# Source/GameClient/GUI/HeaderTemplate.cpp
# Source/GameClient/GUI/IMEManager.cpp
# Source/GameClient/GUI/LoadScreen.cpp
Expand Down
5 changes: 3 additions & 2 deletions GeneralsMD/Code/GameEngine/Include/Common/GameState.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ class GameState : public SubsystemInterface,
SaveCode saveGame( AsciiString filename,
UnicodeString desc,
SaveFileType saveType,
SnapshotType which = SNAPSHOT_SAVELOAD ); ///< save a game
SaveCode missionSave(); ///< do a in between mission save
SnapshotType which = SNAPSHOT_SAVELOAD,
AsciiString *resolvedFilename = nullptr ); ///< save a game
SaveCode missionSave( AsciiString *resolvedFilename = nullptr ); ///< do a in between mission save
SaveCode loadGame( AvailableGameInfo gameInfo ); ///< load a save file
SaveGameInfo *getSaveGameInfo() { return &m_gameInfo; }

Expand Down
1 change: 1 addition & 0 deletions GeneralsMD/Code/GameEngine/Include/Common/GlobalData.h
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ class GlobalData : public SubsystemInterface
Bool m_buildMapCache;
AsciiString m_initialFile; ///< If this is specified, load a specific map from the command-line
AsciiString m_pendingFile; ///< If this is specified, use this map at the next game start
AsciiString m_loadSaveGame; ///< If this is specified, load a save game file from the command-line

std::vector<AsciiString> m_simulateReplays; ///< If not empty, simulate this list of replays and exit.
Int m_simulateReplayJobs; ///< Maximum number of processes to use for simulation, or SIMULATE_REPLAYS_SEQUENTIAL for sequential simulation
Expand Down
24 changes: 24 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameClient/SaveLoadFeedback.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2026 TheSuperHackers
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#pragma once

#include "Common/GameState.h"

void presentSaveResult( SaveCode result, const AsciiString &filename );
void presentLoadResult( SaveCode result, const AsciiString &filename );
14 changes: 14 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,19 @@ Int parseVTune ( char *args[], int num )

#endif // defined(RTS_DEBUG)

// TheSuperHackers @feature bobtista 22/07/2026 Load a save game file from the command line.
Int parseLoadSave(char *args[], int num)
{
if (num > 1)
{
TheWritableGlobalData->m_loadSaveGame = args[1];
TheWritableGlobalData->m_shellMapOn = FALSE;
TheWritableGlobalData->m_playIntro = FALSE;
TheWritableGlobalData->m_playSizzle = FALSE;
}
return 2;
}

//=============================================================================
//=============================================================================

Expand Down Expand Up @@ -1159,6 +1172,7 @@ static CommandLineParam paramsForEngineInit[] =
{ "-noshaders", parseNoShaders },
{ "-quickstart", parseQuickStart },
{ "-useWaveEditor", parseUseWaveEditor },
{ "-loadsave", parseLoadSave },

// TheSuperHackers @feature xezon 03/08/2025 Force full viewport for 'Control Bar Pro' Addons like GenTool did it.
{ "-forcefullviewport", parseFullViewport },
Expand Down
29 changes: 29 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,35 @@ void GameEngine::execute()
DWORD startTime = timeGetTime() / 1000;
#endif

// TheSuperHackers @feature bobtista 22/07/2026 Load a save game directly from the command line.
if (TheGlobalData->m_loadSaveGame.isNotEmpty())
{
AvailableGameInfo gameInfo;
gameInfo.filename = TheGlobalData->m_loadSaveGame;
gameInfo.next = nullptr;
gameInfo.prev = nullptr;

AsciiString fullPath = TheGameState->getFilePathInSaveDirectory(gameInfo.filename);
TheGameState->getSaveGameInfoFromFile(fullPath, &gameInfo.saveGameInfo);
TheGameLogic->prepareNewGame(GAME_SINGLE_PLAYER, DIFFICULTY_NORMAL, 0);

AsciiString filename = gameInfo.filename;
SaveCode result = TheGameState->loadGame(gameInfo);
TheWritableGlobalData->m_loadSaveGame.clear();
if (result == SC_OK)
{
if (TheShell)
{
TheShell->hideShell();
}
}
else
{
DEBUG_LOG(("Failed to load save game '%s'", filename.str()));
m_quitting = TRUE;
}
}

// pretty basic for now
while( !m_quitting )
{
Expand Down
1 change: 1 addition & 0 deletions GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,7 @@ GlobalData::GlobalData()
m_buildMapCache = FALSE;
m_initialFile.clear();
m_pendingFile.clear();
m_loadSaveGame.clear();

m_simulateReplays.clear();
m_simulateReplayJobs = SIMULATE_REPLAYS_SEQUENTIAL;
Expand Down
Loading
Loading