class_name Sprite ## Byte Length: 2 [br] ## SP var signature: String = "SP" ## Byte Type: u8 [br] ## Byte Length: 2 var version: Version ## Byte Type: u16 [br] ## Byte Length: 2 var palette_image_count: int ## Byte Type: u16 [br] ## Byte Length: 2 var rgba_image_count: int ## Length: [member palette_image_count] var palette_image_data: Array[PaletteImageData] ## Byte Type: u8 [br] ## Byte Length: [member rgba_image_count] var rgba_image_data: Array[RGBAImageData] ## Byte Type: u16 [br] ## Byte Length: 256 * 4 (rgba u8) [br] ## The color with palette index 0 can be considered the "background color". [br] ## It must be cleared manually on load. var palette: Array[PaletteColor] static func from_bytes(bytes: PackedByteArray): var sprite = Sprite.new() @warning_ignore("shadowed_variable") var version = Version.new() version.minor = bytes.decode_u8(2) version.major = bytes.decode_u8(3) sprite.version = version sprite.palette_image_count = bytes.decode_u16(4) sprite.rgba_image_count = bytes.decode_u16(6) sprite.palette_image_data = [] as Array[PaletteImageData] var palette_image_offset = 8 var processed_palette_images = 0 while processed_palette_images < sprite.palette_image_count: var data = PaletteImageData.new() data.width = bytes.decode_u16(palette_image_offset) data.height = bytes.decode_u16(palette_image_offset + 2) data.data_size = bytes.decode_u16(palette_image_offset + 4) data.encoded_data = bytes.slice( palette_image_offset + 6, palette_image_offset + 6 + data.data_size ) data.decode() sprite.palette_image_data.append(data) processed_palette_images += 1 palette_image_offset += data.get_byte_length() sprite.rgba_image_data = [] as Array[RGBAImageData] var rgba_image_offset = palette_image_offset var processed_rgba_images = 0 while processed_rgba_images < sprite.rgba_image_count: var data = RGBAImageData.new() data.width = bytes.decode_u16(rgba_image_offset) data.height = bytes.decode_u16(rgba_image_offset + 2) data.data = bytes.slice( rgba_image_offset + 4, rgba_image_offset + 4 + (data.width * data.height * 4) ) sprite.rgba_image_data.append(data) processed_rgba_images += 1 rgba_image_offset += data.get_byte_length() sprite.palette = [] as Array[PaletteColor] var palette_offset = rgba_image_offset while palette_offset < bytes.size(): var color = PaletteColor.new() color.r = bytes.decode_u8(palette_offset) color.g = bytes.decode_u8(palette_offset + 1) color.b = bytes.decode_u8(palette_offset + 2) color.a = bytes.decode_u8(palette_offset + 3) sprite.palette.append(color) palette_offset += 4 #print(sprite.palette_image_data[0].get_rgba_data(sprite.palette)) for idx in sprite.palette_image_data.size(): var d: PaletteImageData = sprite.palette_image_data[idx] var i = Image.create_from_data( d.width, d.height, false, Image.FORMAT_RGBA8, d.get_rgba_data(sprite.palette) ) i.save_png("res://extractor/test/test-" + str(idx).pad_zeros(3) + ".png") return sprite class PaletteImageData: ## Byte Type: u16 ## Byte Length: 2 var width: int ## Byte Type: u16 ## Byte Length: 2 var height: int ## Byte Type: u16 ## Byte Length: 2 var data_size: int ## Byte Type: u8 ## Byte Length: [member data_size] var encoded_data: PackedByteArray var decoded_data: PackedByteArray func get_byte_length() -> int: return 6 + data_size func decode(): decoded_data = PackedByteArray([]) var offset = 0 while offset < encoded_data.size(): var byte = encoded_data.decode_u8(offset) if byte == 0: var length = encoded_data.decode_u8(offset + 1) var padding = PackedByteArray([]) padding.resize(length) padding.fill(0) decoded_data.append_array(padding) offset += 2 else: decoded_data.append(byte) offset += 1 func get_rgba_data(palette: Array[PaletteColor]) -> PackedByteArray: var rgba := PackedByteArray([]) var background_color := palette[0] for idx in decoded_data: var color := palette[idx] if color == background_color: rgba.append_array(PackedByteArray([0, 0, 0, 0])) else: rgba.append_array(color.to_bytes()) return rgba class PaletteColor: const BYTE_LENGTH := 4 var r: int var g: int var b: int var a: int func to_bytes(): return PackedByteArray([r, g, b, 255]) class RGBAImageData: ## Byte Type: u16 ## Byte Length: 2 var width: int ## Byte Type: u16 ## Byte Length: 2 var height: int ## Byte Type: u8 ## Byte Length: width * height * RGBAPixel.byte_length var data: Array[Pixel] class Pixel: const BYTE_LENGTH := 4 var r: int var g: int var b: int var a: int func get_byte_length() -> int: return width * height * data.size()