SaveData

The player's settings and the progress of three players, kept as key=value lines in savedata.txt.

Location

Headersrc/GalaxyEggbert/Game/SaveData.hpp
Sourcessrc/GalaxyEggbert/Game/SaveData.cpp; the folder it writes to: src/GalaxyEggbert/Game/UserFolder.{hpp,cpp}
NamespaceGalaxyEggbert::Game
Engine dependencyNone. Standard library only (<fstream>, <array>); it can be tested without a window.

Purpose

SaveData is the game's persistent profile. It holds the global settings of the Setup screen (sound, volumes, controls, 3D terrain, 3D effects level, frame rate, texture size, full screen) and three independent player slots, shown in the main menu as players A, B and C. Each slot remembers the lives, the mission and world last played, whether there is progress to resume, and 200 door flags: one per mission number, set when that mission has been won, which is what opens the hubs' mission bars and the main hub's golden posts.

The original game stored a fixed 640-byte binary blob. Galaxy Eggbert deliberately does not copy that layout: savedata.txt is plain text, one key=value per line, readable and hand-editable. Each player's own galaxy has a second, separate progress record (galaxyN.* keys), so playing a galaxy never touches the campaign's doors and lives.

Collaborators

Uses

Used by

Key members

A selection of the public API declared in src/GalaxyEggbert/Game/SaveData.hpp. Most setters have a matching getter that is not listed separately.

MemberSignatureWhat it does
kGamerCountstatic constexpr int kGamerCount = 3Number of player slots.
Loadvoid Load()Reads savedata.txt from the user folder. A missing or unreadable file leaves every field at its default.
Savevoid Save() constOverwrites savedata.txt with the current state.
Read / Writevoid Read(std::istream& in)
void Write(std::ostream& out) const
The same text format on any stream; used for the file and for the settings carried inside resume logs and demos.
Resetvoid Reset() noexceptEvery field back to its default, all three slots included (the galaxy mode flag is kept). The host then saves.
SetSelectedGamervoid SetSelectedGamer(int gamer) noexceptChooses the slot the progress accessors work on; clamped to 0..2.
SetLives, SetMissionNumber, SetLastWorld, SetHasProgressvoid SetLives(int lives) noexcept (and similar)Progress of the selected slot, or of its galaxy record while galaxy mode is on.
UnlockMissionDoorvoid UnlockMissionDoor(int mission) noexceptMarks a mission won; the index is the mission number (0..199). Out-of-range numbers are ignored.
IsMissionDoorUnlockedbool IsMissionDoorUnlocked(int mission) const noexceptWhether that door is open for the current slot.
GetMainGatesForGamer, GetSecondaryGatesForGamerint GetMainGatesForGamer(int gamer) const noexceptCounts for the menu's player panel: doors 180..199 are main gates, 0..179 secondary.
SetGalaxyModevoid SetGalaxyMode(bool on) noexceptWhile on, the progress accessors read and write the selected player's galaxy record. The mode itself is never saved.
GalaxyProgress, GetGalaxyGatesForGamer, ResetGalaxyProgressconst GamerSlot& GalaxyProgress(int gamer) const noexceptGalaxy progress of any slot, whatever the mode; reset when a galaxy is imported in place of the old one.
SetEffectsLevelvoid SetEffectsLevel(int level) noexcept3D effects 0..4 (Off, Low, Medium, High, Ultra), clamped.
SetFrameRate, NextFrameRatevoid SetFrameRate(int rate) noexcept
static int NextFrameRate(int rate) noexcept
One of kFrameRates = {60, 120, 144, 240}; any other value becomes 60.
SetTextureScalevoid SetTextureScale(int scale) noexcept0 auto, 1 or 2; anything else is auto. Applies at the next start.
SetEffectsVolume, SetMusicVolume, SetAccelSensitivityvoid SetEffectsVolume(int volume) noexcept0..100, clamped by ClampVolume.

The file: savedata.txt

Write() produces the lines in this order (shown here with the default values; the door strings are 200 characters of 0/1 and are shortened):

soundEnabled=1
jumpRight=1
autoZoom=1
accelActive=0
effectsVolume=100
musicVolume=100
accelSensitivity=50
fullscreen=0
terrainRelief=0
effectsLevel=0
frameRate=60
textureScale=0
selectedGamer=0
gamer0.lives=3
gamer0.missionNumber=0
gamer0.lastWorld=1
gamer0.hasProgress=0
gamer0.doorsUnlocked=0000...0000
gamer1.lives=3
...
gamer2.doorsUnlocked=0000...0000
KeyValues (default)Meaning
soundEnabled0/1 (1)Sounds on or off.
jumpRight, autoZoom, accelActive0/1 (1, 1, 0)The original's Setup toggles: which side the on-screen action buttons sit on, automatic zoom, accelerometer.
effectsVolume, musicVolume0..100 (100)The Setup's volume sliders (a Galaxy addition; 100 is the original loudness).
accelSensitivity0..100 (50)Accelerometer sensitivity.
fullscreen0/1 (0)Desktop window mode; F11 and the menu's Fullscreen / Window button store it.
terrainRelief0/1 (0)The Setup's "3D terrain".
effectsLevel0..4 (0)The Setup's "3D effects". An older key effects3d=1 is still read, as level 3 (High).
frameRate60, 120, 144, 240 (60)Frames drawn per second; the simulation still steps 60 times a second.
textureScale0, 1, 2 (0)The Setup's "Textures": Auto, 1x, 2x.
selectedGamer0..2 (0)The player chosen in the main menu.
gamerN.lives, .missionNumber, .lastWorld, .hasProgress, .doorsUnlockedN = 0..2Campaign progress of player N (lives 3, mission 0, last world 1 by default).
galaxyN.*same fieldsProgress in player N's own galaxy. Written only once that record differs from the default, so a file of a player who never played a galaxy has no such lines.

Reading is tolerant: lines without = and unknown keys are skipped, an out-of-range selectedGamer is ignored, volumes are clamped, and an unknown frame rate falls back to 60.

Where the file lives

Since 2026-11-10 the player's files (savedata.txt, resume.txt, demos/, customworlds/) live in a per-user folder chosen at start (ChooseUserFolder() in src/GalaxyEggbertCNA/main.cpp):

A player of an earlier build, which kept these files in the working folder, has them copied (never moved or overwritten) the first time, when the new folder has no savedata.txt yet (CopyOldUserFiles()).

How it is used

The host never calls Save() directly but goes through GalaxyEggbertGame::SavePlayerData(), which refuses to write while a resume log is being reconstructed or a main-menu demo plays: both run the normal simulation with someone else's settings, and neither may change the player's profile. The checkpoint at the end of a mission looks like this (src/GalaxyEggbertCNA/GalaxyEggbertGameUpdate.cpp):

if (interaction_.GameOverCount() > gameOverCountBeforeUpdate)
{
    // Real MemorizeGamerProgress() checkpoint ...
    if (!editorPlayTestActive_)
    {
        saveData_.SetLives(interaction_.Lives());
        saveData_.SetMissionNumber(worldRuntime_.GetMissionNumber());
        saveData_.SetHasProgress(true);
        SavePlayerData();
    }
    SetPhase(GalaxyEggbert::Def::GamePhase::Lost);
}

At start-up the Resume screen is offered when resume.txt holds a valid resume log or when the selected slot's hasProgress is set; the log wins, the checkpoint (mission number and lives) is the fallback.

Rules and invariants

Tests

Related pages