bsnes/ananke/nall/inflate.hpp

359 lines
8.1 KiB
C++
Raw Normal View History

Update to v091r11 release. byuu says: This release refines HSU1 support as a bidirectional protocol, nests SFC manifests as "release/cartridge" and "release/information" (but release/ is not guaranteed to be finalized just yet), removes the database integration, and adds support for ananke. ananke represents inevitability. It's a library that, when installed, higan can use to load files from the command-line, and also from a new File -> Load Game menu option. I need to change the build rules a bit for it to work on Windows (need to make phoenix a DLL, basically), but it works now on Linux. Right now, it only takes *.sfc file names, looks them up in the included database, converts them to game folders, and returns the game folder path for higan to load. The idea is to continue expanding it to support everything we can that I don't want in the higan core: - load *.sfc, *.smc, *.swc, *.fig files - remove SNES copier headers - split apart merged firmware files - pull in external firmware files (eg dsp1b.rom - these are staying merged, just as SPC7110 prg+dat are merged) - load *.zip and *.7z archives - prompt for selection on multi-file archives - generate manifest files based on heuristics - apply BPS patches The "Load" menu option has been renamed to "Library", to represent games in your library. I'm going to add some sort of suffix to indicate unverified games, and use a different folder icon for those (eg manifests built on heuristics rather than from the database.) So basically, to future end users: File -> Load Game will be how they play games. Library -> (specific system) can be thought of as an infinitely-sized recent games list. purify will likely become a simple stub that invokes ananke's functions. No reason to duplicate all that code.
2012-11-05 08:22:50 +00:00
#ifndef NALL_INFLATE_HPP
#define NALL_INFLATE_HPP
#include <setjmp.h>
namespace nall {
namespace puff {
inline int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
);
}
inline bool inflate(
uint8_t *target, unsigned targetLength,
const uint8_t *source, unsigned sourceLength
) {
unsigned long tl = targetLength, sl = sourceLength;
int result = puff::puff((unsigned char*)target, &tl, (unsigned char*)source, &sl);
return result == 0;
}
namespace puff {
//zlib/contrib/puff.c
//version 2.1*
//author: Mark Adler
//license: zlib
//ported by: byuu
//* I have corrected a bug in fixed(), where it was accessing uninitialized
// memory: calling construct() with lencode prior to initializing lencode.count
enum {
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 int bits(state *s, int need) {
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 int stored(state *s) {
unsigned 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 != 0) {
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 int decode(state *s, huffman *h) {
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 int construct(huffman *h, short *length, int n) {
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 int codes(state *s, huffman *lencode, huffman *distcode) {
int symbol, len;
unsigned 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 != 0) {
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 != 0) {
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 int fixed(state *s) {
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 int dynamic(state *s) {
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 int puff(
unsigned char *dest, unsigned long *destlen,
unsigned char *source, unsigned long *sourcelen
) {
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;
}
}
}
#endif