/////////////////////////////////////////////////////////////////////////////// // /// \file auto_decoder.c /// \brief Autodetect between .lzma Stream and LZMA_Alone formats // // Copyright (C) 2007 Lasse Collin // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // /////////////////////////////////////////////////////////////////////////////// #include "stream_decoder.h" #include "alone_decoder.h" struct lzma_coder_s { /// Stream decoder or LZMA_Alone decoder lzma_next_coder next; uint64_t memlimit; uint32_t flags; enum { SEQ_INIT, SEQ_CODE, SEQ_FINISH, } sequence; }; static lzma_ret auto_decode(lzma_coder *coder, lzma_allocator *allocator, const uint8_t *restrict in, size_t *restrict in_pos, size_t in_size, uint8_t *restrict out, size_t *restrict out_pos, size_t out_size, lzma_action action) { switch (coder->sequence) { case SEQ_INIT: if (*in_pos >= in_size) return LZMA_OK; // Update the sequence now, because we want to continue from // SEQ_CODE even if we return some LZMA_*_CHECK. coder->sequence = SEQ_CODE; // Detect the file format. For now this is simple, since if // it doesn't start with 0xFD (the first magic byte of the // new format), it has to be LZMA_Alone, or something that // we don't support at all. if (in[*in_pos] == 0xFD) { return_if_error(lzma_stream_decoder_init( &coder->next, allocator, coder->memlimit, coder->flags)); } else { return_if_error(lzma_alone_decoder_init(&coder->next, allocator, coder->memlimit)); // If the application wants to know about missing // integrity check or about the check in general, we // need to handle it here, because LZMA_Alone decoder // doesn't accept any flags. if (coder->flags & LZMA_TELL_NO_CHECK) return LZMA_NO_CHECK; if (coder->flags & LZMA_TELL_ANY_CHECK) return LZMA_GET_CHECK; } // Fall through case SEQ_CODE: { const lzma_ret ret = coder->next.code( coder->next.coder, allocator, in, in_pos, in_size, out, out_pos, out_size, action); if (ret != LZMA_STREAM_END || (coder->flags & LZMA_CONCATENATED) == 0) return ret; coder->sequence = SEQ_FINISH; } // Fall through case SEQ_FINISH: // When LZMA_DECODE_CONCATENATED was used and we were decoding // LZMA_Alone file, we need to check check that there is no // trailing garbage and wait for LZMA_FINISH. if (*in_pos < in_size) return LZMA_DATA_ERROR; return action == LZMA_FINISH ? LZMA_STREAM_END : LZMA_OK; default: assert(0); return LZMA_PROG_ERROR; } } static void auto_decoder_end(lzma_coder *coder, lzma_allocator *allocator) { lzma_next_end(&coder->next, allocator); lzma_free(coder, allocator); return; } static lzma_check auto_decoder_get_check(const lzma_coder *coder) { // It is LZMA_Alone if get_check is NULL. return coder->next.get_check == NULL ? LZMA_CHECK_NONE : coder->next.get_check(coder->next.coder); } static lzma_ret auto_decoder_init(lzma_next_coder *next, lzma_allocator *allocator, uint64_t memlimit, uint32_t flags) { lzma_next_coder_init(auto_decoder_init, next, allocator); if (flags & ~LZMA_SUPPORTED_FLAGS) return LZMA_OPTIONS_ERROR; if (next->coder == NULL) { next->coder = lzma_alloc(sizeof(lzma_coder), allocator); if (next->coder == NULL) return LZMA_MEM_ERROR; next->code = &auto_decode; next->end = &auto_decoder_end; next->get_check = &auto_decoder_get_check; next->coder->next = LZMA_NEXT_CODER_INIT; } next->coder->memlimit = memlimit; next->coder->flags = flags; next->coder->sequence = SEQ_INIT; return LZMA_OK; } extern LZMA_API lzma_ret lzma_auto_decoder(lzma_stream *strm, uint64_t memlimit, uint32_t flags) { lzma_next_strm_init(auto_decoder_init, strm, memlimit, flags); strm->internal->supported_actions[LZMA_RUN] = true; strm->internal->supported_actions[LZMA_FINISH] = true; return LZMA_OK; }