re: RATC +0x10 is a u16 pair too, and no bit is a clean predicate

The u16 at +0x10 is 0 in all 2985 bundles; the content is a 16-bit flag word at
+0x12 with 83 distinct values. Reading it as a u32 inflates the field and hides
that the header is built from u16 pairs -- the same shape +0x0c turned out to
have. All 16 bits are used, from 1.4% to 91.5%.

Cross-tabulated every bit against four properties measurable from the bundle:
multi-element, animated, window-starts-at-zero, 30fps. No bit is close to a
clean predicate. The strongest is bit 10 against window-at-zero, 0.79 vs 0.21 --
a real association but not a rule, and exactly the kind of moderate split that
invites over-reading.

Bit meanings stay open, but four candidate readings are excluded rather than
untried and the field is correctly sized. Every property visible in the file has
now been tried, so assigning meanings likely needs the game observed with
individual bundles loaded.
This commit is contained in:
Sylpheed RE agent
2026-08-26 06:59:08 +00:00
parent 92526469d9
commit 7460d71e6e
2 changed files with 39 additions and 3 deletions

View File

@@ -510,7 +510,7 @@ composable bundles with a real declaration table
| `+0x04` | **3** — `0x3C0000` ×2 843, `0x1E0000` ×12, `0x3C0001` ×4 | 🟡 **frame rate in 16.16**: `0x3C0000` is exactly `60.0`, `0x1E0000` exactly `30.0` |
| `+0x08` | 22 — 30, 1200, 120, 60, … | ✅ the **animation length**, in the same unit as a keyframe's `time` — see below |
| `+0x0c` | 179 as a u32 — but it is **two u16 fields**, see below | ✅ structure, 🟡 meaning |
| `+0x10` | 83 — `0x9400`, `0x9200`, `0x8212`, … | ❔ flags; bit 15 set on **91 %** of all bundles |
| `+0x10` | zero `u16` then a 16-bit flag word at `+0x12` | ✅ structure, ❔ bit meanings |
| `+0x18` | **2** — `1280` ×2 829 | ✅ **design width** |
| `+0x1c` | **3** — `720` ×2 823 | ✅ **design height** |
@@ -661,4 +661,38 @@ shape of a playback range or a loop region over the whole animation. 🟡 What i
*is* remains open; what is now excluded is that it is a cached extent of the
keyframes.
`+0x10` is untouched by this and remains ❔.
`+0x10` is untouched by this and remains ❔.
## ✅ `+0x10` is also a `u16` pair — and no bit is a clean predicate
**2026-08-26.** Like `+0x0c`, this is not one 32-bit value. The `u16` at
`+0x10` is **0 in all 2 985 bundles**; the whole content is a 16-bit flag word
at **`+0x12`** with 83 distinct values (`0x9400`, `0x9200`, `0x8000`, `0x8212`,
…). Reading it as a `u32` inflates the field and hides that the header is built
from `u16` pairs — the same shape `+0x0c` turned out to have.
Every one of the 16 bits is used, with shares from 1.4 % (bit 2) to 91.5 %
(bit 15).
**❌ What the bits are not.** I cross-tabulated all 16 bits against four
properties measurable from the bundle itself — whether it has more than one
declared element, whether it is animated at all, whether its `+0x0c` window
starts at 0, and whether it runs at 30 fps rather than 60:
| bit | set | multi-element | animated | window at 0 | 30 fps |
|---|---|---|---|---|---|
| 15 | 91.5 % | 0.57 / 0.81 | 0.95 / 0.86 | 0.32 / 0.69 | 0.05 / 0.00 |
| 12 | 67.4 % | 0.65 / 0.48 | 0.98 / 0.86 | 0.35 / 0.35 | 0.00 / 0.14 |
| 10 | 23.5 % | 0.36 / 0.67 | 0.96 / 0.94 | **0.79 / 0.21** | 0.00 / 0.06 |
| 0 | 8.6 % | 0.91 / 0.56 | 1.00 / 0.94 | 0.08 / 0.37 | 0.00 / 0.05 |
Each cell is share-when-set / share-when-clear; a clean predicate would read
1.00 / 0.00. **Nothing comes close.** The strongest is bit 10 against
"window starts at 0" at 0.79 / 0.21 — a real association but nowhere near a
rule, and the kind of moderate split that would be easy to over-read.
❔ So the bit meanings stay open, but four candidate readings are now excluded
rather than untried, and the field is at least correctly *sized*. Assigning
meanings almost certainly needs the game observed with individual bundles
loaded, not more static correlation — every property visible in the file has now
been tried.