This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/tools/re-capture/dialog_up.py
Sylpheed RE agent f6508dd8ac re: locate both guest hash routines; IXUD solved; two corrections
Found the routines in the disassembly DB rather than guessing from data:
  sub_82447DF0  IDXD tag hash  (lbz+extsb, modulus 0x00FFFFDF, magic 0x2101)
  sub_82447E70  IXUD tag hash  (lhz, 64-bit, modulus 0xFFFFFF67 then 0x00FFFFDF)
Both transcribed instruction-for-instruction into Python and Rust.

IXUD SOLVED. It defeated every single-modulus search because it chains TWO
exact moduli -- the loop reduces mod 2^32-153 in 64-bit arithmetic and only the
result is folded mod 2^24-33. A polynomial mod M1 folded through M2 is not a
polynomial mod anything, which is exactly why the gcd test returned 1. Verified
independently: 86/86 record keys and 108,261/108,261 field tags in
GP_MAIN_GAME_E.pak, and NoRecord -> 0x1c6d9c96.

CORRECTION 1: tag_hash must SIGN-EXTEND each byte (extsb). My reconstruction
used unsigned bytes and matched all 1.27M disc names -- every one is ASCII --
while disagreeing on ~90% of random inputs with a byte >= 0x80 (verified:
18096/20000). The disc could never have caught this; only the disassembly did.

CORRECTION 2: name_hash's reduction is EXACT, not lossy. The module doc claimed
the missing conditional subtract made it something other than %. rlwinm r6,r6,
9,23,31 is just hi>>23, and with RECIP = floor(2^55/M)+1 that is Granlund-
Montgomery magic division -- 0 wrong at every quotient boundary across the full
32-bit domain. Retracted.

cargo test -p sylpheed-formats --lib hash: 10/10.
2026-08-25 11:28:35 +00:00

49 lines
1.7 KiB
Python
Executable File

#!/usr/bin/env python3
"""Is a modal YES/NO dialog on screen?
Written because `wait_screen.sh --tap A` BLIND-taps A, and on this game's
"Load game?" dialog the cursor starts on **NO** — so a blind tap answers NO,
drops back to the save list, and the next tap reopens the dialog. That is a
stable oscillation, and it burned three consecutive 300 s boots as
"NO readyroom" while d-pad and A both worked perfectly.
The game dims the whole frame behind a modal, so the dialog is detectable
without knowing which dialog it is: sample the band where the modal sits and
compare its brightness against the undimmed screen.
Measured on the LOAD GAME save list (1279x675):
save list, no dialog mean 59.0 / 62.4 p95 164 / 199
"Load game?" dialog up mean 34.4 p95 113
so the threshold sits at 45. Used ONLY to decide whether tapping A is safe:
with a dialog up, A is the affirmative/OK button; without one, A may mean
something destructive like "open the dialog again".
⚠️ Calibrated on 1 positive and 2 negatives — thin. Widen it as runs collect
more frames.
Usage: dialog_up.py <png> -> prints metrics, exit 0 if a dialog is up
"""
import sys
import numpy as np
from PIL import Image
THRESHOLD = 45.0
def metric(path):
im = np.asarray(Image.open(path).convert("L"), dtype=float)
h, w = im.shape
box = im[int(h * 0.33):int(h * 0.48), int(w * 0.28):int(w * 0.72)]
return float(box.mean()), float(np.percentile(box, 95))
if __name__ == "__main__":
mean, p95 = metric(sys.argv[1])
up = mean < THRESHOLD
print("%s mean=%.1f p95=%.1f threshold=%.0f" %
("dialog" if up else "no-dialog", mean, p95, THRESHOLD))
sys.exit(0 if up else 1)