[WIP] Cross-reference PSX offsets

This commit is contained in:
Luke Hubmayer-Werner 2023-07-31 23:33:05 +09:30
parent 4a1e2c2171
commit 9d36e2afb5
6 changed files with 159 additions and 124 deletions

View File

@ -0,0 +1,12 @@
Label SNES PSX 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 Also 0x199835
tiles_fist 0x11D710 /btl/ff5_btl.bin:0x021D10 Also /mnu/men_bin.eng:0x00D910
Can't render this file because it has a wrong number of fields in line 2.

View File

@ -1,5 +1,7 @@
extends Node extends Node
const INDEX_FORMAT := Image.FORMAT_L8
var time = 0.0 var time = 0.0
var time_mult = 1.0 var time_mult = 1.0

View File

@ -1,6 +1,6 @@
extends Node extends Node
func load_json(filename: String) -> Dictionary: static func load_json(filename: String) -> Dictionary:
var file := File.new() var file := File.new()
var error := file.open(filename, File.READ) var error := file.open(filename, File.READ)
if error == OK: if error == OK:
@ -11,3 +11,28 @@ func load_json(filename: String) -> Dictionary:
print_debug(result.error_string) print_debug(result.error_string)
print_debug(result.error_line) print_debug(result.error_line)
return {} return {}
static func load_tsv(filename: String, delimiter: String = '\t') -> Dictionary:
var file := File.new()
var error := file.open(filename, File.READ)
if error == OK:
var headers := file.get_csv_line(delimiter)
var n := headers.size()
var output = {}
while file.get_position() < file.get_len():
var line := file.get_csv_line(delimiter)
var entry := {}
for i in range(1, n):
if line.size() > i:
var token := line[i]
entry[headers[i]] = token
if token.begins_with('0x'):
var hex := token.hex_to_int()
if hex > 0:
entry[headers[i]] = hex
output[line[0]] = entry
return output
print_debug(error)
return {}
var SNES_PSX_addresses := load_tsv('res://data/SNES_PSX_addresses.tsv')

View File

@ -0,0 +1,15 @@
const INDEX_FORMAT := globals.INDEX_FORMAT
static func ByteArray(size: int) -> PoolByteArray:
var arr := PoolByteArray()
arr.resize(size)
return arr
static func _4bpp_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(32):
tdata[i*2] = data[i] % 16
tdata[i*2+1] = data[i] / 16
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile

View File

@ -0,0 +1,75 @@
var INDEX_FORMAT := globals.INDEX_FORMAT
static func ByteArray(size: int) -> PoolByteArray:
var arr := PoolByteArray()
arr.resize(size)
return arr
static func mode7_to_tile(data: PoolByteArray) -> Image:
# Easy one, it's just straight data left-to-right, top-to-bottom
var tile := Image.new()
tile.create_from_data(8, 8, false, Image.FORMAT_L8, data)
return tile
static func mode7_compressed_to_tile(data: PoolByteArray, tile_palette: int = 0) -> Image:
# 4 bits per pixel.
var tdata := ByteArray(64)
for i in 32:
var b := data[i]
var j: int = i*2
tdata[j] = (b & 0x0F) | tile_palette
tdata[j+1] = (b >> 4) | tile_palette
return mode7_to_tile(tdata)
static func _4plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[j+16] >> x & 1)<<2) | ((data[j+17] >> x & 1)<<3)
var tile := Image.new()
tile.create_from_data(8, 8, false, Image.FORMAT_L8, tdata)
return tile
static func _3plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[(i/8)+16] >> x & 1)<<2)
var tile := Image.new()
tile.create_from_data(8, 8, false, Image.FORMAT_L8, tdata)
return tile
static func _2plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1)
var tile := Image.new()
tile.create_from_data(8, 8, false, Image.FORMAT_L8, tdata)
return tile
static func _1plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var x = 7 - (i%8)
tdata[i] = (data[i/8] >> x & 1)
var tile := Image.new()
tile.create_from_data(8, 8, false, Image.FORMAT_L8, tdata)
return tile
static func get_tile(rom: File, offset: int, length: int) -> Image:
rom.seek(offset)
var data := rom.get_buffer(length)
var planes := length / 8
match planes:
4:
return _4plane_to_tile(data)
3:
return _3plane_to_tile(data)
2:
return _2plane_to_tile(data)
_:
return _1plane_to_tile(data)

View File

@ -1,10 +1,10 @@
extends Node extends Node
var shader_material = load('res://palette_mat.tres')
# Portability for GLES2 vs GLES3 stuff # Portability for GLES2 vs GLES3 stuff
const INDEX_FORMAT := Image.FORMAT_L8 const INDEX_FORMAT := globals.INDEX_FORMAT
const snes_graphics := preload('res://scripts/loaders/snes/graphics.gd')
const gba_graphics := preload('res://scripts/loaders/gba/graphics.gd')
var shader_material = load('res://palette_mat.tres')
# Offsets # Offsets
@ -17,23 +17,8 @@ const offset_glyphs_kanji := 0x1BD000 # 0x1AA entries, 1bpp
const offset_title_ffv := 0x031243 # Type 02 headed compression, expands to 0x1000 bytes const offset_title_ffv := 0x031243 # Type 02 headed compression, expands to 0x1000 bytes
const offset_title_dragon := 0x032D22 # Type 02 compression, expands to 0x1200 bytes const offset_title_dragon := 0x032D22 # Type 02 compression, expands to 0x1200 bytes
const offset_title_dragon_OAM := 0x033342 # Type 02 headed compression, expands to 0x0120 bytes const offset_title_dragon_OAM := 0x033342 # Type 02 headed compression, expands to 0x0120 bytes
const offset_locations_bg_palettes := 0x03BB00 # 2bytes * 128 colors * 43 sets
const offset_worldmap_blocks := 0x0FF0C0 # 0x0FF3C0 0x0FF6C0
const offset_worldmap_tile_palettes := 0x0FF9C0 # 0x0FFAC0 0x0FFBC0 Length 0x100
const offset_worldmap_palettes := 0x0FFCC0 # 0x0FFDC0 0x0FFEC0 Length 0x100
const offset_worldmap_tiles := 0x1B8000 # 0x1BA000 0x1BC000
const offset_Character_Battle_Sprite_Tiles := 0x120000
const offset_Character_Battle_Sprite_Palettes := 0x14A3C0
const offset_Character_Battle_Sprite_Layouts := 0x14B997
const num_Character_Battle_Sprite_Layouts := 11 const num_Character_Battle_Sprite_Layouts := 11
const offset_Character_Battle_Sprite_Disabled_Palette := 0x00F867
const offset_Character_Battle_Sprite_Stone_Palette := 0x00F807
const offset_Tiles_Fist := 0x11D710 # 3bpp tile
# Arrays to store sprites in # Arrays to store sprites in
var worldmap_palette_imgs = [] var worldmap_palette_imgs = []
var worldmap_palette_textures = [] var worldmap_palette_textures = []
@ -53,17 +38,17 @@ var character_battle_sprite_palette_disabled_texture: ImageTexture
var character_battle_sprite_palette_stone_texture: ImageTexture var character_battle_sprite_palette_stone_texture: ImageTexture
var weapon_textures = {} var weapon_textures = {}
func ByteArray(size: int) -> PoolByteArray: static func ByteArray(size: int) -> PoolByteArray:
var arr := PoolByteArray() var arr := PoolByteArray()
arr.resize(size) arr.resize(size)
return arr return arr
func texture_from_image(image: Image, flags: int = 0) -> ImageTexture: static func texture_from_image(image: Image, flags: int = 0) -> ImageTexture:
var tex = ImageTexture.new() var tex = ImageTexture.new()
tex.create_from_image(image, flags) tex.create_from_image(image, flags)
return tex return tex
func bgr555_to_color(short: int) -> Color: static func bgr555_to_color(short: int) -> Color:
var color = Color() var color = Color()
color.a = 1 color.a = 1
color.r = ((short & 0x1F) / 31.0) color.r = ((short & 0x1F) / 31.0)
@ -71,7 +56,7 @@ func bgr555_to_color(short: int) -> Color:
color.b = (((short >> 10) & 0x1F) / 31.0) color.b = (((short >> 10) & 0x1F) / 31.0)
return color return color
func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image: static func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image:
rom.seek(offset) rom.seek(offset)
var img := Image.new() var img := Image.new()
var rows := length/16 var rows := length/16
@ -84,7 +69,7 @@ func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image:
img.unlock() img.unlock()
return img return img
func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image: static func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image:
# Safe for GLES2 only! # Safe for GLES2 only!
# Implicit sRGB -> linear conversion on ImageTexture creation from RGB8 Image ruins this in GLES3 mode # Implicit sRGB -> linear conversion on ImageTexture creation from RGB8 Image ruins this in GLES3 mode
rom.seek(offset) rom.seek(offset)
@ -100,7 +85,7 @@ func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image:
img.create_from_data(16, length/16, false, Image.FORMAT_RGB8, data) img.create_from_data(16, length/16, false, Image.FORMAT_RGB8, data)
return img return img
func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image: static func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image:
var data = ByteArray(length*2) var data = ByteArray(length*2)
rom.seek(offset) rom.seek(offset)
for i in range(length): for i in range(length):
@ -115,91 +100,12 @@ func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image
img.create_from_data(16, length/16, false, Image.FORMAT_RGBA5551, data) img.create_from_data(16, length/16, false, Image.FORMAT_RGBA5551, data)
return img return img
func generate_palette(rom: File, offset: int, length: int = 16) -> Image: static func generate_palette(rom: File, offset: int, length: int = 16) -> Image:
if length < 16: if length < 16:
length = 16 length = 16
return generate_palette_rgb8(rom, offset, length) return generate_palette_rgb8(rom, offset, length)
func gba_4bpp_to_tile(data: PoolByteArray) -> Image: static func make_tile_atlas(tile_images) -> Image:
var tdata := ByteArray(64)
for i in range(32):
tdata[i*2] = data[i] % 16
tdata[i*2+1] = data[i] / 16
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile
func snes_mode7_to_tile(data: PoolByteArray) -> Image:
# Easy one, it's just straight data left-to-right, top-to-bottom
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, data)
return tile
func snes_mode7_compressed_to_tile(data: PoolByteArray, tile_palette: int = 0) -> Image:
# 4 bits per pixel.
var tdata := ByteArray(64)
for i in 32:
var b := data[i]
var j: int = i*2
tdata[j] = (b & 0x0F) | tile_palette
tdata[j+1] = (b >> 4) | tile_palette
return snes_mode7_to_tile(tdata)
func snes_4plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[j+16] >> x & 1)<<2) | ((data[j+17] >> x & 1)<<3)
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile
func snes_3plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1) | ((data[(i/8)+16] >> x & 1)<<2)
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile
func snes_2plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var j = (i/8)*2
var x = 7 - (i%8)
tdata[i] = (data[j] >> x & 1) | ((data[j+1] >> x & 1)<<1)
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile
func snes_1plane_to_tile(data: PoolByteArray) -> Image:
var tdata := ByteArray(64)
for i in range(64):
var x = 7 - (i%8)
tdata[i] = (data[i/8] >> x & 1)
var tile := Image.new()
tile.create_from_data(8, 8, false, INDEX_FORMAT, tdata)
return tile
func snes_get_tile(rom: File, offset: int, length: int) -> Image:
rom.seek(offset)
var data := rom.get_buffer(length)
var planes := length / 8
match planes:
4:
return snes_4plane_to_tile(data)
3:
return snes_3plane_to_tile(data)
2:
return snes_2plane_to_tile(data)
_:
return snes_1plane_to_tile(data)
func make_tile_atlas(tile_images) -> Image:
var r := Rect2(0, 0, 8, 8) var r := Rect2(0, 0, 8, 8)
var image = Image.new() var image = Image.new()
image.create(128, 128, false, INDEX_FORMAT) image.create(128, 128, false, INDEX_FORMAT)
@ -216,21 +122,21 @@ func snes_load_worldmap(rom: File):
var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles var worldmap_tile_counts = [256, 256, 128] # Only 128 underwater tiles
for world_ts in 3: # Bartz/Combined World, Galuf World, Underwater (world tilesets, not to be confused with the 5 world maps) for world_ts in 3: # Bartz/Combined World, Galuf World, Underwater (world tilesets, not to be confused with the 5 world maps)
var tile_count: int = worldmap_tile_counts[world_ts] var tile_count: int = worldmap_tile_counts[world_ts]
var image := generate_palette(rom, offset_worldmap_palettes + (world_ts*0x100), 0x100) var image := generate_palette(rom, Common.SNES_PSX_addresses['worldmap_palettes']['SNES'] + (world_ts*0x100), 0x100)
worldmap_palette_imgs.append(image) worldmap_palette_imgs.append(image)
worldmap_palette_textures.append(texture_from_image(image)) worldmap_palette_textures.append(texture_from_image(image))
var tile_palettes = [] var tile_palettes = []
rom.seek(offset_worldmap_tile_palettes + (world_ts*0x100)) rom.seek(Common.SNES_PSX_addresses['worldmap_tile_palettes']['SNES'] + (world_ts*0x100))
for pal in 256: for pal in 256:
tile_palettes.append(rom.get_8()) tile_palettes.append(rom.get_8())
var tile_images = [] var tile_images = []
# var tile_textures = [] # var tile_textures = []
rom.seek(offset_worldmap_tiles + (world_ts*0x2000)) rom.seek(Common.SNES_PSX_addresses['worldmap_tiles']['SNES'] + (world_ts*0x2000))
for tile in tile_count: for tile in tile_count:
var tiledata := rom.get_buffer(32) var tiledata := rom.get_buffer(32)
image = snes_mode7_compressed_to_tile(tiledata, tile_palettes[tile]) image = snes_graphics.mode7_compressed_to_tile(tiledata, tile_palettes[tile])
tile_images.append(image) tile_images.append(image)
# tile_textures.append(texture_from_image(image)) # tile_textures.append(texture_from_image(image))
if world_ts == 0: # Waterfall hack: lay it out vertically, pushing out dummy tiles if world_ts == 0: # Waterfall hack: lay it out vertically, pushing out dummy tiles
@ -243,7 +149,7 @@ func snes_load_worldmap(rom: File):
# Block definitions # Block definitions
var block_images = [] var block_images = []
var block_textures = [] var block_textures = []
var block_bank_start: int = offset_worldmap_blocks + (world_ts*0x300) var block_bank_start: int = Common.SNES_PSX_addresses['worldmap_blocks']['SNES'] + (world_ts*0x300)
var block_tile_ids := PoolByteArray() var block_tile_ids := PoolByteArray()
for block in 0xC0: # 192 blocks per world tileset for block in 0xC0: # 192 blocks per world tileset
image = Image.new() image = Image.new()
@ -279,13 +185,13 @@ func snes_load_worldmap(rom: File):
func snes_load_battle_sprites(rom: File): func snes_load_battle_sprites(rom: File):
# Load Battle sprites # Load Battle sprites
rom.seek(offset_Character_Battle_Sprite_Layouts) rom.seek(Common.SNES_PSX_addresses['character_battle_sprite_layouts']['SNES'])
var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6) var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6)
# Character Battle Sprite Tiles # Character Battle Sprite Tiles
for strip in range(0, 22*5): for strip in range(0, 22*5):
var tiles = [] var tiles = []
for i in range(0, 32*48, 32): for i in range(0, 32*48, 32):
tiles.append(snes_get_tile(rom, offset_Character_Battle_Sprite_Tiles + (strip*32*48) + i, 32)) tiles.append(snes_graphics.get_tile(rom, Common.SNES_PSX_addresses['character_battle_sprite_tiles']['SNES'] + (strip*32*48) + i, 32))
var strip_image = Image.new() var strip_image = Image.new()
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT) strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT)
for i in range(6 * num_Character_Battle_Sprite_Layouts): for i in range(6 * num_Character_Battle_Sprite_Layouts):
@ -295,11 +201,11 @@ func snes_load_battle_sprites(rom: File):
# Character Battle Sprite Palettes # Character Battle Sprite Palettes
for palette in range(0, 22*5): for palette in range(0, 22*5):
character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Palettes + (palette*32)))) character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_palettes']['SNES'] + (palette*32))))
character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette)) character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_disabled_palette']['SNES']))
character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette)) character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, Common.SNES_PSX_addresses['character_battle_sprite_stone_palette']['SNES']))
weapon_textures['Fist'] = texture_from_image(snes_get_tile(rom, offset_Tiles_Fist, 24)) weapon_textures['Fist'] = texture_from_image(snes_graphics.get_tile(rom, Common.SNES_PSX_addresses['tiles_fist']['SNES'], 24))
var json_sprite_blocks := Common.load_json('res://data/sprite_blocks.json') var json_sprite_blocks := Common.load_json('res://data/sprite_blocks.json')
@ -312,7 +218,7 @@ func snes_load_map_sprites(rom: File):
for k in json_sprite_blocks.keys(): for k in json_sprite_blocks.keys():
var v = json_sprite_blocks[k] var v = json_sprite_blocks[k]
var start := int(v['start']) var start: int = v['start'].hex_to_int()
var bpp := int(v['bpp']) var bpp := int(v['bpp'])
var definitions = v['definitions'] var definitions = v['definitions']
var total_tiles := 0 var total_tiles := 0
@ -324,16 +230,16 @@ func snes_load_map_sprites(rom: File):
match bpp: match bpp:
1: 1:
for i in total_tiles: for i in total_tiles:
tiles.append(snes_1plane_to_tile(rom.get_buffer(8))) tiles.append(snes_graphics._1plane_to_tile(rom.get_buffer(8)))
2: 2:
for i in total_tiles: for i in total_tiles:
tiles.append(snes_2plane_to_tile(rom.get_buffer(16))) tiles.append(snes_graphics._2plane_to_tile(rom.get_buffer(16)))
3: 3:
for i in total_tiles: for i in total_tiles:
tiles.append(snes_3plane_to_tile(rom.get_buffer(24))) tiles.append(snes_graphics._3plane_to_tile(rom.get_buffer(24)))
4: 4:
for i in total_tiles: for i in total_tiles:
tiles.append(snes_4plane_to_tile(rom.get_buffer(32))) tiles.append(snes_graphics._4plane_to_tile(rom.get_buffer(32)))
_: _:
print_debug('Invalid bpp "%s" in sprite blocks json' % bpp) print_debug('Invalid bpp "%s" in sprite blocks json' % bpp)
sprite_blocks[k] = tiles sprite_blocks[k] = tiles
@ -380,7 +286,7 @@ func load_gba_rom(filename: String):
var tiledata = CommonGBA.LZ77_decompress(rom, address) var tiledata = CommonGBA.LZ77_decompress(rom, address)
tiledata.append(0) # Slicing to end of array causes an engine crash. For shame! tiledata.append(0) # Slicing to end of array causes an engine crash. For shame!
for i in range(8, 57*32+8, 32): for i in range(8, 57*32+8, 32):
tiles.append(gba_4bpp_to_tile(tiledata.subarray(i, i+32))) tiles.append(gba_graphics._4bpp_to_tile(tiledata.subarray(i, i+32)))
var strip_image = Image.new() var strip_image = Image.new()
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT) strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT)
for i in range(6 * num_Character_Battle_Sprite_Layouts): for i in range(6 * num_Character_Battle_Sprite_Layouts):