Use our save struct DSL for some ROM things

This commit is contained in:
Luke Hubmayer-Werner 2023-08-07 01:17:45 +09:30
parent c0dff664eb
commit 877c5d90e4
6 changed files with 127 additions and 23 deletions

View File

@ -1,23 +1,24 @@
Label SNES PSX_file PSX_offset format Comment
locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80
worldmap_blocks 0x0FF0C0 /nar/ff5_binx.bin 0x040300
worldmap_tile_palettes 0x0FF9C0 /nar/ff5_bin3.bin 0x03FB00
worldmap_palettes 0x0FFCC0 /nar/ff5_binx.bin 0x040000
worldmap_tiles 0x1B8000 /nar/ff5_bin3.bin 0x039B00
character_battle_sprite_tiles 0x120000 /mnu/men_bin.eng 0x010200
character_battle_sprite_palettes 0x14A3C0 /btl/ff5_btl.bin 0x0273C0 Also /mnu/men_bin.eng:0x03A5C0
character_battle_sprite_layouts 0x14B997 /btl/ff5_btl.bin 0x028997
character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034
character_battle_sprite_stone_palette 0x00F807 N/A N/A Also 0x199835
tiles_fist 0x11D710 /btl/ff5_btl.bin 0x021D10 Also /mnu/men_bin.eng:0x00D910
tbl_weapons 0x110000 length 0x600
tbl_armors 0x110600 length 0x480
tbl_items 0x110A80 length 0x100
tbl_magic 0x110B80 length 0x800
tbl_equip_types 0x112480 64 of ... length 0x100 - Item Equipment type definitions (64x4 bytes, 2B weapon, 2B armor)
tbl_armors_elem_def 0x112580 64 of ... length 0x140 - Item Armor Element defense 64x(5B - absorb, evade, immunity, half, weakness)
tbl_armors_status 0x1126C0 64 of ... length 0x1C0 - Item Armor Status defense 64x(7B - 4B Initial, 3B Immune)
tbl_item_magic_prices 0x112A00 length 0x300
locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of Palette128Of555
worldmap_blocks 0x0FF0C0 /nar/ff5_binx.bin 0x040300 3 of 192 of 4 of u8
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
worldmap_tiles 0x1B8000 /nar/ff5_bin3.bin 0x039B00 3 of 256 of TileSNESMode7c Add the biases
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
character_battle_sprite_layouts 0x14B997 /btl/ff5_btl.bin 0x028997 11 of 6 of u8
character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 Palette16Of555
character_battle_sprite_stone_palette 0x00F807 N/A N/A Palette16Of555 Also 0x199835
tiles_fist 0x11D710 /btl/ff5_btl.bin 0x021D10 TileSNES3bpp Also /mnu/men_bin.eng:0x00D910
tbl_weapons 0x110000 128 of 12 of u8 length 0x600
tbl_armors 0x110600 96 of 12 of u8 length 0x480
tbl_items 0x110A80 32 of 8 of u8 length 0x100
tbl_magic 0x110B80 256 of 8 of u8 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 5 of u8 length 0x140 - Item Armor Element defense 64x(5B - absorb, evade, immunity, half, weakness)
tbl_armors_status 0x1126C0 64 of 7 of u8 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

1 Label SNES PSX_file PSX_offset format Comment
2 locations_bg_palettes 0x03BB00 /nar/ff5_binx.bin 0x03BF80 43 of Palette128Of555
3 worldmap_blocks 0x0FF0C0 /nar/ff5_binx.bin 0x040300 3 of 192 of 4 of u8
4 worldmap_tile_palettes worldmap_tiles.bias 0x0FF9C0 /nar/ff5_bin3.bin 0x03FB00 3 of 256 of u8 Add to each pixel of the mode7c tiles
5 worldmap_palettes 0x0FFCC0 /nar/ff5_binx.bin 0x040000 3 of Palette128Of555
6 worldmap_tiles 0x1B8000 /nar/ff5_bin3.bin 0x039B00 3 of 256 of TileSNESMode7c Add the biases
7 character_battle_sprite_tiles 0x120000 /mnu/men_bin.eng 0x010200 5 of 22 of 48 of TileSNES4bpp
8 character_battle_sprite_palettes 0x14A3C0 /btl/ff5_btl.bin 0x0273C0 5 of 22 of Palette16Of555 Also /mnu/men_bin.eng:0x03A5C0
9 character_battle_sprite_layouts 0x14B997 /btl/ff5_btl.bin 0x028997 11 of 6 of u8
10 character_battle_sprite_disabled_palette 0x00F867 /mnu/memsave.bin 0x000034 Palette16Of555
11 character_battle_sprite_stone_palette 0x00F807 N/A N/A Palette16Of555 Also 0x199835
12 tiles_fist 0x11D710 /btl/ff5_btl.bin 0x021D10 TileSNES3bpp Also /mnu/men_bin.eng:0x00D910
13 tbl_weapons 0x110000 128 of 12 of u8 length 0x600
14 tbl_armors 0x110600 96 of 12 of u8 length 0x480
15 tbl_items 0x110A80 32 of 8 of u8 length 0x100
16 tbl_magic 0x110B80 256 of 8 of u8 length 0x800
17 tbl_equip_types 0x112480 64 of ... 64 of 4 of u8 length 0x100 - Item Equipment type definitions (64x4 bytes, 2B weapon, 2B armor)
18 tbl_armors_elem_def 0x112580 64 of ... 64 of 5 of u8 length 0x140 - Item Armor Element defense 64x(5B - absorb, evade, immunity, half, weakness)
19 tbl_armors_status 0x1126C0 64 of ... 64 of 7 of u8 length 0x1C0 - Item Armor Status defense 64x(7B - 4B Initial, 3B Immune)
20 tbl_item_magic_prices tbl_prices_items 0x112A00 256 of ItemCost length 0x300 length 0x200
21 tbl_prices_magic 0x112C00 128 of ItemCost length 0x100
22 tbl_charlevels_exp 0x115000 99 of u24
23 tbl_charlevels_hp_base 0x115129 99 of u16
24 tbl_charlevels_mp_base 0x1151EF 99 of u16

View File

@ -1,5 +1,8 @@
extends Node
const STRUCT := preload('res://scripts/struct.gd')
const STRUCT_SNES := preload('res://scripts/loaders/snes/structs.gd')
var structdefs := {}
const loader_cd_image := preload('res://scripts/loaders/cd/image.gd')
var psx_productcode_regex := RegEx.new()
const psx_ff5_productcodes = [
@ -13,15 +16,36 @@ var ROM_filename := 'FF5_SCC_WepTweaks_Inus_Dash.sfc' # 'Final Fantasy V (Japan
var GBA_filename := '2564 - Final Fantasy V Advance (U)(Independent).gba'
var rom_snes := File.new()
var snes_data := {}
var thread := Thread.new()
func load_snes_rom(filename: String):
var error := rom_snes.open(filename, File.READ)
if error == OK:
# Copy entire SNES ROM to a buffer for StreamPeerBuffer usage.
# Unfortunately, the File API is different and slightly worse than the StreamPeer API.
var bytes := rom_snes.get_buffer(rom_snes.get_len())
var buffer = StreamPeerBuffer.new()
buffer.data_array = bytes
SpriteLoader.load_snes_rom(rom_snes)
MapLoader.load_snes_rom(rom_snes)
StringLoader.load_snes_rom(rom_snes, true)
# Don't do this concurrently, avoid file pointer conflicts
var _thread_error = thread.start(SoundLoader, 'parse_rom', rom_snes)
# Can concurrently work with the preloaded StreamPeerBuffer though
for key in Common.SNES_PSX_addresses:
var d = Common.SNES_PSX_addresses[key]
if d.format:
var s: STRUCT.StructType
if d.format in structdefs:
s = structdefs[d.format]
else:
s = STRUCT.get_structarraytype(d.format, structdefs)
structdefs[d.format] = s
if not s:
assert(false, 'Invalid StructType: "%s"' % d.format)
buffer.seek(d.SNES)
snes_data[key] = s.get_value(buffer, [0, 0])
func load_psx_folder(_dirname: String):
pass
@ -42,6 +66,9 @@ func load_psx_image(filename: String):
func _ready():
structdefs.merge(STRUCT.get_base_structarraytypes())
structdefs.merge(STRUCT_SNES.get_structtypes())
STRUCT.parse_struct_definitions_from_tsv_filename('res://data/SNES_save.tsv', structdefs)
var _error := psx_productcode_regex.compile('(S[A-Z]{3}_\\d{3}\\.\\d{2});(\\d)')
load_snes_rom(ROM_filename)

View File

@ -143,7 +143,7 @@ func snes_load_worldmap(rom: File):
worldmap_palette_textures.append(texture_from_image(image))
var tile_palettes = []
rom.seek(Common.SNES_PSX_addresses['worldmap_tile_palettes']['SNES'] + (world_ts*0x100))
rom.seek(Common.SNES_PSX_addresses['worldmap_tiles.bias']['SNES'] + (world_ts*0x100))
for pal in 256:
tile_palettes.append(rom.get_8())
@ -264,7 +264,7 @@ func snes_load_map_sprites(rom: File):
func load_snes_rom(rom: File):
snes_load_battle_sprites(rom)
snes_load_worldmap(rom)
snes_load_map_sprites(rom)
# snes_load_map_sprites(rom)
const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB'

View File

@ -73,3 +73,11 @@ static func get_tile(rom: File, offset: int, length: int) -> Image:
return _2plane_to_tile(data)
_:
return _1plane_to_tile(data)
static func bgr555_to_color(short: int) -> Color:
var color = Color()
color.a = 1
color.r = ((short & 0x1F) / 31.0)
color.g = (((short >> 5) & 0x1F) / 31.0)
color.b = (((short >> 10) & 0x1F) / 31.0)
return color

View File

@ -0,0 +1,68 @@
#warning-ignore-all:shadowed_variable
#warning-ignore-all:unused_argument
const STRUCT := preload('res://scripts/struct.gd')
const GRAPHICS := preload('res://scripts/loaders/snes/graphics.gd')
class TileSNESMode7 extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS.mode7_to_tile(buffer.get_data(64))
class TileSNESMode7c extends STRUCT.StructType:
# Note that this will give a palette offset of 0, you will need to postprocess the values.
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS.mode7_compressed_to_tile(buffer.get_data(32)[1])
class TileSNES4bpp extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS._4plane_to_tile(buffer.get_data(32)[1])
class TileSNES3bpp extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS._3plane_to_tile(buffer.get_data(24)[1])
class TileSNES2bpp extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS._2plane_to_tile(buffer.get_data(16)[1])
class TileSNES1bpp extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS._1plane_to_tile(buffer.get_data(8)[1])
class ColorBGR555 extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return GRAPHICS.bgr555_to_color(buffer.get_u16())
class PaletteOf555 extends STRUCT.StructType:
var length: int = 16
func _init(length: int):
self.length = length
func get_value(buffer: StreamPeer, leftover_bits: Array):
var array := PoolColorArray()
for i in self.length:
array.append(GRAPHICS.bgr555_to_color(buffer.get_u16()))
return array
class ItemCost extends STRUCT.StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
var b0 := buffer.get_u8()
var mantissa := buffer.get_u8()
var exponent := b0 & 0x3F
var is_sellable := (b0 & 0x80) == 0
var is_sellprice_5gil := (b0 & 0x40) == 0x40
var buy_price := mantissa * int(pow(10, exponent))
var sell_price := -1 if not is_sellable else (5 if is_sellprice_5gil else buy_price/2)
return {'buy_price': buy_price, 'sell_price': sell_price}
static func get_structtypes() -> Dictionary:
return {
'TileSNESMode7': TileSNESMode7.new(),
'TileSNESMode7c': TileSNESMode7c.new(),
'TileSNES4bpp': TileSNES4bpp.new(),
'TileSNES3bpp': TileSNES3bpp.new(),
'TileSNES2bpp': TileSNES2bpp.new(),
'TileSNES1bpp': TileSNES1bpp.new(),
'ColorBGR555': ColorBGR555.new(),
'Palette16Of555': PaletteOf555.new(16),
'Palette128Of555': PaletteOf555.new(128),
'ItemCost': ItemCost.new(),
}

View File

@ -4,9 +4,9 @@
# array is used for reference semantics as get and put operations may mutate it
class StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):
return
assert(false, 'Deserialization not implemented')
func put_value(buffer: StreamPeer, value, leftover_bits: Array):
return
assert(false, 'Serialization not implemented')
class U8 extends StructType:
func get_value(buffer: StreamPeer, leftover_bits: Array):