From 91074d1ba78ed9c1590b88ae97941827577a883e Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Wed, 26 Aug 2026 07:44:55 +0000 Subject: [PATCH] re: all savegame variation is in the header; slot region identical -- and my heuristic overstates Splitting the same comparison at the documented slot-table boundary (save+136): the header holds 34 words and ALL 12 differing words, while the slot table holds 35 words and ZERO differences. The slot region is byte-identical between the two same-state saves, so the documented slot fields are untouched by the churn and a slot-region diff is meaningful where a whole-file diff is not. Also corrects my own framing from the previous commit. The slot region contains 6 words in the pointer value range that do NOT change across runs -- a pointer captured from a moving heap would not sit still, so those are data whose values merely land in 0x70-0x8F / 0xB0-0xBF. The value-range test alone does not identify a pointer; a third of what it flags here is not one. What identifies a pointer is being pointer-shaped AND varying, ideally with a shared constant delta. So '26% of the file is heap addresses' overstates it and the defensible figure is the 8 header words that are both. Left the earlier section in place with the correction after it: a value-range classifier on 32-bit words always has a false-positive rate, and quoting its raw count as a fact about the format is the error. --- docs/re/structures/savegame-format.md | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/docs/re/structures/savegame-format.md b/docs/re/structures/savegame-format.md index 9c0c4345..1877e471 100644 --- a/docs/re/structures/savegame-format.md +++ b/docs/re/structures/savegame-format.md @@ -631,3 +631,41 @@ in 12 words**, not 135. Worth stating because the inflated figure looked plausible: half the file changing between saves is exactly what one would expect if saves were dense state, and nothing about it invites a second look. + + +## ✅ Refinement: every difference is in the HEADER; the slot region is identical + +**Same day, one iteration later.** Splitting the same comparison at the +documented slot-table boundary (`save+136`) sharpens the result considerably and +corrects the framing above: + +| region | words | pointer-shaped | **differ between same-state saves** | +|---|---|---|---| +| header, `< 0x88` | 34 | 12 | **12 — all of them** | +| slot table, `≥ 0x88` | 35 | 6 | **0** | + +**The slot region is byte-identical between the two saves.** Every one of the 12 +differing words lives in the header. That is a stronger and more useful statement +than the pointer framing, and it means **the documented slot fields on this page +are untouched by the churn** — they are stable state, and a diff of the slot +region is meaningful in a way that a diff of the whole file is not. + +### ⚠️ And my "pointer-shaped" heuristic has false positives + +The slot region contains **6 words in the pointer value range that do not +change** — `0x73777174`, `0x770F8877`, `0xBC2B75EF`, `0x8187F76B`, +`0x7BE9C030`, `0x829F6B88`. A pointer captured from a moving heap would not sit +still across two runs, so these are data whose values merely land in +`0x70–0x8F`/`0xB0–0xBF`. + +So the value-range test alone **does not identify a pointer** — a third of the +words it flags here are not pointers. What identifies one is the *combination* +of pointer-shaped **and** varying across runs, ideally with a shared constant +delta as three of the header words have (`0x101080`). The previous section's +"26 % of the file is heap addresses" overstates it on that basis; the defensible +figure is the 8 header words that are both pointer-shaped and moving. + +I am leaving the earlier section in place with this correction after it, because +the mistake is instructive: a value-range classifier on 32-bit words will always +have a false-positive rate, and quoting its raw count as a fact about the format +is the error. \ No newline at end of file