#include "LzmaEnc.h"
#include "LzmaDec.h"
+#if defined(__PS3__) || defined(__PSL1GHT__)
+#define __MACTYPES__
+#endif
#include "zlib.h"
#undef TRUE
lzma_allocator allocator;
};
+typedef struct _huff_codec_data huff_codec_data;
+struct _huff_codec_data
+{
+ struct huffman_decoder* decoder;
+};
+
/* codec-private data for the CDZL codec */
typedef struct _cdzl_codec_data cdzl_codec_data;
struct _cdzl_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 {
UINT32 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() */
chd_header header; /* header, extracted from file */
chd_file * parent; /* pointer to parent file, or NULL */
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 */
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 */
PROTOTYPES
***************************************************************************/
+/* core_file wrappers over stdio */
+static core_file *core_stdio_fopen(char const *path);
+static UINT64 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 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);
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);
+
/* cdzl compression codec */
static chd_error cdzl_codec_init(void* codec, uint32_t hunkbytes);
static void cdzl_codec_free(void* codec);
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 err;
+ }
+
+ // then decode the data
+ for (uint32_t 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)
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 CD zlib compression */
{
CHD_CODEC_CD_ZLIB,
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;
if (!chd_compressed(header))
{
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;
/* now read the map */
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);
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;
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)
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_CD_ZLIB:
codec = &newchd->cdzl_codec_data;
break;
CHD_EXPORT chd_error chd_precache(chd_file *chd)
{
-#ifdef _MSC_VER
- size_t size, count;
-#else
- ssize_t size, count;
-#endif
+ INT64 count;
+ UINT64 size;
if (chd->file_cache == NULL)
{
- core_fseek(chd->file, 0, SEEK_END);
- size = core_ftell(chd->file);
- if (size <= 0)
+ size = core_fsize(chd->file);
+ if ((INT64)size <= 0)
return CHDERR_INVALID_DATA;
chd->file_cache = malloc(size);
if (chd->file_cache == NULL)
chd_error err;
core_file *file = NULL;
+ if (filename == NULL)
+ {
+ err = CHDERR_INVALID_PARAMETER;
+ goto cleanup;
+ }
+
/* choose the proper mode */
switch(mode)
{
}
/* open the file */
- file = core_fopen(filename);
+ file = core_stdio_fopen(filename);
if (file == 0)
{
err = CHDERR_FILE_NOT_FOUND;
}
/* 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))
switch (chd->codecintf[i]->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_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;
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
if (chd->file_cache)
free(chd->file_cache);
+ if (chd->parent)
+ chd_close(chd->parent);
+
/* free our memory */
free(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
***************************************************************************/
header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)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;
}
memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES);
header->hunkbytes = get_bigendian_uint32(&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);
header->metaoffset = get_bigendian_uint64(&rawheader[36]);
header->hunkbytes = get_bigendian_uint32(&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);
header->mapoffset = get_bigendian_uint64(&rawheader[40]);
header->metaoffset = get_bigendian_uint64(&rawheader[48]);
header->hunkbytes = get_bigendian_uint32(&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]);
+ 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);
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_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;
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 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 unit_in_hunk = blockoffs % units_in_hunk;
+ UINT8 *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;
}
UINT8 cookie[MAP_ENTRY_SIZE];
UINT32 count;
chd_error err;
- int i;
+ UINT32 i;
/* first allocate memory */
chd->map = (map_entry *)malloc(sizeof(chd->map[0]) * chd->header.totalhunks);
else
err = CHDERR_NONE;
- /* handle an error */
- if (err != CHDERR_NONE)
- free(data);
-
return err;
}
/* deinit the streams */
if (data != NULL)
{
- int i;
-
inflateEnd(&data->inflater);
/* free our fast memory */
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 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 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 offset, int whence) {
+ return core_stdio_fseek_impl((FILE*)file->argp, offset, whence);
+}