diff options
Diffstat (limited to 'src/lib/util/corefile.cpp')
-rw-r--r-- | src/lib/util/corefile.cpp | 1631 |
1 files changed, 927 insertions, 704 deletions
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index 25978afdb2c..e4eda2477d5 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** corefile.c @@ -14,11 +14,17 @@ #include "unicode.h" #include <zlib.h> -#include <stdarg.h> +#include <algorithm> +#include <cstdarg> +#include <cstring> #include <ctype.h> +namespace util { + +namespace { + /*************************************************************************** VALIDATION ***************************************************************************/ @@ -30,613 +36,510 @@ /*************************************************************************** - 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) */ -}; - - -struct zlib_data -{ - z_stream stream; - UINT8 buffer[1024]; - UINT64 realoffset; - UINT64 nextoffset; -}; - - -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 --------------------------------------------------*/ - -static inline int is_directory_separator(char c) +class zlib_data { - return (c == '\\' || c == '/' || c == ':'); -} - +public: + typedef std::unique_ptr<zlib_data> ptr; + static int start_compression(int level, std::uint64_t offset, ptr &data) + { + ptr result(new zlib_data(offset)); + result->reset_output(); + auto const zerr = deflateInit(&result->m_stream, level); + result->m_compress = (Z_OK == zerr); + if (result->m_compress) data = std::move(result); + return zerr; + } + static int start_decompression(std::uint64_t offset, ptr &data) + { + ptr result(new zlib_data(offset)); + auto const zerr = inflateInit(&result->m_stream); + result->m_decompress = (Z_OK == zerr); + if (result->m_decompress) data = std::move(result); + return zerr; + } -/*************************************************************************** - FILE OPEN/CLOSE -***************************************************************************/ - -/*------------------------------------------------- - core_fopen - open a file for access and - return an error code --------------------------------------------------*/ + ~zlib_data() + { + if (m_compress) deflateEnd(&m_stream); + else if (m_decompress) inflateEnd(&m_stream); + } -file_error core_fopen(const char *filename, UINT32 openflags, core_file **file) -{ - file_error filerr = FILERR_NOT_FOUND; + std::size_t buffer_size() const { return sizeof(m_buffer); } + void const *buffer_data() const { return m_buffer; } + void *buffer_data() { return m_buffer; } - /* allocate the file itself */ - *file = (core_file *)malloc(sizeof(**file)); - if (*file == nullptr) - return FILERR_OUT_OF_MEMORY; - memset(*file, 0, sizeof(**file)); + // general-purpose output buffer manipulation + bool output_full() const { return 0 == m_stream.avail_out; } + std::size_t output_space() const { return m_stream.avail_out; } + void set_output(void *data, std::uint32_t size) + { + m_stream.next_out = reinterpret_cast<Bytef *>(data); + m_stream.avail_out = size; + } - /* attempt to open the file */ - filerr = osd_open(filename, openflags, &(*file)->file, &(*file)->length); - (*file)->openflags = openflags; + // working with output to the internal buffer + bool has_output() const { return m_stream.avail_out != sizeof(m_buffer); } + std::size_t output_size() const { return sizeof(m_buffer) - m_stream.avail_out; } + void reset_output() + { + m_stream.next_out = m_buffer; + m_stream.avail_out = sizeof(m_buffer); + } - /* handle errors and return */ - if (filerr != FILERR_NONE) + // general-purpose input buffer manipulation + bool has_input() const { return 0 != m_stream.avail_in; } + std::size_t input_size() const { return m_stream.avail_in; } + void set_input(void const *data, std::uint32_t size) { - core_fclose(*file); - *file = nullptr; + m_stream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(data)); + m_stream.avail_in = size; } - return filerr; -} + // working with input from the internal buffer + void reset_input(std::uint32_t size) + { + m_stream.next_in = m_buffer; + m_stream.avail_in = size; + } -/*------------------------------------------------- - core_fopen_ram_internal - open a RAM-based buffer - for file-like access, possibly copying the data, - and return an error code --------------------------------------------------*/ + int compress() { assert(m_compress); return deflate(&m_stream, Z_NO_FLUSH); } + int finalise() { assert(m_compress); return deflate(&m_stream, Z_FINISH); } + int decompress() { assert(m_decompress); return inflate(&m_stream, Z_SYNC_FLUSH); } -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; + std::uint64_t realoffset() const { return m_realoffset; } + void add_realoffset(std::uint32_t increment) { m_realoffset += increment; } - /* allocate the file itself */ - *file = (core_file *)malloc(sizeof(**file) + (copy_buffer ? length : 0)); - if (*file == nullptr) - return FILERR_OUT_OF_MEMORY; - memset(*file, 0, sizeof(**file)); + bool is_nextoffset(std::uint64_t value) const { return m_nextoffset == value; } + void add_nextoffset(std::uint32_t increment) { m_nextoffset += increment; } - /* copy the buffer, if we're asked to */ - if (copy_buffer) +private: + zlib_data(std::uint64_t offset) + : m_compress(false) + , m_decompress(false) + , m_realoffset(offset) + , m_nextoffset(offset) { - void *dest = ((UINT8 *) *file) + sizeof(**file); - memcpy(dest, data, length); - data = dest; + m_stream.zalloc = Z_NULL; + m_stream.zfree = Z_NULL; + m_stream.opaque = Z_NULL; } - /* claim the buffer */ - (*file)->data = (UINT8 *)data; - (*file)->length = length; - (*file)->openflags = openflags; - - return FILERR_NONE; -} - + bool m_compress, m_decompress; + z_stream m_stream; + std::uint8_t m_buffer[1024]; + std::uint64_t m_realoffset; + std::uint64_t m_nextoffset; +}; -/*------------------------------------------------- - 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) +class core_proxy_file : public core_file { - return core_fopen_ram_internal(data, length, FALSE, openflags, file); -} +public: + core_proxy_file(core_file &file) : m_file(file) { } + virtual ~core_proxy_file() override { } + virtual file_error compress(int level) override { return m_file.compress(level); } + + virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); } + virtual std::uint64_t tell() const override { return m_file.tell(); } + virtual bool eof() const override { return m_file.eof(); } + virtual std::uint64_t size() const override { return m_file.size(); } + + virtual std::uint32_t read(void *buffer, std::uint32_t length) override { return m_file.read(buffer, length); } + virtual int getc() override { return m_file.getc(); } + virtual int ungetc(int c) override { return m_file.ungetc(c); } + virtual char *gets(char *s, int n) override { return m_file.gets(s, n); } + virtual const void *buffer() override { return m_file.buffer(); } + + virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); } + virtual int puts(const char *s) override { return m_file.puts(s); } + virtual int vprintf(const char *fmt, va_list va) override { return m_file.vprintf(fmt, va); } + virtual file_error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } + + virtual file_error flush() override { return m_file.flush(); } + +private: + core_file &m_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) +class core_text_file : public core_file { - return core_fopen_ram_internal(data, length, TRUE, openflags, file); -} - +public: + enum class text_file_type + { + OSD, // OSD dependent encoding format used when BOMs missing + UTF8, // UTF-8 + UTF16BE, // UTF-16 (big endian) + UTF16LE, // UTF-16 (little endian) + UTF32BE, // UTF-32 (UCS-4) (big endian) + UTF32LE // UTF-32 (UCS-4) (little endian) + }; + + virtual int getc() override; + virtual int ungetc(int c) override; + virtual char *gets(char *s, int n) override; + virtual int puts(char const *s) override; + virtual int vprintf(char const *fmt, va_list va) override; + +protected: + core_text_file(std::uint32_t openflags) + : m_openflags(openflags) + , m_text_type(text_file_type::OSD) + , m_back_char_head(0) + , m_back_char_tail(0) + { + } -/*------------------------------------------------- - core_fclose - closes a file --------------------------------------------------*/ + bool read_access() const { return 0U != (m_openflags & OPEN_FLAG_READ); } + bool write_access() const { return 0U != (m_openflags & OPEN_FLAG_WRITE); } + bool no_bom() const { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); } -void core_fclose(core_file *file) -{ - /* close files and free memory */ - if (file->zdata != nullptr) - core_fcompress(file, FCOMPRESS_NONE); - if (file->file != nullptr) - osd_close(file->file); - if (file->data != nullptr && file->data_allocated) - free(file->data); - free(file); -} + bool has_putback() const { return m_back_char_head != m_back_char_tail; } + void clear_putback() { m_back_char_head = m_back_char_tail = 0; } +private: + std::uint32_t const m_openflags; // flags we were opened with + text_file_type m_text_type; // text output format + char m_back_chars[UTF8_CHAR_MAX]; // buffer to hold characters for ungetc + int m_back_char_head; // head of ungetc buffer + int m_back_char_tail; // tail of ungetc buffer +}; -/*------------------------------------------------- - 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) +class core_in_memory_file : public core_text_file { - 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 != nullptr && level == FCOMPRESS_NONE) +public: + core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) + : core_text_file(openflags) + , m_data_allocated(false) + , m_data(copy ? nullptr : data) + , m_offset(0) + , m_length(length) { - int zerr = Z_OK; - - /* flush any remaining data if we are writing */ - while ((file->openflags & OPEN_FLAG_WRITE) != 0 && zerr != Z_STREAM_END) + if (copy) { - 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); - } + void *const buf = allocate(); + if (buf) std::memcpy(buf, data, length); } + } - /* end the appropriate operation */ - if ((file->openflags & OPEN_FLAG_WRITE) != 0) - deflateEnd(&file->zdata->stream); - else - inflateEnd(&file->zdata->stream); + ~core_in_memory_file() override { purge(); } + virtual file_error compress(int level) override { return FILERR_INVALID_ACCESS; } - /* free memory */ - free(file->zdata); - file->zdata = nullptr; - } + virtual int seek(std::int64_t offset, int whence) override; + virtual std::uint64_t tell() const override { return m_offset; } + virtual bool eof() const override; + virtual std::uint64_t size() const override { return m_length; } - /* if we are just starting to compress, allocate a new buffer */ - if (file->zdata == nullptr && level > FCOMPRESS_NONE) - { - int zerr; + virtual std::uint32_t read(void *buffer, std::uint32_t length) override; + virtual void const *buffer() override { return m_data; } - /* allocate memory */ - file->zdata = (zlib_data *)malloc(sizeof(*file->zdata)); - if (file->zdata == nullptr) - return FILERR_OUT_OF_MEMORY; - memset(file->zdata, 0, sizeof(*file->zdata)); + virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; } + virtual file_error truncate(std::uint64_t offset) override; + virtual file_error flush() override { clear_putback(); return FILERR_NONE; } - /* 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); +protected: + core_in_memory_file(std::uint32_t openflags, std::uint64_t length) + : core_text_file(openflags) + , m_data_allocated(false) + , m_data(nullptr) + , m_offset(0) + , m_length(length) + { + } - /* on error, return an error */ - if (zerr != Z_OK) + bool is_loaded() const { return nullptr != m_data; } + void *allocate() + { + if (m_data) return nullptr; + void *data = malloc(m_length); + if (data) { - free(file->zdata); - file->zdata = nullptr; - return FILERR_OUT_OF_MEMORY; + m_data_allocated = true; + m_data = data; } - - /* flush buffers */ - file->bufferbytes = 0; - - /* set the initial offset */ - file->zdata->realoffset = file->offset; - file->zdata->nextoffset = file->offset; + return data; + } + void purge() + { + if (m_data && m_data_allocated) free(const_cast<void *>(m_data)); + m_data_allocated = false; + m_data = nullptr; } - return result; -} - + std::uint64_t offset() const { return m_offset; } + void add_offset(std::uint32_t increment) { m_offset += increment; m_length = (std::max)(m_length, m_offset); } + std::uint64_t length() const { return m_length; } + void set_length(std::uint64_t value) { m_length = value; m_offset = (std::min)(m_offset, m_length); } + static std::size_t safe_buffer_copy( + void const *source, std::size_t sourceoffs, std::size_t sourcelen, + void *dest, std::size_t destoffs, std::size_t destlen); -/*************************************************************************** - FILE POSITIONING -***************************************************************************/ +private: + bool m_data_allocated; // was the data allocated by us? + void const * m_data; // file data, if RAM-based + std::uint64_t m_offset; // current file offset + std::uint64_t m_length; // total file length +}; -/*------------------------------------------------- - core_fseek - seek within a file --------------------------------------------------*/ -int core_fseek(core_file *file, INT64 offset, int whence) +class core_osd_file : public core_in_memory_file { - int err = 0; - - /* error if compressing */ - if (file->zdata != nullptr) - return 1; - - /* flush any buffered char */ - file->back_char_head = 0; - file->back_char_tail = 0; - - /* switch off the relative location */ - switch (whence) +public: + core_osd_file(std::uint32_t openmode, osd_file *file, std::uint64_t length) + : core_in_memory_file(openmode, length) + , m_file(file) + , m_zdata() + , m_bufferbase(0) + , m_bufferbytes(0) { - 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_osd_file() override; + virtual file_error compress(int level) override; -/*------------------------------------------------- - core_ftell - return the current file position --------------------------------------------------*/ - -UINT64 core_ftell(core_file *file) -{ - /* return the current offset */ - return file->offset; -} + virtual int seek(std::int64_t offset, int whence) override; + virtual std::uint32_t read(void *buffer, std::uint32_t length) override; + virtual void const *buffer() override; -/*------------------------------------------------- - core_feof - return 1 if we're at the end - of file --------------------------------------------------*/ + virtual std::uint32_t write(void const *buffer, std::uint32_t length) override; + virtual file_error truncate(std::uint64_t offset) override; + virtual file_error flush() override; -int core_feof(core_file *file) -{ - /* check for buffered chars */ - if (file->back_char_head != file->back_char_tail) - return 0; +protected: - /* if the offset == length, we're at EOF */ - return (file->offset >= file->length); -} + bool is_buffered() const { return (offset() >= m_bufferbase) && (offset() < (m_bufferbase + m_bufferbytes)); } +private: + static constexpr std::size_t FILE_BUFFER_SIZE = 512; -/*------------------------------------------------- - core_fsize - returns the size of a file --------------------------------------------------*/ + file_error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); + file_error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); -UINT64 core_fsize(core_file *file) -{ - return file->length; -} + osd_file * m_file; // OSD file handle + zlib_data::ptr m_zdata; // compression data + std::uint64_t m_bufferbase; // base offset of internal buffer + std::uint32_t m_bufferbytes; // bytes currently loaded into buffer + std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data +}; /*************************************************************************** - FILE READ + INLINE FUNCTIONS ***************************************************************************/ /*------------------------------------------------- - core_fread - read from a file + is_directory_separator - is a given character + a directory separator? The following logic + works for most platforms -------------------------------------------------*/ -UINT32 core_fread(core_file *file, void *buffer, UINT32 length) +static inline int is_directory_separator(char c) { - UINT32 bytes_read = 0; - - /* flush any buffered char */ - file->back_char_head = 0; - file->back_char_tail = 0; - - /* handle real files */ - if (file->file && file->data == nullptr) - { - /* 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); - } + return (c == '\\' || c == '/' || c == ':'); +} - /* 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_text_file +***************************************************************************/ /*------------------------------------------------- - core_fgetc - read a character from a file + getc - read a character from a file -------------------------------------------------*/ -int core_fgetc(core_file *file) +int core_text_file::getc() { int result; - /* refresh buffer, if necessary */ - if (file->back_char_head == file->back_char_tail) + // refresh buffer, if necessary + if (m_back_char_head == m_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) + // do we need to check the byte order marks? + if (tell() == 0) { - UINT8 bom[4]; + std::uint8_t bom[4]; int pos = 0; - if (core_fread(file, bom, 4) == 4) + if (read(bom, 4) == 4) { if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) { - file->text_type = TFT_UTF8; + m_text_type = text_file_type::UTF8; pos = 3; } else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff) { - file->text_type = TFT_UTF32BE; + m_text_type = text_file_type::UTF32BE; pos = 4; } else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00) { - file->text_type = TFT_UTF32LE; + m_text_type = text_file_type::UTF32LE; pos = 4; } else if (bom[0] == 0xfe && bom[1] == 0xff) { - file->text_type = TFT_UTF16BE; + m_text_type = text_file_type::UTF16BE; pos = 2; } else if (bom[0] == 0xff && bom[1] == 0xfe) { - file->text_type = TFT_UTF16LE; + m_text_type = text_file_type::UTF16LE; pos = 2; } else { - file->text_type = TFT_OSD; + m_text_type = text_file_type::OSD; pos = 0; } } - core_fseek(file, pos, SEEK_SET); + seek(pos, SEEK_SET); } - /* fetch the next character */ - switch (file->text_type) + // fetch the next character + utf16_char utf16_buffer[UTF16_CHAR_MAX]; + unicode_char uchar = unicode_char(~0); + switch (m_text_type) { - default: - case TFT_OSD: - readlen = core_fread(file, default_buffer, sizeof(default_buffer)); + default: + case text_file_type::OSD: + { + char default_buffer[16]; + auto const readlen = read(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); + auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0])); + seek(std::int64_t(charlen * sizeof(default_buffer[0])) - readlen, SEEK_CUR); } - break; + } + break; - case TFT_UTF8: - readlen = core_fread(file, utf8_buffer, sizeof(utf8_buffer)); + case text_file_type::UTF8: + { + char utf8_buffer[UTF8_CHAR_MAX]; + auto const readlen = read(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); + auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0])); + seek(std::int64_t(charlen * sizeof(utf8_buffer[0])) - readlen, SEEK_CUR); } - break; + } + break; - case TFT_UTF16BE: - readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); + case text_file_type::UTF16BE: + { + auto const readlen = read(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); + auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); + seek(std::int64_t(charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); } - break; + } + break; - case TFT_UTF16LE: - readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); + case text_file_type::UTF16LE: + { + auto const readlen = read(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); + auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); + seek(std::int64_t(charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); } - break; + } + break; - case TFT_UTF32BE: - if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar)) - uchar = BIG_ENDIANIZE_INT32(uchar); - break; + case text_file_type::UTF32BE: + if (read(&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; + case text_file_type::UTF32LE: + if (read(&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);*/ + // place the new character in the ring buffer + m_back_char_head = 0; + m_back_char_tail = utf8_from_uchar(m_back_chars, ARRAY_LENGTH(m_back_chars), uchar); + //assert(file->back_char_tail != -1); } } - /* now read from the ring buffer */ - if (file->back_char_head != file->back_char_tail) + // now read from the ring buffer + if (m_back_char_head == m_back_char_tail) + result = EOF; + else { - result = file->back_chars[file->back_char_head++]; - file->back_char_head %= ARRAY_LENGTH(file->back_chars); + result = m_back_chars[m_back_char_head++]; + m_back_char_head %= ARRAY_LENGTH(m_back_chars); } - else - result = EOF; return result; } /*------------------------------------------------- - core_ungetc - put back a character read from - a file + ungetc - put back a character read from a + file -------------------------------------------------*/ -int core_ungetc(int c, core_file *file) +int core_text_file::ungetc(int c) { - file->back_chars[file->back_char_tail++] = (char) c; - file->back_char_tail %= ARRAY_LENGTH(file->back_chars); + m_back_chars[m_back_char_tail++] = char(c); + m_back_char_tail %= ARRAY_LENGTH(m_back_chars); return c; } /*------------------------------------------------- - core_fgets - read a line from a text file + gets - read a line from a text file -------------------------------------------------*/ -char *core_fgets(char *s, int n, core_file *file) +char *core_text_file::gets(char *s, int n) { char *cur = s; - /* loop while we have characters */ + // loop while we have characters while (n > 0) { - int c = core_fgetc(file); + int const c = getc(); if (c == EOF) break; - - /* if there's a CR, look for an LF afterwards */ - if (c == 0x0d) + else if (c == 0x0d) // if there's a CR, look for an LF afterwards { - int c2 = core_fgetc(file); + int const c2 = getc(); if (c2 != 0x0a) - core_ungetc(c2, file); + ungetc(c2); *cur++ = 0x0d; n--; break; } - - /* if there's an LF, reinterp as a CR for consistency */ - else if (c == 0x0a) + else if (c == 0x0a) // if there's an LF, reinterp as a CR for consistency { *cur++ = 0x0d; n--; break; } - - /* otherwise, pop the character in and continue */ - *cur++ = c; - n--; + else // otherwise, pop the character in and continue + { + *cur++ = c; + n--; + } } - /* if we put nothing in, return NULL */ + // if we put nothing in, return NULL if (cur == s) return nullptr; @@ -648,335 +551,412 @@ char *core_fgets(char *s, int n, core_file *file) /*------------------------------------------------- - core_fbuffer - return a pointer to the file - buffer; if it doesn't yet exist, load the - file into RAM first + puts - write a line to a text file -------------------------------------------------*/ -const void *core_fbuffer(core_file *file) +int core_text_file::puts(char const *s) { - file_error filerr; - UINT32 read_length; - - /* if we already have data, just return it */ - if (file->data != nullptr || !file->length) - return file->data; + char convbuf[1024]; + char *pconvbuf = convbuf; + int count = 0; - /* allocate some memory */ - file->data = (UINT8 *)malloc(file->length); - if (file->data == nullptr) - return nullptr; - file->data_allocated = TRUE; + // is this the beginning of the file? if so, write a byte order mark + if (tell() == 0 && !no_bom()) + { + *pconvbuf++ = char(0xef); + *pconvbuf++ = char(0xbb); + *pconvbuf++ = char(0xbf); + } - /* read the file */ - filerr = osd_or_zlib_read(file, file->data, 0, file->length, &read_length); - if (filerr != FILERR_NONE || read_length != file->length) + // convert '\n' to platform dependant line endings + while (*s != '\0') { - free(file->data); - file->data = nullptr; - return nullptr; + 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 += write(convbuf, pconvbuf - convbuf); + pconvbuf = convbuf; + } } - /* close the file because we don't need it anymore */ - osd_close(file->file); - file->file = nullptr; - return file->data; + // final flush + if (pconvbuf != convbuf) + count += write(convbuf, pconvbuf - convbuf); + + return count; } /*------------------------------------------------- - core_fload - open a file with the specified - filename, read it into memory, and return a - pointer + vprintf - vfprintf to a text file -------------------------------------------------*/ -file_error core_fload(const char *filename, void **data, UINT32 *length) +int core_text_file::vprintf(char const *fmt, va_list va) { - core_file *file = nullptr; - file_error err; - UINT64 size; + char buf[1024]; + vsnprintf(buf, sizeof(buf), fmt, va); + return puts(buf); +} - /* 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 != nullptr) - *length = (UINT32)size; +/*************************************************************************** + core_in_memory_file +***************************************************************************/ - /* read the data */ - if (core_fread(file, *data, size) != size) +/*------------------------------------------------- + seek - seek within a file +-------------------------------------------------*/ + +int core_in_memory_file::seek(std::int64_t offset, int whence) +{ + // flush any buffered char + clear_putback(); + + // switch off the relative location + switch (whence) { - core_fclose(file); - free(*data); - return FILERR_FAILURE; + case SEEK_SET: + m_offset = offset; + break; + + case SEEK_CUR: + m_offset += offset; + break; + + case SEEK_END: + m_offset = m_length + offset; + break; } + return 0; +} - /* close the file and return data */ - core_fclose(file); - return FILERR_NONE; + +/*------------------------------------------------- + eof - return 1 if we're at the end of file +-------------------------------------------------*/ + +bool core_in_memory_file::eof() const +{ + // check for buffered chars + if (has_putback()) + return 0; + + // if the offset == length, we're at EOF + return (m_offset >= m_length); } -file_error core_fload(const char *filename, dynamic_buffer &data) + +/*------------------------------------------------- + read - read from a file +-------------------------------------------------*/ + +std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length) { - core_file *file = nullptr; - file_error err; - UINT64 size; + clear_putback(); - /* attempt to open the file */ - err = core_fopen(filename, OPEN_FLAG_READ, &file); - if (err != FILERR_NONE) - return err; + // handle RAM-based files + auto const bytes_read = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length); + m_offset += bytes_read; + return bytes_read; +} - /* get the size */ - size = core_fsize(file); - if ((UINT32)size != size) - { - core_fclose(file); - return FILERR_OUT_OF_MEMORY; - } - /* allocate memory */ - data.resize(size); +/*------------------------------------------------- + truncate - truncate a file +-------------------------------------------------*/ - /* read the data */ - if (core_fread(file, &data[0], size) != size) - { - core_fclose(file); - data.clear(); +file_error core_in_memory_file::truncate(std::uint64_t offset) +{ + if (m_length < offset) return FILERR_FAILURE; - } - /* close the file and return data */ - core_fclose(file); + // adjust to new length and offset + set_length(offset); return FILERR_NONE; } - -/*************************************************************************** - FILE WRITE -***************************************************************************/ - /*------------------------------------------------- - core_fwrite - write to a file + safe_buffer_copy - copy safely from one + bounded buffer to another -------------------------------------------------*/ -UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length) +std::size_t core_in_memory_file::safe_buffer_copy( + void const *source, std::size_t sourceoffs, std::size_t sourcelen, + void *dest, std::size_t destoffs, std::size_t destlen) { - UINT32 bytes_written = 0; + auto const sourceavail = sourcelen - sourceoffs; + auto const destavail = destlen - destoffs; + auto const bytes_to_copy = (std::min)(sourceavail, destavail); + if (bytes_to_copy > 0) + { + std::memcpy( + reinterpret_cast<std::uint8_t *>(dest) + destoffs, + reinterpret_cast<std::uint8_t const *>(source) + sourceoffs, + bytes_to_copy); + } + return bytes_to_copy; +} - /* can't write to RAM-based stuff */ - if (file->data != nullptr) - return 0; - /* flush any buffered char */ - file->back_char_head = 0; - file->back_char_tail = 0; - /* invalidate any buffered data */ - file->bufferbytes = 0; +/*************************************************************************** + core_osd_file +***************************************************************************/ - /* do the write */ - osd_or_zlib_write(file, buffer, file->offset, length, &bytes_written); +/*------------------------------------------------- + closes a file +-------------------------------------------------*/ - /* return the number of bytes read */ - file->offset += bytes_written; - file->length = MAX(file->length, file->offset); - return bytes_written; +core_osd_file::~core_osd_file() +{ + // close files and free memory + if (m_zdata) + compress(FCOMPRESS_NONE); + if (m_file) + osd_close(m_file); } /*------------------------------------------------- - core_fputs - write a line to a text file + compress - enable/disable streaming file + compression via zlib; level is 0 to disable + compression, or up to 9 for max compression -------------------------------------------------*/ -int core_fputs(core_file *f, const char *s) +file_error core_osd_file::compress(int level) { - char convbuf[1024]; - char *pconvbuf = convbuf; - int count = 0; + file_error result = FILERR_NONE; - /* 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; - } + // can only do this for read-only and write-only cases + if (read_access() && write_access()) + return FILERR_INVALID_ACCESS; - /* convert '\n' to platform dependant line endings */ - while (*s != 0) + // if we have been compressing, flush and free the data + if (m_zdata && (level == FCOMPRESS_NONE)) { - if (*s == '\n') + int zerr = Z_OK; + + // flush any remaining data if we are writing + while (write_access() != 0 && (zerr != Z_STREAM_END)) { - if (CRLF == 1) /* CR only */ - *pconvbuf++ = 13; - else if (CRLF == 2) /* LF only */ - *pconvbuf++ = 10; - else if (CRLF == 3) /* CR+LF */ + // deflate some more + zerr = m_zdata->finalise(); + if (zerr != Z_STREAM_END && zerr != Z_OK) { - *pconvbuf++ = 13; - *pconvbuf++ = 10; + result = FILERR_INVALID_DATA; + break; } - } - 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; + // write the data + if (m_zdata->has_output()) + { + std::uint32_t actualdata; + auto const filerr = osd_write(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), &actualdata); + if (filerr != FILERR_NONE) + break; + m_zdata->add_realoffset(actualdata); + m_zdata->reset_output(); + } } + + // free memory + m_zdata.reset(); } - /* final flush */ - if (pconvbuf != convbuf) - count += core_fwrite(f, convbuf, pconvbuf - convbuf); + // if we are just starting to compress, allocate a new buffer + if (!m_zdata && (level > FCOMPRESS_NONE)) + { + int zerr; - return count; -} + // initialize the stream and compressor + if (write_access()) + zerr = zlib_data::start_compression(level, offset(), m_zdata); + else + zerr = zlib_data::start_decompression(offset(), m_zdata); + // on error, return an error + if (zerr != Z_OK) + return FILERR_OUT_OF_MEMORY; -/*------------------------------------------------- - core_vfprintf - vfprintf to a text file --------------------------------------------------*/ + // flush buffers + m_bufferbytes = 0; + } -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); + return result; } /*------------------------------------------------- - core_fprintf - vfprintf to a text file + seek - seek within a file -------------------------------------------------*/ -int CLIB_DECL core_fprintf(core_file *f, const char *fmt, ...) +int core_osd_file::seek(std::int64_t offset, int whence) { - int rc; - va_list va; - va_start(va, fmt); - rc = core_vfprintf(f, fmt, va); - va_end(va); - return rc; + // error if compressing + if (m_zdata) + return 1; + else + return core_in_memory_file::seek(offset, whence); } /*------------------------------------------------- - core_truncate - truncate a file + read - read from a file -------------------------------------------------*/ -file_error core_truncate(core_file *f, UINT64 offset) +std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length) { - file_error err; + if (!m_file || is_loaded()) + return core_in_memory_file::read(buffer, length); - /* truncate file */ - err = osd_truncate(f->file, offset); - if (err != FILERR_NONE) - return err; + // flush any buffered char + clear_putback(); - /* and adjust to new length and offset */ - f->length = offset; - f->offset = MIN(f->offset, f->length); + std::uint32_t bytes_read = 0; - return FILERR_NONE; + // if we're within the buffer, consume that first + if (is_buffered()) + bytes_read += safe_buffer_copy(m_buffer, offset() - m_bufferbase, m_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(m_buffer) / 2)) + { + // read as much as makes sense into the buffer + m_bufferbase = offset() + bytes_read; + m_bufferbytes = 0; + osd_or_zlib_read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes); + + // do a bounded copy from the buffer to the destination + bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length); + } + else + { + // read the remainder directly from the file + std::uint32_t new_bytes_read = 0; + osd_or_zlib_read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); + bytes_read += new_bytes_read; + } + } + + // return the number of bytes read + add_offset(bytes_read); + return bytes_read; } /*------------------------------------------------- - core_truncate - flush write buffer + buffer - return a pointer to the file buffer; + if it doesn't yet exist, load the file into + RAM first -------------------------------------------------*/ -file_error core_fflush(core_file *f) +void const *core_osd_file::buffer() { - return osd_fflush(f->file); + // if we already have data, just return it + if (!is_loaded() && length()) + { + // allocate some memory + void *buf = allocate(); + if (!buf) return nullptr; + + // read the file + std::uint32_t read_length = 0; + auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length); + if ((filerr != FILERR_NONE) || (read_length != length())) + purge(); + else + { + // close the file because we don't need it anymore + osd_close(m_file); + m_file = nullptr; + } + } + return core_in_memory_file::buffer(); } - -/*************************************************************************** - FILENAME UTILITIES -***************************************************************************/ - /*------------------------------------------------- - core_filename_extract_base - extract the base - name from a filename; note that this makes - assumptions about path separators + write - write to a file -------------------------------------------------*/ -std::string core_filename_extract_base(const char *name, bool strip_extension) +std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) { - /* find the start of the name */ - const char *start = name + strlen(name); - while (start > name && !is_directory_separator(start[-1])) - start--; + // can't write to RAM-based stuff + if (is_loaded()) + return core_in_memory_file::write(buffer, length); - /* copy the rest into an astring */ - std::string result(start); + // flush any buffered char + clear_putback(); - /* chop the extension if present */ - if (strip_extension) - result = result.substr(0, result.find_last_of('.')); - return result; + // invalidate any buffered data + m_bufferbytes = 0; + + // do the write + std::uint32_t bytes_written = 0; + osd_or_zlib_write(buffer, offset(), length, bytes_written); + + // return the number of bytes written + add_offset(bytes_written); + return bytes_written; } /*------------------------------------------------- - core_filename_ends_with - does the given - filename end with the specified extension? + truncate - truncate a file -------------------------------------------------*/ -int core_filename_ends_with(const char *filename, const char *extension) +file_error core_osd_file::truncate(std::uint64_t offset) { - int namelen = strlen(filename); - int extlen = strlen(extension); - int matches = TRUE; + if (is_loaded()) + return core_in_memory_file::truncate(offset); - /* work backwards checking for a match */ - while (extlen > 0) - if (tolower((UINT8)filename[--namelen]) != tolower((UINT8)extension[--extlen])) - { - matches = FALSE; - break; - } + // truncate file + auto const err = osd_truncate(m_file, offset); + if (err != FILERR_NONE) + return err; - return matches; + // and adjust to new length and offset + set_length(offset); + return FILERR_NONE; } - -/*************************************************************************** - MISC HELPERS -***************************************************************************/ - /*------------------------------------------------- - safe_buffer_copy - copy safely from one - bounded buffer to another + flush - flush file buffers -------------------------------------------------*/ -static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen) +file_error core_osd_file::flush() { - 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; + if (is_loaded()) + return core_in_memory_file::flush(); + + // flush any buffered char + clear_putback(); + + // invalidate any buffered data + m_bufferbytes = 0; + + return osd_fflush(m_file); } @@ -985,50 +965,47 @@ static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sou handles zlib-compressed data -------------------------------------------------*/ -static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) { - /* if no compression, just pass through */ - if (file->zdata == nullptr) - return osd_read(file->file, buffer, offset, length, actual); + // if no compression, just pass through + if (!m_zdata) + return osd_read(m_file, buffer, offset, length, &actual); - /* if the offset doesn't match the next offset, fail */ - if (offset != file->zdata->nextoffset) + // if the offset doesn't match the next offset, fail + if (m_zdata->is_nextoffset(offset)) 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) + // set up the destination + m_zdata->set_output(buffer, length); + while (!m_zdata->output_full()) { - 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 we didn't make progress, report an error or the end + if (m_zdata->has_input()) + zerr = m_zdata->decompress(); if (zerr != Z_OK) { - *actual = length - file->zdata->stream.avail_out; - file->zdata->nextoffset += *actual; + actual = length - m_zdata->output_space(); + m_zdata->add_nextoffset(actual); return (zerr == Z_STREAM_END) ? FILERR_NONE : FILERR_INVALID_DATA; } - /* fetch more data if needed */ - if (file->zdata->stream.avail_in == 0) + // fetch more data if needed + if (!m_zdata->has_input()) { - filerr = osd_read(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata); + std::uint32_t actualdata; + auto const filerr = osd_read(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), &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); + m_zdata->add_realoffset(actual); + m_zdata->reset_input(actualdata); } } - /* we read everything */ - *actual = length; - file->zdata->nextoffset += *actual; + // we read everything + actual = length; + m_zdata->add_nextoffset(actual); return FILERR_NONE; } @@ -1039,61 +1016,307 @@ static file_error osd_or_zlib_read(core_file *file, void *buffer, UINT64 offset, -------------------------------------------------*/ /** - * @fn static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) + * @fn file_error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) * * @brief OSD or zlib write. * - * @param [in,out] file If non-null, the file. * @param buffer The buffer. * @param offset The offset. * @param length The length. - * @param [in,out] actual If non-null, the actual. + * @param [in,out] actual The actual. * * @return A file_error. */ -static file_error osd_or_zlib_write(core_file *file, const void *buffer, UINT64 offset, UINT32 length, UINT32 *actual) +file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) { - /* if no compression, just pass through */ - if (file->zdata == nullptr) - return osd_write(file->file, buffer, offset, length, actual); + // if no compression, just pass through + if (!m_zdata) + return osd_write(m_file, buffer, offset, length, &actual); - /* if the offset doesn't match the next offset, fail */ - if (offset != file->zdata->nextoffset) + // if the offset doesn't match the next offset, fail + if (!m_zdata->is_nextoffset(offset)) 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) + // set up the source + m_zdata->set_input(buffer, length); + while (m_zdata->has_input()) { - 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 we didn't make progress, report an error or the end + auto const zerr = m_zdata->compress(); if (zerr != Z_OK) { - *actual = length - file->zdata->stream.avail_in; - file->zdata->nextoffset += *actual; + actual = length - m_zdata->input_size(); + m_zdata->add_nextoffset(actual); return FILERR_INVALID_DATA; } - /* write more data if we are full up */ - if (file->zdata->stream.avail_out == 0) + // write more data if we are full up + if (m_zdata->output_full()) { - filerr = osd_write(file->file, file->zdata->buffer, file->zdata->realoffset, sizeof(file->zdata->buffer), &actualdata); + std::uint32_t actualdata; + auto const filerr = osd_write(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), &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); + m_zdata->add_realoffset(actualdata); + m_zdata->reset_output(); } } - /* we read everything */ - *actual = length; - file->zdata->nextoffset += *actual; + // we wrote everything + actual = length; + m_zdata->add_nextoffset(actual); return FILERR_NONE; } + +} // anonymous namespace + + + +/*************************************************************************** + core_file +***************************************************************************/ + +/*------------------------------------------------- + open - open a file for access and + return an error code +-------------------------------------------------*/ + +file_error core_file::open(char const *filename, std::uint32_t openflags, ptr &file) +{ + // attempt to open the file + osd_file *f = nullptr; + std::uint64_t length = 0; + auto const filerr = osd_open(filename, openflags, &f, &length); + if (filerr != FILERR_NONE) + return filerr; + + try + { + file = std::make_unique<core_osd_file>(openflags, f, length); + return FILERR_NONE; + } + catch (...) + { + osd_close(f); + return FILERR_OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + open_ram - open a RAM-based buffer for file- + like access and return an error code +-------------------------------------------------*/ + +file_error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +{ + // can only do this for read access + if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) + return FILERR_INVALID_ACCESS; + + try + { + file.reset(new core_in_memory_file(openflags, data, length, false)); + return FILERR_NONE; + } + catch (...) + { + return FILERR_OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + open_ram_copy - open a copy of a RAM-based + buffer for file-like access and return an + error code +-------------------------------------------------*/ + +file_error core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +{ + // can only do this for read access + if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) + return FILERR_INVALID_ACCESS; + + try + { + ptr result(new core_in_memory_file(openflags, data, length, true)); + if (!result->buffer()) + return FILERR_OUT_OF_MEMORY; + + return FILERR_NONE; + } + catch (...) + { + return FILERR_OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + open_proxy - open a proxy to an existing file + object and return an error code +-------------------------------------------------*/ + +file_error core_file::open_proxy(core_file &file, ptr &proxy) +{ + try + { + proxy = std::make_unique<core_proxy_file>(file); + return FILERR_NONE; + } + catch (...) + { + return FILERR_OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + closes a file +-------------------------------------------------*/ + +core_file::~core_file() +{ +} + + +/*------------------------------------------------- + load - open a file with the specified + filename, read it into memory, and return a + pointer +-------------------------------------------------*/ + +file_error core_file::load(char const *filename, void **data, std::uint32_t &length) +{ + ptr file; + + // attempt to open the file + auto const err = open(filename, OPEN_FLAG_READ, file); + if (err != FILERR_NONE) + return err; + + // get the size + auto const size = file->size(); + if (std::uint32_t(size) != size) + return FILERR_OUT_OF_MEMORY; + + // allocate memory + *data = osd_malloc(size); + length = std::uint32_t(size); + + // read the data + if (file->read(*data, size) != size) + { + free(*data); + return FILERR_FAILURE; + } + + // close the file and return data + return FILERR_NONE; +} + +file_error core_file::load(char const *filename, dynamic_buffer &data) +{ + ptr file; + + // attempt to open the file + auto const err = open(filename, OPEN_FLAG_READ, file); + if (err != FILERR_NONE) + return err; + + // get the size + auto const size = file->size(); + if (std::uint32_t(size) != size) + return FILERR_OUT_OF_MEMORY; + + // allocate memory + data.resize(size); + + // read the data + if (file->read(&data[0], size) != size) + { + data.clear(); + return FILERR_FAILURE; + } + + // close the file and return data + return FILERR_NONE; +} + + +/*------------------------------------------------- + printf - printf to a text file +-------------------------------------------------*/ + +int CLIB_DECL core_file::printf(char const *fmt, ...) +{ + va_list va; + va_start(va, fmt); + auto const rc = vprintf(fmt, va); + va_end(va); + return rc; +} + + +/*------------------------------------------------- + protected constructor +-------------------------------------------------*/ + +core_file::core_file() +{ +} + +} // namespace util + + + +/*************************************************************************** + FILENAME UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + core_filename_extract_base - extract the base + name from a filename; note that this makes + assumptions about path separators +-------------------------------------------------*/ + +std::string core_filename_extract_base(const char *name, bool strip_extension) +{ + /* find the start of the name */ + const char *start = name + strlen(name); + while (start > name && !util::is_directory_separator(start[-1])) + start--; + + /* copy the rest into an astring */ + std::string result(start); + + /* chop the extension if present */ + if (strip_extension) + result = result.substr(0, result.find_last_of('.')); + 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; +} |