Two interlocking pieces from the Tsumu HUD/menu-label investigation:
- GP0 ring builder attribution (gpu.h/gpu.c/debug_server.c): op-0x80
entries now carry a bounded guest-stack unwind (bld[6]) + copy-time
$sp (csp); gpu_frame_dump emits func/ra per entry and csp+bld[] for
copies. This cracked the HUD compose: whole label strips are
MoveImaged from the glyph sheet (src 834/852,352 -> HUD cache y=32).
- [[vram_patch]] (text_xlate.cpp/.h + gpu.c hook): third translation
layer for text that exists only as pre-rendered pixels in an
uploaded texture asset (Tsumu HUD labels, menus, prompts live in
the glyph-sheet image; the 144-glyph font has no katakana at all).
On every GP0 0xA0 upload completion the configured rects are
verified halfword-for-halfword against expected JP pixels and only
then rewritten with the target-language pixels via the renderer
facade (software/GL/supersample coherent). Re-applies on every
re-upload; verify-fail leaves foreign assets untouched. Debug:
xlate stats gains vram_patches/vram_applied; new "vpatch"
force-apply subcommand for config iteration without rebuild.
Verified on-screen: HUD "STAGE: 1-1 / BATTERY: 100", pause menu
(CONTINUE/RESTART/GIVE UP/CHANGE CONTROLS, TARGET BATT), title-menu
strips. docs/STRING_TRANSLATION.md Appendix Z documents the model,
mechanism, authoring recipe, and remaining strip backlog.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the LOAD-GAME attempt outcome and why the HUD can't be finished
headless: both memcards are empty (0 used blocks) so LOAD GAME reaches no
stage; NEW GAME still hits the intractable name grid; and the attract
demo's HUD is composed once at stage-load and only displayed (never
per-frame-drawn as glyph quads in the full 554-3754 GP0 capture; its
values are static so there is no dynamic draw to intercept). The drawer
(0x8005443C) + font-tile scheme are cracked; capturing the HUD label's
own glyph quads needs real gameplay (dynamic HUD) reachable only via a
real save or driving past the name grid on an interactive build. No hook
shipped (no discriminator captured -> would risk non-HUD quads; Rule 5).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Live GP0-ring capture (on the name-entry screen, same per-glyph drawer
class as the HUD) cracked the mechanism for the eventual HUD index-remap:
- Drawer PC 0x8005443C draws one textured 0x2C quad per glyph via direct
GP0, building prims in game RAM ~0x80092xxx; U,V into texpage 0x1E
selects the font tile (CLUT 0x7C10).
- Texpage 0x1E holds hiragana AND Latin (the name-entry grid draws A-Z
through this same drawer), so the Latin tile U,Vs the remap needs are
readable straight off the grid's letter quads.
- Remaining blocker: catch the HUD's own stage-load compose to get its
katakana source prims, then rewrite their U,V to Latin tiles (or hook
0x8005443C to remap HUD-position glyph quads). Gated only on reaching a
fresh stage-load (blind grid-confirm navigation didn't land headless; a
reachable build clears it). No fragile partial shipped (Rule 5).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Corrects and refines the Bucket-1/3 findings with live GPU evidence:
- CLEAR!! banner: ALREADY renders in stylized Latin ("CLEAR!!") — verified
on-screen in a gameplay stage. Not Japanese; no translation needed. The
earlier "resister/baked-sprite" assumption was wrong.
- In-game HUD (ステージ/バッテリー): pinned the render model via the always-on
GP0 ring (gpu_frame_dump). The game composites — ~6 draw prims/frame,
rebuilding only the animated foreground (character/blocks, ~8 textured 0x2C
quads at screen y>=31, via BIOS DMA from an OT at ~0x138xxx). The HUD (screen
y~8) is composed ONCE at stage-load, not per-frame, and has no static SJIS
source (RAM-scan proof). Remapping it needs catching that stage-load compose
(a fresh stage load while observing) and a scoped blit/tile intercept — not a
source-patch. NOT shipped: no clean pixel-verified remap this pass, and a
blanket VRAM font swap would corrupt katakana elsewhere (Rule 5, no hacks).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the outcome of the per-glyph translation pass (Buckets 1-3):
- SHIPPED: stage/level/tutorial names (source-patch, f00631e/31ab3c2);
name-entry prompt -> "YOUR NAME?" (source-patch, 31ba92d); name-entry
Latin input confirmed NATIVE (grid already draws the font's A-Z/0-9).
- RESISTERS (not shipped, no fake): the in-game HUD (ステージ/バッテリー)
and the CLEAR!! banner are composed per-glyph at runtime with NO static
source address. Proven: a live 2 MB RAM scan finds ステージ/バッテリー
only inside static message sentences (never a standalone HUD label,
no ステージ: template); クリア/CLEAR/CLEAR exist nowhere as a
standalone banner; and every disc graphic TIM was extracted+rendered —
the only baked text is the already-English TSUMU light logo. So the
source-patch (which needs a static address) cannot reach them; they
require the scoped per-glyph sprite-draw index-remap hook (Appendix Y),
a separate larger effort. Includes the full disc asset map.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the concrete findings for the deferred per-glyph HUD/menu/title
localization: the font glyph-index map (Latin A-Z=73-98, 0-9=105-114;
fullwidth Latin glyphs present, no injection needed), the static LE-SJIS
label storage in fixed-width space-padded slots (level names 0x80070755+,
tutorial/mode labels 0x80070300+), and the cleanest hook design (load-time
source patch: rewrite a label's katakana/hiragana SJIS to fullwidth-Latin
so the existing per-glyph routine draws English).
Not shipped: the verification bar is a rendered-English screenshot, and the
target screens (mode/level-select, in-game HUD) are unreachable in the
headless build — the attract never auto-plays the demo, and reaching them
needs completing the name-entry glyph grid (blind grid nav to the "End"
cell did not land). Committing a RAM-patch without that screenshot would be
unverified coverage. Remaining resisters (HUD runtime counter; title/CLEAR
sprites) documented.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the hard-won runtime findings into STRING_TRANSLATION.md: the
message-table text is little-endian 16-bit Shift-JIS (EXE menu labels are
big-endian); FE-FF/FF-FF/30-FC control framing; message text is
string-pointer-drawn (interceptable) but HUD/title/menu are per-glyph
sprites (need a separate glyph-index hook); struct-embedded NUL records
crash if replaced (apply gated to 0xFFFF-framed messages by default);
capture quality gate; verification status; open items for full coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implementation-ready design for capturing PSX source strings from memory
and substituting translated bytes on the fly, so JP-only titles (Tsumu
Light SLPS-02253 first) can be played in English or any language.
Adapts the author's proven n64recomp prior art (Pocket Monsters Stadium
JP): always-on capture ring keyed by FNV-1a64 content hash, a per-title
multilingual table, and a draw-time apply hook that repoints the string
arg at guest scratch so the original renderer draws the replacement (no
regen, unbounded length, dynamic args preserved).
Grounds the PSX design in real findings: Tsumu text is Shift-JIS (2-byte
fullwidth + 1-byte halfwidth kana) with 0xFFFF-framed message records;
in-EXE text block ~0x80070900-0x80072000 via pointer table at 0x80071474;
menu labels at 0x80010C5C/0x80010C94; font already has fullwidth Latin
(English renders with zero glyph injection). Hooks onto the psx_dispatch
chokepoint (fntrace_record census + g_psx_bios_hle_hook precedent), so no
per-function codegen and works in Release.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>