summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--extractor/grf.gd181
-rw-r--r--network/network.gd103
3 files changed, 173 insertions, 112 deletions
diff --git a/.gitignore b/.gitignore
index 7850089..82eea3b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
/android/
/data/*.grf
+/data/extracted/
diff --git a/extractor/grf.gd b/extractor/grf.gd
index 27dcab0..c0c42a6 100644
--- a/extractor/grf.gd
+++ b/extractor/grf.gd
@@ -1,8 +1,11 @@
+## see https://ragnarokresearchlab.github.io/file-formats/grf
class_name GRF
+var header: Header
+
class Header:
- static var byte_length: int = 46
+ const BYTE_LENGTH := 46
## Byte Length: 15
## Master of Magic
@@ -17,24 +20,182 @@ class Header:
## Byte Type: u32
## Byte Length: 4
- var reserved_files: int
+ var scrambling_seed: int
## Byte Type: u32
## Byte Length: 4
- var file_count: int
+ var scrambled_file_count: int
## Byte Type: u32
## Byte Length: 4
var version: int
- static func from_file(file: FileAccess):
+ func get_file_count() -> int:
+ return scrambled_file_count - scrambling_seed - 7
+
+
+ static func from_bytes(bytes: PackedByteArray):
var header = Header.new()
- header.signature = file.get_buffer(15).get_string_from_utf8()
- header.encryption = file.get_buffer(15)
- header.file_table_offset = file.get_buffer(4).decode_u32(0)
- header.reserved_files = file.get_buffer(4).decode_u32(0)
- header.file_count = file.get_buffer(4).decode_u32(0)
- header.version = file.get_buffer(4).decode_u32(0)
+
+ header.signature = bytes.slice(0, 15).get_string_from_utf8()
+ header.encryption = bytes.slice(15, 15 + 15)
+ header.file_table_offset = bytes.decode_u32(30)
+ header.scrambling_seed = bytes.decode_u32(34)
+ header.scrambled_file_count = bytes.decode_u32(38)
+ header.version = bytes.decode_u32(42)
return header
+
+
+var file_table: FileTable
+
+class FileTable:
+ const BYTE_LENGTH := 8
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var compressed_size: int
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var decompressed_size: int
+
+ var compressed_record_headers: PackedByteArray
+ var decompressed_record_headers: PackedByteArray
+
+
+ func generate_compressed_record_headers(grf_file: FileAccess):
+ compressed_record_headers = grf_file.get_buffer(compressed_size)
+
+
+ func decompress():
+ decompressed_record_headers = compressed_record_headers.decompress(
+ decompressed_size,
+ FileAccess.CompressionMode.COMPRESSION_DEFLATE
+ )
+
+
+ static func from_bytes(bytes: PackedByteArray):
+ var file_table = FileTable.new()
+
+ file_table.compressed_size = bytes.decode_u32(0)
+ file_table.decompressed_size = bytes.decode_u32(4)
+
+ return file_table
+
+
+var file_entries: Array[FileEntry]
+
+class FileEntry:
+ const BYTE_LENGTH := 17
+
+ var file_name: String
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var compressed_size: int
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var byte_aligned_size: int
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var decompressed_size: int
+
+ ## Byte Type: u8
+ ## Byte Length: 1
+ var file_type: int
+
+ ## Byte Type: u32
+ ## Byte Length: 4
+ var offset: int
+
+
+ func get_byte_length() -> int:
+ return BYTE_LENGTH #+ file_name.to_ascii_buffer().size()
+
+
+ func get_file_path() -> String:
+ var parts = file_name.split("\\")
+
+ return "/".join(parts)
+
+
+ func get_contents(grf_file: FileAccess):
+ var previous_position = grf_file.get_position()
+
+ grf_file.seek(Header.BYTE_LENGTH + offset)
+ var buffer = grf_file.get_buffer(byte_aligned_size)
+ var contents = buffer.decompress(
+ decompressed_size,
+ FileAccess.CompressionMode.COMPRESSION_DEFLATE
+ )
+
+ grf_file.seek(previous_position)
+
+ return contents
+
+
+ static func from_bytes_with_filename(bytes: PackedByteArray, file_name: String):
+ var file_entry = FileEntry.new()
+
+ file_entry.file_name = file_name
+ file_entry.compressed_size = bytes.decode_u32(0)
+ file_entry.byte_aligned_size = bytes.decode_u32(4)
+ file_entry.decompressed_size = bytes.decode_u32(8)
+ file_entry.file_type = bytes.decode_u8(12)
+ file_entry.offset = bytes.decode_u32(13)
+
+ return file_entry
+
+
+var file_access: FileAccess
+
+
+static func open(path: String):
+ var grf = GRF.new()
+
+ grf.file_access = FileAccess.open(path, FileAccess.ModeFlags.READ)
+
+ grf.header = GRF.Header.from_bytes(grf.file_access.get_buffer(Header.BYTE_LENGTH))
+
+ grf.file_access.seek(Header.BYTE_LENGTH + grf.header.file_table_offset)
+ grf.file_table = FileTable.from_bytes(grf.file_access.get_buffer(8))
+
+ grf.file_table.generate_compressed_record_headers(grf.file_access)
+ grf.file_table.decompress()
+
+ grf.file_entries = [] as Array[FileEntry]
+ var file_entry_offset = 0
+ while file_entry_offset < grf.file_table.decompressed_record_headers.size():
+ var file_name_size = grf.file_table.decompressed_record_headers.find(0, file_entry_offset) + 1 - file_entry_offset
+ var file_entry = FileEntry.from_bytes_with_filename(
+ grf.file_table.decompressed_record_headers.slice(
+ file_entry_offset + file_name_size,
+ file_entry_offset + file_name_size + FileEntry.BYTE_LENGTH
+ ),
+ 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)
+ file_entry_offset += file_entry.get_byte_length() + file_name_size
+
+ grf.write()
+
+ return grf
+
+
+func write():
+ for file_entry in file_entries:
+ var file_path: String = file_entry.get_file_path()
+
+ var base_directory = DirAccess.open("res://data")
+ base_directory.make_dir_recursive("extracted/" + file_path.get_base_dir())
+
+ var file = FileAccess.open("res://data/extracted/%s" % [file_path], FileAccess.WRITE_READ)
+ file.store_buffer(file_entry.get_contents(file_access))
diff --git a/network/network.gd b/network/network.gd
index 1e665a3..1db3c50 100644
--- a/network/network.gd
+++ b/network/network.gd
@@ -1,32 +1,6 @@
extends Node
-#class LoginServer:
- #var host: String
- #var port: int = 6900
- #var stream: PacketPeerStream = PacketPeerStream.new()
- #var peer: StreamPeerTCP = StreamPeerTCP.new()
- #
- #func _init(host: String, port: int = 6900) -> void:
- #self.host = host
- #self.port = port
- #
- #stream.stream_peer = peer
- #peer.connect_to_host(host, port)
- #peer.poll()
-
-#class CharacterServer:
- #var host: String
- #var port: int = 6121
- #var stream: PacketPeerStream
- #var peer: StreamPeerTCP
-
-#class MapServer:
- #var host: String
- #var port: int = 5121
- #var stream: PacketPeerStream
- #var peer: StreamPeerTCP
-
static var login_server: LoginServer
static var character_server: CharacterServer
static var map_server: MapServer
@@ -35,82 +9,7 @@ static var map_server: MapServer
func _ready() -> void:
login_server = LoginServer.new("127.0.0.1")
- #var login_server_login_packet = LoginServerLoginPacket.new()
- #login_server_login_packet.username = "dweipert"
- #login_server_login_packet.password = "ragnarok"
- #
- #peer.put_data(login_server_login_packet.to_bytes())
- #
- #var p = LoginServerLoginSuccessPacket.from_bytes_via_peer(peer)
- #var character_server_information: CharacterServerInformation = p.character_server_information[0]
- #print(inst_to_dict(p), inst_to_dict(character_server_information))
- #
- #
- #stream2 = PacketPeerStream.new()
- #peer2 = StreamPeerTCP.new()
- #stream2.stream_peer = peer2
- #
- #peer2.connect_to_host(character_server_information.get_server_ip(), character_server_information.server_port)
- #while peer2.get_status() != StreamPeerTCP.STATUS_CONNECTED:
- #print("status: ", peer2.get_status(), " polling: ", peer2.poll())
- #print("working status: ", peer2.get_status())
- #
- #var character_server_login_packet = CharacterServerLoginPacket.new()
- #character_server_login_packet.account_id = p.account_id
- #character_server_login_packet.login_id1 = p.login_id1
- #character_server_login_packet.login_id2 = p.login_id2
- #character_server_login_packet.gender = p.gender
- #
- #print(inst_to_dict(character_server_login_packet))
- #peer2.put_data(character_server_login_packet.to_bytes())
- #
- #var c = CharacterServerLoginSuccessPacket.from_bytes_via_peer(peer2)
- #print(inst_to_dict(c))
- #
- ## get character list
- #var request_character_list_packet = RequestCharacterListPacket.new()
- #peer2.put_data(request_character_list_packet.to_bytes())
- #
- #var rcl = RequestCharacterListSuccessPacket.from_bytes_via_peer(peer2)
- #var character_information: CharacterInformation = rcl.character_information[0]
- #print(inst_to_dict(rcl), inst_to_dict(character_information))
- #
- ## select character
- #var select_character_packet = SelectCharacterPacket.new()
- #select_character_packet.selected_slot = 0
- #peer2.put_data(select_character_packet.to_bytes())
- #
- #var css = CharacterSelectionSuccessPacket.from_bytes_via_peer(peer2)
- #print(inst_to_dict(css))
- #
- #
- #stream3 = PacketPeerStream.new()
- #peer3 = StreamPeerTCP.new()
- #stream3.stream_peer = peer3
- #
- #peer3.connect_to_host(css.get_map_server_ip(), css.map_server_port)
- #while peer3.get_status() != StreamPeerTCP.STATUS_CONNECTED:
- #print("status: ", peer3.get_status(), " polling: ", peer3.poll())
- #print("working status: ", peer3.get_status())
- #
- #var map_server_login_packet = MapServerLoginPacket.new()
- #map_server_login_packet.account_id = p.account_id
- #map_server_login_packet.character_id = css.character_id
- #map_server_login_packet.login_id1 = p.login_id1
- #map_server_login_packet.gender = p.gender
- #
- #peer3.put_data(map_server_login_packet.to_bytes())
- #
- #print(peer3.get_data(6))#in-between packet
- #print(peer3.get_data(4))#in-between packet
- #
- #var msls = MapServerLoginSuccessPacket.from_bytes_via_peer(peer3)
- #print(inst_to_dict(msls))
- #print(msls.get_position())
-
- var file = FileAccess.open("res://data/data.grf", FileAccess.READ)
- var header = GRF.Header.from_file(file)
- print(inst_to_dict(header))
+ var grf = GRF.open("res://data/data.grf")
func _process(_delta: float) -> void: