summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--byte_stream.gd42
-rw-r--r--client.gd8
-rw-r--r--client.tscn12
-rw-r--r--cursor.gd59
-rw-r--r--cursor.tscn2
-rw-r--r--extractor/action.gd222
-rw-r--r--extractor/action_format.gd305
-rw-r--r--extractor/grf.gd20
-rw-r--r--extractor/sprite_format.gd (renamed from extractor/sprite.gd)85
-rw-r--r--login.gd2
-rw-r--r--sprite.gd96
-rw-r--r--sprite.tscn11
-rw-r--r--sprite_resource.gd5
13 files changed, 569 insertions, 300 deletions
diff --git a/byte_stream.gd b/byte_stream.gd
index df55a7d..fe150fc 100644
--- a/byte_stream.gd
+++ b/byte_stream.gd
@@ -7,7 +7,7 @@ var position: int = 0
@warning_ignore("shadowed_variable")
-static func from_bytes(bytes: PackedByteArray):
+static func from_bytes(bytes: PackedByteArray) -> ByteStream:
var byte_stream = ByteStream.new()
byte_stream.bytes = bytes
@@ -18,55 +18,77 @@ static func from_bytes(bytes: PackedByteArray):
@warning_ignore("shadowed_variable")
func seek(position: int):
if position > 0:
- assert(position < bytes.size())
+ assert(position <= bytes.size())
self.position = position
-func decode_u8():
+func advance(jumps: int):
+ position += jumps
+
+
+func get_buffer(length: int) -> ByteStream:
+ var byte_stream = ByteStream.new()
+
+ byte_stream.bytes = bytes.slice(position, position + length)
+ seek(position + length)
+
+ return byte_stream
+
+
+func decode_u8() -> int:
var result = bytes.decode_u8(position)
seek(position + 1)
return result
-func decode_u16():
+func decode_u16() -> int:
var result = bytes.decode_u16(position)
seek(position + 2)
return result
-func decode_u32():
+func decode_u32() -> int:
var result = bytes.decode_u32(position)
seek(position + 4)
return result
-func decode_u64():
+func decode_u64() -> int:
var result = bytes.decode_u64(position)
seek(position + 8)
return result
-func decode_s8():
+func decode_s8() -> int:
var result = bytes.decode_s8(position)
seek(position + 1)
return result
-func decode_s16():
+func decode_s16() -> int:
var result = bytes.decode_s16(position)
seek(position + 2)
return result
-func decode_s32():
+func decode_s32() -> int:
var result = bytes.decode_s32(position)
seek(position + 4)
return result
-func decode_s64():
+func decode_s64() -> int:
var result = bytes.decode_s64(position)
seek(position + 8)
return result
+
+func decode_float() -> float:
+ var result = bytes.decode_float(position)
+ seek(position + 4)
+
+ return result
+
+func get_string_from_utf8(length: int) -> String:
+ return get_buffer(length).bytes.get_string_from_utf8()
diff --git a/client.gd b/client.gd
index a66cfaf..5c3e9a8 100644
--- a/client.gd
+++ b/client.gd
@@ -1,9 +1,13 @@
extends Node
-func _ready() -> void:
+#func _ready() -> void:
#var grf = GRF.open("res://data/data.grf")
#grf.extract("user://data")
#Sprite.from_bytes(FileAccess.get_file_as_bytes("res://data/extracted/data/sprite/cursors.spr"))
- ActionFormat.from_bytes(FileAccess.get_file_as_bytes("res://data/extracted/data/sprite/cursors.act"))
+ #ActionFormat.from_bytes(
+ #ByteStream.from_bytes(
+ #FileAccess.get_file_as_bytes("res://data/extracted/data/sprite/cursors.act")
+ #)
+ #)
diff --git a/client.tscn b/client.tscn
index fdc2b75..7d17661 100644
--- a/client.tscn
+++ b/client.tscn
@@ -1,19 +1,9 @@
-[gd_scene load_steps=11 format=3 uid="uid://cuylx656oarpy"]
+[gd_scene load_steps=3 format=3 uid="uid://cuylx656oarpy"]
[ext_resource type="Script" path="res://client.gd" id="1_e4txq"]
[ext_resource type="PackedScene" uid="uid://n0y3fpb8j820" path="res://cursor.tscn" id="2_m3abr"]
-[ext_resource type="Texture2D" uid="uid://dkc1awf1xpl2o" path="res://extractor/test/test-000.png" id="3_0vwpd"]
-[ext_resource type="Texture2D" uid="uid://bx25ptahbg1ve" path="res://extractor/test/test-001.png" id="4_qgfg3"]
-[ext_resource type="Texture2D" uid="uid://cj32t45v42v3a" path="res://extractor/test/test-002.png" id="5_h0rr4"]
-[ext_resource type="Texture2D" uid="uid://boeeuqg0sephn" path="res://extractor/test/test-003.png" id="6_k7dkb"]
-[ext_resource type="Texture2D" uid="uid://m5yp6e4y4dl2" path="res://extractor/test/test-004.png" id="7_lr2su"]
-[ext_resource type="Texture2D" uid="uid://p5h4en0yc8i5" path="res://extractor/test/test-005.png" id="8_vfr4i"]
-[ext_resource type="Texture2D" uid="uid://c5r5lpebvbcmp" path="res://extractor/test/test-007.png" id="9_im4or"]
-[ext_resource type="Texture2D" uid="uid://bw0ad3gkk1api" path="res://extractor/test/test-008.png" id="10_b8x2v"]
[node name="Client" type="Node"]
script = ExtResource("1_e4txq")
[node name="Cursor" parent="." instance=ExtResource("2_m3abr")]
-arrow_images = Array[Texture2D]([ExtResource("3_0vwpd"), ExtResource("4_qgfg3"), ExtResource("5_h0rr4"), ExtResource("6_k7dkb"), ExtResource("7_lr2su"), ExtResource("8_vfr4i")])
-click_images = Array[Texture2D]([ExtResource("9_im4or"), ExtResource("10_b8x2v")])
diff --git a/cursor.gd b/cursor.gd
index 851441d..b8aac57 100644
--- a/cursor.gd
+++ b/cursor.gd
@@ -1,35 +1,48 @@
-extends Node
+extends Node2D
-@export var arrow_images: Array[Texture2D]
-@export_range(1.0, 10.0, 0.1) var arrow_speed := 7.0
-var arrow_idx := 0.0
+var sprite: Sprite
-@export var click_images: Array[Texture2D]
-@export_range(1.0, 10.0, 0.1) var click_speed := 4.0
-var click_idx := 0.0
+var current_action_idx := 0
+var last_action_idx := 0
-class CurrentCursor:
- var images: Array[Texture2D]
- var speed: float
- var idx: float
+func _ready() -> void:
+ sprite = preload("res://sprite.tscn").instantiate()
+ sprite.load_file("res://data/extracted/data/sprite/cursors.spr")
+ add_child(sprite)
+
+ sprite.action_data.frame_times[0] *= 2.0
+ for idx in sprite.action_data.frame_times.size():
+ sprite.action_data.frame_times[idx] *= 1.0
+
+ Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
-func _process(delta: float) -> void:
+
+func _process(_delta: float) -> void:
+ sprite.global_position = get_global_mouse_position()
+
if Input.get_current_cursor_shape() == Input.CURSOR_ARROW:
- arrow_idx += (delta * arrow_speed)
- if arrow_idx > arrow_images.size():
- arrow_idx = 0
-
- Input.set_custom_mouse_cursor(arrow_images[floor(arrow_idx)], Input.CURSOR_ARROW)
+ current_action_idx = 0
elif Input.get_current_cursor_shape() == Input.CURSOR_POINTING_HAND:
- click_idx += (delta * click_speed)
- if click_idx > click_images.size():
- click_idx = 0
-
- Input.set_custom_mouse_cursor(click_images[floor(click_idx)], Input.CURSOR_POINTING_HAND)
+ current_action_idx = 2
else:
- Input.set_custom_mouse_cursor(null, Input.get_current_cursor_shape())
+ current_action_idx = -1
+
+ if current_action_idx == -1:
+ Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
+ sprite.visible = false
+ elif current_action_idx != last_action_idx:
+ sprite.set_current_action(current_action_idx)
+ Input.mouse_mode = Input.MOUSE_MODE_HIDDEN
+ sprite.visible = true
+
+ if current_action_idx == 0 and sprite.get_node("%SpriteLayers").get_child_count() == 1:
+ Input.set_custom_mouse_cursor(sprite.get_node("%SpriteLayers").get_child(0).texture.get_image())
+ Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
+ sprite.visible = false
+
+ last_action_idx = current_action_idx
func _input(event: InputEvent) -> void:
diff --git a/cursor.tscn b/cursor.tscn
index 05f6703..96efc47 100644
--- a/cursor.tscn
+++ b/cursor.tscn
@@ -2,5 +2,5 @@
[ext_resource type="Script" path="res://cursor.gd" id="1_gyqds"]
-[node name="Cursor" type="Node"]
+[node name="Cursor" type="Node2D"]
script = ExtResource("1_gyqds")
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
diff --git a/login.gd b/login.gd
index c00e3cf..193533e 100644
--- a/login.gd
+++ b/login.gd
@@ -9,7 +9,7 @@ var current_character_information: CharacterInformation
func _ready() -> void:
switch_screen(%Login)
- $AudioStreamPlayer2.play()
+ #$AudioStreamPlayer2.play()
func switch_screen(screen: Node):
diff --git a/sprite.gd b/sprite.gd
new file mode 100644
index 0000000..3bfc961
--- /dev/null
+++ b/sprite.gd
@@ -0,0 +1,96 @@
+class_name Sprite
+extends Node2D
+
+
+var sprite_data: SpriteResource
+var action_data: ActionFormat
+
+var current_action_idx := 0
+var current_frame := 0
+var current_start_time := 0.0
+var accumulator := 0.0
+
+var cache: Dictionary
+
+
+func _ready() -> void:
+ if not sprite_data or not action_data:
+ set_process(false)
+
+
+func load_file(path: String):
+ var basename = path.get_basename()
+
+ sprite_data = load("%s.tres" % basename)
+
+ var act = FileAccess.open("%s.act" % basename, FileAccess.READ)
+ action_data = ActionFormat.from_bytes(ByteStream.from_bytes(
+ act.get_buffer(act.get_length())
+ ))
+
+ if not sprite_data.files_exist():
+ sprite_data.save_to_file() # generate at "runtime"
+
+ set_process(true)
+ set_current_action(0)
+
+
+func set_current_action(idx: int):
+ current_action_idx = idx
+ current_frame = 0
+ current_start_time = 0
+ accumulator = 0
+
+ update(action_data.actions[current_action_idx].motions[current_frame].sprite_layers)
+
+
+func _process(delta: float) -> void:
+ var action = action_data.actions[current_action_idx]
+ var frame_time = ((action_data.frame_times[current_action_idx] * 24) / 1000)
+
+ accumulator += delta
+ if accumulator > current_start_time + frame_time:
+ var motion: ActionFormat.Motion = action.motions[current_frame]
+ update(motion.sprite_layers)
+
+ current_start_time = accumulator
+ current_frame += 1
+ if current_frame >= action.motions.size():
+ set_current_action(current_action_idx) # reset current action
+
+
+func update(sprite_layers: Array[ActionFormat.SpriteLayer]):
+ if not is_processing():
+ return
+
+ var has_different_layer_count = %SpriteLayers.get_child_count() != sprite_layers.size()
+
+ if has_different_layer_count:
+ for node in %SpriteLayers.get_children():
+ node.queue_free()
+
+ for idx in sprite_layers.size():
+ var sprite_layer = sprite_layers[idx]
+
+ var base_dir = sprite_data.filepath.substr(0, sprite_data.filepath.length() - 4) # cut off .spr
+ var image = load("%s/%s.png" % [base_dir, str(sprite_layer.sprite_index).pad_zeros(3)])
+
+ var sprite: Sprite2D
+ if has_different_layer_count:
+ sprite = Sprite2D.new()
+ sprite.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
+ else:
+ sprite = %SpriteLayers.get_child(idx)
+
+ sprite.texture = image
+ sprite.position = sprite_layer.get_position()
+ sprite.self_modulate = sprite_layer.get_color()
+ sprite.scale = sprite_layer.get_scale()
+ sprite.rotation_degrees = sprite_layer.rotation_degrees
+ sprite.flip_h = sprite_layer.flip_h
+
+ # TODO: use sprite and action together to generate AnimatedSprite2D with SpriteFrame Resources?
+ # TODO: no. needs AnimationPlayer with config for different sprite positions in the animation
+
+ if has_different_layer_count:
+ %SpriteLayers.add_child(sprite)
diff --git a/sprite.tscn b/sprite.tscn
new file mode 100644
index 0000000..d88f18e
--- /dev/null
+++ b/sprite.tscn
@@ -0,0 +1,11 @@
+[gd_scene load_steps=2 format=3 uid="uid://b8etnr5ebvrp3"]
+
+[ext_resource type="Script" path="res://sprite.gd" id="1_dohbv"]
+
+[node name="Sprite" type="Node2D"]
+top_level = true
+z_index = 1337
+script = ExtResource("1_dohbv")
+
+[node name="SpriteLayers" type="CanvasGroup" parent="."]
+unique_name_in_owner = true
diff --git a/sprite_resource.gd b/sprite_resource.gd
new file mode 100644
index 0000000..380fac4
--- /dev/null
+++ b/sprite_resource.gd
@@ -0,0 +1,5 @@
+class_name SpriteResource
+extends Resource
+
+
+@export var images: Array[Texture2D]