summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/corefile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/corefile.c')
-rw-r--r--src/lib/util/corefile.c726
1 files changed, 726 insertions, 0 deletions
diff --git a/src/lib/util/corefile.c b/src/lib/util/corefile.c
new file mode 100644
index 00000000000..008e919a8f7
--- /dev/null
+++ b/src/lib/util/corefile.c
@@ -0,0 +1,726 @@
+/***************************************************************************
+
+ corefile.c
+
+ File access functions.
+
+ Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include "corefile.h"
+#include "unicode.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 _core_file core_file -- declared in corefile.h */
+struct _core_file
+{
+ osd_file * file; /* OSD file handle */
+ 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);
+
+
+
+/***************************************************************************
+ 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 = 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 - 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)
+{
+ /* 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 = malloc(sizeof(**file));
+ if (*file == NULL)
+ return FILERR_OUT_OF_MEMORY;
+ memset(*file, 0, sizeof(**file));
+
+ /* claim the buffer */
+ (*file)->data = (UINT8 *)data;
+ (*file)->length = length;
+ (*file)->openflags = openflags;
+
+ return FILERR_NONE;
+}
+
+
+/*-------------------------------------------------
+ core_fclose - closes a file
+-------------------------------------------------*/
+
+void core_fclose(core_file *file)
+{
+ /* close files and free memory */
+ if (file->file != NULL)
+ osd_close(file->file);
+ if (file->data != NULL && file->data_allocated)
+ free(file->data);
+ free(file);
+}
+
+
+
+/***************************************************************************
+ FILE POSITIONING
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fseek - seek within a file
+-------------------------------------------------*/
+
+int core_fseek(core_file *file, INT64 offset, int whence)
+{
+ int err = 0;
+
+ /* 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)
+{
+ file_error filerr;
+ 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;
+ filerr = osd_read(file->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;
+ filerr = osd_read(file->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 = malloc(file->length);
+ if (file->data == NULL)
+ return NULL;
+ file->data_allocated = TRUE;
+
+ /* read the file */
+ filerr = osd_read(file->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;
+}
+
+
+
+/***************************************************************************
+ FILE WRITE
+***************************************************************************/
+
+/*-------------------------------------------------
+ core_fwrite - write to a file
+-------------------------------------------------*/
+
+UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length)
+{
+ UINT32 bytes_written = 0;
+ file_error filerr;
+
+ /* 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 */
+ filerr = osd_write(file->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++ = 0xef;
+ *pconvbuf++ = 0xbb;
+ *pconvbuf++ = 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, int 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 */
+ astring_cpyc(result, start);
+
+ /* chop the extension if present */
+ if (strip_extension)
+ astring_substr(result, 0, astring_rchr(result, 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(filename[--namelen]) != tolower(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;
+}