346 lines
8.4 KiB
C
346 lines
8.4 KiB
C
|
|
#include "util.h"
|
|
|
|
struct unpack_eat_t {
|
|
FILE *fp;
|
|
uint8_t len;
|
|
uint16_t bits;
|
|
uint8_t *dst;
|
|
};
|
|
|
|
static struct unpack_eat_t uneat;
|
|
|
|
static int next_bit(struct unpack_eat_t *u) {
|
|
const int bit = (u->bits & (1 << (16 - u->len))) != 0;
|
|
--u->len;
|
|
if (u->len == 0) {
|
|
u->bits = fread_le16(u->fp);
|
|
u->len = 16;
|
|
}
|
|
return bit;
|
|
}
|
|
|
|
static int zero_bits(struct unpack_eat_t *u, int count) {
|
|
int i = 0;
|
|
for (; i < count; ++i) {
|
|
if (next_bit(u)) {
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static uint8_t get_bits(struct unpack_eat_t *u, int count) {
|
|
assert(count < 8);
|
|
uint8_t val = 0;
|
|
for (int i = 0; i < count; ++i) {
|
|
val = (val << 1) | next_bit(u);
|
|
}
|
|
return val;
|
|
}
|
|
|
|
static void copy_reference(struct unpack_eat_t *u, int count, int offset_hi, int offset_lo) {
|
|
const int16_t offset = offset_hi * 256 + offset_lo;
|
|
for (int i = 0; i < count; ++i) {
|
|
const uint8_t value = u->dst[offset];
|
|
*u->dst++ = value;
|
|
}
|
|
}
|
|
|
|
static int unpack_eat(FILE *in, struct unpack_eat_t *u) {
|
|
uint8_t buffer[17];
|
|
const int header_size = fread(buffer, 1, sizeof(buffer), in);
|
|
if (header_size != 17 || READ_LE_UINT16(buffer + 4) != 0x899D || READ_LE_UINT16(buffer + 6) != 0x6C64) {
|
|
print_error("Unexpected signature for .eat file");
|
|
return 0;
|
|
}
|
|
const uint16_t crc = READ_LE_UINT16(buffer + 12);
|
|
const int output_size = (buffer[14] << 14) + READ_LE_UINT16(buffer + 15);
|
|
print_debug(DBG_UNPACK, "uncompressed size %d crc 0x%04x", output_size, crc);
|
|
|
|
uint8_t *output_buffer = (uint8_t *)malloc(output_size);
|
|
if (!output_buffer) {
|
|
print_error("Failed to allocate EAT unpack buffer, %d bytes", output_size);
|
|
return 0;
|
|
}
|
|
u->fp = in;
|
|
u->dst = output_buffer;
|
|
u->len = 16;
|
|
u->bits = fread_le16(u->fp);
|
|
while (1) {
|
|
while (next_bit(u)) {
|
|
*u->dst++ = fgetc(u->fp);
|
|
}
|
|
const int b = next_bit(u);
|
|
const int offset_lo = fgetc(u->fp);
|
|
if (b) {
|
|
int offset_hi = 0xFE | next_bit(u);
|
|
if (!next_bit(u)) {
|
|
int i = 1;
|
|
for (; i < 4 && !next_bit(u); ++i) {
|
|
offset_hi = (offset_hi << 1) | next_bit(u);
|
|
}
|
|
offset_hi -= (1 << i);
|
|
}
|
|
const int n = zero_bits(u, 4);
|
|
if (n != 4) {
|
|
copy_reference(u, n + 3, offset_hi, offset_lo);
|
|
} else if (next_bit(u)) {
|
|
copy_reference(u, next_bit(u) + 7, offset_hi, offset_lo);
|
|
} else if (!next_bit(u)) {
|
|
copy_reference(u, get_bits(u, 3) + 9, offset_hi, offset_lo);
|
|
} else {
|
|
copy_reference(u, fgetc(u->fp) + 17, offset_hi, offset_lo);
|
|
}
|
|
} else {
|
|
if (next_bit(u)) {
|
|
const int offset_hi = (0xF8 | get_bits(u, 3)) - 1;
|
|
copy_reference(u, 2, offset_hi, offset_lo);
|
|
} else if (offset_lo == 0xFF) {
|
|
break;
|
|
} else {
|
|
copy_reference(u, 2, 0xFF, offset_lo);
|
|
}
|
|
}
|
|
}
|
|
assert((u->dst - output_buffer) == output_size);
|
|
u->dst = output_buffer;
|
|
return output_size;
|
|
}
|
|
|
|
struct unpack_sqz_t {
|
|
uint16_t top_code;
|
|
uint8_t code_size;
|
|
uint16_t new_codes;
|
|
uint8_t *dst;
|
|
int bits_left;
|
|
uint32_t current_bits;
|
|
uint8_t last_code;
|
|
uint16_t previous_code;
|
|
uint16_t prefix[0x1000];
|
|
uint8_t str[0x1000];
|
|
uint8_t stack[0x1000];
|
|
};
|
|
|
|
#define SQZ_CODE_WIDTH 9
|
|
#define SQZ_CODE_BASE (1 << (SQZ_CODE_WIDTH - 1))
|
|
|
|
static const int SQZ_CLEAR_CODE = SQZ_CODE_BASE;
|
|
static const int SQZ_END_CODE = SQZ_CODE_BASE + 1;
|
|
static const int SQZ_NEW_CODES = SQZ_CODE_BASE + 2;
|
|
|
|
static struct unpack_sqz_t unsqz;
|
|
|
|
static uint16_t unpack_sqz_get_bits(FILE *in, struct unpack_sqz_t *u, int count) {
|
|
u->current_bits <<= 8;
|
|
u->current_bits |= fgetc(in);
|
|
u->bits_left += 8;
|
|
if (u->bits_left < count) {
|
|
u->current_bits <<= 8;
|
|
u->current_bits |= fgetc(in);
|
|
u->bits_left += 8;
|
|
}
|
|
const uint32_t code = u->current_bits >> (u->bits_left - count);
|
|
u->bits_left -= count;
|
|
u->current_bits &= (1 << u->bits_left) - 1;
|
|
return code;
|
|
}
|
|
|
|
static uint16_t unpack_sqz_get_code(FILE *in, struct unpack_sqz_t *u) {
|
|
if (u->top_code == u->new_codes && u->code_size != 12) {
|
|
++u->code_size;
|
|
u->top_code <<= 1;
|
|
}
|
|
return unpack_sqz_get_bits(in, u, u->code_size);
|
|
}
|
|
|
|
static uint16_t unpack_sqz_clear_code(FILE *in, struct unpack_sqz_t *u) {
|
|
u->top_code = 1 << SQZ_CODE_WIDTH;
|
|
u->code_size = SQZ_CODE_WIDTH;
|
|
u->new_codes = SQZ_NEW_CODES;
|
|
const uint16_t code = unpack_sqz_get_code(in, u);
|
|
if (code != SQZ_END_CODE) {
|
|
u->previous_code = code;
|
|
*u->dst++ = u->last_code = code & 255;
|
|
}
|
|
return code;
|
|
}
|
|
|
|
static int unpack_sqz(FILE *in, struct unpack_sqz_t *u) {
|
|
uint8_t buf[4];
|
|
fread(buf, 1, sizeof(buf), in);
|
|
assert((buf[1] & 0xF0) == 0x10);
|
|
const int output_size = ((buf[0] & 15) << 16) | READ_LE_UINT16(buf + 2);
|
|
print_debug(DBG_UNPACK, "SQZ uncompressed size %d", output_size);
|
|
uint8_t *output_buffer = (uint8_t *)malloc(output_size);
|
|
if (!output_buffer) {
|
|
print_error("Failed to allocate SQZ unpack buffer, %d bytes", output_size);
|
|
return 0;
|
|
}
|
|
u->dst = output_buffer;
|
|
uint16_t code = unpack_sqz_clear_code(in, u);
|
|
assert(code != SQZ_END_CODE);
|
|
while (1) {
|
|
code = unpack_sqz_get_code(in, u);
|
|
if (code == SQZ_END_CODE) {
|
|
print_debug(DBG_UNPACK, "lzw end code");
|
|
break;
|
|
} else if (code == SQZ_CLEAR_CODE) {
|
|
print_debug(DBG_UNPACK, "lzw clear code");
|
|
unpack_sqz_clear_code(in, u);
|
|
continue;
|
|
}
|
|
const uint16_t current_code = code;
|
|
uint8_t *sp = u->stack;
|
|
if (u->new_codes <= code) {
|
|
*sp++ = u->last_code;
|
|
code = u->previous_code;
|
|
}
|
|
while (code >= SQZ_CODE_BASE) {
|
|
*sp++ = u->str[code];
|
|
code = u->prefix[code];
|
|
}
|
|
*sp++ = u->last_code = code & 255;
|
|
do {
|
|
--sp;
|
|
*u->dst++ = *sp;
|
|
} while (sp != u->stack);
|
|
const uint16_t index = u->new_codes;
|
|
if (index < 0x1000) {
|
|
u->str[index] = u->last_code;
|
|
u->prefix[index] = u->previous_code;
|
|
++u->new_codes;
|
|
}
|
|
u->previous_code = current_code;
|
|
}
|
|
const int count = (u->dst - output_buffer);
|
|
print_debug(DBG_UNPACK, "lzw output size %d (expected %d)", count, output_size);
|
|
assert(count == output_size);
|
|
u->dst = output_buffer;
|
|
return count;
|
|
}
|
|
|
|
struct unpack_sqv_t {
|
|
uint8_t dict_buf[0x200 * 2];
|
|
uint8_t rd[0x1000];
|
|
uint8_t *dst;
|
|
};
|
|
|
|
static struct unpack_sqv_t unsqv;
|
|
|
|
static int unpack_sqv(FILE *in, struct unpack_sqv_t *u) {
|
|
|
|
fread(u->rd, 1, 6, in);
|
|
const int uncompressed_size = (READ_LE_UINT16(u->rd) << 16) + READ_LE_UINT16(u->rd + 2);
|
|
const int dict_len = READ_LE_UINT16(u->rd + 4);
|
|
print_debug(DBG_UNPACK, "SQV uncompressed size %d dict_len %d", uncompressed_size, dict_len);
|
|
assert(dict_len <= 0x400);
|
|
fread(u->dict_buf, 1, dict_len, in);
|
|
|
|
uint8_t *output_buffer = (uint8_t *)malloc(uncompressed_size);
|
|
if (!output_buffer) {
|
|
print_error("Failed to allocate SQV unpack buffer, %d bytes", uncompressed_size);
|
|
return 0;
|
|
}
|
|
|
|
uint8_t *dst = output_buffer;
|
|
|
|
const uint8_t *src = u->rd;
|
|
int bits_count = 1;
|
|
int bytes_count = 2;
|
|
int state = 0;
|
|
int count = 0;
|
|
uint8_t code, prev;
|
|
uint16_t bits = 0;
|
|
uint16_t val = 0;
|
|
while ((dst - output_buffer) < uncompressed_size) {
|
|
--bits_count;
|
|
if (bits_count == 0) {
|
|
bytes_count -= 2;
|
|
if (bytes_count == 0) {
|
|
bytes_count = fread(u->rd, 1, 0x1000, in);
|
|
if (bytes_count == 0) {
|
|
break;
|
|
}
|
|
bytes_count += (bytes_count & 1);
|
|
src = u->rd;
|
|
}
|
|
bits = READ_BE_UINT16(src); src += 2;
|
|
bits_count = 16;
|
|
}
|
|
const bool carry = (bits & 0x8000) != 0;
|
|
bits <<= 1;
|
|
if (carry) {
|
|
val += 2;
|
|
}
|
|
assert((val & 1) == 0);
|
|
assert(val < dict_len);
|
|
val = READ_LE_UINT16(u->dict_buf + val);
|
|
if ((val & 0x8000) == 0) {
|
|
continue;
|
|
}
|
|
val &= ~0x8000;
|
|
switch (state) {
|
|
case 0:
|
|
code = val & 255;
|
|
if (val >> 8) {
|
|
switch (code) {
|
|
case 0:
|
|
state = 1;
|
|
break;
|
|
case 1:
|
|
state = 2;
|
|
break;
|
|
default:
|
|
memset(dst, prev, code);
|
|
dst += code;
|
|
break;
|
|
}
|
|
} else {
|
|
*dst++ = prev = code;
|
|
}
|
|
break;
|
|
case 1:
|
|
memset(dst, prev, val);
|
|
dst += val;
|
|
state = 0;
|
|
break;
|
|
case 2:
|
|
count = (val & 255) << 8;
|
|
state = 3;
|
|
break;
|
|
case 3:
|
|
count |= val & 255;
|
|
memset(dst, prev, count);
|
|
dst += count;
|
|
state = 0;
|
|
break;
|
|
}
|
|
val = 0;
|
|
}
|
|
assert((dst - output_buffer) == uncompressed_size);
|
|
u->dst = output_buffer;
|
|
return uncompressed_size;
|
|
}
|
|
|
|
uint8_t *unpack(FILE *in, int *uncompressed_size) {
|
|
const uint16_t sig = fread_le16(in);
|
|
fseek(in, 0, SEEK_SET);
|
|
if (sig == 0x4CB4) {
|
|
memset(&uneat, 0, sizeof(uneat));
|
|
*uncompressed_size = unpack_eat(in, &uneat);
|
|
return uneat.dst;
|
|
} else if ((sig >> 8) == 0x10) {
|
|
memset(&unsqz, 0, sizeof(unsqz));
|
|
*uncompressed_size = unpack_sqz(in, &unsqz);
|
|
return unsqz.dst;
|
|
} else {
|
|
memset(&unsqv, 0, sizeof(unsqv));
|
|
*uncompressed_size = unpack_sqv(in, &unsqv);
|
|
return unsqv.dst;
|
|
}
|
|
*uncompressed_size = 0;
|
|
return 0;
|
|
}
|
|
|