re: the whole GHAD block is neither stage nor difficulty -- sixteen probe saves

Extends the previous refutation to a sweep. Probed with no effect on STAGE 02 or
Difficulty EASY: every scalar in the GHAD block (+0, +12, +16, +20, +28, +32,
+36 at 1/3/9, +40 u64, +48, +52, +56, +60, +64 raw), SHAB[0].a, and the SHAB
FILL COUNT in both directions -- record 1 filled with a copy of record 0, and
record 0 cleared. The "stage = filled-record count + 1" idea dies with it, and
so does the reading that made SHAB a per-stage result table by that route.

The panel does re-read each slot: slot 02 holds Points 4101 / Clear Ratio 5 %
and displays exactly that while its neighbours show 101 / 6 %.

Left: the phase string, the trailer, or the blob. Recorded caveat -- every save
on disc is genuinely Stage 02 EASY, so "field not found" and "panel does not vary
those two labels per slot" are not yet separated, and another probe round cannot
separate them. The next move is static: find the code that formats STAGE %02d
and read which offset it loads.

savegame_edit.py --set now packs an int into raw_* byte fields.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-11 20:01:26 +00:00
parent eac3435cc7
commit 2da9b8a9b8
2 changed files with 28 additions and 21 deletions

View File

@@ -298,35 +298,38 @@ What the title does with an edited save is itself a finding: on load it
can now develop …"). So the blob's `4`s are authoritative state and its `2`s are
not — see the economy note.
### What the probes then refuted: +36 / +52 / +56 are **not** stage or difficulty
### What the probes then refuted: the whole GHAD block is **not** stage or difficulty
The three fields that all hold `2` were read as "difficulty or stage, undecidable
from one save" from the day the format was parsed. Writing saves makes it
decidable, and the answer is **neither**.
decidable, and the answer is **neither** — and the sweep did not stop there.
Method — [`boot_menu.sh`](../../../tools/re-capture/boot_menu.sh) boots to the
title menu **without loading anything**, and LOAD GAME's slot list renders each
slot's Details panel (`STAGE`, `Game Status`, `Points`, `Times Cleared`) plus a
row (date, `Difficulty`, flight time, clear ratio) straight out of that slot's
payload. Extra slots can be fabricated — copy the save directory and its
`Headers/…/gameNN.header`, patching the UTF-16BE display string and the ASCII
`gameNN` inside it — so **four probes fit in one boot**, read-only, nothing
loaded.
row (date, `Difficulty`, flight time, clear ratio). Extra slots can be fabricated
— copy the save directory and its `Headers/…/gameNN.header`, patching the
UTF-16BE display string and the ASCII `gameNN` inside it — so **five probes fit
in one boot**, read-only, nothing loaded.
Eleven candidate fields were written and read back: `+36` at 1, 3 and 9; `+52`
and `+56` at 1 and 9; `+0`, `+16`, `+32`, `+48`, `+28`, and `SHAB[0].a` — every
one of them left the panel at `STAGE 02 / Declaration of War`, `Difficulty EASY`,
`At Standby`, `Times Cleared 0`.
**Probed, all with no effect on `STAGE 02` or `Difficulty EASY`:** every scalar in
the GHAD block — `+0`, `+12`, `+16`, `+20`, `+28`, `+32`, `+36` (at 1, 3 and 9),
`+40` (u64), `+48`, `+52`, `+56`, `+60`, `+64` (raw) — plus `SHAB[0].a`, plus the
`SHAB` **fill count** in both directions (record 1 filled with a copy of record 0;
record 0 cleared). Sixteen elements.
The negative is meaningful because the panel demonstrably *does* read each
payload: slot 02 shows `Clear Ratio 5 %` against the others' `6 %`, and `Points`
tracked `+24` exactly. Two further controls: patching a slot's **header** string
to `STAGE09 HARD` changed nothing, so the display is payload-driven, not header
text; and the row's date follows the **container FILETIME**, which is why every
fabricated slot showed 18:04.
The panel genuinely re-reads each slot — the control is slot 02, which holds
`Points 4101` and `Clear Ratio 5 %` and displays exactly that while its
neighbours show `101` and `6 %`. A further control: patching a slot's **header**
string to `STAGE09 HARD` changed nothing, and the row's date follows the
**container FILETIME**, so the row is payload-formatted, not header text.
So stage and difficulty live in one of the remaining unprobed fields — `+12`,
`+20`, `+40` (u64), `+60`, `+64` (raw 4) — or in the phase string. That is the
next probe round, and it is now cheap.
**What is left**, and it is a much narrower set than when this started: the
**phase string** (`GP_BUNK`), the trailer (`"BUNK"` + `0x09150000`), or the blob.
One honest caveat on the negative: **every save on disc is genuinely Stage 02 /
EASY**, so "the field was not found" and "the panel does not vary those two
labels per slot" are not yet separated. The next move is not another probe — it
is to find the code that formats `STAGE %02d` and read which offset it loads,
which the static DB can answer directly.
Editing beyond a throwaway slot is still the user's call.