dmg/compress.c (170 lines of code) (raw):

#include "dmg/dmg.h" #include "dmg/compress.h" #include <zlib.h> #include <bzlib.h> #include "dmg/adc.h" #ifdef HAVE_LIBLZMA #include <lzma.h> // LZMA_FAIL_FAST was introduced in 5.3.3, attempting to use it in earlier versions // yields an error. #ifdef LZMA_FAIL_FAST #define LZMA_DECODE_OPTS LZMA_FAIL_FAST #else #define LZMA_DECODE_OPTS 0 #endif static int lzmaDecompress(unsigned char* inBuffer, size_t inSize, unsigned char* outBuffer, size_t outBufSize, size_t *decompSize) { lzma_ret lret; /* More memory than lzma ever needs */ uint64_t error_if_memory_usage_exceeds = 65 * 1024 * 1024; size_t inPos = 0; *decompSize = 0; lret = lzma_stream_buffer_decode(&error_if_memory_usage_exceeds, LZMA_DECODE_OPTS, NULL, inBuffer, &inPos, inSize, outBuffer, decompSize, outBufSize); return lret != LZMA_OK; } static int lzmaCompress(unsigned char *inBuffer, size_t inSize, unsigned char *outBuffer, size_t outBufSize, size_t *compSize, int level) { lzma_ret lret; *compSize = 0; if (level == COMPRESSION_LEVEL_DEFAULT) level = LZMA_PRESET_DEFAULT; lret = lzma_easy_buffer_encode(level, LZMA_CHECK_NONE, NULL, inBuffer, inSize, outBuffer, compSize, outBufSize); return lret != LZMA_OK; } #endif #ifdef HAVE_LZFSE #include <lzfse.h> static int lzfseDecompress(unsigned char* inBuffer, size_t inSize, unsigned char* outBuffer, size_t outBufSize, size_t *decompSize) { *decompSize = lzfse_decode_buffer(outBuffer, outBufSize, inBuffer, inSize, NULL); return !*decompSize; } static int lzfseCompress(unsigned char *inBuffer, size_t inSize, unsigned char *outBuffer, size_t outBufSize, size_t *compSize, int level) { *compSize = lzfse_encode_buffer(outBuffer, outBufSize, inBuffer, inSize, NULL); return !*compSize; } #endif static int bz2Compress(unsigned char *inBuffer, size_t inSize, unsigned char *outBuffer, size_t outBufSize, size_t *compSize, int level) { unsigned int bz2CompSize = outBufSize; if (level == COMPRESSION_LEVEL_DEFAULT) level = 9; int ret = (BZ2_bzBuffToBuffCompress((char*)outBuffer, &bz2CompSize, (char*)inBuffer, inSize, level, 0, 0) != BZ_OK); *compSize = bz2CompSize; return ret; } static int zlibCompress(unsigned char *inBuffer, size_t inSize, unsigned char *outBuffer, size_t outBufSize, size_t *compSize, int level) { *compSize = outBufSize; if (level == COMPRESSION_LEVEL_DEFAULT) level = Z_DEFAULT_COMPRESSION; return (compress2(outBuffer, compSize, inBuffer, inSize, level) != Z_OK); } size_t oldDecompressBuffer(size_t runSectors) { // A reasonable heuristic // Bzip2 at level 1 usually needs the largest extra space, compared to other compressors. // Happens to equal 0x208 for 0x200 sectors, same as before. return runSectors + 4 + (runSectors >> 7); } size_t modernDecompressBuffer(size_t runSectors) { // Modern algorithms need more space for some reason, about double the size of a run. // Sometimes it's a bit more, so add a generous amount of padding. // It's unclear why so much is needed, lzma/lzfse shouldn't need this much in normal usage! return runSectors * 2 + 64; } void initDefaultCompressor(Compressor* comp) { comp->level = COMPRESSION_LEVEL_DEFAULT; getCompressor(comp, COMPRESSOR_DEFAULT); } int getCompressor(Compressor* comp, char *name) { if (strcasecmp(name, "bzip2") == 0) { comp->block_type = BLOCK_BZIP2; comp->compress = bz2Compress; comp->decompressBuffer = oldDecompressBuffer; return 0; } if (strcasecmp(name, "zlib") == 0) { comp->block_type = BLOCK_ZLIB; comp->compress = zlibCompress; comp->decompressBuffer = oldDecompressBuffer; return 0; } #ifdef HAVE_LIBLZMA if (strcasecmp(name, "lzma") == 0) { comp->block_type = BLOCK_LZMA; comp->compress = lzmaCompress; comp->decompressBuffer = modernDecompressBuffer; return 0; } #endif #ifdef HAVE_LZFSE if (strcasecmp(name, "lzfse") == 0) { comp->block_type = BLOCK_LZFSE; comp->compress = lzfseCompress; comp->decompressBuffer = modernDecompressBuffer; return 0; } #endif return 1; } const char *compressionNames() { return "bzip2, zlib" #ifdef HAVE_LIBLZMA ", lzma" #endif #ifdef HAVE_LZFSE ", lzfse" #endif ; } int compressionBlockTypeSupported(uint32_t type) { switch (type) { case BLOCK_ADC: case BLOCK_BZIP2: case BLOCK_ZLIB: #ifdef HAVE_LIBLZMA case BLOCK_LZMA: #endif #ifdef HAVE_LZFSE case BLOCK_LZFSE: #endif return 0; } return 1; } int decompressRun(uint32_t type, unsigned char* inBuffer, size_t inSize, unsigned char* outBuffer, size_t expectedSize) { size_t decompSize; int ret; if (type == BLOCK_ADC) { ret = (adc_decompress(inSize, inBuffer, expectedSize, outBuffer, &decompSize) != inSize); } else if (type == BLOCK_ZLIB) { decompSize = expectedSize; ret = (uncompress(outBuffer, &decompSize, inBuffer, inSize) != Z_OK); } else if (type == BLOCK_BZIP2) { unsigned int bz2DecompSize = expectedSize; ret = (BZ2_bzBuffToBuffDecompress((char*)outBuffer, &bz2DecompSize, (char*)inBuffer, inSize, 0, 0) != BZ_OK); decompSize = bz2DecompSize; #ifdef HAVE_LIBLZMA } else if (type == BLOCK_LZMA) { ret = lzmaDecompress(inBuffer, inSize, outBuffer, expectedSize, &decompSize); #endif #ifdef HAVE_LZFSE } else if (type == BLOCK_LZFSE) { ret = lzfseDecompress(inBuffer, inSize, outBuffer, expectedSize, &decompSize); #endif } else { fprintf(stderr, "Unsupported block type: %#08x\n", type); return 1; } if (ret == 0) { ASSERT(decompSize == expectedSize, "Decompressed size mismatch"); } return ret; }