Overview

Implementation: crates/art.

Where the data lives

Source Location
Vahn art records (RAM) 0x80160EFC (first record) onwards
Noa art records (RAM) 0x80176998 (first record) onwards
Gala art records (RAM) 0x8018BA54 (first record) onwards
Learned Art Constants (RAM) Vahn 0x8008488D, Noa 0x80084CA1, Gala 0x8008506C
On-disc source PROT entry 0x05C4
Miracle Art trigger entries (RAM 801F segment) Vahn's Craze 0x64F4, Noa's Ark 0x6504, Biron Rage 0x6514
Miracle Art trigger entries (PROT 0x05C4 area) Vahn's Craze 0x0CDC, Noa's Ark 0x0CEC, Biron Rage 0x0CFC

Confidence: Inferred - citation chain comes from external RE work cross-referenced with Meth962's earlier observations; a watchpoint trace pinning the runtime read sites is still pending.

Action Constants

Every entry in the battle action queue is one of these 0x00–0x32 values:

Byte Meaning
0x00 Nothing
0x01 Item
0x02 Magic
0x03 Attack
0x04 Spirit
0x05 Escape
0x06 unidentified
0x07 Faint Animation 1
0x08 Faint Animation 2
0x09 unidentified
0x0A Item / Magic Animation
0x0B Block Animation
0x0C Left
0x0D Right
0x0E Down
0x0F Up
0x10 Spirit Animation
0x11–0x18 Empty Slots 1–8 (placeholder, never appears in static data)
0x19 Regular Art Starter
0x1A Special Art Starter
0x1B–0x32 Per-character arts (the constant is shared across characters but resolves to a different art per character - see crates/art/src/tables.rs)

The first 4 directional bytes of a Miracle Art's replacement string are stored on disc with the high nibble's MSB set (0x8C / 0x8D / 0x8E / 0x8F); the runtime ANDs with 0x7F when copying into the queue. legaia_art::miracle::unmask_replacement_byte matches that.

Art record layout

The layout is schema-then-walk: each record begins with a fixed prefix (commands, action constant, anim index), and the remainder is a sequence of variable-width fields whose presence depends on the art. The researcher captured field positions but did not pin every byte - this page documents the schema; crates/art ships a strict parser for the prefix and surfaces the unparsed tail for downstream tooling.

Fixed prefix

+0x00  u8[]   command sequence - values 1=L, 2=R, 3=D, 4=U; terminated by 0x00
       u8     action constant (0x1B..=0x32)
       u8     anim_index (primary)
       u8[5]  anim_extra (reserved; usually 0; some Hyper Arts chain multiple records)

Variable fields (positions documented, exact byte offsets per art-specific)

Field Encoding
Art Name UTF-like string. Populated for Super Arts, Miracle Art finishers, and some Hyper Arts; absent for regular arts (the runtime falls back to a per-character name table).
Art Power (×4) Each byte is a damage multiplier - see Power encoding below.
Damage Timing (×4) One byte per Art Power byte; the animation frame at which that hit fires.
Special Effect Cues (×2) Each cue occupies 2 words: half-word effect_id, then 3 half-words XYZ. Active iff any field is non-zero.
Hit Effect Cues (×4) Each cue is a 32-bit word: high half = timing in frames, low half = constant (0x1A = sound effect, 0x4C = hit effect, …).
Identifier Byte. Some values trigger special animations (0x67 in Heaven's Drop = Thunderbolt).
Anim Speed Byte. Lower = slower playback, higher = faster.
Effect on Enemy Byte. 1 = Burned, 2 = Shocked, others reserved.
Repeat Frames 3 bytes: count, start_frame, end_frame. Replays a frame range; for some arts also repeats the damage from power bytes that fall in the range (Super Tempest's 4 power bytes → 8 actual hits).
Background Byte. 0 = regular, 2 = black (Super Arts and Tornado Flame Hyper Art).
Runtime Address Word. Written by the runtime after the art is used once in battle - always None in static data.

Power encoding

Byte range Defense target Multiplier sequence Notes
0x16–0x1A UDF (Upper Defense Factor) 12, 18, 20, 22, 28 Standard UDF range
0x1B–0x1F LDF (Lower Defense Factor) 12, 18, 20, 22, 28 Standard LDF range
0x0C–0x10 UDF (alt range) 12, 18, 20, 22, 28 UDF-target hits miss short enemies
0x11–0x15 LDF (alt range) 12, 18, 20, 22, 28 LDF-target hits miss floating enemies
any other - - No damage

Concretely: 0x1D = LDF × 20, 0x19 = UDF × 22, 0x1F = LDF × 28, 0x1A = UDF × 28.

Learned Art Constant

A separate per-character byte (0x8008488D Vahn, 0x80084CA1 Noa, 0x8008506C Gala) tracks the highest learned art slot. Slot indices 0..=0x10 resolve to action constants through a per-character indirection table - and that table has holes: Noa skips slots 0x02 and 0x03 because her Hurricane Kick covers all three on-disc levels through a single learned slot.

Crate API: legaia_art::learned_art_action(character, slot) returns the action constant for a slot, or None for holes / out-of-range.

Slot Vahn Noa Gala
0x00 Vahn's Craze (0x1B) Noa's Ark (0x1B) Biron Rage (0x1B)
0x01 Burning Flare (0x1C) Hurricane Kick (0x1C) Explosive Fist (0x1C)
0x02 Fire Blow (0x1D) - Lightning Storm (0x1D)
0x03 Tornado Flame (0x1E) - Thunder Punch (0x1E)
0x04 Cyclone (0x1F) Vulture Blade (0x1F) Bull Horns (0x1F)
0x05 Hurricane (0x20) Frost Breath (0x20) Electro Thrash (0x20)
0x06 PK Combo (0x21) Tempest Break (0x21) Neo Raising (0x21)
0x07 Spin Combo (0x22) Rushing Gale (0x22) Black Rain (0x22)
0x08 Pyro Pummel (0x23) Tough Love (0x23) Side Kick (0x23)
0x09 Cross Kick (0x24) Swan Driver (0x24) Head-Splitter (0x24)
0x0A Power Punch (0x25) Bird Step (0x25) Guillotine (0x25)
0x0B Slash Kick (0x26) Dolphin Attack (0x26) Back Punch (0x26)
0x0C Somersault (0x27) Mirage Lancer (0x27) Ironhead (0x27)
0x0D Charging Scorch (0x28) Blizzard Bash (0x28) Battering Ram (0x28)
0x0E Hyper Elbow (0x29) Sonic Javelin (0x29) Flying Knee Attack (0x29)
0x0F - Acrobatic Blitz (0x2A) -
0x10 - Lizard Tail (0x2B) -

Vahn and Gala stop at 0x0E (15 learned arts each). Noa extends to 0x10 (15 learned arts but spread across 17 slot positions because of the two Hurricane Kick holes).

Art Anim Data

Selects the animation record played when the art fires. The Art Record's anim_index byte at offset +16 indexes a per-character animation table. Slot 0 is always Spirit; slot 3 is always Art Starter. Some slots are holes (e.g. Vahn has no slot 0x0C, Gala has no slot 0x09).

Crate API: legaia_art::art_anim_name(character, anim_index).

Anim Vahn Noa Gala
0x00 Spirit Spirit Spirit
0x01 Power Punch Tempest Break Bull Horns
0x02 Slash Kick Tough Love Head-Splitter
0x03 Art Starter Art Starter Art Starter
0x04 Tornado Flame Hurricane Kick 1 Lightning Storm
0x05 Hurricane Hurricane Kick 2 Back Punch
0x06 Charging Scorch Rushing Gale Ironhead
0x07 PK Combo Swan Driver Battering Ram
0x08 Fire Blow Frost Breath Flying Knee Attack
0x09 Somersault Lizard Tail -
0x0A Cyclone Jurassic Blow 2 Thunder Punch
0x0B Hyper Elbow Bird Step Guillotine
0x0C - Dolphin Attack Explosive Fist
0x0D Burning Flare Vulture Blade Black Rain
0x0E Spin Combo Mirage Lancer -
0x0F Pyro Pummel Blizzard Bash -
0x10 Cross Kick Sonic Javelin Side Kick
0x11 Acrobatic Blitz Electro Thrash -
0x12 - - Neo Raising

Most art records reference exactly one anim slot; a handful (e.g. Hurricane Kick on Noa) use the anim_extra reserved bytes at +17 to chain into a continuation slot for multi-stage animations.

Miracle Arts

Each character has one Miracle Art. When the player enters the exact command sequence for that art, the runtime clears the entire action queue and writes the art's replacement string instead.

Character Art RAM PROT Command sequence
Vahn Vahn's Craze 0x64F4 0x0CDC R D L U L U R D L
Noa Noa's Ark 0x6504 0x0CEC L U R D U L U D R
Gala Biron Rage 0x6514 0x0CFC R R D U D U D L L

Each replacement string follows the shape [L, R, D, U, SpecialStarter, art1, art2, ...] where the four leading directionals are the on-disc MSB-set bytes (0x8C/0x8D/0x8E/0x8F), masked to 0x0C/0x0D/0x0E/0x0F at copy time. The full table is in crates/art/src/miracle.rs.

Super Arts

Super Arts are not invoked by direct command-string match. Instead, after each art finishes, the runtime walks the full action queue and looks for a registered Find pattern. If a Find pattern matches the tail of the queue and all participating arts paid AP, the matched bytes are replaced by a Replace tail that ends with the Super Art's finisher action constant.

Example - Vahn's Tri-Somersault (0x2B):

Find:    19 27 0F 19 1F 0E 19 27
         (Starter Somersault Up Starter Cyclone Down Starter Somersault)
Replace: 19 27 0F 19 1F 0E 1A 2B 2B 2B
         (… SpecialStarter, Tri-Somersault × 3 hits)

Triggers:

  1. The last art of the Find string must be the last action in the queue.
  2. All arts in the Find string must be non-NEW (their AP cost is paid).
  3. Super Arts themselves do not consume AP - the chain arts pay it.

Full per-character tables (5 entries each for Vahn / Noa / Gala = 15 total) are in crates/art/src/super_art.rs.

Arts-name table (DAT_80075EC4)

The display names + AP costs of every Tactical Art live in a static table in SCUS_942.54 at DAT_80075EC4. It's the table the MES interpreter's 0xC5 substitution code reads (see MES bytecode encoding) - the 0xC5 operand XX keys it as (character = XX>>6, art index = XX&0x3F).

The expander (FUN_80036514) scans 20-byte (0x14) records sorted by character, matching (record[+0], record[+1]) against the key, and returns the +0xC name pointer ((&PTR_DAT_80075ED0)[match_index * 5]).

Offset Type Field
+0u8character: 0 Vahn, 1 Noa, 2 Gala
+1u8art display index within the character
+2u8AP cost
+3u8padding
+4u16round value (≈ power/score; exact meaning unconfirmed)
+6u16zero
+8u32pointer to the command-input display string (MES arrow-glyph sequence; its first byte is the input count)
+0xCu32pointer to the name string
+0x10u32aux pointer (second string)

A (99, 99) record named "End" terminates the table. Each character's index 0 entry is the Miracle Art (AP byte 99; the name string opens with a 0xCE 0x09 character-name-substitution control, e.g. "&'s Ark" / Gala's "Biron Rage").

The AP costs are byte-exact against the curated gamedata arts table (every matched art agrees), which makes this the on-disc provenance for that table's ap column + the canonical art display order.

Command-glyph string (+8)

The +8 pointer is the command-input display string - the arrow sequence shown in the arts menu, and an independent on-disc source for each art's directional command (the PROT 0x05C4 art-record command bytes are a best-effort parse pending a watchpoint). Encoding: [count u8] then count two-byte glyph codes. A one-off 0xFF XX marker separates the sequence (0xFF06 for regular arts, 0xFF09 for Miracle arts) and is not a direction. The arrow glyphs map to physical d-pad directions:

Glyph Direction dir code
0x81A9Left1
0x81A8Right2
0x81ABDown3
0x81AAUp4

The string stores the physical direction; the logical action (Arms / Ra-Seru) depends on the character's handedness (Noa's Arms / Ra-Seru are swapped). The codes match the Left=1 / Right=2 / Down=3 / Up=4 encoding the PROT records use. Cross-checking against gamedata surfaces at least one walkthrough error (Vahn's Hyper Elbow is L R L on disc, not Arms / Ra-Seru / High). Decoded by legaia_art::arts_table::parse_from_scus; dump it with art arts-table.

Validation oracle

Because the glyph string is byte-exact ground truth, it serves as the validation oracle for the two derived command sources:

  • The best-effort PROT 0x05C4 parser (legaia_art::parse_record). legaia_art::ArtsOracle::by_command(character, &commands) resolves a decoded command sequence back to a named art; the disc-gated contract test crates/art/tests/arts_table_real.rs runs every art's canonical record bytes through parse_record and asserts the decode round-trips through the oracle. This pins the parser's 1=L,2=R,3=D,4=U command-byte decode against the executable without needing the (still-unpinned) full record stride.
  • The curated legaia-gamedata arts.toml ap + directions columns. The disc-gated test crates/gamedata/tests/arts_scus_oracle.rs matches each curated art to its SCUS row by name and asserts AP + directions agree, with a small explicit allowlist for documented walkthrough errors (currently only Hyper Elbow). A new undocumented divergence fails the test.

See also