mirror of
https://github.com/NinjaCheetah/libWiiPy.git
synced 2025-04-25 21:01:01 -04:00
Added LZ77 decompression module
This commit is contained in:
parent
7c5af6ebe0
commit
47472e7b94
@ -2,4 +2,5 @@
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
|
||||
from .ash import *
|
||||
from .lz77 import *
|
||||
from .u8 import *
|
||||
|
@ -5,7 +5,7 @@
|
||||
# co-authored by NinjaCheetah.
|
||||
# https://github.com/NinjaCheetah/ASH0-tools
|
||||
#
|
||||
# See <link pending> for details about the ASH archive format.
|
||||
# See <link pending> for details about the ASH compression format.
|
||||
|
||||
import io
|
||||
from dataclasses import dataclass as _dataclass
|
||||
|
62
src/libWiiPy/archive/lz77.py
Normal file
62
src/libWiiPy/archive/lz77.py
Normal file
@ -0,0 +1,62 @@
|
||||
# "archive/lz77.py" from libWiiPy by NinjaCheetah & Contributors
|
||||
# https://github.com/NinjaCheetah/libWiiPy
|
||||
#
|
||||
# See https://wiibrew.org/wiki/LZ77 for details about the LZ77 compression format.
|
||||
|
||||
import io
|
||||
|
||||
|
||||
def decompress_lz77(lz77_data: bytes) -> bytes:
|
||||
"""
|
||||
Decompresses LZ77-compressed data and returns the decompressed result. Supports data both with and without the
|
||||
magic number 'LZ77' (which may not be present if the data is embedded in something else).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
lz77_data: bytes
|
||||
The LZ77-compressed data to decompress.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bytes
|
||||
The decompressed data.
|
||||
"""
|
||||
with io.BytesIO(lz77_data) as data:
|
||||
magic = data.read(4)
|
||||
# Assume if we didn't get the magic number that this data starts without it.
|
||||
if magic != b'LZ77':
|
||||
data.seek(0)
|
||||
# Other compression types are used by Nintendo, but only type 0x10 was used on the Wii.
|
||||
compression_type = int.from_bytes(data.read(1))
|
||||
if compression_type != 0x10:
|
||||
raise ValueError("This data is using an unsupported compression type!")
|
||||
decompressed_size = int.from_bytes(data.read(3), byteorder='little')
|
||||
# Use an integer list for storing decompressed data, this is much faster than using (and appending to) a
|
||||
# bytes object.
|
||||
out_data = [0] * decompressed_size
|
||||
pos = 0
|
||||
while pos < decompressed_size:
|
||||
flag = int.from_bytes(data.read(1))
|
||||
# Read bits in the flag from most to least significant.
|
||||
for x in range(7, -1, -1):
|
||||
# Avoids a buffer overrun if the final flag isn't fully used.
|
||||
if pos >= decompressed_size:
|
||||
break
|
||||
# Result of 1, this means we're copying bytes from earlier in the data.
|
||||
if flag & (1 << x):
|
||||
reference = int.from_bytes(data.read(2))
|
||||
length = 3 + ((reference >> 12) & 0xF)
|
||||
offset = pos - (reference & 0xFFF) - 1
|
||||
for _ in range(length):
|
||||
out_data[pos] = out_data[offset]
|
||||
pos += 1
|
||||
offset += 1
|
||||
# Avoids a buffer overrun if the copy length would extend past the end of the file.
|
||||
if pos >= decompressed_size:
|
||||
break
|
||||
# Result of 0, use the next byte directly.
|
||||
else:
|
||||
out_data[pos] = int.from_bytes(data.read(1))
|
||||
pos += 1
|
||||
out_bytes = bytes(out_data)
|
||||
return out_bytes
|
Loading…
x
Reference in New Issue
Block a user