mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
444 lines
13 KiB
Lua
444 lines
13 KiB
Lua
--lualoader, R"EOF(--
|
|
--zlib binding, taken and modified from https://github.com/luapower/zlib
|
|
--Written by Cosmin Apreutesei. Public Domain.
|
|
|
|
ffi.cdef [[
|
|
enum {
|
|
/* flush values*/
|
|
Z_NO_FLUSH = 0,
|
|
Z_PARTIAL_FLUSH = 1,
|
|
Z_SYNC_FLUSH = 2,
|
|
Z_FULL_FLUSH = 3,
|
|
Z_FINISH = 4,
|
|
Z_BLOCK = 5,
|
|
Z_TREES = 6,
|
|
/* return codes */
|
|
Z_OK = 0,
|
|
Z_STREAM_END = 1,
|
|
Z_NEED_DICT = 2,
|
|
Z_ERRNO = -1,
|
|
Z_STREAM_ERROR = -2,
|
|
Z_DATA_ERROR = -3,
|
|
Z_MEM_ERROR = -4,
|
|
Z_BUF_ERROR = -5,
|
|
Z_VERSION_ERROR = -6,
|
|
/* compression values */
|
|
Z_NO_COMPRESSION = 0,
|
|
Z_BEST_SPEED = 1,
|
|
Z_BEST_COMPRESSION = 9,
|
|
Z_DEFAULT_COMPRESSION = -1,
|
|
/* compression levels */
|
|
Z_FILTERED = 1,
|
|
Z_HUFFMAN_ONLY = 2,
|
|
Z_RLE = 3,
|
|
Z_FIXED = 4,
|
|
Z_DEFAULT_STRATEGY = 0,
|
|
/* compression strategies */
|
|
Z_BINARY = 0,
|
|
Z_TEXT = 1,
|
|
Z_ASCII = Z_TEXT, /* for compatibility with 1.2.2 and earlier */
|
|
Z_UNKNOWN = 2,
|
|
/* Possible values of the data_type field (though see inflate()) */
|
|
Z_DEFLATED = 8,
|
|
/* The deflate compression method (the only one supported in this version) */
|
|
Z_NULL = 0, /* for initializing zalloc, zfree, opaque */
|
|
Z_MAX_WBITS = 15 /* 32K LZ77 window */
|
|
};
|
|
|
|
typedef struct {
|
|
int unused;
|
|
} gzFile_s;
|
|
typedef gzFile_s* gzFile;
|
|
|
|
typedef void* (*z_alloc_func)(void* opaque, unsigned items, unsigned size);
|
|
typedef void (*z_free_func)(void* opaque, void* address);
|
|
typedef unsigned (*z_in_func)(void*, unsigned char**);
|
|
typedef int (*z_out_func)(void*, unsigned char*, unsigned);
|
|
|
|
typedef struct z_stream_s {
|
|
const char* next_in;
|
|
unsigned avail_in;
|
|
unsigned long total_in;
|
|
char* next_out;
|
|
unsigned avail_out;
|
|
unsigned long total_out;
|
|
char* msg;
|
|
void* state;
|
|
z_alloc_func zalloc;
|
|
z_free_func zfree;
|
|
void* opaque;
|
|
int data_type;
|
|
unsigned long adler;
|
|
unsigned long reserved;
|
|
} z_stream;
|
|
|
|
typedef struct gz_header_s {
|
|
int text;
|
|
unsigned long time;
|
|
int xflags;
|
|
int os;
|
|
char* extra;
|
|
unsigned extra_len;
|
|
unsigned extra_max;
|
|
char* name;
|
|
unsigned name_max;
|
|
char* comment;
|
|
unsigned comm_max;
|
|
int hcrc;
|
|
int done;
|
|
} gz_header;
|
|
|
|
const char* zlibVersion();
|
|
unsigned long zlibCompileFlags();
|
|
const char* zError(int);
|
|
|
|
int inflate(z_stream*, int flush);
|
|
int inflateEnd(z_stream*);
|
|
|
|
int inflateSetDictionary(z_stream*, const char* dictionary, unsigned dictLength);
|
|
int inflateSync(z_stream*);
|
|
int inflateCopy(z_stream*, z_stream* source);
|
|
int inflateReset(z_stream*);
|
|
int inflateReset2(z_stream*, int windowBits);
|
|
int inflatePrime(z_stream*, int bits, int value);
|
|
long inflateMark(z_stream*);
|
|
int inflateGetHeader(z_stream*, gz_header* head);
|
|
int inflateBack(z_stream*, z_in_func in, void* in_desc, z_out_func out, void* out_desc);
|
|
int inflateBackEnd(z_stream*);
|
|
int inflateInit_(z_stream*, const char* version, int stream_size);
|
|
int inflateInit2_(z_stream*, int windowBits, const char* version, int stream_size);
|
|
int inflateBackInit_(z_stream*, int windowBits, unsigned char* window, const char* version, int stream_size);
|
|
int inflateSyncPoint(z_stream*);
|
|
int inflateUndermine(z_stream*, int);
|
|
|
|
int deflate(z_stream*, int flush);
|
|
int deflateEnd(z_stream*);
|
|
|
|
int deflateSetDictionary(z_stream*, const char* dictionary, unsigned dictLength);
|
|
int deflateCopy(z_stream*, z_stream* source);
|
|
int deflateReset(z_stream*);
|
|
int deflateParams(z_stream*, int level, int strategy);
|
|
int deflateTune(z_stream*, int good_length, int max_lazy, int nice_length, int max_chain);
|
|
unsigned long deflateBound(z_stream*, unsigned long sourceLen);
|
|
int deflatePrime(z_stream*, int bits, int value);
|
|
int deflateSetHeader(z_stream*, gz_header* head);
|
|
int deflateInit_(z_stream*, int level, const char* version, int stream_size);
|
|
int deflateInit2_(z_stream*, int level, int method, int windowBits, int memLevel, int strategy, const char* version,
|
|
int stream_size);
|
|
|
|
int compress(char* dest, unsigned long* destLen, const char* source, unsigned long sourceLen);
|
|
int compress2(char* dest, unsigned long* destLen, const char* source, unsigned long sourceLen, int level);
|
|
unsigned long compressBound(unsigned long sourceLen);
|
|
int uncompress(char* dest, unsigned long* destLen, const char* source, unsigned long sourceLen);
|
|
|
|
gzFile gzdopen(int fd, const char* mode);
|
|
int gzbuffer(gzFile, unsigned size);
|
|
int gzsetparams(gzFile, int level, int strategy);
|
|
int gzread(gzFile, void* buf, unsigned len);
|
|
int gzwrite(gzFile, void const* buf, unsigned len);
|
|
int gzprintf(gzFile, const char* format, ...);
|
|
int gzputs(gzFile, const char* s);
|
|
char* gzgets(gzFile, char* buf, int len);
|
|
int gzputc(gzFile, int c);
|
|
int gzgetc(gzFile);
|
|
int gzungetc(int c, gzFile);
|
|
int gzflush(gzFile, int flush);
|
|
int gzrewind(gzFile);
|
|
int gzeof(gzFile);
|
|
int gzdirect(gzFile);
|
|
int gzclose(gzFile);
|
|
int gzclose_r(gzFile);
|
|
int gzclose_w(gzFile);
|
|
const char* gzerror(gzFile, int* errnum);
|
|
void gzclearerr(gzFile);
|
|
gzFile gzopen(const char*, const char*);
|
|
long gzseek(gzFile, long, int);
|
|
long gztell(gzFile);
|
|
long gzoffset(gzFile);
|
|
|
|
unsigned long adler32(unsigned long adler, const char* buf, unsigned len);
|
|
unsigned long crc32(unsigned long crc, const char* buf, unsigned len);
|
|
unsigned long adler32_combine(unsigned long, unsigned long, long);
|
|
unsigned long crc32_combine(unsigned long, unsigned long, long);
|
|
|
|
const unsigned long* get_crc_table(void);
|
|
]]
|
|
|
|
local C = ffi.load'z'
|
|
|
|
local function version()
|
|
return ffi.string(C.zlibVersion())
|
|
end
|
|
|
|
local function checkz(ret)
|
|
if ret == 0 then return end
|
|
error(ffi.string(C.zError(ret)))
|
|
end
|
|
|
|
local function flate(api)
|
|
return function(...)
|
|
local ret = api(...)
|
|
if ret == 0 then return true end
|
|
if ret == C.Z_STREAM_END then return false end
|
|
checkz(ret)
|
|
end
|
|
end
|
|
|
|
local deflate = flate(C.deflate)
|
|
local inflate = flate(C.inflate)
|
|
|
|
--FUN TIME: windowBits is range 8..15 (default = 15) but can also be -8..15
|
|
--for raw deflate with no zlib header or trailer and can also be greater than
|
|
--15 which reads/writes a gzip header and trailer instead of a zlib wrapper.
|
|
--so I added a format parameter which can be 'deflate', 'zlib', 'gzip'
|
|
--(default = 'zlib') to cover all the cases so that windowBits can express
|
|
--only the window bits in the initial 8..15 range. additionally for inflate,
|
|
--windowBits can be 0 which means use the value in the zlib header of the
|
|
--compressed stream.
|
|
|
|
local function format_windowBits(format, windowBits)
|
|
if format == 'gzip' then windowBits = windowBits + 16 end
|
|
if format == 'deflate' then windowBits = -windowBits end
|
|
return windowBits
|
|
end
|
|
|
|
local function init_deflate(format, level, method, windowBits, memLevel, strategy)
|
|
level = level or C.Z_DEFAULT_COMPRESSION
|
|
method = method or C.Z_DEFLATED
|
|
windowBits = format_windowBits(format, windowBits or C.Z_MAX_WBITS)
|
|
memLevel = memLevel or 8
|
|
strategy = strategy or C.Z_DEFAULT_STRATEGY
|
|
|
|
local strm = ffi.new'z_stream'
|
|
checkz(C.deflateInit2_(strm, level, method, windowBits, memLevel, strategy, version(), ffi.sizeof(strm)))
|
|
ffi.gc(strm, C.deflateEnd)
|
|
return strm, deflate
|
|
end
|
|
|
|
local function init_inflate(format, windowBits)
|
|
windowBits = format_windowBits(format, windowBits or C.Z_MAX_WBITS)
|
|
|
|
local strm = ffi.new'z_stream'
|
|
checkz(C.inflateInit2_(strm, windowBits, version(), ffi.sizeof(strm)))
|
|
ffi.gc(strm, C.inflateEnd)
|
|
return strm, inflate
|
|
end
|
|
|
|
local function inflate_deflate(init)
|
|
return function(read, write, bufsize, ...)
|
|
bufsize = bufsize or 16384
|
|
|
|
local strm, flate = init(...)
|
|
|
|
local buf = ffi.new('uint8_t[?]', bufsize)
|
|
strm.next_out, strm.avail_out = buf, bufsize
|
|
strm.next_in, strm.avail_in = nil, 0
|
|
|
|
if type(read) == 'string' then
|
|
local s = read
|
|
local done
|
|
read = function()
|
|
if done then return end
|
|
done = true
|
|
return s
|
|
end
|
|
elseif type(read) == 'table' then
|
|
local t = read
|
|
local i = 0
|
|
read = function()
|
|
i = i + 1
|
|
return t[i]
|
|
end
|
|
end
|
|
|
|
local t
|
|
local asstring = write == ''
|
|
if type(write) == 'table' or asstring then
|
|
t = asstring and {} or write
|
|
write = function(data, sz)
|
|
t[#t+1] = ffi.string(data, sz)
|
|
end
|
|
end
|
|
|
|
local function flush()
|
|
local sz = bufsize - strm.avail_out
|
|
if sz == 0 then return end
|
|
write(buf, sz)
|
|
strm.next_out, strm.avail_out = buf, bufsize
|
|
end
|
|
|
|
local data, size --data must be anchored as an upvalue!
|
|
while true do
|
|
if strm.avail_in == 0 then --input buffer empty: refill
|
|
data, size = read()
|
|
if not data then --eof: finish up
|
|
local ret
|
|
repeat
|
|
flush()
|
|
until not flate(strm, C.Z_FINISH)
|
|
flush()
|
|
break
|
|
end
|
|
strm.next_in, strm.avail_in = data, size or #data
|
|
end
|
|
flush()
|
|
if not flate(strm, C.Z_NO_FLUSH) then
|
|
flush()
|
|
break
|
|
end
|
|
end
|
|
|
|
if asstring then
|
|
return table.concat(t)
|
|
else
|
|
return t
|
|
end
|
|
end
|
|
end
|
|
|
|
--inflate(read, write[, bufsize][, format][, windowBits])
|
|
local inflate = inflate_deflate(init_inflate)
|
|
--deflate(read, write[, bufsize][, format][, level][, windowBits][, memLevel][, strategy])
|
|
local deflate = inflate_deflate(init_deflate)
|
|
|
|
--utility functions
|
|
|
|
local function compress_tobuffer(data, size, level, buf, sz)
|
|
level = level or -1
|
|
sz = ffi.new('unsigned long[1]', sz)
|
|
checkz(C.compress2(buf, sz, data, size, level))
|
|
return sz[0]
|
|
end
|
|
|
|
local function compress(data, size, level)
|
|
size = size or #data
|
|
local sz = C.compressBound(size)
|
|
local buf = ffi.new('uint8_t[?]', sz)
|
|
sz = compress_tobuffer(data, size, level, buf, sz)
|
|
return ffi.string(buf, sz)
|
|
end
|
|
|
|
local function uncompress_tobuffer(data, size, buf, sz)
|
|
sz = ffi.new('unsigned long[1]', sz)
|
|
checkz(C.uncompress(buf, sz, data, size))
|
|
return sz[0]
|
|
end
|
|
|
|
local function uncompress(data, size, sz)
|
|
local buf = ffi.new('uint8_t[?]', sz)
|
|
sz = uncompress_tobuffer(data, size or #data, buf, sz)
|
|
return ffi.string(buf, sz)
|
|
end
|
|
|
|
--gzip file access functions
|
|
|
|
local function checkz(ret) assert(ret == 0) end
|
|
local function checkminus1(ret) assert(ret ~= -1); return ret end
|
|
local function ptr(o) return o ~= nil and o or nil end
|
|
|
|
local function gzclose(gzfile)
|
|
checkz(C.gzclose(gzfile))
|
|
ffi.gc(gzfile, nil)
|
|
end
|
|
|
|
local function gzopen(filename, mode, bufsize)
|
|
local gzfile = ptr(C.gzopen(filename, mode or 'r'))
|
|
if not gzfile then
|
|
return nil, string.format('errno %d', ffi.errno())
|
|
end
|
|
ffi.gc(gzfile, gzclose)
|
|
if bufsize then C.gzbuffer(gzfile, bufsize) end
|
|
return gzfile
|
|
end
|
|
|
|
local flush_enum = {
|
|
none = C.Z_NO_FLUSH,
|
|
partial = C.Z_PARTIAL_FLUSH,
|
|
sync = C.Z_SYNC_FLUSH,
|
|
full = C.Z_FULL_FLUSH,
|
|
finish = C.Z_FINISH,
|
|
block = C.Z_BLOCK,
|
|
trees = C.Z_TREES,
|
|
}
|
|
|
|
local function gzflush(gzfile, flush)
|
|
checkz(C.gzflush(gzfile, flush_enum[flush]))
|
|
end
|
|
|
|
local function gzread_tobuffer(gzfile, buf, sz)
|
|
return checkminus1(C.gzread(gzfile, buf, sz))
|
|
end
|
|
|
|
local function gzread(gzfile, sz)
|
|
local buf = ffi.new('uint8_t[?]', sz)
|
|
return ffi.string(buf, gzread_tobuffer(gzfile, buf, sz))
|
|
end
|
|
|
|
local function gzwrite(gzfile, data, sz)
|
|
sz = C.gzwrite(gzfile, data, sz or #data)
|
|
if sz == 0 then return nil,'error' end
|
|
return sz
|
|
end
|
|
|
|
local function gzeof(gzfile)
|
|
return C.gzeof(gzfile) == 1
|
|
end
|
|
|
|
local function gzseek(gzfile, ...)
|
|
local narg = select('#',...)
|
|
local whence, offset
|
|
if narg == 0 then
|
|
whence, offset = 'cur', 0
|
|
elseif narg == 1 then
|
|
if type(...) == 'string' then
|
|
whence, offset = ..., 0
|
|
else
|
|
whence, offset = 'cur',...
|
|
end
|
|
else
|
|
whence, offset = ...
|
|
end
|
|
whence = assert(whence == 'set' and 0 or whence == 'cur' and 1)
|
|
return checkminus1(C.gzseek(gzfile, offset, whence))
|
|
end
|
|
|
|
local function gzoffset(gzfile)
|
|
return checkminus1(C.gzoffset(gzfile))
|
|
end
|
|
|
|
ffi.metatype('gzFile_s', {__index = {
|
|
close = gzclose,
|
|
read = gzread,
|
|
write = gzwrite,
|
|
flush = gzflush,
|
|
eof = gzeof,
|
|
seek = gzseek,
|
|
offset = gzoffset,
|
|
}})
|
|
|
|
--checksum functions
|
|
|
|
local function adler32(data, sz, adler)
|
|
adler = adler or C.adler32(0, nil, 0)
|
|
return tonumber(C.adler32(adler, data, sz or #data))
|
|
end
|
|
|
|
local function crc32(data, sz, crc)
|
|
crc = crc or C.crc32(0, nil, 0)
|
|
return tonumber(C.crc32(crc, data, sz or #data))
|
|
end
|
|
|
|
zlib = {
|
|
version = version,
|
|
inflate = inflate,
|
|
deflate = deflate,
|
|
uncompress_tobuffer = uncompress_tobuffer,
|
|
uncompress = uncompress,
|
|
compress_tobuffer = compress_tobuffer,
|
|
compress = compress,
|
|
open = gzopen,
|
|
adler32 = adler32,
|
|
crc32 = crc32,
|
|
}
|
|
|
|
-- )EOF"
|