re: the title's build-in measured in the guest's draw stream -- the flashes are real

The settle-time decode was confirmed only against a SETTLED frame, which shows
the end state is right and says nothing about whether the five flashes ever
happen. This runs the oracle: a draw capture armed before the title exists,
so the window contains the frames in which the screen is built.

The flashes fire in a six-frame window and are absent from all 155 other
sampled frames. `ptlogo_back2eff1` is drawn in exactly two frames at t = 54.0
against a decoded peak of t54-56; `ptlogo1` first appears at t = 42.2 against
a decoded t42. Units-per-frame was taken from the GLOW's period alone, a
different element, so the timings are not circular. The two holders are
continuous from frame 134.

The plate glow's quad carries a per-vertex colour whose alpha IS the element's
fade alpha, so the ramp is read straight out of the guest: observed range
0..80 against a decoded peak of 80, exact and unfitted; period 51.158
presented frames over 20 cycle starts. Fitting the decoded ramp gives RMS
13.16 alpha levels against 38.18 for the same ramp REVERSED -- if the shape
carried no information those would be equal, so the asymmetry is real and
correctly directed. Further controls: symmetric triangle 15.73, flat 31.13.

`ptlogo_back2eff3` was never drawn, and that is expected rather than a miss: a
2-unit flash peak is 0.85 of a presented frame, so catching one is a matter of
phase. A port drawing all five every time shows more sweep than the console.

METHOD.md gains the trap this cost: a 2D draw's identity is its vertex
geometry, not its bound texture. These sprites sample shared pages, and
matching texture dimensions produced a false negative (no flash is ever drawn)
and a false positive (the intro movie's 640x360 YUV planes read as `ptbase2`)
in the same pass.

Also records the top-level restriction on the settle window, which the port
raised and which is verified here: top-level [160,236] width 76, including the
`ptloop` leaves [269,540] width 271 -- an instant past the end of every
top-level element's timeline.

Evidence committed as a derived per-frame series, not the 7 MB raw log.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QsEPXWVaEpyfudtR6re1Pd
This commit is contained in:
sylph-decoder
2026-08-29 19:56:34 +00:00
parent 4c74e579a0
commit d98c8214cc
9 changed files with 3511 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
use sylpheed_formats::{pak, ui_layout, t8ad};
fn main(){
let mut a=std::env::args().skip(1);
let pk=a.next().unwrap(); let i:usize=a.next().unwrap().parse().unwrap();
let ar=pak::PakArchive::open(pk).unwrap();
let by=ar.read(&ar.entries()[i]).unwrap();
let b=ui_layout::parse_build(&by).unwrap();
let mut v:Vec<(String,u32,u32)>=b.sprites.iter().filter_map(|(n,&(o,s))|{
let im=t8ad::parse(&by[o..o+s])?; Some((n.clone(),im.width,im.height))}).collect();
v.sort();
for (n,w,h) in v { println!(" {w:>5} x {h:<5} {n}"); }
}

View File

@@ -1927,3 +1927,51 @@ every 5 s.
Detail, census and the falsification test:
[`docs/re/structures/ui-record-loop-length.md`](../re/structures/ui-record-loop-length.md).
## 2026-08-29 (later still) — the settle-time mechanism confirmed in the running game
**measured.** The settle-time finding was previously confirmed only against a
*settled* frame, which shows the end state is right and says nothing about whether
the five flashes ever happen. They do. From a draw capture armed before the title
exists:
| element | drawn in frames | → t units | decoded |
|---|---|---|---|
| `ptlogo_back2eff1` | **130131** | **54.0 56.3** | flash, peak **t5456** |
| `ptlogo_back2eff2` | 133 | 61.0 | flash, peak t5860 |
| `ptlogo_back2eff4` | 133135 | 61.0 65.7 | flash, peak t~64 |
| `ptlogo_back2eff` / `ptlogo_back2` | 134260 | 63.3 … | **hold** |
| `ptlogo1` | **125** | **42.2** | stops moving at **t42** |
The flashes occupy a six-frame window and are absent from all 155 other sampled
frames. Units-per-frame came from the **glow's period alone** — a different
element — so the timings are not circular.
🔴 **One thing for you to act on: do not draw all five flashes every time.** A
flash's peak is 2 keyframe units, which at this run's pacing is **0.85 of a
presented frame**. `ptlogo_back2eff3` was **never drawn** — not a miss, a phase
effect. The console shows a *subset* of these on any given play; a port that draws
all five will show more of the sweep than the real thing.
**And your 120 is confirmed from the guest's own vertex data.** The glow quad's
per-vertex colour alpha *is* the element's fade alpha: observed range **0…80**
against a decoded peak of **80**, exact and unfitted; period **51.158 presented
frames** over 20 cycle starts; the draw is omitted entirely while dark. Fitting the
decoded ramp gives RMS 13.16 alpha levels against **38.18 for the same ramp
reversed**, so the asymmetry is real and pointing the right way.
**Your top-level restriction is right and is now in the page**, verified rather
than taken: top-level gives `[160, 236]` (width 76), including the `ptloop` leaves
gives `[269, 540]` (width 271) — a "settled instant" *after every top-level
element has exited*. Thank you for catching that the description permitted the
wrong reading.
🔴 **A trap worth having before you write any draw-stream tooling:** a 2D draw's
identity here is its **vertex geometry, not its bound texture**. These sprites
sample large shared pages. Matching texture dimensions told me first that no flash
is ever drawn, and second that `ptbase2` and `pteff04` are drawn in frames 75105 —
those frames are the **intro movie**, whose YUV planes are 640×360 and whose target
is 1280×720. Both wrong, neither loud.
Detail, controls and reach:
[`docs/re/structures/ui-title-buildin-measured.md`](../re/structures/ui-title-buildin-measured.md).

View File

@@ -171,3 +171,4 @@ files, which is how the same ground got covered twice.
| [`structures/ui-tie-break-cost-at-settle.md`](structures/ui-tie-break-cost-at-settle.md) | What the unknown paint-order tie-break costs, in pixels | ✅ **decoded**, closing the open half of Q3: at the settled instant the tie-break costs **at most 1 px at Δ1**, on the **Japanese title only** (`ptlogo2`×`ptlogo_tm`, 5 px shared ink); **exactly 0 px on all five port screens**. The earlier 24-pair bound was a `rest()` count — and 10 of the title's 11 tied pairs are between `ptlogo_back2eff1``eff5`, five transient flashes that are **transparent** on the settled screen ([`ui-settle-time.md`](structures/ui-settle-time.md)). Live pairs at settle: entry 4 → **1**, entry 7 → **2**, the four loading bundles → **0**. ✅ Not a knife-edge — sweeping every keyframe time and midpoint, the count is **flat across the whole settle window**, and the loading bundles' tie is live only at t17t33. ✅ Controls: an overlapping *different*-key swap moves 25 310 / 268 698 / ~765 000 px on the entries reporting zero; zeros are explained by shared-ink counts (the `ptframe` pairs share **0 px** of ink). ⚠️ Entries 0/1/12/15 have **no live control** — their zeros rest on keyframe data, not a render. 🟡 Refutation attempt on the corpus's "24 pairs": **survives** as a rest-pose bound, 16/16 on entry 7. ❔ *Why* ties order as they do is still unknown — and now worth one pixel |
| [`structures/ui-record-loop-length.md`](structures/ui-record-loop-length.md) | Where a looping record's cycle restarts — and the `PRESS Ⓐ` plate's real period | ✅ **decoded**: a nested record is itself a RATC bundle and its header **`+0x08` is the loop length**; its keyframes need not fill it, and the slack is a hold at the final pose. Disc-wide over **1 781** timed nested records: 92.3 % declare exactly their last keyframe time, **7.7 % declare more**, and **0 declare less** — the falsifier (a cycle cannot restart before its own last pose) never fires. 🔴 **The plate's `ptbtn00f` is 105 units of ramp inside a 120-unit cycle, so it holds dark for 15 units** — the port was shipping **105**, and the answer is **120**. ✅ Falsification test against the running game, using a pacing factor measured *independently* on the focus ring (declared 120 → **2.177 s**, factor **1.0885**): to reach the corpus's measured 2.122.34 s, 105 units needs a factor of **1.2111.337** (🔴 excludes the ring's) while 120 needs **1.0601.170** (✅ contains it). Different elements, different bundles, separate runs — tied only by both declaring 120. ⚠️ Says where a cycle *ends*, not which records cycle. ❔ the **top-level** `+0x08` (300 on every `GP_TITLE` entry, elements ending at 244269) is a different question, untouched |
| [`structures/ui-focus-record-pulse-census.md`](structures/ui-focus-record-pulse-census.md) | Every focus record whose glow pulses, and where `rest()` puts it | ✅ **decoded**, disc-wide: **1 130** focus records, **2 664** timed elements, **210 with a varying alpha** — of which **202** have `rest()` == the **peak** (burns bright forever) and **8** land **mid-ramp**. By pak: `PILOTLOG` 116, `MOVIE_THEATER` 54, `HANGAR_ARSENAL` 30, `LEADERBOARD` 8, **`GP_TITLE` 2**. 🟡 Bounds rather than refutes the port's "34 in the export, 2 varying, nothing to fix" — correct, and correct *because* `GP_TITLE` has 2; the pathology sits in the screens a wider port needs next. 🔴 The 8 mid-ramp ones are the worse mode: `py_ranking_btn01f` swings 255→127→255 and `rest()` returns **244**, neither extreme, which looks entirely plausible and nothing reports it. ✅ Control: `ptbtn01f` is genuinely constant (255 throughout) and is **not** flagged; two hits verified keyframe by keyframe. ⚠️ A pulsing element has no resting pose — the question is malformed, not mis-answered; `pose_at(t)` inside the record's declared cycle ([`ui-record-loop-length.md`](structures/ui-record-loop-length.md)) is the only well-formed query. ⚠️ 210 is a **floor**: focus records are matched by the `Xf.rat` name rule, and varying scale/rotation/position is not counted |
| [`structures/ui-title-buildin-measured.md`](structures/ui-title-buildin-measured.md) | The title's build-in and the plate glow, read out of the guest's own draw stream | ✅ **measured** (Canary, `ARM=early` draw capture): the decoded *mechanism* is observed, not just its end state. **The five flashes fire in a six-frame window and are absent from all 155 other sampled frames**; `ptlogo_back2eff1` is drawn in exactly 2 frames at **t = 54.0** against a decoded peak of **t5456**, and `ptlogo1` first appears at **t = 42.2** against a decoded **t42** — with units/frame taken from the **glow's period alone**, a different element. The two holders (`ptlogo_back2eff`, `ptlogo_back2`) are continuous from frame 134. ✅ The glow's per-vertex colour alpha IS its fade alpha: **observed range 0…80 against a decoded peak of 80**, exact and unfitted; **period 51.158 presented frames** over 20 cycle starts; fitting the decoded ramp gives RMS **13.16** against **38.18 reversed** (2.9×), so the asymmetry is real and correctly directed. Structure: frame **107** is a **27-draw composite**, the settled title is 1011 draws naming no sprite — which is why arming at the title sees nothing. ⚠️ **`eff3` was never drawn and that is expected**: a 2-unit flash peak is **0.85 of a presented frame**, so catching one is a matter of phase — 🔴 a port drawing all five every time shows more sweep than the console does. 🔴 **Trap:** matching a bound texture's dimensions to a sprite fails both ways — it missed every flash *and* read the intro movie's 640×360 YUV planes as `ptbase2`. ❔ Run's frame rate not recorded, so nothing is stated in seconds; `eff5` vs `ptlogo_back2eff` not separated |

View File

@@ -1059,3 +1059,24 @@ The general trap: **an aggregate computed per-element is not a state of the
system.** Ask what instant a composite claims to depict, and check that every
element was asked the same question. See
[`structures/ui-settle-time.md`](structures/ui-settle-time.md).
## A 2D draw's identity is its geometry, not its bound texture
The title's sprites **sample large shared texture pages**, so the texture bound to
a draw identifies a page and not an element. Matching a bound texture's dimensions
against a decoded sprite's fails silently in both directions, and one pass here
did both at once:
* **false negative** — "none of the five flash sprites is ever drawn". They are
drawn; they simply never appear as their own texture.
* **false positive** — "`ptbase2` (640×360) and `pteff04` (1280×720) are drawn in
frames 75105". Those frames are the **intro movie**, whose YUV planes and
target happen to be 640×360 and 1280×720.
Re-run against the **quad's vertex rect** in design space and every element
appears where the disc says it should. Canary's own capture code already carries
this warning in a comment, and the corpus had already recorded that the settled
title binds only 1280×768 pages — both were there to be read first.
The general shape: **a coincidence of size is not an identification.** Before
matching on one attribute, ask what else in the frame shares it.

File diff suppressed because it is too large Load Diff

View File

@@ -73,6 +73,23 @@ This is a genuine test rather than a fit: the ring and the plate are different
elements in different bundles, measured in separate runs, and the only thing tying
them together is that both declare a 120-unit cycle.
## ✅ Measured in the running game
The glow's quad carries a per-vertex colour whose **alpha is the element's fade
alpha**, so the ramp can be read straight out of the guest's draw stream
([`ui-title-buildin-measured.md`](ui-title-buildin-measured.md)):
* **observed alpha range 0 … 80, against a decoded peak of 80** — exact, unfitted;
* **period 51.158 presented frames** over 20 consecutive cycle starts;
* the draw is **omitted entirely** while the glow is dark, which is the 11↔10
draw alternation visible in the settled title;
* fitting the decoded ramp gives RMS 13.16 alpha levels against **38.18 for the
same ramp reversed** — the asymmetry is real and in the decoded direction.
This does not re-measure the period in seconds (the run's frame rate was not
recorded), so the falsification below stands on the corpus's wall-clock timings
rather than on this run.
## What this replaces
* 🔴 **105 is wrong** and the port should stop shipping it. The number is **120**,

View File

@@ -26,6 +26,21 @@ vocabulary the file has.
`UiBuild::settle_window()` returns the interval, whose width is how much
confidence the midpoint deserves.
🔴 **Gather the times from the TOP-LEVEL elements only — not from nested leaf
records.** The port raised this after reproducing `[160, 236]` from its own
export, and it is worth stating because the implementation reads correctly either
way while only one is right. Including `GP_TITLE` build 4's `ptloop` leaves, whose
cycles run to 600 and 720, gives **`[269, 540]`** instead — a "settled instant"
that lies *past the end of every top-level element's timeline*, i.e. after the
screen has exited. Verified here: top-level `[160, 236]` width 76, with leaves
`[269, 540]` width 271. A leaf loops on its own clock and says nothing about when
the screen stops changing.
**Confirmed against the running game.** The mechanism this page decodes — five
transient flashes that fire and vanish — is observed in the guest's own draw
stream, with `ptlogo_back2eff1` drawn in exactly two frames at t = 54.0 against a
decoded peak of t5456: [`ui-title-buildin-measured.md`](ui-title-buildin-measured.md).
## The case that found it
`GP_TITLE` build 4. Seven elements share the light-arc band behind the logo:

View File

@@ -0,0 +1,134 @@
# The title's build-in, measured in the guest's own draw stream
**Classification: measured.** Xenia Canary, `ui_draw_capture.sh ARM=early
FRAMES=9000`, 2026-08-29. Evidence:
[`title-glow-alpha-per-frame.csv`](../data/title-glow-alpha-per-frame.csv) — the
per-frame series the numbers below come from. (The 7 MB raw draw log is a scratch
capture and is not committed.)
## What this tests
[`ui-settle-time.md`](ui-settle-time.md) decodes the title's light arc as **five
staggered two-frame flashes** (`ptlogo_back2eff1``eff5`) that fire around t5466
and are gone by t110, with `ptlogo_back2eff` and `ptlogo_back2` holding for the
rest of the screen. That decode was confirmed only against a **settled** frame:
posing at the settle time matched the console, which shows the *end state* is
right and says nothing about whether the flashes ever happen.
[`ui-record-loop-length.md`](ui-record-loop-length.md) decodes the `PRESS Ⓐ`
glow as a **120-unit cycle** containing a 105-unit ramp that peaks at **alpha 80**.
Both are predictions about the running game. This is the run.
## 🔴 First, the trap that nearly produced a false negative
The obvious instrument — match a bound texture's dimensions to a decoded sprite's
**does not work, and fails silently in both directions.**
A first pass reported that none of the five flashes are ever drawn. It also
reported `ptbase2` (640×360) and `pteff04` (1280×720) drawn during frames 75105.
Both were wrong. Those frames are the **intro movie**: four full-screen quads per
frame sampling a pair of 640×360 planes and a 1280×720 target — a video decode
whose plane sizes happen to collide with two sprite sizes. And the flashes were
missing because the title's sprites **sample large shared texture pages**, so the
bound texture identifies a page, not an element.
Canary's own capture code says so in a comment, and the corpus had already
recorded that the settled title's textures are all 1280×768. **The identity of a
2D draw here is its vertex geometry, not its texture.** Re-run against the quad
rects, everything appears.
## The structure of the title in the draw stream
| frames | draws/frame | what |
|---|---|---|
| 2105 | 35 | splashes, then the intro movie |
| **107** | **27** | 🔴 the title **composited**, once |
| 109167 | 6 → 14 | the build-in |
| 1681217 | 1011 | the settled title |
| 1218 | 45 | back to the attract loop |
The settled screen is 1011 draws that never name a sprite; the composite happens
in a single 27-draw frame. **A capture armed at the title therefore sees nothing**
which is why `ARM=early` exists.
## The flashes are real, and they are transient
Matching quads by design-space rect over frames 100260, and converting frames to
keyframe units with **2.346 units/frame — derived from the glow's period alone,
a different element**, taking the composite spike (frame 107) as t=0:
| element | drawn in frames | → t units | decoded |
|---|---|---|---|
| `ptlogo_back2eff1` | **130131** | **54.0 56.3** | flash, peak **t5456** ✅ |
| `ptlogo_back2eff2` | **133** | **61.0** | flash, peak t5860 |
| `ptlogo_back2eff3` | **never** | — | flash, peak t6264 ⚠️ |
| `ptlogo_back2eff4` | **133135** | 61.0 65.7 | flash, peak **t~64** ✅ |
| `ptlogo_back2eff` / `eff5` | 134260 | 63.3 … | 255 at t6466, **holds** ✅ |
| `ptlogo_back2` | 134260 | 63.3 … | 255 at t80, **holds** ✅ |
| `ptlogo1` | **125**148 | **42.2** | stops moving at **t42** ✅ |
**The flashes fire inside a six-frame window and are absent from every one of the
other 155 frames sampled.** The two holders are present continuously from frame
134 onward. That is the decoded mechanism, observed.
⚠️ **`eff3` was never drawn, and that is expected rather than a miss.** A flash's
peak is 2 keyframe units, and at 2.346 units per presented frame that is **0.85 of
a frame** — sub-frame. Whether any given flash is sampled at all depends on phase.
Four of five were caught here; a second run would likely catch a different four.
🔴 **A port that draws all five every time will show more of this sweep than the
console does.**
## The glow's ramp, read out of the guest
The plate glow's quad carries a **per-vertex colour whose alpha is the element's
fade alpha**, so the ramp can be read directly rather than inferred from pixels.
* **Observed alpha range: 0 … 80. Decoded peak: 80.** Exact, and not fitted.
* **Period: 51.158 presented frames**, from the first to the last of **20
consecutive cycle starts** (individual periods 4953).
* The draw is **omitted entirely** while the glow is dark — which is what the
11↔10 draw alternation in the settled title is.
Fitting the decoded 8-keyframe ramp to the 972 measured frames, with **one
disclosed free parameter** (a constant phase offset):
| curve | RMS residual, alpha levels |
|---|---|
| **the decoded ramp**, phase +2.25 units (= **+0.96 frames**, sub-frame) | **13.16** |
| the decoded ramp, no phase fit | 13.68 |
| a symmetric triangle of the same period and peak | 15.73 |
| flat at the mean | 31.13 |
| 🔴 **the decoded ramp REVERSED** | **38.18** |
The reversal control is the one that matters: if the shape carried no
information, forwards and backwards would fit equally. They differ by **2.9×**, so
the measured ramp has the decoded ramp's asymmetry — fast rise, slow fall — in the
decoded direction.
⚠️ The +0.96-frame phase is the expected bias, not a correction: a cycle start is
detected at the first frame with a non-zero alpha, and the decoded curve leaves
zero part-way through a frame.
## Reach and what is not settled
⚠️ **One run, one machine.** 2.346 units per presented frame is *this run's*
pacing, not a property of the game. The internal ratios are what transfer.
**The absolute frame rate of this run was not measured** — Canary logged no fps
and the log has no timestamps — so nothing here is stated in seconds. It did not
need to be: every comparison above is a ratio of measured quantities.
**`ptlogo_tm` was never matched.** At 37×17 it is below the rect tolerance used;
absence here is not evidence.
⚠️ **`eff5` and `ptlogo_back2eff` are the same rect at the same position and were
not separated.** Distinguishing them needs the per-vertex alpha, which the decode
says differs — not attempted.
## Reproducing
```bash
FRAMES=9000 MAXDRAWS=5000000 ARM=early tools/re-capture/ui_draw_capture.sh 900 /tmp/uicap
python3 tools/re-capture/buildin_timeline.py /tmp/uicap/xenia_re_ui_draws_01.log
```

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python3
"""When does each title sprite get DRAWN, frame by frame, in the real game?
Reads a `xenia_re_ui_draws_NN.log` armed early (so the window contains the frames
in which the screen is BUILT, not just its steady state) and reports, per texture
size, the first and last frame it is bound in.
The point is a falsifiable prediction. `docs/re/structures/ui-settle-time.md`
decodes `ptlogo_back2eff1`..`eff5` as five staggered two-frame flashes that sweep
across the logo once and are extinguished by keyframe t110, while
`ptlogo_back2eff` and `ptlogo_back2` hold for the rest of the screen. Four of the
five have UNIQUE decoded dimensions, so the log can confirm or refute that
directly:
eff1 167x126 eff2 258x203 eff3 408x203 eff4 749x203
If they appear in a short contiguous run of early frames and never again, the
decode is right. If they are bound every frame, or never, it is wrong.
buildin_timeline.py <log> [--dims WxH,...]
"""
import re, sys, collections
# GP_TITLE build 4, from `sylpheed-cli`/`sprite_dims`. Two share 1133x280, which
# is why the capture also records per-vertex colour alpha.
KNOWN = {
(167,126): "ptlogo_back2eff1 FLASH t54-58",
(258,203): "ptlogo_back2eff2 FLASH t58-62",
(408,203): "ptlogo_back2eff3 FLASH t62-66",
(749,203): "ptlogo_back2eff4 FLASH t~64",
(1133,280): "ptlogo_back2eff / eff5 (AMBIGUOUS: same size)",
(1118,262): "ptlogo_back2 holds t80-243",
(919,113): "ptlogo1 holds",
(992,104): "ptlogo2 holds",
(1280,720): "pteff04 full-screen",
(640,360): "ptbase2",
(694,20): "ptcopyright",
(399,180): "pteff03 / pteff03a (sweeps)",
(517,131): "ptlogoall_eff",
(235,180): "ptlogoall_eff2",
(640,319): "pteff01",
(37,17): "ptlogo_tm",
}
def main():
path = sys.argv[1]
frame = None
seen = collections.defaultdict(list) # dims -> [frames]
per_frame = collections.Counter()
frames = []
for line in open(path, errors="replace"):
m = re.match(r"--- frame (\d+) ---", line)
if m:
frame = int(m.group(1)); frames.append(frame); continue
if frame is None:
continue
for w, h in re.findall(r"tex\[base=0x[0-9A-F]+ (\d+)x(\d+) fmt=\d+\]", line):
seen[(int(w), int(h))].append(frame)
if line.startswith(("0","1","2","3","4","5","6","7","8","9")) or re.match(r"^\s*\d+ prim=", line):
per_frame[frame] += 1
if not frames:
print("no frames in the log"); return
lo, hi = min(frames), max(frames)
print(f"frames {lo}..{hi} ({len(set(frames))} distinct) draws {sum(per_frame.values())}\n")
rows = []
for dims, fl in seen.items():
s = sorted(set(fl))
rows.append((s[0], dims, s[-1], len(s), len(fl)))
rows.sort()
print(f" {'first':>7} {'last':>7} {'frames':>7} {'draws':>7} {'size':>10} what")
for first, dims, last, nf, nd in rows:
name = KNOWN.get(dims, "")
span = last - first
tag = " ⟵ TRANSIENT" if nf <= 12 and span <= 20 else (" (every frame)" if nf > 0.8*len(set(frames)) else "")
print(f" {first:>7} {last:>7} {nf:>7} {nd:>7} {dims[0]:>4}x{dims[1]:<4} {name}{tag}")
if __name__ == "__main__":
main()