From 73b44c94291c4d086477167a76fd7db239936c3b Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Sun, 6 Mar 2016 18:02:37 +1100 Subject: Turn core_file into a proper class that gets cleaned up safely using unique_ptr Subverted somewhat by chd_file class --- src/lib/util/corefile.cpp | 1631 ++++++++++++++++++++++++++------------------- 1 file changed, 927 insertions(+), 704 deletions(-) (limited to 'src/lib/util/corefile.cpp') 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 -#include +#include +#include +#include #include +namespace util { + +namespace { + /*************************************************************************** VALIDATION ***************************************************************************/ @@ -29,614 +35,511 @@ -/*************************************************************************** - 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 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(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(reinterpret_cast(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(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); - } - - /* 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 +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 +***************************************************************************/ + +/*------------------------------------------------- + seek - seek within a file +-------------------------------------------------*/ - /* read the data */ - if (core_fread(file, *data, size) != size) +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(dest) + destoffs, + reinterpret_cast(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(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(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(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; +} -- cgit v1.2.3-70-g09d2 From 4e3ca74170905e651cfff374bcfc6d58977fc515 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 7 Mar 2016 20:31:52 +1100 Subject: Fix loading zipped image --- src/lib/util/corefile.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/lib/util/corefile.cpp') diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index e4eda2477d5..51819809f93 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -1145,6 +1145,7 @@ file_error core_file::open_ram_copy(void const *data, std::size_t length, std::u if (!result->buffer()) return FILERR_OUT_OF_MEMORY; + file = std::move(result); return FILERR_NONE; } catch (...) -- cgit v1.2.3-70-g09d2 From a5072bfd810ee0f841a644475fd2e811a37a592e Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Tue, 8 Mar 2016 19:37:57 +1100 Subject: Fix complete failure to read zlib compressed data, handle EOF better in compressed files (nw) Fix bug causing crash in pathological case of zero-frame INP file [Vas Crabb] --- src/emu/ioport.cpp | 3 ++- src/lib/util/corefile.cpp | 41 +++++++++++++++++++++-------------------- 2 files changed, 23 insertions(+), 21 deletions(-) (limited to 'src/lib/util/corefile.cpp') diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index a9ac5682b04..9ae33a85a1e 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -3447,7 +3447,8 @@ void ioport_manager::playback_end(const char *message) machine().popmessage("Playback Ended\nReason: %s", message); // display speed stats - m_playback_accumulated_speed /= m_playback_accumulated_frames; + if (m_playback_accumulated_speed > 0) + m_playback_accumulated_speed /= m_playback_accumulated_frames; osd_printf_info("Total playback frames: %d\n", UINT32(m_playback_accumulated_frames)); osd_printf_info("Average recorded speed: %d%%\n", UINT32((m_playback_accumulated_speed * 200 + 1) >> 21)); diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index 51819809f93..b4a246d81f6 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -126,6 +126,7 @@ private: m_stream.zalloc = Z_NULL; m_stream.zfree = Z_NULL; m_stream.opaque = Z_NULL; + m_stream.avail_in = m_stream.avail_out = 0; } bool m_compress, m_decompress; @@ -341,7 +342,7 @@ private: works for most platforms -------------------------------------------------*/ -static inline int is_directory_separator(char c) +inline int is_directory_separator(char c) { return (c == '\\' || c == '/' || c == ':'); } @@ -755,11 +756,11 @@ file_error core_osd_file::compress(int level) int zerr = Z_OK; // flush any remaining data if we are writing - while (write_access() != 0 && (zerr != Z_STREAM_END)) + while (write_access() && (zerr != Z_STREAM_END)) { // deflate some more zerr = m_zdata->finalise(); - if (zerr != Z_STREAM_END && zerr != Z_OK) + if ((zerr != Z_STREAM_END) && (zerr != Z_OK)) { result = FILERR_INVALID_DATA; break; @@ -972,41 +973,41 @@ file_error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, s return osd_read(m_file, buffer, offset, length, &actual); // if the offset doesn't match the next offset, fail - if (m_zdata->is_nextoffset(offset)) + if (!m_zdata->is_nextoffset(offset)) return FILERR_INVALID_ACCESS; // set up the destination + file_error filerr = FILERR_NONE; m_zdata->set_output(buffer, length); while (!m_zdata->output_full()) { - int zerr = Z_OK; - // 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 - m_zdata->output_space(); - m_zdata->add_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 (!m_zdata->has_input()) { - 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; - m_zdata->add_realoffset(actual); + 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; + // adjust everything + actual = length - m_zdata->output_space(); m_zdata->add_nextoffset(actual); - return FILERR_NONE; + return filerr; } @@ -1054,7 +1055,7 @@ file_error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t of // write more data if we are full up if (m_zdata->output_full()) { - std::uint32_t 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; -- cgit v1.2.3-70-g09d2 From 5aea0893a03648efe27025f88d6b98cd48129869 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Wed, 9 Mar 2016 18:02:13 +1100 Subject: Use type-safe printf for core_file and emu_file, surprisingly few knock-on effects Properly fix up a couple of places I64FMT was being used, still more to deal with --- src/devices/sound/discrete.cpp | 17 ++++++++--------- src/devices/sound/esqpump.cpp | 10 +++++----- src/emu/cheat.cpp | 3 +++ src/emu/cheat.h | 2 +- src/emu/fileio.cpp | 19 ++----------------- src/emu/fileio.h | 7 +++++-- src/lib/util/corefile.cpp | 36 +++++++++++++----------------------- src/lib/util/corefile.h | 9 ++++++--- src/mame/drivers/ssem.cpp | 41 ++++++++++++++++++++++++----------------- src/tools/chdman.cpp | 33 ++++++++++++++------------------- 10 files changed, 81 insertions(+), 96 deletions(-) (limited to 'src/lib/util/corefile.cpp') diff --git a/src/devices/sound/discrete.cpp b/src/devices/sound/discrete.cpp index dc66910707f..e09c47c9bf0 100644 --- a/src/devices/sound/discrete.cpp +++ b/src/devices/sound/discrete.cpp @@ -39,6 +39,7 @@ #include "sound/wavwrite.h" #include "discrete.h" #include +#include /* for_each collides with c++ standard libraries - include it here */ #define for_each(_T, _e, _l) for (_T _e = (_l)->begin_ptr() ; _e <= (_l)->end_ptr(); _e++) @@ -647,15 +648,15 @@ void discrete_device::display_profiling(void) total = list_run_time(m_node_list); count = m_node_list.count(); /* print statistics */ - printf("%s",string_format("Total Samples : %16I64d\n", m_total_samples).c_str()); + util::stream_format(std::cout, "Total Samples : %16d\n", m_total_samples); tresh = total / count; - printf("%s",string_format("Threshold (mean): %16I64d\n", tresh / m_total_samples).c_str()); + util::stream_format(std::cout, "Threshold (mean): %16d\n", tresh / m_total_samples ); for_each(discrete_base_node **, node, &m_node_list) { discrete_step_interface *step; if ((*node)->interface(step)) if (step->run_time > tresh) - printf("%3d: %20s %8.2f %10.2f\n", (*node)->index(), (*node)->module_name(), (double) step->run_time / (double) total * 100.0, ((double) step->run_time) / (double) m_total_samples); + util::stream_format(std::cout, "%3d: %20s %8.2f %10.2f\n", (*node)->index(), (*node)->module_name(), double(step->run_time) / double(total) * 100.0, double(step->run_time) / double(m_total_samples)); } /* Task information */ @@ -663,10 +664,10 @@ void discrete_device::display_profiling(void) { tt = step_list_run_time((*task)->step_list); - printf("Task(%d): %8.2f %15.2f\n", (*task)->task_group, tt / (double) total * 100.0, tt / (double) m_total_samples); + util::stream_format(std::cout, "Task(%d): %8.2f %15.2f\n", (*task)->task_group, tt / double(total) * 100.0, tt / double(m_total_samples)); } - printf("Average samples/double->update: %8.2f\n", (double) m_total_samples / (double) m_total_stream_updates); + util::stream_format(std::cout, "Average samples/double->update: %8.2f\n", double(m_total_samples) / double(m_total_stream_updates)); } @@ -737,7 +738,7 @@ void discrete_device::init_nodes(const sound_block_list_t &block_list) task->task_group = block->initial[0]; if (task->task_group < 0 || task->task_group >= DISCRETE_MAX_TASK_GROUPS) fatalerror("discrete_dso_task: illegal task_group %d\n", task->task_group); - //printf("task group %d\n", task->task_group); + //util::stream_format(std::cout, "task group %d\n", task->task_group); task_list.add(task); } break; @@ -870,7 +871,6 @@ void discrete_device::device_start() //m_stream = machine().sound().stream_alloc(*this, 0, 2, 22257); const discrete_block *intf_start = m_intf; - char name[128]; /* If a clock is specified we will use it, otherwise run at the audio sample rate. */ if (this->clock()) @@ -884,9 +884,8 @@ void discrete_device::device_start() m_total_stream_updates = 0; /* create the logfile */ - sprintf(name, "discrete%s.log", this->tag()); if (DISCRETE_DEBUGLOG) - m_disclogfile = fopen(name, "w"); + m_disclogfile = fopen(util::string_format("discrete%s.log", this->tag()).c_str(), "w"); /* enable profiling */ m_profiling = 0; diff --git a/src/devices/sound/esqpump.cpp b/src/devices/sound/esqpump.cpp index e9ad6cedf85..9873029e056 100644 --- a/src/devices/sound/esqpump.cpp +++ b/src/devices/sound/esqpump.cpp @@ -112,7 +112,7 @@ void esq_5505_5510_pump::sound_stream_update(sound_stream &stream, stream_sample e[(ei + 0x1d0f) % 0x4000] = e_next; if (l != e[ei]) { - fprintf(stderr, "expected (%d) but have (%d)\n", e[ei], l); + util::stream_format(std::cerr, "expected (%d) but have (%d)\n", e[ei], l); } ei = (ei + 1) % 0x4000; #endif @@ -133,9 +133,9 @@ void esq_5505_5510_pump::sound_stream_update(sound_stream &stream, stream_sample bool silence = silent_for >= 500; if (was_silence != silence) { if (!silence) { - fprintf(stderr, ".-*\n"); + util::stream_format(std::cerr, ".-*\n"); } else { - fprintf(stderr, "*-.\n"); + util::stream_format(std::cerr, "*-.\n"); } was_silence = silence; } @@ -148,7 +148,7 @@ void esq_5505_5510_pump::sound_stream_update(sound_stream &stream, stream_sample { osd_ticks_t elapsed = now - last_ticks; osd_ticks_t tps = osd_ticks_per_second(); - fprintf(stderr, "%s",string_format("Pump: %d samples in %I64d ticks for %f Hz\n", last_samples, elapsed, last_samples * (double)tps / (double)elapsed).c_str()); + util::stream_format(std::cerr, "Pump: %d samples in %d ticks for %f Hz\n", last_samples, elapsed, last_samples * (double)tps / (double)elapsed); last_ticks = now; while (next_report_ticks <= now) { next_report_ticks += tps; @@ -156,7 +156,7 @@ void esq_5505_5510_pump::sound_stream_update(sound_stream &stream, stream_sample last_samples = 0; #if !PUMP_FAKE_ESP_PROCESSING - fprintf(stderr, "%s",string_format(" ESP spent %I64d ticks on %d samples, %f ticks per sample\n", ticks_spent_processing, samples_processed, (double)ticks_spent_processing / (double)samples_processed).c_str()); + util::stream_format(std::cerr, " ESP spent %d ticks on %d samples, %f ticks per sample\n", ticks_spent_processing, samples_processed, (double)ticks_spent_processing / (double)samples_processed); ticks_spent_processing = 0; samples_processed = 0; #endif diff --git a/src/emu/cheat.cpp b/src/emu/cheat.cpp index 6b24b22fc35..0a799ca6f13 100644 --- a/src/emu/cheat.cpp +++ b/src/emu/cheat.cpp @@ -1031,6 +1031,9 @@ std::unique_ptr &cheat_entry::script_for_state(script_state state) // CHEAT MANAGER //************************************************************************** +const int cheat_manager::CHEAT_VERSION; + + //------------------------------------------------- // cheat_manager - constructor //------------------------------------------------- diff --git a/src/emu/cheat.h b/src/emu/cheat.h index 6badabd14d6..20772e25640 100644 --- a/src/emu/cheat.h +++ b/src/emu/cheat.h @@ -329,7 +329,7 @@ private: symbol_table m_symtable; // global symbol table // constants - static const int CHEAT_VERSION = 1; + static constexpr int CHEAT_VERSION = 1; }; diff --git a/src/emu/fileio.cpp b/src/emu/fileio.cpp index 23f21f8b9b7..79bf746fbfa 100644 --- a/src/emu/fileio.cpp +++ b/src/emu/fileio.cpp @@ -617,25 +617,10 @@ int emu_file::puts(const char *s) // vfprintf - vfprintf to a text file //------------------------------------------------- -int emu_file::vprintf(const char *fmt, va_list va) +int emu_file::vprintf(util::format_argument_pack const &args) { // write the data if we can - return (m_file) ? m_file->vprintf(fmt, va) : 0; -} - - -//------------------------------------------------- -// printf - vfprintf to a text file -//------------------------------------------------- - -int CLIB_DECL emu_file::printf(const char *fmt, ...) -{ - int rc; - va_list va; - va_start(va, fmt); - rc = vprintf(fmt, va); - va_end(va); - return rc; + return m_file ? m_file->vprintf(args) : 0; } diff --git a/src/emu/fileio.h b/src/emu/fileio.h index 9a65349614f..22f36f5362e 100644 --- a/src/emu/fileio.h +++ b/src/emu/fileio.h @@ -135,8 +135,11 @@ public: // writing UINT32 write(const void *buffer, UINT32 length); int puts(const char *s); - int vprintf(const char *fmt, va_list va); - int printf(const char *fmt, ...) ATTR_PRINTF(2,3); + int vprintf(util::format_argument_pack const &args); + template int printf(Format &&fmt, Params &&...args) + { + return vprintf(util::make_format_argument_pack(std::forward(fmt), std::forward(args)...)); + } // buffers void flush(); diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index b4a246d81f6..fd98c8be59f 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -8,14 +8,15 @@ ***************************************************************************/ -#include - #include "corefile.h" + #include "unicode.h" +#include "vecstream.h" + #include #include -#include +#include #include #include @@ -157,7 +158,7 @@ public: 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 int vprintf(util::format_argument_pack 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(); } @@ -184,7 +185,7 @@ public: 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; + virtual int vprintf(util::format_argument_pack const &args) override; protected: core_text_file(std::uint32_t openflags) @@ -192,6 +193,7 @@ protected: , m_text_type(text_file_type::OSD) , m_back_char_head(0) , m_back_char_tail(0) + , m_printf_buffer() { } @@ -208,6 +210,7 @@ private: 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 }; @@ -608,11 +611,12 @@ int core_text_file::puts(char const *s) vprintf - vfprintf to a text file -------------------------------------------------*/ -int core_text_file::vprintf(char const *fmt, va_list va) +int core_text_file::vprintf(util::format_argument_pack const &args) { - char buf[1024]; - vsnprintf(buf, sizeof(buf), fmt, va); - return puts(buf); + m_printf_buffer.seekp(0, ovectorstream::beg); + util::stream_format(m_printf_buffer, args); + m_printf_buffer.put('\0'); + return puts(&m_printf_buffer.vec()[0]); } @@ -1248,20 +1252,6 @@ file_error core_file::load(char const *filename, dynamic_buffer &data) } -/*------------------------------------------------- - 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 -------------------------------------------------*/ diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h index a5837e6de21..7ec76af7cb5 100644 --- a/src/lib/util/corefile.h +++ b/src/lib/util/corefile.h @@ -13,9 +13,9 @@ #ifndef __COREFILE_H__ #define __COREFILE_H__ -#include #include "corestr.h" #include "coretmpl.h" +#include "strformat.h" #include #include @@ -114,8 +114,11 @@ public: virtual int puts(const char *s) = 0; // printf-style text write to a file - virtual int vprintf(const char *fmt, va_list va) = 0; - int CLIB_DECL printf(const char *fmt, ...) ATTR_PRINTF(2,3); + virtual int vprintf(util::format_argument_pack const &args) = 0; + template int printf(Format &&fmt, Params &&...args) + { + return vprintf(util::make_format_argument_pack(std::forward(fmt), std::forward(args)...)); + } // file truncation virtual file_error truncate(std::uint64_t offset) = 0; diff --git a/src/mame/drivers/ssem.cpp b/src/mame/drivers/ssem.cpp index 2fdc6eefebe..a35cb104d92 100644 --- a/src/mame/drivers/ssem.cpp +++ b/src/mame/drivers/ssem.cpp @@ -18,21 +18,29 @@ public: : driver_device(mconfig, type, tag), m_maincpu(*this, "maincpu"), m_store(*this, "store"), - m_screen(*this, "screen") { } - - required_device m_maincpu; - required_shared_ptr m_store; - required_device m_screen; + m_screen(*this, "screen") + { + } - UINT8 m_store_line; virtual void machine_start() override; virtual void machine_reset() override; UINT32 screen_update_ssem(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect); DECLARE_INPUT_CHANGED_MEMBER(panel_check); DECLARE_QUICKLOAD_LOAD_MEMBER(ssem_store); inline UINT32 reverse(UINT32 v); - void glyph_print(bitmap_rgb32 &bitmap, INT32 x, INT32 y, const char *msg, ...) ATTR_PRINTF(5,6); void strlower(char *buf); + +private: + template + void glyph_print(bitmap_rgb32 &bitmap, INT32 x, INT32 y, Format &&fmt, Params &&...args); + + required_device m_maincpu; + required_shared_ptr m_store; + required_device m_screen; + + UINT8 m_store_line; + + util::ovectorstream m_glyph_print_buf; }; @@ -403,20 +411,18 @@ static const UINT8 char_glyphs[0x80][8] = { 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff }, }; -void ssem_state::glyph_print(bitmap_rgb32 &bitmap, INT32 x, INT32 y, const char *msg, ...) +template +void ssem_state::glyph_print(bitmap_rgb32 &bitmap, INT32 x, INT32 y, Format &&fmt, Params &&...args) { - va_list arg_list; - char buf[32768]; - INT32 index = 0; const rectangle &visarea = m_screen->visible_area(); - va_start( arg_list, msg ); - vsprintf( buf, msg, arg_list ); - va_end( arg_list ); + m_glyph_print_buf.seekp(0, util::ovectorstream::beg); + util::stream_format(m_glyph_print_buf, std::forward(fmt), std::forward(args)...); + m_glyph_print_buf.put('\0'); - for(index = 0; index < strlen(buf) && index < 32768; index++) + for(char const *buf = &m_glyph_print_buf.vec()[0]; *buf; buf++) { - UINT8 cur = (UINT8)buf[index]; + UINT8 cur = UINT8(*buf); if(cur < 0x80) { INT32 line = 0; @@ -493,7 +499,7 @@ UINT32 ssem_state::screen_update_ssem(screen_device &screen, bitmap_rgb32 &bitma (m_store[(m_store_line << 2) | 1] << 16) | (m_store[(m_store_line << 2) | 2] << 8) | (m_store[(m_store_line << 2) | 3] << 0)); - glyph_print(bitmap, 0, 272, "%s",string_format("LINE:%02d VALUE:%08x HALT:%I64d", m_store_line, word, m_maincpu->state_int(SSEM_HALT)).c_str()); + glyph_print(bitmap, 0, 272, "LINE:%02u VALUE:%08x HALT:%d", m_store_line, word, m_maincpu->state_int(SSEM_HALT)); return 0; } @@ -609,6 +615,7 @@ QUICKLOAD_LOAD_MEMBER(ssem_state, ssem_store) void ssem_state::machine_start() { save_item(NAME(m_store_line)); + m_glyph_print_buf.reserve(1024); } void ssem_state::machine_reset() diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp index 0358a4c5620..1a80bc76e13 100644 --- a/src/tools/chdman.cpp +++ b/src/tools/chdman.cpp @@ -23,8 +23,10 @@ #include #include #include -#include + +#include #include +#include @@ -105,7 +107,7 @@ const int MODE_GDI = 2; typedef std::unordered_map parameters_t; -static void report_error(int error, const char *format, ...) ATTR_PRINTF(2,3); +template static void report_error(int error, Format &&fmt, Params &&...args); static void do_info(parameters_t ¶ms); static void do_verify(parameters_t ¶ms); static void do_create_raw(parameters_t ¶ms); @@ -727,15 +729,11 @@ static const command_description s_commands[] = // report_error - report an error //------------------------------------------------- -static void report_error(int error, const char *format, ...) +template static void report_error(int error, Format &&fmt, Params &&...args) { // output to stderr - va_list arg; - va_start(arg, format); - vfprintf(stderr, format, arg); - fflush(stderr); - va_end(arg); - fprintf(stderr, "\n"); + util::stream_format(std::cerr, std::forward(fmt), std::forward(args)...); + std::cerr << std::endl; // reset time for progress and return the error lastprogress = 0; @@ -747,7 +745,7 @@ static void report_error(int error, const char *format, ...) // progress - generic progress callback //------------------------------------------------- -static void ATTR_PRINTF(2,3) progress(bool forceit, const char *format, ...) +template static void progress(bool forceit, Format &&fmt, Params &&...args) { // skip if it hasn't been long enough clock_t curtime = clock(); @@ -756,11 +754,8 @@ static void ATTR_PRINTF(2,3) progress(bool forceit, const char *format, ...) lastprogress = curtime; // standard vfprintf stuff here - va_list arg; - va_start(arg, format); - vfprintf(stderr, format, arg); - fflush(stderr); - va_end(arg); + util::stream_format(std::cerr, std::forward(fmt), std::forward(args)...); + std::cerr << std::flush; } @@ -1237,7 +1232,7 @@ void output_track_metadata(int mode, util::core_file &file, int tracknum, const break; } bool needquote = strchr(filename, ' ') != nullptr; - file.printf("%s",string_format("%d %d %d %d %s%s%s %I64d\n", tracknum+1, frameoffs, mode, size, needquote?"\"":"", filename, needquote?"\"":"", discoffs).c_str()); + file.printf("%d %d %d %d %s%s%s %d\n", tracknum+1, frameoffs, mode, size, needquote?"\"":"", filename, needquote?"\"":"", discoffs); } else if (mode == MODE_CUEBIN) { @@ -2598,7 +2593,7 @@ static void do_extract_ld(parameters_t ¶ms) if (err != CHDERR_NONE) { UINT64 filepos = static_cast(input_chd).tell(); - report_error(1, "%s",string_format("Error reading hunk %I64d at offset %I64d from CHD file (%s): %s\n", framenum, filepos, params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err)).c_str()); + report_error(1, "Error reading hunk %d at offset %d from CHD file (%s): %s\n", framenum, filepos, params.find(OPTION_INPUT)->second->c_str(), chd_file::error_string(err)); } // write audio @@ -2606,7 +2601,7 @@ static void do_extract_ld(parameters_t ¶ms) { avi_error avierr = avi_append_sound_samples(output_file, chnum, avconfig.audio[chnum], actsamples, 0); if (avierr != AVIERR_NONE) - report_error(1, "%s",string_format("Error writing samples for hunk %I64d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_error_string(avierr)).c_str()); + report_error(1, "Error writing samples for hunk %d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_error_string(avierr)); } // write video @@ -2614,7 +2609,7 @@ static void do_extract_ld(parameters_t ¶ms) { avi_error avierr = avi_append_video_frame(output_file, fullbitmap); if (avierr != AVIERR_NONE) - report_error(1, "%s",string_format("Error writing video for hunk %I64d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_error_string(avierr)).c_str()); + report_error(1, "Error writing video for hunk %d to file (%s): %s\n", framenum, output_file_str->second->c_str(), avi_error_string(avierr)); } } -- cgit v1.2.3-70-g09d2 From 8dad674507a9e8d5ae6e3cb23df717ea0c41443b Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Thu, 10 Mar 2016 04:41:08 +1100 Subject: Allow seek to position 0 in a vectorstream with empty storage, always reserve 1k for core_file printf buffer --- src/lib/util/corefile.cpp | 1 + src/lib/util/vecstream.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'src/lib/util/corefile.cpp') diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index fd98c8be59f..64169a606fc 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -613,6 +613,7 @@ int core_text_file::puts(char const *s) int core_text_file::vprintf(util::format_argument_pack const &args) { + m_printf_buffer.reserve(1024); m_printf_buffer.seekp(0, ovectorstream::beg); util::stream_format(m_printf_buffer, args); m_printf_buffer.put('\0'); diff --git a/src/lib/util/vecstream.h b/src/lib/util/vecstream.h index 498e35eac5e..bf144501253 100644 --- a/src/lib/util/vecstream.h +++ b/src/lib/util/vecstream.h @@ -116,7 +116,7 @@ public: void reserve(typename vector_type::size_type size) { - if ((m_mode & std::ios_base::out) && (m_storage.size() < size)) + if ((m_mode & std::ios_base::out) && (m_storage.capacity() < size)) { m_storage.reserve(size); adjust(); @@ -150,8 +150,8 @@ protected: bool const out(which & std::ios_base::out); if ((!in && !out) || (in && out && (std::ios_base::cur == dir)) || - (in && (!(m_mode & std::ios_base::in) || !this->gptr())) || - (out && (!(m_mode & std::ios_base::out) || !this->pptr()))) + (in && !(m_mode & std::ios_base::in)) || + (out && !(m_mode & std::ios_base::out))) { return pos_type(off_type(-1)); } -- cgit v1.2.3-70-g09d2