diff options
Diffstat (limited to 'src/lib/util/corefile.c')
-rw-r--r-- | src/lib/util/corefile.c | 726 |
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; +} |