Path: blob/a-new-beginning/SharedDependencies/Sources/libchdr/chd.c
2 views
/***************************************************************************12chd.c34MAME Compressed Hunks of Data file format56****************************************************************************78Copyright Aaron Giles9All rights reserved.1011Redistribution and use in source and binary forms, with or without12modification, are permitted provided that the following conditions are13met:1415* Redistributions of source code must retain the above copyright16notice, this list of conditions and the following disclaimer.17* Redistributions in binary form must reproduce the above copyright18notice, this list of conditions and the following disclaimer in19the documentation and/or other materials provided with the20distribution.21* Neither the name 'MAME' nor the names of its contributors may be22used to endorse or promote products derived from this software23without specific prior written permission.2425THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR26IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED27WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE28DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,29INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES30(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR31SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)32HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,33STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING34IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE35POSSIBILITY OF SUCH DAMAGE.3637***************************************************************************/3839#include <stddef.h>40#include <stdio.h>41#include <stdlib.h>42#include <string.h>43#include <time.h>44#include "chd.h"45#include "cdrom.h"46#include "flac.h"47#include "huffman.h"48#include <LzmaEnc.h>49#include <LzmaDec.h>50#include "zlib.h"5152#define TRUE 153#define FALSE 05455#define MAX(x, y) (((x) > (y)) ? (x) : (y))56#define MIN(x, y) (((x) < (y)) ? (x) : (y))5758#define SHA1_DIGEST_SIZE 205960typedef struct {61uint8_t data[64];62uint16_t datalen;63uint64_t bitlen;64uint16_t state[5];65uint16_t k[4];66} SHA1_CTX;6768typedef struct {69uint8_t data[64];70uint16_t datalen;71uint64_t bitlen;72uint16_t state[4];73} MD5_CTX;7475/***************************************************************************76DEBUGGING77***************************************************************************/7879#define PRINTF_MAX_HUNK (0)8081/***************************************************************************82CONSTANTS83***************************************************************************/8485#define MAP_STACK_ENTRIES 512 /* max number of entries to use on the stack */86#define MAP_ENTRY_SIZE 16 /* V3 and later */87#define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */88#define METADATA_HEADER_SIZE 16 /* metadata header size */89#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */9091#define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */92#define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */9394#define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */9596#define COOKIE_VALUE 0xbaadf00d97#define MAX_ZLIB_ALLOCS 649899#define END_OF_LIST_COOKIE "EndOfListCookie"100101#define NO_MATCH (~0)102103static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };104105/* V3-V4 entry types */106enum107{108V34_MAP_ENTRY_TYPE_INVALID = 0, /* invalid type */109V34_MAP_ENTRY_TYPE_COMPRESSED = 1, /* standard compression */110V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, /* uncompressed data */111V34_MAP_ENTRY_TYPE_MINI = 3, /* mini: use offset as raw data */112V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, /* same as another hunk in this file */113V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, /* same as a hunk in the parent file */114V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 /* compressed with secondary algorithm (usually FLAC CDDA) */115};116117/* V5 compression types */118enum119{120/* codec #0121* these types are live when running */122COMPRESSION_TYPE_0 = 0,123/* codec #1 */124COMPRESSION_TYPE_1 = 1,125/* codec #2 */126COMPRESSION_TYPE_2 = 2,127/* codec #3 */128COMPRESSION_TYPE_3 = 3,129/* no compression; implicit length = hunkbytes */130COMPRESSION_NONE = 4,131/* same as another block in this chd */132COMPRESSION_SELF = 5,133/* same as a hunk's worth of units in the parent chd */134COMPRESSION_PARENT = 6,135136/* start of small RLE run (4-bit length)137* these additional pseudo-types are used for compressed encodings: */138COMPRESSION_RLE_SMALL,139/* start of large RLE run (8-bit length) */140COMPRESSION_RLE_LARGE,141/* same as the last COMPRESSION_SELF block */142COMPRESSION_SELF_0,143/* same as the last COMPRESSION_SELF block + 1 */144COMPRESSION_SELF_1,145/* same block in the parent */146COMPRESSION_PARENT_SELF,147/* same as the last COMPRESSION_PARENT block */148COMPRESSION_PARENT_0,149/* same as the last COMPRESSION_PARENT block + 1 */150COMPRESSION_PARENT_1151};152153/***************************************************************************154MACROS155***************************************************************************/156157#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0)158159/***************************************************************************160TYPE DEFINITIONS161***************************************************************************/162163/* interface to a codec */164typedef struct _codec_interface codec_interface;165struct _codec_interface166{167UINT32 compression; /* type of compression */168const char *compname; /* name of the algorithm */169UINT8 lossy; /* is this a lossy algorithm? */170chd_error (*init)(void *codec, UINT32 hunkbytes); /* codec initialize */171void (*free)(void *codec); /* codec free */172chd_error (*decompress)(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen); /* decompress data */173chd_error (*config)(void *codec, int param, void *config); /* configure */174};175176/* a single map entry */177typedef struct _map_entry map_entry;178struct _map_entry179{180UINT64 offset; /* offset within the file of the data */181UINT32 crc; /* 32-bit CRC of the data */182UINT32 length; /* length of the data */183UINT8 flags; /* misc flags */184};185186/* simple linked-list of hunks used for our CRC map */187typedef struct _crcmap_entry crcmap_entry;188struct _crcmap_entry189{190UINT32 hunknum; /* hunk number */191crcmap_entry * next; /* next entry in list */192};193194/* a single metadata entry */195typedef struct _metadata_entry metadata_entry;196struct _metadata_entry197{198UINT64 offset; /* offset within the file of the header */199UINT64 next; /* offset within the file of the next header */200UINT64 prev; /* offset within the file of the previous header */201UINT32 length; /* length of the metadata */202UINT32 metatag; /* metadata tag */203UINT8 flags; /* flag bits */204};205206/* codec-private data for the ZLIB codec */207208typedef struct _zlib_allocator zlib_allocator;209struct _zlib_allocator210{211UINT32 * allocptr[MAX_ZLIB_ALLOCS];212};213214typedef struct _zlib_codec_data zlib_codec_data;215struct _zlib_codec_data216{217z_stream inflater;218zlib_allocator allocator;219};220221/* codec-private data for the LZMA codec */222#define MAX_LZMA_ALLOCS 64223224typedef struct _lzma_allocator lzma_allocator;225struct _lzma_allocator226{227void *(*Alloc)(void *p, size_t size);228void (*Free)(void *p, void *address); /* address can be 0 */229void (*FreeSz)(void *p, void *address, size_t size); /* address can be 0 */230uint32_t* allocptr[MAX_LZMA_ALLOCS];231};232233typedef struct _lzma_codec_data lzma_codec_data;234struct _lzma_codec_data235{236CLzmaDec decoder;237lzma_allocator allocator;238};239240/* codec-private data for the CDZL codec */241typedef struct _cdzl_codec_data cdzl_codec_data;242struct _cdzl_codec_data {243/* internal state */244zlib_codec_data base_decompressor;245zlib_codec_data subcode_decompressor;246uint8_t* buffer;247};248249/* codec-private data for the CDLZ codec */250typedef struct _cdlz_codec_data cdlz_codec_data;251struct _cdlz_codec_data {252/* internal state */253lzma_codec_data base_decompressor;254zlib_codec_data subcode_decompressor;255uint8_t* buffer;256};257258/* codec-private data for the CDFL codec */259typedef struct _cdfl_codec_data cdfl_codec_data;260struct _cdfl_codec_data {261/* internal state */262int swap_endian;263flac_decoder decoder;264z_stream inflater;265zlib_allocator allocator;266uint8_t* buffer;267};268269/* internal representation of an open CHD file */270struct _chd_file271{272UINT32 cookie; /* cookie, should equal COOKIE_VALUE */273274core_file * file; /* handle to the open core file */275UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */276chd_header header; /* header, extracted from file */277278chd_file * parent; /* pointer to parent file, or NULL */279280map_entry * map; /* array of map entries */281282UINT8 * cache; /* hunk cache pointer */283UINT32 cachehunk; /* index of currently cached hunk */284285UINT8 * compare; /* hunk compare pointer */286UINT32 comparehunk; /* index of current compare data */287288UINT8 * compressed; /* pointer to buffer for compressed data */289const codec_interface * codecintf[4]; /* interface to the codec */290291zlib_codec_data zlib_codec_data; /* zlib codec data */292cdzl_codec_data cdzl_codec_data; /* cdzl codec data */293cdlz_codec_data cdlz_codec_data; /* cdlz codec data */294cdfl_codec_data cdfl_codec_data; /* cdfl codec data */295296crcmap_entry * crcmap; /* CRC map entries */297crcmap_entry * crcfree; /* free list CRC entries */298crcmap_entry ** crctable; /* table of CRC entries */299300UINT32 maxhunk; /* maximum hunk accessed */301302UINT8 compressing; /* are we compressing? */303MD5_CTX compmd5; /* running MD5 during compression */304SHA1_CTX compsha1; /* running SHA1 during compression */305UINT32 comphunk; /* next hunk we will compress */306307UINT8 verifying; /* are we verifying? */308MD5_CTX vermd5; /* running MD5 during verification */309SHA1_CTX versha1; /* running SHA1 during verification */310UINT32 verhunk; /* next hunk we will verify */311312UINT32 async_hunknum; /* hunk index for asynchronous operations */313void * async_buffer; /* buffer pointer for asynchronous operations */314};315316/* a single metadata hash entry */317typedef struct _metadata_hash metadata_hash;318struct _metadata_hash319{320UINT8 tag[4]; /* tag of the metadata in big-endian */321UINT8 sha1[CHD_SHA1_BYTES]; /* hash */322};323324/***************************************************************************325GLOBAL VARIABLES326***************************************************************************/327328static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 };329static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 };330331/***************************************************************************332PROTOTYPES333***************************************************************************/334335/* internal header operations */336static chd_error header_validate(const chd_header *header);337static chd_error header_read(chd_file *chd, chd_header *header);338339/* internal hunk read/write */340static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum);341static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest);342343/* internal map access */344static chd_error map_read(chd_file *chd);345346/* metadata management */347static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry);348349/* zlib compression codec */350static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes);351static void zlib_codec_free(void *codec);352static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);353static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size);354static void zlib_fast_free(voidpf opaque, voidpf address);355356/* lzma compression codec */357static chd_error lzma_codec_init(void *codec, uint32_t hunkbytes);358static void lzma_codec_free(void *codec);359static chd_error lzma_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);360361/* cdzl compression codec */362static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes);363static void cdzl_codec_free(void* codec);364static chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);365366/* cdlz compression codec */367static chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes);368static void cdlz_codec_free(void* codec);369static chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);370371/* cdfl compression codec */372static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes);373static void cdfl_codec_free(void* codec);374static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);375376/***************************************************************************377* LZMA ALLOCATOR HELPER378***************************************************************************379*/380381void *lzma_fast_alloc(void *p, size_t size);382void lzma_fast_free(void *p, void *address);383384/*-------------------------------------------------385* lzma_allocator_init386*-------------------------------------------------387*/388389void lzma_allocator_init(void* p)390{391lzma_allocator *codec = (lzma_allocator *)(p);392393/* reset pointer list */394memset(codec->allocptr, 0, sizeof(codec->allocptr));395codec->Alloc = lzma_fast_alloc;396codec->Free = lzma_fast_free;397}398399/*-------------------------------------------------400* lzma_allocator_free401*-------------------------------------------------402*/403404void lzma_allocator_free(void* p )405{406lzma_allocator *codec = (lzma_allocator *)(p);407408/* free our memory */409for (int i = 0 ; i < MAX_LZMA_ALLOCS ; i++)410{411if (codec->allocptr[i] != NULL)412free(codec->allocptr[i]);413}414}415416/*-------------------------------------------------417* lzma_fast_alloc - fast malloc for lzma, which418* allocates and frees memory frequently419*-------------------------------------------------420*/421422void *lzma_fast_alloc(void *p, size_t size)423{424lzma_allocator *codec = (lzma_allocator *)(p);425426/* compute the size, rounding to the nearest 1k */427size = (size + 0x3ff) & ~0x3ff;428429/* reuse a hunk if we can */430for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++)431{432uint32_t *ptr = codec->allocptr[scan];433if (ptr != NULL && size == *ptr)434{435/* set the low bit of the size so we don't match next time */436*ptr |= 1;437return ptr + 1;438}439}440441/* alloc a new one and put it into the list */442uint32_t *addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t)));443if (addr==NULL)444return NULL;445for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++)446{447if (codec->allocptr[scan] == NULL)448{449codec->allocptr[scan] = addr;450break;451}452}453454/* set the low bit of the size so we don't match next time */455*addr = size | 1;456return addr + 1;457}458459/*-------------------------------------------------460* lzma_fast_free - fast free for lzma, which461* allocates and frees memory frequently462*-------------------------------------------------463*/464465void lzma_fast_free(void *p, void *address)466{467if (address == NULL)468return;469470lzma_allocator *codec = (lzma_allocator *)(p);471472/* find the hunk */473uint32_t *ptr = (uint32_t *)(address) - 1;474for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++)475{476if (ptr == codec->allocptr[scan])477{478/* clear the low bit of the size to allow matches */479*ptr &= ~1;480return;481}482}483}484485/***************************************************************************486* LZMA DECOMPRESSOR487***************************************************************************488*/489490/*-------------------------------------------------491* lzma_codec_init - constructor492*-------------------------------------------------493*/494495chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)496{497lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;498499/* construct the decoder */500LzmaDec_Construct(&lzma_codec->decoder);501502/* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK503* This code assumes that the current version of the encoder imposes the same requirements on the504* decoder as the encoder used to produce the file. This is not necessarily true. The format505* needs to be changed so the encoder properties are written to the file.506507* configure the properties like the compressor did */508CLzmaEncProps encoder_props;509LzmaEncProps_Init(&encoder_props);510encoder_props.level = 9;511encoder_props.reduceSize = hunkbytes;512LzmaEncProps_Normalize(&encoder_props);513514/* convert to decoder properties */515lzma_allocator* alloc = &lzma_codec->allocator;516lzma_allocator_init(alloc);517CLzmaEncHandle enc = LzmaEnc_Create((ISzAlloc*)alloc);518if (!enc)519return CHDERR_DECOMPRESSION_ERROR;520if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)521{522LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc);523return CHDERR_DECOMPRESSION_ERROR;524}525Byte decoder_props[LZMA_PROPS_SIZE];526SizeT props_size = sizeof(decoder_props);527if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)528{529LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);530return CHDERR_DECOMPRESSION_ERROR;531}532LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);533534/* do memory allocations */535if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK)536return CHDERR_DECOMPRESSION_ERROR;537538/* Okay */539return CHDERR_NONE;540}541542/*-------------------------------------------------543* lzma_codec_free544*-------------------------------------------------545*/546547void lzma_codec_free(void* codec)548{549lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;550551/* free memory */552LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator);553}554555/*-------------------------------------------------556* decompress - decompress data using the LZMA557* codec558*-------------------------------------------------559*/560561chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)562{563/* initialize */564lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;565LzmaDec_Init(&lzma_codec->decoder);566567/* decode */568SizeT consumedlen = complen;569SizeT decodedlen = destlen;570ELzmaStatus status;571SRes res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);572if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)573return CHDERR_DECOMPRESSION_ERROR;574return CHDERR_NONE;575}576577/* cdlz */578chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes)579{580cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;581582/* allocate buffer */583cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);584585/* make sure the CHD's hunk size is an even multiple of the frame size */586lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);587zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA);588589if (hunkbytes % CD_FRAME_SIZE != 0)590return CHDERR_CODEC_ERROR;591592return CHDERR_NONE;593}594595void cdlz_codec_free(void* codec)596{597/* TODO */598}599600chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)601{602uint8_t *sector;603cdlz_codec_data* cdlz = (cdlz_codec_data*)codec;604605/* determine header bytes */606uint32_t frames = destlen / CD_FRAME_SIZE;607uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;608uint32_t ecc_bytes = (frames + 7) / 8;609uint32_t header_bytes = ecc_bytes + complen_bytes;610611/* extract compressed length of base */612uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];613if (complen_bytes > 2)614complen_base = (complen_base << 8) | src[ecc_bytes + 2];615616/* reset and decode */617lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA);618zlib_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);619620/* reassemble the data */621for (uint32_t framenum = 0; framenum < frames; framenum++)622{623memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);624memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);625626/* reconstitute the ECC data and sync header */627sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];628if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)629{630memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));631ecc_generate(sector);632}633}634return CHDERR_NONE;635}636637/* cdzl */638639chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes)640{641cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;642643/* make sure the CHD's hunk size is an even multiple of the frame size */644zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);645zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA);646647cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);648if (hunkbytes % CD_FRAME_SIZE != 0)649return CHDERR_CODEC_ERROR;650651return CHDERR_NONE;652}653654void cdzl_codec_free(void *codec)655{656/* TODO */657}658659chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)660{661uint8_t *sector;662cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;663664/* determine header bytes */665uint32_t frames = destlen / CD_FRAME_SIZE;666uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;667uint32_t ecc_bytes = (frames + 7) / 8;668uint32_t header_bytes = ecc_bytes + complen_bytes;669670/* extract compressed length of base */671uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];672if (complen_bytes > 2)673complen_base = (complen_base << 8) | src[ecc_bytes + 2];674675/* reset and decode */676zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA);677zlib_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);678679/* reassemble the data */680for (uint32_t framenum = 0; framenum < frames; framenum++)681{682memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);683memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);684685/* reconstitute the ECC data and sync header */686sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];687if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)688{689memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));690ecc_generate(sector);691}692}693return CHDERR_NONE;694}695696/***************************************************************************697* CD FLAC DECOMPRESSOR698***************************************************************************699*/700701/*------------------------------------------------------702* cdfl_codec_blocksize - return the optimal block size703*------------------------------------------------------704*/705706static uint32_t cdfl_codec_blocksize(uint32_t bytes)707{708/* determine FLAC block size, which must be 16-65535709* clamp to 2k since that's supposed to be the sweet spot */710uint32_t hunkbytes = bytes / 4;711while (hunkbytes > 2048)712hunkbytes /= 2;713return hunkbytes;714}715716chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes)717{718cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;719720cdfl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);721722/* make sure the CHD's hunk size is an even multiple of the frame size */723if (hunkbytes % CD_FRAME_SIZE != 0)724return CHDERR_CODEC_ERROR;725726cdfl->swap_endian = 0;727728/* init the inflater */729cdfl->inflater.next_in = (Bytef *)cdfl; /* bogus, but that's ok */730cdfl->inflater.avail_in = 0;731#if 0732cdfl->allocator.install(cdfl->inflater);733#endif734cdfl->inflater.zalloc = zlib_fast_alloc;735cdfl->inflater.zfree = zlib_fast_free;736cdfl->inflater.opaque = &cdfl->allocator;737int zerr = inflateInit2(&cdfl->inflater, -MAX_WBITS);738739/* convert errors */740if (zerr == Z_MEM_ERROR)741return CHDERR_OUT_OF_MEMORY;742else if (zerr != Z_OK)743return CHDERR_CODEC_ERROR;744745/* flac decoder init */746flac_decoder_init(&cdfl->decoder);747return CHDERR_NONE;748}749750void cdfl_codec_free(void *codec)751{752cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;753inflateEnd(&cdfl->inflater);754}755756chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)757{758cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;759760/* reset and decode */761uint32_t frames = destlen / CD_FRAME_SIZE;762763if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen))764return CHDERR_DECOMPRESSION_ERROR;765uint8_t *buffer = &cdfl->buffer[0];766if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian))767return CHDERR_DECOMPRESSION_ERROR;768769/* inflate the subcode data */770uint32_t offset = flac_decoder_finish(&cdfl->decoder);771cdfl->inflater.next_in = (Bytef *)(src + offset);772cdfl->inflater.avail_in = complen - offset;773cdfl->inflater.total_in = 0;774cdfl->inflater.next_out = &cdfl->buffer[frames * CD_MAX_SECTOR_DATA];775cdfl->inflater.avail_out = frames * CD_MAX_SUBCODE_DATA;776cdfl->inflater.total_out = 0;777int zerr = inflateReset(&cdfl->inflater);778if (zerr != Z_OK)779return CHDERR_DECOMPRESSION_ERROR;780781/* do it */782zerr = inflate(&cdfl->inflater, Z_FINISH);783if (zerr != Z_STREAM_END)784return CHDERR_DECOMPRESSION_ERROR;785if (cdfl->inflater.total_out != frames * CD_MAX_SUBCODE_DATA)786return CHDERR_DECOMPRESSION_ERROR;787788/* reassemble the data */789for (uint32_t framenum = 0; framenum < frames; framenum++)790{791memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);792memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdfl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);793}794795return CHDERR_NONE;796}797/***************************************************************************798CODEC INTERFACES799***************************************************************************/800801static const codec_interface codec_interfaces[] =802{803/* "none" or no compression */804{805CHDCOMPRESSION_NONE,806"none",807FALSE,808NULL,809NULL,810NULL,811NULL812},813814/* standard zlib compression */815{816CHDCOMPRESSION_ZLIB,817"zlib",818FALSE,819zlib_codec_init,820zlib_codec_free,821zlib_codec_decompress,822NULL823},824825/* zlib+ compression */826{827CHDCOMPRESSION_ZLIB_PLUS,828"zlib+",829FALSE,830zlib_codec_init,831zlib_codec_free,832zlib_codec_decompress,833NULL834},835836/* V5 zlib compression */837{838CHD_CODEC_ZLIB,839"zlib (Deflate)",840FALSE,841zlib_codec_init,842zlib_codec_free,843zlib_codec_decompress,844NULL845},846847/* V5 CD zlib compression */848{849CHD_CODEC_CD_ZLIB,850"cdzl (CD Deflate)",851FALSE,852cdzl_codec_init,853cdzl_codec_free,854cdzl_codec_decompress,855NULL856},857858/* V5 CD lzma compression */859{860CHD_CODEC_CD_LZMA,861"cdlz (CD LZMA)",862FALSE,863cdlz_codec_init,864cdlz_codec_free,865cdlz_codec_decompress,866NULL867},868869/* V5 CD flac compression */870{871CHD_CODEC_CD_FLAC,872"cdfl (CD FLAC)",873FALSE,874cdfl_codec_init,875cdfl_codec_free,876cdfl_codec_decompress,877NULL878},879};880881/***************************************************************************882INLINE FUNCTIONS883***************************************************************************/884885/*-------------------------------------------------886get_bigendian_uint64 - fetch a UINT64 from887the data stream in bigendian order888-------------------------------------------------*/889890static inline UINT64 get_bigendian_uint64(const UINT8 *base)891{892return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) |893((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7];894}895896/*-------------------------------------------------897put_bigendian_uint64 - write a UINT64 to898the data stream in bigendian order899-------------------------------------------------*/900901static inline void put_bigendian_uint64(UINT8 *base, UINT64 value)902{903base[0] = value >> 56;904base[1] = value >> 48;905base[2] = value >> 40;906base[3] = value >> 32;907base[4] = value >> 24;908base[5] = value >> 16;909base[6] = value >> 8;910base[7] = value;911}912913/*-------------------------------------------------914get_bigendian_uint48 - fetch a UINT48 from915the data stream in bigendian order916-------------------------------------------------*/917918static inline UINT64 get_bigendian_uint48(const UINT8 *base)919{920return ((UINT64)base[0] << 40) | ((UINT64)base[1] << 32) |921((UINT64)base[2] << 24) | ((UINT64)base[3] << 16) | ((UINT64)base[4] << 8) | (UINT64)base[5];922}923924/*-------------------------------------------------925put_bigendian_uint48 - write a UINT48 to926the data stream in bigendian order927-------------------------------------------------*/928929static inline void put_bigendian_uint48(UINT8 *base, UINT64 value)930{931value &= 0xffffffffffff;932base[0] = value >> 40;933base[1] = value >> 32;934base[2] = value >> 24;935base[3] = value >> 16;936base[4] = value >> 8;937base[5] = value;938}939/*-------------------------------------------------940get_bigendian_uint32 - fetch a UINT32 from941the data stream in bigendian order942-------------------------------------------------*/943944static inline UINT32 get_bigendian_uint32(const UINT8 *base)945{946return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3];947}948949/*-------------------------------------------------950put_bigendian_uint32 - write a UINT32 to951the data stream in bigendian order952-------------------------------------------------*/953954static inline void put_bigendian_uint32(UINT8 *base, UINT32 value)955{956base[0] = value >> 24;957base[1] = value >> 16;958base[2] = value >> 8;959base[3] = value;960}961962/*-------------------------------------------------963put_bigendian_uint24 - write a UINT24 to964the data stream in bigendian order965-------------------------------------------------*/966967static inline void put_bigendian_uint24(UINT8 *base, UINT32 value)968{969value &= 0xffffff;970base[0] = value >> 16;971base[1] = value >> 8;972base[2] = value;973}974975/*-------------------------------------------------976get_bigendian_uint24 - fetch a UINT24 from977the data stream in bigendian order978-------------------------------------------------*/979980static inline UINT32 get_bigendian_uint24(const UINT8 *base)981{982return (base[0] << 16) | (base[1] << 8) | base[2];983}984985/*-------------------------------------------------986get_bigendian_uint16 - fetch a UINT16 from987the data stream in bigendian order988-------------------------------------------------*/989990static inline UINT16 get_bigendian_uint16(const UINT8 *base)991{992return (base[0] << 8) | base[1];993}994995/*-------------------------------------------------996put_bigendian_uint16 - write a UINT16 to997the data stream in bigendian order998-------------------------------------------------*/9991000static inline void put_bigendian_uint16(UINT8 *base, UINT16 value)1001{1002base[0] = value >> 8;1003base[1] = value;1004}10051006/*-------------------------------------------------1007map_extract - extract a single map1008entry from the datastream1009-------------------------------------------------*/10101011static inline void map_extract(const UINT8 *base, map_entry *entry)1012{1013entry->offset = get_bigendian_uint64(&base[0]);1014entry->crc = get_bigendian_uint32(&base[8]);1015entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16);1016entry->flags = base[15];1017}10181019/*-------------------------------------------------1020map_assemble - write a single map1021entry to the datastream1022-------------------------------------------------*/10231024static inline void map_assemble(UINT8 *base, map_entry *entry)1025{1026put_bigendian_uint64(&base[0], entry->offset);1027put_bigendian_uint32(&base[8], entry->crc);1028put_bigendian_uint16(&base[12], entry->length);1029base[14] = entry->length >> 16;1030base[15] = entry->flags;1031}10321033/*-------------------------------------------------1034map_size_v5 - calculate CHDv5 map size1035-------------------------------------------------*/1036static inline int map_size_v5(chd_header* header)1037{1038return header->hunkcount * header->mapentrybytes;1039}10401041/*-------------------------------------------------1042crc16 - calculate CRC16 (from hashing.cpp)1043-------------------------------------------------*/1044uint16_t crc16(const void *data, uint32_t length)1045{1046uint16_t crc = 0xffff;10471048static const uint16_t s_table[256] =1049{10500x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,10510x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,10520x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,10530x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,10540x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,10550xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,10560x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,10570xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,10580x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,10590xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,10600x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,10610xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,10620x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,10630xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,10640x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,10650xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,10660x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,10670x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,10680x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,10690x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,10700xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,10710x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,10720xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,10730x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,10740xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,10750x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,10760xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,10770x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,10780xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,10790x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,10800xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,10810x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef01082};10831084const uint8_t *src = (uint8_t*)data;10851086/* fetch the current value into a local and rip through the source data */1087while (length-- != 0)1088crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++];1089return crc;1090}10911092/*-------------------------------------------------1093compressed - test if CHD file is compressed1094+-------------------------------------------------*/10951096static inline int compressed(chd_header* header) {1097return header->compression[0] != CHD_CODEC_NONE;1098}10991100/*-------------------------------------------------1101decompress_v5_map - decompress the v5 map1102-------------------------------------------------*/11031104static chd_error decompress_v5_map(chd_file* chd, chd_header* header)1105{1106int rawmapsize = map_size_v5(header);11071108if (!compressed(header))1109{1110header->rawmap = (uint8_t*)malloc(rawmapsize);1111core_fseek(chd->file, header->mapoffset, SEEK_SET);1112core_fread(chd->file, header->rawmap, rawmapsize);1113return CHDERR_NONE;1114}11151116/* read the reader */1117uint8_t rawbuf[16];1118core_fseek(chd->file, header->mapoffset, SEEK_SET);1119core_fread(chd->file, rawbuf, sizeof(rawbuf));1120uint32_t const mapbytes = get_bigendian_uint32(&rawbuf[0]);1121uint64_t const firstoffs = get_bigendian_uint48(&rawbuf[4]);1122uint16_t const mapcrc = get_bigendian_uint16(&rawbuf[10]);1123uint8_t const lengthbits = rawbuf[12];1124uint8_t const selfbits = rawbuf[13];1125uint8_t const parentbits = rawbuf[14];11261127/* now read the map */1128uint8_t* compressed = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes);1129core_fseek(chd->file, header->mapoffset + 16, SEEK_SET);1130core_fread(chd->file, compressed, mapbytes);1131struct bitstream* bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes);1132header->rawmap = (uint8_t*)malloc(rawmapsize);11331134/* first decode the compression types */1135struct huffman_decoder* decoder = create_huffman_decoder(16, 8);1136enum huffman_error err = huffman_import_tree_rle(decoder, bitbuf);1137if (err != HUFFERR_NONE)1138return CHDERR_DECOMPRESSION_ERROR;1139uint8_t lastcomp = 0;1140int repcount = 0;1141for (int hunknum = 0; hunknum < header->hunkcount; hunknum++)1142{1143uint8_t *rawmap = header->rawmap + (hunknum * 12);1144if (repcount > 0)1145rawmap[0] = lastcomp, repcount--;1146else1147{1148uint8_t val = huffman_decode_one(decoder, bitbuf);1149if (val == COMPRESSION_RLE_SMALL)1150rawmap[0] = lastcomp, repcount = 2 + huffman_decode_one(decoder, bitbuf);1151else if (val == COMPRESSION_RLE_LARGE)1152rawmap[0] = lastcomp, repcount = 2 + 16 + (huffman_decode_one(decoder, bitbuf) << 4), repcount += huffman_decode_one(decoder, bitbuf);1153else1154rawmap[0] = lastcomp = val;1155}1156}11571158/* then iterate through the hunks and extract the needed data */1159uint64_t curoffset = firstoffs;1160uint32_t last_self = 0;1161uint64_t last_parent = 0;1162for (int hunknum = 0; hunknum < header->hunkcount; hunknum++)1163{1164uint8_t *rawmap = header->rawmap + (hunknum * 12);1165uint64_t offset = curoffset;1166uint32_t length = 0;1167uint16_t crc = 0;1168switch (rawmap[0])1169{1170/* base types */1171case COMPRESSION_TYPE_0:1172case COMPRESSION_TYPE_1:1173case COMPRESSION_TYPE_2:1174case COMPRESSION_TYPE_3:1175curoffset += length = bitstream_read(bitbuf, lengthbits);1176crc = bitstream_read(bitbuf, 16);1177break;11781179case COMPRESSION_NONE:1180curoffset += length = header->hunkbytes;1181crc = bitstream_read(bitbuf, 16);1182break;11831184case COMPRESSION_SELF:1185last_self = offset = bitstream_read(bitbuf, selfbits);1186break;11871188case COMPRESSION_PARENT:1189offset = bitstream_read(bitbuf, parentbits);1190last_parent = offset;1191break;11921193/* pseudo-types; convert into base types */1194case COMPRESSION_SELF_1:1195last_self++;1196case COMPRESSION_SELF_0:1197rawmap[0] = COMPRESSION_SELF;1198offset = last_self;1199break;12001201case COMPRESSION_PARENT_SELF:1202rawmap[0] = COMPRESSION_PARENT;1203last_parent = offset = ( ((uint64_t)hunknum) * ((uint64_t)header->hunkbytes) ) / header->unitbytes;1204break;12051206case COMPRESSION_PARENT_1:1207last_parent += header->hunkbytes / header->unitbytes;1208case COMPRESSION_PARENT_0:1209rawmap[0] = COMPRESSION_PARENT;1210offset = last_parent;1211break;1212}1213/* UINT24 length */1214put_bigendian_uint24(&rawmap[1], length);12151216/* UINT48 offset */1217put_bigendian_uint48(&rawmap[4], offset);12181219/* crc16 */1220put_bigendian_uint16(&rawmap[10], crc);1221}12221223/* verify the final CRC */1224if (crc16(&header->rawmap[0], header->hunkcount * 12) != mapcrc)1225return CHDERR_DECOMPRESSION_ERROR;12261227return CHDERR_NONE;1228}12291230/*-------------------------------------------------1231map_extract_old - extract a single map1232entry in old format from the datastream1233-------------------------------------------------*/12341235static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes)1236{1237entry->offset = get_bigendian_uint64(&base[0]);1238entry->crc = 0;1239entry->length = entry->offset >> 44;1240entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? V34_MAP_ENTRY_TYPE_UNCOMPRESSED : V34_MAP_ENTRY_TYPE_COMPRESSED);1241#ifdef __MWERKS__1242entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL;1243#else1244entry->offset = (entry->offset << 20) >> 20;1245#endif1246}12471248/***************************************************************************1249CHD FILE MANAGEMENT1250***************************************************************************/12511252/*-------------------------------------------------1253chd_open_file - open a CHD file for access1254-------------------------------------------------*/12551256chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd)1257{1258chd_file *newchd = NULL;1259chd_error err;1260int intfnum;12611262/* verify parameters */1263if (file == NULL)1264EARLY_EXIT(err = CHDERR_INVALID_PARAMETER);12651266/* punt if invalid parent */1267if (parent != NULL && parent->cookie != COOKIE_VALUE)1268EARLY_EXIT(err = CHDERR_INVALID_PARAMETER);12691270/* allocate memory for the final result */1271newchd = (chd_file *)malloc(sizeof(**chd));1272if (newchd == NULL)1273EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY);1274memset(newchd, 0, sizeof(*newchd));1275newchd->cookie = COOKIE_VALUE;1276newchd->parent = parent;1277newchd->file = file;12781279/* now attempt to read the header */1280err = header_read(newchd, &newchd->header);1281if (err != CHDERR_NONE)1282EARLY_EXIT(err);12831284/* validate the header */1285err = header_validate(&newchd->header);1286if (err != CHDERR_NONE)1287EARLY_EXIT(err);12881289/* make sure we don't open a read-only file writeable */1290if (mode == CHD_OPEN_READWRITE && !(newchd->header.flags & CHDFLAGS_IS_WRITEABLE))1291EARLY_EXIT(err = CHDERR_FILE_NOT_WRITEABLE);12921293/* also, never open an older version writeable */1294if (mode == CHD_OPEN_READWRITE && newchd->header.version < CHD_HEADER_VERSION)1295EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION);12961297/* if we need a parent, make sure we have one */1298if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT))1299EARLY_EXIT(err = CHDERR_REQUIRES_PARENT);13001301/* make sure we have a valid parent */1302if (parent != NULL)1303{1304/* check MD5 if it isn't empty */1305if (memcmp(nullmd5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0 &&1306memcmp(nullmd5, newchd->parent->header.md5, sizeof(newchd->parent->header.md5)) != 0 &&1307memcmp(newchd->parent->header.md5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0)1308EARLY_EXIT(err = CHDERR_INVALID_PARENT);13091310/* check SHA1 if it isn't empty */1311if (memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0 &&1312memcmp(nullsha1, newchd->parent->header.sha1, sizeof(newchd->parent->header.sha1)) != 0 &&1313memcmp(newchd->parent->header.sha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0)1314EARLY_EXIT(err = CHDERR_INVALID_PARENT);1315}13161317/* now read the hunk map */1318if (newchd->header.version < 5)1319{1320err = map_read(newchd);1321}1322else1323{1324err = decompress_v5_map(newchd, &(newchd->header));1325}1326if (err != CHDERR_NONE)1327EARLY_EXIT(err);132813291330/* allocate and init the hunk cache */1331newchd->cache = (UINT8 *)malloc(newchd->header.hunkbytes);1332newchd->compare = (UINT8 *)malloc(newchd->header.hunkbytes);1333if (newchd->cache == NULL || newchd->compare == NULL)1334EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY);1335newchd->cachehunk = ~0;1336newchd->comparehunk = ~0;13371338/* allocate the temporary compressed buffer */1339newchd->compressed = (UINT8 *)malloc(newchd->header.hunkbytes);1340if (newchd->compressed == NULL)1341EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY);13421343/* find the codec interface */1344if (newchd->header.version < 5)1345{1346for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++)1347{1348if (codec_interfaces[intfnum].compression == newchd->header.compression[0])1349{1350newchd->codecintf[0] = &codec_interfaces[intfnum];1351break;1352}1353}13541355if (intfnum == ARRAY_LENGTH(codec_interfaces))1356EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT);13571358/* initialize the codec */1359if (newchd->codecintf[0]->init != NULL)1360{1361err = (*newchd->codecintf[0]->init)(&newchd->zlib_codec_data, newchd->header.hunkbytes);1362if (err != CHDERR_NONE)1363EARLY_EXIT(err);1364}1365}1366else1367{1368/* verify the compression types and initialize the codecs */1369for (int decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++)1370{1371for (int i = 0 ; i < ARRAY_LENGTH(codec_interfaces) ; i++)1372{1373if (codec_interfaces[i].compression == newchd->header.compression[decompnum])1374{1375newchd->codecintf[decompnum] = &codec_interfaces[i];1376break;1377}1378}13791380if (newchd->codecintf[decompnum] == NULL && newchd->header.compression[decompnum] != 0)1381EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT);13821383/* initialize the codec */1384if (newchd->codecintf[decompnum]->init != NULL)1385{1386void* codec = NULL;1387switch (newchd->header.compression[decompnum])1388{1389case CHD_CODEC_ZLIB:1390codec = &newchd->zlib_codec_data;1391break;13921393case CHD_CODEC_CD_ZLIB:1394codec = &newchd->cdzl_codec_data;1395break;13961397case CHD_CODEC_CD_LZMA:1398codec = &newchd->cdlz_codec_data;1399break;14001401case CHD_CODEC_CD_FLAC:1402codec = &newchd->cdfl_codec_data;1403break;1404}14051406if (codec == NULL)1407EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT);14081409err = (*newchd->codecintf[decompnum]->init)(codec, newchd->header.hunkbytes);1410if (err != CHDERR_NONE)1411EARLY_EXIT(err);1412}1413}1414}14151416/* all done */1417*chd = newchd;1418return CHDERR_NONE;14191420cleanup:1421if (newchd != NULL)1422chd_close(newchd);1423return err;1424}14251426/*-------------------------------------------------1427chd_open - open a CHD file by1428filename1429-------------------------------------------------*/14301431chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd)1432{1433chd_error err;1434core_file *file = NULL;1435UINT32 openflags;14361437/* choose the proper mode */1438switch(mode)1439{1440case CHD_OPEN_READ:1441break;14421443default:1444err = CHDERR_INVALID_PARAMETER;1445goto cleanup;1446}14471448/* open the file */1449file = core_fopen(filename);1450if (file == 0)1451{1452err = CHDERR_FILE_NOT_FOUND;1453goto cleanup;1454}14551456/* now open the CHD */1457err = chd_open_file(file, mode, parent, chd);1458if (err != CHDERR_NONE)1459goto cleanup;14601461/* we now own this file */1462(*chd)->owns_file = TRUE;14631464cleanup:1465if ((err != CHDERR_NONE) && (file != NULL))1466core_fclose(file);1467return err;1468}14691470/*-------------------------------------------------1471chd_close - close a CHD file for access1472-------------------------------------------------*/14731474void chd_close(chd_file *chd)1475{1476/* punt if NULL or invalid */1477if (chd == NULL || chd->cookie != COOKIE_VALUE)1478return;14791480/* deinit the codec */1481if (chd->header.version < 5)1482{1483if (chd->codecintf[0] != NULL && chd->codecintf[0]->free != NULL)1484(*chd->codecintf[0]->free)(&chd->zlib_codec_data);1485}1486else1487{1488/* Free the codecs */1489for (int i = 0 ; i < ARRAY_LENGTH(chd->codecintf); i++)1490{1491void* codec = NULL;14921493if (chd->codecintf[i] == NULL)1494continue;14951496switch (chd->codecintf[i]->compression)1497{1498case CHD_CODEC_CD_LZMA:1499codec = &chd->cdlz_codec_data;1500break;15011502case CHD_CODEC_ZLIB:1503codec = &chd->zlib_codec_data;1504break;15051506case CHD_CODEC_CD_ZLIB:1507codec = &chd->cdzl_codec_data;1508break;15091510case CHD_CODEC_CD_FLAC:1511codec = &chd->cdfl_codec_data;1512break;1513}15141515if (codec)1516{1517(*chd->codecintf[i]->free)(codec);1518}1519}15201521/* Free the raw map */1522if (chd->header.rawmap != NULL)1523free(chd->header.rawmap);1524}15251526/* free the compressed data buffer */1527if (chd->compressed != NULL)1528free(chd->compressed);15291530/* free the hunk cache and compare data */1531if (chd->compare != NULL)1532free(chd->compare);1533if (chd->cache != NULL)1534free(chd->cache);15351536/* free the hunk map */1537if (chd->map != NULL)1538free(chd->map);15391540/* free the CRC table */1541if (chd->crctable != NULL)1542free(chd->crctable);15431544/* free the CRC map */1545if (chd->crcmap != NULL)1546free(chd->crcmap);15471548/* close the file */1549if (chd->owns_file && chd->file != NULL)1550core_fclose(chd->file);15511552if (PRINTF_MAX_HUNK) printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks);15531554/* free our memory */1555free(chd);1556}15571558/*-------------------------------------------------1559chd_core_file - return the associated1560core_file1561-------------------------------------------------*/15621563core_file *chd_core_file(chd_file *chd)1564{1565return chd->file;1566}15671568/*-------------------------------------------------1569chd_error_string - return an error string for1570the given CHD error1571-------------------------------------------------*/15721573const char *chd_error_string(chd_error err)1574{1575switch (err)1576{1577case CHDERR_NONE: return "no error";1578case CHDERR_NO_INTERFACE: return "no drive interface";1579case CHDERR_OUT_OF_MEMORY: return "out of memory";1580case CHDERR_INVALID_FILE: return "invalid file";1581case CHDERR_INVALID_PARAMETER: return "invalid parameter";1582case CHDERR_INVALID_DATA: return "invalid data";1583case CHDERR_FILE_NOT_FOUND: return "file not found";1584case CHDERR_REQUIRES_PARENT: return "requires parent";1585case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable";1586case CHDERR_READ_ERROR: return "read error";1587case CHDERR_WRITE_ERROR: return "write error";1588case CHDERR_CODEC_ERROR: return "codec error";1589case CHDERR_INVALID_PARENT: return "invalid parent";1590case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range";1591case CHDERR_DECOMPRESSION_ERROR: return "decompression error";1592case CHDERR_COMPRESSION_ERROR: return "compression error";1593case CHDERR_CANT_CREATE_FILE: return "can't create file";1594case CHDERR_CANT_VERIFY: return "can't verify file";1595case CHDERR_NOT_SUPPORTED: return "operation not supported";1596case CHDERR_METADATA_NOT_FOUND: return "can't find metadata";1597case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size";1598case CHDERR_UNSUPPORTED_VERSION: return "unsupported CHD version";1599case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify";1600case CHDERR_INVALID_METADATA: return "invalid metadata";1601case CHDERR_INVALID_STATE: return "invalid state";1602case CHDERR_OPERATION_PENDING: return "operation pending";1603case CHDERR_NO_ASYNC_OPERATION: return "no async operation in progress";1604case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format";1605default: return "undocumented error";1606}1607}16081609/***************************************************************************1610CHD HEADER MANAGEMENT1611***************************************************************************/16121613/*-------------------------------------------------1614chd_get_header - return a pointer to the1615extracted header data1616-------------------------------------------------*/16171618const chd_header *chd_get_header(chd_file *chd)1619{1620/* punt if NULL or invalid */1621if (chd == NULL || chd->cookie != COOKIE_VALUE)1622return NULL;16231624return &chd->header;1625}16261627/***************************************************************************1628CORE DATA READ/WRITE1629***************************************************************************/16301631/*-------------------------------------------------1632chd_read - read a single hunk from the CHD1633file1634-------------------------------------------------*/16351636chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer)1637{1638/* punt if NULL or invalid */1639if (chd == NULL || chd->cookie != COOKIE_VALUE)1640return CHDERR_INVALID_PARAMETER;16411642/* if we're past the end, fail */1643if (hunknum >= chd->header.totalhunks)1644return CHDERR_HUNK_OUT_OF_RANGE;16451646/* perform the read */1647return hunk_read_into_memory(chd, hunknum, (UINT8 *)buffer);1648}16491650/***************************************************************************1651METADATA MANAGEMENT1652***************************************************************************/16531654/*-------------------------------------------------1655chd_get_metadata - get the indexed metadata1656of the given type1657-------------------------------------------------*/16581659chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag, UINT8 *resultflags)1660{1661metadata_entry metaentry;1662chd_error err;1663UINT32 count;16641665/* if we didn't find it, just return */1666err = metadata_find_entry(chd, searchtag, searchindex, &metaentry);1667if (err != CHDERR_NONE)1668{1669/* unless we're an old version and they are requesting hard disk metadata */1670if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0)1671{1672char faux_metadata[256];1673UINT32 faux_length;16741675/* fill in the faux metadata */1676sprintf(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);1677faux_length = (UINT32)strlen(faux_metadata) + 1;16781679/* copy the metadata itself */1680memcpy(output, faux_metadata, MIN(outputlen, faux_length));16811682/* return the length of the data and the tag */1683if (resultlen != NULL)1684*resultlen = faux_length;1685if (resulttag != NULL)1686*resulttag = HARD_DISK_METADATA_TAG;1687return CHDERR_NONE;1688}1689return err;1690}16911692/* read the metadata */1693outputlen = MIN(outputlen, metaentry.length);1694core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET);1695count = core_fread(chd->file, output, outputlen);1696if (count != outputlen)1697return CHDERR_READ_ERROR;16981699/* return the length of the data and the tag */1700if (resultlen != NULL)1701*resultlen = metaentry.length;1702if (resulttag != NULL)1703*resulttag = metaentry.metatag;1704if (resultflags != NULL)1705*resultflags = metaentry.flags;1706return CHDERR_NONE;1707}17081709/***************************************************************************1710CODEC INTERFACES1711***************************************************************************/17121713/*-------------------------------------------------1714chd_codec_config - set internal codec1715parameters1716-------------------------------------------------*/17171718chd_error chd_codec_config(chd_file *chd, int param, void *config)1719{1720return CHDERR_INVALID_PARAMETER;1721}17221723/*-------------------------------------------------1724chd_get_codec_name - get the name of a1725particular codec1726-------------------------------------------------*/17271728const char *chd_get_codec_name(UINT32 codec)1729{1730return "Unknown";1731}17321733/***************************************************************************1734INTERNAL HEADER OPERATIONS1735***************************************************************************/17361737/*-------------------------------------------------1738header_validate - check the validity of a1739CHD header1740-------------------------------------------------*/17411742static chd_error header_validate(const chd_header *header)1743{1744int intfnum;17451746/* require a valid version */1747if (header->version == 0 || header->version > CHD_HEADER_VERSION)1748return CHDERR_UNSUPPORTED_VERSION;17491750/* require a valid length */1751if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) ||1752(header->version == 2 && header->length != CHD_V2_HEADER_SIZE) ||1753(header->version == 3 && header->length != CHD_V3_HEADER_SIZE) ||1754(header->version == 4 && header->length != CHD_V4_HEADER_SIZE) ||1755(header->version == 5 && header->length != CHD_V5_HEADER_SIZE))1756return CHDERR_INVALID_PARAMETER;17571758/* Do not validate v5 header */1759if (header->version <= 4)1760{1761/* require valid flags */1762if (header->flags & CHDFLAGS_UNDEFINED)1763return CHDERR_INVALID_PARAMETER;17641765/* require a supported compression mechanism */1766for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++)1767if (codec_interfaces[intfnum].compression == header->compression[0])1768break;17691770if (intfnum == ARRAY_LENGTH(codec_interfaces))1771return CHDERR_INVALID_PARAMETER;17721773/* require a valid hunksize */1774if (header->hunkbytes == 0 || header->hunkbytes >= 65536 * 256)1775return CHDERR_INVALID_PARAMETER;17761777/* require a valid hunk count */1778if (header->totalhunks == 0)1779return CHDERR_INVALID_PARAMETER;17801781/* require a valid MD5 and/or SHA1 if we're using a parent */1782if ((header->flags & CHDFLAGS_HAS_PARENT) && memcmp(header->parentmd5, nullmd5, sizeof(nullmd5)) == 0 && memcmp(header->parentsha1, nullsha1, sizeof(nullsha1)) == 0)1783return CHDERR_INVALID_PARAMETER;17841785/* if we're V3 or later, the obsolete fields must be 0 */1786if (header->version >= 3 &&1787(header->obsolete_cylinders != 0 || header->obsolete_sectors != 0 ||1788header->obsolete_heads != 0 || header->obsolete_hunksize != 0))1789return CHDERR_INVALID_PARAMETER;17901791/* if we're pre-V3, the obsolete fields must NOT be 0 */1792if (header->version < 3 &&1793(header->obsolete_cylinders == 0 || header->obsolete_sectors == 0 ||1794header->obsolete_heads == 0 || header->obsolete_hunksize == 0))1795return CHDERR_INVALID_PARAMETER;1796}17971798return CHDERR_NONE;1799}18001801/*-------------------------------------------------1802header_guess_unitbytes - for older CHD formats,1803guess at the bytes/unit based on metadata1804-------------------------------------------------*/18051806static UINT32 header_guess_unitbytes(chd_file *chd)1807{1808/* look for hard disk metadata; if found, then the unit size == sector size */1809char metadata[512];1810int i0, i1, i2, i3;1811if (chd_get_metadata(chd, HARD_DISK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE &&1812sscanf(metadata, HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4)1813return i3;18141815/* look for CD-ROM metadata; if found, then the unit size == CD frame size */1816if (chd_get_metadata(chd, CDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||1817chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||1818chd_get_metadata(chd, CDROM_TRACK_METADATA2_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||1819chd_get_metadata(chd, GDROM_OLD_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE ||1820chd_get_metadata(chd, GDROM_TRACK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL, NULL) == CHDERR_NONE)1821return CD_FRAME_SIZE;18221823/* otherwise, just map 1:1 with the hunk size */1824return chd->header.hunkbytes;1825}18261827/*-------------------------------------------------1828header_read - read a CHD header into the1829internal data structure1830-------------------------------------------------*/18311832static chd_error header_read(chd_file *chd, chd_header *header)1833{1834UINT8 rawheader[CHD_MAX_HEADER_SIZE];1835UINT32 count;18361837/* punt if NULL */1838if (header == NULL)1839return CHDERR_INVALID_PARAMETER;18401841/* punt if invalid file */1842if (chd->file == NULL)1843return CHDERR_INVALID_FILE;18441845/* seek and read */1846core_fseek(chd->file, 0, SEEK_SET);1847count = core_fread(chd->file, rawheader, sizeof(rawheader));1848if (count != sizeof(rawheader))1849return CHDERR_READ_ERROR;18501851/* verify the tag */1852if (strncmp((char *)rawheader, "MComprHD", 8) != 0)1853return CHDERR_INVALID_DATA;18541855/* extract the direct data */1856memset(header, 0, sizeof(*header));1857header->length = get_bigendian_uint32(&rawheader[8]);1858header->version = get_bigendian_uint32(&rawheader[12]);18591860/* make sure it's a version we understand */1861if (header->version == 0 || header->version > CHD_HEADER_VERSION)1862return CHDERR_UNSUPPORTED_VERSION;18631864/* make sure the length is expected */1865if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) ||1866(header->version == 2 && header->length != CHD_V2_HEADER_SIZE) ||1867(header->version == 3 && header->length != CHD_V3_HEADER_SIZE) ||1868(header->version == 4 && header->length != CHD_V4_HEADER_SIZE) ||1869(header->version == 5 && header->length != CHD_V5_HEADER_SIZE))18701871return CHDERR_INVALID_DATA;18721873/* extract the common data */1874header->flags = get_bigendian_uint32(&rawheader[16]);1875header->compression[0] = get_bigendian_uint32(&rawheader[20]);1876header->compression[1] = CHD_CODEC_NONE;1877header->compression[2] = CHD_CODEC_NONE;1878header->compression[3] = CHD_CODEC_NONE;18791880/* extract the V1/V2-specific data */1881if (header->version < 3)1882{1883int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]);1884header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]);1885header->totalhunks = get_bigendian_uint32(&rawheader[28]);1886header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]);1887header->obsolete_heads = get_bigendian_uint32(&rawheader[36]);1888header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]);1889memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES);1890memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES);1891header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen;1892header->hunkbytes = seclen * header->obsolete_hunksize;1893header->unitbytes = header_guess_unitbytes(chd);1894header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes;1895header->metaoffset = 0;1896}18971898/* extract the V3-specific data */1899else if (header->version == 3)1900{1901header->totalhunks = get_bigendian_uint32(&rawheader[24]);1902header->logicalbytes = get_bigendian_uint64(&rawheader[28]);1903header->metaoffset = get_bigendian_uint64(&rawheader[36]);1904memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES);1905memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES);1906header->hunkbytes = get_bigendian_uint32(&rawheader[76]);1907header->unitbytes = header_guess_unitbytes(chd);1908header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes;1909memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES);1910memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES);1911}19121913/* extract the V4-specific data */1914else if (header->version == 4)1915{1916header->totalhunks = get_bigendian_uint32(&rawheader[24]);1917header->logicalbytes = get_bigendian_uint64(&rawheader[28]);1918header->metaoffset = get_bigendian_uint64(&rawheader[36]);1919header->hunkbytes = get_bigendian_uint32(&rawheader[44]);1920header->unitbytes = header_guess_unitbytes(chd);1921header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes;1922memcpy(header->sha1, &rawheader[48], CHD_SHA1_BYTES);1923memcpy(header->parentsha1, &rawheader[68], CHD_SHA1_BYTES);1924memcpy(header->rawsha1, &rawheader[88], CHD_SHA1_BYTES);1925}19261927/* extract the V5-specific data */1928else if (header->version == 5)1929{1930/* TODO */1931header->compression[0] = get_bigendian_uint32(&rawheader[16]);1932header->compression[1] = get_bigendian_uint32(&rawheader[20]);1933header->compression[2] = get_bigendian_uint32(&rawheader[24]);1934header->compression[3] = get_bigendian_uint32(&rawheader[28]);1935header->logicalbytes = get_bigendian_uint64(&rawheader[32]);1936header->mapoffset = get_bigendian_uint64(&rawheader[40]);1937header->metaoffset = get_bigendian_uint64(&rawheader[48]);1938header->hunkbytes = get_bigendian_uint32(&rawheader[56]);1939header->hunkcount = (header->logicalbytes + header->hunkbytes - 1) / header->hunkbytes;1940header->unitbytes = get_bigendian_uint32(&rawheader[60]);1941header->unitcount = (header->logicalbytes + header->unitbytes - 1) / header->unitbytes;1942memcpy(header->sha1, &rawheader[84], CHD_SHA1_BYTES);1943memcpy(header->parentsha1, &rawheader[104], CHD_SHA1_BYTES);1944memcpy(header->rawsha1, &rawheader[64], CHD_SHA1_BYTES);19451946/* determine properties of map entries */1947header->mapentrybytes = compressed(header) ? 12 : 4;19481949/* hack */1950header->totalhunks = header->hunkcount;1951}19521953/* Unknown version */1954else1955{1956/* TODO */1957}19581959/* guess it worked */1960return CHDERR_NONE;1961}19621963/***************************************************************************1964INTERNAL HUNK READ/WRITE1965***************************************************************************/19661967/*-------------------------------------------------1968hunk_read_into_cache - read a hunk into1969the CHD's hunk cache1970-------------------------------------------------*/19711972static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum)1973{1974chd_error err;19751976/* track the max */1977if (hunknum > chd->maxhunk)1978chd->maxhunk = hunknum;19791980/* if we're already in the cache, we're done */1981if (chd->cachehunk == hunknum)1982return CHDERR_NONE;1983chd->cachehunk = ~0;19841985/* otherwise, read the data */1986err = hunk_read_into_memory(chd, hunknum, chd->cache);1987if (err != CHDERR_NONE)1988return err;19891990/* mark the hunk successfully cached in */1991chd->cachehunk = hunknum;1992return CHDERR_NONE;1993}19941995/*-------------------------------------------------1996hunk_read_into_memory - read a hunk into1997memory at the given location1998-------------------------------------------------*/19992000static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest)2001{2002chd_error err;20032004/* punt if no file */2005if (chd->file == NULL)2006return CHDERR_INVALID_FILE;20072008/* return an error if out of range */2009if (hunknum >= chd->header.totalhunks)2010return CHDERR_HUNK_OUT_OF_RANGE;20112012if (chd->header.version < 5)2013{2014map_entry *entry = &chd->map[hunknum];2015UINT32 bytes;20162017/* switch off the entry type */2018switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK)2019{2020/* compressed data */2021case V34_MAP_ENTRY_TYPE_COMPRESSED:20222023/* read it into the decompression buffer */2024core_fseek(chd->file, entry->offset, SEEK_SET);2025bytes = core_fread(chd->file, chd->compressed, entry->length);2026if (bytes != entry->length)2027return CHDERR_READ_ERROR;20282029/* now decompress using the codec */2030err = CHDERR_NONE;2031void* codec = &chd->zlib_codec_data;2032if (chd->codecintf[0]->decompress != NULL)2033err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes);2034if (err != CHDERR_NONE)2035return err;2036break;20372038/* uncompressed data */2039case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:2040core_fseek(chd->file, entry->offset, SEEK_SET);2041bytes = core_fread(chd->file, dest, chd->header.hunkbytes);2042if (bytes != chd->header.hunkbytes)2043return CHDERR_READ_ERROR;2044break;20452046/* mini-compressed data */2047case V34_MAP_ENTRY_TYPE_MINI:2048put_bigendian_uint64(&dest[0], entry->offset);2049for (bytes = 8; bytes < chd->header.hunkbytes; bytes++)2050dest[bytes] = dest[bytes - 8];2051break;20522053/* self-referenced data */2054case V34_MAP_ENTRY_TYPE_SELF_HUNK:2055if (chd->cachehunk == entry->offset && dest == chd->cache)2056break;2057return hunk_read_into_memory(chd, entry->offset, dest);20582059/* parent-referenced data */2060case V34_MAP_ENTRY_TYPE_PARENT_HUNK:2061err = hunk_read_into_memory(chd->parent, entry->offset, dest);2062if (err != CHDERR_NONE)2063return err;2064break;2065}2066return CHDERR_NONE;2067}2068else2069{2070/* get a pointer to the map entry */2071uint64_t blockoffs;2072uint32_t blocklen;2073uint16_t blockcrc;2074uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum];20752076/* uncompressed case */2077if (!compressed(&chd->header))2078{2079blockoffs = (uint64_t)get_bigendian_uint32(rawmap) * (uint64_t)chd->header.hunkbytes;2080if (blockoffs != 0) {2081core_fseek(chd->file, blockoffs, SEEK_SET);2082core_fread(chd->file, dest, chd->header.hunkbytes);2083/* TODO2084else if (m_parent_missing)2085throw CHDERR_REQUIRES_PARENT; */2086} else if (chd->parent) {2087err = hunk_read_into_memory(chd->parent, hunknum, dest);2088if (err != CHDERR_NONE)2089return err;2090} else {2091memset(dest, 0, chd->header.hunkbytes);2092}2093}20942095/* compressed case */2096blocklen = get_bigendian_uint24(&rawmap[1]);2097blockoffs = get_bigendian_uint48(&rawmap[4]);2098blockcrc = get_bigendian_uint16(&rawmap[10]);2099void* codec = NULL;2100switch (rawmap[0])2101{2102case COMPRESSION_TYPE_0:2103case COMPRESSION_TYPE_1:2104case COMPRESSION_TYPE_2:2105case COMPRESSION_TYPE_3:2106core_fseek(chd->file, blockoffs, SEEK_SET);2107core_fread(chd->file, chd->compressed, blocklen);2108switch (chd->codecintf[rawmap[0]]->compression)2109{2110case CHD_CODEC_CD_LZMA:2111codec = &chd->cdlz_codec_data;2112break;21132114case CHD_CODEC_ZLIB:2115codec = &chd->zlib_codec_data;2116break;21172118case CHD_CODEC_CD_ZLIB:2119codec = &chd->cdzl_codec_data;2120break;21212122case CHD_CODEC_CD_FLAC:2123codec = &chd->cdfl_codec_data;2124break;2125}2126if (codec==NULL)2127return CHDERR_DECOMPRESSION_ERROR;2128chd->codecintf[rawmap[0]]->decompress(codec, chd->compressed, blocklen, dest, chd->header.hunkbytes);2129if (dest != NULL && crc16(dest, chd->header.hunkbytes) != blockcrc)2130return CHDERR_DECOMPRESSION_ERROR;2131return CHDERR_NONE;21322133case COMPRESSION_NONE:2134core_fseek(chd->file, blockoffs, SEEK_SET);2135core_fread(chd->file, dest, chd->header.hunkbytes);2136if (crc16(dest, chd->header.hunkbytes) != blockcrc)2137return CHDERR_DECOMPRESSION_ERROR;2138return CHDERR_NONE;21392140case COMPRESSION_SELF:2141return hunk_read_into_memory(chd, blockoffs, dest);21422143case COMPRESSION_PARENT:2144#if 02145/* TODO */2146if (m_parent_missing)2147return CHDERR_REQUIRES_PARENT;2148return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes);2149#endif2150return CHDERR_DECOMPRESSION_ERROR;2151}2152return CHDERR_NONE;2153}21542155/* We should not reach this code */2156return CHDERR_DECOMPRESSION_ERROR;2157}21582159/***************************************************************************2160INTERNAL MAP ACCESS2161***************************************************************************/21622163/*-------------------------------------------------2164map_read - read the initial sector map2165-------------------------------------------------*/21662167static chd_error map_read(chd_file *chd)2168{2169UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE;2170UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE];2171UINT64 fileoffset, maxoffset = 0;2172UINT8 cookie[MAP_ENTRY_SIZE];2173UINT32 count;2174chd_error err;2175int i;21762177/* first allocate memory */2178chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks);2179if (!chd->map)2180return CHDERR_OUT_OF_MEMORY;21812182/* read the map entries in in chunks and extract to the map list */2183fileoffset = chd->header.length;2184for (i = 0; i < chd->header.totalhunks; i += MAP_STACK_ENTRIES)2185{2186/* compute how many entries this time */2187int entries = chd->header.totalhunks - i, j;2188if (entries > MAP_STACK_ENTRIES)2189entries = MAP_STACK_ENTRIES;21902191/* read that many */2192core_fseek(chd->file, fileoffset, SEEK_SET);2193count = core_fread(chd->file, raw_map_entries, entries * entrysize);2194if (count != entries * entrysize)2195{2196err = CHDERR_READ_ERROR;2197goto cleanup;2198}2199fileoffset += entries * entrysize;22002201/* process that many */2202if (entrysize == MAP_ENTRY_SIZE)2203{2204for (j = 0; j < entries; j++)2205map_extract(&raw_map_entries[j * MAP_ENTRY_SIZE], &chd->map[i + j]);2206}2207else2208{2209for (j = 0; j < entries; j++)2210map_extract_old(&raw_map_entries[j * OLD_MAP_ENTRY_SIZE], &chd->map[i + j], chd->header.hunkbytes);2211}22122213/* track the maximum offset */2214for (j = 0; j < entries; j++)2215if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_COMPRESSED ||2216(chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == V34_MAP_ENTRY_TYPE_UNCOMPRESSED)2217maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length);2218}22192220/* verify the cookie */2221core_fseek(chd->file, fileoffset, SEEK_SET);2222count = core_fread(chd->file, &cookie, entrysize);2223if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize))2224{2225err = CHDERR_INVALID_FILE;2226goto cleanup;2227}22282229/* verify the length */2230if (maxoffset > core_fsize(chd->file))2231{2232err = CHDERR_INVALID_FILE;2233goto cleanup;2234}2235return CHDERR_NONE;22362237cleanup:2238if (chd->map)2239free(chd->map);2240chd->map = NULL;2241return err;2242}22432244/***************************************************************************2245INTERNAL METADATA ACCESS2246***************************************************************************/22472248/*-------------------------------------------------2249metadata_find_entry - find a metadata entry2250-------------------------------------------------*/22512252static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry)2253{2254/* start at the beginning */2255metaentry->offset = chd->header.metaoffset;2256metaentry->prev = 0;22572258/* loop until we run out of options */2259while (metaentry->offset != 0)2260{2261UINT8 raw_meta_header[METADATA_HEADER_SIZE];2262UINT32 count;22632264/* read the raw header */2265core_fseek(chd->file, metaentry->offset, SEEK_SET);2266count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header));2267if (count != sizeof(raw_meta_header))2268break;22692270/* extract the data */2271metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]);2272metaentry->length = get_bigendian_uint32(&raw_meta_header[4]);2273metaentry->next = get_bigendian_uint64(&raw_meta_header[8]);22742275/* flags are encoded in the high byte of length */2276metaentry->flags = metaentry->length >> 24;2277metaentry->length &= 0x00ffffff;22782279/* if we got a match, proceed */2280if (metatag == CHDMETATAG_WILDCARD || metaentry->metatag == metatag)2281if (metaindex-- == 0)2282return CHDERR_NONE;22832284/* no match, fetch the next link */2285metaentry->prev = metaentry->offset;2286metaentry->offset = metaentry->next;2287}22882289/* if we get here, we didn't find it */2290return CHDERR_METADATA_NOT_FOUND;2291}22922293/***************************************************************************2294ZLIB COMPRESSION CODEC2295***************************************************************************/22962297/*-------------------------------------------------2298zlib_codec_init - initialize the ZLIB codec2299-------------------------------------------------*/23002301static chd_error zlib_codec_init(void *codec, uint32_t hunkbytes)2302{2303zlib_codec_data *data = (zlib_codec_data*)codec;2304chd_error err;2305int zerr;23062307/* clear the buffers */2308memset(data, 0, sizeof(zlib_codec_data));23092310/* init the inflater first */2311data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */2312data->inflater.avail_in = 0;2313data->inflater.zalloc = zlib_fast_alloc;2314data->inflater.zfree = zlib_fast_free;2315data->inflater.opaque = &data->allocator;2316zerr = inflateInit2(&data->inflater, -MAX_WBITS);23172318/* convert errors */2319if (zerr == Z_MEM_ERROR)2320err = CHDERR_OUT_OF_MEMORY;2321else if (zerr != Z_OK)2322err = CHDERR_CODEC_ERROR;2323else2324err = CHDERR_NONE;23252326/* handle an error */2327if (err != CHDERR_NONE)2328free(data);23292330return err;2331}23322333/*-------------------------------------------------2334zlib_codec_free - free data for the ZLIB2335codec2336-------------------------------------------------*/23372338static void zlib_codec_free(void *codec)2339{2340zlib_codec_data *data = (zlib_codec_data *)codec;23412342/* deinit the streams */2343if (data != NULL)2344{2345int i;23462347inflateEnd(&data->inflater);23482349/* free our fast memory */2350zlib_allocator alloc = data->allocator;2351for (i = 0; i < MAX_ZLIB_ALLOCS; i++)2352if (alloc.allocptr[i])2353free(alloc.allocptr[i]);2354}2355}23562357/*-------------------------------------------------2358zlib_codec_decompress - decomrpess data using2359the ZLIB codec2360-------------------------------------------------*/23612362static chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)2363{2364zlib_codec_data *data = (zlib_codec_data *)codec;2365int zerr;23662367/* reset the decompressor */2368data->inflater.next_in = (Bytef *)src;2369data->inflater.avail_in = complen;2370data->inflater.total_in = 0;2371data->inflater.next_out = (Bytef *)dest;2372data->inflater.avail_out = destlen;2373data->inflater.total_out = 0;2374zerr = inflateReset(&data->inflater);2375if (zerr != Z_OK)2376return CHDERR_DECOMPRESSION_ERROR;23772378/* do it */2379zerr = inflate(&data->inflater, Z_FINISH);2380if (data->inflater.total_out != destlen)2381return CHDERR_DECOMPRESSION_ERROR;23822383return CHDERR_NONE;2384}23852386/*-------------------------------------------------2387zlib_fast_alloc - fast malloc for ZLIB, which2388allocates and frees memory frequently2389-------------------------------------------------*/23902391static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size)2392{2393zlib_allocator *alloc = (zlib_allocator *)opaque;2394UINT32 *ptr;2395int i;23962397/* compute the size, rounding to the nearest 1k */2398size = (size * items + 0x3ff) & ~0x3ff;23992400/* reuse a hunk if we can */2401for (i = 0; i < MAX_ZLIB_ALLOCS; i++)2402{2403ptr = alloc->allocptr[i];2404if (ptr && size == *ptr)2405{2406/* set the low bit of the size so we don't match next time */2407*ptr |= 1;2408return ptr + 1;2409}2410}24112412/* alloc a new one */2413ptr = (UINT32 *)malloc(size + sizeof(UINT32));2414if (!ptr)2415return NULL;24162417/* put it into the list */2418for (i = 0; i < MAX_ZLIB_ALLOCS; i++)2419if (!alloc->allocptr[i])2420{2421alloc->allocptr[i] = ptr;2422break;2423}24242425/* set the low bit of the size so we don't match next time */2426*ptr = size | 1;2427return ptr + 1;2428}24292430/*-------------------------------------------------2431zlib_fast_free - fast free for ZLIB, which2432allocates and frees memory frequently2433-------------------------------------------------*/24342435static void zlib_fast_free(voidpf opaque, voidpf address)2436{2437zlib_allocator *alloc = (zlib_allocator *)opaque;2438UINT32 *ptr = (UINT32 *)address - 1;2439int i;24402441/* find the hunk */2442for (i = 0; i < MAX_ZLIB_ALLOCS; i++)2443if (ptr == alloc->allocptr[i])2444{2445/* clear the low bit of the size to allow matches */2446*ptr &= ~1;2447return;2448}2449}245024512452