diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | extractor/grf.gd | 181 | ||||
-rw-r--r-- | network/network.gd | 103 |
3 files changed, 173 insertions, 112 deletions
@@ -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: |