diff options
| author | Daniel Weipert <git@mail.dweipert.de> | 2024-12-11 12:55:08 +0100 | 
|---|---|---|
| committer | Daniel Weipert <git@mail.dweipert.de> | 2024-12-11 12:55:08 +0100 | 
| commit | e3c185e05823e30eccd7728ceda2ee57cc66fd4d (patch) | |
| tree | 96cced31157175bb0a3620b0e3b8d99122f298ac /extractor | |
| parent | 688790b5dc0ea8f51a99e42a00c7510b9bd87aa6 (diff) | |
next commit
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 | 
