summaryrefslogtreecommitdiff
path: root/extractor
diff options
context:
space:
mode:
Diffstat (limited to 'extractor')
-rw-r--r--extractor/action_format.gd2
-rw-r--r--extractor/gat_format.gd8
-rw-r--r--extractor/gnd_format.gd294
-rw-r--r--extractor/grf.gd12
-rw-r--r--extractor/rsw_format.gd519
5 files changed, 825 insertions, 10 deletions
diff --git a/extractor/action_format.gd b/extractor/action_format.gd
index 97f6771..d61c590 100644
--- a/extractor/action_format.gd
+++ b/extractor/action_format.gd
@@ -49,7 +49,7 @@ func get_byte_length() -> int:
static func from_bytes(bytes: ByteStream) -> ActionFormat:
var action_format = ActionFormat.new()
- bytes.seek(2)
+ bytes.advance(action_format.signature.length())
@warning_ignore("shadowed_variable")
var version = Version.new()
diff --git a/extractor/gat_format.gd b/extractor/gat_format.gd
index 213972a..712657b 100644
--- a/extractor/gat_format.gd
+++ b/extractor/gat_format.gd
@@ -45,22 +45,22 @@ static func from_bytes(bytes: ByteStream) -> GATFormat:
class Tile:
## Byte Type: f32 [br]
## Byte Length: 4 [br]
- ## Orignal Coordinates_ (0, 0)
+ ## Orignal Coordinates: (0, 0)
var bottom_left_altitude: int
## Byte Type: f32 [br]
## Byte Length: 4 [br]
- ## Orignal Coordinates_ (1, 0)
+ ## Orignal Coordinates: (1, 0)
var bottom_right_altitude: int
## Byte Type: f32 [br]
## Byte Length: 4 [br]
- ## Orignal Coordinates_ (0, 1)
+ ## Orignal Coordinates: (0, 1)
var top_left_altitude: int
## Byte Type: f32 [br]
## Byte Length: 4 [br]
- ## Orignal Coordinates_ (1, 1)
+ ## Orignal Coordinates: (1, 1)
var top_right_altitude: int
## Byte Type: u32 ?[br]
diff --git a/extractor/gnd_format.gd b/extractor/gnd_format.gd
new file mode 100644
index 0000000..f13956e
--- /dev/null
+++ b/extractor/gnd_format.gd
@@ -0,0 +1,294 @@
+class_name GNDFormat
+
+
+## Byte Length: 4 [br]
+## GRAT
+var signature: String = "GRGN"
+
+## Byte Type: u8 [br]
+## Byte Length: 2
+var version: Version
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var width: int
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var height: int
+
+## Byte Type: f32 [br]
+## Byte Length: 4 [br]
+## Always 10.
+var scale: float
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var texture_count: int
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+## Always 80.
+var texture_path_length: int
+
+## Byte Length: [member texture_count] * [member texture_path_length] [br]
+var texture_paths: Array
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var light_map_slice_count: int
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var light_map_width: int
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var light_map_slice_height: int
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var light_map_cells_per_grid: int
+
+## Length: [member light_map_slice_count]
+var light_map_slices: Array
+
+## Byte Type: i32 [br]
+## Byte Length: 4
+var surface_count: int
+
+## Length: [member surface_count]
+var surfaces: Array[Surface]
+
+## Length: [member width] * [member height]
+var ground_mesh_cubes: Array[GroundMeshCube]
+
+
+static func from_bytes(bytes: ByteStream) -> GNDFormat:
+ var gnd_format = GNDFormat.new()
+
+ bytes.advance(gnd_format.signature.length())
+
+ @warning_ignore("shadowed_variable")
+ var version = Version.new()
+ version.major = bytes.decode_u8()
+ version.minor = bytes.decode_u8()
+ gnd_format.version = version
+
+ if version.minor > 7:
+ print(version)
+ return gnd_format
+
+ gnd_format.width = bytes.decode_s32()
+ gnd_format.height = bytes.decode_s32()
+ gnd_format.scale = bytes.decode_float()
+ gnd_format.texture_count = bytes.decode_s32()
+ gnd_format.texture_path_length = bytes.decode_s32()
+
+ gnd_format.texture_paths = []
+ for _n in gnd_format.texture_count:
+ gnd_format.texture_paths.append(
+ bytes.get_string_from_ascii(gnd_format.texture_path_length)
+ )
+
+ gnd_format.light_map_slice_count = bytes.decode_s32()
+ gnd_format.light_map_width = bytes.decode_s32()
+ gnd_format.light_map_slice_height = bytes.decode_s32()
+ gnd_format.light_map_cells_per_grid = bytes.decode_s32()
+
+ #gnd_format.light_map_slices = []
+ #for _n in gnd_format.light_map_slice_count:
+ #gnd_format.light_map_slices.append(LightMapSlice.from_bytes(bytes))
+ bytes.advance(
+ gnd_format.light_map_slice_count *
+ gnd_format.light_map_width *
+ gnd_format.light_map_slice_height *
+ 4
+ )
+
+ gnd_format.surface_count = bytes.decode_s32()
+
+ gnd_format.surfaces = [] as Array[Surface]
+ for _n in gnd_format.surface_count:
+ gnd_format.surfaces.append(Surface.from_bytes(bytes))
+
+ gnd_format.ground_mesh_cubes = [] as Array[GroundMeshCube]
+ for _n in (gnd_format.width * gnd_format.height):
+ gnd_format.ground_mesh_cubes.append(GroundMeshCube.from_bytes(bytes))
+
+ #print(inst_to_dict(gnd_format))
+ return gnd_format
+
+
+class LightMapSlice:
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var pixel_format: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var width: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var height: int
+
+ ## Byte Length: [member width] * [member height]
+ var shadow_map_pixels: ByteStream
+
+ ## Byte Length: [member width] * [member height]
+ var light_map_pixels: ByteStream
+
+
+ static func from_bytes(bytes: ByteStream) -> LightMapSlice:
+ var slice = LightMapSlice.new()
+
+ slice.pixel_format = bytes.decode_s32()
+ slice.width = bytes.decode_s32()
+ slice.height = bytes.decode_s32()
+ slice.shadow_map_pixels = bytes.get_buffer(slice.width * slice.height)
+ slice.light_map_pixels = bytes.get_buffer(slice.width * slice.height)
+
+ #print(inst_to_dict(slice))
+ return slice
+
+
+class Surface:
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var u_bottom_left: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var u_bottom_right: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var u_top_left: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var u_top_right: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var v_bottom_left: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var v_bottom_right: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var v_top_left: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var v_top_right: float
+
+ ## Byte Type: i16 [br]
+ ## Byte Length: 2
+ var texture_index: int
+
+ ## Byte Type: i16 [br]
+ ## Byte Length: 2
+ var light_map_index: int
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 1
+ var vertex_color_blue: int
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 1
+ var vertex_color_green: int
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 1
+ var vertex_color_red: int
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 1
+ var vertex_color_alpha: int
+
+
+ func get_uvs() -> Dictionary: # Dictionary[Vector2, Vector2]
+ return {
+ Vector2(0, 0): Vector2(u_top_left, v_top_left),
+ Vector2(1, 0): Vector2(u_top_right, v_top_right),
+ Vector2(0, 1): Vector2(u_bottom_left, v_bottom_left),
+ Vector2(1, 1): Vector2(u_bottom_right, v_bottom_right),
+ }
+
+
+ static func from_bytes(bytes: ByteStream) -> Surface:
+ var surface = Surface.new()
+
+ surface.u_bottom_left = bytes.decode_float()
+ surface.u_bottom_right = bytes.decode_float()
+ surface.u_top_left = bytes.decode_float()
+ surface.u_top_right = bytes.decode_float()
+ surface.v_bottom_left = bytes.decode_float()
+ surface.v_bottom_right = bytes.decode_float()
+ surface.v_top_left = bytes.decode_float()
+ surface.v_top_right = bytes.decode_float()
+ surface.texture_index = bytes.decode_s16()
+ surface.light_map_index = bytes.decode_s16()
+ surface.vertex_color_blue = bytes.decode_u8()
+ surface.vertex_color_green = bytes.decode_u8()
+ surface.vertex_color_red = bytes.decode_u8()
+ surface.vertex_color_alpha = bytes.decode_u8()
+
+ #print(inst_to_dict(surface))
+ return surface
+
+
+class GroundMeshCube:
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ ## upper left?
+ var bottom_left_height: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ ## upper right?
+ var bottom_right_height: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ ## lower left?
+ var top_left_height: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ ## lower right?
+ var top_right_height: float
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ ## top surface index?
+ var upwards_facing_surface_index: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ ## front surface index?
+ var northern_wall_surface_index: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ ## right surface index?
+ var eastern_wall_surface_index: int
+
+
+ static func from_bytes(bytes: ByteStream) -> GroundMeshCube:
+ var mesh = GroundMeshCube.new()
+
+ mesh.bottom_left_height = bytes.decode_float()
+ mesh.bottom_right_height = bytes.decode_float()
+ mesh.top_left_height = bytes.decode_float()
+ mesh.top_right_height = bytes.decode_float()
+ mesh.upwards_facing_surface_index = bytes.decode_s32()
+ mesh.northern_wall_surface_index = bytes.decode_s32()
+ mesh.eastern_wall_surface_index = bytes.decode_s32()
+
+ #print(inst_to_dict(mesh))
+ return mesh
diff --git a/extractor/grf.gd b/extractor/grf.gd
index 9917a74..08f72ee 100644
--- a/extractor/grf.gd
+++ b/extractor/grf.gd
@@ -190,23 +190,25 @@ static func open(path: String):
return grf
-func extract(destination: String = "res://data"):
+func extract(destination: String = "user://client_data"):
+ DirAccess.make_dir_recursive_absolute(destination)
+
for file_entry in file_entries:
var file_path: String = file_entry.get_file_path()
var base_directory = DirAccess.open(destination)
- base_directory.make_dir_recursive("extracted/" + file_path.get_base_dir())
+ base_directory.make_dir_recursive(file_path.get_base_dir())
- var file = FileAccess.open("%s/extracted/%s" % [destination, file_path], FileAccess.WRITE_READ)
+ var file = FileAccess.open("%s/%s" % [destination, file_path], FileAccess.WRITE_READ)
file.store_buffer(file_entry.get_contents(file_access))
-func convert(destination: String = "res://data"):
+func convert(destination: String = "res://client_data"):
for file_entry in file_entries:
var file_path: String = file_entry.get_file_path()
var base_directory = DirAccess.open(destination)
- var base_directory_path = "extracted/%s" % file_path.get_base_dir()
+ var base_directory_path = "%s" % file_path.get_base_dir()
base_directory.make_dir_recursive(base_directory_path)
base_directory.change_dir(base_directory_path)
diff --git a/extractor/rsw_format.gd b/extractor/rsw_format.gd
new file mode 100644
index 0000000..18874e2
--- /dev/null
+++ b/extractor/rsw_format.gd
@@ -0,0 +1,519 @@
+class_name RSWFormat
+
+
+## Byte Length: 4 [br]
+## GRAT
+var signature: String = "GRSW"
+
+## Byte Type: u8 [br]
+## Byte Length: 2
+var version: Version
+
+## Byte Type: u8, u32 [br]
+## Byte Length: 1, 4
+## Versions: [2.2, 2.5]
+var build_number: int
+
+## Byte Type: u8 [br]
+## Byte Length: 1
+## Versions: [2.5]
+var unknown_render_flag: int
+
+## Byte Type: u8 [br]
+## Byte Length: 40
+var ini_file: String
+
+## Byte Type: u8 [br]
+## Byte Length: 40
+var gnd_file: String
+
+## Byte Type: u8 [br]
+## Byte Length: 40
+var gat_file: String
+
+## Byte Type: u8 [br]
+## Byte Length: 40
+## Versions: [1.4]
+var source_file: String
+
+var water_configuration: WaterConfiguration
+var lighting_parameters: LightingParameters
+
+## Byte Type: u32 [br]
+## Byte Length: 4
+var map_resource_count: int
+
+## Length: [member map_resource_count]
+var map_resources: Array[MapResource]
+
+var quad_tree
+
+
+static func from_bytes(bytes: ByteStream) -> RSWFormat:
+ var rsw_format = RSWFormat.new()
+
+ bytes.advance(rsw_format.signature.length())
+
+ @warning_ignore("shadowed_variable")
+ var version = Version.new()
+ version.major = bytes.decode_u8()
+ version.minor = bytes.decode_u8()
+ rsw_format.version = version
+
+ if version.major < 2:
+ print(version)
+ return rsw_format
+
+ if version.major >= 2 and version.minor >= 5:
+ rsw_format.build_number = bytes.decode_u32()
+ rsw_format.unknown_render_flag = bytes.decode_u8()
+ elif version.major >= 2 and version.minor >= 2:
+ rsw_format.build_number = bytes.decode_u8()
+
+ rsw_format.ini_file = bytes.get_string_from_utf8(40)
+ rsw_format.gnd_file = bytes.get_string_from_utf8(40)
+ rsw_format.gat_file = bytes.get_string_from_utf8(40)
+ rsw_format.source_file = bytes.get_string_from_utf8(40)
+
+ if version.major >= 2 and version.minor <= 5:
+ rsw_format.water_configuration = WaterConfiguration.from_bytes(bytes)
+
+ rsw_format.lighting_parameters = LightingParameters.from_bytes(bytes)
+
+ bytes.advance(16) # map boundaries - bounding box # TODO
+
+ rsw_format.map_resource_count = bytes.decode_u32()
+ rsw_format.map_resources = [] as Array[MapResource]
+ for _n in rsw_format.map_resource_count:
+ var resource_type = bytes.decode_u32()
+ var resource: MapResource
+
+ if resource_type == MapResourceType.Animated3DModel:
+ resource = Animated3DModel.from_bytes(bytes)
+ elif resource_type == MapResourceType.DynamicLightSource:
+ resource = DynamicLightSource.from_bytes(bytes)
+ elif resource_type == MapResourceType.SpatialAudioSource:
+ resource = SpatialAudioSource.from_bytes(bytes)
+ elif resource_type == MapResourceType.ParticleEffectEmitter:
+ resource = ParticleEffectEmitter.from_bytes(bytes)
+
+ rsw_format.map_resources.append(resource)
+
+ # TODO: quadtree
+
+ #print(inst_to_dict(rsw_format))
+ return rsw_format
+
+
+class WaterConfiguration:
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var water_level: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var water_type: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var wave_height: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var wave_speed: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var wave_pitch: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var water_animation_speed: int
+
+
+ static func from_bytes(bytes: ByteStream) -> WaterConfiguration:
+ var config = WaterConfiguration.new()
+
+ config.water_level = bytes.decode_float()
+ config.water_type = bytes.decode_u32()
+ config.wave_height = bytes.decode_float()
+ config.wave_speed = bytes.decode_float()
+ config.wave_pitch = bytes.decode_float()
+ config.water_animation_speed = bytes.decode_u32()
+
+ return config
+
+
+class LightingParameters:
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var longitude: int
+
+ ## Byte Type: i32 [br]
+ ## Byte Length: 4
+ var latitude: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_r: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_g: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_b: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var ambient_color_r: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var ambient_color_g: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var ambient_color_b: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ ## Shadow Map Alpha
+ var intensity: int
+
+
+ func get_diffuse_color() -> Color:
+ return Color(diffuse_color_r, diffuse_color_g, diffuse_color_b)
+
+
+ func get_ambient_color() -> Color:
+ return Color(ambient_color_r, ambient_color_g, ambient_color_b)
+
+
+ static func from_bytes(bytes: ByteStream) -> LightingParameters:
+ var params = LightingParameters.new()
+
+ params.longitude = bytes.decode_s32()
+ params.latitude = bytes.decode_s32()
+ params.diffuse_color_r = bytes.decode_float()
+ params.diffuse_color_g = bytes.decode_float()
+ params.diffuse_color_b = bytes.decode_float()
+ params.ambient_color_r = bytes.decode_float()
+ params.ambient_color_g = bytes.decode_float()
+ params.ambient_color_b = bytes.decode_float()
+ params.intensity = bytes.decode_float()
+
+ return params
+
+
+class MapResource:
+ pass
+
+enum MapResourceType {
+ Animated3DModel = 1,
+ DynamicLightSource = 2,
+ SpatialAudioSource = 3,
+ ParticleEffectEmitter = 4,
+}
+
+enum ModelAnimationType {
+ None = 0,
+ Looping = 2,
+}
+
+enum ParticlePresetEffect {
+ None = -1,
+ Hit_1 = 1,
+ Hit_2 = 2,
+ Hit_3 = 3,
+ Torch = 47,
+ TunaParty = 1097,
+}
+
+
+class Animated3DModel extends MapResource:
+ ## Byte Type: u8 [br]
+ ## Byte Length: 40
+ var name: String
+
+ ## Byte Type: u32 [br]
+ ## Byte Length: 4
+ var animation_type: ModelAnimationType
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var animation_speed_percent: float
+
+ ## Byte Type: u32 [br]
+ ## Byte Length: 4
+ var collision_flags: int
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var model_file: String
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var root_node_name: String
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_z: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var rotation_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var rotation_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var rotation_z: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var scale_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var scale_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var scale_z: float
+
+
+ func get_position() -> Vector3:
+ return Vector3(position_x, position_y, position_z)
+
+
+ func get_rotation() -> Vector3:
+ return Vector3(rotation_x, rotation_y, rotation_z)
+
+
+ func get_scale() -> Vector3:
+ return Vector3(scale_x, scale_y, scale_z)
+
+
+ static func from_bytes(bytes: ByteStream) -> Animated3DModel:
+ var resource = Animated3DModel.new()
+
+ resource.name = bytes.get_string_from_ascii(40)
+ resource.animation_type = bytes.decode_u32()
+ resource.animation_speed_percent = bytes.decode_float()
+ resource.collision_flags = bytes.decode_u32()
+ resource.model_file = bytes.get_string_from_ascii(80)
+ resource.root_node_name = bytes.get_string_from_utf8(80)
+ resource.position_x = bytes.decode_float()
+ resource.position_y = bytes.decode_float()
+ resource.position_z = bytes.decode_float()
+ resource.rotation_x = bytes.decode_float()
+ resource.rotation_y = bytes.decode_float()
+ resource.rotation_z = bytes.decode_float()
+ resource.scale_x = bytes.decode_float()
+ resource.scale_y = bytes.decode_float()
+ resource.scale_z = bytes.decode_float()
+
+ #print(inst_to_dict(resource))
+ return resource
+
+
+class DynamicLightSource extends MapResource:
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var name: String
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_z: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_r: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_g: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var diffuse_color_b: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var light_range: float
+
+
+ func get_position() -> Vector3:
+ return Vector3(position_x, position_y, position_z)
+
+
+ func get_diffuse_color() -> Color:
+ return Color(diffuse_color_r, diffuse_color_g, diffuse_color_b)
+
+
+ static func from_bytes(bytes: ByteStream) -> DynamicLightSource:
+ var resource = DynamicLightSource.new()
+
+ resource.name = bytes.get_string_from_ascii(80)
+ resource.position_x = bytes.decode_float()
+ resource.position_y = bytes.decode_float()
+ resource.position_z = bytes.decode_float()
+ resource.diffuse_color_r = bytes.decode_float()
+ resource.diffuse_color_g = bytes.decode_float()
+ resource.diffuse_color_b = bytes.decode_float()
+ resource.light_range = bytes.decode_float()
+
+ #print(inst_to_dict(resource))
+ return resource
+
+
+class SpatialAudioSource extends MapResource:
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var name: String
+
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var audio_file: String
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_z: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var volume_gain: float
+
+ ## Byte Type: u32 [br]
+ ## Byte Length: 4
+ var width: int
+
+ ## Byte Type: u32 [br]
+ ## Byte Length: 4
+ var height: int
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var audio_range: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var duration: float
+
+
+ func get_position() -> Vector3:
+ return Vector3(position_x, position_y, position_z)
+
+
+ func get_size() -> Vector2:
+ return Vector2(width, height)
+
+
+ static func from_bytes(bytes: ByteStream) -> SpatialAudioSource:
+ var resource = SpatialAudioSource.new()
+
+ resource.name = bytes.get_string_from_ascii(80)
+ resource.audio_file = bytes.get_string_from_ascii(80)
+ resource.position_x = bytes.decode_float()
+ resource.position_y = bytes.decode_float()
+ resource.position_z = bytes.decode_float()
+ resource.volume_gain = bytes.decode_float()
+ resource.width = bytes.decode_u32()
+ resource.height = bytes.decode_u32()
+ resource.audio_range = bytes.decode_float()
+ resource.duration = bytes.decode_float()
+
+ return resource
+
+
+class ParticleEffectEmitter extends MapResource:
+ ## Byte Type: u8 [br]
+ ## Byte Length: 80
+ var name: String
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_x: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_y: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var position_z: float
+
+ @warning_ignore("enum_variable_without_default")
+ ## Byte Type: u32 [br]
+ ## Byte Length: 4
+ var preset_effect: ParticlePresetEffect
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4 [br]
+ ## in frames; 60 means 1 second
+ var emission_delay: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var launch_parameter_a: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var launch_parameter_b: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var launch_parameter_c: float
+
+ ## Byte Type: f32 [br]
+ ## Byte Length: 4
+ var launch_parameter_d: float
+
+
+ func get_position() -> Vector3:
+ return Vector3(position_x, position_y, position_z)
+
+
+ static func from_bytes(bytes: ByteStream) -> ParticleEffectEmitter:
+ var resource = ParticleEffectEmitter.new()
+
+ resource.name = bytes.get_string_from_ascii(80)
+ resource.position_x = bytes.decode_float()
+ resource.position_y = bytes.decode_float()
+ resource.position_z = bytes.decode_float()
+ resource.preset_effect = bytes.decode_u32()
+ resource.emission_delay = bytes.decode_float()
+ resource.launch_parameter_a = bytes.decode_float()
+ resource.launch_parameter_b = bytes.decode_float()
+ resource.launch_parameter_c = bytes.decode_float()
+ resource.launch_parameter_d = bytes.decode_float()
+
+ return resource