489ea1275973f6ff9d3cfc2bc39546132d36ea10
60 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
489ea12759 |
re(ui): the eff-name implication for bit 0x02 is refuted disc-wide
Last iteration I killed the biconditional and reported that the one-way
reading survived: all 10 bit-set sprites on GP_TITLE build 4 are eff
names, so "bit set => eff name". Checked over the disc, that is false.
sprites with a resolvable preceding name 14 709
bit SET & name has 'eff' 2 338
bit SET & name lacks 'eff' 2 657 <-- counterexamples
bit clear & name has 'eff' 1 399
bit clear & name lacks 'eff' 8 315
P(eff | set) = 0.468
P(eff | clear) = 0.144
The implication fails more often than it holds. What survives is an
association -- 3.3x enrichment -- and build 4's 10/10 was a local naming
habit in an 18-element bundle, not a format rule.
The counterexamples are the useful part: pv_loading_ring0,
pv_loading_light0-3, pv_loading_line, px_bunk_line, px_top_extra. Rings,
glows, lights, thin lines -- effect-like artwork that does not carry the
eff naming convention. Consistent with the bit marking effect sprites by
authoring intent rather than by name, which is a description and not a
decode, and is labelled as such.
Names here come from the string immediately preceding each T8aD,
validated 17/18 on build 4 against the RATC child order; the single
mismatch is the known pteff04.t32 -> registered as 8AX case, so this is
the element (opt) name rather than the sprite's registered name. That
mismatch is itself an independent confirmation of the 8AX finding,
reached from the opposite direction.
METHOD: a pattern perfect on one screen can be near-chance on the disc;
and when an association survives a refuted implication, the
counterexamples are the finding.
|
||
|
|
5f701db588 |
re(ui): kill two candidate meanings for the T8aD 0x02 bit, and fix attribution
The bit at +0x04 was recorded as a real field with its meaning "not
diagnosed", noting ptlogo_back2eff is 0x8830 "despite its name". That note
rested on a size match -- and its size is ambiguous, which is the trap
this corpus already records.
First, a sound attribution. T8aD headers appear in the bundle in RATC
CHILD ORDER, verified on GP_TITLE build 4 against an independent property
-- each header's decoded dimensions versus the dimensions the named child
should have: 18 of 18 match, 0 mismatches. Two of those eighteen share a
size (ptlogo_back2eff and ptlogo_back2eff5, both 1133x280), so a size-keyed
lookup cannot separate them; ordering can. Index 12 is back2eff5 (0x8832,
bit set), index 14 is back2eff (0x8830, bit clear). The documented
counterexample is real and correctly attributed -- now on evidence.
Two candidate meanings tested and refuted:
bit <=> name contains "eff" REFUTED: ptlogo_back2eff is an eff
name with the bit clear. All 10
bit-set sprites are eff names, so
the implication holds one way only.
bit <=> the element is transient REFUTED: pteff03/pteff03a carry the
bit and run to t=250, ramping to
a=255 and holding.
Up close, the exception pair differs in two header words: +0x04
0x8832/0x8830 and +0x08 0x8083/0x8081 -- layer keys 32899 and 32897. They
are NOT duplicates: their alpha summaries agree to one decimal (4.5%
opaque, 86.7% clear, mean 19.3) but a pixel compare gives max abs diff 21.
Two renditions of one image at one size, which is why the summaries were
not trusted.
Still not diagnosed, and said so -- but the search space is two smaller
and the attribution beneath it is now sound.
METHOD: T8aD headers sit in child order, use that not the size; and
identical summary statistics are not identical data.
|
||
|
|
c463f98164 |
re: the GPU trace is compiled out of the release build -- both my guesses wrong
Last iteration left two candidates for why trace_gpu_stream produced no
file: the CLI flag not reaching the cvar, or BeginTracing failing
silently. Neither. Following the code instead of guessing:
BeginTracing only sets trace_state_ = kStreaming ("Streaming starts on
the next primary buffer execute"). The file is opened later, in
ExecutePrimaryBuffer, inside
#if XE_ENABLE_TRACE_WRITER_INSTRUMENTATION == 1
and trace_writer.h defines that as 0 under NDEBUG, 1 otherwise -- the
trace writer exists only in debug builds.
Confirmed against the binaries, with a control. The format string
"{:08X}_stream.xtr" lives only inside that guard:
build/bin/Linux/Release/xenia_canary 0 occurrences
build/bin/Linux/Debug/xenia_canary 1 occurrence
/sylph-home/re/canary-build/.../Release/... 0 <- what run-canary uses
The debug binary is the control: it proves the test finds the string when
it is present, so the release zero means something.
So trace_gpu_stream is a no-op in this container's emulator -- the cvar
parses, BeginTracing runs, and nothing can open a file. The kill -9 was
not the cause either, though it would have destroyed a trace had one
existed.
The route exists but is not cheap: a debug build with the writer compiled
in sits at build/bin/Linux/Debug/xenia_canary, 253 MB against Release's
18 MB, so a much slower boot plus a trace of every GPU packet on a disk
at 95%. Recorded as available rather than attempted -- what it would
confirm, the DC_LUT write, is already a well-supported inference, and the
cost is out of proportion to the gain.
METHOD: a cvar existing does not mean the feature is compiled in; and
test a compile-time gate against the binary, with a control.
|
||
|
|
921a3cfc44 |
re: GPU trace attempt produced nothing -- and the config dump is not the flags
Tried to turn the gamma-ramp inference into a direct observation. canary's trace_gpu_stream records gamma ramps as their own command type (kGammaRamp, index 11 in TraceCommandType), so a boot trace should show the write. Two bounded runs produced NO trace file at all -- nothing under the prefix, no .xtr anywhere, no scratch/gpu/. Bounded deliberately: the disk is at 95% (50 GiB free) and a trace of all GPU packets during boot includes video decode, so the runner carried its own watchdog that killed the emulator the moment output passed a 2 GiB cap. It never fired -- there was nothing to cap -- and disk stayed at 95% throughout. Bounding from inside cost nothing and removed any need to gamble on how coarsely I could poll. What the attempt did establish. BeginTracing() runs at GPU init when the cvar is set (graphics_system.cc:237), but EndTracing() runs only from GraphicsSystem::Shutdown() -- so the kill -9 this session has used routinely can never finalise a trace. The second run was stopped with SIGTERM and exited cleanly; still no file, so that is not the whole story. Two candidates remain unseparated: the CLI flag not reaching the cvar, or BeginTracing failing silently. The next run removes the ambiguity by setting trace_gpu_stream in the config FILE instead. And a trap I nearly fell into. The startup config dump showed trace_gpu_stream = false after I passed --trace_gpu_stream=true, which reads as "flag ignored". It is not evidence either way: the gamma run passed --log_mask=12 --log_level=3, its dump printed log_mask = 0 and log_level = 2, and Kernel Debug logging was demonstrably ON -- that run is where VdGetCurrentDisplayGamma was captured. The dump reflects the config file and can neither confirm nor refute a command-line override. (It does not undo the earlier user_language conclusion: absence of a NAME from the dump still shows a cvar is unregistered.) The gamma-ramp write therefore remains an inference, unchanged. |
||
|
|
421c61a517 |
re(ui): close the gamma chain from canary's defaults -- the game writes a ramp
Continues the previous iteration, where the game was measured calling VdGetCurrentDisplayGamma at video init. The remaining link -- does it then WRITE the ramp -- is a GPU register operation (XE_GPU_REG_DC_LUT_RW_INDEX in CommandProcessor::WriteRegister), unlogged and invisible to kernel logging. Two facts from the source close it without instrumenting. 1. The swap-path gamma stage is a PURE LUT. apply_gamma_table.xesli is the whole transform: index by input*255, fetch from a 256-entry ramp buffer, output. No sRGB encode, no second transfer function. 2. The table DEFAULTS TO IDENTITY. CommandProcessor::Initialize fills it with value = i * 0x3FF / 0xFF, and its own comment says the linear default is "what games set when starting with the sRGB (return value 1) VdGetCurrentDisplayGamma". An unwritten ramp is a no-op. So the only transform is a LUT, the LUT is identity unless written, the game queries the display gamma at init, and the capture differs from our composite by gamma 1.34-1.49 -- which identity cannot produce. The guest wrote a non-identity ramp. Labelled an inference, with its weak joint named: it assumes our composite reproduces the PRE-RAMP framebuffer, which it does not exactly. What carries it is the shape -- a systematic ~1.4 fitted on flat patches across three screens is not a compositor bug. The obvious alternative, a fixed sRGB stage in the presenter, fits neither direction: an encode (^0.45) brightens and we measured darkening; a decode (^2.2) darkens far more than 1.4. Direct observation remains available and cheap, and needs the emulator only to boot: a GPU trace records gamma ramps as their own command type, or one log line at the DC_LUT register write would settle it outright. Not done. METHOD: a default value is evidence; and name the weak joint of an inference in the same breath as the conclusion. |
||
|
|
c6ce000002 |
re(ui): the game does query the display gamma -- measured, with its control
Last iteration's corrected experiment, run. Boot with --log_mask=12 --log_level=3 (Kernel logging on, Cpu/Gpu off), which changes nothing about the output and so cannot perturb the capture harness the way the gamma-cvar experiment would have. VdGetCurrentDisplayGamma is called once, at video init: d> VdGetSystemCommandBuffer(701CF830, 701CF804) d> VdGetCurrentDisplayGamma(701CE1F8(00000000), 701CE1F0(0)) d> VdSetDisplayMode(40000000) d> VdGetCurrentDisplayInformation(701CF110) The control is in the same log: 359 VdRetrainEDRAM and 358 VdGetSystemCommandBuffer lines, so an absent call would have been visible. Per the export's own comment the returned type is "used in D3D SetGammaRamp/SetPWLGamma" -- the game asks the question a ramp-builder asks, at the moment one would ask it. Still open, and stated: whether it then WRITES the ramp, and whether the measured gamma 1.34-1.49 is that ramp. The write is a GPU register operation (DC_LUT), invisible to kernel logging; a GPU trace records gamma ramps as a command type (TraceWriter::WriteGammaRamp), which is where to look next. Worth its own METHOD line: this had been parked behind "needs the emulator to reach a menu" for several iterations, and it needed the emulator only to BOOT -- video init happens in the first seconds. A blocker that stops one experiment does not stop every experiment in the same area. |
||
|
|
93b0a6b20f |
re(ui): the gamma confound is refuted from source -- canary applies none of its own
I had parked the tone-curve finding behind "this may be the emulator, not the game: canary applies kernel_display_gamma_type = 2 (BT.709) on output", with a planned run setting it to 0 and re-fitting. Reading the source kills both the confound and the experiment. VdGetCurrentDisplayGamma_entry is a kStub GETTER the guest calls (xboxkrnl_video.cc). Its own comment: "Used in D3D SetGammaRamp/ SetPWLGamma to adjust the ramp for the display." The cvar is a value REPORTED TO THE GAME, which then builds its own ramp. Canary's role is downstream: the guest writes DC_LUT, command_processor.cc reads it into gamma_ramp_256_entry_table_, and the swap path applies it via swap_apply_gamma_pipeline_layout with apply_gamma_table.ps / apply_gamma_pwl.ps compiled in. So there is no emulator-side BT.709 post-process to subtract, and any gamma in a captured frame is a ramp the game installed. What is NOT established, and the reach is stated: that this game installs a ramp at all, or that the measured 1.34-1.49 is it. The run logs cannot say -- kernel exports log at Debug and this harness masks Kernel logging (log_mask = 13, per boot_menu.sh's own comment), so their silence is guaranteed regardless of what the game did. The planned experiment was wrong in design: changing the cvar changes what the GUEST is told and therefore which ramp the GAME builds, so it could never isolate a stage that does not exist -- and it perturbs the capture harness, since skip_intro classifies movie-vs-static on an absolute rmse threshold that a brighter frame biases. The right run changes nothing about the output: LOG_MASK=12 LOG_LEVEL=3 and look for the call and the DC_LUT writes. Also for the port: the ramp depends on the display type the game is told, and canary hard-codes TV/BT.709 where hardware uses a console setting. So this is a display profile, not a fixed property of the game. METHOD: read what a cvar does before building an experiment around it; and an absence in a log is only evidence if the log would have shown it. |
||
|
|
3490fba9e3 |
re(ui): settle 8AX vs ptbase statically -- the game draws the full-res one
I had parked this as "needs a per-draw capture recording texture base addresses". It did not. 8AX (1280x720) and ptbase (640x360 at 200%) are the SAME artwork at two resolutions, which is exactly why comparing either against a capture is inconclusive -- and why comparing their DIFFERENCE is not. Compute 8AX - upscale(ptbase), the detail only 8AX has, and ask whether the capture contains it. Both candidates are first mapped into the capture's tone domain with the measured gamma; without that the residual is dominated by the tone difference and the test is blind. main menu corr +0.0475 controls +0.0032 shift, -0.0075 flip 68% of ceiling title corr +0.0634 controls +0.0095 shift, +0.0086 flip 68% of ceiling Two independent screens, both at 68% of the theoretical ceiling (sd of the 8AX-only detail over sd of the capture residual), 7-15x their matched controls. The controls preserve spatial correlation and destroy only alignment, so they are what "no signal" looks like. So the recommendation changes: resolve the name and draw 8AX at 1:1. Upscaling ptbase 2x is wrong, not merely softer. Still do not draw both -- an opaque layer over an identical one costs fill and hides later changes, and ptbase's element is the one carrying the keyframes, so a consumer needs its timing with 8AX's pixels. Also recorded and withdrawn: a cruder pixel-pair test gave 0.00-0.72 for upscales, 0.98 native and 1.01 for the capture -- apparently decisive. Additive noise raises both terms of that ratio equally and drives any value toward 1; fitting a noise term, both "native + noise" and "bilinear + noise" reproduce the observed numbers. The conclusion is right, that test does not establish it, and it is in REFUTED because the number looks conclusive and is not. Not shown: whether ptbase is also drawn underneath. 8AX is ~86% opaque and carries the same art, so it would hide it either way. |
||
|
|
aaaa08b164 |
docs: the UI decode's own evidence images were unreachable -- 11 links repaired
The brief's rule is to commit reference data beside the finding so the port can be built without a disc. Nothing had ever checked that the docs' cited artifacts actually exist. doc_link_check.py walks every markdown file under docs/, resolves each relative link, and reports targets that are missing -- and separately targets that resolve to a ZERO-BYTE file, which looks fine in any listing. links resolving 1038 -> 1049 missing targets 16 -> 5 empty targets 0 -> 0 +11 resolving and -11 missing against 11 edits: the counts pair, which is the confirmation the pass did what it claimed and touched nothing else. Two of the sixteen were the evidence for the UI layout decode itself. structures/ui-rat-layout.md is what the port is built on, and its two figures -- backing "the tutorial PAUSE menu rebuilds pixel-accurately from its sprites" and "the same method reproduces the main menu" -- were written as captures/ui-layout/... from a file in structures/, one directory too shallow. The headline evidence for the decode could not be opened from its own document. Eleven links had the wrong relative depth with the target present. Each was rewritten only where exactly one candidate path resolved, so nothing was guessed; the first pass left three alone because equivalent spellings (captures/../captures/x) failed to collapse, and a second pass normalised them. Five remain genuinely absent and are left rather than invented: two point at MEMORY.md outside the repo, one at a header in the separate xenia-canary-native tree, and two name documents that were never written (weapon-datasheet-runtime.md, canary-build-verified-env-confound.md). None is port-relevant. A missing document is a different problem from a bad path and is not something a link fix should paper over. |
||
|
|
7373035868 |
re: the port was still being told SE audio is undecodable -- it is not
A resolve-check on HANDOFF's own rows. Q8 read "SE audio is undecodable
from the disc -- no XACT container exists anywhere". menu-audio-cues.md
retracted exactly that ("### Retracting 'cannot be extracted'") and
locates three cues in Static.slb that decode to PCM: d-pad move 0x1ec0
(4 packets), (B) back 0x0ec0 (2), (A) confirm 0x5d6c0 (6), all mono
48 kHz. The retraction landed in docs/re/ and the page the port reads
kept the superseded text -- the fourth time in this corpus.
Writing the rule down has not worked, so there is a tool now.
handoff_lint.py flags every HANDOFF line making a strong negative claim
that links a doc containing retraction language. First run: found the Q8
row, plus one benign false positive (Q3 links a doc whose retraction is
about a sprite count, not about the tie-break -- checked, and HANDOFF
repeats none of the retracted figures). The lint also caught its own bug
first: it reported existing docs as missing because it joined a guessed
repo root, so it now resolves links relative to the file as markdown does.
Separately, EXTRAS's paint-order risk narrows twice more. Of its 15 tied
pairs only 2 overlap, and of those, ptloop01 x ptloop02 are loop*
animations compose skips by default -- so exactly ONE tie can be drawn:
ptframe3 x ptframe4, overlapping 102x132 px. Against live-extras.png that
contested region correlates +0.9622, better than the whole frame (+0.9440)
and inside the range of regions where order cannot matter (+0.8502 /
+0.9903). Consistent with our order, not proof: correlation cannot see a
swap between locally similar art.
15 -> 2 -> 1 -> consistent is now the whole paint-order risk on the five
screens, and HANDOFF says so.
|
||
|
|
f7f9b555f6 |
re(ui): the paint-order tie-break is undecodable from the bundle
Q3 was delivered as "decoded: a u16 layer key at +0x0A". The audit last
iteration showed the key does not fully order a screen -- elements
sharing a key are tied, and on the title that tie-break decides two total
occlusions. This searches for what breaks the tie, and closes it as a
negative with reach.
The game paints the five tied ptlogo_back2eff glows in the order
eff1, eff2, eff5, eff3, eff4. Three static structures were searched:
1. The declaration table. Entries 14-18 are byte-identical apart from the
pivot, which is only half the sprite's own size.
2. The T8aD headers. All five carry identical +0x04 (0x8832) and
identical +0x08/+0x0A (32899, the key itself), differing only in
position and tile count. Searched exhaustively -- every offset
0x00-0x7f, u8/u16/u32, ascending and descending:
fields sorting to the MEASURED order: 0
fields sorting to the DECLARATION order (control): 64
The control is the point: 64 fields can be found that reproduce a
known ordering, so the scan finds ordering fields when they exist. It
finds none for the order the game uses.
3. The RATC child order -- a genuinely different permutation on other
screens -- gives eff1..eff5 here, declaration order again.
All three static orderings give eff1..eff5; the game gives eff1,2,5,3,4.
That agrees with ui-screen-runtime's conclusion from the other direction:
the game builds a reordered child list at load time and paints that.
Q3 now reads honestly: the layer key is decoded and orders 4 of the 5
measured bundles exactly; the tie-break within a key is undecodable, and
a consumer must use a measured order or accept declaration index as an
arbitrary stand-in. The port's exposure remains 2 overlapping tied pairs
on EXTRAS.
METHOD: an exhaustive field search needs a positive control, or "found
nothing" is worthless.
|
||
|
|
ba47bdebe8 |
re(ui): measure the paint-order hedge -- exact on 4 of 5, and bound the rest
`compose` claimed the derived paint order "reproduces both measured orders up to ties". That sentence was never measured and was stale by one: there are three measured orders, not two. examples/paint_order_audit.rs checks it. main menu (entries 5, 8) derived == measured 0 inverted pairs developer splash (11, 14) derived == measured 0 inverted pairs title (entry 4) DIFFERS 8, all same-key ties So the claim holds and the exception is entirely ties -- but two of those ties are total occlusions, not near-misses. The tied family is the five ptlogo_back2eff glows (key 32899); back2eff5 is 1133x280 and FULLY CONTAINS back2eff3 (82,824 px^2 = 100% of the smaller) and back2eff4 (152,047 px^2 = 100%). Derived paints it on top of two glows it entirely covers; the game paints it underneath. A tie-break by declaration index can therefore be wrong by a whole layer. The title itself is unaffected -- it has a measured order. The port's actual exposure, per screen: title, main menu and developer splash all use MEASURED orders; the publisher splash is derived but has ZERO ties, so it is fully determined; EXTRAS is derived with 15 tied pairs of which only 2 OVERLAP. Two element pairs on one screen is the whole risk, and that is what HANDOFF now says -- not the raw 15, which would have overstated it 7x. Reach stated: this compares the derived order against orders measured from the game, not an independent derivation, so where no measured order exists only the tie exposure can be checked. Overlap uses pivot*2 as the element size at its resting placement. Stale comment in compose corrected. METHOD: a hedge in a code comment is an unmeasured claim; and count the cases that can bite, not the ones that match the pattern. |
||
|
|
c3cf3c2e81 |
re: a title negative that survives its own cross-check
Three earlier "the title never appears" claims came from instruments later found broken -- a stale pixel oracle, a 41 s sampling interval, a freezing stream. This one carries its own evidence. title_probe_xchecked.py restarts its capture stream every 30 s AND prints its reading beside an independent `import` grab every 60 s: 1851 frames in 560.2 s = 3.30 fps cross-checks 9, disagreements 1 max glyph 0 t= 62s stream 6.05 | import 0.07 disagree (a fade, logos mid-transition) t=123s stream 7.40 | import 7.49 agree t=183s stream 8.18 | import 8.29 agree t=243s stream 0.23 | import 0.10 agree t=311s stream 89.68 | import 89.51 agree t=371s stream 80.97 | import 81.58 agree t=426s stream 117.43 | import 117.72 agree t=487s stream 77.71 | import 76.25 agree t=546s stream 70.43 | import 70.55 agree Eight of nine agree within 2%, fps held at 3.30 with no collapse to 1.60, and the surface moved through dark and bright phases. So the frames were live: over 560 continuous seconds from launch, sampled 3.3 times a second, the interactive title's green (A) plate never appears while the game renders throughout. The final frame correlates 0.0145 / -0.0047 / 0.0102 with our title / main menu / EXTRAS renders -- attract-movie content, not a UI screen. Why remains unknown. live-title-press-a.png with its 753 glyph pixels proves the title was reachable from this container on 2026-08-28, and clearing the shader cache fixed the black surface but not this. The two emulator-side questions (gamma control, 8AX vs ptbase) are therefore blocked on a characterised failure rather than a suspicion. Neither blocks the five menu screens, so I am returning to static work; the probe is committed for whoever picks it up. METHOD: a probe that cross-checks itself turns "no result" into a result. |
||
|
|
46006406a4 |
re: the fast probe stalls -- its own dense negatives are withdrawn
Cross-checked the instrument built last iteration against an independent grabber while both watched the same screen, and it fails. A single long-lived ffmpeg x11grab stream degrades and then freezes: 862 frames in 540.1 s = 1.60 fps (it starts at 3.98) t=450/480/510/540 s: surface mean 5.21, identical every time At that same moment `import` read surface mean 125.65, and a freshly started ffmpeg stream read 122.43 -- agreeing with import to 3%. So the acquisition was broken, not the analysis: the stream replayed a stale frame while the screen was 24x brighter. That withdraws last iteration's headline. "2391 frames over 600 s from t=0, max glyph 0" cannot distinguish "the title never appeared" from "the stream froze early and repeated one frame 2391 times". Its 3.98 fps was measured over the first 20 s, before the degradation. Sample count is not coverage unless the samples are known independent. Fixed: the stream is now torn down and restarted every 30 s. Startup is ~0.3 s, cheap against the title's window, and it guarantees live frames. Separately, the cache hypothesis was tested and is SUPPORTED. cache, cache0, cache1, cache_host moved aside (to /tmp/xenia-cache-aside, not deleted) and the surface renders again: import reads mean 54.8 and 68.6 with 100% non-black warm content, against 0.07 and 0.08% non-black in the black run; 773 of 862 probe frames had >2% non-black. One run each side and many kill -9s before the black one, so it is supported, not proven -- the old caches are kept for reproduction. Still no title, but that number now comes from a stalling probe and establishes nothing either way. METHOD: validating a probe on static images tests its analysis, not its acquisition -- cross-check against an independent grabber during a run. |
||
|
|
a3946e5005 |
re: the game surface is rendering BLACK -- check that before explaining absences
The named experiment was to attach the fast probe at t=0 so the boot title could not be missed. Done, default config, English: 2391 frames in 600.4 s = 3.98 fps; max glyph 0; hits 0 Ten minutes sampled four times a second FROM LAUNCH, no green-(A) glyph. So "the plate only shows in an early boot window I keep missing" is mine and refuted -- the third explanation refuted in three iterations. Then the check that should have come first. Splitting the raw root grab into bands: y 0- 44 (GTK menu bar) 100.00% non-black mean 210.50 y 45-719 (game surface) 0.08% non-black mean 0.07 The game is rendering black, reproducibly across back-to-back samples, while the guest is alive and polling input (XamInputGetKeystrokeEx past 1201 calls) and MEM-WATCH keeps reporting. The crop and every pixel oracle were correct; there was nothing on the surface to detect. What this does NOT do is retroactively explain the earlier failures, and claiming so would be the fourth over-reach in a row. Those runs had content: run 2 sampled mean 33.1, run 3's classifier measured real frame-to-frame rmse, the gamma_type=0 run measured mean 122.8. The failure mode CHANGED over the session; black is the newest and worst. Hypothesis for the regression, untested: canary's shader/pipeline cache is 47 MB and was last written 23:49 on Aug 28, during the failed runs, and this session has kill -9'd the emulator repeatedly. The test is to move cache* aside and boot again -- one line and one run, not done. Two METHOD lines: ask whether the screen is drawing anything before explaining why a feature of it is missing; and a newly found fault does not retroactively explain older failures. |
||
|
|
e9924ff9e8 |
re: build the fast probe -- and it refutes the diagnosis that motivated it
Last iteration I blamed four failed runs on the probe sampling every ~41 s, slower than the title screen lasts, and withdrew three earlier conclusions on that basis. Building the fix tested the claim and killed it. The speedup is real and control-verified. One long-lived ffmpeg x11grab stream, raw RGB, glyph counted in numpy -- no per-sample process startup, no PNG encode, no convert -crop: wrapper `screenshot` 3.98 s per sample (emulator running) import -window root -> PPM 1.20 s long-lived x11grab stream 0.29 s 13.7x The counter is byte-identical to is_title.py: 753 on the committed title capture, 327 on the main menu. Pointed at a running game it says the opposite of what I expected: 332 frames in 85.3 s = 3.89 fps; max glyph 0 1674 frames in 420.0 s = 3.99 fps; max glyph 0 1674 consecutive samples over seven unbroken minutes, four per second, zero green-(A) pixels. Sampling rate was a real defect that happened not to be the cause. So "neither locale reaches the interactive title without a pad press" -- withdrawn last iteration for want of evidence -- is reinstated, now as a dense measurement, with its reach stated: a MID-RUN window only, silent about the boot title. Leading hypothesis, unconfirmed: the PRESS (A) plate appears only in the boot title window and the attract loop's title carries none, which is exactly what title_states_capture.sh was written to test. The experiment is to start the fast probe from t=0 rather than attach to a run already in progress. METHOD: fixing the instrument is how you test the explanation that blamed it -- a plausible mechanism is a hypothesis, and the fix is its experiment, not its proof. |
||
|
|
cc4e5e04a7 |
re: the boot harness was blinking slower than the title -- diagnosed
Four consecutive runs failed to reach the interactive title, across two locales, two launch paths and two display-gamma settings. I attributed it in turn to a stale oracle, to the locale, and to the attract loop. It was none of those. one `screenshot` call, emulator running: 10.8 s one `screenshot` call, emulator killed: 0.117 s 92x, measured at a 1-minute load average of 1.80 -- so it is contention with the emulator through the X server, not background load. skip_intro.sh takes two grabs per iteration plus a numpy import, giving a median sampling interval of 41 s in the last run (38/82/41/20/30/30/35/ 47/46/45/44/43/21/19). The title lasts "a few seconds" before the attract loop reclaims it -- wait_title.sh's own header says so. The harness was sampling slower than the event it was waiting for. That also explains why runs at 16:43-18:05 the same day succeeded. Withdrawn as CAUSES, though the observations stand: "the JP run never reaches the interactive title", "neither locale reaches it without a pad press", and "the game sat in the attract loop for 604 s". The English control did control for locale -- it just shared the same defect. Also recorded: I set kernel_display_gamma_type = 0 for the gamma control run, which brightens the frame (mid-attract mean 122.8 vs 52.5/82.8 at type 2) -- and skip_intro classifies movie-vs-static on an ABSOLUTE rmse threshold, so the gamma change biased the very classifier the run depended on. Changing a display setting and a capture behaviour in one run confounds both. Config restored to type 2. The fix is not applied: make the probe cheap enough to outpace the title window (small region, no convert round trip, one long-lived process). Every remaining emulator-side question is waiting on that. |
||
|
|
ee73de50be |
re(ui): put a number on the render-vs-capture tone difference
Closes an observation I left dangling last iteration ("the capture is ~4x
darker than the render") and puts a figure on the ❔ INDEX's texture row
already carried: exact gamma/sRGB fidelity untested because a hue
comparison cannot see it.
Geometry first: cross-correlating the main-menu capture against our
render over +/-6 px puts the best alignment at exactly dy=0 dx=0,
correlation 0.9466. Only the tone differs.
Two methods failed before one worked, and both failures are recorded.
Three dark patches gave "4x darker" -- the whole-frame best linear scale
is 0.914, so three patches from one region are not a transfer curve. A
pixel-wise fit over 854,685 pixels then produced a NON-MONOTONIC transfer
(render 96-127 mapping brighter than render 128-159) with mean abs error
10-14 for every candidate model. That is edge misalignment, not a tone
curve: at correlation 0.947 a bright pixel routinely lands on a dark one.
Flat patches fix it -- 16x16 blocks with std < 8 in BOTH images, a
threshold chosen from the counts (0/83/404/1055/1788 at std<3/5/8/12/20):
main menu 404 patches gamma 1.491 err 0.28 (best linear 0.276, 0.34)
EXTRAS 382 patches gamma 1.493 err 0.22 (best linear 0.273, 0.28)
title 506 patches gamma 1.338 err 1.08 (best linear 0.842, 9.02)
Reach, stated because it is narrow: those patches span only render values
~0-60, where gamma and a plain scale are nearly indistinguishable -- the
two menus decide nothing (0.28 vs 0.34, 0.22 vs 0.28) and only the title
separates them. Nothing constrains midtones or highlights.
The held-out control FAILED TO DISCRIMINATE and is reported as such: the
splash's 2918 flat patches are pure black (render 0-4), so every model
scores ~0.00. That is a test with no power, not corroboration.
Confound left open: this compares our composite to what canary DISPLAYS,
and canary applies kernel_display_gamma_type = 2 (BT.709). The exponent
may be its output stage. The discriminating run -- set it to 0, recapture,
refit -- needs one emulator session reaching the main menu and was not
done.
Classified measured, not decoded; HANDOFF says plainly that a port
applying it is authoring.
|
||
|
|
2b4ec20f00 |
re(ui): a full-screen element is dropped on three port screens -- do not "fix" it
Audited what `screen render` silently omits on the port's five screens,
since an element the game draws but we skip is the one defect class the
port agent has actually hit. Everything is accounted for -- kind & 0x4
ghost instances, .prm primitives, loop* animations -- except pteff04.t32
on the title and pteff05.t32 on both menus. Those are kind 0x0, one
keyframe, rest a=255, pivot (640,360): full-screen and opaque.
Cause: the element declares pteff05.t32, but the T8aD behind its `opt `
link is registered under the name 8AX, so build.sprites.get() misses and
compose hits a silent continue. Bytes at 0x0e2035 of GP_TITLE entry 5:
opt ... 70 74 65 66 66 30 35 2e 74 33 32 00 38 41 58 54 38 61 44
p t e f f 0 5 . t 3 2 \0 8 A X T 8 a D
8AX is 1280x720 and present in all six title-family bundles; it is a
sprite in builds 4/5/6 and never an element.
It does not currently show, and that is the useful half. ptbase.t32 is
640x360 drawn at 200% and carries THE SAME ARTWORK: its 2x upscale
differs from 8AX by mean abs diff 2.05 (max 80), and our rendered
background is pixel-identical to 8AX in every patch sampled. So resolving
the name and drawing it in addition would double-draw an opaque
full-screen layer -- invisible as a doubling, which is worse than a
visible bug. Written into HANDOFF as a do-not-do.
The free win, offered and not taken: use 8AX at 1:1 and drop ptbase
instead of upscaling a half-res copy. That is a rendering choice and
ptbase's element carries the keyframes, so it is the port's call.
Not established: which of the two the game actually draws. Both carry the
same art, so pixels cannot separate them; it needs a per-draw capture
recording texture base addresses, since the two differ in size.
|
||
|
|
7d07eb945f |
re: the title capture is not reachable here either; stopping this line
Run 3 used the proven path rather than my own probe: launched exactly as
boot_menu.sh does (DISPLAY=:98, --apu=sdl, /dev/shm/xenia_* cleared, the
existing profile signed in) and drove skip_intro.sh, the detector that is
documented to work and that is stricter than mine (glyph >= 800 AND a
static frame).
It classified every one of 20 samples over 604 s as "movie", waited them
all out, and timed out. A direct check at 604 s says it was right: zero
green-glyph pixels, screen_id = other, warm mean (69,53,40), correlation
0.09 with build 7. The game really was playing attract movies for ten
minutes.
Three runs, two locales, two launch paths, ~35 minutes of emulator time,
no interactive title. Either the attract loop is far longer than the
600-780 s windows tried, or something regressed since
live-title-press-a.png was captured -- that one came from a pad-driven
boot (
|
||
|
|
8783adda14 |
re: the English control removes the locale from the title-capture problem
Two iterations framed the JP title capture as possibly locale-specific. It is not. Same flags, same oracle, English locale: 75 samples over 734 s, every one glyph = 0. The English boot does not present the interactive title either. Canary was alive throughout, polling XamInputGetKeystrokeEx (1801 calls); the frame at 734 s has content but correlates 0.15 with build 4's render and 0.05 with the main menu -- an attract-movie frame, not title art. So neither locale reaches the interactive title in ~12 minutes without a pad press. What is actually untested is the pad: pad.py and nav_to_flight.sh exist, and title_states_capture.sh reaches title states with a flag set this probe did not replicate (--log_ui_draws --ui_draw_capture_frames, plus xdotool window focus). It is a capture problem, not a locale one. MISSION.md updated. Also resolved a caveat I had given the port agent without checking it: "rotation is decoded but not rendered" does NOT affect their five screens at rest. Title, main menu, EXTRAS and both splash halves have ZERO top-level elements with a non-zero rotation. The only rotations on any of them are the title's two nested ptloop records (r = 30 and -45), and at rest those sit at x = 1521 and x = -839 -- a 399-wide sprite entirely off both edges of a 1280 screen. The caveat now applies only to animating the title build-in, where the sweeps cross the screen rotated. Two METHOD lines: run the control before theorising about the difference, and log every sample so a failure is a measurement rather than a silence. Emulator stopped, lock cleared, locale English. |
||
|
|
f10edf1e79 |
re: fix wait_title.sh's stale oracle; the JP title still is not reached
Last iteration's "never reached the title in 787 s" was a broken tool reporting on the world. wait_title.sh was still sampling the single pixel (625,618) that is_title.py had already been written to replace -- its docstring says why: a 1280x720 coordinate sampled against the 1279x675 game surface, so it always reads the copyright line. The replacement sat in the same directory. wait_title.sh now delegates to it. is_title.py passes its own controls before being trusted here: 753 green-glyph pixels on the committed English title capture, 327 on the main menu, threshold 400. Re-ran with the working oracle and the profile flag the English captures use. The game STILL did not present the interactive title -- but that is now a measurement rather than an artefact: not one frame showed a single green-(A) glyph pixel, and content correlation against either build-7 render never exceeded 0.22. Canary was alive and polling XamInputGetKeystrokeEx (601 calls), sitting in the attract movie. So the open question narrowed again, and is written into MISSION.md: whether the attract loop returns to the INTERACTIVE title without a pad press. title_states_capture.sh claims it does on the English boot with no pad input; if that holds, the difference is the locale. Nothing decided about the keyframe-time association or the rest() rule. Emulator stopped, lock cleared, locale restored to English. |
||
|
|
0f1c0f4e14 |
re: the JP-locale capture is not blocked -- I stopped one grep too early
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. |
||
|
|
6d246c7a97 |
re(ui): finish the top-level rotation census; record the JP-capture blocker
Two threads had converged on needing one capture this container cannot take, so this iteration records that and finishes something reachable. BLOCKED, written into MISSION.md rather than worked around: Q1's keyframe time association and the rest() rule for plateau-less elements both now hinge on a running capture of GP_TITLE build 7, the Japanese title. The console language is not settable here -- user_language appears only as DECLARE_int32 at four call sites with no DEFINE anywhere in the tree, and it is absent from the registered cvars in xenia-canary.config.toml. There is no flag to pass, and guessing one is specifically unsafe: run-canary's own header records that xenia calls ShowSimpleMessageBox from ParseLaunchArguments before logging starts, so a bad flag blocks forever with an empty log. Rebuilding canary to add the cvar would be improvising around the blocker; it needs a human decision. Neither question blocks the five menu screens. FINISHED: the disc-wide top-level rotation count, left running four iterations ago as a shell loop over `screen info --geometry` that never completed (it decodes every texture per build). Walking the placement region directly takes seconds. top-level elements with a keyframe group 15 493 carrying a non-zero rotation 2 152 (13.89 %) Both controls pass: GP_TITLE build 4 reports 0 (its rotations are the nested ptloop records) and GP_DIALOG build 0 reports the expected two. The control earned its place -- the first version indexed the pak with a `screen list` BUILD number and got 0 for a screen that has two, because GP_DIALOG build 0 is entry 2. GP_TITLE maps 1:1, which is how the assumption survived. METHOD line added. Two free corroborations of the rotation decode. The rotated population is dominated by tactical-map ship icons -- pbb_destroyer 444, pbr_destroyer 402, pbr_fighter 276 -- i.e. markers rotated to heading, the single largest use of the field on the disc. And GP_TITLE entry 7's Japanese wordmark pieces settle from ALTERNATING tilts: ptlogo3a r = 0, -14, -4, -1, 0, ... ptlogo3b r = 0, +14, +4, +1, 0, ... ptlogo3c r = 0, -14, -4, -1, 0, ... Same magnitudes, opposite signs, all decaying to upright. A misread field does not produce that. |
||
|
|
b973d8cde3 |
re(ui): scale 0 means collapsed, not "unset" -- stop drawing it full size
blit() and fill_quad() both opened with
let sx_pct = if kf.scale_x == 0 { 100 } else { kf.scale_x };
so an element whose pose is collapsed to nothing rendered at FULL SIZE.
I first described this as "a 1-pixel sliver", reading the .max(1) in the
size arithmetic two lines below -- the guard above it meant .max(1) never
saw a zero. Read the whole function, not the lines you went looking for.
Control run before the change: if 0 meant "unset", something on the disc
would use it throughout. Nothing does.
elements with a keyframe group 15 493
at least one keyframe at scale 0 2 166
EVERY keyframe at scale 0 0
zero is a transient the element grows out of 1 762
ptlogo_eff3.t32 runs 0% -> 200%. An "unset" marker no element ever uses
throughout is not a marker.
Both functions now return without drawing when either scale is 0.
Reach: 24 of 24 renders byte-identical across GP_TITLE (all 16 builds),
GP_PAUSE_MENU and GP_OPTIONS -- additive on every screen the port needs.
195 elements have a guessed rest pose at scale 0 and 126 of those have a
non-zero alpha, so the old code painted them; all 126 are in
GP_READY_ROOM.pak, which S1 already declared a no-go. They are
tactical-map sprites at sx=0/sy=100, mid-horizontal-wipe, that the
coercion drew full-width.
So: a real correctness fix with no visible effect on the menu port.
Suite green, 122 passed / 0 failed across 3 suites at commit time.
|
||
|
|
c2c69b02be |
re(ui): size the rest() guess disc-wide, and refute my own proposed fix
Follows up the defect found last iteration: rest()'s dwell fallback is
guessing whenever it runs. Two things were open -- how big it is, and
whether "rest = the last keyframe" is the fix. Both are now answered, and
the second is answered no.
plateau_census.py walks the placement regions directly instead of going
through `screen info --geometry`, which decodes every texture and cannot
do a disc-wide pass in reasonable time. Its control reproduces GP_TITLE
build 7's three fallback elements and names ptlogo_eff3.t32 among them
before counting anything.
elements with a keyframe group 15 493
no plateau -> rest pose is guessed 3 807 (24.57 %)
... current rule returns invisible 1 711 (44.9 %)
... current rule returns scale=0 195 ( 5.1 %)
the two candidate rules agree 1 911 (50.2 %)
195 elements get a rest pose with scale 0%, which is not a pose. And
disc-wide the choice of rule is not cosmetic: the candidates agree half
the time.
But the port's exposure is one element. Across main menu, EXTRAS, title
and the developer splash, 14 elements are plateau-less and the two rules
agree on 13. The single disagreement is palogo_anima_eff.t32.
And "last keyframe" loses there, on a control that needed no new capture:
the splash carries three sibling glows with identical structure and
identical times --
palogo_gamearts_eff 15:a=0 30:a=255 45:a=255 -:a=0 plateau -> visible
palogo_seta_eff 15:a=0 30:a=255 45:a=255 -:a=0 plateau -> visible
palogo_anima_eff 15:a=0 30:a=255 45:a=212 -:a=0 no plateau
-- differing in one byte. "Last keyframe" makes anima alone invisible
while its two siblings stay lit. The capture agrees weakly: box-mean
ratios capture/render are gamearts 0.717, seta 0.723, anima 0.772, and a
glow we drew that the game does not would put anima below its siblings,
not above.
So the defect is measured and the fix is still undecided. Nothing in the
decoder changed.
|
||
|
|
b947fa8e8e |
re(ui): withdraw the render evidence -- rest()'s dwell fallback is unsound
Last iteration I reported a 13.1% render change in GP_TITLE build 7 as
evidence AGAINST the keyframe-time shift, arguing that language twins
should match in brightness. Withdrawn. Localising the diff to an element
shows it is not about the time association at all.
The element is ptlogo_eff3.t32, a transient bloom:
46: (98,42) 100%,100% a=0
61: (108,72) 0%,0% a=0
103: (108,72) 200%,200% a=255 r=80
-: (108,72) 0%,0% a=0 r=150
No two adjacent poses are equal, so there is no plateau, and rest() falls
through to its longest-dwell fallback. The longest gap is 61->103, during
which the sprite grows from nothing to 200% at full alpha and then
collapses. The rule returns whichever end of that movement the indexing
lands on: the invisible frame as decoded, the 200% peak shifted. An
896x389 sprite at 200% is larger than the screen, which accounts for the
entire 13.1% and the entire 4.9-unit luminance gap. I was comparing a
heuristic against itself.
The defect generalises, and structurally. A dwell gap is time spent
interpolating FROM pose k TO pose k+1; neither pose is held during it
unless the two are equal -- which is a plateau, and the plateau path has
already returned by then. So the fallback is guaranteed to be guessing
every time it is reached, under any reading of the times. Every element
with no two adjacent identical poses has a guessed rest pose, in our
renderer and in anything built from it.
Consequences: the case FOR the shift (26x on the hold:fade-out ratio) is
no longer opposed by render evidence -- 10 of 11 builds byte-identical,
the 11th differing only through this heuristic. It is still not adopted,
now because it flips this element to the visibly wrong answer, so the
shift and a decision about plateau-less elements must land together, and
neither half has a capture to verify against.
Default-mode suite green (122 passed, 0 failed across 3 suites so far).
|
||
|
|
6f23f4d113 |
re(ui): the keyframe-time shift is favoured 26x by timing, rejected by a render
Follow-up on last iteration's unadopted candidate (+36 holds the NEXT pose's time, not its own). Two new results, pointing opposite ways, and both are reported. FOR, and calibration-free: the observed full-alpha hold : fade-out ratio on palogo_gamearts is 83 : 13 frames = 6.38. The shifted reading predicts 8.00. The current reading predicts 0.25 -- off by 26x. With the glow's 2 units/frame fixed and nothing else free, the current reading says the logo holds full alpha for 2.0 frames; the capture holds it for 83. This is no longer the shape argument the candidate rested on. Also for: rest()'s plain max-dwell fallback picks a=0 -- a transparent pose, for a publisher logo -- under the current reading, and the visible a=255 hold under the shift. Only the rest_plateau special case rescues the render today, and that is the case the port agent reported a bug in. AGAINST: rendering every build of six UI paks under both readings, 10 of 11 compared are byte-identical and one changes -- GP_TITLE build 7, the Japanese twin of build 4, by 13.1% of pixels. Build 4, the one verified against a live capture, is unchanged either way, so the single build the shift moves is the one with no capture to adjudicate it. The proxy goes against the shift: language twins are the same artwork, and build 7 reads 70.94 mean luminance as decoded against build 4's 71.41, but 76.32 shifted. Correlation does not separate them (0.6206 vs 0.6201). These constrain different things -- timing versus pose selection -- and rest() is a heuristic layered on the times, so moving the times moves its tie-breaks. Adopting the shift means revisiting that heuristic in the same change, with no build-7 capture to verify against. Default UNCHANGED. Experiment reachable via SYLPHEED_KF_TIME_SHIFT=1. Shifted-mode suite: 122 passed, 0 failed across 3 suites at commit time. |
||
|
|
531840b417 |
re(ui): Q1 -- the interpolation law holds, the group timeline does not
Q1's gate asks whether the ramp is linear. It is, and that result stands: it rests on the splash's _eff glows, which reproduce exactly. This adds the part that does not. The test is a calibration, not a fit. Fix the clock on palogo_gamearts_eff -- declared 15-unit fade-in 0@15 -> 255@30 against captured alphas 34,68,102,136,170,204,238, a constant step of 34, giving t = 2f - 171 -- then check that against the glow's own next landmark: its declared hold ends t=45, predicted frame 108.0, observed last full-alpha frame 107. Then apply it to palogo_gamearts in the same bundle and the same frames, with no free parameter left: declared a=232 at t=206 -> frame 188.5, observed alpha 255 declared a= 32 at t=210 -> frame 190.5, observed alpha 255 The logo is still at full alpha nine frames after it should read 32; its fade-out runs ~17 frames late; its declared 80-frame fade-in is never drawn. Not culling -- the same element is submitted down to a=7 on the way out. Calibration-free version: the declared fade-out spends 12 of 16 units dropping 23/255 of the alpha, and the capture has no such plateau. Candidate, offered and NOT adopted: if +36 held the NEXT keyframe's time, the fade-out shape fits (RMS 4.05 vs 12.13, two elements) and the decoder's "last block's time is unreadable" special case disappears -- the last block would simply have no successor. Rejected for now because it explains neither the missing fade-in nor the lateness, and because the _eff elements cannot discriminate between the readings at all (with four blocks the shift only relabels the phases). Decoder unchanged. Also withdrawn, mine, within the iteration: "the _eff glows hold a constant alpha 33". They ramp 34 -> 255 in steps of 34. I printed the series minimum and read it as its range, with a "14 distinct colours" column sitting next to it saying otherwise. |
||
|
|
f11fde51e5 |
re(ui): rotation is not nested-only, and the pivot-anchored scale is measured
Two corrections and one new confirmation, all from finishing the sweep I left running last iteration. Refuted, by my own sweep, within the hour: "rotation appears to live only in nested .rat leaf records". That held for GP_TITLE, GP_BUNK and GP_CHALLENGE -- the three archives the sweep had reached when I wrote it -- and fails on GP_DIALOG and GP_DEBRIEFING_PILOTLOG, which rotate top-level elements. Scoping the claim made it cheap to withdraw, but the sentence should have waited for the sweep. Those top-level cases are the best evidence on the disc, and they show up in `screen info --geometry` without a hex dump. GP_DIALOG build 0: pceff03/pceff04 ramp r = 90 -> 30 -> 10 -> 3 -> 0 while alpha ramps 0 -> 255 and they slide into place -- a swing-in settling upright. Build 6: pzeff02 ramps 43 -> 61 -> 75 -> 90 while scaling 112% -> 200% and fading to 0 -- a spin-out. Rows committed as reference data. New: the pivot-anchored scale term in blit, kf.x - pivot*(scale-100)/100, was implemented and reasoned about but never measured, because every element previously examined sits at 100% scale where the term is exactly zero. The ptloop pair scale 600% and 800%, where it is worth 450 and 630 px. Formula predicts centre y = 360.0 for both; capture measures 359.1 and 360.0. Top-left anchoring predicts 810 and 990. Horizontally it makes t-from-position agree with t-from-alpha to 0.33 / 0.65 units against ~8 without it. That agreement does NOT prove linear interpolation -- both fields were inverted through the same linear map, so a shared easing curve cancels. It shows position and alpha ride one shared parameter. Recorded as such. The disc-wide sweep is still running; the count is incomplete, the existence is settled. |
||
|
|
67fa1a1b0b |
re(ui): decode keyframe +12 as screen-plane rotation in degrees
The rotated quads on the title screen come from the keyframe block after all. The earlier negative -- "every GP_TITLE build 4 element has all three angle words at zero" -- read the right bytes over too small a region: it walked the top-level declaration table, and the rotated elements are the nested leaf records ptloop01.rat / ptloop02.rat. Confirmed against the framebuffer rather than against our own renderer. The two records declare +12 = 30 and -45; the GPU capture submits their quads at +30.26 and -45.28 degrees -- magnitude and sign, two different values. Corroborated by shape in GP_BUNK 117ca14f, where +12 ramps 0 -> 360 with position, scale and alpha constant: a spin in place. Identifying which draw it was needed edge lengths, not bounding boxes: 400x1076 and 400x1444 against pteff03/pteff03a 399x180 at the elements' two different declared scales, 600% (1080) and 800% (1440). The same test names three known-positives in the capture (ptlogo1, ptcopyright, ptbtn00), so it passes its own control. Keyframe gains rotation_deg plus unknown_4/unknown_8, carried rather than dropped. NOT rendered -- ui_layout::blit is axis-aligned only, so the reference renderer and the port will both draw these upright until a rotating blit exists. The census tool ships with the trap that broke its first version: nested RATC blobs are not 4-byte aligned, so an aligned scan found 0/3 of its own control blocks and missed 16 341 blocks. Disc-wide +12 is non-zero in 14.50 % of 83 862 blocks. sylpheed-formats tests, SYLPHEED_DISC set: 131 passed, 0 failed across the 6 suites finished at commit time; the run had not yet completed. |
||
|
|
130ad8d1b3 |
re: the skewed draw is not the swoosh -- refuting the identification five
iterations of work were built on I flagged last iteration that "draw 2 is the swoosh" was an inference I had never checked. Checking it took one line, and it is wrong. Converting the quads from NDC to screen space: quad A spans y -209 to 925, quad B spans y -292 to 1012. Both cover the full screen height and run well off it. The swoosh, ptlogo_back2, rests at (71,126) and is a 234-pixel band at y 126 to 360. Draw 2 is not it. A candidate offered as one: the two ptloop sweeps. pteff03a is 399x180 with element scale (100,800), so 399x1440, and pteff03 carries (100,600) -- two long thin sprites, two quads, and the drawn vertex alphas 0xC3 and 0xB6 sit inside the 0x80 to 0xff ramp those records declare. Not confirmed; no texture or position match was made. If that is right it matters, and I have written it conditionally rather than acting on it: our renderer parks those sweeps at their final keyframe, both off screen, and draws nothing, while the game draws them across the screen. That would mean the capture caught them mid-sweep inside their t=150 to 600 window, and that the "groups hold" reading needs re-examining for those two elements. What this cost is worth stating plainly. Five iterations -- the additive blend test, the pivot analysis, the vertex-colour capture -- were built on an identification made by elimination on one screen and never checked against the draw's own coordinates. The eliminations themselves stand, because each was measured against the capture rather than against the identification. The chain that pointed them at ptlogo_back2 did not. METHOD takes the general form. |
||
|
|
6930d301c1 |
re: full suite green, and correcting the route I recommended last
iteration Two loose ends, both on my own work. The disc-gated suite finally completed: all six binaries, 131 passed, 0 failed, one pre-existing ignored. So the rest() fix and the T8adImage flags field are fully verified rather than verified-on-three-binaries. The reason earlier attempts produced empty logs was that the tool timeout's SIGTERM reached the whole process group; setsid nohup survives it, which is now a METHOD line. And a correction that matters more. Last iteration I closed the swoosh as undecodable from the disc and named a per-draw GPU capture as the next route, "because it reads the actual blend state". It does not. Reading command_processor.cc, each captured draw records primitive type, index count, index-buffer address, VS and PS ucode hashes, the pixel shader's texture bindings, and vertex attribute 0 of binding 0. There is no RB_BLENDCONTROL dump. So the route splits, and I have said so rather than leaving the wrong version standing: the capture can test a per-draw VERTEX COLOUR today with no code change, which would explain white-versus-pink directly, and getting the blend mode itself needs a Canary change to dump the blend registers. Either way it is instrumentation rather than another field. The general lesson goes in METHOD too: validate a recommendation before leaving it as advice. A named next step is a claim like any other, and I made it without checking. |
||
|
|
5d02d5cd60 |
re: additive blending refuted, the swoosh is not displaced, and the
residual is smaller than I said Testing the candidate I raised last iteration rather than carrying it. Blending bit-0x02 sprites additively moves every measure the wrong way -- whole-frame mean diff +0.55 to +1.04, swoosh-band mean +1.83 to +3.98, band edge-correlation 0.6971 down to 0.5578. So the bit is real and independent but does not select an additive blend. I reverted the experiment and kept the word as T8adImage::flags, documented and not acted on; the render is byte-identical to before. Second refutation: the swoosh is not displaced. Shifting the band over plus or minus 80 by 8 pixels peaks sharply at zero, 0.7342, falling to 0.22 at 24 px. So the pivot story is dead twice over -- inert at scale 100, and no displacement to explain anyway. And I have restated the residual, because earlier sections overstated it. The +16 to +34 band tiles I quoted were measured WITHOUT --primitives. With the dim drawn the band's average is nearly right at +1.83; what is wrong is its structure, tiles running -38.6 then +33.8 and cancelling. Six candidates eliminated now and none confirmed. One caveat I owe the port agent about the capture I gave them: it is at t=4.0s, roughly 174 keyframe units into a screen whose elements have keyframes out to t=600. I judged "settled" from mean luminance, which cannot see a thin sprite still moving. It is settled for the bulk of the screen and not proven settled for every element -- which is a live alternative explanation for a structural difference in exactly the band the sweeps cross. METHOD: cargo build passing does not mean cargo test compiles. Adding the field built the library in 1.48s and broke two test-only struct literals; cargo test failed with exit 101. |
||
|
|
e0d02dce71 |
re: a candidate blend flag -- T8aD header +0x04, bit 0x02
Continuing the swoosh. Last iteration ended with "the next step is finding where a blend mode would be encoded, and I do not know the format carries one". It does carry a candidate. The kind field is not it -- the swoosh sprites are kind 0x0, the same as ordinary ones. But the T8aD header word at +0x04 splits the title's sprites exactly along effect versus normal: pteff01, pteff03a, ptlogo_back2eff1..5 and both ptlogoall_eff are 0x8832, while ptlogo1, ptlogo2, ptlogo_tm, ptbase2, ptlogo_back2 and ptcopyright are 0x8830. One bit, 0x02. Disc-wide it behaves like a real flag rather than an artefact: 19216 sprites, 18 distinct values, bit 0x02 set in 27.1 percent, and it toggles against otherwise identical words -- 0x8830 against 0x8832, 0x0830 against 0x0832, 0x0810 against 0x0812, 0x0030 against 0x0032. Marked as correlation and not decode, because nothing here shows the bit MEANS additive. The one thing that makes it more than a guess is ptlogo_back2eff, which carries 0x8830 despite having eff in its name -- so the split is the field's and not my pattern-matching on names. The test is to blend bit-0x02 sprites additively and re-correlate the title against the plate-free capture. METHOD gets the trap that cost the first attempt: searching for a sprite's name and taking the next T8aD returns the SAME header for every sprite, because the names all live together in the declaration table. It failed its own control at once -- different sprites reporting identical dimensions -- and the fix was to match on width and height instead of on proximity. |
||
|
|
2a0afc1405 |
port: land the pivot finding on the handoff (third assertion miss)
The scripted HANDOFF edit asserted on text an earlier edit had rewrapped, so the pivot and colour findings landed in docs/re and not on the page the port agent reads. Third time this session; METHOD now says the rule was insufficient and names the mechanical fix. |
||
|
|
e249b2e2ff |
re: the swoosh's pivot belongs to the Japanese sprite, and the colour is
not in any field I can find Chasing the swoosh defect. The colour half is a clean negative and the geometry half now has a number. Ruled out for the colour: every keyframe of every swoosh element carries fade 0x??ffffff -- white RGB with only the alpha varying -- and no tint is anything but white. The texture is not pink either; ptlogo_back2 decodes blue-leaning at (175,174,198) and its glow warm at (255,253,234). So the magenta edge the game draws comes out of blending, and I have said that rather than leaving "wrong colour" as an undifferentiated mystery. The geometry half: the declared pivot does not match the element's own texture. Build 4's ptlogo_back2 pivot is (500,117), which is exactly half the JAPANESE sprite at 1000x234, not half its own English one at 1118x262 -- off by 59 px, the right order to make the swoosh too thick and extend too far right. The layout record is authored once and shared while the sprites are swapped per language, which ui-rat-layout.md warns about in general; this is that warning measured on the screen where our render disagrees with the capture. It cuts both ways -- the Japanese build's ptlogo1 is off by 40. Marked as a quantified candidate rather than a diagnosis, because whether it bites depends on which pivot our compositor uses and I did not check. One METHOD line, because the first version of that check was misleading: a pivot-equality test conflates rounding with real breakage. Equality flagged 74 percent of title elements. Split by magnitude it is 17 exact, 54 off by a pixel from odd dimensions, and 24 genuinely off -- and those 24 are the finding. |
||
|
|
4bc970688f |
re: rest() fixed -- a trailing keyframe run is the hold when it is
VISIBLE, not when its pose repeats The port agent's report was right about the defect and about which elements it hits, and its proposed condition does not survive the case the exclusion was written for. Their test -- the final untimed keyframe has the same pose as the last timed one -- is also true of pgptitle.rat, whose trailing run is two identical transparent frames. Adopting it would erase the word PAUSE again, which a committed capture disproves. What separates the two is alpha: an exit fades the element out so its last keyframe is transparent, while an element with no exit ends on the pose you can see. So rest_plateau now accepts a trailing run exactly when it is visible. Verified against a capture rather than against another renderer, which is the point their own report made: ptframe1's rest moves from (620,108) t=16 to (440,108) t=62; the changed pixels are 10082 in a bounding box of x 440-839, y 108-577, which is exactly the 400x470 at (440,108) they predicted; and correlation against main-menu-oracle.png over that region improves from 0.9596 to 0.9748. The PAUSE wordmark is unchanged across all three pause builds. This also closes a question ui-paint-order-key.md has carried for a while, that ptframe1 and ptframe2 rest at alpha 0 while the capture shows the menu frame plainly. One trap cost most of this iteration and goes in METHOD: CARGO_TARGET_DIR is redirected in this container, so ./target/debug/sylpheed-cli is hours stale and every render I made against it was old code. Byte-identical before and after reads as "no effect" when it actually means "you ran the old binary". The full disc-gated test suite was still executing when this was committed; the verification above is artifact-based. |
||
|
|
ed4e5c7b37 |
re: build 4 captured without the plate, and keyframe groups hold rather
than loop The port agent ranked a plate-free capture of build 4 above any further static RE, so that came first. B from the main menu returns to the title and the plate fades in a beat later, which opens a clean window. Recorded at 20 fps from the press: the art appears at 1.10 s, builds in to 3.70 s, sits settled and unobstructed until 5.00 s, and the plate arrives at 5.10 s -- the band jumps from 282 to 3755 bright pixels. Committed the frame at 4.0 s as the reference for the cyan glow slab they report drawing and the game not having. Their other sub-question -- whether a keyframe group loops or holds -- falls out of the decoded sweeps plus a measurement I already had, and the two agree. ptloop01's final keyframe parks pteff03.t32 at x=1521 and ptloop02's parks pteff03a.t32 at x=-839, both off-screen on a 1280-wide design; and over 18 s of settled title the centre tiles sit at sd <= 0.01 when a looping group would recross the screen every 7.5 s. So groups HOLD at the last keyframe. The loop*.rat name is misleading. Also recorded, in the corpus rather than only in their report: the rest_plateau bug, with their exact identifying condition -- the final untimed keyframe has the same pose as the last timed one -- the six elements it misses on main_menu, and the bracket it drops. That closes an open question ui-paint-order-key.md has carried for a while about ptframe1 and ptframe2 resting at alpha 0 while the capture shows the frame plainly. Same two elements, same cause. Not fixed yet; the change is in ui_layout's rest(). And a METHOD line I would not have written myself: two renderers agreeing is not evidence the field is right. Their composite and screen render matched to 3/255 on main_menu and both omitted two elements the game draws, because both read one field through one decoder. |
||
|
|
83293c3f81 |
port: land the loop decode on the handoff, and note the edit trap
Second iteration running where a scripted HANDOFF edit failed its assertion on rewrapped text, leaving the port's page stale while docs/re had the finding. Patched by reading the file first, and METHOD now says to check the commit's file count for a silently dropped edit. |
||
|
|
3717293fa9 |
re: the loop records decode -- and they are not what I measured
Two of my own claims from last iteration die here, and the decode is the thing that kills them. Searching for the SHAPE instead of an assumed count found it immediately: fade words matching 0x??ffffff at alternating strides of 24 and 16, which is a 40-byte block carrying fade at +0 and tint at +24 -- exactly the build layout, starting at +0x68. So "a leaf record's keyframes are not in the build's 40-byte layout" is withdrawn. They are. My scan failed because I demanded 29 strictly-increasing times, having read the word at +0x004 as a keyframe count; the records hold THREE. Decoded, both are horizontal light sweeps. ptloop01 runs pteff03.t32 from x=-639 to x=1521 over t=150..600, which is 450 units or 7.5 s; ptloop02 runs pteff03a.t32 the other way over 570 units or 9.5 s. Which refutes the second thing. Last iteration I attributed the measured 2.2 s oscillation to these elements because build 4 declares them -- association, not evidence. A 7.5 s cycle would give about three peaks in a 22 s capture and I counted eight. So the loops are not what I measured, the 2.2 s stands as a measurement, and its source is now explicitly unidentified rather than wrongly assigned. METHOD gets the general form, because it is what cost the previous iteration: do not hard-code an expected element count into a structure scan. Scan for the shape and let the count fall out. |
||
|
|
9f39e8ee72 |
re: the splash confirmed against the running game, and timed
Closing the amber I raised last iteration: the splash renders had no framebuffer capture to diff against. Recording the boot from the moment the window appears, at 2 fps, catches it before the movie. Both halves match their own render at zero shift, each with the OTHER half as a negative control: publisher frame 0.9146 against entry 10 and 0.027 against entry 11; developer frame 0.9792 against entry 11 and -0.033 against entry 10. The EN/JP twins score 0.876 and 0.966, so the test tells a screen from a different screen but cannot tell a pair apart, and I have said so rather than implying it discriminates. The recording also times the front of the boot, which Q6 and Q7 only had from the title onward: SQUARE ENIX from about 0.5 to 4.0 s, black, developer logos from about 5.0 to 7.5 s, black, then ADV.wmv from about 9.5 s. One trap avoided and written into METHOD. The frames around 9.5 to 12.5 s show SQUARE ENIX again in cyan, and read from the capture alone that is a third logo screen. It is not -- it is the intro movie's own opening, which the milestone-2 notes describe as white SQUARE ENIX plus cyan glow plus red diamonds. A logo appearing twice may be two different things. |
||
|
|
b302558d26 |
re: the developer splash renders -- screen 1 of 5 finally has a composite
Two pages of this corpus disagreed. MISSION says the splash "is the RATC screen, which already renders"; ui-paint-order-key.md says it "cannot be rendered by screen render at all". Running the tool both ways settles it. screen list --all shows all 16 GP_TITLE entries instead of 12, and the four the default listing drops are the splash, each half shipped twice: entries 10 and 13 are the white SQUARE ENIX publisher logo, entries 11 and 14 the GAME ARTS / SETA / studio anima developer logos. Entry 11's seven elements are the three logos, their three _eff glows and the palogo_eff0.prm backdrop -- exactly the composition ui-paint-order-key.md had measured for the splash without being able to draw it. So the "cannot be rendered" line is wrong and is corrected in place. What is true is narrower and worth keeping: the splash is invisible to the DEFAULT listing because is_build wants a .rat child, so anyone who does not pass --all concludes it is missing. That goes in METHOD -- a default filter can hide a whole screen and the corpus will record it as absent. The payoff is for the port rather than for the RE: the first of the five screens now has a reference composite, which it did not have. Marked amber on one point -- there is no framebuffer capture of the splash in this repo, so the match to the running game is by description against the milestone-1 notes, not by pixels. |
||
|
|
cc72f8b5a8 |
re: the phase transitions -- and withdrawing yesterday's refutation
because the test probed the wrong state Nine stores to the phase field this+132, control-gated on the 136 query returning at least the 18 known ones. Attributed to the handler each lives in, they give the phase graph: entry -> 2, then 2 -> 0 (splash), 2 -> 3, 3 -> 4, and 4 -> 2 on event 0. That last edge is the problem with what I did last iteration. The event-0 block sets TWO fields one instruction apart -- stw r28,136(r30) for state 0 and stw r11,132(r30) with r11 = 2 for phase 2. So B from the menu lands in PHASE 2, the same phase as the boot title, and the test I ran believing it probed phase 4 state 0 probed nothing of the kind. So the refutation is withdrawn. Worse for me and better for the idea: A working on the B-returned title is exactly what the hypothesis predicts, since phase 2 is the phase that references BUTTON. The hypothesis is back to untested, now consistent with two observations rather than one, and what it still needs is the attract-returned title's phase -- which no test so far has read. The measurement from that iteration stands, because it does not depend on the phase: the B-returned title accepts A, so only the ATTRACT-returned title is inert. I have said so explicitly in the page rather than letting the withdrawal take the good half down with it. METHOD gets the lesson: before testing "state X behaves like this", check your route actually reaches state X. When a transition writes more than one field, read the whole block and not just the store you were looking for. |
||
|
|
4380305921 |
re: phase 0 is the splash, phase 2 is the title -- and a candidate answer
to this corpus's oldest input puzzle Continuing Q6 on the phases left unread. Strings each phase handler references, plus whether it carries its own jump table: phase 0 sub_821C5690 LOGO no switch phase 2 sub_821C5818 BASE_INFO, BUTTON, TITLE_SCREEN no switch phase 3 sub_821C5EC0 (none) one switch phase 4 sub_821C6458 BASE_INFO, LOADING, TITLE_MENU, TITLE_SCREEN Phase 0 referencing LOGO is a second independent confirmation that it is the developer splash -- the iterate3E notes reached the same function from the guest side and named the splash's LOGO items. Phase 2 draws the title WITH the PRESS A plate, which the archive side had already established is a build of its own. Which produces something worth more than either: the title is installed from TWO places, phase 2 and phase 4 state 0. Same screen, different code. canary-scripted-input-traps.md has recorded for months, and never explained, that the boot title accepts A while the attract-returned title accepts nothing, with the giveaway that a draw capture in each is identical. Two code paths installing one screen is exactly that shape. I have written it into that page as a candidate with the cheap test named -- read this+132 on each title -- and marked it untested, because it is. One query in this iteration failed its own control and I threw its half away: counting stw rX,136(r30) per phase returned 0 for phase 4, which has 18, because the operand text has a space the pattern did not allow. The bctr half passes its control and is reported. METHOD gets the underlying trap: instructions.function is unpopulated for most rows, so a query scoped on it silently returns nothing. |
||
|
|
86e52b01d0 |
re: the menu's music is BGM_103 -- found by accident while chasing Q6,
confirmed three ways Chasing where the event code comes from, sub_821C5580 turned out to do two things worth having. For Q6 it arms the outer gate: li r11,3 ; stw r11,16(r28) is exactly the value sub_821C7850 tests before dispatching, which answers the "what does this+16 == 3 gate on" question I left open last iteration. It then passes r5 = [r27+4], an event read out of a structure, so the vocabulary is still not enumerable. Of the four callers of sub_821C7850, three pass constants -- 0, 0 and 5 -- and this one does not. The accident is a few instructions earlier: li r5, 1103 into a sound-play call. 1103 is a BGM cue id, BGM_103. That closes a residual I had written off as undecodable: which bank the menu plays. The cue table genuinely cannot say, since its BGM entries are numeric -- but the code can, and it checks out three independent ways. The census says BGM_103.slb is two waves of 3876864 and 3930112 bytes; the XMA probe at the main menu saw two stereo streams of 3876864 and 3930112 bytes. Byte for byte on both. That also corrects me. bgm-two-stems.md said those observed sizes matched no bank's declared waves and inferred the decoder gets a window rather than a whole wave. Wrong on both counts -- I had checked only the BGM_0xx rows of the census because that was the block on screen. METHOD gets it: check a measurement against the whole reference set, not the part you happened to be reading. |
||
|
|
c8c7677f6d |
re: the lookup argument is decoded at 46 of 48 sites -- and it refutes my
own corroboration from last iteration Last iteration I listed a candidate name vocabulary from "upper-case strings the callers reference", marked it amber because those are not proven arguments, and named the check: confirm per call site which string actually lands in r5. Ran the check. It fired. The first extractor FAILED ITS OWN CONTROL -- a backward scan for addi r5 recovered 7 of 48 sites and missed both title sites I had already read by eye, because there the name is built in r27 and reaches r5 via a later mr. Rewritten as a forward register simulation it reproduces all three known sites and recovers 46 of 48, giving 28 distinct names. Two of my own claims die. DIFFICULTY and EXTRA_MENU are NOT arguments at any site -- they are strings that merely live in the same functions, so the "independently corroborated by measurement" line I wrote was an artifact of the loose filter. Only TUTORIAL_MENU survives. And sub_821CC860 is not a screen factory at all: its real arguments include BG, BLACK, FADE, FILE, KEY, PAD, SOUND and GAMMA_RGB. It is a generic name-keyed lookup, mostly config, which the title part happens to call with its three state names. The title finding itself is untouched -- sub_821C6458 really does transition by calling this with TITLE_SCREEN, TITLE_MENU and LOADING. What is gone is the inference that the 28 names are a screen vocabulary. Two METHOD lines, both earned here: strings a function references are not arguments it passes, and a backward scan for a destination register misses arguments staged through a scratch register. |
||
|
|
0d7199bc22 |
re: the UI sound effects ARE extractable -- retracting "cannot be
extracted", and the tool was in the build all along Two iterations ago I closed Q8 by declaring the SE audio undecodable: Static.slb has no wave boundaries, there is no XACT container anywhere, and I said the index "exists only at runtime" as though that put it out of reach. The disc half of that stands. The conclusion did not. This build of Canary carries a cvar called xma_param_probe, added by this project, whose own comment says it logs each XMA stream's parameters and head bytes so raw sound.pak entries can be matched to real decode params. It has been sitting in the startup CONFIG DUMP of every log I have read this session. Run with it, driving the main menu: a d-pad move spawns a new mono 48 kHz stream of 4 packets / 8192 bytes, and B spawns a different one of 2 packets / 4096 bytes. Searching their logged head bytes in Static.slb finds each at exactly one offset -- 0x1ec0 and 0x0ec0 -- and the two are contiguous, 0x0ec0 + 4096 = 0x1ec0. So the bank is a packed run of whole 2048-byte packets with no delimiters, which is precisely why the seek scan found nothing: there is nothing to find. A wave is (offset, packet count) and nothing else. That splits Q8's binding cleanly. Event -> WAVE is now measured: the port can have the audio. Event -> cue NAME is still a name match on the authors' identifiers, and the page says so. The same run settled something for Q10 too. Sitting on the main menu, TWO stereo 48 kHz streams were decoding simultaneously. bgm-two-stems.md said that observation was what it needed and that this container could not make it; it can, and a music bank's two waves are now measured as simultaneous rather than only inferred. METHOD gets the general lesson, because it cost two iterations: check what instrumentation the local build already has before declaring a question blocked on tooling. |
||
|
|
aea529e7dc |
re: S00A.wmv confirmed off the running game -- the movie was never
ambiguous, the sampling was Continuing last iteration's amber rather than starting something new. The fix was already named there: stop using 5-second screenshots and record the display. Recorded with ffmpeg x11grab at 10 fps and matched every 0.5 s. Across the 25 consecutive samples from capture 5.0 s to 17.5 s the S00A playhead is strictly non-decreasing -- 1.0 through 11.0 s, advancing at essentially real time -- with scores at or above 0.96 and four of them at 0.999 or 1.000 against a runner-up in the 0.78-0.83 range. S00A is the top match on 23 of the 27 frames carrying signal. So MS00A -> S00A.wmv is decoded from the manifest AND measured off the game, and the intro begins about 4.5 s after A on the save slot. The previous attempt's failure is worth its own METHOD line, because it did not look like a sampling problem: it looked like weak evidence for the wrong film. Sparse sampling produced contrast-23 frames, a playhead that would not join up, and one frame preferring ADV. Sampling does not weaken a signal gracefully; it turns it into noise shaped like a different answer. One aside recorded and not chased: in the S00A 5.5-10 s window, ADV also scores 0.97-0.99 at its OWN monotone playhead of 33.5-37.5 s. Two films matching strongly with two consistent playheads is not noise -- it reads as the boot movie being a trailer cut from the story cutscenes, which also explains why the sparse run kept flipping between them. |
||
|
|
dfcaac221e |
re: the new-game path completes, the intro is only weakly identified, and
this container kills long runs Three results, and the middle one is deliberately not upgraded. First: NEW GAME -> DIFFICULTY -> NORMAL -> SELECT DATA -> pick a slot -> a movie plays, with no crash. The previous run's throw at 0x82307128 is therefore not inherent to the menu path -- the same six presses got through it -- which fits the incomplete-cache trigger the corpus already names. n=1 either way, and the note says so. Worth recording because one observation had already hardened once into "A on NEW GAME hangs", and it would have hardened again into "the new-game path crashes". Second: with the path open, the manifest's MS00A -> S00A.wmv finally got a runtime test, and it did NOT reproduce the quality of the attract-movie identification. The first four samples give a clean monotonic S00A playhead of 2, 6, 9, 13 s and the strongest frame matches at 0.916, but the run as a whole is not one advancing timeline, most frames sit at contrast 23-37 where this page's own rule says discrimination is poor, and one high-contrast frame prefers ADV. So it stays amber: consistent with S00A, corroborating the manifest decode, not confirming it. A clean answer needs the 30fps x11grab route rather than 5-second screenshots. Third, and it bounds the rest: the emulator vanished at ~145 s with no crash line in its own log, last entries routine MEM-WATCH at 1.15 GB RSS. Second time this session -- the other was ~50 s into a boot. That is an external kill on a box with ~1 GB free and swap exhausted, not a guest fault. METHOD and the handoff's residue table both now say dynamic experiments here have to fit in about two minutes of guest time, which is why the DELTASABER plates went unseen again: they need a mission load and the run never got there. |