At a glance

In the game
The moment NEW GAME is confirmed - the party's opening stats, names, bag and gold
Where
Table at SCUS VA 0x80078C4C; bag and world-state seeds are instruction immediates in FUN_80034A6C / FUN_800560B4
Size
4 records × 26 bytes (8 × u16 stats + 10-byte name)
Expands into
The live save records at 0x80084708 + n × 0x414
Parser
legaia_game_tables::new_game (StartingParty, StartingInventory, new_game_seed_words, seed_live_records); CLI asset new-game <SCUS> [--json]
Confidence
Confirmed - byte-search of the executable cross-validated against a live town01 save state; seed stores read off the disassembly
Used by
Boot, Level-up, Randomizer

What a New Game is made of

Confirming NEW GAME launches the field overlay with a fresh game state built from four sources. Only the first is a table.

1
Party template
Four 26-byte rows - stats + name for Vahn, Noa, Gala, Terra - expanded into the live records.
StartingParty
2
Starting bag
Code-built: one (id, count) slot, Healing Leaf ×5, written into the owned-item array.
StartingInventory
3
World-state seed
Fixed immediates into the save context: 500 gold, party-slot bytes, a cleared story-flag bank.
new_game_seed_words
4
Opening scene
The prologue opdeene loads first and the opening chain plays through to town01 (Rim Elm), the first interactive scene and the executable's default map name. A developer initmap.txt override exists but is gated off in retail.
engine-core::new_game

At a true New Game only Vahn has joined; the other rows are the templates used as each character is introduced.

Template row (26 B) hp mp agl atkudf ldf spd int name Code literals level 1, cap 100, bag, 500 gold Seed routine 4-iteration loop Live record ×4 0x414 B each, stats + level Save context bag, gold, party, flags
Template stats and code literals meet in the seed routine, which writes the four live records and the save context.

Record layout

One row per character, 26 bytes, no header: eight little-endian u16 stats and a NUL-padded name.

+0hp_maxu16
+2mp_maxu16
+4aglu16
+6atku16
+8udfu16
+10ldfu16
+12spdu16
+14intelu16
+16display name (NUL-padded)10 B
FieldMeaning
hp_max / mp_maxalso the starting HP / MP
agllands in the live agility cells (+0x110 current, +0x122 max) - not the cap cells, which are a literal 100
udf / ldfupper (physical) / lower (magical) defence
spdturn-order initiative seed
display namethe default name the name-entry screen later pre-fills

Decoded values

SlotNameHPMPAGLATKuDEFlDEFSPDINT
0Vahn18020100241612199
1Noa15010120211311303
2Gala21040803043301520
3Terra4002002004520174525

Starting bag and world state (code literals)

The new-game data-init routine holds the save-context base in a register for its whole body and writes fixed values at offsets from it. The bag is one (id, count) pair in the owned-item array at SC + 0x1818 - a single ordered list shared by every item category, which the menu merely filters into Items / Goods / Key tabs. A seeded slot can therefore hold any id, which is how the randomizer's starting-bag toggles seed keys and accessories.

SC offsetWidthFieldValue
+0x1818 / +0x18191 + 1bag slot 0: item id, count0x77 Healing Leaf, 5
+0x4541party byte3
+0x457 / +0x4581party bytes0
+0x459 .. +0x45B1party-slot bytes1 / 2 / 3
+0x45C4gold0x1F4 = 500
+0x460 / +0x464 / +0x470 / +0x4784counters0
+0x590 .. +0x59C4words0x44 / 0x21 / 0x10 / 0x48
+0x1618 .. +0x18170x200story flagscleared after the bag write - the Door-of-Wind warp mask at +0x161C is inside, so any warp preset is applied after the clear

Widths come from the store opcodes (sb / sh / sw), not from the decompiler's globals naming. StartingInventory decodes the bag by replaying the routine's register-sourced stores, so it reads back either the retail byte pair or the randomizer's packed halfword (id | count << 8).

Details: the seed instruction shapes
OpTop 16 bitsMeaning
addiu $v0, $zero, imm0x2402load constant into the source register
sb $v0, off($s0)0xA202byte store at SC + off
sh $v0, off($s0)0xA602halfword store at SC + off

The bag write is four instructions at 0x80034B0C. A six-instruction loop after it zeroes the 512 bytes below the bag, but both callers of the routine memset the whole SC[0..0x1A18) first, so that loop is redundant - the reclaimable region is 10 instructions (40 bytes) from 0x80034B04. Three further absolute stores (0x80073EF4 = 0xE40, 0x80073EF8 = 0x2DC0, 0x80073EFC = 0) are not save-context cells.

The entry seat

Three further stores in the same routine are absolute rather than save-context cells: 0x80073EF4 = 0xE40, 0x80073EF8 = 0x2DC0, 0x80073EFC = 0. They are the field initialiser's destination-entry operand - the same words a door's scene change or the world-map arrival writes. The first scene a New Game loads is the prologue opdeene, so the seed seats the player there at world (0xE40, 0x2DC0), tile (28, 91); opdeene's entry script reads the seat once to pick its region (and view window) before the opening record moves the player on.

Starting level and experience

The template expander also initialises each live record's progression cells. The shown level is read from +0x130 directly, never re-derived from experience: a record with level-10 experience but +0x130 == 1 shows LV 1.

Record offsetFieldVanilla seed
+0x0 (u32)cumulative experience0
+0x4 (u32)next-level threshold (the status screen's “next”)Vahn/Terra 121, Noa 102, Gala 140 - the per-slot level-up correction, not a per-character curve
+0x130 (u8)displayed level; the applier adds 1 per level-up1
+0x131 (u8)second per-character byte - seeded, then read by nothing on the disc (see below)1

+0x131 is write-only. The seed's li $v0, 0x1 at 0x800561C4 feeds sb $v0, 0x6F9($s0) (+0x131) at 0x800561C8 and sb $v0, 0x6F8($s0) (+0x130) at 0x800561CC, and 0x800561C8 is the only instruction anywhere on the disc that touches the byte: sweeping SCUS_942.54 plus every based overlay image for a lb/lbu/sb at displacement 0x131, or at the four slots' 0x6F9/0xB0D/0xF21/0x1335 off the 0x80084140 game-state block, returns that store alone, and no wider access covers it. The "magic-rank candidate" reading is therefore retired - the magic-rank counter is capture-pinned at record +0x9C.

Details: how the starting-level option retargets the seed

The routine seeds +0x4 by storing one register literal and reloading it twice for the per-character values. The patch controls that register and reclaims the reload sites, seeding Vahn, Noa and Gala at level N with same-size edits:

  • Level - a packed addiu $v0, (1 << 8) | N; sh $v0, 0x6F8($s0); nop at 0x800561C4..CC sets +0x130 = N and keeps +0x131 = 1.
  • Experience - one addiu $t0 preload at 0x800560FC (midpoint of reach(N)..reach(N+1)) feeds three sw $t0 stores at 0x80056100 / 08 / 18 (Vahn 0x5C8, Noa 0x9DC, Gala 0xDF0).
  • Next threshold - the literal at 0x800560F0 loads reach(N+1); all three stores take it, and the applier re-applies the per-slot correction on the first level-up.
  • Stats - each growth slot's template stats are recomputed to level N.

Seeding only slot 0 would leave Noa at 0 experience and Gala with a stale threshold of 140 (a spurious early level-up); seeding all three keeps the roster coherent.

In the port

legaia_game_tables::new_game reads the table and the seed routine's literals from the user's own SCUS_942.54 (same address-to-file-offset resolver as the item-name table): StartingParty for the rows, StartingInventory for the bag (it replays the routine's stores, so it reads both the retail byte pair and the randomizer's packed halfword), new_game_seed_words for the world-state cells and seed_live_records for the expander's record stores. CLI: asset new-game <SCUS> [--json].

Retail's seed loop fills all four live records, so a member who joins later (field op 0x3C) already has a level-1 record. The engine's New Game roster is Vahn alone and keeps the template beside it; when a party list names a slot the roster has no record for, World::set_active_party seeds that slot from the template, so a joining member fights with real HP rather than an empty record counted alive.

How we know

The table base comes from a byte-search of the executable for Vahn's stat run and name, cross-checked field by field against the live record in an early town01 save state; the bag, gold and seat literals are read off the seed routines' disassembly.

Provenance: the routines involved
FunctionAddressWhat it proves
Template expanderFUN_800560B4The 4-iteration loop copying template fields into live records; literal 100 into the cap cells; level / threshold seeds
New-game data initFUN_80034A6CBag pair, gold and party bytes as immediates; story-flag clear
CallersFUN_8001DCF8 (new-game branch, flag 0x8007B7AC), FUN_8001FFA4Both memset the save context first
Boot chainFUN_80025B64 → FUN_801D6704Field overlay entered with the fresh state
Opening scene0x8007050C; FUN_8001D424 gated by _DAT_8007B8C2Default map buffer town01; the initmap.txt developer override is off in retail
Table base0x80078C4CByte-search for Vahn's stat run + name; every field cross-checked against the live record at 0x80084708 in a town01 save

See also