154 lines
3.6 KiB
C
Executable File
154 lines
3.6 KiB
C
Executable File
|
|
/* uncompress data packed with DIET.EXE */
|
|
|
|
#include "unpack.h"
|
|
#include "util.h"
|
|
|
|
static const bool _crc = false;
|
|
|
|
struct unpack_eat_t {
|
|
FILE *fp;
|
|
uint8_t len;
|
|
uint16_t bits;
|
|
uint8_t *dst;
|
|
};
|
|
|
|
static struct unpack_eat_t g_unpack;
|
|
|
|
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(struct unpack_eat_t *u, uint8_t *output_buffer, int output_buffer_size) {
|
|
uint8_t buffer[17];
|
|
const int header_size = fread(buffer, 1, sizeof(buffer), u->fp);
|
|
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);
|
|
if (output_size > output_buffer_size) {
|
|
print_error("Invalid output buffer size %d, need %d", output_buffer_size, output_size);
|
|
return 0;
|
|
}
|
|
if (_crc) { /* CCITT */
|
|
uint16_t tbl[256];
|
|
for (int i = 0; i < 256; ++i) {
|
|
uint16_t b = i;
|
|
for (int j = 0; j < 8; ++j) {
|
|
if (b & 1) {
|
|
b = (b >> 1) ^ 0xA001;
|
|
} else {
|
|
b >>= 1;
|
|
}
|
|
}
|
|
tbl[i] = b;
|
|
}
|
|
uint16_t ax = 0;
|
|
while (1) {
|
|
const uint8_t b = fgetc(u->fp);
|
|
if (feof(u->fp)) {
|
|
break;
|
|
}
|
|
ax = (ax >> 8) ^ tbl[(ax ^ b) & 0xFF];
|
|
}
|
|
if (ax != crc) {
|
|
print_error("Invalid CRC 0x%x, expected 0x%x", ax, crc);
|
|
return 0;
|
|
}
|
|
fseek(u->fp, 17, SEEK_SET);
|
|
}
|
|
|
|
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);
|
|
return output_size;
|
|
}
|
|
|
|
int unpack(const char *filename, uint8_t *dst, int size) {
|
|
int uncompressed_size = 0;
|
|
FILE *fp = fopen(filename, "rb");
|
|
if (fp) {
|
|
memset(&g_unpack, 0, sizeof(struct unpack_eat_t));
|
|
g_unpack.fp = fp;
|
|
uncompressed_size = unpack_eat(&g_unpack, dst, size);
|
|
fclose(fp);
|
|
} else {
|
|
print_error("Unable to open '%s'", filename);
|
|
}
|
|
return uncompressed_size;
|
|
}
|