summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/corefile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/corefile.cpp')
-rw-r--r--src/lib/util/corefile.cpp1642
1 files changed, 929 insertions, 713 deletions
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp
index 25978afdb2c..64169a606fc 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
@@ -8,17 +8,24 @@
***************************************************************************/
-#include <assert.h>
-
#include "corefile.h"
+
#include "unicode.h"
+#include "vecstream.h"
+
#include <zlib.h>
-#include <stdarg.h>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
#include <ctype.h>
+namespace util {
+
+namespace {
+
/***************************************************************************
VALIDATION
***************************************************************************/
@@ -30,613 +37,513 @@
/***************************************************************************
- 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
+class zlib_data
{
- 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
--------------------------------------------------*/
+public:
+ typedef std::unique_ptr<zlib_data> ptr;
-static inline int is_directory_separator(char c)
-{
- return (c == '\\' || c == '/' || c == ':');
-}
-
-
-
-/***************************************************************************
- FILE OPEN/CLOSE
-***************************************************************************/
+ 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;
+ }
-/*-------------------------------------------------
- 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;
+ m_stream.avail_in = m_stream.avail_out = 0;
}
- /* 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(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); }
+ 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(util::format_argument_pack<std::ostream> const &args) 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)
+ , m_printf_buffer()
+ {
+ }
-/*-------------------------------------------------
- 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
+ ovectorstream m_printf_buffer; // persistent buffer for formatted output
+};
-/*-------------------------------------------------
- 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
--------------------------------------------------*/
+ virtual int seek(std::int64_t offset, int whence) override;
-UINT64 core_ftell(core_file *file)
-{
- /* return the current offset */
- return file->offset;
-}
+ virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
+ virtual void const *buffer() override;
+ 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;
-/*-------------------------------------------------
- core_feof - return 1 if we're at the end
- of file
--------------------------------------------------*/
+protected:
-int core_feof(core_file *file)
-{
- /* check for buffered chars */
- if (file->back_char_head != file->back_char_tail)
- return 0;
+ bool is_buffered() const { return (offset() >= m_bufferbase) && (offset() < (m_bufferbase + m_bufferbytes)); }
- /* if the offset == length, we're at EOF */
- return (file->offset >= file->length);
-}
+private:
+ static constexpr std::size_t FILE_BUFFER_SIZE = 512;
+ 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);
-/*-------------------------------------------------
- core_fsize - returns the size of a file
--------------------------------------------------*/
-
-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)
+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);
- }
-
- /* 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;
- }
- }
- }
+ return (c == '\\' || c == '/' || c == ':');
+}
- /* 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 +555,414 @@ 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(util::format_argument_pack<std::ostream> const &args)
{
- core_file *file = nullptr;
- file_error err;
- UINT64 size;
+ m_printf_buffer.reserve(1024);
+ m_printf_buffer.seekp(0, ovectorstream::beg);
+ util::stream_format<std::ostream, std::ostream>(m_printf_buffer, args);
+ m_printf_buffer.put('\0');
+ return puts(&m_printf_buffer.vec()[0]);
+}
- /* 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
+***************************************************************************/
+
+/*-------------------------------------------------
+ seek - seek within a file
+-------------------------------------------------*/
+
+int core_in_memory_file::seek(std::int64_t offset, int whence)
+{
+ // flush any buffered char
+ clear_putback();
- /* read the data */
- if (core_fread(file, *data, size) != size)
+ // 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() && (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,51 +971,48 @@ 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
+ file_error filerr = FILERR_NONE;
+ 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 (zerr != Z_OK)
+ // if we didn't make progress, report an error or the end
+ if (m_zdata->has_input())
{
- *actual = length - file->zdata->stream.avail_out;
- file->zdata->nextoffset += *actual;
- return (zerr == Z_STREAM_END) ? FILERR_NONE : FILERR_INVALID_DATA;
+ auto const zerr = m_zdata->decompress();
+ if (Z_OK != zerr)
+ {
+ if (Z_STREAM_END != zerr) filerr = FILERR_INVALID_DATA;
+ break;
+ }
}
- /* 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);
- 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);
+ std::uint32_t actualdata = 0;
+ filerr = osd_read(m_file, m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), &actualdata);
+ if (filerr != FILERR_NONE) break;
+ m_zdata->add_realoffset(actualdata);
+ m_zdata->reset_input(actualdata);
+ if (!m_zdata->has_input()) break;
}
}
- /* we read everything */
- *actual = length;
- file->zdata->nextoffset += *actual;
- return FILERR_NONE;
+ // adjust everything
+ actual = length - m_zdata->output_space();
+ m_zdata->add_nextoffset(actual);
+ return filerr;
}
@@ -1039,61 +1022,294 @@ 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 = 0;
+ 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;
+
+ file = std::move(result);
+ 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;
+}
+
+
+/*-------------------------------------------------
+ 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;
+}