Files
project-solana/games/rac1/host/overrides/func_0020C468.c
T
2026-10-13 20:04:31 -04:00

90 lines
3.4 KiB
C

#include <stdint.h>
#include <stdlib.h>
/* func_0020C468(src, dst): unpack a "WAD" compressed block (Insomniac's
* LZ77 variant, close to LZO1X) and return the unpacked size.
*
* Ported from the UYA Wrapper (UYA's copy is func_0039B760). RAC1's retail
* version is the same hand-written asm: it checks the "WAD" magic and streams
* the input through the scratchpad in 0x2000-byte halves by DMA. Here the
* input is read straight from memory; a far match with distance 0 and length
* above 1 is the encoder's "go to the next 0x2000-byte chunk" marker, which in
* a linear read means skipping to the next chunk boundary of the data (which
* starts 0x10 bytes into the block). */
#include "common.h"
s32 func_0020C468(u8 *src, u8 *dst)
{
u8 *start = dst;
{ /* testing: UYA_LOADLOG=1 */
static int on = -1;
if (on < 0) on = getenv("UYA_LOADLOG") != 0;
if (on) { uint32_t uya_frame_count(void); const char *rc_stack_scan(void); uya_log("v%u WAD unpack %08X -> %08X: %.3s; stack%s", uya_frame_count(), (u32)(uintptr_t)src, (u32)(uintptr_t)dst, (const char *)src, rc_stack_scan()); }
}
if (src[0] != 'W' || src[1] != 'A' || src[2] != 'D') return 0;
u32 size = src[3] | (src[4] << 8) | (src[5] << 16) | ((u32)src[6] << 24);
u8 *data = src + 0x10, *in = data, *end = src + size;
u32 flag, n;
u8 *m;
if (in[0] > 0x11) { /* leading literal run */
n = *in++ - 0x11;
while (n--) *dst++ = *in++;
goto after_literals;
}
for (;;) {
/* state A: a literal run may come next */
if (in >= end) break;
flag = *in++;
if (flag < 0x10) {
n = flag ? flag : *in++ + 0xF;
n += 3;
while (n--) *dst++ = *in++;
after_literals:
if (in >= end) break;
flag = *in++;
}
/* a match */
for (;;) {
if (flag >= 0x40) {
m = dst - 1 - ((flag >> 2) & 7);
m -= *in++ << 3;
n = (flag >> 5) - 1;
} else if (flag >= 0x20) {
n = flag & 0x1F;
if (!n) n = *in++ + 0x1F;
m = dst - 1 - ((in[0] >> 2) + (in[1] << 6));
in += 2;
} else if (flag >= 0x10) {
m = dst - ((flag & 8) << 11);
n = flag & 7;
if (!n) n = *in++ + 7;
m -= (in[0] >> 2) + (in[1] << 6);
in += 2;
if (m == dst) {
if (n != 1) { /* next-chunk marker */
u32 off = (u32)(in - data);
in = data + ((off + 0x1FFF) & ~0x1FFFu);
goto state_a;
}
goto trailing; /* distance 0, length 1: no copy */
}
m -= 0x4000;
} else {
uya_unimpl("func_0020C468: bad WAD stream");
return (s32)(dst - start);
}
*dst++ = *m++;
*dst++ = *m++;
do { *dst++ = *m++; } while ((s32)--n > 0);
trailing:
n = in[-2] & 3; /* up to 3 literals after a match */
if (!n) goto state_a;
while (n--) *dst++ = *in++;
if (in >= end) return (s32)(dst - start);
flag = *in++;
}
state_a:;
}
return (s32)(dst - start);
}