Toolkit to reconstruct a loadable current_run.save / current_run_mp.save
for Slay the Spire 2 from the game's completed-run history files, plus the
Steam Cloud tricks needed to actually deploy the result.
This started as a rescue mission after a multiplayer death wiped an
in-progress run. The game keeps a .run history entry for every completed
run but deletes the live save on death. Turns out that history entry is
rich enough — paired with a known-good save used as a schema template —
to rebuild an approximate pre-death state the game will load.
These are the traps that will cost you hours if you skip them. Every point here came from an actual failure mode I hit building this.
-
The game's in-process cloud sync will wipe any local save that isn't already in Steam's cloud manifest.
CloudSaveStore.SyncCloudToLocalInternalruns on every launch; the "not on remote → delete local" branch runs before the menu renders. A naive "drop rebuilt file in saves folder" approach fails silently. -
Steam Offline Mode alone does NOT stop this. Steam's in-process
ISteamRemoteStorageAPIs use the local cloud cache — they return the same answers online or offline. You need either (a) real app- level cloud disable or (b) to trick Steam's manifest. Both are documented indocs/steam-cloud-bypass.md. -
Disabling cloud in game properties requires a FULL Steam restart. Many people think they disabled cloud but Steam still reports
app: Trueto the game because the setting wasn't reloaded. Fully quit Steam from the tray icon (not just close the window) and check Task Manager confirmssteam.exeis gone before relaunching. -
The death-clear propagates to Steam Cloud. When you die, the game calls
DeleteCurrentMultiplayerRun(), which deletes both local AND cloud copies in the same call. Cloud is not a backup against death. Onlyhistory/*.runfiles survive.
- On a modern NVMe SSD with TRIM enabled (default), files deleted more than a couple of minutes ago are essentially unrecoverable from hardware. Samsung 990 PRO's garbage collection zeros TRIMed blocks aggressively. Don't waste time with Recuva/PhotoRec if the delete was hours ago — go straight to the history-rebuild path below.
-
Always snapshot before running anything here. The toolkit does this automatically in
recover_mp_run.py, but if you use the lower-level scripts manually, runscripts/snapshot.pyfirst. The--applyflag onrebuild_run.pywill overwrite files; back up. -
If game's
Report Bugbutton appears, do NOT click it. ClickIgnore/无视. The bug report may include your rebuilt save, which the devs can correctly identify as hand-edited.
-
Rebuilt runs have a fresh RNG state. Upcoming shops, card rewards, random events will differ from what the original run would have shown. Ascension, seed, deck, relics, HP, gold, map position are preserved — everything downstream of "next random roll" is a parallel universe. For byte-for-byte replay you'd need a headless STS2 simulator; that's out of scope here.
-
Multiplayer clients can't recover anything. Only the host writes
current_run_mp.save. If you were a joiner in the lost game, the save only existed on the host's machine. -
For ongoing protection, install Rewind. This toolkit is a post-mortem rescue tool. Rewind mod maintains its own checkpoint store that bypasses the death-clear flow entirely — much better for daily protection.
| Field | Recovered? |
|---|---|
| Character, deck, relics, potions | yes |
| HP, gold, max_hp | yes (from last pre-death map-point stats) |
| Ascension, modifiers, game mode, seed | yes |
| Map position, path, events seen | yes (via map_point_history) |
| RNG state for upcoming shops / draws | no — parallel universe |
| Shared relic pool progress | no |
| Map drawings (doodles) | no |
| Mid-combat / mid-event state | no |
The rebuilt run resumes at a map-point boundary (just before the death encounter by default). Luck-line fields are reset; you won't get the same shop offers or card draws as the original run, but the loadout, progression and position are preserved.
sts2_save/ library
paths.py locate saves / cloud mirror / vdf on disk
rebuild.py rebuild SerializableRun from RunHistory
validate.py schema match against a known-good template
steam_vdf.py inject entries into remotecache.vdf
snapshot.py cold backup helper
cli/ thin wrappers wired to `sts2-*` entry points
docs/ technical write-ups
examples/ scenario walkthroughs
.claude/skills/ Claude Code skill definition
pyproject.toml console_scripts + build metadata
- Windows (Steam save paths are Windows-specific)
- Python 3.10+
- No Python dependencies for the core toolkit
Optional:
pyautogui+pygetwindowfor the UI-launch test harness (not required for rebuild)
git clone https://github.com/mufanq/sts2-save-rebuild
cd sts2-save-rebuild
pip install .This installs five console commands:
| Command | Purpose |
|---|---|
sts2-recover-mp |
end-to-end pipeline (recommended) |
sts2-rebuild |
single save rebuild |
sts2-validate |
schema check against template |
sts2-snapshot |
cold backup |
sts2-inject-vdf |
inject entries into Steam's cloud manifest |
# 1. Fully quit Steam (tray icon -> Exit — NOT just close the window)
# 2. Run the end-to-end pipeline
sts2-recover-mp --backup-dir "C:/my-sts2-backups"
# 3. Launch Steam in Offline Mode, start the game, open Multiplayer.
# The 'Continue' button should appear and load your pre-death state.What it does, in order:
- Cold-backs-up your whole saves tree to
--backup-dir. - Picks the most recent
history/*.runas the source. - Picks a MP schema template (live
current_run_mp.save, the most recent*.VAL.corruptvariant, or the SP save as fallback). - Calls
rebuildwith--snapshot-offset 1(pre-death). - Static-validates the rebuilt JSON shape against the template.
- Mirrors the rebuilt file into Steam's
userdata/.../remote/directory with matching timestamps. - Injects entries into
remotecache.vdfso Steam's cloud-vs-local sync reports the file as "in cloud" and skips the delete branch.
The game has its own in-process cloud sync on startup
(CloudSaveStore.SyncCloudToLocalInternal). If it sees a local file whose
key isn't in Steam's local cloud manifest, it deletes the local file
and its .backup on every launch. Just dropping a rebuilt file into the
saves folder isn't enough — the next launch wipes it.
Workarounds the community has tried:
| Approach | Works? |
|---|---|
| App-level "Keep saves in Steam Cloud" unchecked + full Steam restart | yes — cleanest if you remember to fully exit Steam first |
| Steam client "Go Offline" | no for this game — the in-process sync still runs |
| Firewall block on Steam | fragile; Steam may refuse to launch the game |
Edit remotecache.vdf so Steam reports the file as present in cloud |
yes, and what this toolkit does. Must be combined with Offline Mode so Steam doesn't reconcile our injected entry with Valve's servers |
See docs/steam-cloud-bypass.md for the full mechanism.
# Rebuild only, write next to template
sts2-rebuild `
--history "$env:APPDATA\SlayTheSpire2\steam\<id>\modded\profile1\saves\history\1776390784.run" `
--template "$env:APPDATA\SlayTheSpire2\steam\<id>\modded\profile1\saves\current_run_mp.save" `
--snapshot-offset 1 `
--output "./rebuilt.save"
# Validate shape matches template
sts2-validate `
--template "$env:APPDATA\SlayTheSpire2\...\current_run_mp.save" `
--rebuilt "./rebuilt.save"
# Inject into Steam's cloud manifest (Steam must be fully quit)
sts2-inject-vdf `
--vdf "C:\Program Files (x86)\Steam\userdata\<steam3>\2868840\remotecache.vdf" `
--file-local "$env:APPDATA\SlayTheSpire2\...\current_run_mp.save" `
--relative-key "modded/profile1/saves/current_run_mp.save" `
--with-backupNot installing? You can run the modules directly from a clone:
python -m sts2_save.cli.rebuild --history ... --template ...Nothing here is reversible-by-magic, but the toolkit tries hard:
sts2-recover-mpalways cold-backs-up before touching anything.sts2-inject-vdfwrites a timestamped.bak_<ts>alongside the vdf.sts2-rebuildwrites to a fresh file by default;--applyin-place mode moves the original to.pre_rebuild_<ts>.
You can always drop the backup back on top of the live folder. If the
game refuses a rebuilt save, it will rename it to *.MIG.corrupt or
*.VAL.corrupt — both are valid JSON you can diff against the template
to diagnose.
- Will not work after TRIM on NVMe SSD. If your save was deleted
hours ago, recovery from unlinked sectors is near-impossible on modern
SSDs. This toolkit rebuilds from
history/*.runfiles, which the game keeps intact. - Schema drift. The game's
SerializableRunschema has migrated from v9 to v16 over patches.rebuilduses a template to sidestep migrations and pass strict JSON deserialization. If your template is from a much older build than current game, try a newer one. - Multiplayer-specific. Rebuilt MP saves pass
CanonicalizeSavebecause playernet_ids are preserved from the history. Your co-op partners need to rejoin a lobby you host — the save only carries the final party state. - This is a reverse-engineered workaround, not a supported path. Do not use it to grief friends in competitive play, and expect that future game updates can break the schema assumptions.
A skill definition is included at .claude/skills/sts2-save-rebuild.md.
If you use Claude Code, copy that file into your project's .claude/skills/
directory (or symlink the whole repo in) and the assistant will know when
and how to invoke the toolkit based on user requests like "I lost my run"
or "how do I recover a MP save".
The skill file encodes the full decision tree: single-player vs multiplayer vs client, SSD-TRIM considerations, Steam Cloud preconditions (Steam must be fully quit), and post-deployment instructions (Offline Mode launch). It also lists the exact log patterns and rename suffixes to look for when a rebuilt save fails to load, mapped to their root causes.
MIT for this repository's own code.
Slay the Spire 2 © Mega Crit Games. This toolkit does not redistribute any game assets, game code, or decompilation output. It only reads player-owned save files that the game produces and writes replacement save files conforming to the same JSON schema. If you don't own the game, don't use this toolkit.
- Rewind mod (Nexus #211) — better long-term protection: https://www.nexusmods.com/slaythespire2/mods/211
- STS2Saves mod (Nexus #459) — snapshot management: https://www.nexusmods.com/slaythespire2/mods/459
- ModSmith's decompilation guide, if you want to read the source yourself: https://cpimhoff.github.io/Sts2-ModSmith/docs/setup/decompile.html