From 5eb4b6d575980adffef0d23e7dbc531d1d899209 Mon Sep 17 00:00:00 2001 From: Xzonn Date: Sat, 10 Feb 2024 23:08:49 +0800 Subject: [PATCH] Add a compressor --- scripts/compressor.py | 68 ++++++++++++++++++++++++++++++++++++++++ scripts/decompressor.py | 31 +++--------------- scripts/import_images.py | 8 +++-- 3 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 scripts/compressor.py diff --git a/scripts/compressor.py b/scripts/compressor.py new file mode 100644 index 0000000..a6e22f8 --- /dev/null +++ b/scripts/compressor.py @@ -0,0 +1,68 @@ +import struct + +def compress(uncompressed: bytearray | bytes) -> bytearray: + def find_largest(uncompressed: bytearray | bytes, uncompressed_pos: int) -> tuple[int, int, int]: + start = max(0, uncompressed_pos - 0x10000) + before = uncompressed[start :] + after = uncompressed[uncompressed_pos : uncompressed_pos + 4] + + max_len = 0 + max_len_offset = -1 + offset = before.find(after) + while offset > -1 and start + offset < uncompressed_pos: + this_len = 4 + while offset + this_len < len(before) and uncompressed_pos + this_len < len(uncompressed) and this_len < 0x103 and before[offset + this_len] == uncompressed[uncompressed_pos + this_len]: + this_len += 1 + + if this_len > max_len: + max_len = this_len + max_len_offset = start + offset + offset = before.find(after, offset + max_len) + + if max_len_offset != -1: + return 0, max_len_offset, max_len + + if uncompressed[uncompressed_pos:uncompressed_pos + 4] == b"\0\0\0\0": + zero_pos = uncompressed_pos + 4 + while zero_pos < len(uncompressed) and zero_pos < uncompressed_pos + 0x103 and uncompressed[zero_pos] == 0: + zero_pos += 1 + if zero_pos < 0xffff: + zero_len = zero_pos - uncompressed_pos + return 0, 0x10000 - zero_len, zero_len + + return 1, 0, 0 + + compressed = bytearray() + compressed.extend(struct.pack("<4I", 0x01da3d12, len(uncompressed), 0, 0)) + + uncompressed_pos = 0 + bits = 0 + while uncompressed_pos < len(uncompressed): + bits_pos = len(compressed) + compressed.append(0) + bits = 0 + for i in range(8): + if uncompressed_pos >= len(uncompressed): + break + + bits_i, max_len_offset, max_len = find_largest(uncompressed, uncompressed_pos) + + if bits_i == 0: + real_offset = (max_len_offset + 0x10000 - 0xff - 4) & 0xffff + real_len = max_len - 4 + compressed.extend(struct.pack(" len(uncompressed): + compressed = bytearray() + compressed.extend(struct.pack("<4I", 0x00da3d12, len(uncompressed), 0xffffffff, 0)) + compressed.extend(uncompressed) + else: + compressed[0x08:0x0c] = struct.pack("> 1) - n |= (n >> 2) - n |= (n >> 4) - n |= (n >> 8) - n |= (n >> 16) - return n - (n >> 1) - # Thanks to: Jason Harley # Reference: https://github.com/Jas2o/KyleHyde/blob/main/KyleHyde/Formats/HotelDusk/Decompress.cs def decompress(compressed: bytearray | bytes) -> bytearray: @@ -33,26 +25,13 @@ def decompress(compressed: bytearray | bytes) -> bytearray: compressed_pos += 1 uncompressed_pos += 1 else: - offset, = struct.unpack("= 0x8000: - signed -= 0x10000 - if signed < 0 and signed + 0xff + 4 >= 0: - offset = signed - elif posHi >= 0x20000 and offHi < posHi: - if offset + posHi < uncompressed_pos: - offset += posHi - elif offset + 0x10000 < uncompressed_pos: - offset += 0x10000 - elif posHi >= 0x10000 and offHi < posHi and offset + posHi < uncompressed_pos: - offset += posHi - offset += 0xff + 4 + while offset < uncompressed_pos - 0x10000: + offset += 0x10000 if offset < 0 or offset + len >= sizeun: for x in range(len): diff --git a/scripts/import_images.py b/scripts/import_images.py index 6d62a5e..5d573ad 100644 --- a/scripts/import_images.py +++ b/scripts/import_images.py @@ -4,6 +4,7 @@ from PIL import Image import decompressor +import compressor os.makedirs("temp_files/pack/", exist_ok=True) @@ -105,8 +106,11 @@ def get_xy_tile(width: int, height: int) -> Generator[tuple[int, int], Any, None decompressed[0x10 + i * 2 : 0x12 + i * 2] = struct.pack("