summaryrefslogtreecommitdiff
path: root/rbutil/rbutilqt/mspack/kwajd.c
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/mspack/kwajd.c')
-rw-r--r--rbutil/rbutilqt/mspack/kwajd.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/mspack/kwajd.c b/rbutil/rbutilqt/mspack/kwajd.c
new file mode 100644
index 0000000000..d891b6a7e3
--- /dev/null
+++ b/rbutil/rbutilqt/mspack/kwajd.c
@@ -0,0 +1,555 @@
1/* This file is part of libmspack.
2 * (C) 2003-2010 Stuart Caie.
3 *
4 * KWAJ is a format very similar to SZDD. KWAJ method 3 (LZH) was
5 * written by Jeff Johnson.
6 *
7 * libmspack is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Lesser General Public License (LGPL) version 2.1
9 *
10 * For further details, see the file COPYING.LIB distributed with libmspack
11 */
12
13/* KWAJ decompression implementation */
14
15#include <system.h>
16#include <kwaj.h>
17
18/* prototypes */
19static struct mskwajd_header *kwajd_open(
20 struct mskwaj_decompressor *base, const char *filename);
21static void kwajd_close(
22 struct mskwaj_decompressor *base, struct mskwajd_header *hdr);
23static int kwajd_read_headers(
24 struct mspack_system *sys, struct mspack_file *fh,
25 struct mskwajd_header *hdr);
26static int kwajd_extract(
27 struct mskwaj_decompressor *base, struct mskwajd_header *hdr,
28 const char *filename);
29static int kwajd_decompress(
30 struct mskwaj_decompressor *base, const char *input, const char *output);
31static int kwajd_error(
32 struct mskwaj_decompressor *base);
33
34static struct kwajd_stream *lzh_init(
35 struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out);
36static int lzh_decompress(
37 struct kwajd_stream *kwaj);
38static void lzh_free(
39 struct kwajd_stream *kwaj);
40static int lzh_read_lens(
41 struct kwajd_stream *kwaj,
42 unsigned int type, unsigned int numsyms,
43 unsigned char *lens, unsigned short *table);
44static int lzh_read_input(
45 struct kwajd_stream *kwaj);
46
47
48/***************************************
49 * MSPACK_CREATE_KWAJ_DECOMPRESSOR
50 ***************************************
51 * constructor
52 */
53struct mskwaj_decompressor *
54 mspack_create_kwaj_decompressor(struct mspack_system *sys)
55{
56 struct mskwaj_decompressor_p *self = NULL;
57
58 if (!sys) sys = mspack_default_system;
59 if (!mspack_valid_system(sys)) return NULL;
60
61 if ((self = (struct mskwaj_decompressor_p *) sys->alloc(sys, sizeof(struct mskwaj_decompressor_p)))) {
62 self->base.open = &kwajd_open;
63 self->base.close = &kwajd_close;
64 self->base.extract = &kwajd_extract;
65 self->base.decompress = &kwajd_decompress;
66 self->base.last_error = &kwajd_error;
67 self->system = sys;
68 self->error = MSPACK_ERR_OK;
69 }
70 return (struct mskwaj_decompressor *) self;
71}
72
73/***************************************
74 * MSPACK_DESTROY_KWAJ_DECOMPRESSOR
75 ***************************************
76 * destructor
77 */
78void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *base)
79{
80 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
81 if (self) {
82 struct mspack_system *sys = self->system;
83 sys->free(self);
84 }
85}
86
87/***************************************
88 * KWAJD_OPEN
89 ***************************************
90 * opens a KWAJ file without decompressing, reads header
91 */
92static struct mskwajd_header *kwajd_open(struct mskwaj_decompressor *base,
93 const char *filename)
94{
95 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
96 struct mskwajd_header *hdr;
97 struct mspack_system *sys;
98 struct mspack_file *fh;
99
100 if (!self) return NULL;
101 sys = self->system;
102
103 fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ);
104 hdr = (struct mskwajd_header *) sys->alloc(sys, sizeof(struct mskwajd_header_p));
105 if (fh && hdr) {
106 ((struct mskwajd_header_p *) hdr)->fh = fh;
107 self->error = kwajd_read_headers(sys, fh, hdr);
108 }
109 else {
110 if (!fh) self->error = MSPACK_ERR_OPEN;
111 if (!hdr) self->error = MSPACK_ERR_NOMEMORY;
112 }
113
114 if (self->error) {
115 if (fh) sys->close(fh);
116 if (hdr) sys->free(hdr);
117 hdr = NULL;
118 }
119
120 return hdr;
121}
122
123/***************************************
124 * KWAJD_CLOSE
125 ***************************************
126 * closes a KWAJ file
127 */
128static void kwajd_close(struct mskwaj_decompressor *base,
129 struct mskwajd_header *hdr)
130{
131 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
132 struct mskwajd_header_p *hdr_p = (struct mskwajd_header_p *) hdr;
133
134 if (!self || !self->system) return;
135
136 /* close the file handle associated */
137 self->system->close(hdr_p->fh);
138
139 /* free the memory associated */
140 self->system->free(hdr);
141
142 self->error = MSPACK_ERR_OK;
143}
144
145/***************************************
146 * KWAJD_READ_HEADERS
147 ***************************************
148 * reads the headers of a KWAJ format file
149 */
150static int kwajd_read_headers(struct mspack_system *sys,
151 struct mspack_file *fh,
152 struct mskwajd_header *hdr)
153{
154 unsigned char buf[16];
155 int i;
156
157 /* read in the header */
158 if (sys->read(fh, &buf[0], kwajh_SIZEOF) != kwajh_SIZEOF) {
159 return MSPACK_ERR_READ;
160 }
161
162 /* check for "KWAJ" signature */
163 if (((unsigned int) EndGetI32(&buf[kwajh_Signature1]) != 0x4A41574B) ||
164 ((unsigned int) EndGetI32(&buf[kwajh_Signature2]) != 0xD127F088))
165 {
166 return MSPACK_ERR_SIGNATURE;
167 }
168
169 /* basic header fields */
170 hdr->comp_type = EndGetI16(&buf[kwajh_CompMethod]);
171 hdr->data_offset = EndGetI16(&buf[kwajh_DataOffset]);
172 hdr->headers = EndGetI16(&buf[kwajh_Flags]);
173 hdr->length = 0;
174 hdr->filename = NULL;
175 hdr->extra = NULL;
176 hdr->extra_length = 0;
177
178 /* optional headers */
179
180 /* 4 bytes: length of unpacked file */
181 if (hdr->headers & MSKWAJ_HDR_HASLENGTH) {
182 if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ;
183 hdr->length = EndGetI32(&buf[0]);
184 }
185
186 /* 2 bytes: unknown purpose */
187 if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN1) {
188 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
189 }
190
191 /* 2 bytes: length of section, then [length] bytes: unknown purpose */
192 if (hdr->headers & MSKWAJ_HDR_HASUNKNOWN2) {
193 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
194 i = EndGetI16(&buf[0]);
195 if (sys->seek(fh, (off_t)i, MSPACK_SYS_SEEK_CUR)) return MSPACK_ERR_SEEK;
196 }
197
198 /* filename and extension */
199 if (hdr->headers & (MSKWAJ_HDR_HASFILENAME | MSKWAJ_HDR_HASFILEEXT)) {
200 off_t pos = sys->tell(fh);
201 char *fn = (char *) sys->alloc(sys, (size_t) 13);
202
203 /* allocate memory for maximum length filename */
204 if (! fn) return MSPACK_ERR_NOMEMORY;
205 hdr->filename = fn;
206
207 /* copy filename if present */
208 if (hdr->headers & MSKWAJ_HDR_HASFILENAME) {
209 if (sys->read(fh, &buf[0], 9) != 9) return MSPACK_ERR_READ;
210 for (i = 0; i < 9; i++, fn++) if (!(*fn = buf[i])) break;
211 pos += (i < 9) ? i+1 : 9;
212 if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START))
213 return MSPACK_ERR_SEEK;
214 }
215
216 /* copy extension if present */
217 if (hdr->headers & MSKWAJ_HDR_HASFILEEXT) {
218 *fn++ = '.';
219 if (sys->read(fh, &buf[0], 4) != 4) return MSPACK_ERR_READ;
220 for (i = 0; i < 4; i++, fn++) if (!(*fn = buf[i])) break;
221 pos += (i < 4) ? i+1 : 4;
222 if (sys->seek(fh, pos, MSPACK_SYS_SEEK_START))
223 return MSPACK_ERR_SEEK;
224 }
225 *fn = '\0';
226 }
227
228 /* 2 bytes: extra text length then [length] bytes of extra text data */
229 if (hdr->headers & MSKWAJ_HDR_HASEXTRATEXT) {
230 if (sys->read(fh, &buf[0], 2) != 2) return MSPACK_ERR_READ;
231 i = EndGetI16(&buf[0]);
232 hdr->extra = (char *) sys->alloc(sys, (size_t)i+1);
233 if (! hdr->extra) return MSPACK_ERR_NOMEMORY;
234 if (sys->read(fh, hdr->extra, i) != i) return MSPACK_ERR_READ;
235 hdr->extra[i] = '\0';
236 hdr->extra_length = i;
237 }
238 return MSPACK_ERR_OK;
239}
240
241/***************************************
242 * KWAJD_EXTRACT
243 ***************************************
244 * decompresses a KWAJ file
245 */
246static int kwajd_extract(struct mskwaj_decompressor *base,
247 struct mskwajd_header *hdr, const char *filename)
248{
249 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
250 struct mspack_system *sys;
251 struct mspack_file *fh, *outfh;
252
253 if (!self) return MSPACK_ERR_ARGS;
254 if (!hdr) return self->error = MSPACK_ERR_ARGS;
255
256 sys = self->system;
257 fh = ((struct mskwajd_header_p *) hdr)->fh;
258
259 /* seek to the compressed data */
260 if (sys->seek(fh, hdr->data_offset, MSPACK_SYS_SEEK_START)) {
261 return self->error = MSPACK_ERR_SEEK;
262 }
263
264 /* open file for output */
265 if (!(outfh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) {
266 return self->error = MSPACK_ERR_OPEN;
267 }
268
269 self->error = MSPACK_ERR_OK;
270
271 /* decompress based on format */
272 if (hdr->comp_type == MSKWAJ_COMP_NONE ||
273 hdr->comp_type == MSKWAJ_COMP_XOR)
274 {
275 /* NONE is a straight copy. XOR is a copy xored with 0xFF */
276 unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) KWAJ_INPUT_SIZE);
277 if (buf) {
278 int read, i;
279 while ((read = sys->read(fh, buf, KWAJ_INPUT_SIZE)) > 0) {
280 if (hdr->comp_type == MSKWAJ_COMP_XOR) {
281 for (i = 0; i < read; i++) buf[i] ^= 0xFF;
282 }
283 if (sys->write(outfh, buf, read) != read) {
284 self->error = MSPACK_ERR_WRITE;
285 break;
286 }
287 }
288 if (read < 0) self->error = MSPACK_ERR_READ;
289 sys->free(buf);
290 }
291 else {
292 self->error = MSPACK_ERR_NOMEMORY;
293 }
294 }
295 else if (hdr->comp_type == MSKWAJ_COMP_SZDD) {
296 self->error = lzss_decompress(sys, fh, outfh, KWAJ_INPUT_SIZE,
297 LZSS_MODE_EXPAND);
298 }
299 else if (hdr->comp_type == MSKWAJ_COMP_LZH) {
300 struct kwajd_stream *lzh = lzh_init(sys, fh, outfh);
301 self->error = (lzh) ? lzh_decompress(lzh) : MSPACK_ERR_NOMEMORY;
302 lzh_free(lzh);
303 }
304 else {
305 self->error = MSPACK_ERR_DATAFORMAT;
306 }
307
308 /* close output file */
309 sys->close(outfh);
310
311 return self->error;
312}
313
314/***************************************
315 * KWAJD_DECOMPRESS
316 ***************************************
317 * unpacks directly from input to output
318 */
319static int kwajd_decompress(struct mskwaj_decompressor *base,
320 const char *input, const char *output)
321{
322 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
323 struct mskwajd_header *hdr;
324 int error;
325
326 if (!self) return MSPACK_ERR_ARGS;
327
328 if (!(hdr = kwajd_open(base, input))) return self->error;
329 error = kwajd_extract(base, hdr, output);
330 kwajd_close(base, hdr);
331 return self->error = error;
332}
333
334/***************************************
335 * KWAJD_ERROR
336 ***************************************
337 * returns the last error that occurred
338 */
339static int kwajd_error(struct mskwaj_decompressor *base)
340{
341 struct mskwaj_decompressor_p *self = (struct mskwaj_decompressor_p *) base;
342 return (self) ? self->error : MSPACK_ERR_ARGS;
343}
344
345/***************************************
346 * LZH_INIT, LZH_DECOMPRESS, LZH_FREE
347 ***************************************
348 * unpacks KWAJ method 3 files
349 */
350
351/* import bit-reading macros and code */
352#define BITS_TYPE struct kwajd_stream
353#define BITS_VAR lzh
354#define BITS_ORDER_MSB
355#define BITS_NO_READ_INPUT
356#define READ_BYTES do { \
357 if (i_ptr >= i_end) { \
358 if ((err = lzh_read_input(lzh))) return err; \
359 i_ptr = lzh->i_ptr; \
360 i_end = lzh->i_end; \
361 } \
362 INJECT_BITS(*i_ptr++, 8); \
363} while (0)
364#include <readbits.h>
365
366/* import huffman-reading macros and code */
367#define TABLEBITS(tbl) KWAJ_TABLEBITS
368#define MAXSYMBOLS(tbl) KWAJ_##tbl##_SYMS
369#define HUFF_TABLE(tbl,idx) lzh->tbl##_table[idx]
370#define HUFF_LEN(tbl,idx) lzh->tbl##_len[idx]
371#define HUFF_ERROR return MSPACK_ERR_DATAFORMAT
372#include <readhuff.h>
373
374/* In the KWAJ LZH format, there is no special 'eof' marker, it just
375 * ends. Depending on how many bits are left in the final byte when
376 * the stream ends, that might be enough to start another literal or
377 * match. The only easy way to detect that we've come to an end is to
378 * guard all bit-reading. We allow fake bits to be read once we reach
379 * the end of the stream, but we check if we then consumed any of
380 * those fake bits, after doing the READ_BITS / READ_HUFFSYM. This
381 * isn't how the default readbits.h read_input() works (it simply lets
382 * 2 fake bytes in then stops), so we implement our own.
383 */
384#define READ_BITS_SAFE(val, n) do { \
385 READ_BITS(val, n); \
386 if (lzh->input_end && bits_left < lzh->input_end) \
387 return MSPACK_ERR_OK; \
388} while (0)
389
390#define READ_HUFFSYM_SAFE(tbl, val) do { \
391 READ_HUFFSYM(tbl, val); \
392 if (lzh->input_end && bits_left < lzh->input_end) \
393 return MSPACK_ERR_OK; \
394} while (0)
395
396#define BUILD_TREE(tbl, type) \
397 STORE_BITS; \
398 err = lzh_read_lens(lzh, type, MAXSYMBOLS(tbl), \
399 &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0)); \
400 if (err) return err; \
401 RESTORE_BITS; \
402 if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), \
403 &HUFF_LEN(tbl,0), &HUFF_TABLE(tbl,0))) \
404 return MSPACK_ERR_DATAFORMAT;
405
406#define WRITE_BYTE do { \
407 if (lzh->sys->write(lzh->output, &lzh->window[pos], 1) != 1) \
408 return MSPACK_ERR_WRITE; \
409} while (0)
410
411static struct kwajd_stream *lzh_init(struct mspack_system *sys,
412 struct mspack_file *in, struct mspack_file *out)
413{
414 struct kwajd_stream *lzh;
415
416 if (!sys || !in || !out) return NULL;
417 if (!(lzh = (struct kwajd_stream *) sys->alloc(sys, sizeof(struct kwajd_stream)))) return NULL;
418
419 lzh->sys = sys;
420 lzh->input = in;
421 lzh->output = out;
422 return lzh;
423}
424
425static int lzh_decompress(struct kwajd_stream *lzh)
426{
427 register unsigned int bit_buffer;
428 register int bits_left, i;
429 register unsigned short sym;
430 unsigned char *i_ptr, *i_end, lit_run = 0;
431 int j, pos = 0, len, offset, err;
432 unsigned int types[6];
433
434 /* reset global state */
435 INIT_BITS;
436 RESTORE_BITS;
437 memset(&lzh->window[0], LZSS_WINDOW_FILL, (size_t) LZSS_WINDOW_SIZE);
438
439 /* read 6 encoding types (for byte alignment) but only 5 are needed */
440 for (i = 0; i < 6; i++) READ_BITS_SAFE(types[i], 4);
441
442 /* read huffman table symbol lengths and build huffman trees */
443 BUILD_TREE(MATCHLEN1, types[0]);
444 BUILD_TREE(MATCHLEN2, types[1]);
445 BUILD_TREE(LITLEN, types[2]);
446 BUILD_TREE(OFFSET, types[3]);
447 BUILD_TREE(LITERAL, types[4]);
448
449 while (!lzh->input_end) {
450 if (lit_run) READ_HUFFSYM_SAFE(MATCHLEN2, len);
451 else READ_HUFFSYM_SAFE(MATCHLEN1, len);
452
453 if (len > 0) {
454 len += 2;
455 lit_run = 0; /* not the end of a literal run */
456 READ_HUFFSYM_SAFE(OFFSET, j); offset = j << 6;
457 READ_BITS_SAFE(j, 6); offset |= j;
458
459 /* copy match as output and into the ring buffer */
460 while (len-- > 0) {
461 lzh->window[pos] = lzh->window[(pos+4096-offset) & 4095];
462 WRITE_BYTE;
463 pos++; pos &= 4095;
464 }
465 }
466 else {
467 READ_HUFFSYM_SAFE(LITLEN, len); len++;
468 lit_run = (len == 32) ? 0 : 1; /* end of a literal run? */
469 while (len-- > 0) {
470 READ_HUFFSYM_SAFE(LITERAL, j);
471 /* copy as output and into the ring buffer */
472 lzh->window[pos] = j;
473 WRITE_BYTE;
474 pos++; pos &= 4095;
475 }
476 }
477 }
478 return MSPACK_ERR_OK;
479}
480
481static void lzh_free(struct kwajd_stream *lzh)
482{
483 struct mspack_system *sys;
484 if (!lzh || !lzh->sys) return;
485 sys = lzh->sys;
486 sys->free(lzh);
487}
488
489static int lzh_read_lens(struct kwajd_stream *lzh,
490 unsigned int type, unsigned int numsyms,
491 unsigned char *lens, unsigned short *table)
492{
493 register unsigned int bit_buffer;
494 register int bits_left;
495 unsigned char *i_ptr, *i_end;
496 unsigned int i, c, sel;
497 int err;
498
499 RESTORE_BITS;
500 switch (type) {
501 case 0:
502 i = numsyms; c = (i==16)?4: (i==32)?5: (i==64)?6: (i==256)?8 :0;
503 for (i = 0; i < numsyms; i++) lens[i] = c;
504 break;
505
506 case 1:
507 READ_BITS_SAFE(c, 4); lens[0] = c;
508 for (i = 1; i < numsyms; i++) {
509 READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = c;
510 else { READ_BITS_SAFE(sel, 1); if (sel == 0) lens[i] = ++c;
511 else { READ_BITS_SAFE(c, 4); lens[i] = c; }}
512 }
513 break;
514
515 case 2:
516 READ_BITS_SAFE(c, 4); lens[0] = c;
517 for (i = 1; i < numsyms; i++) {
518 READ_BITS_SAFE(sel, 2);
519 if (sel == 3) READ_BITS_SAFE(c, 4); else c += (char) sel-1;
520 lens[i] = c;
521 }
522 break;
523
524 case 3:
525 for (i = 0; i < numsyms; i++) {
526 READ_BITS_SAFE(c, 4); lens[i] = c;
527 }
528 break;
529 }
530 STORE_BITS;
531 return MSPACK_ERR_OK;
532}
533
534static int lzh_read_input(struct kwajd_stream *lzh) {
535 int read;
536 if (lzh->input_end) {
537 lzh->input_end += 8;
538 lzh->inbuf[0] = 0;
539 read = 1;
540 }
541 else {
542 read = lzh->sys->read(lzh->input, &lzh->inbuf[0], KWAJ_INPUT_SIZE);
543 if (read < 0) return MSPACK_ERR_READ;
544 if (read == 0) {
545 lzh->input_end = 8;
546 lzh->inbuf[0] = 0;
547 read = 1;
548 }
549 }
550
551 /* update i_ptr and i_end */
552 lzh->i_ptr = &lzh->inbuf[0];
553 lzh->i_end = &lzh->inbuf[read];
554 return MSPACK_ERR_OK;
555}