123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- /*
- Copyright (c) 2011, 2012, Simon Howard
- Permission to use, copy, modify, and/or distribute this software
- for any purpose with or without fee is hereby granted, provided
- that the above copyright notice and this permission notice appear
- in all copies.
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
- CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
- NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
- #include <stdlib.h>
- #include <string.h>
- #include <inttypes.h>
- #include "lha_decoder.h"
- // Parameters for ring buffer, used for storing history. This acts
- // as the dictionary for copy operations.
- #define RING_BUFFER_SIZE 4096
- #define START_OFFSET 18
- // Threshold offset. In the copy operation, the copy length is a 4-bit
- // value, giving a range 0..15. The threshold offsets this so that it
- // is interpreted as 3..18 - a more useful range.
- #define THRESHOLD 3
- // Size of output buffer. Must be large enough to hold the results of
- // a complete "run" (see below).
- #define OUTPUT_BUFFER_SIZE (15 + THRESHOLD) * 8
- // Decoder for the -lz5- compression method used by LArc.
- //
- // This processes "runs" of eight commands, each of which is either
- // "output a character" or "copy block". The result of that run
- // is written into the output buffer.
- typedef struct {
- uint8_t ringbuf[RING_BUFFER_SIZE];
- unsigned int ringbuf_pos;
- LHADecoderCallback callback;
- void *callback_data;
- } LHALZ5Decoder;
- static void fill_initial(LHALZ5Decoder *decoder)
- {
- unsigned int i, j;
- uint8_t *p;
- p = decoder->ringbuf;
- // For each byte value, the history buffer includes a run of 13
- // bytes all with that value. This is useful eg. for text files
- // that include a long run like this (eg. ===========).
- for (i = 0; i < 256; ++i) {
- for (j = 0; j < 13; ++j) {
- *p++ = (uint8_t) i;
- }
- }
- // Next we include all byte values ascending and descending.
- for (i = 0; i < 256; ++i) {
- *p++ = (uint8_t) i;
- }
- for (i = 0; i < 256; ++i) {
- *p++ = (uint8_t) (255 - i);
- }
- // Block of zeros, and then ASCII space characters. I think these are
- // towards the end of the range because they're most likely to be
- // useful and therefore last to get overwritten?
- for (i = 0; i < 128; ++i) {
- *p++ = 0;
- }
- for (i = 0; i < 110; ++i) {
- *p++ = ' ';
- }
- // Final 18 characters are all zeros, probably because of START_OFFSET.
- for (i = 0; i < 18; ++i) {
- *p++ = 0;
- }
- }
- static int lha_lz5_init(void *data, LHADecoderCallback callback,
- void *callback_data)
- {
- LHALZ5Decoder *decoder = data;
- fill_initial(decoder);
- decoder->ringbuf_pos = RING_BUFFER_SIZE - START_OFFSET;
- decoder->callback = callback;
- decoder->callback_data = callback_data;
- return 1;
- }
- // Add a single byte to the output buffer.
- static void output_byte(LHALZ5Decoder *decoder, uint8_t *buf,
- size_t *buf_len, uint8_t b)
- {
- buf[*buf_len] = b;
- ++*buf_len;
- decoder->ringbuf[decoder->ringbuf_pos] = b;
- decoder->ringbuf_pos = (decoder->ringbuf_pos + 1) % RING_BUFFER_SIZE;
- }
- // Output a "block" of data from the specified range in the ring buffer.
- static void output_block(LHALZ5Decoder *decoder,
- uint8_t *buf,
- size_t *buf_len,
- unsigned int start,
- unsigned int len)
- {
- unsigned int i;
- for (i = 0; i < len; ++i) {
- output_byte(decoder, buf, buf_len,
- decoder->ringbuf[(start + i) % RING_BUFFER_SIZE]);
- }
- }
- // Process a "run" of LZ5-compressed data (a control byte followed by
- // eight "commands").
- static size_t lha_lz5_read(void *data, uint8_t *buf)
- {
- LHALZ5Decoder *decoder = data;
- uint8_t bitmap;
- unsigned int bit;
- size_t result;
- // Start from an empty buffer.
- result = 0;
- // Read the bitmap byte first.
- if (!decoder->callback(&bitmap, 1, decoder->callback_data)) {
- return 0;
- }
- // Each bit in the bitmap is a command.
- // If the bit is set, it is an "output byte" command.
- // If it is not set, it is a "copy block" command.
- for (bit = 0; bit < 8; ++bit) {
- if ((bitmap & (1 << bit)) != 0) {
- uint8_t b;
- if (!decoder->callback(&b, 1, decoder->callback_data)) {
- break;
- }
- output_byte(decoder, buf, &result, b);
- } else {
- uint8_t cmd[2];
- unsigned int seqstart, seqlen;
- if (!decoder->callback(cmd, 2, decoder->callback_data)) {
- break;
- }
- seqstart = (((unsigned int) cmd[1] & 0xf0) << 4)
- | cmd[0];
- seqlen = ((unsigned int) cmd[1] & 0x0f) + THRESHOLD;
- output_block(decoder, buf, &result, seqstart, seqlen);
- }
- }
- return result;
- }
- LHADecoderType lha_lz5_decoder = {
- lha_lz5_init,
- NULL,
- lha_lz5_read,
- sizeof(LHALZ5Decoder),
- OUTPUT_BUFFER_SIZE,
- RING_BUFFER_SIZE
- };
|