diff --git a/1/NES_addresses.tsv b/1/NES_addresses.tsv new file mode 100644 index 0000000..f3d1a9e --- /dev/null +++ b/1/NES_addresses.tsv @@ -0,0 +1,5 @@ +Label Address Format Comment +character_battle_sprite_tiles 0x25000 12 of 32 of TileNES2bpp +weapon_battle_sprite_tiles 0x26800 128 of TileNES2bpp +title_logo_tiles 0x27400 96 of TileNES2bpp "6x8 logo, gothic text - 8x2 ""FINAL"", 12x2 ""FANTASY"", 8x1 ""ソタチツテトナニ""" +character_battle_sprite_palettes 12 of Palette FINDME diff --git a/2/NES_addresses.tsv b/2/NES_addresses.tsv new file mode 100644 index 0000000..6e46906 --- /dev/null +++ b/2/NES_addresses.tsv @@ -0,0 +1,3 @@ +Label Address Format Comment +character_battle_sprite_tiles 0x25E00 9 of 32 of TileNES2bpp +character_battle_sprite_palettes 9 of Palette FINDME diff --git a/3/NES_addresses.tsv b/3/NES_addresses.tsv new file mode 100644 index 0000000..57b881d --- /dev/null +++ b/3/NES_addresses.tsv @@ -0,0 +1,3 @@ +Label Address Format Comment +character_battle_sprite_tiles 0x50000 22 of 42 of TileNES2bpp +character_battle_sprite_palettes 22 of Palette FINDME diff --git a/4/SNES_PSX_addresses.tsv b/4/SNES_PSX_addresses.tsv new file mode 100644 index 0000000..ff41db2 --- /dev/null +++ b/4/SNES_PSX_addresses.tsv @@ -0,0 +1,6 @@ +Label SNES PSX_file PSX_offset format Comment +character_battle_sprite_tiles 0xD0000 /mnu/men_bin.eng 14 of 64 of TileSNES4bpp FIXME - PSX +character_battle_sprite_piggy_tiles 0xD7000 48 of TileSNES4bpp FIXME - PSX +character_battle_sprite_golbez_tiles 0xD7600 27 of TileSNES4bpp FIXME - PSX +character_battle_sprite_anna_tiles 0xD7960 14 of TileSNES4bpp FIXME - PSX +character_battle_sprite_palettes 0xE7D00 /btl/ff5_btl.bin 16 of Palette16Of555 FIXME - PSX diff --git a/5/addresses_SNES_PSX.tsv b/5/addresses_SNES_PSX.tsv new file mode 100644 index 0000000..eb88839 --- /dev/null +++ b/5/addresses_SNES_PSX.tsv @@ -0,0 +1,89 @@ +Label SNES PSX_file PSX_offset format Comment +character_battle_sprite_stone_palette 0x00F807 N/A N/A Palette16Of555 Also 0x199835 +character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 Palette16Of555 +locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of Palette128Of555 +font_glyphs_dialogue 0x03E800 256 of SNESTritile length 0x1800 +sfx_brr_data 0x041E3F Use the below SPC pointers +sfx_brr_pointers 0x041F4F 8 of 2 of u16 SPC memory addresses not ROM. Start address followed by loop address. +sfx_adsrs 0x041F71 8 of u16 +sfx_samplerates 0x041F83 8 of u16 +sfx_data 0x041F95 Contains SPC pointers and tracks +bgm_song_pointers 0x043B97 72 of u24 +bgm_instrument_brr_pointers 0x043C6F 35 of u24 +bgm_instrument_loop_starts 0x043CD8 35 of u16 +bgm_instrument_samplerates 0x043D1E 35 of u16 +bgm_instrument_adsrs 0x043D64 35 of u16 +bgm_instrument_indices 0x043DAA 72 of 16 of u16 length 0x900 +enemy_battle_sprite_palettes 0x0ED000 See enemy_battle_sprite_data for pointers. Some are 8 colours instead of 16. +worldmap_blocks 0x0FF0C0 /nar/ff5_binx.bin 0x040300 3 of 4 of 192 of u8 # Top-left corners, top-right corners, bottom-left corners, bottom-right corners +worldmap_tiles.bias 0x0FF9C0 /nar/ff5_bin3.bin 0x03FB00 3 of 256 of u8 Add to each pixel of the mode7c tiles +worldmap_palettes 0x0FFCC0 /nar/ff5_binx.bin 0x040000 3 of Palette128Of555 +enemy_stats 0x100000 384 of EnemyStats +enemy_battle_sprite_layouts_small 0x10D004 102 of 8 of u8 length 0x330 +enemy_battle_sprite_layouts_large 0x10D334 72 of 16 of u16 length 0x900 +enemy_battle_sprite_mini 0x10E003 4 of TileSNES4bpp length 0x80 +enemy_battle_sprite_frog 0x10E083 4 of TileSNES4bpp length 0x80 +enemy_battle_sprite_shadow 0x10E103 3 of TileSNES4bpp length 0x60 +tbl_weapons 0x110000 128 of WeaponData length 0x600 +tbl_armors 0x110600 96 of ArmorData length 0x480 +tbl_items 0x110A80 32 of ItemData length 0x100 +tbl_magic 0x110B80 256 of MagicData length 0x800 +tbl_equip_types 0x112480 64 of 4 of u8 length 0x100 - Item Equipment type definitions (64x4 bytes, 2B weapon, 2B armor) +tbl_armors_elem_def 0x112580 64 of ElemDef length 0x140 - Item Armor Element defense 64x(5B - absorb, evade, immunity, half, weakness) +tbl_armors_status 0x1126C0 64 of StatusEffect length 0x1C0 - Item Armor Status defense 64x(7B - 4B Initial, 3B Immune) +tbl_prices_items 0x112A00 256 of ItemCost length 0x200 +tbl_prices_magic 0x112C00 128 of ItemCost length 0x100 +tbl_charlevels_exp 0x115000 99 of u24 +tbl_charlevels_hp_base 0x115129 99 of u16 +tbl_charlevels_mp_base 0x1151EF 99 of u16 +ptrs_job_ability_lists 0x1152C0 21 of u16 +job_ability_counts 0x1152EA 22 of u8 +job_ability_lists 0x115300 length 0x12C - u16 ABP required, u8 ID. Pointers are from bank 0x110000 and start at $5300 +tbl_char_stat_bonuses 0x11551E 5 of 4 of u8 +tbl_job_base_stats 0x1156B0 22 of 4 of u8 +tbl_job_default_equipment 0x115708 22 of 4 of u8 +tbl_job_default_commands 0x115760 22 of 4 of u8 +tbl_job_innates 0x1157B8 22 of u16 +tbl_ability_stats_commands 0x115E00 256 of 4 of u8 length 0x400 +tbl_ability_stats_passives 0x116308 33 of 4 of u8 length 0x84 +character_initial_data 0x117000 4 of Character length 0x140 +attack_layouts 0x117FA0 5 of 2048 of u8 length 0x2400 +weapon_palettes 0x11A3A0 +enemy_battle_status_sprite_palette 0x11A400 Palette16Of555 +tiles_fist 0x11D710 /btl/ff5_btl.bin 0x021D10 TileSNES3bpp Also /mnu/men_bin.eng:0x00D910 +font_glyphs_small 0x11F000 256 of TileSNES2bpp length 0x1000 +character_battle_sprite_tiles 0x120000 /mnu/men_bin.eng 0x010200 5 of 22 of 48 of TileSNES4bpp +character_battle_sprite_palettes 0x14A3C0 /btl/ff5_btl.bin 0x0273C0 5 of 22 of Palette16Of555 Also /mnu/men_bin.eng:0x03A5C0 +enemy_battle_sprite_data 0x14B180 384 of EnemySpriteData length 0x780 +character_battle_sprite_layouts 0x14B997 /btl/ff5_btl.bin 0x028997 11 of 6 of u8 +tbl_battle_backgrounds 0x14BA21 34 of BattleBackgroundData +battle_background_palettes 0x14BB31 84 of Palette16Of555 +ptrs_battle_background_tile_animations 0x14C5B1 8 of u16 bank 0x140000 (0xD40000) +battle_background_tile_animations_data 0x14C5C1 see above +ptrs_battle_background_palette_animations 0x14C6CD 3 of u16 bank 0x140000 (0xD40000) +battle_background_palette_animations_data 0x14C6D3 see above +ptrs_battle_background_tilemap_flips 0x14C736 9 of u16 bank 0x140000 (0xD40000) +battle_background_tilemap_flips_data 0x14C75C see above +ptrs_battle_background_tilemaps 0x14C86D 28 of u16 bank 0x140000 (0xD40000) +? 0x14C8A5 +battle_background_tilemaps_data 0x14E09B see above +enemy_battle_sprite_tiles 0x150000 See enemy_battle_sprite_data for pointers +ptrs_battle_background_tileset_skips 0x184157 21 of u24 RAM addresses, subtract 0x7FC000 from results to get offset from tileset +ptrs_battle_background_tilesets 0x184196 21 of u24 ROM addresses, subtract 0xC00000 +lzss_battle_background_tilesets 0x1841D5 see pointers above, 4bpp +? 0x18DE36 +ptrs_event_data 0x18E080 687 of u16 +event_data 0x18E5E0 see above, links to extended event data +tiles_attack_anims 0x190000 +ptrs_anim_unk1 0x19A486 405 of u16 bank offsets to below + 0x19A7B0 padding +anim_unk1 0x19A7C0 see above +ptrs_anim_unk2 0x19B35E 1669 of u16 bank offsets to below, animation frame data??? also to anim_unk1 +anim_unk2 0x19C068 see above +tiles_npcs 0x1A0000 +worldmap_tiles.0 0x1B8000 /nar/ff5_bin3.bin 0x039B00 256 of TileSNESMode7c Add the biases +worldmap_tiles.1 0x1BA000 /nar/ff5_bin3.bin 0x039B00 256 of TileSNESMode7c Add the biases +worldmap_tiles.2 0x1BC000 /nar/ff5_bin3.bin 0x039B00 128 of TileSNESMode7c Add the biases +font_glyphs_kanji 0x1BD000 426 of SNESTritile length of 0x27F0 +? 0x1BF800 +RPGe_font_character_widths 0x203225 512 of u8 RPGe only, Includes the 1px spacing diff --git a/5/glyphs/SNES_dialog.txt b/5/glyphs/SNES_dialog.txt new file mode 100644 index 0000000..eee56e1 --- /dev/null +++ b/5/glyphs/SNES_dialog.txt @@ -0,0 +1,256 @@ +  +\n +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +バ +ば +ビ +び +ブ +ぶ +ベ +べ +ボ +ぼ +ガ +が +ギ +ぎ +グ +ぐ +ゲ +げ +ゴ +ご +ザ +ざ +ジ +じ +ズ +ず +ゼ +ぜ +ゾ +ぞ +ダ +だ +ヂ +ぢ +ヅ +づ +デ +で +ド +ど +ヴ +パ +ぱ +ピ +ぴ +プ +ぷ +ペ +ぺ +ポ +ぽ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +_m +_H +_P +ハ +は +ヒ +ひ +フ +ふ +ヘ +へ +ホ +ほ +カ +か +キ +き +ク +く +ケ +け +コ +こ +サ +さ +シ +し +ス +す +セ +せ +ソ +そ +タ +た +チ +ち +ツ +つ +テ +て +ト +と +ウ +う +ア +あ +イ +い +エ +え +オ +お +ナ +な +ニ +に +ヌ +ぬ +ネ +ね +ノ +の +マ +ま +ミ +み +ム +む +メ +め +モ +も +ラ +ら +リ +り +ル +る +レ +れ +ロ +ろ +ヤ +や +ユ +ゆ +ヨ +よ +ワ +わ +ン +ん +ヲ +を +ッ +っ +ャ +ゃ +ュ +ゅ +ョ +ょ +ァ +ー +ィ +⋯ +ぅ +! +ェ +? +ォ +% +/ +: +「 +」 +。 +A +B +X +Y +L +R +E +H +M +P +S +C +T +← +→ ++ +、 +◯ +『 +F +°C +・ +( +) +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  diff --git a/5/glyphs/SNES_dialog_RPGe.txt b/5/glyphs/SNES_dialog_RPGe.txt new file mode 100644 index 0000000..0df393c --- /dev/null +++ b/5/glyphs/SNES_dialog_RPGe.txt @@ -0,0 +1,256 @@ + +\n + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +バ +ば +ビ +び +ブ +ぶ +ベ +べ +ボ +ぼ +ガ +が +ギ +ぎ +グ +ぐ +ゲ +げ +ゴ +ご +ザ +ざ +ジ +じ +ズ +ず +ゼ +ぜ +ゾ +ぞ +ダ +だ +ヂ +ぢ +ヅ +づ +デ +で +ド +ど +ヴ +パ +ぱ +ピ +ぴ +プ +ぷ +ペ +“ +‘ + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +_m +_H +_P +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +il +it + +li +ll +’ +” +: +; +, +( +) +/ +! +? +. +ti +fi +ラ +ら +p +el +‘ +“ +if +lt +tl +ir +tt +や +ユ +ゆ +ヨ +よ +ワ +わ +ン +ん +ヲ +を +ッ +っ +ャ +ゃ +ュ +ゅ +ョ +ょ +ァ +ー +ィ +⋯ +ぅ +! +ェ +? +ォ +% +/ +: +「 +」 +。 +A +B +X +Y +L +R +E +H +M +P +S +C +T +← +→ ++ +、 +◯ +『 + +°C +・ +( +) + + + + + + + + + + + + + + + + + + + + + diff --git a/5/glyphs/SNES_dialog_kanji.txt b/5/glyphs/SNES_dialog_kanji.txt new file mode 100644 index 0000000..5306635 --- /dev/null +++ b/5/glyphs/SNES_dialog_kanji.txt @@ -0,0 +1,426 @@ +王 +行 +力 +様 +飛 +世 +界 +封 +入 +城 +竜 +士 +船 +印 +海 +父 +人 +見 +魔 +物 +大 +者 +本 +何 +戦 +出 +気 +手 +言 +石 +守 +辺 +無 +心 +間 +前 +風 +帰 +私 +生 +年 +武 +器 +兵 +火 +使 +中 +急 +時 +森 +来 +砂 +動 +女 +待 +臣 +地 +助 +当 +今 +上 +悪 +泉 +騎 +思 +持 +変 +水 +塔 +草 +仲 +復 +目 +作 +分 +知 +機 +一 +姫 +最 +聞 +械 +神 +流 +乗 +取 +町 +殿 +事 +空 +勇 +村 +早 +子 +格 +納 +伝 +消 +書 +長 +話 +合 +所 +場 +脱 +北 +後 +全 +忍 +獣 +詩 +吟 +落 +自 +階 +説 +残 +親 +活 +休 +姉 +破 +土 +度 +記 +発 +赤 +侍 +死 +国 +旅 +完 +小 +林 +古 +図 +礼 +商 +島 +邪 +部 +狩 +精 +姿 +防 +向 +先 +解 +板 +下 +台 +賢 +対 +木 +成 +命 +配 +法 +飲 +回 +願 +門 +東 +開 +貸 +増 +危 +舘 +道 +身 +老 +西 +近 +層 +第 +青 +光 +読 +外 +理 +強 +同 +谷 +負 +意 +学 +攻 +屋 +体 +運 +河 +聖 +必 +南 +方 +黒 +絶 +食 +艇 +山 +穴 +名 +受 +暁 +傷 +少 +鼻 +倉 +然 +的 +男 +用 +酒 +安 +現 +代 +立 +具 +育 +続 +通 +会 +庫 +飼 +日 +窟 +砲 +広 +化 +博 +以 +兄 +室 +洞 +別 +番 +昔 +住 +吸 +去 +宝 +毒 +売 +好 +枝 +踊 +月 +巣 +底 +明 +感 +宿 +召 +喚 +決 +形 +団 +異 +初 +険 +家 +息 +連 +集 +墓 +著 +跡 +遺 +内 +悲 +教 +返 +頭 +再 +高 +借 +登 +走 +過 +敵 +鏡 +愛 +魚 +燃 +幅 +音 +血 +滝 +次 +閉 +求 +声 +箱 +軍 +泣 +港 +冊 +秒 +憶 +潜 +爆 +恋 +石 +左 +役 +放 +衛 +母 +建 +角 +信 +師 +元 +失 +字 +歩 +遊 +頂 +震 +収 +追 +暗 +橋 +起 +約 +束 +文 +反 +絵 +能 +歌 +弱 +境 +波 +針 +千 +炎 +望 +雨 +金 +希 +花 +不 +白 +剣 +陸 +勝 +烏 +笛 +究 +三 +探 +夜 +病 +半 +美 +敗 +友 +研 +予 +支 +々 +除 +数 +店 +翼 +充 +填 +薬 +弟 +経 +験 +値 +闘 +得 +闇 +混 +乱 +点 +速 +閃 +射 +斬 +鉄 +宣 +告 +電 +磁 +圧 +昇 +重 +倍 +妖 +刀 +拡 +散 +影 +御 +正 +漠 +特 +胸 +永 +遠 +議 +浮 +眠 +逃 +滅 +囗 +極 +湖 +利 +周 +緑 +切 +紙 +胸 +平 +和 +宇 +宙 +則 +孤 +溝 +  diff --git a/5/glyphs/SNES_small.txt b/5/glyphs/SNES_small.txt new file mode 100644 index 0000000..5b4fe13 --- /dev/null +++ b/5/glyphs/SNES_small.txt @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[stone] +[toad] +[mini] +[float] +[poison] +[KO] +[blind] + + + + + + + + + + + + + + + + + + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +_m +_H +_P +ハ +は +ヒ +ひ +フ +ふ +ヘ +へ +ホ +ほ +カ +か +キ +き +ク +く +ケ +け +コ +こ +サ +さ +シ +し +ス +す +セ +せ +ソ +そ +タ +た +チ +ち +ツ +つ +テ +て +ト +と +ウ +う +ア +あ +イ +い +エ +え +オ +お +ナ +な +ニ +に +ヌ +ぬ +ネ +ね +ノ +の +マ +ま +ミ +み +ム +む +メ +め +モ +も +ラ +ら +リ +り +ル +る +レ +れ +ロ +ろ +ヤ +や +ユ +ゆ +ヨ +よ +ワ +わ +ン +ん +ヲ +を +ッ +っ +ャ +ゃ +ュ +ゅ +ョ +ょ +ァ +ー +ィ +‥ +ぅ +! +ェ +? +ォ +% +/ +: +「 +」 +。 +A +B +X +Y +L +R +E +H +M +P +S +C +T +↑ +→ ++ +[洋剣] +[wh.mag] +[blk.mag] +🕒 +[刂] +[槍] +[鉞] +[刀] +[棒] +[杖] +[弓] +♪ +[鞭] +[bell] +[shield] +[helmet] +[armor] +[ring] + + + + + + + + + + + diff --git a/5/glyphs/SNES_small_RPGe.txt b/5/glyphs/SNES_small_RPGe.txt new file mode 100644 index 0000000..5f3ffab --- /dev/null +++ b/5/glyphs/SNES_small_RPGe.txt @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +[stone] +[toad] +[mini] +[float] +[poison] +[KO] +[blind] + + + + + + + + + + + + + + + + + + +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +_m +_H +_P +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +a +b +c +d +e +f +g +h +i +j +k +l +m +n +o +p +q +r +s +t +u +v +w +x +y +z +il +it + +li +ll +' +" +: +; +, +( +) +/ +! +? +. +ti +fi +Bl +a +pe +l +' +" +if +lt +tl +ir +tt +や +ユ +ゆ +ヨ +よ +ワ +わ +ン +ん +ヲ +を +[key] +[shoe] +◆ +[hammer] +⛺ +[ribbon] +[potion] +[shirt] +♪ +- +[shuriken] +‥ +[scroll] +! +[claw] +? +[glove] +% +/ +: +「 +」 +. +A +B +X +Y +L +R +E +H +M +P +S +C +T +↑ +→ ++ +[sword] +[wh.mag] +[blk.mag] +🕒 +[knife] +[spear] +[axe] +[katana] +[rod] +[staff] +[bow] +[harp] +[whip] +[bell] +[shield] +[helmet] +[armor] +[ring] + + + + + + + + + + + diff --git a/5/glyphs/SNES_small_dakuten.txt b/5/glyphs/SNES_small_dakuten.txt new file mode 100644 index 0000000..86852b5 --- /dev/null +++ b/5/glyphs/SNES_small_dakuten.txt @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +バ +ば +ビ +び +ブ +ぶ +ベ +べ +ボ +ぼ +ガ +が +ギ +ぎ +グ +ぐ +ゲ +げ +ゴ +ご +ザ +ざ +ジ +じ +ズ +ず +ゼ +ぜ +ゾ +ぞ +ダ +だ +ヂ +ぢ +ヅ +づ +デ +で +ド +ど +ヴ +パ +ぱ +ピ +ぴ +プ +ぷ +ペ +ぺ +ポ +ぽ +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +_m +_H +_P +ハ +は +ヒ +ひ +フ +ふ +ヘ +へ +ホ +ほ +カ +か +キ +き +ク +く +ケ +け +コ +こ +サ +さ +シ +し +ス +す +セ +せ +ソ +そ +タ +た +チ +ち +ツ +つ +テ +て +ト +と +ウ +う +ア +あ +イ +い +エ +え +オ +お +ナ +な +ニ +に +ヌ +ぬ +ネ +ね +ノ +の +マ +ま +ミ +み +ム +む +メ +め +モ +も +ラ +ら +リ +り +ル +る +レ +れ +ロ +ろ +ヤ +や +ユ +ゆ +ヨ +よ +ワ +わ +ン +ん +ヲ +を +ッ +っ +ャ +ゃ +ュ +ゅ +ョ +ょ +ァ +ー +ィ +‥ +ぅ +! +ェ +? +ォ +% +/ +: +「 +」 +。 +A +B +X +Y +L +R +E +H +M +P +S +C +T +↑ +→ ++ +[洋剣] +[wh.mag] +[blk.mag] +🕒 +[刂] +[槍] +[鉞] +[刀] +[棒] +[杖] +[弓] +♪ +[鞭] +[bell] +[shield] +[helmet] +[armor] +[ring] + + + + + + + + + + + diff --git a/5/sprite_blocks.json b/5/sprite_blocks.json new file mode 100644 index 0000000..612e127 --- /dev/null +++ b/5/sprite_blocks.json @@ -0,0 +1,189 @@ +{ + "NPCs sprites": { + "start": "0x1A0000", + "bpp": 4, + "definitions": [ + [2, 2, 2, "Crystal shards"], + [2, 2, 2, "sparkle"], + [2, 2, 2, "speech bubble exclamation"], + [2, 2, 2, "speech bubble question"], + [2, 2, 2, "speech bubble heart"], + [2, 2, 2, "binding barrier"], + [2, 2, 2, "wisp flame"], + [2, 2, 20, "lightning"], + [2, 2, 4, "sparkle glow"], + [2, 2, 2, "void demon that Leviathan kills (second palette)"], + [2, 2, 2, "void demon Triton/Nereid/Phobos"], + [2, 2, 2, "void demon Wendigo (second palette)"], + [2, 2, 2, "void demon Azulmagia (third palette)"], + [2, 2, 2, "void demon Catastrophe (third palette)"], + [2, 2, 1, "void demon Calofisteri"], + [2, 2, 1, "fallen soldier"], + [2, 2, 2, "lightning 2"], + [2, 2, 2, "sparkle glow 2"], + [2, 2, 2, "blob shadow"], + [2, 2, 2, "void demon Halicarnassus (second palette)"], + [2, 2, 2, "Faris hair blowing in the wind"], + [2, 4, 1, "rainbow stripes"], + [2, 2, 2, "pirate sleeping standing"], + [2, 2, 2, "Faris hair blowing in the wind 2"], + [2, 2, 2, "WingRaptor (2x2 wing, 2x2 body)"], + [2, 2, 1, "Melusine"], + [2, 2, 1, "jumping in shock"], + [2, 2, 1, "blank"], + [2, 2, 1, "fallen woman"], + [2, 2, 1, "fallen Cid"], + [2, 2, 1, "fallen Mid"], + [2, 2, 4, "Crystal upper"], + [2, 2, 4, "Crystal lower"], + [2, 2, 4, "fireball"], + [2, 2, 4, "sparkle glow 3"], + [2, 2, 4, "kite-shaped crystal"], + [2, 2, 4, "puff of smoke"], + [2, 2, 4, "fire geyser"], + [2, 2, 4, "flame in ball"], + [2, 2, 4, "growing sparkle"], + [2, 2, 4, "Abductor"], + [2, 2, 4, "Galuf glowing"], + [2, 2, 4, "Goblin?"], + [2, 2, 4, "kingly guy"], + [2, 2, 4, "Queen Karnak"], + [2, 2, 4, "bard"], + [2, 2, 4, "kid"], + [2, 2, 4, "old woman"], + [2, 2, 4, "merchant guy"], + [2, 2, 4, "sheep"], + [2, 2, 4, "girl"], + [2, 2, 2, "Garula"], + [2, 2, 2, "Archeoaevis"], + [2, 2, 4, "guy"], + [2, 2, 4, "turban guy"], + [2, 2, 4, "snake"], + [2, 2, 1, "mummy"], + [2, 2, 1, "small kid"], + [2, 2, 2, "blank"], + [2, 2, 4, "Magissa"], + [2, 2, 4, "Gilgamesh"], + [2, 2, 4, "Faris dress"], + [2, 2, 4, "Skeleton"], + [2, 2, 4, "cutscene bird"], + [2, 2, 4, "Omega"], + [2, 2, 8, "King Tycoon strip (some tiles not 2x2)"], + [2, 2, 8, "Pirate strip (some tiles not 2x2)"], + [2, 2, 8, "Boko strip (some tiles not 2x2)"], + [2, 2, 8, "Black Chocobo strip (some tiles not 2x2)"], + [2, 2, 8, "servant lady strip (some tiles not 2x2)"], + [2, 2, 8, "King Walse strip (some tiles not 2x2)"], + [2, 2, 8, "Chancellor strip (some tiles not 2x2)"], + [2, 2, 8, "Werewolf strip (some tiles not 2x2)"], + [2, 2, 8, "Moogle strip (some tiles not 2x2)"], + [2, 2, 8, "Kelger strip (some tiles not 2x2)"], + [2, 2, 8, "Xezat strip (some tiles not 2x2)"], + [2, 2, 8, "Ghido strip (some tiles not 2x2)"], + [2, 2, 8, "Dorgann strip (some tiles not 2x2)"], + [2, 2, 4, "Dwarf"], + [2, 2, 4, "Chicobo"], + [2, 2, 8, "kid strip (some tiles not 2x2)"], + [2, 2, 8, "woman strip (some tiles not 2x2)"], + [2, 2, 8, "old man with pointy hat strip (some tiles not 2x2)"], + [2, 2, 8, "scholar strip (some tiles not 2x2)"], + [2, 2, 8, "soldier strip (some tiles not 2x2)"], + [2, 2, 8, "dancer strip (some tiles not 2x2)"], + [2, 2, 8, "soldier 2 strip (some tiles not 2x2)"], + [2, 2, 8, "Cid strip (some tiles not 2x2)"], + [2, 2, 8, "Mid strip (some tiles not 2x2)"], + [2, 2, 6, "Black Chocobo flying"], + [2, 2, 2, "blank"], + [2, 2, 8, "Exdeath strip (some tiles not 2x2)"], + [2, 2, 16, "mini strip (some tiles not 2x2)"], + [2, 2, 16, "frog strip (some tiles not 2x2)"], + [2, 2, 16, "Bartz strip (some tiles not 2x2)"], + [2, 2, 16, "Lenna strip (some tiles not 2x2)"], + [2, 2, 16, "Galuf strip (some tiles not 2x2)"], + [2, 2, 16, "Faris strip (some tiles not 2x2)"], + [2, 2, 16, "Krile strip (some tiles not 2x2)"], + [2, 2, 1, "fallen King Tycoon"], + [2, 2, 1, "fallen Durgann?"], + [2, 2, 1, "fallen Queen Karnak"], + [2, 2, 19, "Syldra?"], + [2, 2, 1, "Dragongrass?"], + [2, 2, 1, "arrow"], + [2, 2, 2, "book"], + [2, 2, 1, "map"], + [2, 2, 1, "torn page"], + [2, 2, 4, "Dragongrass attack mode"], + [2, 4, 1, "Siren"], + [2, 2, 1, "fallen soldier 2"], + [2, 2, 1, "campfire sticks"], + [2, 2, 2, "dragon head"], + [2, 2, 1, "King Tycoon's Helmet"], + [2, 2, 1, "picture?"], + [2, 2, 1, "music box?"], + [2, 6, 1, "some fire pillar thing"], + [2, 2, 2, "bubbles"], + [2, 2, 1, "void demon Twintania"], + [2, 2, 1, "egg"], + [2, 2, 1, "pendant"], + [2, 2, 1, "elder branch"], + [2, 2, 1, "elder branch tiara"], + [2, 2, 1, "magic mirror?"], + [2, 2, 1, "piece of glass?"], + [2, 2, 1, "piece of adamantine?"], + [2, 2, 1, "bomb"], + [2, 2, 1, "vertical rope"], + [2, 2, 1, "antlion pincer?"], + [2, 2, 1, "elder branch 2???"], + [2, 2, 1, "stone tablet"], + [2, 2, 1, "void demon Apanda"], + [2, 4, 1, "gargoyle statue"], + [2, 2, 6, "Leviathan?"], + [2, 2, 1, "Sealed Weapon - Excalibur"], + [2, 2, 1, "Sealed Weapon - Assassin Dagger"], + [2, 2, 1, "Sealed Weapon - Sasuke's Katana"], + [2, 2, 1, "Sealed Weapon - Holy Lance"], + [2, 2, 1, "Sealed Weapon - Rune Axe"], + [2, 2, 1, "Sealed Weapon - Masamune"], + [2, 2, 1, "Sealed Weapon - Yoichi's Bow"], + [2, 2, 1, "Sealed Weapon - Fire Lash"], + [2, 2, 1, "Sealed Weapon - Sage Staff"], + [2, 2, 1, "Sealed Weapon - Magus Rod"], + [2, 2, 1, "Sealed Weapon - Apollo's Harp"], + [2, 2, 1, "Sealed Weapon - Gaia's Bell"], + [2, 2, 1, "Bartz tied up"], + [2, 2, 1, "Lenna tied up"], + [2, 2, 1, "Galuf tied up"], + [2, 2, 1, "void demon Necrophobe (palette 2)"], + [1, 1, 64, "Hiryuu tiles"] + ] + }, + "Overworld / World map sprites": { + "start": "0x1B3A00", + "bpp": 3, + "definitions": [ + [2, 2, 1, "Tent"], + [2, 2, 1, "Cabin"], + [1, 1, 1, "shadow large"], + [1, 1, 1, "shadow medium"], + [1, 1, 1, "shadow small"], + [1, 1, 1, "question mark"], + [1, 1, 1, "up arrow"], + [1, 1, 1, "plus large"], + [1, 1, 1, "plus small"], + [1, 1, 1, "x small"], + [2, 2, 6, "Ship"], + [2, 2, 6, "Hiryuu"], + [2, 2, 6, "Steamship"], + [2, 2, 6, "Airship"], + [2, 2, 6, "Submarine underwater"], + [2, 2, 6, "Submarine surface"], + [2, 2, 6, "Airship/Submarine transformation"] + ] + }, + "Overworld sprites": { + "start": "0x1B4D80", + "bpp": 4, + "definitions": [ + [2, 1, 202, "TBC"] + ] + } +} \ No newline at end of file diff --git a/5/string_blocks.tsv b/5/string_blocks.tsv new file mode 100644 index 0000000..a9f5926 --- /dev/null +++ b/5/string_blocks.tsv @@ -0,0 +1,15 @@ +name num_entries address snes_address bytes snes_bytes rpge_ptr_offset snes_ptr_offset dialog null_terminated +ability_names 33 0x116200 8 +battle_commands 96 0x201150 0x115800 7 5 +character_names 5 0x115500 6 +dialogue 0x900 0x2013F0 0x082220 3 2 0x000000 0x0A0000 True +enemy_names 384 0x200050 0x105C00 10 8 +items 0x100 0x111380 9 +item_long_names 0x120 0x275860 0x111380 24 9 +item_descriptions 128 0x114000 2 0x110000 0x110000 True True +job_names 22 0x115600 8 +job_and_ability_descs 133 0x117140 2 0x110000 0x110000 True +magics 87 0x111C80 6 +magics2 73 0x111E8A 9 +menu_strings 139 0x00F987 2 0x270000 0x000000 True +zone_names 164 0x107000 2 0x270000 0x107200 True \ No newline at end of file diff --git a/5/structs/SNES.tsv b/5/structs/SNES.tsv new file mode 100644 index 0000000..650f01c --- /dev/null +++ b/5/structs/SNES.tsv @@ -0,0 +1,165 @@ +struct AttackType +u1 is_blue_magic +u1 is_white_magic +u1 is_black_magic +u1 is_dimension_magic +u1 is_summon +u1 is_sound +u1 is_longrange +u1 is_physical + +struct AttackProperties +u2 action_delay +u1 roulette +u1 target_enemy_by_default +u1 can_target_either_side +u1 can_select_target +u1 always_target_all +u1 can_target_all + +struct WeaponData +AttackProperties attack_properties +AttackType attack_type +u6 item_type +u1 is_unthrowable +u1 byte_2_leftover +EquipStatBonus bonus +u6 description +u1 can_double_grip +u1 only_double_grip +u1 is_wonder_rod +u1 is_action_on_use +u1 can_magic_sword +u1 is_action_on_hit +u1 is_5.4 +u1 is_initiative +u1 is_knife_parry +u1 is_sword_parry +u7 spell_cast +u1 is_break_after_use +u8 attack_power +u7 attack_effect +u1 byte_8_leftover +u8 param0 # accuracy_percent for sub-100 weapons, crit rate on katanas, element on Flametongue/Icebrand/Excalibur/AirKnife/Trident/WindSpear +u8 param1 # on_hit_effect_percent for weapons that cast actions on hit, also Rune weapons bonus attack power +u8 param2 # action_on_hit for weapons that cast actions on hit, Also Rune weapons 5 mana cost on attack, also element of attack for rods + +struct ArmorData +u8 slot # b0 - 0x01 acc, 0x02 body, 0x04 head, 0x08 shield, 0xF0 unused +u8 weight # b1 +u6 item_type +u1 is_unthrowable +u1 byte_2_leftover +EquipStatBonus bonus # b3 +u6 description # b4 +u2 byte_4_leftovers +u1 is_improve_catch # b5 +u1 is_become_undead +u1 is_improve_dance +u1 is_halve_mp_cost +u1 is_improve_steal +u1 is_improve_brawl +u1 is_elf_cape_dodge +u1 is_block_all_magic # b5 +u8 evasion # b6 +u8 defense +u8 m_evasion +u8 m_def # b9 +u8 elem_effect # b10 - 01 ½ lightning, 02 ½ all, 03 immune poison damage, 04 absorb fire+immune ice+weak water, 05 absorb water+immune fire+weak lightning, 06 Bone Mail (absorb poison, halve ice, weak fire/holy), 07 absorb fire, 08 absorb ice +u8 status_effect # b11 - 01 auto-regen, 02 auto-doom, 03 auto-haste + immune slow/stop/paralyse/sleep, 04 immune petrify, 05 Ribbon, 06 immune confuse, 07 immune silence, 08 immune blind, 09 immune poison, 0A bone mail, 0B auto-reflect, 0C immune mini, 0D immune old/zombie, 0E auto-sap + immune sleep, 0F auto-blink, 10 immune mini/paralyse, 11 immune confuse/mini, 12 immune confuse/toad, 13 immune toad/paralyse + + +struct ItemData +AttackProperties attack_properties +u8 attack_type +u1 unk0 +u1 is_unmixable +u1 unk1 +u1 is_magic_lamp +u1 is_undrinkable +u1 is_unusable_in_battle +u1 is_unthrowable +u1 unk2 +u6 description # b3 +u2 byte_3_leftovers +u7 attack_formula +u1 is_unavoidable +u8 param_0 +u8 param_1 +u8 param_2 + + +struct MagicData +AttackProperties attack_properties +u8 attack_type +u4 meteo_extra_hits +u2 unk +u1 is_learnable +u1 is_monster_bit +u7 mp_cost +u1 is_unreflectable +u7 attack_formula +u1 is_unavoidable +u8 param_0 +u8 param_1 +u8 param_2 + + +struct ElemDef +u8 absorb +u8 evade +u8 immune +u8 half +u8 weak + +struct StatusEffect +4 of u8 initial +3 of u8 immune + + +struct EnemySpriteData +u7 tileset_offset_hi # combined with lo, << 3, + 0x150000 +u1 is_3bpp +u8 tileset_offset_lo +u2 palette_offset_hi # combined with lo, << 4, + 0x0ED000 +u4 unk +u1 is_separate_shadow +u1 is_large_layout +u8 palette_offset_lo +u8 layout_id # Small? <<3, + 0x10D004, take 8 bytes. Large? <<5, + 0x10D334, take 32 bytes. + +struct BattleBackgroundData +u8 tileset_id +2 of u8 palette_ids +u8 tilemap_id +u8 tilemap_flips_id +u8 tilemap_v_flips_id # Unused, all 0xFF = no flips +u8 tilecycle_id +u8 palcycle_id + +struct EnemyStats +u8 speed +u8 attack_power +u8 attack_multiplier +u8 evasion +u8 defense +u8 magic_power +u8 magic_defense +u8 magic_evasion +u16 max_hp +u16 max_mp +u16 reward_exp +u16 reward_gil +u8 attack_graphics +u8 elements_immune +u24 status_immune +u8 elements_absorb +AttackType attack_type +u8 elements_weak +u8 monster_type +u8 flags_25 +u16 status_26 +u16 status_28 +u8 29 +u8 30 +u8 level # 31 diff --git a/5/structs/SNES_save.tsv b/5/structs/SNES_save.tsv new file mode 100644 index 0000000..c304524 --- /dev/null +++ b/5/structs/SNES_save.tsv @@ -0,0 +1,161 @@ +struct Character +u3 character_id # 0=Bartz, 1=Lenna, 2=Galuf, 3=Faris, 4=Krile, 5/6/7 unused +u1 is_female # 0=male, 1=female +u2 unk0 # Two unknown, possibly unused bits +u1 is_absent # 0=present, 1=absent +u1 is_back_row +u8 current_job_id +u8 level +u24 experience +u16 hp_current +u16 hp_max +u16 mp_current +u16 mp_max +u8 equipped_head +u8 equipped_body +u8 equipped_acc +u8 equipped_rh_shield +u8 equipped_lh_shield +u8 equipped_rh_weapon +u8 equipped_lh_weapon +u8 caught_monster +u8 ability_1 +u8 ability_2 +u8 ability_3 +u8 ability_4 +u8 status_1 +u8 status_2 +u8 status_3 +u8 status_4 +u8 action_flags +u8 damage_mod +u16 innates +u8 magic_element_up +u8 equip_weight +u8 base_strength +u8 base_agility +u8 base_stamina +u8 base_magic +u8 current_strength +u8 current_agility +u8 current_stamina +u8 current_magic +u8 evasion +u8 defense +u8 magic_evasion +u8 magic_defense +u8 elemental_absorb +u8 elemental_evade +u8 elemental_immune +u8 elemental_half +u8 elemental_weakness +u8 resistance_status_1 +u8 resistance_status_2 +u8 resistance_status_3 +u8 specialty_weapon +u8 specialty_equipment +u8 current_job_level +u16 current_job_abp +u8 spell_level_1 +u8 spell_level_2 +u8 spell_level_3 +u32 equipment_category +u16 attack +u8 attack_id_reaction_unused +u8 unk1 +u8 unk2 +u8 unk3 +u8 freelancer_strength +u8 freelancer_agility +u8 freelancer_stamina +u8 freelancer_magic +u16 freelancer_innates + +struct Job_progress +u12 abp +u4 level + +struct Config +u3 battle_speed # 0=1 in-game, ..., 5=6 in-game +u1 is_wait_mode # 0=active, 1=wait??? +u3 message_speed # 0=1 in-game, ..., 5=6 in-game +u1 command_set # 0=window, 1=shortcut +u5 menu_color_r +u5 menu_color_g +u5 menu_color_b +u1 padding +u1 reequip_mode # 0=optimum, 1=empty +u1 is_mono # 0=stereo, 1=mono +u1 is_memory_cursor # 0=reset, 1=memory +u4 unk0 +u1 hide_atb_gauge # 0=show, 1=hide +u6 unk1 +u1 is_controller_custom # 0=no, 1=yes +u1 is_controller_2p # 0=no, 1=yes +u8 button_A # Bit of action +u8 button_B # Bit of action +u8 button_X # Bit of action +u8 button_Y # Bit of action +u8 button_L # Bit of action +u8 button_R # Bit of action +u8 button_Select # Bit of action +4 of u8 character_player_nums # 0=controller 1, 1=controller 2 +4 of 4 of u8 character_shortcut_commands # 0=ability_1, 1=ability_2, 2=ability_3, 3=ability_4 + +struct Vehicle +u2 mode_switching +u3 movement_type +u3 map_id +u7 unk0 +u1 is_hidden # 0=show, 1=hide +u8 x +u8 y + +struct Save_slot +4 of Character characters +256 of u8 inventory_item_ids +256 of u8 inventory_item_qtys +u24 unlocked_jobs +4 of 22 of Job_progress character_jobs_progress +4 of u8 character_abilities_learned_count +4 of 20 of u8 character_abilities_learned +u24 current_gil +u32 game_time_frames +u16 num_enemies_defeated +32 of u8 magic_learned +Config config +5 of 6 of u8 character_names # Bartz, Lenna, Galuf, Faris, Krile. Dialog is hardcoded for everyone except Bartz's name anyway... +6 of u8 unk0 +u8 magic_lamp_next_summon +u8 num_battles_escaped # Brave Blade vs Chicken Knife +u8 wonder_rod_magic +9 of u8 unk1 +u16 num_total_battles +u16 num_times_saved +u8 last_battle_results # 0=victory, 1=game over, 2=escaped +15 of u8 flags_battle_events +32 of u8 flags_treasure_opened +32 of u8 unk_probably_still_flags_treasure_opened +96 of u8 flags_events # RAM map mentions $D8E000. This is likely critical to story progression and scripting. +96 of u8 unk_probably_still_flags_events +u16 map_id_inner +u16 map_id_world +u8 pos_x +u8 pos_y +u8 current_character_sprite +u8 current_character_facing +u8 current_vehicle +Vehicle veh_chocobo +Vehicle veh_black_chocobo +Vehicle veh_hiryuu +Vehicle veh_submarine +Vehicle veh_steamship +Vehicle veh_airship +u16 teleport_map_id +u8 teleport_map_x +u8 teleport_map_y +u8 initial_seed +u8 walking_speed # 0=normal, 1=double (fast), 80=half (slow) +u8 timed_event_active +u16 timed_event_timer +u16 timed_event_end diff --git a/5/structs/SNES_stubs.tsv b/5/structs/SNES_stubs.tsv new file mode 100644 index 0000000..800214b --- /dev/null +++ b/5/structs/SNES_stubs.tsv @@ -0,0 +1,9 @@ +struct ItemCost # All definitions here are intended to be overriden in code, but provide correctly-sized stubs for programs that don't care +u1 is_sellable +u1 is_sellprice_5gil +u6 exponent +u8 mantissa # buy_price = mantissa * int(pow(10, exponent)); sell_price = -1 if not is_sellable else (5 if is_sellprice_5gil else buy_price/2) + +struct EquipStatBonus +u1 is_stat_bonus # 0 -> data is elemental bonus bitfield; 1 -> 4bit toggle fields for affected stats, 3bit selector of bonus enum +u7 data # Meaning changes based on is_stat_bonus diff --git a/reference_implementation.py b/reference_implementation.py new file mode 100644 index 0000000..ab4d346 --- /dev/null +++ b/reference_implementation.py @@ -0,0 +1,283 @@ +# This is a reference implementation of parsing, serializing, and deserializing ChocolateBird's struct definitions. +# It is ported from the original GDscript to Python. +import logging +from io import TextIOWrapper +from struct import pack_into, unpack_from, calcsize +from collections.abc import Buffer + + +class LeftoverBits: + number_bits = 0 + bit_buffer = 0 # Stored as an integer + + +class ReadBuffer: + backing_buffer: Buffer + position: int = 0 + + def __init__(self, backing_buffer, position: int = 0) -> None: + self.backing_buffer = backing_buffer + self.position = position + + def get(self, format) -> tuple: + values = unpack_from(format, self.backing_buffer, self.position) + self.position += calcsize(format) # TODO: cache this + return values + + +class WriteBuffer: + backing_buffer: Buffer + position: int = 0 + + def __init__(self, backing_buffer, position: int = 0) -> None: + self.backing_buffer = backing_buffer + self.position = position + + def put(self, format, *values) -> None: + pack_into(format, self.backing_buffer, self.position, *values) + self.position += calcsize(format) # TODO: cache this + + +class StructType: + name: str = None + def __repr__(self) -> str: + if self.name: + return self.name + return super().__repr__() + + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + raise NotImplementedError('Deserialization not implemented') + def put_value(self, buffer: WriteBuffer, value, leftover_bits: LeftoverBits): + raise NotImplementedError('Serialization not implemented') + + +class SimpleStruct(StructType): + format: str + def __init__(self, format, name=None) -> None: + self.format = format + self.name = name + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + return buffer.get(self.format)[0] + def put_value(self, buffer: WriteBuffer, value, leftover_bits: LeftoverBits): + buffer.put(self.format, value) + + +class U24(StructType): + name = 'u24' + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + u16, u8 = buffer.get('> 16) + + +class S24(StructType): + name = 's24' + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + u16, u8 = buffer.get('> 16) + + +class UBits(StructType): + bits = 8 + + def __init__(self, bits: int): + self.bits = bits + self.name = f'u{bits:d}' + + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + while leftover_bits.number_bits < self.bits: + leftover_bits.bit_buffer |= buffer.get('> self.bits + leftover_bits.number_bits -= self.bits + return value + + def put_value(self, buffer: WriteBuffer, value, leftover_bits: LeftoverBits): + leftover_bits.bit_buffer |= value << leftover_bits.number_bits + leftover_bits.number_bits += self.bits + while leftover_bits.number_bits >= 8: + buffer.put('> 8 + + +class Struct(StructType): + members: list # Array of [name, StructType] + + def __init__(self, name=None) -> None: + self.name = name + self.members = [] + + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits): + return {key: struct_type.get_value(buffer, leftover_bits) for key, struct_type in self.members} + + def put_value(self, buffer: WriteBuffer, value, leftover_bits: LeftoverBits): + for key, struct_type in self.members: + if not (key in value): + logging.error(f'Key "{key}" missing from value supplied') + return + struct_type.put_value(buffer, value[key], leftover_bits) + + +class StructArrayType(StructType): + count: int + struct_type: StructType + + def __init__(self, count, struct_type, name=None) -> None: + self.count = count + self.struct_type = struct_type + self.name = name + + def get_value(self, buffer: ReadBuffer, leftover_bits: LeftoverBits) -> list: + return [self.struct_type.get_value(buffer, leftover_bits) for i in range(self.count)] + + def put_value(self, buffer: WriteBuffer, value, leftover_bits: LeftoverBits) -> None: + if len(value) < self.count: + logging.error('Not enough values supplied') + return + for i in range(self.count): + self.struct_type.put_value(buffer, value[i], leftover_bits) + + +def get_base_structarraytypes() -> dict: + return { + 'u8': SimpleStruct(' 0: + inner_type = UBits(b) + existing_structs[f'u{b:d}'] = inner_type # Cache it for future use + if not inner_type: + logging.error(f'typestring "{type}" has no matches for "{t}" in existing structs') + return + + l: int = len(tokens) + if l == 1: + return inner_type + # Our parsing goal is to turn 'a of b of c of d' into StructArrayType, b>, a> + # Our strategy is to parse backwards over the tokens, changing inner_type at each point + # a of b of c of (d) + # a of b of (c of d) + # a of (b of c of d) + # (a of b of c of d) + # done + i: int = l-2 + while i > -1: + match tokens[i]: + case 'of': + i -= 1 + l1: int = int(tokens[i]) + if l1 > 1: + inner_type = StructArrayType(l1, inner_type, name=type) # Might be worth caching these later on if we use them more + i -= 1 + case k: + logging.error(f'Invalid keyword used in type designator: "{k}"') + return + return inner_type + + +def parse_struct_definitions_from_tsv_file(tsv_file: TextIOWrapper, existing_structs: dict) -> None: + current_struct: Struct + lines = tsv_file.read().rstrip().split('\n') + for line in lines: + # logging.debug(line) + tokens = line.split('\t') + size = len(tokens) + if size < 2: + continue + # Size is at least 2 + type, label = tokens[:2] + if type == 'struct': + # New struct declaration + current_struct = Struct(name=label) + existing_structs[label] = current_struct + elif type and label: + current_struct.members.append([label, get_structarraytype(type, existing_structs)]) + # TODO: Maybe store the trailing comments somewhere? + + +def parse_struct_definitions_from_tsv_filename(filename: str, existing_structs: dict) -> None: + with open(filename, 'r') as file: + parse_struct_definitions_from_tsv_file(file, existing_structs) + + +def load_ff5_snes_struct_definitions() -> dict: + existing_structs = get_base_structarraytypes() + parse_struct_definitions_from_tsv_filename('structs_SNES_stubs.tsv', existing_structs) + parse_struct_definitions_from_tsv_filename('5/structs/SNES_stubs.tsv', existing_structs) + parse_struct_definitions_from_tsv_filename('5/structs/SNES.tsv', existing_structs) + parse_struct_definitions_from_tsv_filename('5/structs/SNES_save.tsv', existing_structs) + return existing_structs + + +# Basic TSV dumper +# This is mostly unrelated, but helpful for debugging. +def flatten_keys(d: dict, prefix: str = '') -> dict: + output = {} + for k, v in d.items(): + if isinstance(v, dict): + flat = flatten_keys(v, f'{prefix}{k}.') + for k2, v2 in flat.items(): + output[k2] = v2 + else: + output[f'{prefix}{k}'] = v + return output + + +def dump_tsv(filename, table): + table_flat = [flatten_keys(d) for d in table] + hex_digits = len(f'{len(table_flat)-1:X}') # See how long the hex representation of the last number will be, so we can zero-pad the rest to match. + hex_format = f'0{hex_digits}X' + + with open(filename, 'w') as file: + headers = list(table_flat[0].keys()) + file.write('\t'.join(['ID'] + headers) + '\n') + for i, entry in enumerate(table_flat): + file.write('\t'.join([f'0x{i:{hex_format}}'] + [str(entry[key]) for key in headers]) + '\n') + + +# Example usage: run this script with a positional filename argument for a FF5 .sfc and it will load EnemyStats from it. +if __name__ == '__main__': + existing_structs = load_ff5_snes_struct_definitions() + leftover_bits = LeftoverBits() + + from sys import argv + if len(argv) > 1: + rom_filename = argv[1] + if rom_filename.endswith('.sfc'): + with open(rom_filename, 'rb') as file: + rom_data = file.read() + buffer = ReadBuffer(rom_data, 0x100000) + # enemy_stats = existing_structs['EnemyStats'].get_value(buffer, leftover_bits) + enemy_stats = get_structarraytype('384 of EnemyStats', existing_structs).get_value(buffer, leftover_bits) + print('Loaded EnemyStats table to enemy_stats! If you ran the python interpreter with -i flag, you can now examine it in the REPL.') + # Also add a basic dumper + if len(argv) > 2 and argv[2] == 'dump_tsv': + dump_tsv('enemy_stats.tsv', enemy_stats) + print('Dumped enemy_stats table to enemy_stats.tsv!') + else: + print(f'Argument "{rom_filename}" doesn\'t end in ".sfc", so it was not parsed as a FF5 SNES ROM.') + else: + print('Example usage: run this script with a positional filename argument for a FF5 .sfc and it will load EnemyStats from it.') diff --git a/structs_SNES_stubs.tsv b/structs_SNES_stubs.tsv new file mode 100644 index 0000000..90e3fdd --- /dev/null +++ b/structs_SNES_stubs.tsv @@ -0,0 +1,29 @@ +struct TileSNESMode7 # All definitions here are intended to be overriden in code, but provide correctly-sized stubs for programs that don't care +64 of u8 data + +struct TileSNESMode7c +32 of u8 data + +struct TileSNES4bpp +32 of u8 data + +struct TileSNES3bpp +24 of u8 data + +struct TileSNES2bpp +16 of u8 data + +struct TileSNES1bpp +8 of u8 data + +struct SNESTritile +24 of u8 data + +struct ColorBGR555 +u16 data + +struct Palette16Of555 # Typed PoolColorArray in GDscript +16 of ColorBGR555 data + +struct Palette128Of555 # Typed PoolColorArray in GDscript +128 of ColorBGR555 data