mirror of
https://github.com/pmret/papermario.git
synced 2024-11-08 20:12:30 +01:00
29c3ffa2e0
* clean * git subrepo pull --force tools/splat subrepo: subdir: "tools/splat" merged: "901241040d" upstream: origin: "https://github.com/ethteck/splat.git" branch: "master" commit: "901241040d" git-subrepo: version: "0.4.5" origin: "https://github.com/ingydotnet/git-subrepo" commit: "aa416e4" * splat update * more matches after nop hack * git subrepo pull --force tools/splat subrepo: subdir: "tools/splat" merged: "715ee0ad55" upstream: origin: "https://github.com/ethteck/splat.git" branch: "master" commit: "715ee0ad55" git-subrepo: version: "0.4.5" origin: "https://github.com/ingydotnet/git-subrepo" commit: "aa416e4" * Renames, match boot_idle * one mo * wips * fish func Co-authored-by: @JaThePlayer * sushie dun * warnings * clean * match a nok func * nok_02 stuff * nok_04 party image * func_802BD5D8_3174F8 * LoadPartyImage & stuff * warnings
109 lines
3.1 KiB
Python
109 lines
3.1 KiB
Python
import argparse
|
|
import struct
|
|
import sys
|
|
|
|
try:
|
|
from .. import log
|
|
from .decompressor import Decompressor
|
|
except ImportError:
|
|
print(f"Run as python3 -m util.n64.Miodecompress")
|
|
sys.exit(1)
|
|
|
|
|
|
class GenericMio0Decompressor(Decompressor):
|
|
def __init__(
|
|
self, unpacked_offset, compressed_offset, uncompressed_offset, header_length
|
|
):
|
|
self.unpacked_offset = unpacked_offset
|
|
self.compressed_offset = compressed_offset
|
|
self.uncompressed_offset = uncompressed_offset
|
|
self.header_length = header_length
|
|
|
|
@staticmethod
|
|
def read_word(data, offset):
|
|
(res,) = struct.unpack(">I", data[offset : offset + 4])
|
|
return res
|
|
|
|
@staticmethod
|
|
def read_short(data, offset):
|
|
(res,) = struct.unpack(">H", data[offset : offset + 2])
|
|
return res
|
|
|
|
def decompress(self, in_bytes, byte_order="big") -> bytearray:
|
|
magic = in_bytes[0:4]
|
|
if magic != b"MIO0":
|
|
log.error(f"MIO0 magic is incorrect: {magic}")
|
|
|
|
unpacked_size = self.read_word(in_bytes, self.unpacked_offset)
|
|
comp_offset = self.read_word(in_bytes, self.compressed_offset)
|
|
uncomp_offset = self.read_word(in_bytes, self.uncompressed_offset)
|
|
|
|
layout_data = struct.iter_unpack(">I", in_bytes[self.header_length :])
|
|
uncompressed_data = struct.iter_unpack(">B", in_bytes[uncomp_offset:])
|
|
compressed_data = struct.iter_unpack(">H", in_bytes[comp_offset:])
|
|
|
|
idx = 0
|
|
ret = bytearray(unpacked_size)
|
|
|
|
mask_bit_counter = 0
|
|
while idx < unpacked_size:
|
|
if mask_bit_counter == 0:
|
|
(current_mask,) = next(layout_data)
|
|
mask_bit_counter = 32
|
|
|
|
if current_mask & 0x80000000:
|
|
(ud,) = next(uncompressed_data)
|
|
ret[idx] = ud
|
|
idx += 1
|
|
else:
|
|
(length_offset,) = next(compressed_data)
|
|
|
|
length = (length_offset >> 12) + 3
|
|
index = (length_offset & 0xFFF) + 1
|
|
offset = idx - index
|
|
|
|
if not (3 <= length <= 18):
|
|
log.error(f"Invalid length: {length}, corrupt data?")
|
|
|
|
if not (1 <= index <= 4096):
|
|
log.error(f"Invalid index: {index}, corrupt data?")
|
|
|
|
for i in range(length):
|
|
ret[idx] = ret[offset + i]
|
|
idx += 1
|
|
|
|
current_mask <<= 1
|
|
mask_bit_counter -= 1
|
|
|
|
return ret
|
|
|
|
|
|
class Mio0Decompressor(GenericMio0Decompressor):
|
|
def __init__(self):
|
|
super().__init__(
|
|
unpacked_offset=4,
|
|
compressed_offset=8,
|
|
uncompressed_offset=12,
|
|
header_length=16,
|
|
)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("infile")
|
|
parser.add_argument("outfile")
|
|
args = parser.parse_args()
|
|
|
|
with open(args.infile, "rb") as f:
|
|
raw_bytes = f.read()
|
|
|
|
miodecompress = Mio0Decompressor()
|
|
decompressed = miodecompress.decompress(raw_bytes)
|
|
|
|
with open(args.outfile, "wb") as f:
|
|
f.write(decompressed)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|