diff --git a/Node2D.gd b/Node2D.gd index 94333ad..bc349b1 100644 --- a/Node2D.gd +++ b/Node2D.gd @@ -2,6 +2,7 @@ extends Node2D var PC = load('PC.tscn') var PCs = [] +var worldmap_blocks = [] var sfx_buttons = [] func _ready(): @@ -22,6 +23,16 @@ func _ready(): # PCs[-1].material.set_shader_param('palette', SpriteLoader.character_battle_sprite_palette_textures[45]) # PCs[-1].texture = SpriteLoader.weapon_textures['Fist'] # add_child(PCs[-1]) + for i in 3: + worldmap_blocks.append(TextureRect.new()) + worldmap_blocks[-1].texture = SpriteLoader.worldmap_block_atlas_textures[i] + # worldmap_blocks[-1].texture = SpriteLoader.worldmap_tile_atlas_textures[i] + worldmap_blocks[-1].material = SpriteLoader.shader_material.duplicate() + worldmap_blocks[-1].material.set_shader_param('palette', SpriteLoader.worldmap_palette_textures[i]) + # worldmap_blocks[-1].rect_scale *= 2 + worldmap_blocks[-1].rect_position.x = i*256 + worldmap_blocks[-1].rect_position.y = 280 + add_child(worldmap_blocks[-1]) _create_sfx_buttons() diff --git a/README.md b/README.md index 2d3baba..a097aef 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ I have mostly solved parsing of SNES menus in a sister project, however there ar - [ ] NPC sprites (solved problem, soon™) - [ ] Vehicle sprites (solved problem, soon™) ### World Maps (Fields?) -- [ ] Tiles (solved problem, soon™) +- [x] Tiles - [ ] Tilemaps (solved problem, soon™) - [ ] Dynamic changes (e.g. meteors, breaking seals, sinking island, pirate cave, voids) (might hardcode these later) - [ ] Pathing (I have vague recollections about this) diff --git a/scripts/loaders/sprite_loader.gd b/scripts/loaders/sprite_loader.gd index e882b03..e30d439 100644 --- a/scripts/loaders/sprite_loader.gd +++ b/scripts/loaders/sprite_loader.gd @@ -2,15 +2,43 @@ extends Node var shader_material = load('res://palette_mat.tres') -const offset_Character_Battle_Sprite_Tiles: int = 0x120000 -const offset_Character_Battle_Sprite_Palettes: int = 0x14A3C0 -const offset_Character_Battle_Sprite_Layouts: int = 0x14B997 -const num_Character_Battle_Sprite_Layouts: int = 11 +# Offsets -const offset_Character_Battle_Sprite_Disabled_Palette: int = 0x00F867 -const offset_Character_Battle_Sprite_Stone_Palette: int = 0x00F807 +# Glyphs +const offset_glyphs_small := 0x1FF000 # 0x100 entries, 2bpp +const offset_glyphs_dialog := 0x03E800 # 0x100 entries, 1bpp +const offset_glyphs_kanji := 0x1BD000 # 0x1AA entries, 1bpp -const offset_Tiles_Fist: int = 0x11D710 # 3bpp tile +# Images +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_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 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 +var worldmap_palette_imgs = [] +var worldmap_palette_textures = [] +var worldmap_tile_imgs = [] +var worldmap_block_individual_imgs = [] +var worldmap_block_individual_textures = [] +var worldmap_block_atlas_imgs = [] +var worldmap_block_atlas_textures = [] +# var worldmap_tile_atlas_textures = [] #var character_battle_sprite_tiles = [] #var character_battle_sprite_palette_imgs = [] @@ -40,11 +68,13 @@ func bgr555_to_color(short: int) -> Color: func generate_palette_rgbf(rom: File, offset: int, length: int = 16) -> Image: rom.seek(offset) var img := Image.new() - img.create(length, 1, false, Image.FORMAT_RGBF) + var rows := length/16 + img.create(16, rows, false, Image.FORMAT_RGBF) img.lock() - for i in range(length): - var color = bgr555_to_color(rom.get_16()) - img.set_pixel(i, 0, color) + for y in rows: + for x in 16: + var color = bgr555_to_color(rom.get_16()) + img.set_pixel(x, y, color) img.unlock() return img @@ -61,7 +91,7 @@ func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image: data[j+1] = color.g8 data[j+2] = color.b8 var img := Image.new() - img.create_from_data(length, 1, false, Image.FORMAT_RGB8, data) + img.create_from_data(16, length/16, false, Image.FORMAT_RGB8, data) return img func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image: @@ -76,10 +106,12 @@ func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image data[i*2] = rgb5_a1 & 0xFF data[(i*2)+1] = rgb5_a1 >> 8 var img := Image.new() - img.create_from_data(length, 1, false, Image.FORMAT_RGBA5551, data) + img.create_from_data(16, length/16, false, Image.FORMAT_RGBA5551, data) return img func generate_palette(rom: File, offset: int, length: int = 16) -> Image: + if length < 16: + length = 16 return generate_palette_rgb8(rom, offset, length) func gba_4bpp_to_tile(data: PoolByteArray) -> Image: @@ -91,6 +123,22 @@ func gba_4bpp_to_tile(data: PoolByteArray) -> Image: tile.create_from_data(8, 8, false, Image.FORMAT_R8, 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, Image.FORMAT_R8, 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): @@ -145,7 +193,66 @@ func snes_get_tile(rom: File, offset: int, length: int) -> Image: return snes_1plane_to_tile(data) +func snes_load_worldmap(rom: File): + # 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(rom, offset_worldmap_palettes + (world_ts*0x100), 0x100) + worldmap_palette_imgs.append(image) + worldmap_palette_textures.append(texture_from_image(image)) + + var tile_palettes = [] + rom.seek(offset_worldmap_tile_palettes + (world_ts*0x100)) + for pal in 256: + tile_palettes.append(rom.get_8()) + + var tile_images = [] + # var tile_textures = [] + rom.seek(offset_worldmap_tiles + (world_ts*0x2000)) + for tile in tile_count: + var tiledata := rom.get_buffer(32) + image = snes_mode7_compressed_to_tile(tiledata, tile_palettes[tile]) + tile_images.append(image) + # tile_textures.append(texture_from_image(image)) + worldmap_tile_imgs.append(tile_images) + + # Block definitions + var block_images = [] + var block_textures = [] + var block_bank_start: int = offset_worldmap_blocks + (world_ts*0x300) + for block in 0xC0: # 192 blocks per world tileset + image = Image.new() + image.create(16, 16, false, Image.FORMAT_R8) + for tile in 4: + rom.seek(block_bank_start + block + (tile * 0xC0)) # Horrible interleaving scheme + var src_idx := rom.get_8() + if src_idx < tile_count: + image.blit_rect(tile_images[src_idx], Rect2(0, 0, 8, 8), Vector2((tile%2)*8, (tile/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) + # Make block atlas + image = Image.new() + image.create(16*16, 16*12, false, Image.FORMAT_R8) + 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, Image.FORMAT_R8) + # 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)) + + for world_map in 5: # Bartz World, Galuf World, Combined World, Underwater Galuf World, Underwater Combined World + pass + func load_snes_rom(rom: File): + # Load Battle sprites rom.seek(offset_Character_Battle_Sprite_Layouts) var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6) # Character Battle Sprite Tiles @@ -168,6 +275,8 @@ func load_snes_rom(rom: File): weapon_textures['Fist'] = texture_from_image(snes_get_tile(rom, offset_Tiles_Fist, 24)) + snes_load_worldmap(rom) + const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB' const gba_marker_pos_US := 0x12FE10