summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/lib/util/corefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/lib/util/corefile.c')
-rw-r--r--trunk/src/lib/util/corefile.c1086
1 files changed, 1086 insertions, 0 deletions
diff --git a/trunk/src/lib/util/corefile.c b/trunk/src/lib/util/corefile.c
new file mode 100644
index 00000000000..0a907aeabc4
--- /dev/null
+++ b/trunk/src/lib/util/corefile.c
@@ -0,0 +1,1086 @@
+/***************************************************************************
+
+ corefile.c
+
+ File access functions.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "corefile.h"
+#include "unicode.h"
+#include <zlib.h>
+
+#include <stdarg.h>
+#include <ctype.h>
+
+
+
+/***************************************************************************
+ VALIDATION
+***************************************************************************/
+
+#if !defined(CRLF) || (CRLF < 1) || (CRLF > 3)
+#error CRLF undefined: must be 1 (CR), 2 (LF) or 3 (CR/LF)
+#endif
+
+
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+#define FILE_BUFFER_SIZE 512
+
+#define OPEN_FLAG_HAS_CRC 0x10000
+
+
+
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+enum _text_file_type
+{
+ TFT_OSD = 0, /* OSD depdendent encoding format used when BOMs missing */
+ TFT_UTF8, /* UTF-8 */
+ TFT_UTF16BE, /* UTF-16 (big endian) */
+ TFT_UTF16LE, /* UTF-16 (little endian) */
+ TFT_UTF32BE, /* UTF-32 (UCS-4) (big endian) */
+ TFT_UTF32LE /* UTF-32 (UCS-4) (little endian) */
+};
+typedef enum _text_file_type text_file_type;
+
+
+typedef struct _zlib_data zlib_data;
+struct _zlib_data
+{
+ z_stream stream;
+ UINT8 buffer[1024];
+ UINT64 realoffset;
+ UINT64 nextoffset;
+};
+
+
+/* typedef struct _core_file core_file -- declared in corefile.h */
+struct _core_file
+{
+ osd_file * file; /* OSD file handle */
+ zlib_data * zdata; /* compression data */
+ UINT32 openflags; /* flags we were opened with */
+ UINT8 data_allocated; /* was the data allocated by us? */
+ UINT8 * data; /* file data, if RAM-based */
+ UINT64 offset; /* current file offset */
+ UINT64 length; /* total file length */
+ text_file_type text_type; /* text output format */
+ char back_chars[UTF8_CHAR_MAX]; /* buffer to hold characters for ungetc */
+ int back_char_head; /* head of ungetc buffer */
+ int back_char_tail; /* tail of ungetc buffer */
+ UINT64 bufferbase; /* base offset of internal buffer */
+ UINT32 bufferbytes; /* bytes currently loaded into buffer */
+ UINT8 buffer[FILE_BUFFER_SIZE]; /* buffer data */
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* misc helpers */
+static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen);
+static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual);
+static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual);
+
+
+
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+/*-------------------------------------------------
+ is_directory_separator - is a given character
+ a directory separator? The following logic
+ works for most platforms
+-------------------------------------------------*/
+
+INLINE int is_directory_separator(char c)
+{
+ return (c == '\\' || c == '/' || c == ':');
+}
+
+
+
+/***************************************************************************
+ FILE OPEN/CLOSE
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fopen - open a file for access and
+ return an error code
+-------------------------------------------------*/
+
+file_error core_fopen(const char *filename, UINT32 openflags, core_file **file)
+{
+ file_error filerr = FILERR_NOT_FOUND;
+
+ /* allocate the file itself */
+ *file = (core_file *)malloc(sizeof(**file));
+ if (*file == NULL)
+ return FILERR_OUT_OF_MEMORY;
+ memset(*file, 0, sizeof(**file));
+
+ /* attempt to open the file */
+ filerr = osd_open(filename, openflags, &(*file)->file, &(*file)->length);
+ (*file)->openflags = openflags;
+
+ /* handle errors and return */
+ if (filerr != FILERR_NONE)
+ {
+ core_fclose(*file);
+ *file = NULL;
+ }
+ return filerr;
+}
+
+
+/*-------------------------------------------------
+ core_fopen_ram_internal - open a RAM-based buffer
+ for file-like access, possibly copying the data,
+ and return an error code
+-------------------------------------------------*/
+
+static file_error core_fopen_ram_internal(const void *data, size_t length, int copy_buffer, UINT32 openflags, core_file **file)
+{
+ /* can only do this for read access */
+ if ((openflags & OPEN_FLAG_WRITE) != 0)
+ return FILERR_INVALID_ACCESS;
+ if ((openflags & OPEN_FLAG_CREATE) != 0)
+ return FILERR_INVALID_ACCESS;
+
+ /* allocate the file itself */
+ *file = (core_file *)malloc(sizeof(**file) + (copy_buffer ? length : 0));
+ if (*file == NULL)
+ return FILERR_OUT_OF_MEMORY;
+ memset(*file, 0, sizeof(**file));
+
+ /* copy the buffer, if we're asked to */
+ if (copy_buffer)
+ {
+ void *dest = ((UINT8 *) *file) + sizeof(**file);
+ memcpy(dest, data, length);
+ data = dest;
+ }
+
+ /* claim the buffer */
+ (*file)->data = (UINT8 *)data;
+ (*file)->length = length;
+ (*file)->openflags = openflags;
+
+ return FILERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ core_fopen_ram - open a RAM-based buffer for
+ file-like access and return an error code
+-------------------------------------------------*/
+
+file_error core_fopen_ram(const void *data, size_t length, UINT32 openflags, core_file **file)
+{
+ return core_fopen_ram_internal(data, length, FALSE, openflags, file);
+}
+
+
+/*-------------------------------------------------
+ core_fopen_ram_copy - open a copy of a RAM-based
+ buffer for file-like access and return an error code
+-------------------------------------------------*/
+
+file_error core_fopen_ram_copy(const void *data, size_t length, UINT32 openflags, core_file **file)
+{
+ return core_fopen_ram_internal(data, length, TRUE, openflags, file);
+}
+
+
+/*-------------------------------------------------
+ core_fclose - closes a file
+-------------------------------------------------*/
+
+void core_fclose(core_file *file)
+{
+ /* close files and free memory */
+ if (file->zdata != NULL)
+ core_fcompress(file, FCOMPRESS_NONE);
+ if (file->file != NULL)
+ osd_close(file->file);
+ if (file->data != NULL && file->data_allocated)
+ free(file->data);
+ free(file);
+}
+
+
+/*-------------------------------------------------
+ core_fcompress - enable/disable streaming file
+ compression via zlib; level is 0 to disable
+ compression, or up to 9 for max compression
+-------------------------------------------------*/
+
+file_error core_fcompress(core_file *file, int level)
+{
+ file_error result = FILERR_NONE;
+
+ /* can only do this for read-only and write-only cases */
+ if ((file->openflags & OPEN_FLAG_WRITE) != 0 && (file->openflags & OPEN_FLAG_READ) != 0)
+ return FILERR_INVALID_ACCESS;
+
+ /* if we have been compressing, flush and free the data */
+ if (file->zdata != NULL && level == FCOMPRESS_NONE)
+ {
+ int zerr = Z_OK;
+
+ /* flush any remaining data if we are writing */
+ while ((file->openflags & OPEN_FLAG_WRITE) != 0 && zerr != Z_STREAM_END)
+ {
+ UINT32 actualdata;
+ file_error filerr;
+
+ /* deflate some more */
+ zerr = deflate(&file->zdata->stream, Z_FINISH);
+ if (zerr != Z_STREAM_END && zerr != Z_OK)
+ {
+ result = FILERR_INVALID_DATA;
+ break;
+ }
+
+ /* write the data */
+ if (file->zdata->stream.avail_out != sizeof(file->zdata->buffer))
+ {
+ filerr = osd_write(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer) - file->zdata->stream.avail_out, &actualdata);
+ if (filerr != FILERR_NONE)
+ break;
+ file->zdata->realoffset += actualdata;
+ file->zdata->stream.next_out = file->zdata->buffer;
+ file->zdata->stream.avail_out = sizeof(file->zdata->buffer);
+ }
+ }
+
+ /* end the appropriate operation */
+ if ((file->openflags & OPEN_FLAG_WRITE) != 0)
+ deflateEnd(&file->zdata->stream);
+ else
+ inflateEnd(&file->zdata->stream);
+
+ /* free memory */
+ free(file->zdata);
+ file->zdata = NULL;
+ }
+
+ /* if we are just starting to compress, allocate a new buffer */
+ if (file->zdata == NULL && level > FCOMPRESS_NONE)
+ {
+ int zerr;
+
+ /* allocate memory */
+ file->zdata = (zlib_data *)malloc(sizeof(*file->zdata));
+ if (file->zdata == NULL)
+ return FILERR_OUT_OF_MEMORY;
+ memset(file->zdata, 0, sizeof(*file->zdata));
+
+ /* initialize the stream and compressor */
+ if ((file->openflags & OPEN_FLAG_WRITE) != 0)
+ {
+ file->zdata->stream.next_out = file->zdata->buffer;
+ file->zdata->stream.avail_out = sizeof(file->zdata->buffer);
+ zerr = deflateInit(&file->zdata->stream, level);
+ }
+ else
+ zerr = inflateInit(&file->zdata->stream);
+
+ /* on error, return an error */
+ if (zerr != Z_OK)
+ {
+ free(file->zdata);
+ file->zdata = NULL;
+ return FILERR_OUT_OF_MEMORY;
+ }
+
+ /* flush buffers */
+ file->bufferbytes = 0;
+
+ /* set the initial offset */
+ file->zdata->realoffset = file->offset;
+ file->zdata->nextoffset = file->offset;
+ }
+
+ return result;
+}
+
+
+
+/***************************************************************************
+ FILE POSITIONING
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fseek - seek within a file
+-------------------------------------------------*/
+
+int core_fseek(core_file *file, INT64 offset, int whence)
+{
+ int err = 0;
+
+ /* error if compressing */
+ if (file->zdata != NULL)
+ return 1;
+
+ /* flush any buffered char */
+ file->back_char_head = 0;
+ file->back_char_tail = 0;
+
+ /* switch off the relative location */
+ switch (whence)
+ {
+ case SEEK_SET:
+ file->offset = offset;
+ break;
+
+ case SEEK_CUR:
+ file->offset += offset;
+ break;
+
+ case SEEK_END:
+ file->offset = file->length + offset;
+ break;
+ }
+ return err;
+}
+
+
+/*-------------------------------------------------
+ core_ftell - return the current file position
+-------------------------------------------------*/
+
+UINT64 core_ftell(core_file *file)
+{
+ /* return the current offset */
+ return file->offset;
+}
+
+
+/*-------------------------------------------------
+ core_feof - return 1 if we're at the end
+ of file
+-------------------------------------------------*/
+
+int core_feof(core_file *file)
+{
+ /* check for buffered chars */
+ if (file->back_char_head != file->back_char_tail)
+ return 0;
+
+ /* if the offset == length, we're at EOF */
+ return (file->offset >= file->length);
+}
+
+
+/*-------------------------------------------------
+ core_fsize - returns the size of a file
+-------------------------------------------------*/
+
+UINT64 core_fsize(core_file *file)
+{
+ return file->length;
+}
+
+
+
+/***************************************************************************
+ FILE READ
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fread - read from a file
+-------------------------------------------------*/
+
+UINT32 core_fread(core_file *file, void *buffer, UINT32 length)
+{
+ UINT32 bytes_read = 0;
+
+ /* flush any buffered char */
+ file->back_char_head = 0;
+ file->back_char_tail = 0;
+
+ /* handle real files */
+ if (file->data == NULL)
+ {
+ /* if we're within the buffer, consume that first */
+ if (file->offset >= file->bufferbase && file->offset < file->bufferbase + file->bufferbytes)
+ bytes_read += safe_buffer_copy(file->buffer, file->offset - file->bufferbase, file->bufferbytes, buffer, bytes_read, length);
+
+ /* if we've got a small amount left, read it into the buffer first */
+ if (bytes_read < length)
+ {
+ if (length - bytes_read < sizeof(file->buffer) / 2)
+ {
+ /* read as much as makes sense into the buffer */
+ file->bufferbase = file->offset + bytes_read;
+ file->bufferbytes = 0;
+ osd_or_zlib_read(file, file->buffer, file->bufferbase, sizeof(file->buffer), &file->bufferbytes);
+
+ /* do a bounded copy from the buffer to the destination */
+ bytes_read += safe_buffer_copy(file->buffer, 0, file->bufferbytes, buffer, bytes_read, length);
+ }
+
+ /* read the remainder directly from the file */
+ else
+ {
+ UINT32 new_bytes_read = 0;
+ osd_or_zlib_read(file, (UINT8 *)buffer + bytes_read, file->offset + bytes_read, length - bytes_read, &new_bytes_read);
+ bytes_read += new_bytes_read;
+ }
+ }
+ }
+
+ /* handle RAM-based files */
+ else
+ bytes_read += safe_buffer_copy(file->data, (UINT32)file->offset, file->length, buffer, bytes_read, length);
+
+ /* return the number of bytes read */
+ file->offset += bytes_read;
+ return bytes_read;
+}
+
+
+/*-------------------------------------------------
+ core_fgetc - read a character from a file
+-------------------------------------------------*/
+
+int core_fgetc(core_file *file)
+{
+ int result;
+
+ /* refresh buffer, if necessary */
+ if (file->back_char_head == file->back_char_tail)
+ {
+ utf16_char utf16_buffer[UTF16_CHAR_MAX];
+ char utf8_buffer[UTF8_CHAR_MAX];
+ char default_buffer[16];
+ unicode_char uchar = (unicode_char) ~0;
+ int readlen, charlen;
+
+ /* do we need to check the byte order marks? */
+ if (file->offset == 0)
+ {
+ UINT8 bom[4];
+ int pos = 0;
+
+ if (core_fread(file, bom, 4) == 4)
+ {
+ if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
+ {
+ file->text_type = TFT_UTF8;
+ pos = 3;
+ }
+ else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
+ {
+ file->text_type = TFT_UTF32BE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ {
+ file->text_type = TFT_UTF32LE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xfe && bom[1] == 0xff)
+ {
+ file->text_type = TFT_UTF16BE;
+ pos = 2;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe)
+ {
+ file->text_type = TFT_UTF16LE;
+ pos = 2;
+ }
+ else
+ {
+ file->text_type = TFT_OSD;
+ pos = 0;
+ }
+ }
+ core_fseek(file, pos, SEEK_SET);
+ }
+
+ /* fetch the next character */
+ switch (file->text_type)
+ {
+ default:
+ case TFT_OSD:
+ readlen = core_fread(file, default_buffer, sizeof(default_buffer));
+ if (readlen > 0)
+ {
+ charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0]));
+ core_fseek(file, (INT64) (charlen * sizeof(default_buffer[0])) - readlen, SEEK_CUR);
+ }
+ break;
+
+ case TFT_UTF8:
+ readlen = core_fread(file, utf8_buffer, sizeof(utf8_buffer));
+ if (readlen > 0)
+ {
+ charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0]));
+ core_fseek(file, (INT64) (charlen * sizeof(utf8_buffer[0])) - readlen, SEEK_CUR);
+ }
+ break;
+
+ case TFT_UTF16BE:
+ readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer));
+ if (readlen > 0)
+ {
+ charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
+ core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR);
+ }
+ break;
+
+ case TFT_UTF16LE:
+ readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer));
+ if (readlen > 0)
+ {
+ charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
+ core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR);
+ }
+ break;
+
+ case TFT_UTF32BE:
+ if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar))
+ uchar = BIG_ENDIANIZE_INT32(uchar);
+ break;
+
+ case TFT_UTF32LE:
+ if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar))
+ uchar = LITTLE_ENDIANIZE_INT32(uchar);
+ break;
+ }
+
+ if (uchar != ~0)
+ {
+ /* place the new character in the ring buffer */
+ file->back_char_head = 0;
+ file->back_char_tail = utf8_from_uchar(file->back_chars, ARRAY_LENGTH(file->back_chars), uchar);
+/* assert(file->back_char_tail != -1);*/
+ }
+ }
+
+ /* now read from the ring buffer */
+ if (file->back_char_head != file->back_char_tail)
+ {
+ result = file->back_chars[file->back_char_head++];
+ file->back_char_head %= ARRAY_LENGTH(file->back_chars);
+ }
+ else
+ result = EOF;
+
+ return result;
+}
+
+
+/*-------------------------------------------------
+ core_ungetc - put back a character read from
+ a file
+-------------------------------------------------*/
+
+int core_ungetc(int c, core_file *file)
+{
+ file->back_chars[file->back_char_tail++] = (char) c;
+ file->back_char_tail %= ARRAY_LENGTH(file->back_chars);
+ return c;
+}
+
+
+/*-------------------------------------------------
+ core_fgets - read a line from a text file
+-------------------------------------------------*/
+
+char *core_fgets(char *s, int n, core_file *file)
+{
+ char *cur = s;
+
+ /* loop while we have characters */
+ while (n > 0)
+ {
+ int c = core_fgetc(file);
+ if (c == EOF)
+ break;
+
+ /* if there's a CR, look for an LF afterwards */
+ if (c == 0x0d)
+ {
+ int c2 = core_fgetc(file);
+ if (c2 != 0x0a)
+ core_ungetc(c2, file);
+ *cur++ = 0x0d;
+ n--;
+ break;
+ }
+
+ /* if there's an LF, reinterp as a CR for consistency */
+ else if (c == 0x0a)
+ {
+ *cur++ = 0x0d;
+ n--;
+ break;
+ }
+
+ /* otherwise, pop the character in and continue */
+ *cur++ = c;
+ n--;
+ }
+
+ /* if we put nothing in, return NULL */
+ if (cur == s)
+ return NULL;
+
+ /* otherwise, terminate */
+ if (n > 0)
+ *cur++ = 0;
+ return s;
+}
+
+
+/*-------------------------------------------------
+ core_fbuffer - return a pointer to the file
+ buffer; if it doesn't yet exist, load the
+ file into RAM first
+-------------------------------------------------*/
+
+const void *core_fbuffer(core_file *file)
+{
+ file_error filerr;
+ UINT32 read_length;
+
+ /* if we already have data, just return it */
+ if (file->data != NULL)
+ return file->data;
+
+ /* allocate some memory */
+ file->data = (UINT8 *)malloc(file->length);
+ if (file->data == NULL)
+ return NULL;
+ file->data_allocated = TRUE;
+
+ /* read the file */
+ filerr = osd_or_zlib_read(file, file->data, 0, file->length, &read_length);
+ if (filerr != FILERR_NONE || read_length != file->length)
+ {
+ free(file->data);
+ file->data = NULL;
+ return NULL;
+ }
+
+ /* close the file because we don't need it anymore */
+ osd_close(file->file);
+ file->file = NULL;
+ return file->data;
+}
+
+
+/*-------------------------------------------------
+ core_fload - open a file with the specified
+ filename, read it into memory, and return a
+ pointer
+-------------------------------------------------*/
+
+file_error core_fload(const char *filename, void **data, UINT32 *length)
+{
+ core_file *file = NULL;
+ file_error err;
+ UINT64 size;
+
+ /* attempt to open the file */
+ err = core_fopen(filename, OPEN_FLAG_READ, &file);
+ if (err != FILERR_NONE)
+ return err;
+
+ /* get the size */
+ size = core_fsize(file);
+ if ((UINT32)size != size)
+ {
+ core_fclose(file);
+ return FILERR_OUT_OF_MEMORY;
+ }
+
+ /* allocate memory */
+ *data = osd_malloc(size);
+ if (length != NULL)
+ *length = (UINT32)size;
+
+ /* read the data */
+ if (core_fread(file, *data, size) != size)
+ {
+ core_fclose(file);
+ free(*data);
+ return FILERR_FAILURE;
+ }
+
+ /* close the file and return data */
+ core_fclose(file);
+ return FILERR_NONE;
+}
+
+file_error core_fload(const char *filename, dynamic_buffer &data)
+{
+ core_file *file = NULL;
+ file_error err;
+ UINT64 size;
+
+ /* attempt to open the file */
+ err = core_fopen(filename, OPEN_FLAG_READ, &file);
+ if (err != FILERR_NONE)
+ return err;
+
+ /* get the size */
+ size = core_fsize(file);
+ if ((UINT32)size != size)
+ {
+ core_fclose(file);
+ return FILERR_OUT_OF_MEMORY;
+ }
+
+ /* allocate memory */
+ data.resize(size);
+
+ /* read the data */
+ if (core_fread(file, data, size) != size)
+ {
+ core_fclose(file);
+ data.reset();
+ return FILERR_FAILURE;
+ }
+
+ /* close the file and return data */
+ core_fclose(file);
+ return FILERR_NONE;
+}
+
+
+
+/***************************************************************************
+ FILE WRITE
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fwrite - write to a file
+-------------------------------------------------*/
+
+UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length)
+{
+ UINT32 bytes_written = 0;
+
+ /* can't write to RAM-based stuff */
+ if (file->data != NULL)
+ return 0;
+
+ /* flush any buffered char */
+ file->back_char_head = 0;
+ file->back_char_tail = 0;
+
+ /* invalidate any buffered data */
+ file->bufferbytes = 0;
+
+ /* do the write */
+ osd_or_zlib_write(file, buffer, file->offset, length, &bytes_written);
+
+ /* return the number of bytes read */
+ file->offset += bytes_written;
+ file->length = MAX(file->length, file->offset);
+ return bytes_written;
+}
+
+
+/*-------------------------------------------------
+ core_fputs - write a line to a text file
+-------------------------------------------------*/
+
+int core_fputs(core_file *f, const char *s)
+{
+ char convbuf[1024];
+ char *pconvbuf = convbuf;
+ int count = 0;
+
+ /* is this the beginning of the file? if so, write a byte order mark */
+ if (f->offset == 0 && !(f->openflags & OPEN_FLAG_NO_BOM))
+ {
+ *pconvbuf++ = (char)0xef;
+ *pconvbuf++ = (char)0xbb;
+ *pconvbuf++ = (char)0xbf;
+ }
+
+ /* convert '\n' to platform dependant line endings */
+ while (*s != 0)
+ {
+ if (*s == '\n')
+ {
+ if (CRLF == 1) /* CR only */
+ *pconvbuf++ = 13;
+ else if (CRLF == 2) /* LF only */
+ *pconvbuf++ = 10;
+ else if (CRLF == 3) /* CR+LF */
+ {
+ *pconvbuf++ = 13;
+ *pconvbuf++ = 10;
+ }
+ }
+ else
+ *pconvbuf++ = *s;
+ s++;
+
+ /* if we overflow, break into chunks */
+ if (pconvbuf >= convbuf + ARRAY_LENGTH(convbuf) - 10)
+ {
+ count += core_fwrite(f, convbuf, pconvbuf - convbuf);
+ pconvbuf = convbuf;
+ }
+ }
+
+ /* final flush */
+ if (pconvbuf != convbuf)
+ count += core_fwrite(f, convbuf, pconvbuf - convbuf);
+
+ return count;
+}
+
+
+/*-------------------------------------------------
+ core_vfprintf - vfprintf to a text file
+-------------------------------------------------*/
+
+int core_vfprintf(core_file *f, const char *fmt, va_list va)
+{
+ char buf[1024];
+ vsnprintf(buf, sizeof(buf), fmt, va);
+ return core_fputs(f, buf);
+}
+
+
+/*-------------------------------------------------
+ core_fprintf - vfprintf to a text file
+-------------------------------------------------*/
+
+int CLIB_DECL core_fprintf(core_file *f, const char *fmt, ...)
+{
+ int rc;
+ va_list va;
+ va_start(va, fmt);
+ rc = core_vfprintf(f, fmt, va);
+ va_end(va);
+ return rc;
+}
+
+
+
+/***************************************************************************
+ FILENAME UTILITIES
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_filename_extract_base - extract the base
+ name from a filename; note that this makes
+ assumptions about path separators
+-------------------------------------------------*/
+
+astring &core_filename_extract_base(astring &result, const char *name, bool strip_extension)
+{
+ /* find the start of the name */
+ const char *start = name + strlen(name);
+ while (start > name && !is_directory_separator(start[-1]))
+ start--;
+
+ /* copy the rest into an astring */
+ result.cpy(start);
+
+ /* chop the extension if present */
+ if (strip_extension)
+ result.substr(0, result.rchr(0, '.'));
+ return result;
+}
+
+
+/*-------------------------------------------------
+ core_filename_ends_with - does the given
+ filename end with the specified extension?
+-------------------------------------------------*/
+
+int core_filename_ends_with(const char *filename, const char *extension)
+{
+ int namelen = strlen(filename);
+ int extlen = strlen(extension);
+ int matches = TRUE;
+
+ /* work backwards checking for a match */
+ while (extlen > 0)
+ if (tolower((UINT8)filename[--namelen]) != tolower((UINT8)extension[--extlen]))
+ {
+ matches = FALSE;
+ break;
+ }
+
+ return matches;
+}
+
+
+
+/***************************************************************************
+ MISC HELPERS
+***************************************************************************/
+
+/*-------------------------------------------------
+ safe_buffer_copy - copy safely from one
+ bounded buffer to another
+-------------------------------------------------*/
+
+static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen)
+{
+ UINT32 sourceavail = sourcelen - sourceoffs;
+ UINT32 destavail = destlen - destoffs;
+ UINT32 bytes_to_copy = MIN(sourceavail, destavail);
+ if (bytes_to_copy > 0)
+ memcpy((UINT8 *)dest + destoffs, (const UINT8 *)source + sourceoffs, bytes_to_copy);
+ return bytes_to_copy;
+}
+
+
+/*-------------------------------------------------
+ osd_or_zlib_read - wrapper for osd_read that
+ handles zlib-compressed data
+-------------------------------------------------*/
+
+static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual)
+{
+ /* if no compression, just pass through */
+ if (file->zdata == NULL)
+ return osd_read(file->file, buffer, offset, length, actual);
+
+ /* if the offset doesn't match the next offset, fail */
+ if (offset != file->zdata->nextoffset)
+ return FILERR_INVALID_ACCESS;
+
+ /* set up the destination */
+ file->zdata->stream.next_out = (Bytef *)buffer;
+ file->zdata->stream.avail_out = length;
+ while (file->zdata->stream.avail_out != 0)
+ {
+ file_error filerr;
+ UINT32 actualdata;
+ int zerr = Z_OK;
+
+ /* if we didn't make progress, report an error or the end */
+ if (file->zdata->stream.avail_in != 0)
+ zerr = inflate(&file->zdata->stream, Z_SYNC_FLUSH);
+ if (zerr != Z_OK)
+ {
+ *actual = length - file->zdata->stream.avail_out;
+ file->zdata->nextoffset += *actual;
+ return (zerr == Z_STREAM_END) ? FILERR_NONE : FILERR_INVALID_DATA;
+ }
+
+ /* fetch more data if needed */
+ if (file->zdata->stream.avail_in == 0)
+ {
+ filerr = osd_read(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata);
+ if (filerr != FILERR_NONE)
+ return filerr;
+ file->zdata->realoffset += actualdata;
+ file->zdata->stream.next_in = file->zdata->buffer;
+ file->zdata->stream.avail_in = sizeof(file->zdata->buffer);
+ }
+ }
+
+ /* we read everything */
+ *actual = length;
+ file->zdata->nextoffset += *actual;
+ return FILERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ osd_or_zlib_write - wrapper for osd_write that
+ handles zlib-compressed data
+-------------------------------------------------*/
+
+static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual)
+{
+ /* if no compression, just pass through */
+ if (file->zdata == NULL)
+ return osd_write(file->file, buffer, offset, length, actual);
+
+ /* if the offset doesn't match the next offset, fail */
+ if (offset != file->zdata->nextoffset)
+ return FILERR_INVALID_ACCESS;
+
+ /* set up the source */
+ file->zdata->stream.next_in = (Bytef *)buffer;
+ file->zdata->stream.avail_in = length;
+ while (file->zdata->stream.avail_in != 0)
+ {
+ file_error filerr;
+ UINT32 actualdata;
+ int zerr;
+
+ /* if we didn't make progress, report an error or the end */
+ zerr = deflate(&file->zdata->stream, Z_NO_FLUSH);
+ if (zerr != Z_OK)
+ {
+ *actual = length - file->zdata->stream.avail_in;
+ file->zdata->nextoffset += *actual;
+ return FILERR_INVALID_DATA;
+ }
+
+ /* write more data if we are full up */
+ if (file->zdata->stream.avail_out == 0)
+ {
+ filerr = osd_write(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata);
+ if (filerr != FILERR_NONE)
+ return filerr;
+ file->zdata->realoffset += actualdata;
+ file->zdata->stream.next_out = file->zdata->buffer;
+ file->zdata->stream.avail_out = sizeof(file->zdata->buffer);
+ }
+ }
+
+ /* we read everything */
+ *actual = length;
+ file->zdata->nextoffset += *actual;
+ return FILERR_NONE;
+}
+
+