Last iteration I wrote into MISSION.md that a Japanese-locale capture is impossible here, because user_language is DECLARE_int32 at four call sites with no DEFINE and no entry in xenia-canary.config.toml. That is true, and it was not the question. The language is PERSISTED: kernel_state.cc builds XConfig over <storage_root>/xconfig.settings, SetDefaults() only supplies a value when the file has none, and the file is writable. Checking where a setting is stored rather than where it is configured turned "blocked, needs a human decision" into a two-line edit. Withdrawn from MISSION.md; METHOD and REFUTED lines added. The field is located from struct landmarks rather than a hard-coded offset, and the check re-runs on every invocation so it fails loudly if the layout moves: music_volume 0.7f at User+449 -> BE float at 2727 -> User base 0x8e6 language at User+44 -> reads 1 (kEnglish) at 0x912 country at User+64 -> reads 103 (US) at 0x926 XLanguage::kJapanese = 2 (xbox.h:307). set_console_language.py wraps it with a backup and a --restore. The capture itself is still NOT taken, for a smaller reason than I claimed. A run with the locale set to Japanese booted fine but never reached the title in 787 s: wait_title.sh's green-(A) oracle never fired and burst-sampling found no frame correlating above 0.18 with either build-7 render -- the run sat in the attract loop. So it needs a longer or pad-driven run, not a rebuilt emulator. Emulator stopped, lock cleared, locale restored to English. Nothing is decided about the keyframe-time association or the rest() rule; this only changes what standing between us and deciding them.
73 lines
2.9 KiB
Python
Executable File
73 lines
2.9 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Set the emulated console's language by editing canary's own persisted XConfig.
|
|
|
|
There is no `--user_language` flag in this tree: the cvar is DECLARE_int32 at
|
|
four call sites with no DEFINE, and it is absent from xenia-canary.config.toml.
|
|
⚠ Do NOT pass it anyway -- xenia calls ShowSimpleMessageBox from
|
|
ParseLaunchArguments BEFORE logging starts, so an unknown flag blocks forever
|
|
with an empty log (see run-canary's header).
|
|
|
|
The supported route is the file. kernel_state.cc builds XConfig over
|
|
`<storage_root>/xconfig.settings`; SetDefaults() hard-codes language = kEnglish,
|
|
and whatever is in the file wins.
|
|
|
|
The offset is NOT hard-coded here -- it is located from struct landmarks every
|
|
run, so this keeps working if the layout moves:
|
|
|
|
music_volume = 0.7f at User+449 -> gives the User base
|
|
language = u32 at User+44 -> must currently read a valid XLanguage
|
|
country = u8 at User+64 -> sanity check
|
|
|
|
set_console_language.py ja # or: en, or a raw integer
|
|
set_console_language.py --restore # put back the .bak this wrote
|
|
|
|
XLanguage (xbox.h): 1 en, 2 ja, 3 de, 4 fr, 5 es, 6 it, 7 ko, 8 zh-TW, 9 pt.
|
|
Restore when you are done: other captures in this corpus assume English.
|
|
"""
|
|
import struct, sys, shutil, pathlib
|
|
|
|
PATH = pathlib.Path("/sylph-home/re/.local/share/Xenia/xconfig.settings")
|
|
BAK = PATH.with_suffix(".settings.agent-bak")
|
|
LANGS = {"en": 1, "ja": 2, "de": 3, "fr": 4, "es": 5, "it": 6, "ko": 7, "zh": 8, "pt": 9}
|
|
|
|
def find_language_offset(d):
|
|
"""User base from the music_volume landmark; language sits 405 bytes before."""
|
|
for o in range(len(d) - 4):
|
|
if abs(struct.unpack_from(">f", d, o)[0] - 0.7) < 1e-6:
|
|
user = o - 449
|
|
if user < 0:
|
|
continue
|
|
lang = user + 44
|
|
cur = struct.unpack_from(">I", d, lang)[0]
|
|
if 1 <= cur <= 12: # a plausible XLanguage
|
|
return lang, cur, d[user + 64]
|
|
raise SystemExit("could not locate the User block -- layout changed?")
|
|
|
|
def main():
|
|
arg = sys.argv[1] if len(sys.argv) > 1 else "--show"
|
|
if arg == "--restore":
|
|
if not BAK.exists():
|
|
raise SystemExit("no backup to restore")
|
|
shutil.copy2(BAK, PATH)
|
|
d = PATH.read_bytes()
|
|
off, cur, _ = find_language_offset(d)
|
|
print(f"restored; language at 0x{off:x} is now {cur}")
|
|
return
|
|
d = bytearray(PATH.read_bytes())
|
|
off, cur, country = find_language_offset(bytes(d))
|
|
print(f"language at 0x{off:x} = {cur} (country byte = {country})")
|
|
if arg == "--show":
|
|
return
|
|
want = LANGS.get(arg, None)
|
|
if want is None:
|
|
want = int(arg)
|
|
if not BAK.exists():
|
|
shutil.copy2(PATH, BAK)
|
|
print(f"backed up -> {BAK}")
|
|
struct.pack_into(">I", d, off, want)
|
|
PATH.write_bytes(bytes(d))
|
|
print(f"language {cur} -> {want}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|