diff options
Diffstat (limited to 'extractor')
-rw-r--r-- | extractor/action.gd | 222 | ||||
-rw-r--r-- | extractor/action_format.gd | 305 | ||||
-rw-r--r-- | extractor/grf.gd | 20 | ||||
-rw-r--r-- | extractor/sprite_format.gd (renamed from extractor/sprite.gd) | 85 |
4 files changed, 380 insertions, 252 deletions
diff --git a/extractor/action.gd b/extractor/action.gd deleted file mode 100644 index 1865786..0000000 --- a/extractor/action.gd +++ /dev/null @@ -1,222 +0,0 @@ -class_name ActionFormat - - -## Byte Length: 2 [br] -## SP -var signature: String = "AC" - -## Byte Type: u8 [br] -## Byte Length: 2 -var version: Version - -## Byte Type: u16 [br] -## Byte Length: 2 -var action_count: int - -## Byte Type: u8 [br] -## Byte Length: 10 -var reserved: PackedByteArray - -## Length: [member action_count] -var actions: Array[Action] - -## Byte Type: u16 [br] -## Byte Length: 2 -var event_count: int - -## Length: [member event_count] -var events: Array[Event] - -## Byte Type: f32 [br] -## Byte Length: 4 -var frame_times: float - - -func get_byte_length() -> int: - var length := 22 - - for action in actions: - length += action.get_byte_length() - - length += Event.BYTE_LENGTH * event_count - - return length - - -static func from_bytes(bytes: PackedByteArray): - var action = ActionFormat.new() - - @warning_ignore("shadowed_variable") - var version = Version.new() - version.minor = bytes.decode_u8(2) - version.major = bytes.decode_u8(3) - action.version = version - - print(version) - - action.action_count = bytes.decode_u16(4) - action.reserved = bytes.slice(6, 6 + 10) - - # TODO - action.actions = [] as Array[Action] - - action.event_count = 0 - - action.frame_times = 0.0 - - print(inst_to_dict(action)) - - return action - - -class Action: - ## Byte Type: u32 [br] - ## Byte Length: 4 - var motion_count: int - - ## Length: [member motion_count] - var motions: Array[Motion] - - - func get_byte_length() -> int: - var length := 4 - for motion in motions: - length += motion.get_byte_length() - - return length - - -class Motion: - ## Byte Type: u8 [br] - ## Byte Length: 32 - var unused: PackedByteArray - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var sprite_layer_count: int - - ## Length: [member sprite_layer_count] - var sprite_layers: Array[SpriteLayer] - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var event_id: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var sprite_anchor_count: int - - ## Length: [member sprite_anchor_count] - var sprite_anchors: Array[SpriteAnchor] - - - func get_byte_length() -> int: - return 44 + SpriteLayer.BYTE_LENGTH * sprite_layer_count + SpriteAnchor.BYTE_LENGTH * sprite_anchor_count - - -class SpriteLayer: - const BYTE_LENGTH := 48 - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var position_u: int - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var position_v: int - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var sprite_index: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var is_flipped_vertical: int - - ## Byte Type: u8 [br] - ## Byte Length: 1 - var color_r: int - - ## Byte Type: u8 [br] - ## Byte Length: 1 - var color_g: int - - ## Byte Type: u8 [br] - ## Byte Length: 1 - var color_b: int - - ## Byte Type: u8 [br] - ## Byte Length: 1 - var color_a: int - - ## Byte Type: f32 [br] - ## Byte Length: 4 - var scale_u: float - - ## Byte Type: f32 [br] - ## Byte Length: 4 - var scale_v: float - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var rotation_degrees: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var sprite_type: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var sprite_width: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var sprite_height: int - - - func get_position() -> Vector2: - return Vector2(position_u, position_v) - - - func get_color() -> Color: - return Color8(color_r, color_g, color_b, color_a) - - - func get_scale() -> Vector2: - return Vector2(scale_u, scale_v) - - - func get_size() -> Vector2: - return Vector2(sprite_width, sprite_height) - - -class SpriteAnchor: - const BYTE_LENGTH := 16 - - ## Byte Type: u8 [br] - ## Byte Length: 4 - var unused: PackedByteArray - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var position_u: int - - ## Byte Type: i32 [br] - ## Byte Length: 4 - var position_v: int - - ## Byte Type: u32 [br] - ## Byte Length: 4 - var flag: int - - - func get_position() -> Vector2: - return Vector2(position_u, position_v) - - -class Event: - const BYTE_LENGTH := 40 - - ## Byte Type: u8 [br] - ## Byte Length: 40 - var name: String diff --git a/extractor/action_format.gd b/extractor/action_format.gd new file mode 100644 index 0000000..ff2ba64 --- /dev/null +++ b/extractor/action_format.gd @@ -0,0 +1,305 @@ +class_name ActionFormat + + +## Byte Length: 2 [br] +## AC +var signature: String = "AC" + +## Byte Type: u8 [br] +## Byte Length: 2 +var version: Version + +## Byte Type: u16 [br] +## Byte Length: 2 +var action_count: int + +## Byte Type: u8 [br] +## Byte Length: 10 +var reserved: PackedByteArray + +## Length: [member action_count] +var actions: Array[ActionData] + +## Byte Type: u32 [br] +## Byte Length: 4 +var event_count: int + +## Length: [member event_count] +var events: Array[Event] + +## Byte Type: f32 [br] +## Byte Length: [member action_count] [br] +## The times are given in the unit of "ticks per displayed frame". [br] +## Multiply by 24 to get the time in milliseconds. +var frame_times: Array[float] + + +func get_byte_length() -> int: + var length := 20 + + for action in actions: + length += action.get_byte_length() + + length += Event.BYTE_LENGTH * event_count + length += 4 * action_count + + return length + + +static func from_bytes(bytes: ByteStream) -> ActionFormat: + var action_format = ActionFormat.new() + + bytes.seek(2) + + @warning_ignore("shadowed_variable") + var version = Version.new() + version.minor = bytes.decode_u8() + version.major = bytes.decode_u8() + action_format.version = version + + action_format.action_count = bytes.decode_u16() + action_format.reserved = bytes.get_buffer(10).bytes + + action_format.actions = [] as Array[ActionData] + for idx in action_format.action_count: + var action = ActionData.from_bytes(bytes) + action_format.actions.append(action) + + action_format.event_count = bytes.decode_u32() + action_format.events = [] as Array[Event] + for idx in action_format.event_count: + var event = Event.from_bytes(bytes) + action_format.events.append(event) + + action_format.frame_times = [] as Array[float] + for idx in action_format.action_count: + action_format.frame_times.append(bytes.decode_float()) + + return action_format + + +class ActionData: + ## Byte Type: u32 [br] + ## Byte Length: 4 + var motion_count: int + + ## Length: [member motion_count] + var motions: Array[Motion] + + + func get_byte_length() -> int: + var length := 4 + for motion in motions: + length += motion.get_byte_length() + + return length + + + static func from_bytes(bytes: ByteStream) -> ActionData: + var action = ActionData.new() + + action.motion_count = bytes.decode_u32() + action.motions = [] as Array[Motion] + for idx in action.motion_count: + var motion = Motion.from_bytes(bytes) + action.motions.append(motion) + + return action + + +class Motion: + ## Byte Type: u8 [br] + ## Byte Length: 32 + var unused: PackedByteArray + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var sprite_layer_count: int + + ## Length: [member sprite_layer_count] + var sprite_layers: Array[SpriteLayer] + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var event_id: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var sprite_anchor_count: int + + ## Length: [member sprite_anchor_count] + var sprite_anchors: Array[SpriteAnchor] + + + func get_byte_length() -> int: + return 44 + SpriteLayer.BYTE_LENGTH * sprite_layer_count + SpriteAnchor.BYTE_LENGTH * sprite_anchor_count + + + static func from_bytes(bytes: ByteStream): + var motion = Motion.new() + + motion.unused = bytes.get_buffer(32).bytes + + motion.sprite_layer_count = bytes.decode_u32() + motion.sprite_layers = [] as Array[SpriteLayer] + for idx in motion.sprite_layer_count: + var sprite_layer = SpriteLayer.from_bytes(bytes) + motion.sprite_layers.append(sprite_layer) + + motion.event_id = bytes.decode_s32() + + motion.sprite_anchor_count = bytes.decode_u32() + motion.sprite_anchors = [] as Array[SpriteAnchor] + for idx in motion.sprite_anchor_count: + var sprite_anchor = SpriteAnchor.from_bytes(bytes) + motion.sprite_anchor_count.append(sprite_anchor) + + return motion + + +class SpriteLayer: + const BYTE_LENGTH := 44 + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var position_u: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var position_v: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var sprite_index: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var flip_h: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var color_r: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var color_g: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var color_b: int + + ## Byte Type: u8 [br] + ## Byte Length: 1 + var color_a: int + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var scale_u: float + + ## Byte Type: f32 [br] + ## Byte Length: 4 + var scale_v: float + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var rotation_degrees: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var type: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var width: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var height: int + + + func get_position() -> Vector2: + return Vector2(position_u, position_v) + + + func get_color() -> Color: + return Color8(color_r, color_g, color_b, color_a) + + + func get_scale() -> Vector2: + return Vector2(scale_u, scale_v) + + + func get_size() -> Vector2: + return Vector2(width, height) + + + static func from_bytes(bytes: ByteStream): + var sprite_layer = SpriteLayer.new() + + sprite_layer.position_u = bytes.decode_s32() + sprite_layer.position_v = bytes.decode_s32() + sprite_layer.sprite_index = bytes.decode_s32() + sprite_layer.flip_h = bytes.decode_u32() + sprite_layer.color_r = bytes.decode_u8() + sprite_layer.color_g = bytes.decode_u8() + sprite_layer.color_b = bytes.decode_u8() + sprite_layer.color_a = bytes.decode_u8() + sprite_layer.scale_u = bytes.decode_float() + sprite_layer.scale_v = bytes.decode_float() + sprite_layer.rotation_degrees = bytes.decode_s32() + sprite_layer.type = bytes.decode_u32() + sprite_layer.width = bytes.decode_u32() + sprite_layer.height = bytes.decode_u32() + + return sprite_layer + + +class SpriteAnchor: + const BYTE_LENGTH := 16 + + ## Byte Type: u8 [br] + ## Byte Length: 4 + var unused: PackedByteArray + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var position_u: int + + ## Byte Type: i32 [br] + ## Byte Length: 4 + var position_v: int + + ## Byte Type: u32 [br] + ## Byte Length: 4 + var flag: int + + + func get_position() -> Vector2: + return Vector2(position_u, position_v) + + + static func from_bytes(bytes: ByteStream): + var sprite_anchor = SpriteAnchor.new() + + sprite_anchor.unused = bytes.get_buffer(4).bytes + sprite_anchor.position_u = bytes.decode_s32() + sprite_anchor.position_v = bytes.decode_s32() + sprite_anchor.flag = bytes.decode_u32() + + return sprite_anchor + + +class Event: + const BYTE_LENGTH := 40 + + ## Byte Type: u8 [br] + ## Byte Length: 40 + var name: String + + + static func from_bytes(bytes: ByteStream): + var event = Event.new() + + event.name = bytes.get_string_from_utf8(BYTE_LENGTH) + + return event diff --git a/extractor/grf.gd b/extractor/grf.gd index 3739c8c..7e7e93c 100644 --- a/extractor/grf.gd +++ b/extractor/grf.gd @@ -140,6 +140,7 @@ class FileEntry: @warning_ignore("shadowed_variable") static func from_bytes_with_filename(bytes: PackedByteArray, file_name: String): + print(file_name) var file_entry = FileEntry.new() file_entry.file_name = file_name @@ -177,10 +178,10 @@ static func open(path: String): file_entry_offset + file_name_size, file_entry_offset + file_name_size + FileEntry.BYTE_LENGTH ), - grf.file_table.decompressed_record_headers.slice( + GRF.decode_string(grf.file_table.decompressed_record_headers.slice( file_entry_offset, file_entry_offset + file_name_size - ).get_string_from_ascii() + )) ) grf.file_entries.append(file_entry) @@ -201,3 +202,18 @@ func extract(destination: String = "res://data"): # TODO: write pngs for sprites (and .tres files maybe if necessary) # TODO: (also maybe write .tres files for action data files(whatever they are)) + + + +static func decode_string(bytes: PackedByteArray): + return bytes.get_string_from_ascii() + # TODO: check unicode codepoints and parse accordingly + var string = bytes.get_string_from_utf32() + if string == "": + string = bytes.get_string_from_utf16() + if string == "": + string = bytes.get_string_from_utf8() + if string == "": + string = bytes.get_string_from_ascii() + + return string diff --git a/extractor/sprite.gd b/extractor/sprite_format.gd index 410ce07..fab0873 100644 --- a/extractor/sprite.gd +++ b/extractor/sprite_format.gd @@ -1,4 +1,4 @@ -class_name Sprite +class_name SpriteFormat ## Byte Length: 2 [br] @@ -30,23 +30,33 @@ var rgba_image_data: Array[RGBAImageData] ## It must be cleared manually on load. var palette: Array[PaletteColor] +var filepath: String -static func from_bytes(bytes: PackedByteArray): - var sprite = Sprite.new() + +static func from_file(path: String) -> SpriteFormat: + var sprite_format = from_bytes(FileAccess.get_file_as_bytes(path)) + + sprite_format.filepath = path + + return sprite_format + + +static func from_bytes(bytes: PackedByteArray) -> SpriteFormat: + var sprite_format = SpriteFormat.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_format.version = version - sprite.palette_image_count = bytes.decode_u16(4) - sprite.rgba_image_count = bytes.decode_u16(6) + sprite_format.palette_image_count = bytes.decode_u16(4) + sprite_format.rgba_image_count = bytes.decode_u16(6) - sprite.palette_image_data = [] as Array[PaletteImageData] + sprite_format.palette_image_data = [] as Array[PaletteImageData] var palette_image_offset = 8 var processed_palette_images = 0 - while processed_palette_images < sprite.palette_image_count: + while processed_palette_images < sprite_format.palette_image_count: var data = PaletteImageData.new() data.width = bytes.decode_u16(palette_image_offset) data.height = bytes.decode_u16(palette_image_offset + 2) @@ -59,28 +69,32 @@ static func from_bytes(bytes: PackedByteArray): data.decode() - sprite.palette_image_data.append(data) + sprite_format.palette_image_data.append(data) processed_palette_images += 1 palette_image_offset += data.get_byte_length() - sprite.rgba_image_data = [] as Array[RGBAImageData] + sprite_format.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: + while processed_rgba_images < sprite_format.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) - ) + data.data = [] as Array[RGBAImageData.Pixel] + for _idx in (data.width * data.height): + var pixel = RGBAImageData.Pixel.new() + pixel.r = bytes.decode_u8(rgba_image_offset + 4) + pixel.g = bytes.decode_u8(rgba_image_offset + 5) + pixel.b = bytes.decode_u8(rgba_image_offset + 6) + pixel.a = bytes.decode_u8(rgba_image_offset + 7) + data.data.append(pixel) - sprite.rgba_image_data.append(data) + sprite_format.rgba_image_data.append(data) processed_rgba_images += 1 rgba_image_offset += data.get_byte_length() - sprite.palette = [] as Array[PaletteColor] + sprite_format.palette = [] as Array[PaletteColor] var palette_offset = rgba_image_offset while palette_offset < bytes.size(): var color = PaletteColor.new() @@ -88,23 +102,38 @@ static func from_bytes(bytes: PackedByteArray): 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) + sprite_format.palette.append(color) palette_offset += 4 - #print(sprite.palette_image_data[0].get_rgba_data(sprite.palette)) + return sprite_format + + +func save_to_file(): + assert(filepath != "") + + var base_dir = filepath.substr(0, filepath.length() - 4) # cut off .spr + DirAccess.make_dir_recursive_absolute(base_dir) - 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, + for idx in palette_image_data.size(): + var data: PaletteImageData = palette_image_data[idx] + var image = Image.create_from_data( + data.width, + data.height, false, Image.FORMAT_RGBA8, - d.get_rgba_data(sprite.palette) + data.get_rgba_data(palette) ) - i.save_png("res://extractor/test/test-" + str(idx).pad_zeros(3) + ".png") + + image.save_png("%s/%s.png" % [base_dir, str(idx).pad_zeros(3)]) + + +func files_exist() -> bool: + assert(filepath != "") + + var base_dir = filepath.substr(0, filepath.length() - 4) # cut off .spr - return sprite + return DirAccess.dir_exists_absolute(base_dir) + class PaletteImageData: @@ -201,4 +230,4 @@ class RGBAImageData: func get_byte_length() -> int: - return width * height * data.size() + return 4 + width * height * Pixel.BYTE_LENGTH |