mirror of https://github.com/bsnes-emu/bsnes.git
350 lines
8.0 KiB
C++
350 lines
8.0 KiB
C++
#pragma once
|
|
|
|
//a bad implementation of inflate from zlib/minizip
|
|
//todo: replace with Talarubi's version
|
|
|
|
#include <setjmp.h>
|
|
|
|
namespace nall::Decode {
|
|
|
|
namespace puff {
|
|
inline auto puff(
|
|
unsigned char* dest, unsigned long* destlen,
|
|
unsigned char* source, unsigned long* sourcelen
|
|
) -> int;
|
|
}
|
|
|
|
inline auto inflate(
|
|
uint8_t* target, uint targetLength,
|
|
const uint8_t* source, uint sourceLength
|
|
) -> bool {
|
|
unsigned long tl = targetLength, sl = sourceLength;
|
|
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
|
|
return result == 0;
|
|
}
|
|
|
|
namespace puff {
|
|
|
|
enum : uint {
|
|
MAXBITS = 15,
|
|
MAXLCODES = 286,
|
|
MAXDCODES = 30,
|
|
FIXLCODES = 288,
|
|
MAXCODES = MAXLCODES + MAXDCODES,
|
|
};
|
|
|
|
struct state {
|
|
unsigned char* out;
|
|
unsigned long outlen;
|
|
unsigned long outcnt;
|
|
|
|
unsigned char* in;
|
|
unsigned long inlen;
|
|
unsigned long incnt;
|
|
int bitbuf;
|
|
int bitcnt;
|
|
|
|
jmp_buf env;
|
|
};
|
|
|
|
struct huffman {
|
|
short* count;
|
|
short* symbol;
|
|
};
|
|
|
|
inline auto bits(state* s, int need) -> int {
|
|
long val;
|
|
|
|
val = s->bitbuf;
|
|
while(s->bitcnt < need) {
|
|
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
|
val |= (long)(s->in[s->incnt++]) << s->bitcnt;
|
|
s->bitcnt += 8;
|
|
}
|
|
|
|
s->bitbuf = (int)(val >> need);
|
|
s->bitcnt -= need;
|
|
|
|
return (int)(val & ((1L << need) - 1));
|
|
}
|
|
|
|
inline auto stored(state* s) -> int {
|
|
uint len;
|
|
|
|
s->bitbuf = 0;
|
|
s->bitcnt = 0;
|
|
|
|
if(s->incnt + 4 > s->inlen) return 2;
|
|
len = s->in[s->incnt++];
|
|
len |= s->in[s->incnt++] << 8;
|
|
if(s->in[s->incnt++] != (~len & 0xff) ||
|
|
s->in[s->incnt++] != ((~len >> 8) & 0xff)
|
|
) return 2;
|
|
|
|
if(s->incnt + len > s->inlen) return 2;
|
|
if(s->out != nullptr) {
|
|
if(s->outcnt + len > s->outlen) return 1;
|
|
while(len--) s->out[s->outcnt++] = s->in[s->incnt++];
|
|
} else {
|
|
s->outcnt += len;
|
|
s->incnt += len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline auto decode(state* s, huffman* h) -> int {
|
|
int len, code, first, count, index, bitbuf, left;
|
|
short* next;
|
|
|
|
bitbuf = s->bitbuf;
|
|
left = s->bitcnt;
|
|
code = first = index = 0;
|
|
len = 1;
|
|
next = h->count + 1;
|
|
while(true) {
|
|
while(left--) {
|
|
code |= bitbuf & 1;
|
|
bitbuf >>= 1;
|
|
count = *next++;
|
|
if(code - count < first) {
|
|
s->bitbuf = bitbuf;
|
|
s->bitcnt = (s->bitcnt - len) & 7;
|
|
return h->symbol[index + (code - first)];
|
|
}
|
|
index += count;
|
|
first += count;
|
|
first <<= 1;
|
|
code <<= 1;
|
|
len++;
|
|
}
|
|
left = (MAXBITS + 1) - len;
|
|
if(left == 0) break;
|
|
if(s->incnt == s->inlen) longjmp(s->env, 1);
|
|
bitbuf = s->in[s->incnt++];
|
|
if(left > 8) left = 8;
|
|
}
|
|
|
|
return -10;
|
|
}
|
|
|
|
inline auto construct(huffman* h, short* length, int n) -> int {
|
|
int symbol, len, left;
|
|
short offs[MAXBITS + 1];
|
|
|
|
for(len = 0; len <= MAXBITS; len++) h->count[len] = 0;
|
|
for(symbol = 0; symbol < n; symbol++) h->count[length[symbol]]++;
|
|
if(h->count[0] == n) return 0;
|
|
|
|
left = 1;
|
|
for(len = 1; len <= MAXBITS; len++) {
|
|
left <<= 1;
|
|
left -= h->count[len];
|
|
if(left < 0) return left;
|
|
}
|
|
|
|
offs[1] = 0;
|
|
for(len = 1; len < MAXBITS; len++) offs[len + 1] = offs[len] + h->count[len];
|
|
|
|
for(symbol = 0; symbol < n; symbol++) {
|
|
if(length[symbol] != 0) h->symbol[offs[length[symbol]]++] = symbol;
|
|
}
|
|
|
|
return left;
|
|
}
|
|
|
|
inline auto codes(state* s, huffman* lencode, huffman* distcode) -> int {
|
|
int symbol, len;
|
|
uint dist;
|
|
static const short lens[29] = {
|
|
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
|
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258
|
|
};
|
|
static const short lext[29] = {
|
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
|
|
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0
|
|
};
|
|
static const short dists[30] = {
|
|
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
|
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
|
8193, 12289, 16385, 24577
|
|
};
|
|
static const short dext[30] = {
|
|
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
|
|
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
|
|
12, 12, 13, 13
|
|
};
|
|
|
|
do {
|
|
symbol = decode(s, lencode);
|
|
if(symbol < 0) return symbol;
|
|
if(symbol < 256) {
|
|
if(s->out != nullptr) {
|
|
if(s->outcnt == s->outlen) return 1;
|
|
s->out[s->outcnt] = symbol;
|
|
}
|
|
s->outcnt++;
|
|
} else if(symbol > 256) {
|
|
symbol -= 257;
|
|
if(symbol >= 29) return -10;
|
|
len = lens[symbol] + bits(s, lext[symbol]);
|
|
|
|
symbol = decode(s, distcode);
|
|
if(symbol < 0) return symbol;
|
|
dist = dists[symbol] + bits(s, dext[symbol]);
|
|
#ifndef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
|
if(dist > s->outcnt) return -11;
|
|
#endif
|
|
|
|
if(s->out != nullptr) {
|
|
if(s->outcnt + len > s->outlen) return 1;
|
|
while(len--) {
|
|
s->out[s->outcnt] =
|
|
#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOO_FAR
|
|
dist > s->outcnt ? 0 :
|
|
#endif
|
|
s->out[s->outcnt - dist];
|
|
s->outcnt++;
|
|
}
|
|
} else {
|
|
s->outcnt += len;
|
|
}
|
|
}
|
|
} while(symbol != 256);
|
|
|
|
return 0;
|
|
}
|
|
|
|
inline auto fixed(state* s) -> int {
|
|
static int virgin = 1;
|
|
static short lencnt[MAXBITS + 1], lensym[FIXLCODES];
|
|
static short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
|
static huffman lencode, distcode;
|
|
|
|
if(virgin) {
|
|
int symbol = 0;
|
|
short lengths[FIXLCODES];
|
|
|
|
lencode.count = lencnt;
|
|
lencode.symbol = lensym;
|
|
distcode.count = distcnt;
|
|
distcode.symbol = distsym;
|
|
|
|
for(; symbol < 144; symbol++) lengths[symbol] = 8;
|
|
for(; symbol < 256; symbol++) lengths[symbol] = 9;
|
|
for(; symbol < 280; symbol++) lengths[symbol] = 7;
|
|
for(; symbol < FIXLCODES; symbol++) lengths[symbol] = 8;
|
|
construct(&lencode, lengths, FIXLCODES);
|
|
|
|
for(symbol = 0; symbol < MAXDCODES; symbol++) lengths[symbol] = 5;
|
|
construct(&distcode, lengths, MAXDCODES);
|
|
|
|
virgin = 0;
|
|
}
|
|
|
|
return codes(s, &lencode, &distcode);
|
|
}
|
|
|
|
inline auto dynamic(state* s) -> int {
|
|
int nlen, ndist, ncode, index, err;
|
|
short lengths[MAXCODES];
|
|
short lencnt[MAXBITS + 1], lensym[MAXLCODES];
|
|
short distcnt[MAXBITS + 1], distsym[MAXDCODES];
|
|
huffman lencode, distcode;
|
|
static const short order[19] = {
|
|
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
|
};
|
|
|
|
lencode.count = lencnt;
|
|
lencode.symbol = lensym;
|
|
distcode.count = distcnt;
|
|
distcode.symbol = distsym;
|
|
|
|
nlen = bits(s, 5) + 257;
|
|
ndist = bits(s, 5) + 1;
|
|
ncode = bits(s, 4) + 4;
|
|
if(nlen > MAXLCODES || ndist > MAXDCODES) return -3;
|
|
|
|
for(index = 0; index < ncode; index++) lengths[order[index]] = bits(s, 3);
|
|
for(; index < 19; index++) lengths[order[index]] = 0;
|
|
|
|
err = construct(&lencode, lengths, 19);
|
|
if(err != 0) return -4;
|
|
|
|
index = 0;
|
|
while(index < nlen + ndist) {
|
|
int symbol, len;
|
|
|
|
symbol = decode(s, &lencode);
|
|
if(symbol < 16) {
|
|
lengths[index++] = symbol;
|
|
} else {
|
|
len = 0;
|
|
if(symbol == 16) {
|
|
if(index == 0) return -5;
|
|
len = lengths[index - 1];
|
|
symbol = 3 + bits(s, 2);
|
|
} else if(symbol == 17) {
|
|
symbol = 3 + bits(s, 3);
|
|
} else {
|
|
symbol = 11 + bits(s, 7);
|
|
}
|
|
if(index + symbol > nlen + ndist) return -6;
|
|
while(symbol--) lengths[index++] = len;
|
|
}
|
|
}
|
|
|
|
if(lengths[256] == 0) return -9;
|
|
|
|
err = construct(&lencode, lengths, nlen);
|
|
if(err < 0 || (err > 0 && nlen - lencode.count[0] != 1)) return -7;
|
|
|
|
err = construct(&distcode, lengths + nlen, ndist);
|
|
if(err < 0 || (err > 0 && ndist - distcode.count[0] != 1)) return -8;
|
|
|
|
return codes(s, &lencode, &distcode);
|
|
}
|
|
|
|
inline auto puff(
|
|
unsigned char* dest, unsigned long* destlen,
|
|
unsigned char* source, unsigned long* sourcelen
|
|
) -> int {
|
|
state s;
|
|
int last, type, err;
|
|
|
|
s.out = dest;
|
|
s.outlen = *destlen;
|
|
s.outcnt = 0;
|
|
|
|
s.in = source;
|
|
s.inlen = *sourcelen;
|
|
s.incnt = 0;
|
|
s.bitbuf = 0;
|
|
s.bitcnt = 0;
|
|
|
|
if(setjmp(s.env) != 0) {
|
|
err = 2;
|
|
} else {
|
|
do {
|
|
last = bits(&s, 1);
|
|
type = bits(&s, 2);
|
|
err = type == 0 ? stored(&s)
|
|
: type == 1 ? fixed(&s)
|
|
: type == 2 ? dynamic(&s)
|
|
: -1;
|
|
if(err != 0) break;
|
|
} while(!last);
|
|
}
|
|
|
|
if(err <= 0) {
|
|
*destlen = s.outcnt;
|
|
*sourcelen = s.incnt;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
}
|
|
|
|
}
|