data/claims/lufia-ii-tables.yamlLufia II's data tables (USA ROM, read through vinheim3's disassembly and Cellenseres's decompilation)
Each of the 224 monsters has a record reached through a table of 224 offsets at $96:85C0: the battle loader copies its 13-letter name, four words from +$11 to +$17, and four bytes from +$19 to +$1C, which it doubles into words; the routine at $81:FBDB reads the experience and gold it pays as words at +$1D and +$1F; and a list of three-byte entries from +$21, ended by 0, holds the drop: in an entry of kind 3 the low 9 bits name the item and the top 7 bits give the chance in percent. Terror Wave names the four words HP, MP, attack, and defense, the four bytes agility, intelligence, guts, and magic resistance, and the byte at +$0D the level. 132 monsters carry a drop. HP runs up to 65535 (Egg Dragon) and experience up to 60000 (Egg Dragon). lufia-ii-tables/monsters
code/bank_001.s#L18568-L18633rep #ACCU_8 ; $fc2a : $c2, $20
and #$00ff.w ; $fc2c : $29, $ff, $00
asl ; $fc2f : $0a
tay ; $fc30 : $a8
lda InBattleEnemyData.w, Y ; $fc31 : $b9, $c0, $85
clc ; $fc34 : $18
adc #InBattleEnemyData.w ; $fc35 : $69, $c0, $85
tax ; $fc38 : $aa
; Load addr of enemy data in +45
ldy #$0045.w ; $fc39 : $a0, $45, $00
sta [wLoadedInBattleEnemyStruct], Y ; $fc3c : $97, $b2
sep #ACCU_8 ; $fc3e : $e2, $20
…Our readingoffset = table[monster]; record = $96:85C0 + offset18568–18575copy the 13-byte name18583–18589skip the level byte; copy byte $0E18592–18598copy the words at $11 and $13 into the fighter18601–18609copy the words at $15 and $1718611–18619for each byte at $19-$1C: store the byte times 2 as a word18621–18633lufia2-disasm: a WLA-DX disassembly of Lufia II: Rise of the Sinistrals (USA) · code/bank_001.s#L18396-L18439rep #ACCU_8 ; $fb1a : $c2, $20
stz wCurrEnemyGoldGiven.w ; $fb1c : $9c, $fe, $09
lda wHitEnemyIdx.w ; $fb1f : $ad, $fa, $09
and #$00ff.w ; $fb22 : $29, $ff, $00
asl ; $fb25 : $0a
tax ; $fb26 : $aa
lda InBattleEnemyData.l, X ; $fb27 : $bf, $c0, $85, $96
clc ; $fb2b : $18
adc #$0021.w ; $fb2c : $69, $21, $00
tax ; $fb2f : $aa
sep #ACCU_8 ; $fb30 : $e2, $20
br_01_fb32:
…Our readingwalk the monster's entry list from record + $2118396–18406an entry of kind 0 ends the list18409–18410for kind 3 with a nonzero word: chance = the word's high byte / 218413–18424if random(100) < chance and random(4) = 1: the drop is the word's low 9 bits18425–18439lufia2-disasm: a WLA-DX disassembly of Lufia II: Rise of the Sinistrals (USA) · code/bank_001.s#L18522-L18541Call_01_fbdb:
phx ; $fbdb : $da
lda wHitEnemyIdx.w ; $fbdc : $ad, $fa, $09
rep #ACCU_8 ; $fbdf : $c2, $20
and #$00ff.w ; $fbe1 : $29, $ff, $00
asl ; $fbe4 : $0a
tax ; $fbe5 : $aa
lda InBattleEnemyData.l, X ; $fbe6 : $bf, $c0, $85, $96
tax ; $fbea : $aa
sep #ACCU_8 ; $fbeb : $e2, $20
lda InBattleEnemyData.l+$1d, X ; $fbed : $bf, $dd, $85, $96
sta wCurrEnemyExpGiven.w ; $fbf1 : $8d, $fc, $09
…Our readingexperience = the fallen enemy's word at +$1D; gold = its word at +$1F18524–18539Terror Wave: a Lufia 2 randomizer · tables/struct_monster.txt#L1-L15name_text,13,str
level,1
unknown,1
battle_sprite,1
palette,1
hp,2
mp,2
attack,2
defense,2
agility,1
intelligence,1
guts,1
…lufia2-disasm: a WLA-DX disassembly of Lufia II: Rise of the Sinistrals (USA) · code/bank_000.s#L398-L430phb ; $8299 : $8b
phk ; $829a : $4b
plb ; $829b : $ab
phx ; $829c : $da
phy ; $829d : $5a
php ; $829e : $08
sep #ACCU_8|IDX_8 ; $829f : $e2, $30
xba ; $82a1 : $eb
; check counter to updating RNG
ldx wCounterTilSlowlyUpdatingRNG.w ; $82a2 : $ae, $59, $05
inx ; $82a5 : $e8
cpx #$37.b ; $82a6 : $e0, $37
…Our readingevery 55 calls, refill the random bytes408–416return (A × the next random byte) / 256, from 0 to A − 1419–425Lufia II - Rise of the Sinistrals (USA) · 0xB05C0-0xB4F18 — $96:85C0, the offset table and the recordsChecked by claude, 2026-11-08 — read the battle loader and the drop roll in the disassembly, and decoded every record from the ROM's bytes by those offsetsRevised after a check by a second AI model, 2026-11-08.The layout and values are read by the quoted routines; the field names are Terror Wave's. The battle loader skips the level byte. The drop column gives the stored percent; the routine then also needs a 1-in-4 roll, so a 40 there drops on about 10% of kills. Agility through magic resistance are the stored bytes; in battle each is twice the value shown.
Each of the 467 items, from no. 0 (no equipment) to no. 466, has a record reached through a table of 467 offsets at $96:CF69 and a 12-letter name at $9E:C7E8. The record's first nine bytes are copied as a block, and four mask bytes follow; each set mask bit, lowest first, adds a word. Byte 7's lowest set bit picks the equipment slot, byte 8 has one bit per character who can equip the item, the word at +5 is the price the Ancient Cave holds under its floor's limit, the seven words of mask bits 4 to 10 are what equipping adds, and mask bit 15's word names the item's IP attack. Terror Wave names the rest: bytes 0 to 4 use flags, more flags, targeting, icon, and sprite; byte 7's bits, low first, weapon, armor, shield, helmet, ring, and jewel; and the seven added words attack, defense, strength, agility, intelligence, guts, and magic resistance. The table holds 116 weapons, 66 armors, 45 shields, 53 helmets, 41 rings, 33 jewels, and 108 items with no kind bit. lufia-ii-tables/items
src/item/item_records.c#L82-L173static void ItemRecordAddress(
const Lufia2Memory *memory,
Lufia2CpuState *cpu) {
Push8(memory, cpu, PackStatus(cpu));
SetAccumulatorWidth(cpu, 0);
SetIndexWidth(cpu, 0);
LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu, ITEM, 0));
And16(cpu, 0x01ffu);
AslA16(cpu);
TransferAToX(cpu);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(0x96cf69u, cpu->x)));
cpu->carry = 0;
…Our readingrecord = $96:CF69 + table[item & $1FF]88–95clear the buffer and copy the name (ItemName, from $9E:C7E8 + item × 12)109–117copy the record's first 9 bytes to $0B84124–132read the 4 mask bytes after them133–140for each of the 32 mask bits, low bit first: if set, the next word goes to $0B8D + 2 × bit141–170Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/item/item_records.c#L36-L65/* $81:F2A9: 12-character item name, trailing spaces cleared. */
static void ItemName(
const Lufia2Memory *memory,
Lufia2CpuState *cpu) {
PushDataBank(memory, cpu);
LoadA8(cpu, 0x9eu);
PushAccumulator8(memory, cpu);
PullDataBank(memory, cpu);
SetAccumulatorWidth(cpu, 0); /* F2AE */
LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu, ITEM, 0));
And16(cpu, 0x01ffu);
AslA16(cpu);
…Our readingname = 12 letters at $9E:C7E8 + (item & $1FF) × 1241–65Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/menu/menu_screen.c#L1418-L1428{
unsigned i;
LoadX16(cpu, Read16Direct(memory, cpu, 0x2au));
for (i = 0; i < 7u; ++i) {
LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu,
(uint16_t)(0x0086u + 2u * i), cpu->x));
cpu->carry = 0;
Add16Value(cpu, Read16AbsoluteIndexed(memory, cpu,
(uint16_t)(0x0b95u + 2u * i), 0));
StoreAAbsolute16(memory, cpu, (uint16_t)(0x0086u + 2u * i), cpu->x);Our readingfor each of the 6 equipped items: add the 7 words from $0B95 (mask bits 4-10) to the member's 7 bonus words1421–1428Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/item/item_equipment_kind.c#L13-L20OpLda(memory, cpu, OpAbs(cpu, WRAM_RECORD_BUFFER + 0x14u));
OpAndValue(cpu, 0xffu);
OpSta(memory, cpu, OpDp(cpu, DP_EQUIPMENT_KIND_MASK));
OpLoadA(cpu, 0xffffu);
do {
OpIncA(cpu);
OpLsrMem(memory, cpu, OpDp(cpu, DP_EQUIPMENT_KIND_MASK));
} while (!cpu->carry);Our readingthe equipment slot is the lowest set bit of record byte 713–20Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/menu/menu_shop_member_equipment.c#L18-L22SHOP_TX_CALL(0x82e56bu, 0x829302u, 2u);
OpAndValue(cpu, OpReadM(memory, cpu, OpAbs(cpu, WRAM_RECORD_BUFFER + 0x15u)));
cpu->carry = 0u;
OpAdcValue(cpu, 0xffu);
RolA8(cpu);Our readinga member can equip the item when its bit is set in record byte 818–22Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/cave/ancient_cave.c#L126-L131OpRepWidths(cpu, 0x20u); /* 9091 */
LoadA16(cpu,
WramRead16At(records, ITEM_RECORD_TABLE + ITEM_RECORD_PRICE, record));
OpCmp(memory, cpu, OpDp(cpu, CAVE_DP_PRICE_LIMIT));
if (cpu->carry)
goto next;Our readingprice = the item record's word at +5; skip the item if it is at or above the floor's limit127–131Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/cave/wram.h#L42-L47ITEM_RECORD_BANK = 0x96,
ITEM_RECORD_TABLE = 0xcf69,
ITEM_RECORD_TABLE_BYTES = 0x03a4,
ITEM_RECORD_FLAGS = 0,
ITEM_RECORD_FLAGS2 = 1,
ITEM_RECORD_PRICE = 5,Terror Wave: a Lufia 2 randomizer · tables/struct_item.txt#L1-L10usability,bit:consumable equipable u02 cursed fruit unsellable usable_menu usable_battle
unknown,bit:u10 u11 u12 u13 u14 ban_ancient_cave u16 u17
targetting,1
icon,1
sprite,1
price,2
item_type,bit:weapon armor shield helmet ring jewel u66 u67
equipability,bit:maxim selan guy artea tia dekar lexis u77
misc1,bit:menu_effect battle_effect weapon_effect armor_effect increase_atp increase_dfp increase_str increase_agl
misc2,bit:increase_int increase_gut increase_mgr u93 u94 battle_animation u96 ip_effectLufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/battle/battle_ip.c#L77-L90LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu, 0x0babu, 0));
StoreTable16(memory, cpu, 0x01u);
if (!cpu->zero) {
PushIndex(memory, cpu);
SimulateJslFrame(memory, cpu, 0x81u, 0xc1bfu);
(void)Lufia2BattleReadIpRecordPointer(memory, cpu);
SimulateRtlFrame(memory, cpu);
TransferAToX(cpu);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(0x840003u, cpu->x)));
StoreTable16(memory, cpu, 0x03u);
SetAccumulatorWidth(cpu, 1);
LoadA8(cpu, Read8(memory, LongIndexedAddress(0x840005u, cpu->x)));
…Our readingthe IP word is mask bit 15's ($0BAB); if nonzero, look up its IP record77–84copy the record's word at +3 and its cost byte at +585–89Lufia II - Rise of the Sinistrals (USA) · 0xB4F69-0xB7ADD — $96:CF69, the offset table and the recordsLufia II - Rise of the Sinistrals (USA) · 0xF47E8-0xF5DCB — $9E:C7E8, the namesChecked by claude, 2026-11-08 — read the record loader, the equipment-bonus sum, the slot and member checks, and the IP lookup; decoded every record from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.Mask bits 0-3 (by Terror Wave's names the menu, battle, weapon, and armor effects), 13 (its battle animation), and 14 also carry words; the table leaves them out. Kind "other" is bit 6, set on 5 items (Hook, Bomb, Arrow, Fire arrow, Hammer). The equip byte's order (Maxim first, then Selan, Guy, Artea, Tia, Dekar, Lexis) is Terror Wave's; the code tests the member's own bit.
Equipment names its IP attack by number; 158 numbers, from 2 to 168, appear in the item table. Entry n − 1 of a table of pointers at $84:8F10 leads to the attack's record in bank $84, whose byte at +5 is its IP cost and whose name starts at +6. Costs run from 8 to 255; the battle menu offers an attack only when the member's IP byte is at least its cost. lufia-ii-tables/ip-attacks
About: Lufia II: Rise of the Sinistrals Super meter
CodeLufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) ·src/battle/battle_action_records.c#L25-L36Lufia2ExecutionResult Lufia2BattleReadIpRecordPointer(
const Lufia2Memory *memory, Lufia2CpuState *cpu) {
LoadA16(cpu, (uint16_t)(cpu->accumulator - 1u));
if (cpu->negative) {
TransferDirectToA(cpu);
return ExecutionReturned(0x81f46au);
}
AslA16(cpu);
LoadX16(cpu, cpu->accumulator);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(IP_RECORD_POINTERS, cpu->x)));
cpu->carry = false;Our readingn = IP word − 1; none if negative28–32pointer = the word at $84:8F10 + 2n33–35Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/battle/battle_ip.c#L77-L132LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu, 0x0babu, 0));
StoreTable16(memory, cpu, 0x01u);
if (!cpu->zero) {
PushIndex(memory, cpu);
SimulateJslFrame(memory, cpu, 0x81u, 0xc1bfu);
(void)Lufia2BattleReadIpRecordPointer(memory, cpu);
SimulateRtlFrame(memory, cpu);
TransferAToX(cpu);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(0x840003u, cpu->x)));
StoreTable16(memory, cpu, 0x03u);
SetAccumulatorWidth(cpu, 1);
LoadA8(cpu, Read8(memory, LongIndexedAddress(0x840005u, cpu->x)));
…Our readingfor each equipped item with an IP word: copy its record's word at +3 and cost at +577–89copy the name from +6 until a 0 byte, at most 13 letters91–106usable if the member's IP byte ≥ the cost126–132Lufia II - Rise of the Sinistrals (USA) · 0x20F10-0x2105F — $84:8F10, the pointersLufia II - Rise of the Sinistrals (USA) · 0x21074-0x21B3D — the recordsChecked by claude, 2026-11-08 — read the IP pointer lookup and the battle IP list; decoded each attack the items name from the ROM's bytesChecked by a second AI model, 2026-11-08.The member's IP byte is +$BC of its battle record, the byte that taking damage raises (lufia-ii/ip-gain); the decompilation calls the copy it compares LEVEL. The word at +0 is the attack's effect and +2 its animation by the editors' reading (Terror Wave); this claim doesn't use them.
Each of the 40 spells has a record reached through a table of 40 offsets at $95:FA5B: an 8-letter name, a byte at +10 with one bit per character who can learn it (in Terror Wave's order), the MP cost at +12, the shop price as a word at +15, and the battle script's offset at +17; Terror Wave calls the byte at +9 the element. Costs run from 0 MP (Reset and Light) to 30 MP and prices to 65000 gold; Guy and Dekar learn none, and Fry and Zap are Artea's alone. lufia-ii-tables/spells
src/item/item_records.c#L177-L223static void SpellRecordAddress(
const Lufia2Memory *memory,
Lufia2CpuState *cpu) {
SetAccumulatorWidth(cpu, 0);
LoadA16(cpu, Read16AbsoluteIndexed(memory, cpu, SPELL, 0));
And16(cpu, 0x00ffu);
AslA16(cpu);
TransferAToX(cpu);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(0x95fa5bu, cpu->x)));
cpu->carry = 0;
Add16Value(cpu, 0xfa5bu);
Write16Absolute(memory, cpu, SPELL_RECORD, cpu->accumulator);
…Our readingrecord = $95:FA5B + table[spell]180–188copy the 8-letter name, a 0, then the record's next 11 bytes203–221Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/item/menu_application.c#L9-L10USE_RECORD_SPELL_COST = WRAM_RECORD_BUFFER + 0x0du,
USE_RECORD_SPELL_SCRIPT = WRAM_RECORD_BUFFER + 0x12u,Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/menu/menu_spell_shop.c#L16SPELL_SHOP_RECORD_PRICE = WRAM_RECORD_BUFFER + 0x10u,Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/menu/menu_shop_member_spells.c#L19OpAndValue(cpu, OpReadM(memory, cpu, OpAbs(cpu, WRAM_RECORD_BUFFER + 0x0bu)));Terror Wave: a Lufia 2 randomizer · tables/struct_spell.txt#L1-L8name_text,8,str
unk1,1
element,1
characters,bit:maxim selan guy artea tia dekar lexis u37
unk4,1
mp_cost,1
zero,2
price,2Lufia II - Rise of the Sinistrals (USA) · 0xAFA5B-0xAFF14 — $95:FA5B, the offset table and the recordsChecked by claude, 2026-11-08 — read the spell record loader and the fields the menu (MP cost, script), the spell shop (price), and its member check (learner bits) read; decoded every record from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.The copy puts the name at $0B77, a 0 at $0B7F, and record bytes 8-18 at $0B80-$0B8A, so the menu's cost at buffer + $0D is record byte 12, the shop's price at + $10 is bytes 15-16, the script at + $12 is bytes 17-18, and the learner mask at + $0B is byte 10. The element byte, by Terror Wave's name, groups the attack lines (1: Spark, Fireball, Firebird; 2: Gale, Blizzard, Ice Valk; 3: Flash, Bolt, Thunder; 4: Droplet, Vortex, Dragon); no code read here uses it.
All seven characters share one experience curve: reaching level 2 takes 10 experience, level 10 2891, level 50 1548710, and level 99 9999999. The game computes it rather than storing it. Starting from a step of 20 (stored as 5120, in 1/256ths), each level adds the step to a running sum and then grows the step by step × (f + 1) / 256, where f is a factor that changes every eight levels: 190, 64, 49, 31, 17, 13, 5, 2, 2, 1, 1, 1, 1. The requirement is the sum / 256 − 10, except that reaching level 99 takes 9,999,999 flat: at level 98 the routine returns that figure instead. lufia-ii-tables/experience
code/bank_001.s#L18195-L18326phb ; $f9e9 : $8b
lda #$97.b ; $f9ea : $a9, $97
pha ; $f9ec : $48
plb ; $f9ed : $ab
phx ; $f9ee : $da
lda #$70.b ; $f9ef : $a9, $70
sta WRMPYA.w ; $f9f1 : $8d, $02, $42
tdc ; $f9f4 : $7b
lda wHitEnemyIdx.w ; $f9f5 : $ad, $fa, $09
sta WRMPYB.w ; $f9f8 : $8d, $03, $42
asl ; $f9fb : $0a
tax ; $f9fc : $aa
…Our readingrow = $97:B633 + member × $70; step = the member's big-endian word at $97:B99E; sum = 018195–18217if the current level is 98 or more: the requirement for the next is 9,999,999 @18219-18223, 18317-18321multiplier = the row's factor + 118227–18231repeat: sum += step18294–18301stop when the counter reaches the level18303–18304every eighth counter, move to the next row's factor18234–18247step += step × multiplier / 256, byte by byte18250–18291requirement = sum / 256 − 1018306–18314Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/level_up.c#L20-L60/* $81:F979: level-up of member $09FA. */
Lufia2ExecutionResult Lufia2PartyLevelUpCheck(
const Lufia2Memory *memory,
Lufia2CpuState *cpu) {
unsigned i;
LoadX16(cpu, Read16AbsoluteIndexed(memory, cpu, 0x09fau, 0));
SimulateJsrFrame(memory, cpu, 0xf97eu);
(void)Lufia2BattleTable9EBA(memory, cpu);
SimulateRtsFrame(memory, cpu);
LoadAAbsolute8(memory, cpu, 0x000eu, cpu->x);
StoreAAbsolute8(memory, cpu, 0x09feu, 0);
StoreZeroAbsolute8(memory, cpu, 0x09ffu, 0);
…Our readingrequirement = the curve at the member's level (record + $0E)27–33if the stored requirement (record + $62) differs from it, stop34–40compare the member's experience (record + $5F, 3 bytes) with the requirement41–45if experience ≥ requirement and the level isn't 99: level + 1, and store the new level's requirement46–59Lufia II - Rise of the Sinistrals (USA) · 0xBB62C-0xBB9AB — $97:B62C, the growth rows with their factors, through the start steps at $97:B99EChecked by claude, 2026-11-08 — read the experience routine in the disassembly and the level-up check, and ran the routine's arithmetic over the ROM's factors for each characterRevised after a check by a second AI model, 2026-11-08.The level-up check compares experience with the curve at the current level, so the curve at level L is the total that reaches level L + 1; the table lists the total that reaches each level. The multiply adds the high byte of the direct page register as rounding; the column assumes it is 0, as the decompilation's entry checks require of its callers. The rows' eighth bytes and the start words are the same for all seven characters.
Cited by: Growth rate
Each character's stats grow by fixed steps, in sixteenths of a point, from a row of seven bytes that changes every eight levels: 7 characters × $70 bytes at $97:B62C, of which levels 2 to 99 reach the first 13 rows of 8 bytes; the fourteenth row is zeros. A stat at level L is its base plus ⌊(sum of the steps for levels 2 to L) / 16⌋. Terror Wave names the seven bytes HP, MP, strength, agility, intelligence, guts, and magic resistance; the guts byte is 0 in every row, so guts stays at its base. lufia-ii-tables/growth
src/party/stats.c#L218-L243Lufia2ExecutionResult Lufia2PartyBaseStats(
const Lufia2Memory *memory, Lufia2CpuState *cpu) {
LoadAAbsolute8(memory, cpu, MEMBER, 0);
StoreAAbsolute8(memory, cpu, SNES_WRMPYA, 0);
StoreA8Absolute(memory, cpu, SNES_WRMPYB, 0x0eu);
PushIndex(memory, cpu);
PushY(memory, cpu);
LoadX16(cpu, 0u);
LoadY16(cpu, Read16AbsoluteIndexed(memory, cpu, SNES_RDMPYL, 0));
PushY(memory, cpu);
StoreA8Absolute(memory, cpu, SNES_WRMPYB, 0x70u);
const Lufia2Wram wram = WramViewOfCaller(memory, cpu);
…Our readingbase offset = member × 14220–227row offset = member × $70228sum the steps, divide by 16, add the bases232–236Terror Wave: a Lufia 2 randomizer · tables/struct_char_growth.txt#L1-L8hp,1
mp,1
str,1
agl,1
int,1
gut,1
mgr,1
unk,1Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/stats.c#L150-L215enum {
BASE_STAT_COUNT = 7u,
BASE_GROWTH_ROW = 0xb2u,
BASE_GROWTH_TABLE = 0xb62cu,
BASE_VALUES_TABLE = 0xb93cu,
BASE_GROWTH_ROW_LEVELS = 8u,
BASE_GROWTH_SCALE_SHIFT = 4u
};
static Byte8Result AddBaseStatByte(
Lufia2Wram wram, uint16_t field, uint8_t increment, bool decimal) {
const uint8_t previous = WramReadAt(wram, STATS, field);
const Byte8Result sum = Sum8Mode(increment, previous, false, decimal);
…Our readingrow = $97:B62C + the caller's product, member × $70174–176for each level from 2 to L: every eighth level move 8 bytes on; add the row's 7 bytes to 7 sums179–193divide each sum by 16196–204add each base byte from $97:B93C + member × 14, a word apart206–215Lufia II - Rise of the Sinistrals (USA) · 0xBB62C-0xBB93B — $97:B62C, the rowsChecked by claude, 2026-11-08 — read the base-stat routine in the decompilation and decoded the rows from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.The row's eighth byte is the experience factor (lufia-ii-tables/experience). Rows run from level 2 to 8, then eight levels each, so the thirteenth covers 97 to 99.
Cited by: Growth rate
Each character's base stats are seven words at $97:B93C, 14 bytes per character, of which the routine adds only the low byte, each to the growth row's stat in the same place: Maxim starts from 20, 0, 12, 9, 8, 100, 5 (by Terror Wave's names, HP, MP, strength, agility, intelligence, guts, magic resistance). lufia-ii-tables/base-stats
Value: character Maxim, Selan, Guy, Artea, Tia, Dekar, Lexis · HP 20, 20, 20, 20, 10, 10, 10 · MP 0, 0, 0, 0, 0, 0, 0 · STR 12, 20, 8, 8, 8, 8, 8 · AGL 9, 9, 9, 9, 9, 9, 9 · INT 8, 7, 7, 7, 7, 7, 7 · GUT 100, 80, 90, 60, 60, 100, 70 · MGR 5, 5, 5, 5, 5, 5, 5
CodeLufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) ·src/party/stats.c#L218-L243Lufia2ExecutionResult Lufia2PartyBaseStats(
const Lufia2Memory *memory, Lufia2CpuState *cpu) {
LoadAAbsolute8(memory, cpu, MEMBER, 0);
StoreAAbsolute8(memory, cpu, SNES_WRMPYA, 0);
StoreA8Absolute(memory, cpu, SNES_WRMPYB, 0x0eu);
PushIndex(memory, cpu);
PushY(memory, cpu);
LoadX16(cpu, 0u);
LoadY16(cpu, Read16AbsoluteIndexed(memory, cpu, SNES_RDMPYL, 0));
PushY(memory, cpu);
StoreA8Absolute(memory, cpu, SNES_WRMPYB, 0x70u);
const Lufia2Wram wram = WramViewOfCaller(memory, cpu);
…Our readingbase offset = member × 14220–227add the bases from that offset235–236Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/stats.c#L206-L215static Byte8Result AddInitialBaseStats(
Lufia2Wram wram, uint16_t member_offset, bool decimal) {
Byte8Result total = {0};
for (unsigned stat = 0u; stat < BASE_STAT_COUNT; ++stat) {
const uint8_t base = WramReadAt(wram, BASE_VALUES_TABLE, member_offset);
total = AddBaseStatByte(wram, (uint16_t)(stat * 2u), base, decimal);
member_offset = (uint16_t)(member_offset + 2u);
}
return total;
}Our readingfor each of the 7 stats: add the low byte of the word at $97:B93C + offset; offset + 2209–213Lufia II - Rise of the Sinistrals (USA) · 0xBB93C-0xBB99DChecked by claude, 2026-11-08 — read the base-stat routine's offsets and last step and decoded the words from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.At level 99, before equipment, Maxim has 694 HP, 464 MP, and 364 strength, Dekar 949 HP and 669 strength, and Tia 476 MP. The table is computed from the growth rows and base stats: each stat is its base plus a sixteenth of its summed steps, rounded down. lufia-ii-tables/stats-by-level
The seven capsule monsters have five forms each, 35 records reached through offsets at $97:DCB8 by the monster's number × 5 + form − 1. The stat routine pairs the byte at +$15 and the five at +$18 to +$1C with six growth codes at +$1D to +$22. Terror Wave names the rest of the record: a 12-letter name, an alignment byte at +14, and the eight stat bytes from +$15 as HP, attack, defense, strength, agility, intelligence, guts, and magic resistance. Foomy's line runs Foomy S, Foomy M, Foomy L, Foomy H, Gold Fox. lufia-ii-tables/capsule-monsters
src/party/capsule.c#L247-L260/* $82:C38B: $09C4 = record of monster and form ($97:DCB8). */
static void CapsuleRecord(const Lufia2Memory *memory, Lufia2CpuState *cpu) {
const Lufia2Wram wram = WramViewOfCaller(memory, cpu);
const uint8_t monster = WramRead(wram, CAPSULE);
const uint8_t form = WramRead(wram, FORM);
const uint8_t record = (uint8_t)(((monster & 7u) << 2) + monster + form - 1u);
SetAccumulatorWidth(cpu, 0);
LoadA16(cpu, Read16Long(memory, LongIndexedAddress(0x97dcb8u, 2u * record)));
cpu->carry = 0;
Add16Value(cpu, 0xdcb8u);
TransferAToX(cpu);
WramWrite16(wram, RECORD, cpu->x);
…Our readingrecord = $97:DCB8 + table[(monster & 7) × 4 + monster + form − 1]250–259Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/capsule.c#L273-L330static void CapsuleGrowth(const Lufia2Memory *memory, Lufia2CpuState *cpu) {
const Lufia2Wram wram = WramViewOfCaller(memory, cpu);
const uint16_t level = WramRead16(wram, LEVEL) & 0x00ffu;
WramWrite16(wram, GROWTH_ROW, (uint16_t)((cpu->accumulator & 0x00ffu) << 3));
WramWrite16(wram, GROWTH_SUM, 0);
if (level != 1u) {
uint16_t counter;
WramWrite16(wram, GROWTH_FORM, WramRead16(wram, FORM));
WramWrite16(wram, GROWTH_FORM + 1u, 0);
WramWrite16(wram, GROWTH_COUNTER, 1);
do {
uint16_t block;
…Our readinga stat's growth: rate row = code × 8277for each counter from 1 to L − 1: rate = the row's byte for band counter / 16279–295from the band equal to the form, halve the rate; past it, halve again297–301sum the rates; divide the sum by 16302–310base bytes at +$15 and +$18-$1C pair with growth codes at +$1D-$22316–318Terror Wave: a Lufia 2 randomizer · tables/struct_capsule.txt#L1-L20name_text,12,str
zero,1
capsule_class,1
alignment,1
start_skills,3,list
upgrade_skills,3,list
hp,1
attack,1
defense,1
strength,1
agility,1
intelligence,1
…Lufia II - Rise of the Sinistrals (USA) · 0xBDCB8-0xBEE47 — $97:DCB8, the offsets and the recordsChecked by claude, 2026-11-08 — read the record lookup and the stat routine in the decompilation and decoded every record from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.Attack and defense have no growth code. The alignment byte holds one bit per line (Foomy's 0, then 1, 2, 4, 8, 16, 32). Terror Wave calls byte 13 the class and bytes 15 to 20 the starting and upgrade skills.
A capsule monster's growth code picks a row of eight rates at $A6:F420, one per 16-level band; each level adds its band's rate, and the sum is divided by 16. The table gives the rates as stored, before the routine halves them: a band equal to the monster's form adds half its rate, and each later band a quarter. The stored rates fall with the bands, from as much as 128 to 0. lufia-ii-tables/capsule-growth-rates
src/party/capsule.c#L43GROWTH_RATES = 0xa6f420u,Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/capsule.c#L273-L311static void CapsuleGrowth(const Lufia2Memory *memory, Lufia2CpuState *cpu) {
const Lufia2Wram wram = WramViewOfCaller(memory, cpu);
const uint16_t level = WramRead16(wram, LEVEL) & 0x00ffu;
WramWrite16(wram, GROWTH_ROW, (uint16_t)((cpu->accumulator & 0x00ffu) << 3));
WramWrite16(wram, GROWTH_SUM, 0);
if (level != 1u) {
uint16_t counter;
WramWrite16(wram, GROWTH_FORM, WramRead16(wram, FORM));
WramWrite16(wram, GROWTH_FORM + 1u, 0);
WramWrite16(wram, GROWTH_COUNTER, 1);
do {
uint16_t block;
…Our readingrow = code × 8; sum = 0277–278for each counter from 1 to L − 1: band = counter / 16; rate = the row's byte for the band284–295if the band ≥ the form: halve the rate; if it is past the form, halve it again297–301sum += rate302–305sum / 16309–310Lufia II - Rise of the Sinistrals (USA) · 0x137420-0x13749FChecked by claude, 2026-11-08 — read the capsule stat routine and decoded the rows from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.The band is the level counter divided by 16, so band 0 covers the steps to levels 2-16 and band 6 those to 97-99; the records use codes 1 to 15, and row 0 is all zeros.
Each capsule monster's fifth form comes from one fruit and goes back with another: a table of seven item words at $8E:E49A names the fruit that changes it to form 5, one at $8E:E4A8 the fruit that reverts it, and one at $8E:E4B6 the form it must be in and returns to. Foomy turns into the Gold Fox on a Secret fruit at form 4. lufia-ii-tables/capsule-fruit
src/party/capsule_feeding.c#L162-L202static bool ChangeSpecialForm(CapsuleFoodCall *call) {
const Lufia2Memory *memory = call->memory;
Lufia2CpuState *cpu = call->cpu;
if (!FoodChild(call, 0x82cc6du, 0x82c482u, 2u))
return false;
OpLda(memory, cpu, OpAbsX(cpu, 0u));
OpAndValue(cpu, CAPSULE_SPECIAL_FORM_BIT);
const bool reverting = !cpu->zero;
OpRepWidths(cpu, 0x20u);
OpLdx(cpu, OpReadX(memory, cpu, OpDp(cpu, DP_CAPSULE_TABLE_INDEX)));
OpLda(memory, cpu, OpLongX(cpu, reverting
? ROM_CAPSULE_REVERSION_FOOD
…Our readingreverting = the monster's state has the form-5 bit (8)167–169the item must be the monster's word in the revert table if reverting, else in the change table171–176to change, the saved form must equal the monster's word in the form table178–184set or clear the form-5 bit; form = the form table's word when reverting, else 5189–201Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/party/capsule_feeding.c#L10-L12ROM_CAPSULE_EVOLUTION_FOOD = 0x8ee49au,
ROM_CAPSULE_REVERSION_FOOD = 0x8ee4a8u,
ROM_CAPSULE_REVERSION_FORM = 0x8ee4b6u,Lufia II - Rise of the Sinistrals (USA) · 0x7649A-0x764C3 — $8E:E49A, the three tablesChecked by claude, 2026-11-08 — read the special-form routine in the decompilation and decoded the three tables from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.The shop table at $97:EE9F holds offsets to 59 shops. Each starts with a kind byte, a flag byte, and a third byte; flag bit 0 lists the party's own items, each of bits 1 to 5 adds a list, and bit 7 one more menu entry. The first four lists are words ended by 0, and the fifth is bytes ended by $FF. Terror Wave names bit 0 pawn, bits 1 to 5 prize coins, items, weapons, armor, and spells, and bit 7 sell. lufia-ii-tables/shops
src/menu/menu_screen.c#L1034-L1099Lufia2ExecutionResult Lufia2MenuShopSetup(
const Lufia2Memory *memory,
Lufia2CpuState *cpu) {
static const uint16_t kReturns[4][2] = {
{0xe2cau, 0xe2cdu}, {0xe2dau, 0xe2ddu}, {0xe2eau, 0xe2edu},
{0xe2fau, 0xe2fdu}};
unsigned i;
Jsr(memory, cpu, 0xe299u); /* E729 */
LoadX16(cpu, 0xee9fu);
StoreXDirect16(memory, cpu, 0x08u);
LoadA8(cpu, 0x97u);
StoreADirect8(memory, cpu, 0x0au);
…Our readingrecord = $97:EE9F + table[shop]1043–1055kind = byte 0; flags = byte 11057–1062flag bit 0: list the party's own items1067–1076flag bits 1-5: register one list each1077–1088flag bit 7: one more list entry1089–1098Lufia2Decomp: semantic reconstruction of Lufia II: Rise of the Sinistrals (SNES, USA) · src/menu/menu_screen.c#L941-L958static void ShopCount(const Lufia2Memory *memory, Lufia2CpuState *cpu,
int bytes) {
SetAccumulatorWidth(cpu, 0);
LoadY16(cpu, 0xffffu);
do {
IncrementY16(cpu);
LoadA16(cpu, Read16Long(memory, DirectLongPointer(memory, cpu, 0x08u)));
if (bytes)
And16(cpu, 0x00ffu);
OpStepMem(memory, cpu, OpDp(cpu, 0x08u), 1);
if (!bytes)
OpStepMem(memory, cpu, OpDp(cpu, 0x08u), 1);
…Our readingcount a list: read a word, or its low byte for the fifth list943–952stop at 0, or at $FF for the fifth list953–954Terror Wave: a Lufia 2 randomizer · tables/struct_shop.txt#L1-L3unknown0,1
shop_type,bit:pawn coin item weapon armor spell unk16 sell
unknown2,1Lufia II - Rise of the Sinistrals (USA) · 0xBEE9F-0xBF63A — $97:EE9F, the offsets and the recordsChecked by claude, 2026-11-08 — read the shop setup in the decompilation and decoded every shop from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.The lowest offset is 512, so the table holds 256 offsets; slots 0, 6, 7, 17 and 63 to 255 are 0, which leaves 59. Decoded by the counted encodings, every list gives item and spell names that fit the shop, and each shop ends where the next one's offset begins. Item prices are in lufia-ii-tables/items and spell prices in lufia-ii-tables/spells.
The Ancient Cave's enemies by floor: an enemy on floor N is one of the six entries from 2(N − 1) of a 200-byte table of monster numbers at $94:D95C, which names 149 different monsters in all. Floor 1 holds Goblin, Goblin Mage, Armor goblin, Regal Goblin, Troll, Ork; floor 98 Dark Warrior, Ochi Warrior, Sly Fox, Pierre. lufia-ii-tables/cave-enemies
src/cave/cave_objects.c#L106-L117OpLda(memory, cpu, OpAbs(cpu, CAVE_FLOOR)); /* 9A30 */
OpDecA(cpu);
OpAslA(cpu);
OpSta(memory, cpu, OpDp(cpu, DP_SCRATCH_A));
LoadA8(cpu, 0x06u);
Lufia2CaveRandomBelow(memory, cpu, 0x9a39u);
cpu->carry = 0;
OpAdc(memory, cpu, OpDp(cpu, DP_SCRATCH_A));
OpSepWidths(cpu, 0x30u); /* 9A3F */
OpTax(cpu);
OpLda(memory, cpu, OpLongX(cpu, 0x94d95cu));
OpSta(memory, cpu, OpAbsY(cpu, 0xf966u));Our readingindex = (floor - 1) × 2 + random(6)106–114monster = $94:D95C[index]116–117Lufia II - Rise of the Sinistrals (USA) · 0xA595C-0xA5A23 — $94:D95C, the monster numbers; names from lufia-ii-tables/monstersWhich enemies an Ancient Cave floor can hold depends only on its number: each enemy placed on floor N is entry 2(N − 1) plus a random 0 to 5 of a 200-entry table, so each floor has six kinds, a floor shares four of them with the next, and each entry serves three floors. A floor makes 4 to 7 tries to place an enemy, each at a random spot of a random cell, and a try fails when no cell is found or the spot is beside the start or another object; a treasure room adds up to 13 more, and a floor holds at most 20. CodeChecked by claude, 2026-11-08 — read the enemy pick in the decompilation and decoded the table from the ROM's bytesRevised after a check by a second AI model, 2026-11-08.Where a floor's six entries name the same monster twice, that monster is twice as likely there (floor 98 has Dark Warrior and Ochi Warrior twice). Each floor shares four entries with the next, so only the table's last 2 entries serve floor 98 alone.
The Ancient Cave's weapon and armour lists by floor, as the builder makes them from the item records: the price ceiling, how many items each list holds, and how many the chest pick can reach, which is the count modulo 128. From floor 60 the ceiling is gone and the lists stay at 88 weapons and 185 armour pieces, of which 57 can be drawn; the armour list first passes 127 items on floor 10. lufia-ii-tables/cave-item-pools
Value
{
"floor": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
"60–98"
],
"ceiling": [
2000,
3000,
4000,
5000,
6000,
7000,
8000,
9000,
10000,
11000,
12000,
13000,
14000,
15000,
16000,
17000,
18000,
19000,
20000,
21000,
22000,
23000,
24000,
25000,
26000,
27000,
28000,
29000,
30000,
31000,
32000,
33000,
34000,
35000,
36000,
37000,
38000,
39000,
40000,
41000,
42000,
43000,
44000,
45000,
46000,
47000,
48000,
49000,
50000,
51000,
52000,
53000,
54000,
55000,
56000,
57000,
58000,
59000,
60000,
null
],
"weapons": [
15,
18,
20,
24,
26,
33,
34,
37,
43,
44,
46,
47,
51,
52,
55,
55,
55,
57,
58,
62,
63,
63,
64,
66,
67,
67,
67,
69,
70,
73,
74,
74,
74,
74,
75,
75,
76,
76,
76,
76,
76,
76,
76,
76,
77,
77,
78,
79,
80,
83,
84,
87,
87,
87,
87,
87,
87,
87,
87,
88
],
"weaponsDrawn": [
15,
18,
20,
24,
26,
33,
34,
37,
43,
44,
46,
47,
51,
52,
55,
55,
55,
57,
58,
62,
63,
63,
64,
66,
67,
67,
67,
69,
70,
73,
74,
74,
74,
74,
75,
75,
76,
76,
76,
76,
76,
76,
76,
76,
77,
77,
78,
79,
80,
83,
84,
87,
87,
87,
87,
87,
87,
87,
87,
88
],
"armour": [
35,
43,
61,
72,
85,
95,
102,
115,
122,
129,
134,
137,
140,
144,
148,
148,
148,
153,
154,
157,
157,
158,
158,
159,
161,
163,
163,
167,
167,
169,
169,
171,
172,
174,
177,
177,
177,
178,
178,
179,
179,
180,
180,
180,
180,
180,
180,
180,
180,
183,
184,
184,
184,
185,
185,
185,
185,
185,
185,
185
],
"armourDrawn": [
35,
43,
61,
72,
85,
95,
102,
115,
122,
1,
6,
9,
12,
16,
20,
20,
20,
25,
26,
29,
29,
30,
30,
31,
33,
35,
35,
39,
39,
41,
41,
43,
44,
46,
49,
49,
49,
50,
50,
51,
51,
52,
52,
52,
52,
52,
52,
52,
52,
55,
56,
56,
56,
57,
57,
57,
57,
57,
57,
57
],
"firstArmour": [
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron",
"Apron"
],
"lastDrawnArmour": [
"Glass ring",
"Speedy ring",
"Thunder ring",
"Fury ring",
"Eagle rock",
"Eagle rock",
"Eagle rock",
"Cancer rock",
"Cancer rock",
"Apron",
"Frock",
"Coat",
"Light armor",
"Chainmail",
"Chain armor",
"Chain armor",
"Chain armor",
"Iron plate",
"Metal mail",
"Light jacket",
"Light jacket",
"Metal coat",
"Metal coat",
"Silver mail",
"Quilted silk",
"Power cape",
"Power cape",
"Plate armor",
"Plate armor",
"Silk robe",
"Silk robe",
"Crystal mail",
"Crystal robe",
"Metal jacket",
"Bright armor",
"Bright armor",
"Bright armor",
"Bright cloth",
"Bright cloth",
"Power robe",
"Power robe",
"Magic scale",
"Magic scale",
"Magic scale",
"Magic scale",
"Magic scale",
"Magic scale",
"Magic scale",
"Magic scale",
"Full mail",
"Old armor",
"Old armor",
"Old armor",
"Zircon plate",
"Zircon plate",
"Zircon plate",
"Zircon plate",
"Zircon plate",
"Zircon plate",
"Zircon plate"
]
}A count of 0 (a list of exactly 128) still gives the list's first item. The ceiling column is empty from floor 60, where the builder sets the limit to $FFFF. The builder reads item ids 0 to 465 (466 items), so item 466 is never listed; the lists keep item order.
The Ancient Cave's four fixed chest lists, as item words in the ROM: the nine Iris treasures at $91:FFCA, the nine restoratives at $91:FFDC (Potion and Hi-Magic twice each), the 41 blue-chest items at $94:EEA0, which are also the items the cave lets the party keep, and the 31 other consumables at $94:F13D. lufia-ii-tables/cave-chest-lists
0x8FFCA-0x8FFED — $91:FFCA and $91:FFDC, nine words eachLufia II - Rise of the Sinistrals (USA) · 0xA6EA0-0xA6EF1 — $94:EEA0, 41 wordsLufia II - Rise of the Sinistrals (USA) · 0xA713D-0xA717A — $94:F13D, 31 wordsChecked by claude, 2026-11-08 — decoded the four tables from the ROM's bytes at the addresses the chest, entry and exit routines read; names from the item name tableChecked by a second AI model, 2026-11-08.The blue list is read whole by the chest roll ($83:91C8, 41 entries) and by the entry and exit tests, which compare $52 bytes. The other consumables list holds Ex-Magic and Hi-Magic twice each.