main
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2f838040ca |
docs: stop telling people to use the retired xenia-rs
Instructions and generated text that still sent readers to `xenia-rs`, which is
archived and deleted locally. Provenance and dated research records are left as
they are — "lifted from xenia-rs", or a finding stating what database it was
measured against, is still true, and rewriting it would falsify the record.
Changed, because each one tells someone what to do today or writes stale text:
docs/re/README.md the RE toolchain guide queried `xenia-rs/sylpheed.db`
with `xenia-rs/zq.py`, and its "Dynamic" bullet said
to prefer xenia-rs's probe suite OVER Canary. Now:
`sylpheed.db` at the repo root (how to build it and
re-stamp names), `tools/zq.py`, dynamic = Canary, and
`.rdata` reads from the `.pe` (offset = VA - 0x82000000)
in place of `--dump-addr`.
challenge-mission-gate.md, structures/achievements.md
reproduction commands `python3 xenia-rs/zq.py dis …`
crates/sylpheed-xexdb/SCHEMA.md
titled after the retired `xenia-analysis` crate,
citing `xenia-rs dis`, pointing at a `xenia-analysis`
source path
sylpheed-xexdb/src/formatter.rs
WROTE "generated by xenia-rs" into every disassembly
it produced (no test pins the banner)
sylph-xexdb.rs, db.rs "SQLite" / "DuckDB writer for xenia-rs"
sylpheed-formats/src/hash.rs
pointed at `xenia-rs/RE_SYMBOLS.md`, now at
`docs/re/RE_SYMBOLS.md`
Verified: every rewritten reproduction command was extracted from the doc and
run as written against the regenerated database — all 5 exit 0 and return the
disassembly they describe. The README's function count (25 676) is the database's
own. `cargo fmt --all -- --check` clean.
Not changed, deliberately: the README's Oracle bullet says Canary's native Linux
ELF "crashes / does not run". That is about Canary, not xenia-rs, and it conflicts
with a July note that the native build works — unverified either way here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
| cb677fe34f |
re(challenge): the gate is CLEARED STAGES -- my achievement reading was wrong
Third revision of this claim, and this one has the writer.
Word A (singleton +80) has exactly ONE writer in the image. Scanning all 22
callers of the +80 struct copier (0x82175110) for one that stores to the copy's
word 0 gives a single hit, 0x821C1820, inside GamePart_StageClear:
if (this+1004 & 0x20000) skip ; already recorded
copy local = singleton->progress ; src = obj+80
local.word0 |= 1 << (this+84)
if changed: singleton->set(local) ; assign + async persist
and this+84 is the STAGE NUMBER, by two independent uses: 0x821C1760 indexes a
20-byte per-stage record array with it (this + (x+7)*20, three words plus an
8-byte timestamp -- the savegame's 16x20 SHAB shape), and 0x821C1EEC/0x821C1924
pass it as the index into the config key "STAGE" (0x820A2540), the debriefing's
px_deb_stage01..16 sprite list.
So a challenge mission's REQUIREMENT n means "stage n has been cleared", and the
<24 / >=24 split is the disc's own stage numbering: story 1-16 and tutorial 18-23
in word A, challenge 24-29 as word B bits 0-5. TimeAttack needs stage 16 -- the
last story mission -- and the other five chain off challenge stages 25-29. That
is what the raw numbers suggested in the first place.
REFUTED: "the bit space is the game's 24 achievements". ACHIEVEMENTS_REQUIREMENTS
having 24 entries and the gate splitting at 24 is a COINCIDENCE -- 24 is also the
first challenge stage's id. Nothing copies the Debriefing's masks (+208/+736/+740)
into +80, which is the check that should have preceded the claim.
The achievement work itself stands and is kept, retitled and rescoped in
structures/achievements.md: the XACH table (.pe 0x8FBCBC, 36-byte records,
1000G self-check), the 24 names/descriptions, the on-disc ACHIEVEMENTS_REQUIREMENTS
list, and the XACHIEVEMENT_DETAILS/XAM read path. It just does not gate the
challenge missions.
Still open: word B (+1956) has no known writer -- GamePart_StageClear's
unconditional 1 << x would land a challenge stage on word A bits 24-29, not word
B, so clearing a challenge mission must be recorded by another path (presumably
the one that also stores its Time/Points record).
|
|||
| 047ad995a0 |
re(achievements): earned state comes from XAM, and my bit numbering was wrong
Two findings, one of them a correction to the previous commit. FOUND: GamePart_Debriefing enumerates XACHIEVEMENT_DETAILS from XAM (0x8218F888). The records are 36 bytes -- confirmed by the arithmetic, not by eye: the count is a byte count divided by 36 through the 0x38E38E39 multiply-high magic plus srawi 3. Field +0 is used as a shift amount, field +32 is tested for 0x00020000, and the buffer sits behind a handle that is waited on (0x824AA330 with -1) then closed (0x824AA3E0). That is the XDK struct exactly, with XACHIEVEMENT_DETAILS_ACHIEVED == 0x20000, via the XamUserCreateAchievementEnumerator / XEnumerate pattern. So the title does NOT persist earned achievements itself -- it asks the console. The lever for achievement-gated content is the emulator's PROFILE data, not the savegame. Independently, the singleton the challenge gate reads is not the object holding the save block: re-scanning all 202 accessor call sites WITH function- boundary stops touches only +80/+1956/+1960/+1964 and none of the known save offsets (+304/+316/+320/+336/+380/+440). The earlier scan that seemed to find them had register tracking bleeding into the next function -- a false positive I am recording rather than quietly dropping. CORRECTED: the previous commit claimed "bit n <-> achievement n+1" and treated the Debriefing award pass and the challenge gate as the same bit space, both as confirmed. Neither holds up: - the only place the image is observed turning an achievement into a bit does 1 << dwId with 1-based ids, so bit = the id and bit 0 is unused; - the "list index is the bit index" step assumed 0x82448338's out-parameter is the loop ordinal. It is the child entry's first word out of a 12-byte array, and whether that is an ordinal, an id or a name hash is not pinned; - the Debriefing's masks are its own fields (+208/+736/+740). Nothing observed copies them into the singleton's +80, and no writer of that bitmask has been found at all. The requirement-type/id agreement still stands, but it confirms the LIST ORDER, not the bit numbering. Consequence: if the join holds, TimeAttack's REQUIREMENT 16 is achievement 16 (Night Ravens Patch), not 17 (Solar System Defense Award). Both read plausibly as a first-challenge gate, which is exactly why it needs evidence and not the better story. Also noted: +1960 is a third bitfield on the same singleton, and a what-changed pass at 0x8219F3A4 diffs it and reports each newly set bit as bit + 64 -- so there is a wider flag-id space whose bases are not yet worked out. |
|||
| 90da38bb2b |
re(challenge): the gate's bit space is the game's 24 ACHIEVEMENTS
Static only. Last commit left "REQUIREMENT is a bit index into a progress bitfield" with the space unidentified. It is the achievement space, and both halves are now readable off the disc and the executable. - GamePart_Debriefing (0x8218CF38-0x82191B18) awards them: sub_8218F9A8 walks the on-disc ACHIEVEMENTS_REQUIREMENTS list (tables.pak #16, schema 744c0519), and for entry index n tests bit n, evaluates the entry when clear, and sets the bit when satisfied. The list is literally ACHIEVEMENT01..ACHIEVEMENT24 -- 24 entries, which is exactly where the challenge gate splits word A from word B. - The XEX carries the definitions: XACH at .pe 0x8FBCBC, 36-byte records {id, name_id, unlocked_desc_id, locked_desc_id, image_id u32, gamerscore u16, pad, flags u32, 16 zero bytes}, strings from one XSTR per language (English is table #5). tools/xach_dump.py parses it. SELF-CHECK: the 24 gamerscores sum to exactly 1000, the retail total -- a wrong stride does not land on a round 1000. - The two sources agree on ORDER independently: the requirement types ShootDownAircrafts 1000/10000, ShootDownShips 100, ShootDownWeight MegaTons, GetAllWeapons and GetAllAchievements line up with ids 19-24 exactly as XACH names them. So bit n <-> achievement n+1 is evidence, not inference. (Those last two are requirement TYPES, not debug cheats, despite how they read.) - Corollary: TimeAttack's REQUIREMENT 16 -- the one value that sits in direct value-before-key adjacency, so it survives IDXD dedup -- is bit 16 = achievement 17, "Solar System Defense Award", i.e. finish the story campaign. The other five values (25-29) are >= 24 and so index word B, a second flag space, plausibly a challenge-clear chain. Still 🟡. REFUTED, from the last commit: the stores to +1956 in 0x822AF278 / sub_822C8748 are NOT this singleton. That object comes from 0x822CEB30, checks a +2652 flag and stores string POINTERS at +1956/+2024 -- and a pointer ANDed with 1<<n is meaningless as a gate. So nothing in the image writes this singleton's +1956 field-wise, and where the mask persists (save vs Xbox profile) is open. XEX imports are by ordinal, so absent XamUser* strings are not evidence either way. |