2023-07-25 14:21:10 +09:30
|
|
|
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
|
|
|
|
|
|
|
|
const offset_Character_Battle_Sprite_Disabled_Palette: int = 0x00F867
|
|
|
|
const offset_Character_Battle_Sprite_Stone_Palette: int = 0x00F807
|
|
|
|
|
|
|
|
const offset_Tiles_Fist: int = 0x11D710 # 3bpp tile
|
|
|
|
|
|
|
|
#var character_battle_sprite_tiles = []
|
|
|
|
#var character_battle_sprite_palette_imgs = []
|
|
|
|
var character_battle_sprite_palette_textures = []
|
|
|
|
var character_battle_sprite_palette_disabled_texture: ImageTexture
|
|
|
|
var character_battle_sprite_palette_stone_texture: ImageTexture
|
|
|
|
var weapon_textures = {}
|
|
|
|
|
2023-07-25 16:33:45 +09:30
|
|
|
func ByteArray(size: int) -> PoolByteArray:
|
|
|
|
var arr := PoolByteArray()
|
|
|
|
arr.resize(size)
|
|
|
|
return arr
|
|
|
|
|
2023-07-25 14:21:10 +09:30
|
|
|
func texture_from_image(image: Image, flags: int = 0) -> ImageTexture:
|
|
|
|
var tex = ImageTexture.new()
|
|
|
|
tex.create_from_image(image, flags)
|
|
|
|
return tex
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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)
|
|
|
|
img.lock()
|
|
|
|
for i in range(length):
|
|
|
|
var color = bgr555_to_color(rom.get_16())
|
|
|
|
img.set_pixel(i, 0, color)
|
|
|
|
img.unlock()
|
|
|
|
return img
|
|
|
|
|
|
|
|
func generate_palette_rgb8(rom: File, offset: int, length: int = 16) -> Image:
|
2023-07-25 14:49:32 +09:30
|
|
|
# Safe for GLES2 only!
|
|
|
|
# Implicit sRGB -> linear conversion on ImageTexture creation from RGB8 Image ruins this in GLES3 mode
|
2023-07-25 14:21:10 +09:30
|
|
|
rom.seek(offset)
|
|
|
|
var data = ByteArray(length*3)
|
|
|
|
# img.lock()
|
|
|
|
for i in length:
|
|
|
|
var color := bgr555_to_color(rom.get_16())
|
|
|
|
var j: int = i*3
|
|
|
|
data[j] = color.r8
|
|
|
|
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)
|
|
|
|
return img
|
|
|
|
|
|
|
|
func generate_palette_rgb5_a1(rom: File, offset: int, length: int = 16) -> Image:
|
|
|
|
var data = ByteArray(length*2)
|
|
|
|
rom.seek(offset)
|
|
|
|
for i in range(length):
|
|
|
|
var bgr555 := rom.get_16()
|
|
|
|
var r := bgr555 & 0x1F
|
|
|
|
var g := (bgr555 >> 5) & 0x1F
|
|
|
|
var b := (bgr555 >> 10) & 0x1F
|
|
|
|
var rgb5_a1 := (r << 11) | (g << 6) | (b << 1) | 1
|
|
|
|
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)
|
|
|
|
return img
|
|
|
|
|
|
|
|
func generate_palette(rom: File, offset: int, length: int = 16) -> Image:
|
2023-07-25 14:49:32 +09:30
|
|
|
return generate_palette_rgb8(rom, offset, length)
|
2023-07-25 14:21:10 +09:30
|
|
|
|
|
|
|
func gba_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, Image.FORMAT_R8, tdata)
|
|
|
|
return tile
|
|
|
|
|
|
|
|
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, Image.FORMAT_R8, 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, Image.FORMAT_R8, 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, Image.FORMAT_R8, 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, Image.FORMAT_R8, 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)
|
|
|
|
|
|
|
|
|
2023-07-25 16:33:45 +09:30
|
|
|
func load_snes_rom(rom: File):
|
2023-07-25 14:21:10 +09:30
|
|
|
rom.seek(offset_Character_Battle_Sprite_Layouts)
|
|
|
|
var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6)
|
|
|
|
# Character Battle Sprite Tiles
|
|
|
|
for strip in range(0, 22*5):
|
|
|
|
var tiles = []
|
|
|
|
for i in range(0, 32*48, 32):
|
|
|
|
tiles.append(snes_get_tile(rom, offset_Character_Battle_Sprite_Tiles + (strip*32*48) + i, 32))
|
|
|
|
var strip_image = Image.new()
|
|
|
|
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, Image.FORMAT_R8)
|
|
|
|
for i in range(6 * num_Character_Battle_Sprite_Layouts):
|
|
|
|
strip_image.blit_rect(tiles[battle_strip_layouts[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, (i/2) * 8))
|
|
|
|
strip_images.append(strip_image)
|
|
|
|
strip_textures.append(texture_from_image(strip_image))
|
|
|
|
|
|
|
|
# Character Battle Sprite Palettes
|
|
|
|
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_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette))
|
|
|
|
character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette))
|
|
|
|
|
|
|
|
weapon_textures['Fist'] = texture_from_image(snes_get_tile(rom, offset_Tiles_Fist, 24))
|
|
|
|
|
|
|
|
|
|
|
|
const gba_marker := 'FINAL FANTASY V ADVANCE SYGMAB'
|
|
|
|
const gba_marker_pos_US := 0x12FE10
|
|
|
|
const gba_marker_pos_EU := 0x131294
|
|
|
|
const gba_offset_battle_sprite_tiles := 504
|
|
|
|
const gba_offset_battle_sprite_palettes := 1064
|
|
|
|
const gba_offset_battle_sprite_layouts := 9435
|
|
|
|
func load_gba_rom(filename: String):
|
|
|
|
var rom := File.new()
|
|
|
|
var offset_marker: int
|
|
|
|
var result = rom.open(filename, File.READ)
|
|
|
|
if result != 0:
|
|
|
|
print_debug('ROM failed to open: ', result)
|
|
|
|
return
|
|
|
|
|
|
|
|
rom.seek(gba_marker_pos_US)
|
|
|
|
if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker:
|
|
|
|
offset_marker = gba_marker_pos_US
|
|
|
|
else:
|
|
|
|
rom.seek(gba_marker_pos_EU)
|
|
|
|
if rom.get_buffer(len(gba_marker)).get_string_from_ascii() == gba_marker:
|
|
|
|
offset_marker = gba_marker_pos_EU
|
|
|
|
else:
|
|
|
|
print_debug('ROM does not match known US/EU ROMs, aborting')
|
|
|
|
return
|
|
|
|
|
|
|
|
# Battle Sprite Tiles
|
|
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_layouts)
|
|
|
|
var battle_strip_layouts = rom.get_buffer(num_Character_Battle_Sprite_Layouts * 6)
|
|
|
|
for strip in range(0, 26*5):
|
|
|
|
var tiles = []
|
|
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_tiles + strip*4)
|
|
|
|
var address = rom.get_32() - 0x08000000
|
2023-07-25 16:33:45 +09:30
|
|
|
var tiledata = CommonGBA.LZ77_decompress(rom, address)
|
2023-07-25 14:21:10 +09:30
|
|
|
tiledata.append(0) # Slicing to end of array causes an engine crash. For shame!
|
|
|
|
for i in range(8, 57*32+8, 32):
|
|
|
|
tiles.append(gba_4bpp_to_tile(tiledata.subarray(i, i+32)))
|
|
|
|
var strip_image = Image.new()
|
|
|
|
strip_image.create(16, 24 * num_Character_Battle_Sprite_Layouts, false, Image.FORMAT_R8)
|
|
|
|
for i in range(6 * num_Character_Battle_Sprite_Layouts):
|
|
|
|
strip_image.blit_rect(tiles[battle_strip_layouts[i]], Rect2(0, 0, 8, 8), Vector2((i%2) * 8, (i/2) * 8))
|
|
|
|
strip_images.append(strip_image)
|
|
|
|
strip_textures.append(texture_from_image(strip_image))
|
|
|
|
|
|
|
|
# Character Battle Sprite Palettes
|
|
|
|
for palette in range(0, 26*5):
|
|
|
|
rom.seek(offset_marker + gba_offset_battle_sprite_palettes + palette*4)
|
|
|
|
character_battle_sprite_palette_textures.append(texture_from_image(generate_palette(rom, rom.get_32() - 0x08000000 + 8)))
|
|
|
|
# character_battle_sprite_palette_disabled_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Disabled_Palette))
|
|
|
|
# character_battle_sprite_palette_stone_texture = texture_from_image(generate_palette(rom, offset_Character_Battle_Sprite_Stone_Palette))
|
|
|
|
|
|
|
|
|
|
|
|
var strip_images = []
|
|
|
|
var strip_textures = []
|
2023-07-25 16:33:45 +09:30
|
|
|
# func _ready():
|
|
|
|
#pass
|