123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- /*
- 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 <limits.h>
- #include "crc16.h"
- #include "lha_decoder.h"
- // Null decoder, used for -lz4-, -lh0-, -pm0-:
- extern LHADecoderType lha_null_decoder;
- // LArc compression algorithms:
- extern LHADecoderType lha_lz5_decoder;
- extern LHADecoderType lha_lzs_decoder;
- // LHarc compression algorithms:
- extern LHADecoderType lha_lh1_decoder;
- extern LHADecoderType lha_lh4_decoder;
- extern LHADecoderType lha_lh5_decoder;
- extern LHADecoderType lha_lh6_decoder;
- extern LHADecoderType lha_lh7_decoder;
- extern LHADecoderType lha_lhx_decoder;
- // PMarc compression algorithms:
- extern LHADecoderType lha_pm1_decoder;
- extern LHADecoderType lha_pm2_decoder;
- static struct {
- char *name;
- LHADecoderType *dtype;
- } decoders[] = {
- { "-lz4-", &lha_null_decoder },
- { "-lz5-", &lha_lz5_decoder },
- { "-lzs-", &lha_lzs_decoder },
- { "-lh0-", &lha_null_decoder },
- { "-lh1-", &lha_lh1_decoder },
- { "-lh4-", &lha_lh4_decoder },
- { "-lh5-", &lha_lh5_decoder },
- { "-lh6-", &lha_lh6_decoder },
- { "-lh7-", &lha_lh7_decoder },
- { "-lhx-", &lha_lhx_decoder },
- { "-pm0-", &lha_null_decoder },
- { "-pm1-", &lha_pm1_decoder },
- { "-pm2-", &lha_pm2_decoder },
- };
- LHADecoder *lha_decoder_new(LHADecoderType *dtype,
- LHADecoderCallback callback,
- void *callback_data,
- size_t stream_length)
- {
- LHADecoder *decoder;
- void *extra_data;
- // Space is allocated together: the LHADecoder structure,
- // then the private data area used by the algorithm,
- // followed by the output buffer,
- decoder = calloc(1, sizeof(LHADecoder) + dtype->extra_size
- + dtype->max_read);
- if (decoder == NULL) {
- return NULL;
- }
- decoder->dtype = dtype;
- decoder->progress_callback = NULL;
- decoder->last_block = UINT_MAX;
- decoder->outbuf_pos = 0;
- decoder->outbuf_len = 0;
- decoder->stream_pos = 0;
- decoder->stream_length = stream_length;
- decoder->decoder_failed = 0;
- decoder->crc = 0;
- // Private data area follows the structure.
- extra_data = decoder + 1;
- decoder->outbuf = ((uint8_t *) extra_data) + dtype->extra_size;
- if (dtype->init != NULL
- && !dtype->init(extra_data, callback, callback_data)) {
- free(decoder);
- return NULL;
- }
- return decoder;
- }
- LHADecoderType *lha_decoder_for_name(char *name)
- {
- unsigned int i;
- for (i = 0; i < sizeof(decoders) / sizeof(*decoders); ++i) {
- if (!strcmp(name, decoders[i].name)) {
- return decoders[i].dtype;
- }
- }
- // Unknown?
- return NULL;
- }
- void lha_decoder_free(LHADecoder *decoder)
- {
- if (decoder->dtype->free != NULL) {
- decoder->dtype->free(decoder + 1);
- }
- free(decoder);
- }
- // Check if the stream has progressed far enough that the progress callback
- // should be invoked again.
- static void check_progress_callback(LHADecoder *decoder)
- {
- unsigned int block;
- block = (decoder->stream_pos + decoder->dtype->block_size - 1)
- / decoder->dtype->block_size;
- // If the stream has advanced by another block, invoke the callback
- // function. Invoke it multiple times if it has advanced by
- // more than one block.
- while (decoder->last_block != block) {
- ++decoder->last_block;
- decoder->progress_callback(decoder->last_block,
- decoder->total_blocks,
- decoder->progress_callback_data);
- }
- }
- void lha_decoder_monitor(LHADecoder *decoder,
- LHADecoderProgressCallback callback,
- void *callback_data)
- {
- decoder->progress_callback = callback;
- decoder->progress_callback_data = callback_data;
- decoder->total_blocks
- = (decoder->stream_length + decoder->dtype->block_size - 1)
- / decoder->dtype->block_size;
- check_progress_callback(decoder);
- }
- size_t lha_decoder_read(LHADecoder *decoder, uint8_t *buf, size_t buf_len)
- {
- size_t filled, bytes;
- // When we reach the end of the stream, we must truncate the
- // decompressed data at exactly the right point (stream_length),
- // or we may read a few extra false byte(s) by mistake.
- // Reduce buf_len when we get to the end to limit it to the
- // real number of remaining characters.
- if (decoder->stream_pos + buf_len > decoder->stream_length) {
- buf_len = decoder->stream_length - decoder->stream_pos;
- }
- // Try to fill up the buffer that has been passed with as much
- // data as possible. Each call to read() will fill up outbuf
- // with some data; this is then copied into buf, with some
- // data left at the end for the next call.
- filled = 0;
- while (filled < buf_len) {
- // Try to empty out some of the output buffer first.
- bytes = decoder->outbuf_len - decoder->outbuf_pos;
- if (buf_len - filled < bytes) {
- bytes = buf_len - filled;
- }
- memcpy(buf + filled, decoder->outbuf + decoder->outbuf_pos,
- bytes);
- decoder->outbuf_pos += bytes;
- filled += bytes;
- // If we previously encountered a failure reading from
- // the decoder, don't try to call the read function again.
- if (decoder->decoder_failed) {
- break;
- }
- // If outbuf is now empty, we can process another run to
- // re-fill it.
- if (decoder->outbuf_pos >= decoder->outbuf_len) {
- decoder->outbuf_len
- = decoder->dtype->read(decoder + 1,
- decoder->outbuf);
- decoder->outbuf_pos = 0;
- }
- // No more data to be read?
- if (decoder->outbuf_len == 0) {
- decoder->decoder_failed = 1;
- break;
- }
- }
- // Update CRC.
- lha_crc16_buf(&decoder->crc, buf, filled);
- // Track stream position.
- decoder->stream_pos += filled;
- // Check progress callback, if one is set:
- if (decoder->progress_callback != NULL) {
- check_progress_callback(decoder);
- }
- return filled;
- }
- uint16_t lha_decoder_get_crc(LHADecoder *decoder)
- {
- return decoder->crc;
- }
- size_t lha_decoder_get_length(LHADecoder *decoder)
- {
- return decoder->stream_pos;
- }
|