Skip to content
Merged
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
18 changes: 15 additions & 3 deletions Generals/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ void GameEngine::init()

// GeneralsX @feature felipebraz 08/06/2026 Auto-create SagePatch.ini in user data dir with defaults.
{
static const char *const USER_GAME_DATA_INI_PATH = "Data\\INI\\GameData.ini";

AsciiString sagePatchPath = TheWritableGlobalData->getPath_UserData();
sagePatchPath.concat("SagePatch.ini");

Expand All @@ -424,8 +426,9 @@ void GameEngine::init()
"; -----------------------------------------------------------------------------\n"
"; SagePatch - Casual QoL overrides for GeneralsX\n"
";\n"
"; Loaded by the engine after the BIG-archived Data/INI/GameData.ini, so values\n"
"; here override (not append to) the originals.\n"
"; Loaded by the engine after the BIG-archived Data/INI/GameData.ini, so values here\n"
"; override (not append to) the originals. A loose Data/INI/GameData.ini that you author\n"
"; yourself is applied after this file and therefore still wins over it.\n"
"; -----------------------------------------------------------------------------\n"
"\n"
"GameData\n"
Expand All @@ -437,7 +440,7 @@ void GameEngine::init()
" EnforceMaxCameraHeight = No\n"
" ; Keyboard scroll - vanilla 0.5 is sluggish, double it.\n"
" KeyboardScrollSpeedFactor = 1.0\n"
" ; ~5% more terrain drawn at max zoom to fix terrain pop-in.\n"
" ; ~5%% more terrain drawn at max zoom to fix terrain pop-in.\n"
" TerrainDrawDistanceScale = 1.05\n"
// GeneralsX @tweak felipebraz 20/06/2026 Default render FPS limit to 60 FPS in SagePatch.ini
" UseFPSLimit = Yes\n"
Expand Down Expand Up @@ -480,6 +483,15 @@ void GameEngine::init()
}

ini.load(sagePatchPath, INI_LOAD_OVERWRITE, nullptr);

// GeneralsX @bugfix kumait 13/08/2026 SagePatch defaults must not clobber a user-authored
// Data/INI/GameData.ini. That loose file shadows the BIG-archived one when the engine loads
// GameData above, so replaying it here restores user precedence over SagePatch. Installs
// without a loose GameData.ini are unaffected, and the archived original is never reloaded.
if (TheLocalFileSystem->doesFileExist(USER_GAME_DATA_INI_PATH))
{
ini.load(USER_GAME_DATA_INI_PATH, INI_LOAD_OVERWRITE, nullptr);
}
}
}

Expand Down
18 changes: 15 additions & 3 deletions GeneralsMD/Code/GameEngine/Source/Common/GameEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ void GameEngine::init()
// GeneralsX @feature felipebraz 08/06/2026 Auto-create SagePatch.ini in user data dir with defaults.
// This replaces the run.sh copy approach with engine-managed defaults.
{
static const char *const USER_GAME_DATA_INI_PATH = "Data\\INI\\GameData.ini";

AsciiString sagePatchPath = TheWritableGlobalData->getPath_UserData();
sagePatchPath.concat("SagePatch.ini");

Expand All @@ -483,8 +485,9 @@ void GameEngine::init()
"; -----------------------------------------------------------------------------\n"
"; SagePatch - Casual QoL overrides for GeneralsX\n"
";\n"
"; Loaded by the engine after the BIG-archived Data/INI/GameData.ini, so values\n"
"; here override (not append to) the originals.\n"
"; Loaded by the engine after the BIG-archived Data/INI/GameData.ini, so values here\n"
"; override (not append to) the originals. A loose Data/INI/GameData.ini that you author\n"
"; yourself is applied after this file and therefore still wins over it.\n"
"; -----------------------------------------------------------------------------\n"
"\n"
"GameData\n"
Expand All @@ -496,7 +499,7 @@ void GameEngine::init()
" EnforceMaxCameraHeight = No\n"
" ; Keyboard scroll - vanilla 0.5 is sluggish, double it.\n"
" KeyboardScrollSpeedFactor = 1.0\n"
" ; ~5% more terrain drawn at max zoom to fix terrain pop-in.\n"
" ; ~5%% more terrain drawn at max zoom to fix terrain pop-in.\n"
" TerrainDrawDistanceScale = 1.05\n"
// GeneralsX @tweak felipebraz 20/06/2026 Default render FPS limit to 60 FPS in SagePatch.ini
" UseFPSLimit = Yes\n"
Expand Down Expand Up @@ -539,6 +542,15 @@ void GameEngine::init()
}

ini.load(sagePatchPath, INI_LOAD_OVERWRITE, nullptr);

// GeneralsX @bugfix kumait 13/08/2026 SagePatch defaults must not clobber a user-authored
// Data/INI/GameData.ini. That loose file shadows the BIG-archived one when the engine loads
// GameData above, so replaying it here restores user precedence over SagePatch. Installs
// without a loose GameData.ini are unaffected, and the archived original is never reloaded.
if (TheLocalFileSystem->doesFileExist(USER_GAME_DATA_INI_PATH))
{
ini.load(USER_GAME_DATA_INI_PATH, INI_LOAD_OVERWRITE, nullptr);
}
}
}

Expand Down
11 changes: 11 additions & 0 deletions docs/WORKLOG/2026-08-DIARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,14 @@

### Known Remaining Issue
- `DX8Wrapper::Resize_And_Position_Window()` (`dx8wrapper.cpp`) hardcodes position `(0,0)` on SDL platforms where the Win32 path centers on the current monitor's work area, and passes physical pixels to the `SetWindowPos` compat shim, which forwards them to `SDL_SetWindowSize()`. The window-mode pass in `W3DDisplay` corrects both afterwards, so the visible result is right, but the window can briefly flash at the wrong geometry. Fixing it at source touches shared `Core/` code and needs Linux validation before it lands.

## 13/08/2026
### Stop SagePatch.ini From Overriding a User-Authored GameData.ini
- Reported symptom: `MaxCameraHeight` set in a loose `Data/INI/GameData.ini` had no effect in game.
- Root cause was load order in `GameEngine::init()`. The engine loads `Data\INI\Default\GameData` and `Data\INI\GameData` through `initSubsystem()`, then loads the auto-generated `SagePatch.ini` from the user data dir with `INI_LOAD_OVERWRITE`. SagePatch ships a hardcoded `GameData` block (`MaxCameraHeight = 350.0`, `MinCameraHeight = 100.0`, `EnforceMaxCameraHeight = No`, ...), so it silently overwrote whatever the user had authored.
- The loose `Data/INI/GameData.ini` shadows the BIG-archived copy, because `FileSystem::openFile()` resolves local before archive. The archived original is therefore never a factor once a user file exists, so the fix replays that loose file after `SagePatch.ini`, guarded by `TheLocalFileSystem->doesFileExist()`. Installs without a loose `GameData.ini` keep the previous behavior.
- Reloading is safe: under `INI_LOAD_OVERWRITE`, `GlobalData::parseGameDataDefinition()` is plain field assignment with no `newOverride()` push, and the extra load is not fed to `xferCRC`, so the startup light CRC is unchanged.
- Fixed the generated `~5% more terrain` comment, which was passed as a `fprintf` format string and lost the `%` and the following space in every `SagePatch.ini` written to date.
- Updated the generated header comment to document the precedence rule.
- Known trade-off: a loose `GameData.ini` must be a complete copy of the archived file (it shadows, not merges), so the replay makes *every* key in it authoritative — including stock values the user copied without meaning to change. Concretely, stock pins `FramesPerSecondLimit = 30`, so a user file based on the stock copy reverts SagePatch's 60 FPS default unless the user edits that line too. Keys absent from stock (e.g. `TerrainDrawDistanceScale`) keep their SagePatch values.
- Applied to both Generals and Zero Hour.
Loading