X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=deps%2Flibchdr%2Fsrc%2Flibchdr_chd.c;h=076202b3bfdfa8506520ac7062e173947910a547;hb=refs%2Fheads%2Flibretro;hp=913abaa2a9f977960067815fb481dd91366d0ad5;hpb=2ff0b5124f2e17a290121e1eeecf45db1d9e2c85;p=pcsx_rearmed.git diff --git a/deps/libchdr/src/libchdr_chd.c b/deps/libchdr/src/libchdr_chd.c index 913abaa2..19e9c4d5 100644 --- a/deps/libchdr/src/libchdr_chd.c +++ b/deps/libchdr/src/libchdr_chd.c @@ -41,16 +41,21 @@ #include #include #include +#include #include #include #include #include #include +#include #include "LzmaEnc.h" #include "LzmaDec.h" -#include "zlib.h" +#if defined(__PS3__) || defined(__PSL1GHT__) +#define __MACTYPES__ +#endif +#include #undef TRUE #undef FALSE @@ -84,6 +89,11 @@ #define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ +#define CHD_MAX_HUNK_SIZE (128 * 1024 * 1024) /* hunk size probably shouldn't be more than 128MB */ + +/* we're currently only using this for CD/DVDs, if we end up with more than 10GB data, it's probably invalid */ +#define CHD_MAX_FILE_SIZE (10ULL * 1024 * 1024 * 1024) + #define COOKIE_VALUE 0xbaadf00d #define MAX_ZLIB_ALLOCS 64 @@ -157,10 +167,10 @@ enum typedef struct _codec_interface codec_interface; struct _codec_interface { - UINT32 compression; /* type of compression */ + uint32_t compression; /* type of compression */ const char *compname; /* name of the algorithm */ - UINT8 lossy; /* is this a lossy algorithm? */ - chd_error (*init)(void *codec, UINT32 hunkbytes); /* codec initialize */ + uint8_t lossy; /* is this a lossy algorithm? */ + chd_error (*init)(void *codec, uint32_t hunkbytes); /* codec initialize */ void (*free)(void *codec); /* codec free */ chd_error (*decompress)(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); /* decompress data */ chd_error (*config)(void *codec, int param, void *config); /* configure */ @@ -170,22 +180,22 @@ struct _codec_interface typedef struct _map_entry map_entry; struct _map_entry { - UINT64 offset; /* offset within the file of the data */ - UINT32 crc; /* 32-bit CRC of the data */ - UINT32 length; /* length of the data */ - UINT8 flags; /* misc flags */ + uint64_t offset; /* offset within the file of the data */ + uint32_t crc; /* 32-bit CRC of the data */ + uint32_t length; /* length of the data */ + uint8_t flags; /* misc flags */ }; /* a single metadata entry */ typedef struct _metadata_entry metadata_entry; struct _metadata_entry { - UINT64 offset; /* offset within the file of the header */ - UINT64 next; /* offset within the file of the next header */ - UINT64 prev; /* offset within the file of the previous header */ - UINT32 length; /* length of the metadata */ - UINT32 metatag; /* metadata tag */ - UINT8 flags; /* flag bits */ + uint64_t offset; /* offset within the file of the header */ + uint64_t next; /* offset within the file of the next header */ + uint64_t prev; /* offset within the file of the previous header */ + uint32_t length; /* length of the metadata */ + uint32_t metatag; /* metadata tag */ + uint8_t flags; /* flag bits */ }; /* codec-private data for the ZLIB codec */ @@ -193,8 +203,8 @@ struct _metadata_entry typedef struct _zlib_allocator zlib_allocator; struct _zlib_allocator { - UINT32 * allocptr[MAX_ZLIB_ALLOCS]; - UINT32 * allocptr2[MAX_ZLIB_ALLOCS]; + uint32_t * allocptr[MAX_ZLIB_ALLOCS]; + uint32_t * allocptr2[MAX_ZLIB_ALLOCS]; }; typedef struct _zlib_codec_data zlib_codec_data; @@ -224,6 +234,18 @@ struct _lzma_codec_data lzma_allocator allocator; }; +typedef struct _huff_codec_data huff_codec_data; +struct _huff_codec_data +{ + struct huffman_decoder* decoder; +}; + +typedef struct _zstd_codec_data zstd_codec_data; +struct _zstd_codec_data +{ + ZSTD_DStream *dstream; +}; + /* codec-private data for the CDZL codec */ typedef struct _cdzl_codec_data cdzl_codec_data; struct _cdzl_codec_data { @@ -246,6 +268,14 @@ struct _cdlz_codec_data { uint8_t* buffer; }; +/* codec-private data for the FLAC codec */ +typedef struct _flac_codec_data flac_codec_data; +struct _flac_codec_data { + /* internal state */ + int native_endian; + flac_decoder decoder; +}; + /* codec-private data for the CDFL codec */ typedef struct _cdfl_codec_data cdfl_codec_data; struct _cdfl_codec_data { @@ -258,13 +288,23 @@ struct _cdfl_codec_data { uint8_t* buffer; }; +typedef struct _cdzs_codec_data cdzs_codec_data; +struct _cdzs_codec_data +{ + zstd_codec_data base_decompressor; +#ifdef WANT_SUBCODE + zstd_codec_data subcode_decompressor; +#endif + uint8_t* buffer; +}; + /* internal representation of an open CHD file */ struct _chd_file { - UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ + uint32_t cookie; /* cookie, should equal COOKIE_VALUE */ core_file * file; /* handle to the open core file */ - UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ + uint64_t file_size; /* size of the core file */ chd_header header; /* header, extracted from file */ chd_file * parent; /* pointer to parent file, or NULL */ @@ -272,26 +312,31 @@ struct _chd_file map_entry * map; /* array of map entries */ #ifdef NEED_CACHE_HUNK - UINT8 * cache; /* hunk cache pointer */ - UINT32 cachehunk; /* index of currently cached hunk */ + uint8_t * cache; /* hunk cache pointer */ + uint32_t cachehunk; /* index of currently cached hunk */ - UINT8 * compare; /* hunk compare pointer */ - UINT32 comparehunk; /* index of current compare data */ + uint8_t * compare; /* hunk compare pointer */ + uint32_t comparehunk; /* index of current compare data */ #endif - UINT8 * compressed; /* pointer to buffer for compressed data */ + uint8_t * compressed; /* pointer to buffer for compressed data */ const codec_interface * codecintf[4]; /* interface to the codec */ zlib_codec_data zlib_codec_data; /* zlib codec data */ + lzma_codec_data lzma_codec_data; /* lzma codec data */ + huff_codec_data huff_codec_data; /* huff codec data */ + flac_codec_data flac_codec_data; /* flac codec data */ + zstd_codec_data zstd_codec_data; /* zstd codec data */ cdzl_codec_data cdzl_codec_data; /* cdzl codec data */ cdlz_codec_data cdlz_codec_data; /* cdlz codec data */ cdfl_codec_data cdfl_codec_data; /* cdfl codec data */ + cdzs_codec_data cdzs_codec_data; /* cdzs codec data */ #ifdef NEED_CACHE_HUNK - UINT32 maxhunk; /* maximum hunk accessed */ + uint32_t maxhunk; /* maximum hunk accessed */ #endif - UINT8 * file_cache; /* cache of underlying file */ + uint8_t * file_cache; /* cache of underlying file */ }; @@ -299,28 +344,36 @@ struct _chd_file GLOBAL VARIABLES ***************************************************************************/ -static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 }; -static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; +static const uint8_t nullmd5[CHD_MD5_BYTES] = { 0 }; +static const uint8_t nullsha1[CHD_SHA1_BYTES] = { 0 }; /*************************************************************************** PROTOTYPES ***************************************************************************/ +/* core_file wrappers over stdio */ +static core_file *core_stdio_fopen(char const *path); +static uint64_t core_stdio_fsize(core_file *file); +static size_t core_stdio_fread(void *ptr, size_t size, size_t nmemb, core_file *file); +static int core_stdio_fclose(core_file *file); +static int core_stdio_fclose_nonowner(core_file *file); // alternate fclose used by chd_open_file +static int core_stdio_fseek(core_file* file, int64_t offset, int whence); + /* internal header operations */ static chd_error header_validate(const chd_header *header); static chd_error header_read(chd_file *chd, chd_header *header); /* internal hunk read/write */ #ifdef NEED_CACHE_HUNK -static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum); +static chd_error hunk_read_into_cache(chd_file *chd, uint32_t hunknum); #endif -static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest); +static chd_error hunk_read_into_memory(chd_file *chd, uint32_t hunknum, uint8_t *dest); /* internal map access */ static chd_error map_read(chd_file *chd); /* metadata management */ -static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); +static chd_error metadata_find_entry(chd_file *chd, uint32_t metatag, uint32_t metaindex, metadata_entry *metaentry); /* zlib compression codec */ static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes); @@ -335,6 +388,22 @@ static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes); static void lzma_codec_free(void *codec); static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +/* huff compression codec */ +static chd_error huff_codec_init(void *codec, uint32_t hunkbytes); +static void huff_codec_free(void *codec); +static chd_error huff_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* flac compression codec */ +static chd_error flac_codec_init(void *codec, uint32_t hunkbytes); +static void flac_codec_free(void *codec); +static chd_error flac_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + +/* zstd compression codec */ +static chd_error zstd_codec_init(void *codec, uint32_t hunkbytes); +static void zstd_codec_free(void *codec); +static chd_error zstd_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + + /* cdzl compression codec */ static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes); static void cdzl_codec_free(void* codec); @@ -350,6 +419,11 @@ static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes); static void cdfl_codec_free(void* codec); static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); +/* cdzs compression codec */ +static chd_error cdzs_codec_init(void *codec, uint32_t hunkbytes); +static void cdzs_codec_free(void *codec); +static chd_error cdzs_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); + /*************************************************************************** * LZMA ALLOCATOR HELPER *************************************************************************** @@ -392,6 +466,28 @@ static void lzma_allocator_free(void* p ) } } +/*------------------------------------------------- + * lzma_allocator_free_unused + * free unused buffers only + *------------------------------------------------- + */ + +static void lzma_allocator_free_unused(lzma_allocator *codec) +{ + int i; + + for (i = 0; i < MAX_LZMA_ALLOCS; i++) + { + uint32_t *ptr = codec->allocptr[i]; + if (ptr && (*ptr & 1) == 0) + { + free(codec->allocptr[i]); + codec->allocptr[i] = NULL; + codec->allocptr2[i] = NULL; + } + } +} + /*------------------------------------------------- * lzma_fast_alloc - fast malloc for lzma, which * allocates and frees memory frequently @@ -533,6 +629,7 @@ static chd_error lzma_codec_init(void* codec, uint32_t hunkbytes) return CHDERR_DECOMPRESSION_ERROR; } LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc); + lzma_allocator_free_unused(alloc); /* do memory allocations */ if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK) @@ -622,22 +719,39 @@ static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t { uint32_t framenum; cdlz_codec_data* cdlz = (cdlz_codec_data*)codec; + chd_error decomp_err; + uint32_t complen_base; /* determine header bytes */ - uint32_t frames = destlen / CD_FRAME_SIZE; - uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; - uint32_t ecc_bytes = (frames + 7) / 8; - uint32_t header_bytes = ecc_bytes + complen_bytes; + const uint32_t frames = destlen / CD_FRAME_SIZE; + const uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + const uint32_t ecc_bytes = (frames + 7) / 8; + const uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* input may be truncated, double-check */ + if (complen < (ecc_bytes + 2)) + return CHDERR_DECOMPRESSION_ERROR; /* extract compressed length of base */ - uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; if (complen_bytes > 2) + { + if (complen < (ecc_bytes + 3)) + return CHDERR_DECOMPRESSION_ERROR; + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + } + if (complen < (header_bytes + complen_base)) + return CHDERR_DECOMPRESSION_ERROR; /* reset and decode */ - lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); + decomp_err = lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; #ifdef WANT_SUBCODE - zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + decomp_err = zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; #endif /* reassemble the data */ @@ -705,22 +819,39 @@ static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t { uint32_t framenum; cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; + chd_error decomp_err; + uint32_t complen_base; /* determine header bytes */ - uint32_t frames = destlen / CD_FRAME_SIZE; - uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; - uint32_t ecc_bytes = (frames + 7) / 8; - uint32_t header_bytes = ecc_bytes + complen_bytes; + const uint32_t frames = destlen / CD_FRAME_SIZE; + const uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + const uint32_t ecc_bytes = (frames + 7) / 8; + const uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* input may be truncated, double-check */ + if (complen < (ecc_bytes + 2)) + return CHDERR_DECOMPRESSION_ERROR; /* extract compressed length of base */ - uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; if (complen_bytes > 2) + { + if (complen < (ecc_bytes + 3)) + return CHDERR_DECOMPRESSION_ERROR; + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + } + if (complen < (header_bytes + complen_base)) + return CHDERR_DECOMPRESSION_ERROR; /* reset and decode */ - zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); + decomp_err = zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; #ifdef WANT_SUBCODE - zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + decomp_err = zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; #endif /* reassemble the data */ @@ -746,24 +877,122 @@ static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t return CHDERR_NONE; } +/*************************************************************************** + * HUFFMAN DECOMPRESSOR + *************************************************************************** + */ + +static chd_error huff_codec_init(void* codec, uint32_t hunkbytes) +{ + huff_codec_data* huff_codec = (huff_codec_data*) codec; + huff_codec->decoder = create_huffman_decoder(256, 16); + return CHDERR_NONE; +} + +static void huff_codec_free(void *codec) +{ + huff_codec_data* huff_codec = (huff_codec_data*) codec; + delete_huffman_decoder(huff_codec->decoder); +} + +static chd_error huff_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + huff_codec_data* huff_codec = (huff_codec_data*) codec; + struct bitstream* bitbuf = create_bitstream(src, complen); + + // first import the tree + enum huffman_error err = huffman_import_tree_huffman(huff_codec->decoder, bitbuf); + if (err != HUFFERR_NONE) + { + free(bitbuf); + return CHDERR_DECOMPRESSION_ERROR; + } + + // then decode the data + uint32_t cur; + for (cur = 0; cur < destlen; cur++) + dest[cur] = huffman_decode_one(huff_codec->decoder, bitbuf); + bitstream_flush(bitbuf); + chd_error result = bitstream_overflow(bitbuf) ? CHDERR_DECOMPRESSION_ERROR : CHDERR_NONE; + + free(bitbuf); + return result; +} + /*************************************************************************** * CD FLAC DECOMPRESSOR *************************************************************************** */ /*------------------------------------------------------ - * cdfl_codec_blocksize - return the optimal block size + * flac_codec_blocksize - return the optimal block size *------------------------------------------------------ */ -static uint32_t cdfl_codec_blocksize(uint32_t bytes) +static uint32_t flac_codec_blocksize(uint32_t bytes) { /* determine FLAC block size, which must be 16-65535 * clamp to 2k since that's supposed to be the sweet spot */ - uint32_t hunkbytes = bytes / 4; - while (hunkbytes > 2048) - hunkbytes /= 2; - return hunkbytes; + uint32_t blocksize = bytes / 4; + while (blocksize > 2048) + blocksize /= 2; + return blocksize; +} + +static chd_error flac_codec_init(void *codec, uint32_t hunkbytes) +{ + uint16_t native_endian = 0; + flac_codec_data *flac = (flac_codec_data*)codec; + + /* make sure the CHD's hunk size is an even multiple of the sample size */ + if (hunkbytes % 4 != 0) + return CHDERR_CODEC_ERROR; + + /* determine whether we want native or swapped samples */ + *(uint8_t *)(&native_endian) = 1; + flac->native_endian = (native_endian & 1); + + /* flac decoder init */ + if (flac_decoder_init(&flac->decoder)) + return CHDERR_OUT_OF_MEMORY; + + return CHDERR_NONE; +} + +static void flac_codec_free(void *codec) +{ + flac_codec_data *flac = (flac_codec_data*)codec; + flac_decoder_free(&flac->decoder); +} + +static chd_error flac_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + flac_codec_data *flac = (flac_codec_data*)codec; + int swap_endian; + + if (src[0] == 'L') + swap_endian = !flac->native_endian; + else if (src[0] == 'B') + swap_endian = flac->native_endian; + else + return CHDERR_DECOMPRESSION_ERROR; + + if (!flac_decoder_reset(&flac->decoder, 44100, 2, flac_codec_blocksize(destlen), src + 1, complen - 1)) + return CHDERR_DECOMPRESSION_ERROR; + if (!flac_decoder_decode_interleaved(&flac->decoder, (int16_t *)(dest), destlen/4, swap_endian)) + return CHDERR_DECOMPRESSION_ERROR; + flac_decoder_finish(&flac->decoder); + + return CHDERR_NONE; +} + +static uint32_t cdfl_codec_blocksize(uint32_t bytes) +{ + // for CDs it seems that CD_MAX_SECTOR_DATA is the right target + uint32_t blocksize = bytes / 4; + while (blocksize > CD_MAX_SECTOR_DATA) + blocksize /= 2; + return blocksize; } static chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes) @@ -851,6 +1080,180 @@ static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t return CHDERR_NONE; } + + +/*************************************************************************** + * ZSTD DECOMPRESSOR + *************************************************************************** + */ + +/*------------------------------------------------- + * zstd_codec_init - constructor + *------------------------------------------------- + */ + +static chd_error zstd_codec_init(void* codec, uint32_t hunkbytes) +{ + zstd_codec_data* zstd_codec = (zstd_codec_data*) codec; + + zstd_codec->dstream = ZSTD_createDStream(); + if (!zstd_codec->dstream) { + printf("NO DSTREAM CREATED!\n"); + return CHDERR_DECOMPRESSION_ERROR; + } + return CHDERR_NONE; +} + +/*------------------------------------------------- + * zstd_codec_free + *------------------------------------------------- + */ + +static void zstd_codec_free(void* codec) +{ + zstd_codec_data* zstd_codec = (zstd_codec_data*) codec; + + ZSTD_freeDStream(zstd_codec->dstream); +} + +/*------------------------------------------------- + * decompress - decompress data using the ZSTD + * codec + *------------------------------------------------- + */ +static chd_error zstd_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + /* initialize */ + zstd_codec_data* zstd_codec = (zstd_codec_data*) codec; + //reset decompressor + size_t zstd_res = ZSTD_initDStream(zstd_codec->dstream); + if (ZSTD_isError(zstd_res)) + { + printf("INITI DSTREAM FAILED!\n"); + return CHDERR_DECOMPRESSION_ERROR; + } + + ZSTD_inBuffer input = {src, complen, 0}; + ZSTD_outBuffer output = {dest, destlen, 0 }; + + while ((input.pos < input.size) && (output.pos < output.size)) + { + zstd_res = ZSTD_decompressStream(zstd_codec->dstream, &output, &input); + if (ZSTD_isError(zstd_res)) + { + printf("DECOMPRESSION ERROR IN LOOP\n"); + return CHDERR_DECOMPRESSION_ERROR; + } + } + if (output.pos != output.size) + { + printf("OUTPUT DOESN'T MATCH!\n"); + return CHDERR_DECOMPRESSION_ERROR; + } + return CHDERR_NONE; + +} + +/* cdzs */ +static chd_error cdzs_codec_init(void* codec, uint32_t hunkbytes) +{ + chd_error ret; + cdzs_codec_data* cdzs = (cdzs_codec_data*) codec; + + /* allocate buffer */ + cdzs->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); + if (cdzs->buffer == NULL) + return CHDERR_OUT_OF_MEMORY; + + /* make sure the CHD's hunk size is an even multiple of the frame size */ + ret = zstd_codec_init(&cdzs->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); + if (ret != CHDERR_NONE) + return ret; + +#ifdef WANT_SUBCODE + ret = zstd_codec_init(&cdzs->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA); + if (ret != CHDERR_NONE) + return ret; +#endif + + if (hunkbytes % CD_FRAME_SIZE != 0) + return CHDERR_CODEC_ERROR; + + return CHDERR_NONE; +} + +static void cdzs_codec_free(void* codec) +{ + cdzs_codec_data* cdzs = (cdzs_codec_data*) codec; + free(cdzs->buffer); + zstd_codec_free(&cdzs->base_decompressor); +#ifdef WANT_SUBCODE + zstd_codec_free(&cdzs->subcode_decompressor); +#endif +} + +static chd_error cdzs_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) +{ + uint32_t framenum; + cdzs_codec_data* cdzs = (cdzs_codec_data*)codec; + chd_error decomp_err; + uint32_t complen_base; + + /* determine header bytes */ + const uint32_t frames = destlen / CD_FRAME_SIZE; + const uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; + const uint32_t ecc_bytes = (frames + 7) / 8; + const uint32_t header_bytes = ecc_bytes + complen_bytes; + + /* input may be truncated, double-check */ + if (complen < (ecc_bytes + 2)) + return CHDERR_DECOMPRESSION_ERROR; + + /* extract compressed length of base */ + complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; + if (complen_bytes > 2) + { + if (complen < (ecc_bytes + 3)) + return CHDERR_DECOMPRESSION_ERROR; + + complen_base = (complen_base << 8) | src[ecc_bytes + 2]; + } + if (complen < (header_bytes + complen_base)) + return CHDERR_DECOMPRESSION_ERROR; + + /* reset and decode */ + decomp_err = zstd_codec_decompress(&cdzs->base_decompressor, &src[header_bytes], complen_base, &cdzs->buffer[0], frames * CD_MAX_SECTOR_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; +#ifdef WANT_SUBCODE + decomp_err = zstd_codec_decompress(&cdzs->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzs->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); + if (decomp_err != CHDERR_NONE) + return decomp_err; +#endif + + /* reassemble the data */ + for (framenum = 0; framenum < frames; framenum++) + { + uint8_t *sector; + + memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzs->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); +#ifdef WANT_SUBCODE + memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzs->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); +#endif + +#ifdef WANT_RAW_DATA_SECTOR + /* reconstitute the ECC data and sync header */ + sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; + if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) + { + memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + ecc_generate(sector); + } +#endif + } + return CHDERR_NONE; +} + /*************************************************************************** CODEC INTERFACES ***************************************************************************/ @@ -901,6 +1304,49 @@ static const codec_interface codec_interfaces[] = NULL }, + /* V5 lzma compression */ + { + CHD_CODEC_LZMA, + "lzma (LZMA)", + FALSE, + lzma_codec_init, + lzma_codec_free, + lzma_codec_decompress, + NULL + }, + + /* V5 huffman compression */ + { + CHD_CODEC_HUFFMAN, + "Huffman", + FALSE, + huff_codec_init, + huff_codec_free, + huff_codec_decompress, + NULL + }, + + /* V5 flac compression */ + { + CHD_CODEC_FLAC, + "flac (FLAC)", + FALSE, + flac_codec_init, + flac_codec_free, + flac_codec_decompress, + NULL + }, + /* V5 zstd compression */ + { + CHD_CODEC_ZSTD, + "ZStandard", + FALSE, + zstd_codec_init, + zstd_codec_free, + zstd_codec_decompress, + NULL + }, + /* V5 CD zlib compression */ { CHD_CODEC_CD_ZLIB, @@ -933,6 +1379,17 @@ static const codec_interface codec_interfaces[] = cdfl_codec_decompress, NULL }, + /* V5 CD zstd compression */ + { + CHD_CODEC_CD_ZSTD, + "cdzs (CD ZStandard)", + FALSE, + cdzs_codec_init, + cdzs_codec_free, + cdzs_codec_decompress, + NULL + } + }; /*************************************************************************** @@ -940,22 +1397,22 @@ static const codec_interface codec_interfaces[] = ***************************************************************************/ /*------------------------------------------------- - get_bigendian_uint64 - fetch a UINT64 from + get_bigendian_uint64_t - fetch a uint64_t from the data stream in bigendian order -------------------------------------------------*/ -static inline UINT64 get_bigendian_uint64(const UINT8 *base) +static inline uint64_t get_bigendian_uint64_t(const uint8_t *base) { - return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) | - ((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7]; + return ((uint64_t)base[0] << 56) | ((uint64_t)base[1] << 48) | ((uint64_t)base[2] << 40) | ((uint64_t)base[3] << 32) | + ((uint64_t)base[4] << 24) | ((uint64_t)base[5] << 16) | ((uint64_t)base[6] << 8) | (uint64_t)base[7]; } /*------------------------------------------------- - put_bigendian_uint64 - write a UINT64 to + put_bigendian_uint64_t - write a uint64_t to the data stream in bigendian order -------------------------------------------------*/ -static inline void put_bigendian_uint64(UINT8 *base, UINT64 value) +static inline void put_bigendian_uint64_t(uint8_t *base, uint64_t value) { base[0] = value >> 56; base[1] = value >> 48; @@ -972,10 +1429,10 @@ static inline void put_bigendian_uint64(UINT8 *base, UINT64 value) the data stream in bigendian order -------------------------------------------------*/ -static inline UINT64 get_bigendian_uint48(const UINT8 *base) +static inline uint64_t get_bigendian_uint48(const uint8_t *base) { - return ((UINT64)base[0] << 40) | ((UINT64)base[1] << 32) | - ((UINT64)base[2] << 24) | ((UINT64)base[3] << 16) | ((UINT64)base[4] << 8) | (UINT64)base[5]; + return ((uint64_t)base[0] << 40) | ((uint64_t)base[1] << 32) | + ((uint64_t)base[2] << 24) | ((uint64_t)base[3] << 16) | ((uint64_t)base[4] << 8) | (uint64_t)base[5]; } /*------------------------------------------------- @@ -983,7 +1440,7 @@ static inline UINT64 get_bigendian_uint48(const UINT8 *base) the data stream in bigendian order -------------------------------------------------*/ -static inline void put_bigendian_uint48(UINT8 *base, UINT64 value) +static inline void put_bigendian_uint48(uint8_t *base, uint64_t value) { value &= 0xffffffffffff; base[0] = value >> 40; @@ -994,21 +1451,21 @@ static inline void put_bigendian_uint48(UINT8 *base, UINT64 value) base[5] = value; } /*------------------------------------------------- - get_bigendian_uint32 - fetch a UINT32 from + get_bigendian_uint32_t - fetch a uint32_t from the data stream in bigendian order -------------------------------------------------*/ -static inline UINT32 get_bigendian_uint32(const UINT8 *base) +static inline uint32_t get_bigendian_uint32_t(const uint8_t *base) { return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3]; } /*------------------------------------------------- - put_bigendian_uint32 - write a UINT32 to + put_bigendian_uint32_t - write a uint32_t to the data stream in bigendian order -------------------------------------------------*/ -static inline void put_bigendian_uint32(UINT8 *base, UINT32 value) +static inline void put_bigendian_uint32_t(uint8_t *base, uint32_t value) { base[0] = value >> 24; base[1] = value >> 16; @@ -1021,7 +1478,7 @@ static inline void put_bigendian_uint32(UINT8 *base, UINT32 value) the data stream in bigendian order -------------------------------------------------*/ -static inline void put_bigendian_uint24(UINT8 *base, UINT32 value) +static inline void put_bigendian_uint24(uint8_t *base, uint32_t value) { value &= 0xffffff; base[0] = value >> 16; @@ -1034,27 +1491,27 @@ static inline void put_bigendian_uint24(UINT8 *base, UINT32 value) the data stream in bigendian order -------------------------------------------------*/ -static inline UINT32 get_bigendian_uint24(const UINT8 *base) +static inline uint32_t get_bigendian_uint24(const uint8_t *base) { return (base[0] << 16) | (base[1] << 8) | base[2]; } /*------------------------------------------------- - get_bigendian_uint16 - fetch a UINT16 from + get_bigendian_uint16 - fetch a uint16_t from the data stream in bigendian order -------------------------------------------------*/ -static inline UINT16 get_bigendian_uint16(const UINT8 *base) +static inline uint16_t get_bigendian_uint16(const uint8_t *base) { return (base[0] << 8) | base[1]; } /*------------------------------------------------- - put_bigendian_uint16 - write a UINT16 to + put_bigendian_uint16 - write a uint16_t to the data stream in bigendian order -------------------------------------------------*/ -static inline void put_bigendian_uint16(UINT8 *base, UINT16 value) +static inline void put_bigendian_uint16(uint8_t *base, uint16_t value) { base[0] = value >> 8; base[1] = value; @@ -1065,10 +1522,10 @@ static inline void put_bigendian_uint16(UINT8 *base, UINT16 value) entry from the datastream -------------------------------------------------*/ -static inline void map_extract(const UINT8 *base, map_entry *entry) +static inline void map_extract(const uint8_t *base, map_entry *entry) { - entry->offset = get_bigendian_uint64(&base[0]); - entry->crc = get_bigendian_uint32(&base[8]); + entry->offset = get_bigendian_uint64_t(&base[0]); + entry->crc = get_bigendian_uint32_t(&base[8]); entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16); entry->flags = base[15]; } @@ -1078,10 +1535,10 @@ static inline void map_extract(const UINT8 *base, map_entry *entry) entry to the datastream -------------------------------------------------*/ -static inline void map_assemble(UINT8 *base, map_entry *entry) +static inline void map_assemble(uint8_t *base, map_entry *entry) { - put_bigendian_uint64(&base[0], entry->offset); - put_bigendian_uint32(&base[8], entry->crc); + put_bigendian_uint64_t(&base[0], entry->offset); + put_bigendian_uint32_t(&base[8], entry->crc); put_bigendian_uint16(&base[12], entry->length); base[14] = entry->length >> 16; base[15] = entry->flags; @@ -1092,6 +1549,11 @@ static inline void map_assemble(UINT8 *base, map_entry *entry) -------------------------------------------------*/ static inline int map_size_v5(chd_header* header) { + // Avoid overflow due to corrupted data. + const uint32_t max_hunkcount = (UINT32_MAX / header->mapentrybytes); + if (header->hunkcount > max_hunkcount) + return -1; + return header->hunkcount * header->mapentrybytes; } @@ -1160,7 +1622,7 @@ static inline int chd_compressed(chd_header* header) { static chd_error decompress_v5_map(chd_file* chd, chd_header* header) { int result = 0; - int hunknum; + uint32_t hunknum; int repcount = 0; uint8_t lastcomp = 0; uint32_t last_self = 0; @@ -1176,12 +1638,19 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) uint8_t rawbuf[16]; struct huffman_decoder* decoder; enum huffman_error err; - uint64_t curoffset; + uint64_t curoffset; int rawmapsize = map_size_v5(header); + if (rawmapsize < 0) + return CHDERR_INVALID_FILE; if (!chd_compressed(header)) { + if ((header->mapoffset + rawmapsize) >= chd->file_size || (header->mapoffset + rawmapsize) < header->mapoffset) + return CHDERR_INVALID_FILE; + header->rawmap = (uint8_t*)malloc(rawmapsize); + if (header->rawmap == NULL) + return CHDERR_OUT_OF_MEMORY; core_fseek(chd->file, header->mapoffset, SEEK_SET); result = core_fread(chd->file, header->rawmap, rawmapsize); return CHDERR_NONE; @@ -1190,7 +1659,7 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) /* read the reader */ core_fseek(chd->file, header->mapoffset, SEEK_SET); result = core_fread(chd->file, rawbuf, sizeof(rawbuf)); - mapbytes = get_bigendian_uint32(&rawbuf[0]); + mapbytes = get_bigendian_uint32_t(&rawbuf[0]); firstoffs = get_bigendian_uint48(&rawbuf[4]); mapcrc = get_bigendian_uint16(&rawbuf[10]); lengthbits = rawbuf[12]; @@ -1198,11 +1667,21 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) parentbits = rawbuf[14]; /* now read the map */ + if ((header->mapoffset + mapbytes) < header->mapoffset || (header->mapoffset + mapbytes) >= chd->file_size) + return CHDERR_INVALID_FILE; compressed_ptr = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes); + if (compressed_ptr == NULL) + return CHDERR_OUT_OF_MEMORY; core_fseek(chd->file, header->mapoffset + 16, SEEK_SET); result = core_fread(chd->file, compressed_ptr, mapbytes); bitbuf = create_bitstream(compressed_ptr, sizeof(uint8_t) * mapbytes); header->rawmap = (uint8_t*)malloc(rawmapsize); + if (header->rawmap == NULL) + { + free(compressed_ptr); + free(bitbuf); + return CHDERR_OUT_OF_MEMORY; + } /* first decode the compression types */ decoder = create_huffman_decoder(16, 8); @@ -1229,7 +1708,16 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) rawmap[0] = lastcomp, repcount--; else { - uint8_t val = huffman_decode_one(decoder, bitbuf); + uint8_t val; + if (bitstream_overflow(bitbuf)) + { + free(compressed_ptr); + free(bitbuf); + delete_huffman_decoder(decoder); + return CHDERR_DECOMPRESSION_ERROR; + } + + val = huffman_decode_one(decoder, bitbuf); if (val == COMPRESSION_RLE_SMALL) rawmap[0] = lastcomp, repcount = 2 + huffman_decode_one(decoder, bitbuf); else if (val == COMPRESSION_RLE_LARGE) @@ -1319,9 +1807,9 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header) entry in old format from the datastream -------------------------------------------------*/ -static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes) +static inline void map_extract_old(const uint8_t *base, map_entry *entry, uint32_t hunkbytes) { - entry->offset = get_bigendian_uint64(&base[0]); + entry->offset = get_bigendian_uint64_t(&base[0]); entry->crc = 0; entry->length = entry->offset >> 44; entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED); @@ -1340,7 +1828,24 @@ static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 h chd_open_file - open a CHD file for access -------------------------------------------------*/ -CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) +CHD_EXPORT chd_error chd_open_file(FILE *file, int mode, chd_file *parent, chd_file **chd) { + core_file *stream = malloc(sizeof(core_file)); + if (!stream) + return CHDERR_OUT_OF_MEMORY; + stream->argp = file; + stream->fsize = core_stdio_fsize; + stream->fread = core_stdio_fread; + stream->fclose = core_stdio_fclose_nonowner; + stream->fseek = core_stdio_fseek; + + return chd_open_core_file(stream, mode, parent, chd); +} + +/*------------------------------------------------- + chd_open_core_file - open a CHD file for access +-------------------------------------------------*/ + +CHD_EXPORT chd_error chd_open_core_file(core_file *file, int mode, chd_file *parent, chd_file **chd) { chd_file *newchd = NULL; chd_error err; @@ -1362,6 +1867,9 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, newchd->cookie = COOKIE_VALUE; newchd->parent = parent; newchd->file = file; + newchd->file_size = core_fsize(file); + if ((int64_t)newchd->file_size <= 0) + EARLY_EXIT(err = CHDERR_INVALID_FILE); /* now attempt to read the header */ err = header_read(newchd, &newchd->header); @@ -1382,8 +1890,15 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); /* if we need a parent, make sure we have one */ - if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT)) - EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + if (parent == NULL) + { + /* Detect parent requirement for versions below 5 */ + if (newchd->header.version < 5 && newchd->header.flags & CHDFLAGS_HAS_PARENT) + EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + /* Detection for version 5 and above - if parentsha1 != 0, we have a parent */ + else if (newchd->header.version >= 5 && memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0) + EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + } /* make sure we have a valid parent */ if (parent != NULL) @@ -1417,8 +1932,8 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, #ifdef NEED_CACHE_HUNK /* allocate and init the hunk cache */ - newchd->cache = (UINT8 *)malloc(newchd->header.hunkbytes); - newchd->compare = (UINT8 *)malloc(newchd->header.hunkbytes); + newchd->cache = (uint8_t *)malloc(newchd->header.hunkbytes); + newchd->compare = (uint8_t *)malloc(newchd->header.hunkbytes); if (newchd->cache == NULL || newchd->compare == NULL) EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); newchd->cachehunk = ~0; @@ -1426,7 +1941,7 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, #endif /* allocate the temporary compressed buffer */ - newchd->compressed = (UINT8 *)malloc(newchd->header.hunkbytes); + newchd->compressed = (uint8_t *)malloc(newchd->header.hunkbytes); if (newchd->compressed == NULL) EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); @@ -1455,7 +1970,8 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, } else { - int decompnum; + int decompnum, needsinit; + /* verify the compression types and initialize the codecs */ for (decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++) { @@ -1472,8 +1988,21 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, if (newchd->codecintf[decompnum] == NULL && newchd->header.compression[decompnum] != 0) EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + /* ensure we don't try to initialize the same codec twice */ + /* this is "normal" for chds where the user overrides the codecs, it'll have none repeated */ + needsinit = (newchd->codecintf[decompnum]->init != NULL); + for (i = 0; i < decompnum; i++) + { + if (newchd->codecintf[decompnum] == newchd->codecintf[i]) + { + /* already initialized */ + needsinit = 0; + break; + } + } + /* initialize the codec */ - if (newchd->codecintf[decompnum]->init != NULL) + if (needsinit) { void* codec = NULL; switch (newchd->header.compression[decompnum]) @@ -1482,6 +2011,22 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, codec = &newchd->zlib_codec_data; break; + case CHD_CODEC_LZMA: + codec = &newchd->lzma_codec_data; + break; + + case CHD_CODEC_HUFFMAN: + codec = &newchd->huff_codec_data; + break; + + case CHD_CODEC_FLAC: + codec = &newchd->flac_codec_data; + break; + + case CHD_CODEC_ZSTD: + codec = &newchd->zstd_codec_data; + break; + case CHD_CODEC_CD_ZLIB: codec = &newchd->cdzl_codec_data; break; @@ -1493,6 +2038,10 @@ CHD_EXPORT chd_error chd_open_file(core_file *file, int mode, chd_file *parent, case CHD_CODEC_CD_FLAC: codec = &newchd->cdfl_codec_data; break; + + case CHD_CODEC_CD_ZSTD: + codec = &newchd->cdzs_codec_data; + break; } if (codec == NULL) @@ -1522,24 +2071,16 @@ cleanup: CHD_EXPORT chd_error chd_precache(chd_file *chd) { -#ifdef _MSC_VER - size_t size, count; -#else - ssize_t size, count; -#endif + int64_t count; if (chd->file_cache == NULL) { - core_fseek(chd->file, 0, SEEK_END); - size = core_ftell(chd->file); - if (size <= 0) - return CHDERR_INVALID_DATA; - chd->file_cache = malloc(size); + chd->file_cache = malloc(chd->file_size); if (chd->file_cache == NULL) return CHDERR_OUT_OF_MEMORY; core_fseek(chd->file, 0, SEEK_SET); - count = core_fread(chd->file, chd->file_cache, size); - if (count != size) + count = core_fread(chd->file, chd->file_cache, chd->file_size); + if (count != chd->file_size) { free(chd->file_cache); chd->file_cache = NULL; @@ -1560,6 +2101,12 @@ CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_error err; core_file *file = NULL; + if (filename == NULL) + { + err = CHDERR_INVALID_PARAMETER; + goto cleanup; + } + /* choose the proper mode */ switch(mode) { @@ -1572,7 +2119,7 @@ CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent, } /* open the file */ - file = core_fopen(filename); + file = core_stdio_fopen(filename); if (file == 0) { err = CHDERR_FILE_NOT_FOUND; @@ -1580,12 +2127,7 @@ CHD_EXPORT chd_error chd_open(const char *filename, int mode, chd_file *parent, } /* now open the CHD */ - err = chd_open_file(file, mode, parent, chd); - if (err != CHDERR_NONE) - goto cleanup; - - /* we now own this file */ - (*chd)->owns_file = TRUE; + return chd_open_core_file(file, mode, parent, chd); cleanup: if ((err != CHDERR_NONE) && (file != NULL)) @@ -1616,27 +2158,61 @@ CHD_EXPORT void chd_close(chd_file *chd) for (i = 0 ; i < ARRAY_LENGTH(chd->codecintf); i++) { void* codec = NULL; + int j, needsfree; if (chd->codecintf[i] == NULL) continue; - switch (chd->codecintf[i]->compression) + /* only free each codec at max once */ + needsfree = 1; + for (j = 0; j < i; j++) { - case CHD_CODEC_CD_LZMA: - codec = &chd->cdlz_codec_data; + if (chd->codecintf[i] == chd->codecintf[j]) + { + needsfree = 0; break; + } + } + if (!needsfree) + continue; + switch (chd->codecintf[i]->compression) + { case CHD_CODEC_ZLIB: codec = &chd->zlib_codec_data; break; + case CHD_CODEC_LZMA: + codec = &chd->lzma_codec_data; + break; + + case CHD_CODEC_HUFFMAN: + codec = &chd->huff_codec_data; + break; + + case CHD_CODEC_FLAC: + codec = &chd->flac_codec_data; + break; + + case CHD_CODEC_ZSTD: + codec = &chd->zstd_codec_data; + break; + case CHD_CODEC_CD_ZLIB: codec = &chd->cdzl_codec_data; break; + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + case CHD_CODEC_CD_FLAC: codec = &chd->cdfl_codec_data; break; + + case CHD_CODEC_CD_ZSTD: + codec = &chd->cdzs_codec_data; + break; } if (codec) @@ -1667,7 +2243,7 @@ CHD_EXPORT void chd_close(chd_file *chd) free(chd->map); /* close the file */ - if (chd->owns_file && chd->file != NULL) + if (chd->file != NULL) core_fclose(chd->file); #ifdef NEED_CACHE_HUNK @@ -1676,6 +2252,9 @@ CHD_EXPORT void chd_close(chd_file *chd) if (chd->file_cache) free(chd->file_cache); + if (chd->parent) + chd_close(chd->parent); + /* free our memory */ free(chd); } @@ -1749,6 +2328,41 @@ CHD_EXPORT const chd_header *chd_get_header(chd_file *chd) return &chd->header; } +/*------------------------------------------------- + chd_read_header - read CHD header data + from file into the pointed struct +-------------------------------------------------*/ +CHD_EXPORT chd_error chd_read_header(const char *filename, chd_header *header) +{ + chd_error err = CHDERR_NONE; + chd_file chd; + + /* punt if NULL */ + if (filename == NULL || header == NULL) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* open the file */ + chd.file = core_stdio_fopen(filename); + if (chd.file == NULL) + EARLY_EXIT(err = CHDERR_FILE_NOT_FOUND); + + /* attempt to read the header */ + err = header_read(&chd, header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* validate the header */ + err = header_validate(header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + +cleanup: + if (chd.file != NULL) + core_fclose(chd.file); + + return err; +} + /*************************************************************************** CORE DATA READ/WRITE ***************************************************************************/ @@ -1758,7 +2372,7 @@ CHD_EXPORT const chd_header *chd_get_header(chd_file *chd) file -------------------------------------------------*/ -CHD_EXPORT chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) +CHD_EXPORT chd_error chd_read(chd_file *chd, uint32_t hunknum, void *buffer) { /* punt if NULL or invalid */ if (chd == NULL || chd->cookie != COOKIE_VALUE) @@ -1769,7 +2383,7 @@ CHD_EXPORT chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) return CHDERR_HUNK_OUT_OF_RANGE; /* perform the read */ - return hunk_read_into_memory(chd, hunknum, (UINT8 *)buffer); + return hunk_read_into_memory(chd, hunknum, (uint8_t *)buffer); } /*************************************************************************** @@ -1781,11 +2395,11 @@ CHD_EXPORT chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) of the given type -------------------------------------------------*/ -CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags) +CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, uint32_t searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t *resultlen, uint32_t *resulttag, uint8_t *resultflags) { metadata_entry metaentry; chd_error err; - UINT32 count; + uint32_t count; /* if we didn't find it, just return */ err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); @@ -1795,11 +2409,11 @@ CHD_EXPORT chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 se if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0) { char faux_metadata[256]; - UINT32 faux_length; + uint32_t faux_length; /* fill in the faux metadata */ - sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, chd->header.hunkbytes / chd->header.obsolete_hunksize); - faux_length = (UINT32)strlen(faux_metadata) + 1; + sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, (chd->header.obsolete_hunksize != 0) ? (chd->header.hunkbytes / chd->header.obsolete_hunksize) : 0); + faux_length = (uint32_t)strlen(faux_metadata) + 1; /* copy the metadata itself */ memcpy(output, faux_metadata, MIN(outputlen, faux_length)); @@ -1850,7 +2464,7 @@ CHD_EXPORT chd_error chd_codec_config(chd_file *chd, int param, void *config) particular codec -------------------------------------------------*/ -CHD_EXPORT const char *chd_get_codec_name(UINT32 codec) +CHD_EXPORT const char *chd_get_codec_name(uint32_t codec) { return "Unknown"; } @@ -1920,6 +2534,10 @@ static chd_error header_validate(const chd_header *header) return CHDERR_INVALID_PARAMETER; } + /* some basic size checks to prevent huge mallocs */ + if (header->hunkbytes >= CHD_MAX_HUNK_SIZE || ((uint64_t)header->hunkbytes * (uint64_t)header->totalhunks) >= CHD_MAX_FILE_SIZE) + return CHDERR_INVALID_PARAMETER; + return CHDERR_NONE; } @@ -1928,7 +2546,7 @@ static chd_error header_validate(const chd_header *header) guess at the bytes/unit based on metadata -------------------------------------------------*/ -static UINT32 header_guess_unitbytes(chd_file *chd) +static uint32_t header_guess_unitbytes(chd_file *chd) { /* look for hard disk metadata; if found, then the unit size == sector size */ char metadata[512]; @@ -1956,8 +2574,8 @@ static UINT32 header_guess_unitbytes(chd_file *chd) static chd_error header_read(chd_file *chd, chd_header *header) { - UINT8 rawheader[CHD_MAX_HEADER_SIZE]; - UINT32 count; + uint8_t rawheader[CHD_MAX_HEADER_SIZE]; + uint32_t count; /* punt if NULL */ if (header == NULL) @@ -1979,8 +2597,8 @@ static chd_error header_read(chd_file *chd, chd_header *header) /* extract the direct data */ memset(header, 0, sizeof(*header)); - header->length = get_bigendian_uint32(&rawheader[8]); - header->version = get_bigendian_uint32(&rawheader[12]); + header->length = get_bigendian_uint32_t(&rawheader[8]); + header->version = get_bigendian_uint32_t(&rawheader[12]); /* make sure it's a version we understand */ if (header->version == 0 || header->version > CHD_HEADER_VERSION) @@ -1996,8 +2614,8 @@ static chd_error header_read(chd_file *chd, chd_header *header) return CHDERR_INVALID_DATA; /* extract the common data */ - header->flags = get_bigendian_uint32(&rawheader[16]); - header->compression[0] = get_bigendian_uint32(&rawheader[20]); + header->flags = get_bigendian_uint32_t(&rawheader[16]); + header->compression[0] = get_bigendian_uint32_t(&rawheader[20]); header->compression[1] = CHD_CODEC_NONE; header->compression[2] = CHD_CODEC_NONE; header->compression[3] = CHD_CODEC_NONE; @@ -2005,17 +2623,19 @@ static chd_error header_read(chd_file *chd, chd_header *header) /* extract the V1/V2-specific data */ if (header->version < 3) { - int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]); - header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]); - header->totalhunks = get_bigendian_uint32(&rawheader[28]); - header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]); - header->obsolete_heads = get_bigendian_uint32(&rawheader[36]); - header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]); + int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32_t(&rawheader[76]); + header->obsolete_hunksize = get_bigendian_uint32_t(&rawheader[24]); + header->totalhunks = get_bigendian_uint32_t(&rawheader[28]); + header->obsolete_cylinders = get_bigendian_uint32_t(&rawheader[32]); + header->obsolete_heads = get_bigendian_uint32_t(&rawheader[36]); + header->obsolete_sectors = get_bigendian_uint32_t(&rawheader[40]); memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); - header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen; + header->logicalbytes = (uint64_t)header->obsolete_cylinders * (uint64_t)header->obsolete_heads * (uint64_t)header->obsolete_sectors * (uint64_t)seclen; header->hunkbytes = seclen * header->obsolete_hunksize; header->unitbytes = header_guess_unitbytes(chd); + if (header->unitbytes == 0) + return CHDERR_INVALID_DATA; header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; header->metaoffset = 0; } @@ -2023,13 +2643,15 @@ static chd_error header_read(chd_file *chd, chd_header *header) /* extract the V3-specific data */ else if (header->version == 3) { - header->totalhunks = get_bigendian_uint32(&rawheader[24]); - header->logicalbytes = get_bigendian_uint64(&rawheader[28]); - header->metaoffset = get_bigendian_uint64(&rawheader[36]); + header->totalhunks = get_bigendian_uint32_t(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64_t(&rawheader[28]); + header->metaoffset = get_bigendian_uint64_t(&rawheader[36]); memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); - header->hunkbytes = get_bigendian_uint32(&rawheader[76]); + header->hunkbytes = get_bigendian_uint32_t(&rawheader[76]); header->unitbytes = header_guess_unitbytes(chd); + if (header->unitbytes == 0) + return CHDERR_INVALID_DATA; header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES); memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES); @@ -2038,11 +2660,13 @@ static chd_error header_read(chd_file *chd, chd_header *header) /* extract the V4-specific data */ else if (header->version == 4) { - header->totalhunks = get_bigendian_uint32(&rawheader[24]); - header->logicalbytes = get_bigendian_uint64(&rawheader[28]); - header->metaoffset = get_bigendian_uint64(&rawheader[36]); - header->hunkbytes = get_bigendian_uint32(&rawheader[44]); + header->totalhunks = get_bigendian_uint32_t(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64_t(&rawheader[28]); + header->metaoffset = get_bigendian_uint64_t(&rawheader[36]); + header->hunkbytes = get_bigendian_uint32_t(&rawheader[44]); header->unitbytes = header_guess_unitbytes(chd); + if (header->unitbytes == 0) + return CHDERR_INVALID_DATA; header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; memcpy(header->sha1, &rawheader[48], CHD_SHA1_BYTES); memcpy(header->parentsha1, &rawheader[68], CHD_SHA1_BYTES); @@ -2053,16 +2677,20 @@ static chd_error header_read(chd_file *chd, chd_header *header) else if (header->version == 5) { /* TODO */ - header->compression[0] = get_bigendian_uint32(&rawheader[16]); - header->compression[1] = get_bigendian_uint32(&rawheader[20]); - header->compression[2] = get_bigendian_uint32(&rawheader[24]); - header->compression[3] = get_bigendian_uint32(&rawheader[28]); - header->logicalbytes = get_bigendian_uint64(&rawheader[32]); - header->mapoffset = get_bigendian_uint64(&rawheader[40]); - header->metaoffset = get_bigendian_uint64(&rawheader[48]); - header->hunkbytes = get_bigendian_uint32(&rawheader[56]); + header->compression[0] = get_bigendian_uint32_t(&rawheader[16]); + header->compression[1] = get_bigendian_uint32_t(&rawheader[20]); + header->compression[2] = get_bigendian_uint32_t(&rawheader[24]); + header->compression[3] = get_bigendian_uint32_t(&rawheader[28]); + header->logicalbytes = get_bigendian_uint64_t(&rawheader[32]); + header->mapoffset = get_bigendian_uint64_t(&rawheader[40]); + header->metaoffset = get_bigendian_uint64_t(&rawheader[48]); + header->hunkbytes = get_bigendian_uint32_t(&rawheader[56]); + if (header->hunkbytes == 0) + return CHDERR_INVALID_DATA; header->hunkcount = (header->logicalbytes + header->hunkbytes - 1) / header->hunkbytes; - header->unitbytes = get_bigendian_uint32(&rawheader[60]); + header->unitbytes = get_bigendian_uint32_t(&rawheader[60]); + if (header->unitbytes == 0) + return CHDERR_INVALID_DATA; header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes; memcpy(header->sha1, &rawheader[84], CHD_SHA1_BYTES); memcpy(header->parentsha1, &rawheader[104], CHD_SHA1_BYTES); @@ -2094,7 +2722,7 @@ static chd_error header_read(chd_file *chd, chd_header *header) hunk -------------------------------------------------*/ -static UINT8* hunk_read_compressed(chd_file *chd, UINT64 offset, size_t size) +static uint8_t* hunk_read_compressed(chd_file *chd, uint64_t offset, size_t size) { #ifdef _MSC_VER size_t bytes; @@ -2103,10 +2731,17 @@ static UINT8* hunk_read_compressed(chd_file *chd, UINT64 offset, size_t size) #endif if (chd->file_cache != NULL) { - return chd->file_cache + offset; + if ((offset + size) > chd->file_size || (offset + size) < offset) + return NULL; + else + return chd->file_cache + offset; } else { + /* make sure it isn't larger than the compressed buffer */ + if (size > chd->header.hunkbytes) + return NULL; + core_fseek(chd->file, offset, SEEK_SET); bytes = core_fread(chd->file, chd->compressed, size); if (bytes != size) @@ -2120,7 +2755,7 @@ static UINT8* hunk_read_compressed(chd_file *chd, UINT64 offset, size_t size) hunk -------------------------------------------------*/ -static chd_error hunk_read_uncompressed(chd_file *chd, UINT64 offset, size_t size, UINT8 *dest) +static chd_error hunk_read_uncompressed(chd_file *chd, uint64_t offset, size_t size, uint8_t *dest) { #ifdef _MSC_VER size_t bytes; @@ -2129,6 +2764,9 @@ static chd_error hunk_read_uncompressed(chd_file *chd, UINT64 offset, size_t siz #endif if (chd->file_cache != NULL) { + if ((offset + size) > chd->file_size || (offset + size) < offset) + return CHDERR_READ_ERROR; + memcpy(dest, chd->file_cache + offset, size); } else @@ -2147,7 +2785,7 @@ static chd_error hunk_read_uncompressed(chd_file *chd, UINT64 offset, size_t siz the CHD's hunk cache -------------------------------------------------*/ -static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) +static chd_error hunk_read_into_cache(chd_file *chd, uint32_t hunknum) { chd_error err; @@ -2176,7 +2814,7 @@ static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) memory at the given location -------------------------------------------------*/ -static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest) +static chd_error hunk_read_into_memory(chd_file *chd, uint32_t hunknum, uint8_t *dest) { chd_error err; @@ -2194,8 +2832,8 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des if (chd->header.version < 5) { map_entry *entry = &chd->map[hunknum]; - UINT32 bytes; - UINT8* compressed_bytes; + uint32_t bytes; + uint8_t* compressed_bytes; /* switch off the entry type */ switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) @@ -2208,7 +2846,9 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des /* read it into the decompression buffer */ compressed_bytes = hunk_read_compressed(chd, entry->offset, entry->length); if (compressed_bytes == NULL) + { return CHDERR_READ_ERROR; + } /* now decompress using the codec */ err = CHDERR_NONE; @@ -2229,7 +2869,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des /* mini-compressed data */ case V34_MAP_ENTRY_TYPE_MINI: - put_bigendian_uint64(&dest[0], entry->offset); + put_bigendian_uint64_t(&dest[0], entry->offset); for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) dest[bytes] = dest[bytes - 8]; break; @@ -2261,12 +2901,12 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des uint16_t blockcrc; #endif uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum]; - UINT8* compressed_bytes; + uint8_t* compressed_bytes; /* uncompressed case */ if (!chd_compressed(&chd->header)) { - blockoffs = (uint64_t)get_bigendian_uint32(rawmap) * (uint64_t)chd->header.hunkbytes; + blockoffs = (uint64_t)get_bigendian_uint32_t(rawmap) * (uint64_t)chd->header.hunkbytes; if (blockoffs != 0) { core_fseek(chd->file, blockoffs, SEEK_SET); int result = core_fread(chd->file, dest, chd->header.hunkbytes); @@ -2302,21 +2942,41 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des return CHDERR_READ_ERROR; switch (chd->codecintf[rawmap[0]]->compression) { - case CHD_CODEC_CD_LZMA: - codec = &chd->cdlz_codec_data; - break; - case CHD_CODEC_ZLIB: codec = &chd->zlib_codec_data; break; + case CHD_CODEC_LZMA: + codec = &chd->lzma_codec_data; + break; + + case CHD_CODEC_HUFFMAN: + codec = &chd->huff_codec_data; + break; + + case CHD_CODEC_FLAC: + codec = &chd->flac_codec_data; + break; + + case CHD_CODEC_ZSTD: + codec = &chd->zstd_codec_data; + break; + case CHD_CODEC_CD_ZLIB: codec = &chd->cdzl_codec_data; break; + case CHD_CODEC_CD_LZMA: + codec = &chd->cdlz_codec_data; + break; + case CHD_CODEC_CD_FLAC: codec = &chd->cdfl_codec_data; break; + + case CHD_CODEC_CD_ZSTD: + codec = &chd->cdzs_codec_data; + break; } if (codec==NULL) return CHDERR_CODEC_ERROR; @@ -2343,13 +3003,33 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des return hunk_read_into_memory(chd, blockoffs, dest); case COMPRESSION_PARENT: -#if 0 - /* TODO */ - if (m_parent_missing) + if (chd->parent == NULL) return CHDERR_REQUIRES_PARENT; - return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); -#endif - return CHDERR_DECOMPRESSION_ERROR; + uint8_t units_in_hunk = chd->header.hunkbytes / chd->header.unitbytes; + + /* blockoffs is aligned to units_in_hunk */ + if (blockoffs % units_in_hunk == 0) { + return hunk_read_into_memory(chd->parent, blockoffs / units_in_hunk, dest); + /* blockoffs is not aligned to units_in_hunk */ + } else { + uint32_t unit_in_hunk = blockoffs % units_in_hunk; + uint8_t *buf = malloc(chd->header.hunkbytes); + /* Read first half of hunk which contains blockoffs */ + err = hunk_read_into_memory(chd->parent, blockoffs / units_in_hunk, buf); + if (err != CHDERR_NONE) { + free(buf); + return err; + } + memcpy(dest, buf + unit_in_hunk * chd->header.unitbytes, (units_in_hunk - unit_in_hunk) * chd->header.unitbytes); + /* Read second half of hunk which contains blockoffs */ + err = hunk_read_into_memory(chd->parent, (blockoffs / units_in_hunk) + 1, buf); + if (err != CHDERR_NONE) { + free(buf); + return err; + } + memcpy(dest + (units_in_hunk - unit_in_hunk) * chd->header.unitbytes, buf, unit_in_hunk * chd->header.unitbytes); + free(buf); + } } return CHDERR_NONE; } @@ -2368,13 +3048,13 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des static chd_error map_read(chd_file *chd) { - UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; - UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; - UINT64 fileoffset, maxoffset = 0; - UINT8 cookie[MAP_ENTRY_SIZE]; - UINT32 count; + uint32_t entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; + uint8_t raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; + uint64_t fileoffset, maxoffset = 0; + uint8_t cookie[MAP_ENTRY_SIZE]; + uint32_t count; chd_error err; - int i; + uint32_t i; /* first allocate memory */ chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks); @@ -2429,7 +3109,7 @@ static chd_error map_read(chd_file *chd) } /* verify the length */ - if (maxoffset > core_fsize(chd->file)) + if (maxoffset > chd->file_size) { err = CHDERR_INVALID_FILE; goto cleanup; @@ -2451,7 +3131,7 @@ cleanup: metadata_find_entry - find a metadata entry -------------------------------------------------*/ -static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry) +static chd_error metadata_find_entry(chd_file *chd, uint32_t metatag, uint32_t metaindex, metadata_entry *metaentry) { /* start at the beginning */ metaentry->offset = chd->header.metaoffset; @@ -2460,19 +3140,20 @@ static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metai /* loop until we run out of options */ while (metaentry->offset != 0) { - UINT8 raw_meta_header[METADATA_HEADER_SIZE]; - UINT32 count; + uint8_t raw_meta_header[METADATA_HEADER_SIZE]; + uint32_t count; /* read the raw header */ - core_fseek(chd->file, metaentry->offset, SEEK_SET); + if (core_fseek(chd->file, metaentry->offset, SEEK_SET) != 0) + break; count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); if (count != sizeof(raw_meta_header)) break; /* extract the data */ - metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]); - metaentry->length = get_bigendian_uint32(&raw_meta_header[4]); - metaentry->next = get_bigendian_uint64(&raw_meta_header[8]); + metaentry->metatag = get_bigendian_uint32_t(&raw_meta_header[0]); + metaentry->length = get_bigendian_uint32_t(&raw_meta_header[4]); + metaentry->next = get_bigendian_uint64_t(&raw_meta_header[8]); /* flags are encoded in the high byte of length */ metaentry->flags = metaentry->length >> 24; @@ -2525,10 +3206,6 @@ static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) else err = CHDERR_NONE; - /* handle an error */ - if (err != CHDERR_NONE) - free(data); - return err; } @@ -2544,8 +3221,6 @@ static void zlib_codec_free(void *codec) /* deinit the streams */ if (data != NULL) { - int i; - inflateEnd(&data->inflater); /* free our fast memory */ @@ -2595,7 +3270,7 @@ static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) { zlib_allocator *alloc = (zlib_allocator *)opaque; uintptr_t paddr = 0; - UINT32 *ptr; + uint32_t *ptr; int i; /* compute the size, rounding to the nearest 1k */ @@ -2616,7 +3291,7 @@ static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) } /* alloc a new one */ - ptr = (UINT32 *)malloc(size + sizeof(UINT32) + ZLIB_MIN_ALIGNMENT_BYTES); + ptr = (uint32_t *)malloc(size + sizeof(uint32_t) + ZLIB_MIN_ALIGNMENT_BYTES); if (!ptr) return NULL; @@ -2625,7 +3300,7 @@ static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) if (!alloc->allocptr[i]) { alloc->allocptr[i] = ptr; - paddr = (((uintptr_t)ptr) + sizeof(UINT32) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1)); + paddr = (((uintptr_t)ptr) + sizeof(uint32_t) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1)); alloc->allocptr2[i] = (uint32_t*)paddr; break; } @@ -2645,7 +3320,7 @@ static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) static void zlib_fast_free(voidpf opaque, voidpf address) { zlib_allocator *alloc = (zlib_allocator *)opaque; - UINT32 *ptr = (UINT32 *)address; + uint32_t *ptr = (uint32_t *)address; int i; /* find the hunk */ @@ -2670,3 +3345,87 @@ static void zlib_allocator_free(voidpf opaque) if (alloc->allocptr[i]) free(alloc->allocptr[i]); } + +/*------------------------------------------------- + core_stdio_fopen - core_file wrapper over fopen +-------------------------------------------------*/ +static core_file *core_stdio_fopen(char const *path) { + core_file *file = malloc(sizeof(core_file)); + if (!file) + return NULL; + if (!(file->argp = fopen(path, "rb"))) { + free(file); + return NULL; + } + file->fsize = core_stdio_fsize; + file->fread = core_stdio_fread; + file->fclose = core_stdio_fclose; + file->fseek = core_stdio_fseek; + return file; +} + +/*------------------------------------------------- + core_stdio_fsize - core_file function for + getting file size with stdio +-------------------------------------------------*/ +static uint64_t core_stdio_fsize(core_file *file) { +#if defined USE_LIBRETRO_VFS + #define core_stdio_fseek_impl fseek + #define core_stdio_ftell_impl ftell +#elif defined(__WIN32__) || defined(_WIN32) || defined(WIN32) || defined(__WIN64__) + #define core_stdio_fseek_impl _fseeki64 + #define core_stdio_ftell_impl _ftelli64 +#elif defined(_LARGEFILE_SOURCE) && defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 + #define core_stdio_fseek_impl fseeko64 + #define core_stdio_ftell_impl ftello64 +#elif defined(__PS3__) && !defined(__PSL1GHT__) || defined(__SWITCH__) || defined(__vita__) + #define core_stdio_fseek_impl(x,y,z) fseek(x,(off_t)y,z) + #define core_stdio_ftell_impl(x) (off_t)ftell(x) +#else + #define core_stdio_fseek_impl fseeko + #define core_stdio_ftell_impl ftello +#endif + FILE *fp; + uint64_t p, rv; + fp = (FILE*)file->argp; + + p = core_stdio_ftell_impl(fp); + core_stdio_fseek_impl(fp, 0, SEEK_END); + rv = core_stdio_ftell_impl(fp); + core_stdio_fseek_impl(fp, p, SEEK_SET); + return rv; +} + +/*------------------------------------------------- + core_stdio_fread - core_file wrapper over fread +-------------------------------------------------*/ +static size_t core_stdio_fread(void *ptr, size_t size, size_t nmemb, core_file *file) { + return fread(ptr, size, nmemb, (FILE*)file->argp); +} + +/*------------------------------------------------- + core_stdio_fclose - core_file wrapper over fclose +-------------------------------------------------*/ +static int core_stdio_fclose(core_file *file) { + int err = fclose((FILE*)file->argp); + if (err == 0) + free(file); + return err; +} + +/*------------------------------------------------- + core_stdio_fclose_nonowner - don't call fclose because + we don't own the underlying file, but do free the + core_file because libchdr did allocate that itself. +-------------------------------------------------*/ +static int core_stdio_fclose_nonowner(core_file *file) { + free(file); + return 0; +} + +/*------------------------------------------------- + core_stdio_fseek - core_file wrapper over fclose +-------------------------------------------------*/ +static int core_stdio_fseek(core_file* file, int64_t offset, int whence) { + return core_stdio_fseek_impl((FILE*)file->argp, offset, whence); +}