====== EAHD ======
This is a compression format created by MBL Research and used by EA Games.
----
===== Header =====
The header takes up the first 5 bytes of the compressed file
^Offset^#Bytes^Description^
|0x0|2|Magic Number 0x10FB|
|0x2|3|Uncompressed size|
----
===== Decompression Code =====
==== Python ====
=== EAHD parser class ===
class EAHD:
def __init__(self, src):
self.src = src
def __bytes_to_int24(self, arr, offset):
return int.from_bytes(arr[offset:offset+3], byteorder='big')
def __bytes_to_int_reversed(self, arr, offset):
return int.from_bytes(arr[offset:offset+4], byteorder='little')
def __memset(self, dst, dst_offset, value, amount):
for i in range(amount):
dst[dst_offset + i] = value
def __memcpy(self, dst, dst_offset, dst_backwards_offset, amount):
src_offset = dst_offset - dst_backwards_offset
for i in range(amount):
dst[dst_offset + i] = dst[src_offset + i]
def __fetch_bytes(self, dst, dst_offset, dst_backwards_offset, amount):
if dst_backwards_offset > 1:
self.__memcpy(dst, dst_offset, dst_backwards_offset, amount)
else:
self.__memset(dst, dst_offset, dst[dst_offset - dst_backwards_offset], amount)
return dst_offset + amount
def __copy_literal(self, dst, dst_offset, src, src_offset, amount):
for i in range(amount):
dst[dst_offset + i] = src[src_offset + i]
def parse(self):
decompressed_size = self.__bytes_to_int24(self.src, 0x2)
dst = [0] * decompressed_size # Decompressed data list
src_offset = 0x5
dst_offset = 0
while True:
cmd = self.__bytes_to_int_reversed(self.src, src_offset)
if cmd & 0x80 == 0: # Covers commands 00 to 7F
src_offset += 2
# Copy bytes from the compressed data array
amount_to_copy = cmd & 0x3
self.__copy_literal(dst, dst_offset, self.src, src_offset, amount_to_copy)
src_offset += amount_to_copy
dst_offset += amount_to_copy
# Fetch bytes from previous decompressed data
backward_fetch_offset = ((cmd << 3) & 0x300) + ((cmd >> 8) & 0xFF) + 1
amount_of_bytes_to_fetch = ((cmd >> 2) & 0x7) + 3
dst_offset = self.__fetch_bytes(dst, dst_offset, backward_fetch_offset, amount_of_bytes_to_fetch)
continue
if cmd & 0x40 == 0: # Covers commands 0x80 to 0xBF
# Copy bytes from the compressed data array
src_offset += 3
amount_to_copy = ((cmd >> 8) >> 6) & 0x3
self.__copy_literal(dst, dst_offset, self.src, src_offset, amount_to_copy)
src_offset += amount_to_copy
dst_offset += amount_to_copy
# Fetch bytes from previous decompressed data
backward_fetch_offset = (((cmd >> 8) << 8) & 0x3F00) + ((cmd >> 16) & 0xFF) + 1
amount_of_bytes_to_fetch = (cmd & 0x3F) + 4
dst_offset = self.__fetch_bytes(dst, dst_offset, backward_fetch_offset, amount_of_bytes_to_fetch)
continue
if cmd & 0x20 == 0: # Covers commands 0xC0 to 0xDF
src_offset += 4
amount_to_copy = cmd & 0x3
self.__copy_literal(dst, dst_offset, self.src, src_offset, amount_to_copy)
src_offset += amount_to_copy
dst_offset += amount_to_copy
# Fetch bytes from previous decompressed data
backward_fetch_offset = ((cmd << 12) & 0x10000) + (((cmd >> 8) << 8) & 0xFF00) + 1 + ((cmd >> 16) & 0xFF)
amount_of_bytes_to_fetch = ((cmd << 6) & 0x300) + ((cmd >> 24) & 0xFF) + 5
dst_offset = self.__fetch_bytes(dst, dst_offset, backward_fetch_offset, amount_of_bytes_to_fetch)
continue
src_offset += 1
if (cmd & 0xFF) < 0xFC: # Covers commands 0xE0 to 0xFB
# Copy bytes from the compressed data array
amount_to_copy = ((cmd & 0x1F) + 1) * 4
self.__copy_literal(dst, dst_offset, self.src, src_offset, amount_to_copy)
src_offset += amount_to_copy
dst_offset += amount_to_copy
continue
if cmd & 3 != 0: # Copy any left-over bytes
# Copy bytes from the compressed data array
amount_to_copy = cmd & 0x3
self.__copy_literal(dst, dst_offset, self.src, src_offset, amount_to_copy)
src_offset += amount_to_copy
dst_offset += amount_to_copy
break # A command of 0xFC, 0xFD, 0xFE, or 0xFF will end the decompression routine.
return dst
=== Using the EAHD parser ===
from eahd import EAHD
parser = EAHD(bytes) # 'bytes' is the list of compressed bytes.
uncompressed_bytes = parser.parse()