From c876d3bbefe0dc00c27ca0c12d29da5874946962 Mon Sep 17 00:00:00 2001 From: Dominik Riebeling Date: Wed, 15 Dec 2021 21:04:28 +0100 Subject: rbutil: Merge rbutil with utils folder. rbutil uses several components from the utils folder, and can be considered part of utils too. Having it in a separate folder is an arbitrary split that doesn't help anymore these days, so merge them. This also allows other utils to easily use libtools.make without the need to navigate to a different folder. Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21 --- utils/rbutilqt/mspack/kwajd.c | 566 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 566 insertions(+) create mode 100644 utils/rbutilqt/mspack/kwajd.c (limited to 'utils/rbutilqt/mspack/kwajd.c') diff --git a/utils/rbutilqt/mspack/kwajd.c b/utils/rbutilqt/mspack/kwajd.c new file mode 100644 index 0000000000..24e0b0613b --- /dev/null +++ b/utils/rbutilqt/mspack/kwajd.c @@ -0,0 +1,566 @@ +/* This file is part of libmspack. + * (C) 2003-2011 Stuart Caie. + * + * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was + * written by Jeff Johnson. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* KWAJ decompression implementation */ + +#include "system-mspack.h" +#include "kwaj.h" +#include "mszip.h" + +/* prototypes */ +static struct mskwajd_header *kwajd_open( + struct mskwaj_decompressor *base, const char *filename); +static void kwajd_close( + struct mskwaj_decompressor *base, struct mskwajd_header *hdr); +static int kwajd_read_headers( + struct mspack_system *sys, struct mspack_file *fh, + struct mskwajd_header *hdr); +static int kwajd_extract( + struct mskwaj_decompressor *base, struct mskwajd_header *hdr, + const char *filename); +static int kwajd_decompress( + struct mskwaj_decompressor *base, const char *input, const char *output); +static int kwajd_error( + struct mskwaj_decompressor *base); + +static struct kwajd_stream *lzh_init( + struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out); +static int lzh_decompress( + struct kwajd_stream *kwaj); +static void lzh_free( + struct kwajd_stream *kwaj); +static int lzh_read_lens( + struct kwajd_stream *kwaj, + unsigned int type, unsigned int numsyms, + unsigned char *lens); +static int lzh_read_input( + struct kwajd_stream *kwaj); + + +/*************************************** + * MSPACK_CREATE_KWAJ_DECOMPRESSOR + *************************************** + * constructor + */ +struct mskwaj_decompressor * + mspack_create_kwaj_decompressor(struct mspack_system *sys) +{ + struct mskwaj_decompressor_p *self = NULL; + + if (!sys) sys = mspack_default_system; + if (!mspack_valid_system(sys)) return NULL; + + if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) { + self->base.open = &kwajd_open; + self->base.close = &kwajd_close; + self->base.extract = &kwajd_extract; + self->base.decompress = &kwajd_decompress; + self->base.last_error = &kwajd_error; + self->system = sys; + self->error = MSPACK_ERR_OK; + } + return (struct mskwaj_decompressor *) self; +} + +/*************************************** + * MSPACK_DESTROY_KWAJ_DECOMPRESSOR + *************************************** + * destructor + */ +void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + if (self) { + struct mspack_system *sys = self->system; + sys->free(self); + } +} + +/*************************************** + * KWAJD_OPEN + *************************************** + * opens a KWAJ file without decompressing, reads header + */ +static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base, + const char *filename) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header *hdr; + struct mspack_system *sys; + struct mspack_file *fh; + + if (!self) return NULL; + sys = self->system; + + fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ); + hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p)); + if (fh && hdr) { + ((struct mskwajd_header_p *) hdr)->fh = fh; + self->error = kwajd_read_headers(sys, fh, hdr); + } + else { + if (!fh) self->error = MSPACK_ERR_OPEN; + if (!hdr) self->error = MSPACK_ERR_NOMEMORY; + } + + if (self->error) { + if (fh) sys->close(fh); + sys->free(hdr); + hdr = NULL; + } + + return hdr; +} + +/*************************************** + * KWAJD_CLOSE + *************************************** + * closes a KWAJ file + */ +static void kwajd_close(struct mskwaj_decompressor *base, + struct mskwajd_header *hdr) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr; + + if (!self || !self->system) return; + + /* close the file handle associated */ + self->system->close(hdr_p->fh); + + /* free the memory associated */ + self->system->free(hdr); + + self->error = MSPACK_ERR_OK; +} + +/*************************************** + * KWAJD_READ_HEADERS + *************************************** + * reads the headers of a KWAJ format file + */ +static int kwajd_read_headers(struct mspack_system *sys, + struct mspack_file *fh, + struct mskwajd_header *hdr) +{ + unsigned char buf[16]; + int i; + + /* read in the header */ + if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) { + return MSPACK_ERR_READ; + } + + /* check for "KWAJ" signature */ + if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) || + ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088)) + { + return MSPACK_ERR_SIGNATURE; + } + + /* basic header fields */ + hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]); + hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]); + hdr->headers = EndGetI16(&buf[kwajh_Flags]); + hdr->length = 0; + hdr->filename = NULL; + hdr->extra = NULL; + hdr->extra_length = 0; + + /* optional headers */ + + /* 4 bytes: length of unpacked file */ + if (hdr->headers & MSKWAJ_HDR_HASLENGTH) { + if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ; + hdr->length = EndGetI32(&buf[0]); + } + + /* 2 bytes: unknown purpose */ + if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + } + + /* 2 bytes: length of section, then [length] bytes: unknown purpose */ + if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK; + } + + /* filename and extension */ + if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) { + int len; + /* allocate memory for maximum length filename */ + char *fn = (char *) sys->alloc(sys, (size_t) 13); + if (!(hdr->filename = fn)) return MSPACK_ERR_NOMEMORY; + + /* copy filename if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILENAME) { + /* read and copy up to 9 bytes of a null terminated string */ + if ((len = sys->read(fh, &buf[0], 9)) < 2) return MSPACK_ERR_READ; + for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; + /* if string was 9 bytes with no null terminator, reject it */ + if (i == 9 && buf[8] != '\0') return MSPACK_ERR_DATAFORMAT; + /* seek to byte after string ended in file */ + if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) + return MSPACK_ERR_SEEK; + fn--; /* remove the null terminator */ + } + + /* copy extension if present */ + if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) { + *fn++ = '.'; + /* read and copy up to 4 bytes of a null terminated string */ + if ((len = sys->read(fh, &buf[0], 4)) < 2) return MSPACK_ERR_READ; + for (i = 0; i < len; i++) if (!(*fn++ = buf[i])) break; + /* if string was 4 bytes with no null terminator, reject it */ + if (i == 4 && buf[3] != '\0') return MSPACK_ERR_DATAFORMAT; + /* seek to byte after string ended in file */ + if (sys->seek(fh, (off_t)(i + 1 - len), MSPACK_SYS_SEEK_CUR)) + return MSPACK_ERR_SEEK; + fn--; /* remove the null terminator */ + } + *fn = '\0'; + } + + /* 2 bytes: extra text length then [length] bytes of extra text data */ + if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) { + if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ; + i = EndGetI16(&buf[0]); + hdr->extra = (char *) sys->alloc(sys, (size_t)i+1); + if (! hdr->extra) return MSPACK_ERR_NOMEMORY; + if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ; + hdr->extra[i] = '\0'; + hdr->extra_length = i; + } + return MSPACK_ERR_OK; +} + +/*************************************** + * KWAJD_EXTRACT + *************************************** + * decompresses a KWAJ file + */ +static int kwajd_extract(struct mskwaj_decompressor *base, + struct mskwajd_header *hdr, const char *filename) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mspack_system *sys; + struct mspack_file *fh, *outfh; + + if (!self) return MSPACK_ERR_ARGS; + if (!hdr) return self->error = MSPACK_ERR_ARGS; + + sys = self->system; + fh = ((struct mskwajd_header_p *) hdr)->fh; + + /* seek to the compressed data */ + if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) { + return self->error = MSPACK_ERR_SEEK; + } + + /* open file for output */ + if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { + return self->error = MSPACK_ERR_OPEN; + } + + self->error = MSPACK_ERR_OK; + + /* decompress based on format */ + if (hdr->comp_type == MSKWAJ_COMP_NONE || + hdr->comp_type == MSKWAJ_COMP_XOR) + { + /* NONE is a straight copy. XOR is a copy xored with 0xFF */ + unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE); + if (buf) { + int read, i; + while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) { + if (hdr->comp_type == MSKWAJ_COMP_XOR) { + for (i = 0; i < read; i++) buf[i] ^= 0xFF; + } + if (sys->write(outfh, buf, read) != read) { + self->error = MSPACK_ERR_WRITE; + break; + } + } + if (read < 0) self->error = MSPACK_ERR_READ; + sys->free(buf); + } + else { + self->error = MSPACK_ERR_NOMEMORY; + } + } + else if (hdr->comp_type == MSKWAJ_COMP_SZDD) { + self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE, + LZSS_MODE_EXPAND); + } + else if (hdr->comp_type == MSKWAJ_COMP_LZH) { + struct kwajd_stream *lzh = lzh_init(sys, fh, outfh); + self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY; + lzh_free(lzh); + } + else if (hdr->comp_type == MSKWAJ_COMP_MSZIP) { + struct mszipd_stream *zip = mszipd_init(sys,fh,outfh,KWAJ_INPUT_SIZE,0); + self->error = (zip) ? mszipd_decompress_kwaj(zip) : MSPACK_ERR_NOMEMORY; + mszipd_free(zip); + } + else { + self->error = MSPACK_ERR_DATAFORMAT; + } + + /* close output file */ + sys->close(outfh); + + return self->error; +} + +/*************************************** + * KWAJD_DECOMPRESS + *************************************** + * unpacks directly from input to output + */ +static int kwajd_decompress(struct mskwaj_decompressor *base, + const char *input, const char *output) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + struct mskwajd_header *hdr; + int error; + + if (!self) return MSPACK_ERR_ARGS; + + if (!(hdr = kwajd_open(base, input))) return self->error; + error = kwajd_extract(base, hdr, output); + kwajd_close(base, hdr); + return self->error = error; +} + +/*************************************** + * KWAJD_ERROR + *************************************** + * returns the last error that occurred + */ +static int kwajd_error(struct mskwaj_decompressor *base) +{ + struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base; + return (self) ? self->error : MSPACK_ERR_ARGS; +} + +/*************************************** + * LZH_INIT, LZH_DECOMPRESS, LZH_FREE + *************************************** + * unpacks KWAJ method 3 files + */ + +/* import bit-reading macros and code */ +#define BITS_TYPE struct kwajd_stream +#define BITS_VAR lzh +#define BITS_ORDER_MSB +#define BITS_NO_READ_INPUT +#define READ_BYTES do { \ + if (i_ptr >= i_end) { \ + if ((err = lzh_read_input(lzh))) return err; \ + i_ptr = lzh->i_ptr; \ + i_end = lzh->i_end; \ + } \ + INJECT_BITS(*i_ptr++, 8); \ +} while (0) +#include + +/* import huffman-reading macros and code */ +#define TABLEBITS(tbl) KWAJ_TABLEBITS +#define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS +#define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx] +#define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx] +#define HUFF_ERROR return MSPACK_ERR_DATAFORMAT +#include + +/* In the KWAJ LZH format, there is no special 'eof' marker, it just + * ends. Depending on how many bits are left in the final byte when + * the stream ends, that might be enough to start another literal or + * match. The only easy way to detect that we've come to an end is to + * guard all bit-reading. We allow fake bits to be read once we reach + * the end of the stream, but we check if we then consumed any of + * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This + * isn't how the default readbits.h read_input() works (it simply lets + * 2 fake bytes in then stops), so we implement our own. + */ +#define READ_BITS_SAFE(val, n) do { \ + READ_BITS(val, n); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ +} while (0) + +#define READ_HUFFSYM_SAFE(tbl, val) do { \ + READ_HUFFSYM(tbl, val); \ + if (lzh->input_end && bits_left < lzh->input_end) \ + return MSPACK_ERR_OK; \ +} while (0) + +#define BUILD_TREE(tbl, type) \ + STORE_BITS; \ + err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), &HUFF_LEN(tbl,0)); \ + if (err) return err; \ + RESTORE_BITS; \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \ + &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \ + return MSPACK_ERR_DATAFORMAT; + +#define WRITE_BYTE do { \ + if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \ + return MSPACK_ERR_WRITE; \ +} while (0) + +static struct kwajd_stream *lzh_init(struct mspack_system *sys, + struct mspack_file *in, struct mspack_file *out) +{ + struct kwajd_stream *lzh; + + if (!sys || !in || !out) return NULL; + if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL; + + lzh->sys = sys; + lzh->input = in; + lzh->output = out; + return lzh; +} + +static int lzh_decompress(struct kwajd_stream *lzh) +{ + register unsigned int bit_buffer; + register int bits_left, i; + register unsigned short sym; + unsigned char *i_ptr, *i_end, lit_run = 0; + int j, pos = 0, len, offset, err; + unsigned int types[6]; + + /* reset global state */ + INIT_BITS; + RESTORE_BITS; + memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE); + + /* read 6 encoding types (for byte alignment) but only 5 are needed */ + for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4); + + /* read huffman table symbol lengths and build huffman trees */ + BUILD_TREE(MATCHLEN1, types[0]); + BUILD_TREE(MATCHLEN2, types[1]); + BUILD_TREE(LITLEN, types[2]); + BUILD_TREE(OFFSET, types[3]); + BUILD_TREE(LITERAL, types[4]); + + while (!lzh->input_end) { + if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len); + else READ_HUFFSYM_SAFE(MATCHLEN1, len); + + if (len > 0) { + len += 2; + lit_run = 0; /* not the end of a literal run */ + READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6; + READ_BITS_SAFE(j, 6); offset |= j; + + /* copy match as output and into the ring buffer */ + while (len-- > 0) { + lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095]; + WRITE_BYTE; + pos++; pos &= 4095; + } + } + else { + READ_HUFFSYM_SAFE(LITLEN, len); len++; + lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */ + while (len-- > 0) { + READ_HUFFSYM_SAFE(LITERAL, j); + /* copy as output and into the ring buffer */ + lzh->window[pos] = j; + WRITE_BYTE; + pos++; pos &= 4095; + } + } + } + return MSPACK_ERR_OK; +} + +static void lzh_free(struct kwajd_stream *lzh) +{ + struct mspack_system *sys; + if (!lzh || !lzh->sys) return; + sys = lzh->sys; + sys->free(lzh); +} + +static int lzh_read_lens(struct kwajd_stream *lzh, + unsigned int type, unsigned int numsyms, + unsigned char *lens) +{ + register unsigned int bit_buffer; + register int bits_left; + unsigned char *i_ptr, *i_end; + unsigned int i, c, sel; + int err; + + RESTORE_BITS; + switch (type) { + case 0: + i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0; + for (i = 0; i < numsyms; i++) lens[i] = c; + break; + + case 1: + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c; + else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c; + else { READ_BITS_SAFE(c, 4); lens[i] = c; }} + } + break; + + case 2: + READ_BITS_SAFE(c, 4); lens[0] = c; + for (i = 1; i < numsyms; i++) { + READ_BITS_SAFE(sel, 2); + if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1; + lens[i] = c; + } + break; + + case 3: + for (i = 0; i < numsyms; i++) { + READ_BITS_SAFE(c, 4); lens[i] = c; + } + break; + } + STORE_BITS; + return MSPACK_ERR_OK; +} + +static int lzh_read_input(struct kwajd_stream *lzh) { + int read; + if (lzh->input_end) { + lzh->input_end += 8; + lzh->inbuf[0] = 0; + read = 1; + } + else { + read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE); + if (read < 0) return MSPACK_ERR_READ; + if (read == 0) { + lzh->input_end = 8; + lzh->inbuf[0] = 0; + read = 1; + } + } + + /* update i_ptr and i_end */ + lzh->i_ptr = &lzh->inbuf[0]; + lzh->i_end = &lzh->inbuf[read]; + return MSPACK_ERR_OK; +} -- cgit v1.2.3