Studio does not write a frame bitmap directly to disk. Its ARM9 serialiser receives regional editor state, appends pointers to prepared blocks in a queue, then leaves packaging and storage to later functions. The four entries below are loaded ARM9 addresses in the studied builds, not positions in a PPM or offsets in the ROM file. Function names used here describe observed roles and are not recovered Nintendo symbols. [1]
| Build | Serialiser entry | Instruction bytes | Main state | Metadata | Thumbnail |
|---|---|---|---|---|---|
| JP V1 | 0200704C | 972 | 02114240 | 021143A0 | 02114760 |
| JP V2 | 02007534 | 1,232 | 02133D60 | 02133EC0 | 02134280 |
| US V2 | 02007664 | 1,232 | 02173C60 | 02173D80 | 02174140 |
| EU V2 | 02007530 | 1,232 | 021A8EC0 | 021A8FE0 | 021A93A0 |
Follow the source queue
The append queue holds up to 32 source pointers and 32 corresponding lengths. Pointer slots begin at queue offset +0, length slots at +0x80, and count, cursor and pending fields at +0x100, +0x104 and +0x108. Reset state sits at +0x10C; two mode bytes follow. Appending records a pointer, so changing a source buffer before materialisation can change the emitted bytes. A zero-length append fails. The 1 MiB staging allocation accepts at most 0xFFF70 body bytes, reserving 0x90 for the later package output. [1]
For an ordinary note, let n be the zero-based final frame index. The table byte count is ((n+1) × 4) & 0xffff; encoded-frame bytes are rounded up to four; sound-effect flag storage is (n+4) & ~3. The queue emits the 16-byte file header, 0x90-byte metadata, 0x600-byte thumbnail, eight-byte animation header, frame table, encoded frames, flag bytes, 0x20-byte audio header, then BGM, SE1, SE2 and SE3. A three-frame note with five raw frame bytes and track lengths 3, 0, 1 and 4 yields 1,770 body bytes in V2 and 1,768 in JP V1, before the package trailer. [1]
Header values made by this path
- The file-header word at
+0x04is eight plus table bytes plus padded frame bytes. It excludes the flag and audio sections. - The file-header word at
+0x08is emitted sound bytes in V2, but JP V1 leaves it zero in this path. - The halfword at
+0x0Cstoresn, notn+1. The halfword at+0x0Eis0x24in all four observed serialisers. - The animation header stores the table length at
+0, zero halfwords at+2and+4, and animation flags at+6. [1]
Why a regional state offset matters
The final-frame halfword is main-state +0x1C in JP V1 and +0x22 in the three V2 builds. Encoded frame byte count is at +0x3C, +0x38, +0x3C and +0x40 respectively. The encoded-data pointer is at +0x4C, +0x4C, +0x34 and +0x3C. A tool that applies the US offset to EU V2 reads a different value before any PPM field is written. The animation, file and audio header buffers are consistently at main-state +0x60, +0xA0 and +0xE0. [1]
Flags and modes are independent
JP V1 constructs animation flags from conditional bits 0x01 and 0x02. V2 starts at 0x40 and can add bits through 0x20. The US V2 trace ties bit 0x01 to main byte +2 being zero, bit 0x04 to byte +0xF being nonzero, and bits 0x10/0x20 to two indexed provider results being zero. Two other bits depend on predicates. These are branch conditions; their full user-facing meanings are not all established. The metadata lock halfword is a separate field. [1]
Modes 7 and 8 bypass ordinary metadata updates in different ways. V2 mode 9 suppresses metadata and thumbnail regeneration and resumes under a working mode 4; JP V1 has no mapped mode-9 branch. Mode 2 has a service-dependent choice of metadata source. Treat these as distinct original save paths, not interchangeable “write PPM” options. [1]
Audio and edge conditions
V2 rounds each emitted audio track length to an even byte count; JP V1 uses the recorded lengths. Neither the audio rounding bytes nor frame-alignment bytes are explicitly filled with zero by this function; they come from the existing backing buffer. The audio header's speed bytes are at +0x10 and +0x11, while +0x12..+0x1F is not initialised here. Artificial values such as n=0x3FFF expose the halfword truncation in the frame-table length and can make a queue append fail without a separate serialiser guard. These are instruction-level observations, not known UI outputs. [1]
To validate a new implementation, compare queued source order, lengths and final bytes separately. A matching rendered animation is insufficient: mode-dependent metadata, padding and version fields can differ while playback looks the same. Packaging and acceptance continue after this body is built. [1][2]
