diff --git a/data/SNES_PSX_addresses.tsv b/data/SNES_PSX_addresses.tsv index 4da9a7a..5e6b2c2 100644 --- a/data/SNES_PSX_addresses.tsv +++ b/data/SNES_PSX_addresses.tsv @@ -1,9 +1,11 @@ Label SNES PSX_file PSX_offset format Comment 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_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 -worldmap_tiles 0x1B8000 /nar/ff5_bin3.bin 0x039B00 3 of 256 of TileSNESMode7c Add the biases +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 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 diff --git a/scripts/loaders/RomLoader.gd b/scripts/loaders/RomLoader.gd index c823f02..dd75fef 100644 --- a/scripts/loaders/RomLoader.gd +++ b/scripts/loaders/RomLoader.gd @@ -27,8 +27,8 @@ func load_snes_rom(filename: String): 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) + # 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) @@ -45,7 +45,20 @@ func load_snes_rom(filename: String): if not s: assert(false, 'Invalid StructType: "%s"' % d.format) buffer.seek(d.SNES) - snes_data[key] = s.get_value(buffer, [0, 0]) + if '.' in key: + var keysplit: PoolStringArray = key.split('.', 1) + var k0 := keysplit[0] + var k1 = keysplit[1] + if k1.is_valid_integer(): + k1 = int(k1) + if not (k0 in snes_data): + snes_data[k0] = {k1: s.get_value(buffer, [0, 0])} + else: + snes_data[k0][k1] = s.get_value(buffer, [0, 0]) + else: + snes_data[key] = s.get_value(buffer, [0, 0]) + SpriteLoader.load_from_structs(snes_data) + MapLoader.load_snes_rom(rom_snes) func load_psx_folder(_dirname: String): pass @@ -73,24 +86,6 @@ func _ready(): var _error := psx_productcode_regex.compile('(S[A-Z]{3}_\\d{3}\\.\\d{2});(\\d)') load_snes_rom(ROM_filename) # Debugging breakpoint - for i in 128: - var weapon = snes_data.tbl_weapons[i] - if weapon.byte_2_leftover: - print('Weapon %d ($%02X) has byte_2_leftover set' % [i, i]) - if weapon.byte_8_leftover: - print('Weapon %d ($%02X) has byte_8_leftover set' % [i, i]) - if weapon['is_5.4']: - print('Weapon %d ($%02X) has 5.4 set' % [i, i]) - for i in 96: - var armor = snes_data.tbl_armors[i] - if armor.byte_0_leftover: - print('Armor %d ($%02X) "%s" has byte_0_leftover set' % [i, i, StringLoader.tables.items[i+0x80]]) - if armor.byte_4_leftovers: - print('Armor %d ($%02X) "%s" has byte_4_leftovers set' % [i, i, StringLoader.tables.items[i+0x80]]) - if armor.byte_2_leftovers: - print('Armor %d ($%02X) "%s" has byte_2_leftovers set to %d ($%02X)' % [i, i, StringLoader.tables.items[i+0x80], armor.byte_2_leftovers, armor.byte_2_leftovers]) - if armor.gear_special: - print('Armor %d ($%02X) "%s" has gear_special set to %d ($%02X)' % [i, i, StringLoader.tables.items[i+0x80], armor.gear_special, armor.gear_special]) pass func _exit_tree() -> void: diff --git a/scripts/loaders/SpriteLoader.gd b/scripts/loaders/SpriteLoader.gd index 549e46a..31b884c 100644 --- a/scripts/loaders/SpriteLoader.gd +++ b/scripts/loaders/SpriteLoader.gd @@ -121,6 +121,19 @@ static func generate_palette(rom: File, offset: int, length: int = 16) -> Image: length = 16 return generate_palette_rgb8(rom, offset, length) +static func generate_palette_from_colorarray(colors: PoolColorArray, format:=Image.FORMAT_RGBF) -> Image: + var img := Image.new() + var rows := len(colors)/16 + img.create(16, rows, false, format) + img.lock() + var i := 0 + for y in rows: + for x in 16: + img.set_pixel(x, y, colors[i]) + i += 1 + img.unlock() + return img + static func make_tile_atlas(tile_images) -> Image: var r := Rect2(0, 0, 8, 8) var image = Image.new() @@ -260,12 +273,90 @@ func snes_load_map_sprites(rom: File): print_debug('Invalid bpp "%s" in sprite blocks json' % bpp) sprite_blocks[k] = tiles +static func bias_tile(unbiased: PoolByteArray, bias: int) -> Image: + var image := Image.new() + var biased = ByteArray(64) + for i in 64: + biased[i] = unbiased[i] + bias + image.create_from_data(8, 8, false, INDEX_FORMAT, biased) + return image func load_snes_rom(rom: File): snes_load_battle_sprites(rom) snes_load_worldmap(rom) # snes_load_map_sprites(rom) +func load_from_structs(data: Dictionary): + # Load Battle sprites + for character_tiles in data.character_battle_sprite_tiles: + for job_tiles in character_tiles: + var strip_image = Image.new() + # Should probably refactor later + strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, INDEX_FORMAT) + for sprite in num_Character_Battle_Sprite_Layouts: + var sprite_tiles: PoolByteArray = data.character_battle_sprite_layouts[sprite] + for i in 6: + strip_image.blit_rect(job_tiles[sprite_tiles[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, ((i/2) * 8) + (sprite * 24))) + strip_images.append(strip_image) + strip_textures.append(texture_from_image(strip_image)) + # Character Battle Sprite Palettes + for character_palettes in data.character_battle_sprite_palettes: + for job_palette in character_palettes: + character_battle_sprite_palette_textures.append(texture_from_image(generate_palette_from_colorarray(job_palette))) + character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette_from_colorarray(data.character_battle_sprite_disabled_palette)) + character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette_from_colorarray(data.character_battle_sprite_stone_palette)) + weapon_textures['Fist'] = texture_from_image(data.tiles_fist) + # Load World Map + # Load Worldmap Graphics + 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) + var tile_count: int = worldmap_tile_counts[world_ts] + var image := generate_palette_from_colorarray(data.worldmap_palettes[world_ts]) + worldmap_palette_imgs.append(image) + worldmap_palette_textures.append(texture_from_image(image)) + var tile_biases: PoolByteArray = data.worldmap_tiles.bias[world_ts] + var tile_images_unbiased = data.worldmap_tiles[world_ts] + var tile_images := [] + for i in tile_count: + tile_images.append(bias_tile(tile_images_unbiased[i].get_data(), tile_biases[i])) + if world_ts == 0: # Waterfall hack: lay it out vertically, pushing out dummy tiles + tile_images[0x97] = tile_images[0x88] + tile_images[0x98] = tile_images[0x87] + worldmap_tile_individual_imgs.append(tile_images) + worldmap_tile_atlas_textures.append(texture_from_image(make_tile_atlas(tile_images))) + # Block definitions + var block_images = [] + var block_textures = [] + var block_tile_ids := PoolByteArray() + for block in 0xC0: # 192 blocks per world tileset + image = Image.new() + image.create(16, 16, false, INDEX_FORMAT) + for i in 4: + var src_idx: int = data.worldmap_blocks[world_ts][i][block] + block_tile_ids.append(src_idx) + if src_idx < tile_count: + image.blit_rect(tile_images[src_idx], Rect2(0, 0, 8, 8), Vector2((i%2)*8, (i/2)*8)) + block_images.append(image) + block_textures.append(texture_from_image(image)) + worldmap_block_individual_imgs.append(block_images) + worldmap_block_individual_textures.append(block_textures) + worldmap_block_tile_ids.append(block_tile_ids) + # Make block atlas + # image = Image.new() + # image.create(16*16, 16*12, false, INDEX_FORMAT) + # for block in 0xC0: + # image.blit_rect(block_images[block], Rect2(0, 0, 16, 16), Vector2((block%16)*16, (block/16)*16)) + # worldmap_block_atlas_imgs.append(image) + # worldmap_block_atlas_textures.append(texture_from_image(image)) + + # # DEBUG: Make tile atlas + # image = Image.new() + # image.create(16*8, (tile_count/16)*8, false, INDEX_FORMAT) + # for tile in tile_count: + # image.blit_rect(tile_images[tile], Rect2(0, 0, 8, 8), Vector2((tile%16)*8, (tile/16)*8)) + # worldmap_tile_atlas_textures.append(texture_from_image(image)) + + MapLoader.update_worldmap_block_tile_ids(worldmap_block_tile_ids) const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB' const gba_marker_pos_US := 0x12FE10 diff --git a/shaders/worldmap_shader.gdshader b/shaders/worldmap_shader.gdshader index da683e1..a07d077 100644 --- a/shaders/worldmap_shader.gdshader +++ b/shaders/worldmap_shader.gdshader @@ -1,6 +1,7 @@ shader_type canvas_item; uniform sampler2D tile_atlas : hint_normal; uniform sampler2D palette : hint_normal; +uniform float palette_rows = 8.0; // 128 colours // uniform float tile_width = 8.0; uniform float tilemap_width = 512.0; // Require square tilemap for now uniform bool enable_sea_scroll = true; // Disable on underwater maps (tileset 3) @@ -73,7 +74,7 @@ void fragment() { vec2 lut_uv = get_tile_atlas_uv(s, UV); float color_id = texture(tile_atlas, lut_uv).r; float color_idx16 = color_id * index_scale; - float pal_row = trunc(color_idx16) / 16.0; + float pal_row = trunc(color_idx16) / palette_rows; float pal_col = fract(color_idx16); vec2 palette_uv = vec2(pal_col, pal_row); COLOR = texture(palette, palette_uv); diff --git a/test_scene.gd b/test_scene.gd index 024f1ec..9d98f20 100644 --- a/test_scene.gd +++ b/test_scene.gd @@ -25,11 +25,11 @@ func _ready(): $PartyMenu.update_labels(data) ThemeManager.set_menu_color_555(data.config.menu_color_r, data.config.menu_color_g, data.config.menu_color_b) - var lbl = Label.new() - for i in 22: - lbl.text = lbl.text + '%s - %s\n' % [StringLoader.get_job_name(i), StringLoader.get_job_desc(i)] - for i in 78: - lbl.text = lbl.text + '\n%s - %s' % [StringLoader.get_ability_name(i), StringLoader.get_ability_desc(i)] - for i in range(128, 161): - lbl.text = lbl.text + '\n%s - %s' % [StringLoader.get_ability_name(i), StringLoader.get_ability_desc(i)] - add_child(lbl) + # var lbl = Label.new() + # for i in 22: + # lbl.text = lbl.text + '%s - %s\n' % [StringLoader.get_job_name(i), StringLoader.get_job_desc(i)] + # for i in 78: + # lbl.text = lbl.text + '\n%s - %s' % [StringLoader.get_ability_name(i), StringLoader.get_ability_desc(i)] + # for i in range(128, 161): + # lbl.text = lbl.text + '\n%s - %s' % [StringLoader.get_ability_name(i), StringLoader.get_ability_desc(i)] + # add_child(lbl) diff --git a/test_scene.tscn b/test_scene.tscn index 0029cda..287521e 100644 --- a/test_scene.tscn +++ b/test_scene.tscn @@ -14,14 +14,12 @@ theme = ExtResource( 6 ) script = ExtResource( 3 ) [node name="audio_system" parent="." instance=ExtResource( 5 )] -visible = false position = Vector2( 0, 160 ) [node name="worldmap_system" parent="." instance=ExtResource( 2 )] -visible = false [node name="battle_sprites" parent="." instance=ExtResource( 4 )] -visible = false [node name="PartyMenu" parent="." instance=ExtResource( 1 )] +visible = false margin_right = 320.0 diff --git a/worldmap_palette_mat.tres b/worldmap_palette_mat.tres index 70d1b0d..e9f369b 100644 --- a/worldmap_palette_mat.tres +++ b/worldmap_palette_mat.tres @@ -4,6 +4,7 @@ [resource] shader = ExtResource( 1 ) +shader_param/palette_rows = 8.0 shader_param/tilemap_width = 512.0 shader_param/enable_sea_scroll = true shader_param/enable_waterfall_scroll = true