diff options
| author | Daniel Weipert <git@mail.dweipert.de> | 2025-01-05 16:54:36 +0100 | 
|---|---|---|
| committer | Daniel Weipert <git@mail.dweipert.de> | 2025-01-05 16:54:36 +0100 | 
| commit | 35b0f811f23f029110373798b19d9d0895d907f0 (patch) | |
| tree | 2e4906a992c2569fa0f89dbe0f079010a734cc41 /extractor | |
| parent | e08a29e73ea4f7e6d78e8e7f5a6e7033dbc1f542 (diff) | |
next commit
Diffstat (limited to 'extractor')
| -rw-r--r-- | extractor/action_format.gd | 182 | ||||
| -rw-r--r-- | extractor/grf.gd | 225 | ||||
| -rw-r--r-- | extractor/map.gd | 34 | ||||
| -rw-r--r-- | extractor/rsm_format.gd | 521 | ||||
| -rw-r--r-- | extractor/rsw_format.gd | 34 | ||||
| -rw-r--r-- | extractor/version.gd | 20 | 
6 files changed, 799 insertions, 217 deletions
| diff --git a/extractor/action_format.gd b/extractor/action_format.gd index d61c590..25a794e 100644 --- a/extractor/action_format.gd +++ b/extractor/action_format.gd @@ -82,6 +82,188 @@ static func from_bytes(bytes: ByteStream) -> ActionFormat:  	return action_format +func convert(name: String, sprites_path: String) -> Node2D: +	var node := Node2D.new() +	node.name = name +	node.set_script(load("res://extractor/actions.gd")) +	 +	var animation_player := AnimationPlayer.new() +	animation_player.name = "AnimationPlayer" +	animation_player.unique_name_in_owner = true +	node.add_child(animation_player) +	animation_player.owner = node +	 +	var sprite_layers := CanvasGroup.new() +	sprite_layers.name = "SpriteLayers" +	sprite_layers.unique_name_in_owner = true +	 +	node.add_child(sprite_layers) +	sprite_layers.owner = node +	 +	var track_properties = [ +		"animation", +		"frame", +		"speed_scale", +		"position", +		"self_modulate", +		"scale", +		"rotation_degrees", +		"flip_h", +		"visible", +	] +	 +	var sprite_frames := SpriteFrames.new() +	#sprite_frames.add_animation("default") +	for img_file_path in DirAccess.get_files_at(sprites_path): +		if img_file_path.ends_with(".png"): +			sprite_frames.add_frame("default", load("%s/%s" % [sprites_path, img_file_path])) +	 +	var animation_library := AnimationLibrary.new() +	 +	# get max number of sprite layers for all actions +	var action_sprite_layers_max_count = actions.reduce(func(accum, action: ActionData): +		return max(accum, action.motions.reduce(func(accum2, motion: Motion): +			return max(accum2, motion.sprite_layer_count) +		, 0)) +	, 0) +	 +	# add Nodes for each sprite layer +	for sprite_layer_idx in action_sprite_layers_max_count: +		var sprite = AnimatedSprite2D.new() +		sprite.centered = false # 必要!! +		sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST +		sprite.sprite_frames = sprite_frames +		sprite.name = str(sprite_layer_idx).pad_zeros(3) +		sprite_layers.add_child(sprite) +		sprite.owner = node +	 +	for action_idx in actions.size(): +		var action: ActionData = actions[action_idx] +		var frame_timing_base := ((frame_times[action_idx] * 24) / 1000) +		 +		# TODO: check why this is necessary +		if sprites_path.contains("cursors") and action_idx == 0: +			frame_timing_base = ((frame_times[action_idx] * 24 * 2) / 1000) +		 +		# add animation for each action +		var animation := Animation.new() +		animation.loop_mode = Animation.LOOP_LINEAR +		animation.length = frame_timing_base * action.motion_count +		animation_library.add_animation(str(action_idx).pad_zeros(3), animation) +		 +		# get max number of sprite layers for current action motions +		var motion_sprite_layers_max_count = action.motions.reduce(func(accum, motion: Motion): +			return max(accum, motion.sprite_layer_count) +		, 0) +		 +		# add animation tracks for each sprite layer +		for sprite_layer_idx in motion_sprite_layers_max_count: +			var sprite := sprite_layers.get_child(sprite_layer_idx) +			for property_idx in track_properties.size(): +				var track_idx = (sprite_layer_idx * track_properties.size()) + property_idx +				animation.add_track(Animation.TYPE_VALUE, track_idx) +				animation.value_track_set_update_mode(track_idx, Animation.UPDATE_DISCRETE) +				animation.track_set_path( +					track_idx, +					"%s:%s" % ["SpriteLayers/" + sprite.name, track_properties[property_idx]] +				) +		 +		for i in range(motion_sprite_layers_max_count, action_sprite_layers_max_count): +			var sprite := sprite_layers.get_child(i) +			var track_idx = animation.add_track(Animation.TYPE_VALUE) +			animation.track_set_path( +				track_idx, +				"%s:visible" % ["SpriteLayers/" + sprite.name] +			) +			animation.track_insert_key(track_idx, 0.0, false) +		 +		# add animation tracks +		for motion_idx in action.motions.size(): +			var motion: Motion = action.motions[motion_idx] +			 +			# TODO: no animations to speak of available ? sprite_index = -1 +			#if motion.event_id == -1: +				#continue +			 +			var timing = motion_idx * frame_timing_base +			var visible_key = 0 +			 +			# add visible = false animation tracks to other sprite_layers +			for i in motion_sprite_layers_max_count: +				var track_idx = i * track_properties.size() + track_properties.find("visible") +				visible_key = animation.track_insert_key(track_idx, timing, false) +			 +			for sprite_layer_idx in motion.sprite_layers.size(): +				var layer: SpriteLayer = motion.sprite_layers[sprite_layer_idx] +				 +				if layer.sprite_index == -1: +					continue +				 +				var track_base_idx = sprite_layer_idx * track_properties.size() +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("animation"), +					timing, +					"default" +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("frame"), +					timing, +					layer.sprite_index +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("speed_scale"), +					timing, +					1.0 +				) +				 +				var layer_image := sprite_frames.get_frame_texture("default", layer.sprite_index) +				var position: Vector2 = layer.get_position() - ceil(layer_image.get_size() / 2) # for fixing half pixel drawing +				var rotated = layer_image.get_size().rotated(deg_to_rad(layer.rotation_degrees)) +				var distance = layer_image.get_size() - rotated +				animation.track_insert_key( +					track_base_idx + track_properties.find("position"), +					timing, +					position + (distance / 2) +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("self_modulate"), +					timing, +					layer.get_color() +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("scale"), +					timing, +					layer.get_scale() +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("rotation_degrees"), +					timing, +					layer.rotation_degrees +				) +				 +				animation.track_insert_key( +					track_base_idx + track_properties.find("flip_h"), +					timing, +					layer.flip_h +				) +				 +				animation.track_set_key_value( +					track_base_idx + track_properties.find("visible"), +					visible_key, +					true +				) +	 +	animation_player.add_animation_library("", animation_library) +	 +	return node + +  class ActionData:  	## Byte Type: u32 [br]  	## Byte Length: 4 diff --git a/extractor/grf.gd b/extractor/grf.gd index 1d19046..a723fdc 100644 --- a/extractor/grf.gd +++ b/extractor/grf.gd @@ -230,186 +230,15 @@ func convert(destination: String = "res://client_data"):  			var sprite = SpriteFormat.from_bytes(file_entry.get_contents(file_access))  			sprite.save_to_file(base_file_directory_path) -		elif file_path.ends_with(".act") and file_path.contains(player_head_path_part): -			continue +		elif file_path.ends_with(".act") and file_path.contains(player_head_path_part): #or file_path.contains(player_body_path_part): +			#continue  			if not FileAccess.file_exists("%s/000.png.import" % base_file_directory_path):  				continue -			var scene := PackedScene.new() -			var scene_root := Node2D.new() -			scene_root.name = "Actions" -			scene_root.set_script(load("res://extractor/actions.gd")) -			 -			var animation_player := AnimationPlayer.new() -			animation_player.name = "AnimationPlayer" -			animation_player.unique_name_in_owner = true -			scene_root.add_child(animation_player) -			animation_player.owner = scene_root -			 -			var sprite_layers := CanvasGroup.new() -			sprite_layers.name = "SpriteLayers" -			sprite_layers.unique_name_in_owner = true -			 -			scene_root.add_child(sprite_layers) -			sprite_layers.owner = scene_root -			 -			var track_properties = [ -				"animation", -				"frame", -				"speed_scale", -				"position", -				"self_modulate", -				"scale", -				"rotation_degrees", -				"flip_h", -				"visible", -			] -			 -			var sprite_frames := SpriteFrames.new() -			#sprite_frames.add_animation("default") -			for img_file_path in DirAccess.get_files_at(base_file_directory_path): -				if img_file_path.ends_with(".png"): -					sprite_frames.add_frame("default", load("%s/%s" % [base_file_directory_path, img_file_path])) -			 -			var animation_library := AnimationLibrary.new() -			var action_data := ActionFormat.from_bytes(ByteStream.from_bytes(file_entry.get_contents(file_access))) -			 -			# get max number of sprite layers for all actions -			var action_sprite_layers_max_count = action_data.actions.reduce(func(accum, action: ActionFormat.ActionData): -				return max(accum, action.motions.reduce(func(accum2, motion: ActionFormat.Motion): -					return max(accum2, motion.sprite_layer_count) -				, 0)) -			, 0) +			var action := ActionFormat.from_bytes(ByteStream.from_bytes(file_entry.get_contents(file_access))) +			var scene_root := action.convert(file_name, base_file_directory_path) -			# add Nodes for each sprite layer -			for sprite_layer_idx in action_sprite_layers_max_count: -				var sprite = AnimatedSprite2D.new() -				sprite.centered = false # 必要!! -				sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST -				sprite.sprite_frames = sprite_frames -				sprite.name = str(sprite_layer_idx).pad_zeros(3) -				sprite_layers.add_child(sprite) -				sprite.owner = scene_root -			 -			for action_idx in action_data.actions.size(): -				var action: ActionFormat.ActionData = action_data.actions[action_idx] -				var frame_timing_base := ((action_data.frame_times[action_idx] * 24) / 1000) -				 -				if file_path.contains("cursors") and action_idx == 0: -					frame_timing_base = ((action_data.frame_times[action_idx] * 24 * 2) / 1000) -				 -				# add animation for each action -				var animation := Animation.new() -				animation.loop_mode = Animation.LOOP_LINEAR -				animation.length = frame_timing_base * action.motion_count -				animation_library.add_animation(str(action_idx).pad_zeros(3), animation) -				 -				# get max number of sprite layers for current action motions -				var motion_sprite_layers_max_count = action.motions.reduce(func(accum, motion: ActionFormat.Motion): -					return max(accum, motion.sprite_layer_count) -				, 0) -				 -				# add animation tracks for each sprite layer -				for sprite_layer_idx in motion_sprite_layers_max_count: -					var sprite := sprite_layers.get_child(sprite_layer_idx) -					for property_idx in track_properties.size(): -						var track_idx = (sprite_layer_idx * track_properties.size()) + property_idx -						animation.add_track(Animation.TYPE_VALUE, track_idx) -						animation.value_track_set_update_mode(track_idx, Animation.UPDATE_DISCRETE) -						animation.track_set_path( -							track_idx, -							"%s:%s" % ["SpriteLayers/" + sprite.name, track_properties[property_idx]] -						) -				 -				for i in range(motion_sprite_layers_max_count, action_sprite_layers_max_count): -					var sprite := sprite_layers.get_child(i) -					var track_idx = animation.add_track(Animation.TYPE_VALUE) -					animation.track_set_path( -						track_idx, -						"%s:visible" % ["SpriteLayers/" + sprite.name] -					) -					animation.track_insert_key(track_idx, 0.0, false) -				 -				# add animation tracks -				for motion_idx in action.motions.size(): -					var motion: ActionFormat.Motion = action.motions[motion_idx] -					 -					# TODO: no animations to speak of available ? -					if motion.event_id == -1: -						continue -					 -					var timing = motion_idx * frame_timing_base -					var visible_key = 0 -					 -					# add visible = false animation tracks to other sprite_layers -					for i in motion_sprite_layers_max_count: -						var track_idx = i * track_properties.size() + track_properties.find("visible") -						visible_key = animation.track_insert_key(track_idx, timing, false) -					 -					for sprite_layer_idx in motion.sprite_layers.size(): -						var layer: ActionFormat.SpriteLayer = motion.sprite_layers[sprite_layer_idx] -						 -						var track_base_idx = sprite_layer_idx * track_properties.size() -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("animation"), -							timing, -							"default" -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("frame"), -							timing, -							layer.sprite_index -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("speed_scale"), -							timing, -							1.0 -						) -						 -						var layer_image := sprite_frames.get_frame_texture("default", layer.sprite_index) -						var position: Vector2 = layer.get_position() - ceil(layer_image.get_size() / 2) # for fixing half pixel drawing -						var rotated = layer_image.get_size().rotated(deg_to_rad(layer.rotation_degrees)) -						var distance = layer_image.get_size() - rotated -						animation.track_insert_key( -							track_base_idx + track_properties.find("position"), -							timing, -							position + (distance / 2) -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("self_modulate"), -							timing, -							layer.get_color() -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("scale"), -							timing, -							layer.get_scale() -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("rotation_degrees"), -							timing, -							layer.rotation_degrees -						) -						 -						animation.track_insert_key( -							track_base_idx + track_properties.find("flip_h"), -							timing, -							layer.flip_h -						) -						 -						animation.track_set_key_value( -							track_base_idx + track_properties.find("visible"), -							visible_key, -							true -						) -			 -			animation_player.add_animation_library("", animation_library) +			var scene := PackedScene.new()  			scene.pack(scene_root)  			# TODO: doesn't work if png is not imported via editor focus => run game twice @@ -418,53 +247,15 @@ func convert(destination: String = "res://client_data"):  		# Map.rsw and .gnd and .gat -		if file_path.ends_with(".rsw") and file_path.contains("pay_dun"): +		if file_path.ends_with(".rsw") and (file_path.contains("pay_dun") or file_path.contains("iz_int") or file_path.contains("int_land")):  			var rsw = RSWFormat.from_bytes(ByteStream.from_bytes(file_entry.get_contents(file_access))) -			 -			var gnd_file_path = "res://client_data/data/%s" % rsw.gnd_file -			var gnd = GNDFormat.from_bytes(ByteStream.from_bytes(FileAccess.get_file_as_bytes(gnd_file_path))) -			 -			var gat_file_path = "res://client_data/data/%s" % rsw.gat_file -			var gat = GATFormat.from_bytes(ByteStream.from_bytes(FileAccess.get_file_as_bytes(gat_file_path))) +			var scene_root := rsw.convert(file_name, "res://client_data")  			var scene := PackedScene.new() -			var scene_root := Node3D.new() -			scene_root.name = file_name -			 -			for resource in rsw.map_resources: -				if resource is RSWFormat.SpatialAudioSource: -					var audio_file_path := "res://client_data/data/wav/%s" % resource.audio_file -					if not FileAccess.file_exists(audio_file_path): -						continue -					 -					var audio = AudioStreamPlayer3D.new() -					audio.stream = load(audio_file_path) -					audio.name = resource.audio_file -					audio.position = resource.get_position() -					audio.volume_linear = resource.volume_gain -					audio.max_distance = resource.audio_range -					scene_root.add_child(audio, true) -					audio.owner = scene_root -			 -			var surfrace_tool := SurfaceTool.new() -			for surface: GNDFormat.Surface in gnd.surfaces: -				pass -				#surfrace_tool.add_vertex() -			  			scene.pack(scene_root)  			ResourceSaver.save(scene, "%s/%s/%s.tscn" % [destination, base_directory_path, file_name])  static func decode_string(bytes: PackedByteArray): +	# TODO: use iconv to decode EUC-KR  	return bytes.get_string_from_ascii() -	@warning_ignore("unreachable_code") -	# 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/map.gd b/extractor/map.gd new file mode 100644 index 0000000..c3a9713 --- /dev/null +++ b/extractor/map.gd @@ -0,0 +1,34 @@ +extends Node3D + + +func _ready() -> void: +	# add player +	var map_server_login_success_packet: MapServerLoginSuccessPacket = Network.map_server.received_packets[MapServerLoginSuccessPacket.HEADER][0] +	var initial_player_position: Vector2 = map_server_login_success_packet.get_position() +	 +	var player = preload("res://entities/player.tscn").instantiate() +	player.position = Vector3(initial_player_position.x, 0, initial_player_position.y) +	add_child(player) +	 +	# listen to packets +	Network.map_server.received_packet.connect(func(packet: Packet): +		if packet is ChangeMapPacket: +			player.position.x = packet.get_position().x +			player.position.z = packet.get_position().y +	) +	 +	# play audio +	for node: AudioStreamPlayer3D in find_children("se_*"): +		node.play() +	 +	# add HUD TODO: add all HUD as HUD scene +	var chat_window = preload("res://ui/chat_window.tscn").instantiate() +	add_child(chat_window) +	 +	# TODO: load map. +	# TODO: whatever else needs to be loaded after converting from rsw +	 +	var map_loaded_packet := MapLoadedPacket.new() +	Network.map_server.send(map_loaded_packet) +	 +	# TODO: check which map server packets to send next diff --git a/extractor/rsm_format.gd b/extractor/rsm_format.gd index d3dabb0..cf83af9 100644 --- a/extractor/rsm_format.gd +++ b/extractor/rsm_format.gd @@ -73,10 +73,56 @@ static func from_bytes(bytes: ByteStream) -> RSMFormat:  	version.minor = bytes.decode_u8()  	rsm_format.version = version +	rsm_format.animation_length = bytes.decode_u32() +	rsm_format.shade_type = bytes.decode_u32() +	 +	if version.higher_than(1, 3): # >= 1.4 +		rsm_format.alpha = bytes.decode_u8() +	 +	if version.lower_than(2, 2): # < 2.2 +		rsm_format.reserved = bytes.get_buffer(16).bytes +	 +	if version.higher_than(2, 1): # >= 2.2 +		rsm_format.frames_per_second = bytes.decode_float() +	 +	if version.lower_than(2, 3): # < 2.3 +		rsm_format.texture_count = bytes.decode_u32() +		 +		rsm_format.texture_names = [] as Array[String] +		for _n in rsm_format.texture_count: +			rsm_format.texture_names.append(bytes.get_string_from_utf8(40)) +	 +	rsm_format.root_node_name = bytes.get_string_from_utf8(40) +	 +	if version.higher_than(2, 1): # >= 2.2 +		rsm_format.root_node_count = bytes.decode_u32() +		 +		rsm_format.root_node_names = [] as Array[String] +		for _n in rsm_format.root_node_count: +			rsm_format.root_node_names.append(bytes.get_string_from_utf8(40)) +	 +	rsm_format.node_count = bytes.decode_u32() +	rsm_format.nodes = [] as Array[ModelNode] +	for _n in rsm_format.node_count: +		rsm_format.nodes.append(ModelNode.from_bytes(bytes, version)) +	  	print(inst_to_dict(rsm_format)) +	#print(inst_to_dict(rsm_format.nodes[0].texture_coordinates[0])) +	#rsm_format.nodes[0].texture_coordinates.clear() +	#print(inst_to_dict(rsm_format.nodes[0].faces[0])) +	#rsm_format.nodes[0].faces.clear() +	#print(inst_to_dict(rsm_format.nodes[0]))  	return rsm_format +func convert() -> Node3D: +	var node := Node3D.new() +	node.name = root_node_name +	#node.set_script(load("res://extractor/model.gd")) +	 +	return node + +  class ModelNode:  	## Byte Type: u8 [br]  	## Byte Length: 40 @@ -85,3 +131,478 @@ class ModelNode:  	## Byte Type: u8 [br]  	## Byte Length: 40  	var parent_node_name: String +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	## Versions: [<2.3] +	var texture_count: int +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	## Length: [member texture_count] [br] +	## Versions: [<2.3] +	var texture_indices: Array[int] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	## Versions: [>=2.3] +	var texture_name_count: int +	 +	## Byte Length: 40 [br] +	## Length: [member texture_name_count] [br] +	## Versions: [>=2.3] +	var texture_names: Array[String] +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 9 [br] +	## Length: 9 [br] +	## 3 x 3 Matrix. Each element represents a column. +	var offset_matrix: Array[Vector3] +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	## Versions: [<2.2] +	var translation_1: Vector3 +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	var translation_2: Vector3 +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 [br] +	## Versions: [<2.2] +	var rotation_angle: float +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	## Versions: [<2.2] +	var rotation_axis: Vector3 +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	## Versions: [<2.2] +	var scale: Vector3 +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var vertex_position_count: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 * [member vertex_position_count] [br] +	## Length: [member vertex_position_count] +	var vertex_positions: Array[Vector3] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var texture_coordinate_count: int +	 +	## Length: [member texture_coordinate_count] +	var texture_coordinates: Array[TextureCoordinate] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var face_count: int +	 +	## Length: [member face_count] +	var faces: Array[Face] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	## Versions: [>=1.6] +	var scale_keyframe_count: int +	 +	## Length: [member scale_keyframe_count] +	## Versions: [>=1.6] +	var scale_keyframes: Array[ScaleKeyframe] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var rotation_keyframe_count: int +	 +	## Length: [member scale_keyframe_count] +	var rotation_keyframes: Array[RotationKeyframe] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	## Versions: [>=2.2] +	var translation_keyframe_count: int +	 +	## Length: [member scale_keyframe_count] +	## Versions: [>=2.2] +	var translation_keyframes: Array[TranslationKeyframe] +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	## Versions: [>=2.3] +	var textures_keyframe_count: int +	 +	## Length: [member scale_keyframe_count] +	## Versions: [>=2.3] +	var textures_keyframes: Array[TexturesKeyframe] +	 +	 +	static func from_bytes(bytes: ByteStream, version: Version) -> ModelNode: +		var node = ModelNode.new() +		 +		node.node_name = bytes.get_string_from_utf8(40) +		node.parent_node_name = bytes.get_string_from_utf8(40) +		 +		if version.lower_than(2, 3): # < 2.3 +			node.texture_count = bytes.decode_u32() +			 +			node.texture_indices = [] as Array[int] +			for _n in node.texture_count: +				node.texture_indices.append(bytes.decode_u32()) +		 +		if version.higher_than(2, 2): # >= 2.3 +			node.texture_name_count = bytes.decode_u32() +			 +			node.texture_names = [] as Array[String] +			for _n in node.texture_name_count: +				node.texture_names.append(bytes.get_string_from_utf8(40)) +		 +		node.offset_matrix = [] as Array[Vector3] +		for _in in 3: +			node.offset_matrix.append(Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			)) +		 +		if version.lower_than(2, 2): +			node.translation_1 = Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			) +		 +		node.translation_2 = Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			) +		 +		if version.lower_than(2, 2): +			node.rotation_angle = bytes.decode_float() +			node.rotation_axis = Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			) +			node.scale = Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			) +		 +		node.vertex_position_count = bytes.decode_u32() +		node.vertex_positions = [] as Array[Vector3] +		 +		for _n in node.vertex_position_count: +			node.vertex_positions.append(Vector3( +				bytes.decode_float(), +				bytes.decode_float(), +				bytes.decode_float() +			)) +		 +		node.texture_coordinate_count = bytes.decode_u32() +		node.texture_coordinates = [] as Array[TextureCoordinate] +		 +		for _n in node.texture_coordinate_count: +			node.texture_coordinates.append(TextureCoordinate.from_bytes(bytes, version)) +		 +		node.face_count = bytes.decode_u32() +		node.faces = [] as Array[Face] +		 +		for _n in node.face_count: +			node.faces.append(Face.from_bytes(bytes, version)) +		 +		if version.higher_than(1, 5): # >= 1.6 +			node.scale_keyframe_count = bytes.decode_u32() +			node.scale_keyframes = [] as Array[ScaleKeyframe] +			 +			for _n in node.scale_keyframe_count: +				node.scale_keyframes.append(ScaleKeyframe.from_bytes(bytes)) +		 +		node.rotation_keyframe_count = bytes.decode_u32() +		node.rotation_keyframes = [] as Array[RotationKeyframe] +		 +		for _n in node.rotation_keyframe_count: +			node.rotation_keyframes.append(RotationKeyframe.from_bytes(bytes)) +		 +		if version.higher_than(2, 1): # >= 2.2 +			node.translation_keyframe_count = bytes.decode_u32() +			node.translation_keyframes = [] as Array[ScaleKeyframe] +			 +			for _n in node.translation_keyframe_count: +				node.translation_keyframes.append(TranslationKeyframe.from_bytes(bytes)) +		 +		if version.higher_than(2, 2): # >= 2.3 +			node.textures_keyframe_count = bytes.decode_u32() +			node.textures_keyframes = [] as Array[TexturesKeyframe] +			 +			for _n in node.textures_keyframe_count: +				node.textures_keyframes.append(TexturesKeyframe.from_bytes(bytes)) +		 +		return node + + +class TextureCoordinate: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	## Versions: [>=1.2] +	var color: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 2 [br] +	## Note: possibly wrong if version < 1.2 +	var coordinates: Vector2 +	 +	 +	static func from_bytes(bytes: ByteStream, version: Version) -> TextureCoordinate: +		var data = TextureCoordinate.new() +		 +		if version.higher_than(1, 1): +			data.color = bytes.decode_u32() +		 +		data.coordinates = Vector2( +			bytes.decode_float(), +			bytes.decode_float() +		) +		 +		return data + + +class Face: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	## Versions: [>=2.2] +	var length: int +	 +	## Byte Type: u16 [br] +	## Byte Length: 2 * 3 +	## Length: 3 +	var vertex_position_indices: Array[int] +	 +	## Byte Type: u16 [br] +	## Byte Length: 2 * 3 +	## Length: 3 +	var texture_coordinate_indices: Array[int] +	 +	## Byte Type: u16 [br] +	## Byte Length: 2 +	var texture_index: int +	 +	## Byte Type: u16 [br] +	## Byte Length: 2 +	var padding: int +	 +	## Byte Type: i32 [br] +	## Byte Length: 4 +	var two_sided: int +	 +	## Byte Type: i32 [br] +	## Byte Length: 4 +	var smooth_group: int +	 +	## Byte Type: i32 [br] +	## Length: ([member length] - 24) / 4 [br] +	## Versions: [>=2.2] +	# TODO: saturating_sub? +	var smooth_group_extra: Array[int] +	 +	 +	static func from_bytes(bytes: ByteStream, version: Version) -> Face: +		var data = Face.new() +		 +		if version.higher_than(2, 1): +			data.length = bytes.decode_u32() +		 +		data.vertex_position_indices = [] as Array[int] +		for _in in 3: +			data.vertex_position_indices.append(bytes.decode_u16()) +		 +		data.texture_coordinate_indices = [] as Array[int] +		for _in in 3: +			data.texture_coordinate_indices.append(bytes.decode_u16()) +		 +		data.texture_index = bytes.decode_u16() +		data.padding = bytes.decode_u16() +		data.two_sided = bytes.decode_s32() +		data.smooth_group = bytes.decode_s32() +		 +		if version.higher_than(2, 1): +			data.smooth_group_extra = [] as Array[int] +			 +			for _n in ((data.length - 24) / 4): +				data.smooth_group_extra.append(bytes.decode_s32()) +		 +		return data + + +class ScaleKeyframe: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	var frame: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	var scale: Vector3 +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 [br] +	var reserved: float +	 +	 +	static func from_bytes(bytes: ByteStream) -> ScaleKeyframe: +		var data = ScaleKeyframe.new() +		 +		data.frame = bytes.decode_u32() +		data.scale = Vector3( +			bytes.decode_float(), +			bytes.decode_float(), +			bytes.decode_float() +		) +		data.reserved = bytes.decode_float() +		 +		return data + + +class RotationKeyframe: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	var frame: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	var rotation: Vector4 +	 +	 +	static func from_bytes(bytes: ByteStream) -> RotationKeyframe: +		var data = RotationKeyframe.new() +		 +		data.frame = bytes.decode_u32() +		data.rotation = Vector4( +			bytes.decode_float(), +			bytes.decode_float(), +			bytes.decode_float(), +			bytes.decode_float() +		) +		 +		return data + + +class TranslationKeyframe: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	var frame: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 * 3 [br] +	var translation: Vector3 +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 [br] +	var reserved: float +	 +	 +	static func from_bytes(bytes: ByteStream) -> ScaleKeyframe: +		var data = ScaleKeyframe.new() +		 +		data.frame = bytes.decode_u32() +		data.translation = Vector3( +			bytes.decode_float(), +			bytes.decode_float(), +			bytes.decode_float() +		) +		data.reserved = bytes.decode_float() +		 +		return data + + +enum TextureOperation { +	## Texture translation on the X axis. The texture is tiled. +	Translation_X, +	 +	## Texture translation on the Y axis. The texture is tiled. +	Translation_Y, +	 +	## Texture multiplication on the X axis. The texture is tiled. +	Scale_X, +	 +	## Texture multiplication on the Y axis. The texture is tiled. +	Scale_Y, +	 +	## Texture rotation around (0, 0). The texture is not tiled. +	Rotation, +} + + +class TexturesKeyframe: +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var texture_index: int +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var texture_keyframe_count: int +	 +	## Length: [member texture_keyframe_count] +	var texture_keyframes: Array[TextureKeyframe] +	 +	 +	static func from_bytes(bytes: ByteStream) -> TexturesKeyframe: +		var data = TexturesKeyframe.new() +		 +		data.texture_index = bytes.decode_u32() +		data.texture_keyframe_count = bytes.decode_u32() +		 +		data.texture_keyframes = [] as Array[TextureKeyframe] +		for _n in data.texture_keyframe_count: +			data.texture_keyframes.append(TextureKeyframe.from_bytes(bytes)) +		 +		return data + + +class TextureKeyframe: +	## Byte Type: u8 +	var operation_type: TextureOperation +	 +	## Byte Type: u32 [br] +	## Byte Length: 4 +	var frame_count: int +	 +	## Length: [member frame_count] +	var texture_frames: Array[TextureFrame] +	 +	 +	static func from_bytes(bytes: ByteStream) -> TextureKeyframe: +		var data = TextureKeyframe.new() +		 +		data.operation_type = bytes.decode_u8() +		data.frame_count = bytes.decode_u32() +		 +		data.texture_frames = [] as Array[TextureFrame] +		for _n in data.frame_count: +			data.texture_frames.append(TextureFrame.from_bytes(bytes)) +		 +		return data + + +class TextureFrame: +	## Byte Type: u32 [br] +	## Byte Length: 4 [br] +	var frame: int +	 +	## Byte Type: f32 [br] +	## Byte Length: 4 [br] +	var translation: float +	 +	 +	static func from_bytes(bytes: ByteStream) -> TextureFrame: +		var data = TextureFrame.new() +		 +		data.frame = bytes.decode_u32() +		data.translation = bytes.decode_float() +		 +		return data diff --git a/extractor/rsw_format.gd b/extractor/rsw_format.gd index ea1bf26..831a1cb 100644 --- a/extractor/rsw_format.gd +++ b/extractor/rsw_format.gd @@ -105,6 +105,40 @@ static func from_bytes(bytes: ByteStream) -> RSWFormat:  	return rsw_format +func convert(name: String, data_path: String) -> Node3D: +	var gnd_file_path = "%s/data/%s" % [data_path, gnd_file] +	var gnd = GNDFormat.from_bytes(ByteStream.from_bytes(FileAccess.get_file_as_bytes(gnd_file_path))) +	 +	var gat_file_path = "%s/data/%s" % [data_path, gat_file] +	var gat = GATFormat.from_bytes(ByteStream.from_bytes(FileAccess.get_file_as_bytes(gat_file_path))) +	 +	var node := Node3D.new() +	node.name = name +	node.set_script(load("res://extractor/map.gd")) +	 +	for resource in map_resources: +		if resource is RSWFormat.SpatialAudioSource: +			var audio_file_path := "%s/data/wav/%s" % [data_path, resource.audio_file] +			if not FileAccess.file_exists(audio_file_path): +				continue +			 +			var audio = AudioStreamPlayer3D.new() +			audio.stream = load(audio_file_path) +			audio.name = resource.audio_file +			audio.position = resource.get_position() +			audio.volume_linear = resource.volume_gain +			audio.max_distance = resource.audio_range +			node.add_child(audio, true) +			audio.owner = node +	 +	var surface_tool := SurfaceTool.new() +	for surface: GNDFormat.Surface in gnd.surfaces: +		pass +		#surface_tool.add_vertex() +	 +	return node + +  class WaterConfiguration:  	## Byte Type: f32 [br]  	## Byte Length: 4 diff --git a/extractor/version.gd b/extractor/version.gd index 679a8f0..ae97a10 100644 --- a/extractor/version.gd +++ b/extractor/version.gd @@ -11,5 +11,25 @@ var major: int  var minor: int +func lower_than(compare_major: int, compare_minor: int) -> bool: +	if (major > compare_major): +		return false +	 +	if (major == compare_major): +		return minor < compare_minor +	 +	return true + + +func higher_than(compare_major: int, compare_minor: int) -> bool: +	if (major > compare_major): +		return true +	 +	if (major == compare_major): +		return minor > compare_minor +	 +	return false + +  func _to_string() -> String:  	return "%s.%s" % [major, minor] | 
