diff options
Diffstat (limited to 'src/lib/util/chdcodec.c')
-rw-r--r-- | src/lib/util/chdcodec.c | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/src/lib/util/chdcodec.c b/src/lib/util/chdcodec.c new file mode 100644 index 00000000000..b0649cbd29b --- /dev/null +++ b/src/lib/util/chdcodec.c @@ -0,0 +1,1325 @@ +/*************************************************************************** + + chdcodec.c + + Codecs used by the CHD format + +**************************************************************************** + + Copyright Aaron Giles + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name 'MAME' nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + +***************************************************************************/ + +#include "chd.h" +#include "hashing.h" +#include "avhuff.h" +#include "flac.h" +#include <zlib.h> +#include "lib7z/lzmaenc.h" +#include "lib7z/lzmadec.h" +#include <new> + +// function that should exist but doesn't in the official release +extern "C" SRes LzmaDec_Allocate_MAME(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc); + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> chd_zlib_allocator + +// allocation helper clas for zlib +class chd_zlib_allocator +{ +public: + // construction/destruction + chd_zlib_allocator(); + ~chd_zlib_allocator(); + + // installation + void install(z_stream &stream); + +private: + // internal helpers + static voidpf fast_alloc(voidpf opaque, uInt items, uInt size); + static void fast_free(voidpf opaque, voidpf address); + + static const int MAX_ZLIB_ALLOCS = 64; + UINT32 * m_allocptr[MAX_ZLIB_ALLOCS]; +}; + + +// ======================> chd_zlib_compressor + +// ZLIB compressor +class chd_zlib_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_zlib_compressor(chd_file &chd, bool lossy); + ~chd_zlib_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + z_stream m_deflater; + chd_zlib_allocator m_allocator; +}; + + +// ======================> chd_zlib_decompressor + +// ZLIB decompressor +class chd_zlib_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_zlib_decompressor(chd_file &chd, bool lossy); + ~chd_zlib_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + z_stream m_inflater; + chd_zlib_allocator m_allocator; +}; + + +// ======================> chd_lzma_allocator + +// allocation helper clas for zlib +class chd_lzma_allocator : public ISzAlloc +{ +public: + // construction/destruction + chd_lzma_allocator(); + ~chd_lzma_allocator(); + +private: + // internal helpers + static void *fast_alloc(void *p, size_t size); + static void fast_free(void *p, void *address); + + static const int MAX_LZMA_ALLOCS = 64; + UINT32 * m_allocptr[MAX_LZMA_ALLOCS]; +}; + + +// ======================> chd_lzma_compressor + +// LZMA compressor +class chd_lzma_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_lzma_compressor(chd_file &chd, bool lossy); + ~chd_lzma_compressor(); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + + // helpers + static void configure_properties(CLzmaEncProps &props, chd_file &chd); + +private: + // internal state + CLzmaEncProps m_props; + chd_lzma_allocator m_allocator; +}; + + +// ======================> chd_lzma_decompressor + +// LZMA decompressor +class chd_lzma_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_lzma_decompressor(chd_file &chd, bool lossy); + ~chd_lzma_decompressor(); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + CLzmaProps m_props; + CLzmaDec m_decoder; + chd_lzma_allocator m_allocator; +}; + + +// ======================> chd_huffman_compressor + +// Huffman compressor +class chd_huffman_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_huffman_compressor(chd_file &chd, bool lossy); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + huffman_8bit_encoder m_encoder; +}; + + +// ======================> chd_huffman_decompressor + +// Huffman decompressor +class chd_huffman_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_huffman_decompressor(chd_file &chd, bool lossy); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + huffman_8bit_decoder m_decoder; +}; + + +// ======================> chd_flac_compressor + +// FLAC compressor +class chd_flac_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_flac_compressor(chd_file &chd, bool lossy, bool bigendian); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal state + bool m_swap_endian; + flac_encoder m_encoder; +}; + +// big-endian variant +class chd_flac_compressor_be : public chd_flac_compressor +{ +public: + // construction/destruction + chd_flac_compressor_be(chd_file &chd, bool lossy) + : chd_flac_compressor(chd, lossy, true) { } +}; + +// little-endian variant +class chd_flac_compressor_le : public chd_flac_compressor +{ +public: + // construction/destruction + chd_flac_compressor_le(chd_file &chd, bool lossy) + : chd_flac_compressor(chd, lossy, false) { } +}; + + +// ======================> chd_flac_decompressor + +// FLAC decompressor +class chd_flac_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_flac_decompressor(chd_file &chd, bool lossy, bool bigendian); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + +private: + // internal state + bool m_swap_endian; + flac_decoder m_decoder; +}; + +// big-endian variant +class chd_flac_decompressor_be : public chd_flac_decompressor +{ +public: + // construction/destruction + chd_flac_decompressor_be(chd_file &chd, bool lossy) + : chd_flac_decompressor(chd, lossy, true) { } +}; + +// little-endian variant +class chd_flac_decompressor_le : public chd_flac_decompressor +{ +public: + // construction/destruction + chd_flac_decompressor_le(chd_file &chd, bool lossy) + : chd_flac_decompressor(chd, lossy, false) { } +}; + + +// ======================> chd_avhuff_compressor + +// A/V compressor +class chd_avhuff_compressor : public chd_compressor +{ +public: + // construction/destruction + chd_avhuff_compressor(chd_file &chd, bool lossy); + + // core functionality + virtual UINT32 compress(const UINT8 *src, UINT32 srclen, UINT8 *dest); + +private: + // internal helpers + void postinit(); + + // internal state + avhuff_encoder m_encoder; + bool m_postinit; +}; + + +// ======================> chd_avhuff_decompressor + +// A/V decompressor +class chd_avhuff_decompressor : public chd_decompressor +{ +public: + // construction/destruction + chd_avhuff_decompressor(chd_file &chd, bool lossy); + + // core functionality + virtual void decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen); + virtual void configure(int param, void *config); + +private: + // internal state + avhuff_decoder m_decoder; +}; + + + +//************************************************************************** +// CODEC LIST +//************************************************************************** + +// static list of available known codecs +const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = +{ + { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor<chd_zlib_compressor>, &chd_codec_list::construct_decompressor<chd_zlib_decompressor> }, + { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor<chd_lzma_compressor>, &chd_codec_list::construct_decompressor<chd_lzma_decompressor> }, + { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor<chd_huffman_compressor>, &chd_codec_list::construct_decompressor<chd_huffman_decompressor> }, + { CHD_CODEC_FLAC_BE, false, "FLAC, big-endian", &chd_codec_list::construct_compressor<chd_flac_compressor_be>, &chd_codec_list::construct_decompressor<chd_flac_decompressor_be> }, + { CHD_CODEC_FLAC_LE, false, "FLAC, little-endian", &chd_codec_list::construct_compressor<chd_flac_compressor_le>, &chd_codec_list::construct_decompressor<chd_flac_decompressor_le> }, + { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor<chd_avhuff_compressor>, &chd_codec_list::construct_decompressor<chd_avhuff_decompressor> }, +}; + + + +//************************************************************************** +// CHD CODEC +//************************************************************************** + +//------------------------------------------------- +// chd_codec - constructor +//------------------------------------------------- + +chd_codec::chd_codec(chd_file &file, bool lossy) + : m_chd(file), + m_lossy(lossy) +{ +} + + +//------------------------------------------------- +// ~chd_codec - destructor +//------------------------------------------------- + +chd_codec::~chd_codec() +{ +} + + +//------------------------------------------------- +// configure - configuration +//------------------------------------------------- + +void chd_codec::configure(int param, void *config) +{ + // if not overridden, it is always a failure + throw CHDERR_INVALID_PARAMETER; +} + + + +//************************************************************************** +// CHD COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_compressor - constructor +//------------------------------------------------- + +chd_compressor::chd_compressor(chd_file &file, bool lossy) + : chd_codec(file, lossy) +{ +} + + + +//************************************************************************** +// CHD DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_decompressor - constructor +//------------------------------------------------- + +chd_decompressor::chd_decompressor(chd_file &file, bool lossy) + : chd_codec(file, lossy) +{ +} + + + +//************************************************************************** +// CHD CODEC LIST +//************************************************************************** + +//------------------------------------------------- +// new_compressor - create a new compressor +// instance of the given type +//------------------------------------------------- + +chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &file) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : (*entry->m_construct_compressor)(file, entry->m_lossy); +} + + +//------------------------------------------------- +// new_compressor - create a new decompressor +// instance of the given type +//------------------------------------------------- + +chd_decompressor *chd_codec_list::new_decompressor(chd_codec_type type, chd_file &file) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : (*entry->m_construct_decompressor)(file, entry->m_lossy); +} + + +//------------------------------------------------- +// codec_name - return the name of the given +// codec +//------------------------------------------------- + +const char *chd_codec_list::codec_name(chd_codec_type type) +{ + // find in the list and construct the class + const codec_entry *entry = find_in_list(type); + return (entry == NULL) ? NULL : entry->m_name; +} + + +//------------------------------------------------- +// find_in_list - create a new compressor +// instance of the given type +//------------------------------------------------- + +const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type type) +{ + // find in the list and construct the class + for (int listnum = 0; listnum < ARRAY_LENGTH(s_codec_list); listnum++) + if (s_codec_list[listnum].m_type == type) + return &s_codec_list[listnum]; + return NULL; +} + + + +//************************************************************************** +// CODEC INSTANCE +//************************************************************************** + +//------------------------------------------------- +// chd_compressor_group - constructor +//------------------------------------------------- + +chd_compressor_group::chd_compressor_group(chd_file &file, UINT32 compressor_list[4]) + : m_hunkbytes(file.hunk_bytes()), + m_compress_test(m_hunkbytes) +#if CHDCODEC_VERIFY_COMPRESSION + ,m_decompressed(m_hunkbytes) +#endif +{ + // verify the compression types and initialize the codecs + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + { + m_compressor[codecnum] = NULL; + if (compressor_list[codecnum] != CHD_CODEC_NONE) + { + m_compressor[codecnum] = chd_codec_list::new_compressor(compressor_list[codecnum], file); + if (m_compressor[codecnum] == NULL) + throw CHDERR_UNKNOWN_COMPRESSION; +#if CHDCODEC_VERIFY_COMPRESSION + m_decompressor[codecnum] = chd_codec_list::new_decompressor(compressor_list[codecnum], file); + if (m_decompressor[codecnum] == NULL) + throw CHDERR_UNKNOWN_COMPRESSION; +#endif + } + } +} + + +//------------------------------------------------- +// ~chd_compressor_group - destructor +//------------------------------------------------- + +chd_compressor_group::~chd_compressor_group() +{ + // delete the codecs and the test buffer + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + delete m_compressor[codecnum]; +} + + +//------------------------------------------------- +// find_best_compressor - iterate over all codecs +// to determine which one produces the best +// compression for this hunk +//------------------------------------------------- + +INT8 chd_compressor_group::find_best_compressor(const UINT8 *src, UINT8 *compressed, UINT32 &complen) +{ + // determine best compression technique + complen = m_hunkbytes; + INT8 compression = -1; + for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++) + if (m_compressor[codecnum] != NULL) + { + // attempt to compress, swallowing errors + try + { + // if this is the best one, copy the data into the permanent buffer + UINT32 compbytes = m_compressor[codecnum]->compress(src, m_hunkbytes, m_compress_test); +#if CHDCODEC_VERIFY_COMPRESSION + try + { + memset(m_decompressed, 0, m_hunkbytes); + m_decompressor[codecnum]->decompress(m_compress_test, compbytes, m_decompressed, m_hunkbytes); + } + catch (...) + { + } + + if (memcmp(src, m_decompressed, m_hunkbytes) != 0) + { + compbytes = m_compressor[codecnum]->compress(src, m_hunkbytes, m_compress_test); + try + { + m_decompressor[codecnum]->decompress(m_compress_test, compbytes, m_decompressed, m_hunkbytes); + } + catch (...) + { + memset(m_decompressed, 0, m_hunkbytes); + } + } +printf(" codec%d=%d bytes \n", codecnum, compbytes); +#endif + if (compbytes < complen) + { + compression = codecnum; + complen = compbytes; + memcpy(compressed, m_compress_test, compbytes); + } + } + catch (...) { } + } + + // if the best is none, copy it over + if (compression == -1) + memcpy(compressed, src, m_hunkbytes); + return compression; +} + + + +//************************************************************************** +// ZLIB ALLOCATOR HELPER +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_allocator - constructor +//------------------------------------------------- + +chd_zlib_allocator::chd_zlib_allocator() +{ + // reset pointer list + memset(m_allocptr, 0, sizeof(m_allocptr)); +} + + +//------------------------------------------------- +// ~chd_zlib_allocator - constructor +//------------------------------------------------- + +chd_zlib_allocator::~chd_zlib_allocator() +{ + // free our memory + for (int memindex = 0; memindex < ARRAY_LENGTH(m_allocptr); memindex++) + delete[] m_allocptr[memindex]; +} + + +//------------------------------------------------- +// install - configure the allocators for a +// stream +//------------------------------------------------- + +void chd_zlib_allocator::install(z_stream &stream) +{ + stream.zalloc = &chd_zlib_allocator::fast_alloc; + stream.zfree = &chd_zlib_allocator::fast_free; + stream.opaque = this; +} + + +//------------------------------------------------- +// zlib_fast_alloc - fast malloc for ZLIB, which +// allocates and frees memory frequently +//------------------------------------------------- + +voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size) +{ + chd_zlib_allocator *codec = reinterpret_cast<chd_zlib_allocator *>(opaque); + + // compute the size, rounding to the nearest 1k + size = (size * items + 0x3ff) & ~0x3ff; + + // reuse a hunk if we can + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + { + UINT32 *ptr = codec->m_allocptr[scan]; + if (ptr != NULL && size == *ptr) + { + // set the low bit of the size so we don't match next time + *ptr |= 1; + return ptr + 1; + } + } + + // alloc a new one and put it into the list + UINT32 *ptr = reinterpret_cast<UINT32 *>(new UINT8[size + sizeof(UINT32)]); + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + if (codec->m_allocptr[scan] == NULL) + { + codec->m_allocptr[scan] = ptr; + break; + } + + // set the low bit of the size so we don't match next time + *ptr = size | 1; + return ptr + 1; +} + + +//------------------------------------------------- +// zlib_fast_free - fast free for ZLIB, which +// allocates and frees memory frequently +//------------------------------------------------- + +void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address) +{ + chd_zlib_allocator *codec = reinterpret_cast<chd_zlib_allocator *>(opaque); + + // find the hunk + UINT32 *ptr = reinterpret_cast<UINT32 *>(address) - 1; + for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++) + if (ptr == codec->m_allocptr[scan]) + { + // clear the low bit of the size to allow matches + *ptr &= ~1; + return; + } +} + + + +//************************************************************************** +// ZLIB COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_compressor - constructor +//------------------------------------------------- + +chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, bool lossy) + : chd_compressor(chd, lossy) +{ + // initialize the deflater + m_deflater.next_in = (Bytef *)this; // bogus, but that's ok + m_deflater.avail_in = 0; + m_allocator.install(m_deflater); + int zerr = deflateInit2(&m_deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_zlib_compressor - destructor +//------------------------------------------------- + +chd_zlib_compressor::~chd_zlib_compressor() +{ + deflateEnd(&m_deflater); +} + + +//------------------------------------------------- +// compress - compress data using the ZLIB codec +//------------------------------------------------- + +UINT32 chd_zlib_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // reset the decompressor + m_deflater.next_in = const_cast<Bytef *>(src); + m_deflater.avail_in = srclen; + m_deflater.total_in = 0; + m_deflater.next_out = dest; + m_deflater.avail_out = srclen; + m_deflater.total_out = 0; + int zerr = deflateReset(&m_deflater); + if (zerr != Z_OK) + throw CHDERR_COMPRESSION_ERROR; + + // do it + zerr = deflate(&m_deflater, Z_FINISH); + + // if we ended up with more data than we started with, return an error + if (zerr != Z_STREAM_END || m_deflater.total_out >= srclen) + throw CHDERR_COMPRESSION_ERROR; + + // otherwise, return the length + return m_deflater.total_out; +} + + + +//************************************************************************** +// ZLIB DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_zlib_decompressor - constructor +//------------------------------------------------- + +chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, bool lossy) + : chd_decompressor(chd, lossy) +{ + // init the inflater first + m_inflater.next_in = (Bytef *)this; // bogus, but that's ok + m_inflater.avail_in = 0; + m_allocator.install(m_inflater); + int zerr = inflateInit2(&m_inflater, -MAX_WBITS); + + // convert errors + if (zerr == Z_MEM_ERROR) + throw std::bad_alloc(); + else if (zerr != Z_OK) + throw CHDERR_CODEC_ERROR; +} + + +//------------------------------------------------- +// ~chd_zlib_decompressor - destructor +//------------------------------------------------- + +chd_zlib_decompressor::~chd_zlib_decompressor() +{ + inflateEnd(&m_inflater); +} + + +//------------------------------------------------- +// decompress - decompress data using the ZLIB +// codec +//------------------------------------------------- + +void chd_zlib_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // reset the decompressor + m_inflater.next_in = const_cast<Bytef *>(src); + m_inflater.avail_in = complen; + m_inflater.total_in = 0; + m_inflater.next_out = dest; + m_inflater.avail_out = destlen; + m_inflater.total_out = 0; + int zerr = inflateReset(&m_inflater); + if (zerr != Z_OK) + throw CHDERR_DECOMPRESSION_ERROR; + + // do it + zerr = inflate(&m_inflater, Z_FINISH); + if (m_inflater.total_out != destlen) + throw CHDERR_DECOMPRESSION_ERROR; +} + + + +//************************************************************************** +// LZMA ALLOCATOR HELPER +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_allocator - constructor +//------------------------------------------------- + +chd_lzma_allocator::chd_lzma_allocator() +{ + // reset pointer list + memset(m_allocptr, 0, sizeof(m_allocptr)); + + // set our pointers + Alloc = &chd_lzma_allocator::fast_alloc; + Free = &chd_lzma_allocator::fast_free; +} + + +//------------------------------------------------- +// ~chd_lzma_allocator - constructor +//------------------------------------------------- + +chd_lzma_allocator::~chd_lzma_allocator() +{ + // free our memory + for (int memindex = 0; memindex < ARRAY_LENGTH(m_allocptr); memindex++) + delete[] m_allocptr[memindex]; +} + + +//------------------------------------------------- +// lzma_fast_alloc - fast malloc for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void *chd_lzma_allocator::fast_alloc(void *p, size_t size) +{ + chd_lzma_allocator *codec = reinterpret_cast<chd_lzma_allocator *>(p); + + // compute the size, rounding to the nearest 1k + size = (size + 0x3ff) & ~0x3ff; + + // reuse a hunk if we can + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + { + UINT32 *ptr = codec->m_allocptr[scan]; + if (ptr != NULL && size == *ptr) + { + // set the low bit of the size so we don't match next time + *ptr |= 1; + return ptr + 1; + } + } + + // alloc a new one and put it into the list + UINT32 *ptr = reinterpret_cast<UINT32 *>(new UINT8[size + sizeof(UINT32)]); + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + if (codec->m_allocptr[scan] == NULL) + { + codec->m_allocptr[scan] = ptr; + break; + } + + // set the low bit of the size so we don't match next time + *ptr = size | 1; + return ptr + 1; +} + + +//------------------------------------------------- +// lzma_fast_free - fast free for lzma, which +// allocates and frees memory frequently +//------------------------------------------------- + +void chd_lzma_allocator::fast_free(void *p, void *address) +{ + if (address == NULL) + return; + + chd_lzma_allocator *codec = reinterpret_cast<chd_lzma_allocator *>(p); + + // find the hunk + UINT32 *ptr = reinterpret_cast<UINT32 *>(address) - 1; + for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++) + if (ptr == codec->m_allocptr[scan]) + { + // clear the low bit of the size to allow matches + *ptr &= ~1; + return; + } +} + + + +//************************************************************************** +// LZMA COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_compressor - constructor +//------------------------------------------------- + +chd_lzma_compressor::chd_lzma_compressor(chd_file &chd, bool lossy) + : chd_compressor(chd, lossy) +{ + // initialize the properties + configure_properties(m_props, chd); +} + + +//------------------------------------------------- +// ~chd_lzma_compressor - destructor +//------------------------------------------------- + +chd_lzma_compressor::~chd_lzma_compressor() +{ +} + + +//------------------------------------------------- +// compress - compress data using the LZMA codec +//------------------------------------------------- + +UINT32 chd_lzma_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // allocate the encoder + CLzmaEncHandle encoder = LzmaEnc_Create(&m_allocator); + if (encoder == NULL) + throw CHDERR_COMPRESSION_ERROR; + + try + { + // configure the encoder + SRes res = LzmaEnc_SetProps(encoder, &m_props); + if (res != SZ_OK) + throw CHDERR_COMPRESSION_ERROR; + + // run it + UINT32 complen = srclen; + res = LzmaEnc_MemEncode(encoder, dest, &complen, src, srclen, 0, NULL, &m_allocator, &m_allocator); + if (res != SZ_OK) + throw CHDERR_COMPRESSION_ERROR; + + // clean up + LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator); + return complen; + } + catch (...) + { + // destroy before re-throwing + LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator); + throw; + } +} + + +//------------------------------------------------- +// configure_properties - configure the LZMA +// codec +//------------------------------------------------- + +void chd_lzma_compressor::configure_properties(CLzmaEncProps &props, chd_file &chd) +{ + LzmaEncProps_Init(&props); + props.level = 9; + props.reduceSize = chd.hunk_bytes(); + LzmaEncProps_Normalize(&props); +} + + + +//************************************************************************** +// LZMA DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_lzma_decompressor - constructor +//------------------------------------------------- + +chd_lzma_decompressor::chd_lzma_decompressor(chd_file &chd, bool lossy) + : chd_decompressor(chd, lossy) +{ + // construct the decoder + LzmaDec_Construct(&m_decoder); + + // configure the properties like the compressor did + CLzmaEncProps encoder_props; + chd_lzma_compressor::configure_properties(encoder_props, chd); + + // convert to decoder properties + CLzmaProps decoder_props; + decoder_props.lc = encoder_props.lc; + decoder_props.lp = encoder_props.lp; + decoder_props.pb = encoder_props.pb; + decoder_props.dicSize = encoder_props.dictSize; + + // do memory allocations + SRes res = LzmaDec_Allocate_MAME(&m_decoder, &decoder_props, &m_allocator); + if (res != SZ_OK) + throw CHDERR_DECOMPRESSION_ERROR; +} + + +//------------------------------------------------- +// ~chd_lzma_decompressor - destructor +//------------------------------------------------- + +chd_lzma_decompressor::~chd_lzma_decompressor() +{ + // free memory + LzmaDec_Free(&m_decoder, &m_allocator); +} + + +//------------------------------------------------- +// decompress - decompress data using the LZMA +// codec +//------------------------------------------------- + +void chd_lzma_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // initialize + LzmaDec_Init(&m_decoder); + + // decode + UINT32 consumedlen = complen; + UINT32 decodedlen = destlen; + ELzmaStatus status; + SRes res = LzmaDec_DecodeToBuf(&m_decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); + if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) + throw CHDERR_DECOMPRESSION_ERROR; +} + + + +//************************************************************************** +// HUFFMAN COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_huffman_compressor - constructor +//------------------------------------------------- + +chd_huffman_compressor::chd_huffman_compressor(chd_file &chd, bool lossy) + : chd_compressor(chd, lossy) +{ +} + + +//------------------------------------------------- +// compress - compress data using the Huffman +// codec +//------------------------------------------------- + +UINT32 chd_huffman_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + UINT32 complen; + if (m_encoder.encode(src, srclen, dest, srclen, complen) != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; + return complen; +} + + + +//************************************************************************** +// HUFFMAN DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_huffman_decompressor - constructor +//------------------------------------------------- + +chd_huffman_decompressor::chd_huffman_decompressor(chd_file &chd, bool lossy) + : chd_decompressor(chd, lossy) +{ +} + + +//------------------------------------------------- +// decompress - decompress data using the Huffman +// codec +//------------------------------------------------- + +void chd_huffman_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + if (m_decoder.decode(src, complen, dest, destlen) != HUFFERR_NONE) + throw CHDERR_COMPRESSION_ERROR; +} + + + +//************************************************************************** +// FLAC COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_flac_compressor - constructor +//------------------------------------------------- + +chd_flac_compressor::chd_flac_compressor(chd_file &chd, bool lossy, bool bigendian) + : chd_compressor(chd, lossy) +{ + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast<UINT8 *>(&native_endian) = 1; + if (native_endian == 1) + m_swap_endian = bigendian; + else + m_swap_endian = !bigendian; + + // configure the encoder + m_encoder.set_sample_rate(44100); + m_encoder.set_num_channels(2); + m_encoder.set_block_size(chd.hunk_bytes()); + m_encoder.set_strip_metadata(true); +} + + +//------------------------------------------------- +// compress - compress data using the FLAC codec +//------------------------------------------------- + +UINT32 chd_flac_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // reset and encode + m_encoder.reset(dest, chd().hunk_bytes()); + if (!m_encoder.encode_interleaved(reinterpret_cast<const INT16 *>(src), srclen / 4, m_swap_endian)) + throw CHDERR_COMPRESSION_ERROR; + + // finish up + UINT32 complen = m_encoder.finish(); + if (complen >= chd().hunk_bytes()) + throw CHDERR_COMPRESSION_ERROR; + return complen; +} + + + +//************************************************************************** +// FLAC COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_flac_decompressor - constructor +//------------------------------------------------- + +chd_flac_decompressor::chd_flac_decompressor(chd_file &chd, bool lossy, bool bigendian) + : chd_decompressor(chd, lossy) +{ + // determine whether we want native or swapped samples + UINT16 native_endian = 0; + *reinterpret_cast<UINT8 *>(&native_endian) = 1; + if (native_endian == 1) + m_swap_endian = bigendian; + else + m_swap_endian = !bigendian; +} + + +//------------------------------------------------- +// decompress - decompress data using the FLAC +// codec +//------------------------------------------------- + +void chd_flac_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // reset and decode + if (!m_decoder.reset(44100, 2, chd().hunk_bytes() / 4, src, complen)) + throw CHDERR_DECOMPRESSION_ERROR; + if (!m_decoder.decode_interleaved(reinterpret_cast<INT16 *>(dest), destlen / 4, m_swap_endian)) + throw CHDERR_DECOMPRESSION_ERROR; + + // finish up + m_decoder.finish(); +} + + + +//************************************************************************** +// AVHUFF COMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_avhuff_compressor - constructor +//------------------------------------------------- + +chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, bool lossy) + : chd_compressor(chd, lossy), + m_postinit(false) +{ + try + { + // attempt to do a post-init now + postinit(); + } + catch (chd_error &) + { + // if we're creating a new CHD, it won't work but that's ok + } +} + + +//------------------------------------------------- +// compress - compress data using the A/V codec +//------------------------------------------------- + +UINT32 chd_avhuff_compressor::compress(const UINT8 *src, UINT32 srclen, UINT8 *dest) +{ + // if we haven't yet set up the avhuff code, do it now + if (!m_postinit) + postinit(); + + // make sure short frames are padded with 0 + if (src != NULL) + { + int size = avhuff_encoder::raw_data_size(src); + while (size < srclen) + if (src[size++] != 0) + throw CHDERR_INVALID_DATA; + } + + // encode the audio and video + UINT32 complen; + avhuff_error averr = m_encoder.encode_data(src, dest, complen); + if (averr != AVHERR_NONE || complen > srclen) + throw CHDERR_COMPRESSION_ERROR; + return complen; +} + + +//------------------------------------------------- +// postinit - actual initialization of avhuff +// happens here, on the first attempt to compress +// or decompress data +//------------------------------------------------- + +void chd_avhuff_compressor::postinit() +{ + // get the metadata + astring metadata; + chd_error err = chd().read_metadata(AV_METADATA_TAG, 0, metadata); + if (err != CHDERR_NONE) + throw err; + + // extract the info + int fps, fpsfrac, width, height, interlaced, channels, rate; + if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + throw CHDERR_INVALID_METADATA; + + // compute the bytes per frame + UINT32 fps_times_1million = fps * 1000000 + fpsfrac; + UINT32 max_samples_per_frame = (UINT64(rate) * 1000000 + fps_times_1million - 1) / fps_times_1million; + UINT32 bytes_per_frame = 12 + channels * max_samples_per_frame * 2 + width * height * 2; + if (bytes_per_frame > chd().hunk_bytes()) + throw CHDERR_INVALID_METADATA; + + // done with post-init + m_postinit = true; +} + + + +//************************************************************************** +// AVHUFF DECOMPRESSOR +//************************************************************************** + +//------------------------------------------------- +// chd_avhuff_decompressor - constructor +//------------------------------------------------- + +chd_avhuff_decompressor::chd_avhuff_decompressor(chd_file &chd, bool lossy) + : chd_decompressor(chd, lossy) +{ +} + + +//------------------------------------------------- +// decompress - decompress data using the A/V +// codec +//------------------------------------------------- + +void chd_avhuff_decompressor::decompress(const UINT8 *src, UINT32 complen, UINT8 *dest, UINT32 destlen) +{ + // decode the audio and video + avhuff_error averr = m_decoder.decode_data(src, complen, dest); + if (averr != AVHERR_NONE) + throw CHDERR_DECOMPRESSION_ERROR; + + // pad short frames with 0 + if (dest != NULL) + { + int size = avhuff_encoder::raw_data_size(dest); + if (size < destlen) + memset(dest + size, 0, destlen - size); + } +} + + +//------------------------------------------------- +// config - codec-specific configuration for the +// A/V codec +//------------------------------------------------- + +void chd_avhuff_decompressor::configure(int param, void *config) +{ + // if we're getting the decompression configuration, apply it now + if (param == AVHUFF_CODEC_DECOMPRESS_CONFIG) + m_decoder.configure(*reinterpret_cast<avhuff_decompress_config *>(config)); + + // anything else is invalid + else + throw CHDERR_INVALID_PARAMETER; +} |