From 85d3f7b2348249342f2def012e71b3f1ef5a1e29 Mon Sep 17 00:00:00 2001 From: Daniel Weipert Date: Sat, 30 Nov 2024 17:52:16 +0100 Subject: next commit --- extractor/grf.gd | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 171 insertions(+), 10 deletions(-) (limited to 'extractor') 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)) -- cgit v1.2.3