summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author Vas Crabb <cuavas@users.noreply.github.com>2021-10-05 03:34:45 +1100
committer GitHub <noreply@github.com>2021-10-05 03:34:45 +1100
commitaeb9eae87469e67bc6a91caf3840c34c11d959fc (patch)
tree45bffedf374dbce47caca633ad7bda547592e6c9 /src/lib
parent33723892a3f06e678d817b36aef84364c32848ec (diff)
util: Further API cleanups: (#8661)
* Turned `core_file` into an implementation of `random_read_write`. * Turned PNG errors into a standard error category. * Added a helper for generating what look like derived classes on-the-fly.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/formats/cbm_crt.cpp50
-rw-r--r--src/lib/formats/cbm_crt.h2
-rw-r--r--src/lib/util/abi.h117
-rw-r--r--src/lib/util/cdrom.cpp7
-rw-r--r--src/lib/util/chd.cpp19
-rw-r--r--src/lib/util/corefile.cpp581
-rw-r--r--src/lib/util/corefile.h37
-rw-r--r--src/lib/util/delegate.cpp8
-rw-r--r--src/lib/util/delegate.h26
-rw-r--r--src/lib/util/dynamicclass.cpp229
-rw-r--r--src/lib/util/dynamicclass.h488
-rw-r--r--src/lib/util/dynamicclass.ipp505
-rw-r--r--src/lib/util/flac.cpp48
-rw-r--r--src/lib/util/flac.h18
-rw-r--r--src/lib/util/harddisk.cpp60
-rw-r--r--src/lib/util/harddisk.h6
-rw-r--r--src/lib/util/ioprocs.cpp354
-rw-r--r--src/lib/util/ioprocs.h10
-rw-r--r--src/lib/util/ioprocsfilter.cpp194
-rw-r--r--src/lib/util/ioprocsfilter.h11
-rw-r--r--src/lib/util/msdib.cpp76
-rw-r--r--src/lib/util/msdib.h8
-rw-r--r--src/lib/util/path.cpp3
-rw-r--r--src/lib/util/png.cpp452
-rw-r--r--src/lib/util/png.h40
-rw-r--r--src/lib/util/xmlfile.cpp20
-rw-r--r--src/lib/util/xmlfile.h2
27 files changed, 2419 insertions, 952 deletions
diff --git a/src/lib/formats/cbm_crt.cpp b/src/lib/formats/cbm_crt.cpp
index a61c3247844..2aca7688711 100644
--- a/src/lib/formats/cbm_crt.cpp
+++ b/src/lib/formats/cbm_crt.cpp
@@ -41,6 +41,8 @@
#include "cbm_crt.h"
+#include "corefile.h"
+
#include "osdcore.h" // osd_printf_*
@@ -134,9 +136,10 @@ std::string cbm_crt_get_card(util::core_file &file)
{
// read the header
cbm_crt_header header;
- file.read(&header, CRT_HEADER_LENGTH);
+ size_t actual;
+ std::error_condition err = file.read(&header, CRT_HEADER_LENGTH, actual);
- if (memcmp(header.signature, CRT_SIGNATURE, 16) == 0)
+ if (!err && (CRT_HEADER_LENGTH == actual) && !memcmp(header.signature, CRT_SIGNATURE, 16))
{
uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
@@ -153,11 +156,14 @@ std::string cbm_crt_get_card(util::core_file &file)
bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_size, int *exrom, int *game)
{
+ size_t actual;
+
// read the header
cbm_crt_header header;
- file.read(&header, CRT_HEADER_LENGTH);
+ if (file.read(&header, CRT_HEADER_LENGTH, actual))
+ return false;
- if (memcmp(header.signature, CRT_SIGNATURE, 16) != 0)
+ if ((CRT_HEADER_LENGTH != actual) || (memcmp(header.signature, CRT_SIGNATURE, 16) != 0))
return false;
uint16_t hardware = pick_integer_be(header.hardware, 0, 2);
@@ -177,11 +183,12 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
while (!file.eof())
{
cbm_crt_chip chip;
- file.read(&chip, CRT_CHIP_LENGTH);
+ if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual))
+ return false;
- uint16_t address = pick_integer_be(chip.start_address, 0, 2);
- uint16_t size = pick_integer_be(chip.image_size, 0, 2);
- uint16_t type = pick_integer_be(chip.chip_type, 0, 2);
+ const uint16_t address = pick_integer_be(chip.start_address, 0, 2);
+ const uint16_t size = pick_integer_be(chip.image_size, 0, 2);
+ const uint16_t type = pick_integer_be(chip.chip_type, 0, 2);
if (LOG)
{
@@ -198,7 +205,8 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
default: osd_printf_verbose("Invalid CHIP loading address!\n"); break;
}
- file.seek(size, SEEK_CUR);
+ if (file.seek(size, SEEK_CUR))
+ return false;
}
return true;
@@ -211,25 +219,33 @@ bool cbm_crt_read_header(util::core_file &file, size_t *roml_size, size_t *romh_
bool cbm_crt_read_data(util::core_file &file, uint8_t *roml, uint8_t *romh)
{
+ if (file.seek(CRT_HEADER_LENGTH, SEEK_SET))
+ return false;
+
uint32_t roml_offset = 0;
uint32_t romh_offset = 0;
- file.seek(CRT_HEADER_LENGTH, SEEK_SET);
-
while (!file.eof())
{
+ size_t actual;
+
cbm_crt_chip chip;
- file.read(&chip, CRT_CHIP_LENGTH);
+ if (file.read(&chip, CRT_CHIP_LENGTH, actual) || (CRT_CHIP_LENGTH != actual))
+ return false;
- uint16_t address = pick_integer_be(chip.start_address, 0, 2);
- uint16_t size = pick_integer_be(chip.image_size, 0, 2);
+ const uint16_t address = pick_integer_be(chip.start_address, 0, 2);
+ const uint16_t size = pick_integer_be(chip.image_size, 0, 2);
+ std::error_condition err;
switch (address)
{
- case 0x8000: file.read(roml + roml_offset, size); roml_offset += size; break;
- case 0xa000: file.read(romh + romh_offset, size); romh_offset += size; break;
- case 0xe000: file.read(romh + romh_offset, size); romh_offset += size; break;
+ case 0x8000: err = file.read(roml + roml_offset, size, actual); roml_offset += size; break;
+ case 0xa000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break;
+ case 0xe000: err = file.read(romh + romh_offset, size, actual); romh_offset += size; break;
+ // FIXME: surely one needs to report an error or skip over the data if the load address is not recognised?
}
+ if (err) // TODO: check size - all bets are off if the address isn't recognised anyway
+ return false;
}
return true;
diff --git a/src/lib/formats/cbm_crt.h b/src/lib/formats/cbm_crt.h
index d7b257f3e9e..7696dcc276e 100644
--- a/src/lib/formats/cbm_crt.h
+++ b/src/lib/formats/cbm_crt.h
@@ -14,7 +14,7 @@
#include "formats/imageutl.h"
-#include "corefile.h"
+#include "utilfwd.h"
//**************************************************************************
diff --git a/src/lib/util/abi.h b/src/lib/util/abi.h
new file mode 100644
index 00000000000..b9e18f16352
--- /dev/null
+++ b/src/lib/util/abi.h
@@ -0,0 +1,117 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/// \file
+/// \brief ABI feature macros
+///
+/// Macros that are useful for writing ABI-dependent code.
+#ifndef MAME_LIB_UTIL_ABI_H
+#define MAME_LIB_UTIL_ABI_H
+
+#pragma once
+
+
+/// \brief Itanium C++ ABI
+///
+/// Value of #MAME_ABI_CXX_TYPE when compiled with a variant of the
+/// Itanium C++ ABI.
+/// \sa MAME_ABI_CXX_TYPE MAME_ABI_CXX_MSVC
+#define MAME_ABI_CXX_ITANIUM 0
+
+/// \brief Microsoft Visual C++ ABI
+///
+/// Value of #MAME_ABI_CXX_TYPE when compiled with a variant of the
+/// Microsoft Visual C++ ABI.
+/// \sa MAME_ABI_CXX_TYPE MAME_ABI_CXX_ITANIUM
+#define MAME_ABI_CXX_MSVC 1
+
+
+/// \brief Standard Itanium C++ ABI member function pointers
+///
+/// Value of #MAME_ABI_CXX_ITANIUM_MFP_TYPE when compiled with a variant
+/// of the Itanium C++ ABI using the standard representation of
+/// pointers to non-static member functions.
+/// \sa MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
+#define MAME_ABI_CXX_ITANIUM_MFP_STANDARD 0
+
+/// \brief ARM Itanium C++ ABI member function pointers
+///
+/// Value of #MAME_ABI_CXX_ITANIUM_MFP_TYPE when compiled with a variant
+/// of the Itanium C++ ABI using the 32-bit ARM representation of
+/// pointers to non-static member functions.
+/// \sa MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_STANDARD
+#define MAME_ABI_CXX_ITANIUM_MFP_ARM 1
+
+
+/// \def MAME_ABI_FNDESC_SIZE
+/// \brief Size of function descriptors
+///
+/// Size of function descriptors as a multiple of the size of a pointer,
+/// or zero if function pointers point to the function entry point
+/// directly.
+#if (defined(__ppc64__) || defined(__PPC64__)) && !defined(__APPLE__) && !defined(__LITTLE_ENDIAN__)
+ #define MAME_ABI_FNDESC_SIZE 3 // entry point (PC), TOC (R2), environment (R11)
+#elif defined(__ia64__)
+ #define MAME_ABI_FNDESC_SIZE 2 // GP, entry point
+#else
+ #define MAME_ABI_FNDESC_SIZE 0 // function pointers point to entry point directly
+#endif
+
+
+/// \def MAME_ABI_CXX_TYPE
+/// \brief C++ ABI type
+///
+/// A constant representing the C++ ABI.
+/// \sa MAME_ABI_CXX_ITANIUM MAME_ABI_CXX_MSVC
+#if defined(_MSC_VER)
+ #define MAME_ABI_CXX_TYPE MAME_ABI_CXX_MSVC
+#else
+ #define MAME_ABI_CXX_TYPE MAME_ABI_CXX_ITANIUM
+#endif
+
+
+/// \def MAME_ABI_CXX_MEMBER_CALL
+/// \brief Member function calling convention qualifier
+///
+/// A qualifier for functions and function pointers that may be used to
+/// specify that the calling convention for non-static member functions
+/// should be used.
+#if defined(__GNUC__) && defined(__MINGW32__) && !defined(__x86_64__) && defined(__i386__)
+ #define MAME_ABI_CXX_MEMBER_CALL __thiscall
+#else
+ #define MAME_ABI_CXX_MEMBER_CALL
+#endif
+
+
+/// \def MAME_ABI_CXX_VTABLE_FNDESC
+/// \brief Whether function descriptors are stored in virtual tables
+///
+/// Non-zero if function descriptors are stored in virtual tables
+/// directly, or zero if function entries in virtual tables are
+/// conventional function pointers.
+/// \sa MAME_ABI_FNDESC_SIZE
+#if defined(__ia64__)
+ #define MAME_ABI_CXX_VTABLE_FNDESC 1 // function descriptors stored directly in vtable
+#else
+ #define MAME_ABI_CXX_VTABLE_FNDESC 0 // conventional function pointers in vtable
+#endif
+
+
+/// \def MAME_ABI_CXX_ITANIUM_MFP_TYPE
+/// Itanium C++ member function representation
+///
+/// A constant representing the representation of pointers to non-static
+/// member functions in use with the Itanium C++ ABI. Only valid if
+/// compiled with a variant of the Itanium C++ ABI.
+/// \sa MAME_ABI_CXX_ITANIUM_MFP_STANDARD MAME_ABI_CXX_ITANIUM_MFP_ARM
+/// MAME_ABI_CXX_TYPE
+#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
+ #define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
+#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
+ #define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
+#elif defined(__EMSCRIPTEN__)
+ #define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_ARM
+#else
+ #define MAME_ABI_CXX_ITANIUM_MFP_TYPE MAME_ABI_CXX_ITANIUM_MFP_STANDARD
+#endif
+
+#endif // MAME_LIB_UTIL_ABI_H
diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp
index 7510a1f9b47..8545ed4725a 100644
--- a/src/lib/util/cdrom.cpp
+++ b/src/lib/util/cdrom.cpp
@@ -510,8 +510,11 @@ std::error_condition read_partial_sector(cdrom_file *file, void *dest, uint32_t
// printf("Reading sector %d from track %d at offset %lu\n", chdsector, tracknum, sourcefileoffset);
- srcfile.seek(sourcefileoffset, SEEK_SET);
- srcfile.read(dest, length);
+ size_t actual;
+ result = srcfile.seek(sourcefileoffset, SEEK_SET);
+ if (!result)
+ result = srcfile.read(dest, length, actual);
+ // FIXME: if (actual < length) report error
needswap = file->track_info.track[tracknum].swap;
}
diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp
index 29792fc36df..1b4800c74f1 100644
--- a/src/lib/util/chd.cpp
+++ b/src/lib/util/chd.cpp
@@ -702,17 +702,14 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
if (filerr)
return filerr;
- util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
- if (!io)
- return std::errc::not_enough_memory;
// create the file normally, then claim the file
- std::error_condition chderr = create(std::move(io), logicalbytes, hunkbytes, unitbytes, compression);
+ std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression);
// if an error happened, close and delete the file
if (chderr)
{
- io.reset();
+ file.reset();
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
}
return chderr;
@@ -745,17 +742,14 @@ std::error_condition chd_file::create(std::string_view filename, uint64_t logica
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
if (filerr)
return filerr;
- util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
- if (!io)
- return std::errc::not_enough_memory;
// create the file normally, then claim the file
- std::error_condition chderr = create(std::move(io), logicalbytes, hunkbytes, compression, parent);
+ std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent);
// if an error happened, close and delete the file
if (chderr)
{
- io.reset();
+ file.reset();
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
}
return chderr;
@@ -787,12 +781,9 @@ std::error_condition chd_file::open(std::string_view filename, bool writeable, c
std::error_condition filerr = util::core_file::open(filename, openflags, file);
if (filerr)
return filerr;
- util::random_read_write::ptr io = util::core_file_read_write(std::move(file));
- if (!io)
- return std::errc::not_enough_memory;
// now open the CHD
- std::error_condition err = open(std::move(io), writeable, parent);
+ std::error_condition err = open(std::move(file), writeable, parent);
if (err)
return err;
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp
index 0c72e1337be..6406aea49f4 100644
--- a/src/lib/util/corefile.cpp
+++ b/src/lib/util/corefile.cpp
@@ -21,6 +21,7 @@
#include <cctype>
#include <cstring>
#include <iterator>
+#include <limits>
namespace util {
@@ -45,26 +46,30 @@ class core_proxy_file : public core_file
{
public:
core_proxy_file(core_file &file) noexcept : m_file(file) { }
- virtual ~core_proxy_file() override { }
- 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 std::error_condition seek(std::int64_t offset, int whence) noexcept override { return m_file.seek(offset, whence); }
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override { return m_file.tell(result); }
+ virtual std::error_condition length(std::uint64_t &result) noexcept override { return m_file.length(result); }
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read(buffer, length, actual); }
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_at(offset, buffer, length, actual); }
+
+ virtual std::error_condition finalize() noexcept override { return m_file.finalize(); }
+ virtual std::error_condition flush() noexcept override { return m_file.flush(); }
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write(buffer, length, actual); }
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_at(offset, buffer, length, actual); }
+
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 void const *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(std::string_view 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 std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); }
- virtual std::error_condition flush() override { return m_file.flush(); }
-
private:
core_file &m_file;
};
@@ -99,12 +104,12 @@ protected:
{
}
- 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); }
+ bool read_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_READ); }
+ bool write_access() const noexcept { return 0U != (m_openflags & OPEN_FLAG_WRITE); }
+ bool no_bom() const noexcept { return 0U != (m_openflags & OPEN_FLAG_NO_BOM); }
- 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; }
+ bool has_putback() const noexcept { return m_back_char_head != m_back_char_tail; }
+ void clear_putback() noexcept { m_back_char_head = m_back_char_tail = 0; }
private:
std::uint32_t const m_openflags; // flags we were opened with
@@ -119,7 +124,7 @@ private:
class core_in_memory_file : public core_text_file
{
public:
- core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy)
+ core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept
: core_text_file(openflags)
, m_data_allocated(false)
, m_data(copy ? nullptr : data)
@@ -136,20 +141,26 @@ public:
~core_in_memory_file() override { purge(); }
- virtual int seek(std::int64_t offset, int whence) override;
- virtual std::uint64_t tell() const override { return m_offset; }
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override;
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override { result = m_offset; return std::error_condition(); }
+ virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_length; return std::error_condition(); }
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
+ virtual std::error_condition finalize() noexcept override { return std::error_condition(); }
+ virtual std::error_condition flush() noexcept override { clear_putback(); return std::error_condition(); }
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
+
virtual bool eof() const override;
- virtual std::uint64_t size() const override { return m_length; }
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
virtual void const *buffer() override { return m_data; }
- virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; }
virtual std::error_condition truncate(std::uint64_t offset) override;
- virtual std::error_condition flush() override { clear_putback(); return std::error_condition(); }
protected:
- core_in_memory_file(std::uint32_t openflags, std::uint64_t length)
+ core_in_memory_file(std::uint32_t openflags, std::uint64_t length) noexcept
: core_text_file(openflags)
, m_data_allocated(false)
, m_data(nullptr)
@@ -158,10 +169,11 @@ protected:
{
}
- bool is_loaded() const { return nullptr != m_data; }
- void *allocate()
+ bool is_loaded() const noexcept { return nullptr != m_data; }
+
+ void *allocate() noexcept
{
- if (m_data)
+ if (m_data || (std::numeric_limits<std::size_t>::max() < m_length))
return nullptr;
void *data = malloc(m_length);
if (data)
@@ -171,7 +183,8 @@ protected:
}
return data;
}
- void purge()
+
+ void purge() noexcept
{
if (m_data && m_data_allocated)
free(const_cast<void *>(m_data));
@@ -179,14 +192,14 @@ protected:
m_data = nullptr;
}
- 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); }
+ std::uint64_t offset() const noexcept { return m_offset; }
+ void add_offset(std::size_t increment) noexcept { m_offset += increment; m_length = (std::max)(m_length, m_offset); }
+ std::uint64_t length() const noexcept { return m_length; }
+ void set_length(std::uint64_t value) noexcept { m_length = value; }
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);
+ void *dest, std::size_t destoffs, std::size_t destlen) noexcept;
private:
bool m_data_allocated; // was the data allocated by us?
@@ -196,35 +209,38 @@ private:
};
-class core_osd_file : public core_in_memory_file
+class core_osd_file final : public core_in_memory_file
{
public:
core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length)
: core_in_memory_file(openmode, length)
, m_file(std::move(file))
- , m_bufferbase(0)
- , m_bufferbytes(0)
{
}
~core_osd_file() override;
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
+ virtual std::error_condition finalize() noexcept override;
+ virtual std::error_condition flush() noexcept override;
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+
virtual void const *buffer() override;
- virtual std::uint32_t write(void const *buffer, std::uint32_t length) override;
virtual std::error_condition truncate(std::uint64_t offset) override;
- virtual std::error_condition flush() override;
protected:
- bool is_buffered() const { return (offset() >= m_bufferbase) && (offset() < (m_bufferbase + m_bufferbytes)); }
+ bool is_buffered(std::uint64_t offset) const noexcept { return (offset >= m_bufferbase) && (offset < (m_bufferbase + m_bufferbytes)); }
private:
static constexpr std::size_t FILE_BUFFER_SIZE = 512;
osd_file::ptr m_file; // OSD file handle
- std::uint64_t m_bufferbase; // base offset of internal buffer
- std::uint32_t m_bufferbytes; // bytes currently loaded into buffer
+ std::uint64_t m_bufferbase = 0U; // base offset of internal buffer
+ std::uint32_t m_bufferbytes = 0U; // bytes currently loaded into buffer
std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data
};
@@ -240,54 +256,57 @@ private:
int core_text_file::getc()
{
- int result;
-
// refresh buffer, if necessary
if (m_back_char_head == m_back_char_tail)
{
// do we need to check the byte order marks?
- if (tell() == 0)
+ std::uint64_t pos;
+ if (!tell(pos))
{
- std::uint8_t bom[4];
- int pos = 0;
-
- if (read(bom, 4) == 4)
+ if (!pos)
{
- if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
- {
- m_text_type = text_file_type::UTF8;
- pos = 3;
- }
- else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
- {
- m_text_type = text_file_type::UTF32BE;
- pos = 4;
- }
- else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ std::size_t readlen;
+ std::uint8_t bom[4];
+ read(bom, 4, readlen);
+ if (readlen == 4)
{
- m_text_type = text_file_type::UTF32LE;
- pos = 4;
- }
- else if (bom[0] == 0xfe && bom[1] == 0xff)
- {
- m_text_type = text_file_type::UTF16BE;
- pos = 2;
- }
- else if (bom[0] == 0xff && bom[1] == 0xfe)
- {
- m_text_type = text_file_type::UTF16LE;
- pos = 2;
- }
- else
- {
- m_text_type = text_file_type::OSD;
- pos = 0;
+ if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
+ {
+ m_text_type = text_file_type::UTF8;
+ pos = 3;
+ }
+ else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff)
+ {
+ m_text_type = text_file_type::UTF32BE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00)
+ {
+ m_text_type = text_file_type::UTF32LE;
+ pos = 4;
+ }
+ else if (bom[0] == 0xfe && bom[1] == 0xff)
+ {
+ m_text_type = text_file_type::UTF16BE;
+ pos = 2;
+ }
+ else if (bom[0] == 0xff && bom[1] == 0xfe)
+ {
+ m_text_type = text_file_type::UTF16LE;
+ pos = 2;
+ }
+ else
+ {
+ m_text_type = text_file_type::OSD;
+ pos = 0;
+ }
}
+ seek(pos, SEEK_SET); // FIXME: don't assume seeking is possible, check for errors
}
- seek(pos, SEEK_SET);
}
// fetch the next character
+ // FIXME: all of this plays fast and loose with error checking and seeks backwards far too frequently
char16_t utf16_buffer[UTF16_CHAR_MAX];
auto uchar = char32_t(~0);
switch (m_text_type)
@@ -296,7 +315,8 @@ int core_text_file::getc()
case text_file_type::OSD:
{
char default_buffer[16];
- auto const readlen = read(default_buffer, sizeof(default_buffer));
+ std::size_t readlen;
+ read(default_buffer, sizeof(default_buffer), readlen);
if (readlen > 0)
{
auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0]));
@@ -308,7 +328,8 @@ int core_text_file::getc()
case text_file_type::UTF8:
{
char utf8_buffer[UTF8_CHAR_MAX];
- auto const readlen = read(utf8_buffer, sizeof(utf8_buffer));
+ std::size_t readlen;
+ read(utf8_buffer, sizeof(utf8_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0]));
@@ -319,7 +340,8 @@ int core_text_file::getc()
case text_file_type::UTF16BE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ std::size_t readlen;
+ read(utf16_buffer, sizeof(utf16_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -330,7 +352,8 @@ int core_text_file::getc()
case text_file_type::UTF16LE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ std::size_t readlen;
+ read(utf16_buffer, sizeof(utf16_buffer), readlen);
if (readlen > 0)
{
auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -340,13 +363,23 @@ int core_text_file::getc()
break;
case text_file_type::UTF32BE:
- if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
- uchar = big_endianize_int32(uchar);
+ {
+ // FIXME: deal with read returning short
+ std::size_t readlen;
+ read(&uchar, sizeof(uchar), readlen);
+ if (sizeof(uchar) == readlen)
+ uchar = big_endianize_int32(uchar);
+ }
break;
case text_file_type::UTF32LE:
- if (read(&uchar, sizeof(uchar)) == sizeof(uchar))
- uchar = little_endianize_int32(uchar);
+ {
+ // FIXME: deal with read returning short
+ std::size_t readlen;
+ read(&uchar, sizeof(uchar), readlen);
+ if (sizeof(uchar) == readlen)
+ uchar = little_endianize_int32(uchar);
+ }
break;
}
@@ -360,6 +393,7 @@ int core_text_file::getc()
}
// now read from the ring buffer
+ int result;
if (m_back_char_head == m_back_char_tail)
result = EOF;
else
@@ -433,21 +467,32 @@ char *core_text_file::gets(char *s, int n)
/*-------------------------------------------------
- puts - write a line to a text file
+ puts - write a string to a text file
-------------------------------------------------*/
int core_text_file::puts(std::string_view s)
{
+ // TODO: what to do about write errors or short writes (interrupted)?
+ // The API doesn't lend itself to reporting the error as the return
+ // value includes extra bytes inserted like the UTF-8 marker and
+ // carriage returns.
char convbuf[1024];
char *pconvbuf = convbuf;
int count = 0;
// is this the beginning of the file? if so, write a byte order mark
- if (tell() == 0 && !no_bom())
+ if (!no_bom())
{
- *pconvbuf++ = char(0xef);
- *pconvbuf++ = char(0xbb);
- *pconvbuf++ = char(0xbf);
+ std::uint64_t offset;
+ if (!tell(offset))
+ {
+ if (!offset)
+ {
+ *pconvbuf++ = char(0xef);
+ *pconvbuf++ = char(0xbb);
+ *pconvbuf++ = char(0xbf);
+ }
+ }
}
// convert '\n' to platform dependant line endings
@@ -471,14 +516,20 @@ int core_text_file::puts(std::string_view s)
// if we overflow, break into chunks
if (pconvbuf >= convbuf + std::size(convbuf) - 10)
{
- count += write(convbuf, pconvbuf - convbuf);
+ std::size_t written;
+ write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ count += written;
pconvbuf = convbuf;
}
}
// final flush
if (pconvbuf != convbuf)
- count += write(convbuf, pconvbuf - convbuf);
+ {
+ std::size_t written;
+ write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ count += written;
+ }
return count;
}
@@ -507,27 +558,49 @@ int core_text_file::vprintf(util::format_argument_pack<std::ostream> const &args
seek - seek within a file
-------------------------------------------------*/
-int core_in_memory_file::seek(std::int64_t offset, int whence)
+std::error_condition core_in_memory_file::seek(std::int64_t offset, int whence) noexcept
{
// flush any buffered char
- clear_putback();
+ clear_putback(); // TODO: report errors; also, should the argument check happen before this?
// switch off the relative location
switch (whence)
{
case SEEK_SET:
+ if (0 > offset)
+ return std::errc::invalid_argument;
m_offset = offset;
- break;
+ return std::error_condition();
case SEEK_CUR:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_offset)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_offset)
+ {
+ return std::errc::invalid_argument;
+ }
m_offset += offset;
- break;
+ return std::error_condition();
case SEEK_END:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_length)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_length)
+ {
+ return std::errc::invalid_argument;
+ }
m_offset = m_length + offset;
- break;
+ return std::error_condition();
+
+ default:
+ return std::errc::invalid_argument;
}
- return 0;
}
@@ -550,14 +623,29 @@ bool core_in_memory_file::eof() const
read - read from a file
-------------------------------------------------*/
-std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length)
+std::error_condition core_in_memory_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
clear_putback();
// 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;
+ if (m_offset < m_length)
+ actual = safe_buffer_copy(m_data, std::size_t(m_offset), std::size_t(m_length), buffer, 0, length);
+ else
+ actual = 0U;
+ m_offset += actual;
+ return std::error_condition();
+}
+
+std::error_condition core_in_memory_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ clear_putback();
+
+ // handle RAM-based files
+ if (offset < m_length)
+ actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(m_length), buffer, 0, length);
+ else
+ actual = 0U;
+ return std::error_condition();
}
@@ -583,7 +671,7 @@ std::error_condition core_in_memory_file::truncate(std::uint64_t offset)
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)
+ void *dest, std::size_t destoffs, std::size_t destlen) noexcept
{
auto const sourceavail = sourcelen - sourceoffs;
auto const destavail = destlen - destoffs;
@@ -618,45 +706,65 @@ core_osd_file::~core_osd_file()
read - read from a file
-------------------------------------------------*/
-std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length)
+std::error_condition core_osd_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ // since osd_file works like pread/pwrite, implement in terms of read_at
+ // core_osd_file is delcared final, so a derived class can't interfere
+ std::error_condition err = read_at(offset(), buffer, length, actual);
+ add_offset(actual);
+ return err;
+}
+
+
+std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
{
if (!m_file || is_loaded())
- return core_in_memory_file::read(buffer, length);
+ return core_in_memory_file::read_at(offset, buffer, length, actual);
// flush any buffered char
clear_putback();
- std::uint32_t bytes_read = 0;
+ actual = 0U;
+ std::error_condition err;
// 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 (is_buffered(offset))
+ actual += safe_buffer_copy(m_buffer, offset - m_bufferbase, m_bufferbytes, buffer, actual, length);
// if we've got a small amount left, read it into the buffer first
- if (bytes_read < length)
+ if (actual < length)
{
- if ((length - bytes_read) < (sizeof(m_buffer) / 2))
+ if ((length - actual) < (sizeof(m_buffer) / 2))
{
// read as much as makes sense into the buffer
- m_bufferbase = offset() + bytes_read;
- m_bufferbytes = 0;
- m_file->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);
+ m_bufferbase = offset + actual;
+ err = m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
+
+ // do a bounded copy from the buffer to the destination if it succeeded
+ if (!err)
+ actual += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, actual, length);
+ else
+ m_bufferbytes = 0U;
}
else
{
// read the remainder directly from the file
- std::uint32_t new_bytes_read = 0;
- m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read);
- bytes_read += new_bytes_read;
+ do
+ {
+ // may need to split into chunks if size_t is larger than 32 bits
+ std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length - actual);
+ std::uint32_t bytes_read;
+ err = m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + actual, offset + actual, chunk, bytes_read);
+ if (err || !bytes_read)
+ break;
+ actual += bytes_read;
+ }
+ while (actual < length);
}
}
- // return the number of bytes read
- add_offset(bytes_read);
- return bytes_read;
+ // return any errors
+ return err;
}
@@ -672,17 +780,29 @@ void const *core_osd_file::buffer()
if (!is_loaded() && length())
{
// allocate some memory
- void *buf = allocate();
+ void *const buf = allocate();
if (!buf)
return nullptr;
// read the file
- std::uint32_t read_length = 0;
- auto const filerr = m_file->read(buf, 0, length(), read_length);
- if (filerr || (read_length != length()))
- purge();
- else
- m_file.reset(); // close the file because we don't need it anymore
+ std::uint64_t bytes_read = 0;
+ std::uint64_t remaining = length();
+ std::uint8_t *ptr = reinterpret_cast<std::uint8_t *>(buf);
+ while (remaining)
+ {
+ std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), remaining);
+ std::uint32_t read_length;
+ std::error_condition const filerr = m_file->read(ptr, bytes_read, chunk, read_length);
+ if (filerr || !read_length)
+ {
+ purge();
+ return core_in_memory_file::buffer();
+ }
+ bytes_read += read_length;
+ remaining -= read_length;
+ ptr += read_length;
+ }
+ m_file.reset(); // close the file because we don't need it anymore
}
return core_in_memory_file::buffer();
}
@@ -692,25 +812,46 @@ void const *core_osd_file::buffer()
write - write to a file
-------------------------------------------------*/
-std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length)
+std::error_condition core_osd_file::write(void const *buffer, std::size_t length, std::size_t &actual) noexcept
+{
+ // since osd_file works like pread/pwrite, implement in terms of write_at
+ // core_osd_file is delcared final, so a derived class can't interfere
+ std::error_condition err = write_at(offset(), buffer, length, actual);
+ add_offset(actual);
+ return err;
+}
+
+
+std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
{
// can't write to RAM-based stuff
if (is_loaded())
- return core_in_memory_file::write(buffer, length);
+ return core_in_memory_file::write_at(offset, buffer, length, actual);
// flush any buffered char
clear_putback();
// invalidate any buffered data
- m_bufferbytes = 0;
+ m_bufferbytes = 0U;
- // do the write
- std::uint32_t bytes_written = 0;
- m_file->write(buffer, offset(), length, bytes_written);
-
- // return the number of bytes written
- add_offset(bytes_written);
- return bytes_written;
+ // do the write - may need to split into chunks if size_t is larger than 32 bits
+ actual = 0U;
+ while (length)
+ {
+ // bytes written not valid on error
+ std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
+ std::uint32_t bytes_written;
+ std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written);
+ if (err)
+ return err;
+ assert(chunk >= bytes_written);
+ offset += bytes_written;
+ buffer = reinterpret_cast<std::uint8_t const *>(buffer) + bytes_written;
+ length -= bytes_written;
+ actual += bytes_written;
+ set_length((std::max)(this->length(), offset));
+ }
+ return std::error_condition();
}
@@ -724,7 +865,7 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
return core_in_memory_file::truncate(offset);
// truncate file
- auto const err = m_file->truncate(offset);
+ std::error_condition err = m_file->truncate(offset);
if (err)
return err;
@@ -738,7 +879,16 @@ std::error_condition core_osd_file::truncate(std::uint64_t offset)
flush - flush file buffers
-------------------------------------------------*/
-std::error_condition core_osd_file::flush()
+std::error_condition core_osd_file::finalize() noexcept
+{
+ if (is_loaded())
+ return core_in_memory_file::finalize();
+
+ return std::error_condition();
+}
+
+
+std::error_condition core_osd_file::flush() noexcept
{
if (is_loaded())
return core_in_memory_file::flush();
@@ -747,7 +897,7 @@ std::error_condition core_osd_file::flush()
clear_putback();
// invalidate any buffered data
- m_bufferbytes = 0;
+ m_bufferbytes = 0U;
return m_file->flush();
}
@@ -765,24 +915,19 @@ std::error_condition core_osd_file::flush()
return an error code
-------------------------------------------------*/
-std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept
{
- try
- {
- // attempt to open the file
- osd_file::ptr f;
- std::uint64_t length = 0;
- auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
- if (filerr)
- return filerr;
-
- file = std::make_unique<core_osd_file>(openflags, std::move(f), length);
- return std::error_condition();
- }
- catch (...)
- {
- return std::errc::not_enough_memory;
- }
+ // attempt to open the file
+ osd_file::ptr f;
+ std::uint64_t length = 0;
+ auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view
+ if (filerr)
+ return filerr;
+
+ try { file = std::make_unique<core_osd_file>(openflags, std::move(f), length); }
+ catch (...) { return std::errc::not_enough_memory; }
+
+ return std::error_condition();
}
@@ -791,21 +936,26 @@ std::error_condition core_file::open(std::string_view filename, std::uint32_t op
like access and return an error code
-------------------------------------------------*/
-std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
{
// can only do this for read access
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
return std::errc::invalid_argument;
- try
- {
- file.reset(new core_in_memory_file(openflags, data, length, false));
- return std::error_condition();
- }
- catch (...)
- {
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
+
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
+
+ ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, false));
+ if (!result)
return std::errc::not_enough_memory;
- }
+
+ file = std::move(result);
+ return std::error_condition();
}
@@ -815,25 +965,26 @@ std::error_condition core_file::open_ram(void const *data, std::size_t length, s
error code
-------------------------------------------------*/
-std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
+std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept
{
// can only do this for read access
if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
return std::errc::invalid_argument;
- try
- {
- ptr result(new core_in_memory_file(openflags, data, length, true));
- if (!result->buffer())
- return std::errc::not_enough_memory;
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
- file = std::move(result);
- return std::error_condition();
- }
- catch (...)
- {
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
+
+ ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, true));
+ if (!result || !result->buffer())
return std::errc::not_enough_memory;
- }
+
+ file = std::move(result);
+ return std::error_condition();
}
@@ -842,7 +993,7 @@ std::error_condition core_file::open_ram_copy(void const *data, std::size_t leng
object and return an error code
-------------------------------------------------*/
-std::error_condition core_file::open_proxy(core_file &file, ptr &proxy)
+std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept
{
ptr result(new (std::nothrow) core_proxy_file(file));
if (!result)
@@ -868,75 +1019,91 @@ core_file::~core_file()
pointer
-------------------------------------------------*/
-std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length)
+std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) noexcept
{
- ptr file;
+ std::error_condition err;
// attempt to open the file
- auto const err = open(filename, OPEN_FLAG_READ, file);
+ ptr file;
+ err = open(filename, OPEN_FLAG_READ, file);
if (err)
return err;
// get the size
- auto const size = file->size();
- if (std::uint32_t(size) != size)
+ std::uint64_t size;
+ err = file->length(size);
+ if (err)
+ return err;
+ else if (std::uint32_t(size) != size) // TODO: change interface to use size_t rather than uint32_t for output size
return std::errc::file_too_large;
// allocate memory
- *data = malloc(size);
+ *data = std::malloc(std::size_t(size));
if (!*data)
return std::errc::not_enough_memory;
length = std::uint32_t(size);
// read the data
- if (file->read(*data, size) != size)
+ if (size)
{
- free(*data);
- return std::errc::io_error; // TODO: revisit this error code
+ std::size_t actual;
+ err = file->read(*data, std::size_t(size), actual);
+ if (err || (size != actual))
+ {
+ std::free(*data);
+ *data = nullptr;
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
+ }
}
// close the file and return data
return std::error_condition();
}
-std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data)
+std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data) noexcept
{
- ptr file;
+ std::error_condition err;
// attempt to open the file
- auto const err = open(filename, OPEN_FLAG_READ, file);
+ ptr file;
+ err = open(filename, OPEN_FLAG_READ, file);
if (err)
return err;
// get the size
- auto const size = file->size();
- if (std::uint32_t(size) != size)
+ std::uint64_t size;
+ err = file->length(size);
+ if (err)
+ return err;
+ else if (std::size_t(size) != size)
return std::errc::file_too_large;
// allocate memory
- try { data.resize(size); }
+ try { data.resize(std::size_t(size)); }
catch (...) { return std::errc::not_enough_memory; }
// read the data
- if (file->read(&data[0], size) != size)
+ if (size)
{
- data.clear();
- return std::errc::io_error; // TODO: revisit this error code
+ std::size_t actual;
+ err = file->read(&data[0], std::size_t(size), actual);
+ if (err || (size != actual))
+ {
+ data.clear();
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
+ }
}
// close the file and return data
return std::error_condition();
}
-
-/*-------------------------------------------------
- protected constructor
--------------------------------------------------*/
-
-core_file::core_file()
-{
-}
-
} // namespace util
diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h
index 19febbb46f1..802dd1348a1 100644
--- a/src/lib/util/corefile.h
+++ b/src/lib/util/corefile.h
@@ -12,6 +12,7 @@
#pragma once
+#include "ioprocs.h"
#include "osdfile.h"
#include "strformat.h"
@@ -34,7 +35,7 @@ namespace util {
TYPE DEFINITIONS
***************************************************************************/
-class core_file
+class core_file : public random_read_write
{
public:
typedef std::unique_ptr<core_file> ptr;
@@ -43,16 +44,16 @@ public:
// ----- file open/close -----
// open a file with the specified filename
- static std::error_condition open(std::string_view filename, std::uint32_t openflags, ptr &file);
+ static std::error_condition open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept;
// open a RAM-based "file" using the given data and length (read-only)
- static std::error_condition open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file);
+ static std::error_condition open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept;
// open a RAM-based "file" using the given data and length (read-only), copying the data
- static std::error_condition open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file);
+ static std::error_condition open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file) noexcept;
// open a proxy "file" that forwards requests to another file object
- static std::error_condition open_proxy(core_file &file, ptr &proxy);
+ static std::error_condition open_proxy(core_file &file, ptr &proxy) noexcept;
// close an open file
virtual ~core_file();
@@ -60,24 +61,12 @@ public:
// ----- file positioning -----
- // adjust the file pointer within the file
- virtual int seek(std::int64_t offset, int whence) = 0;
-
- // return the current file pointer
- virtual std::uint64_t tell() const = 0;
-
// return true if we are at the EOF
virtual bool eof() const = 0;
- // return the total size of the file
- virtual std::uint64_t size() const = 0;
-
// ----- file read -----
- // standard binary read from a file
- virtual std::uint32_t read(void *buffer, std::uint32_t length) = 0;
-
// read one character from the file
virtual int getc() = 0;
@@ -92,15 +81,12 @@ public:
virtual const void *buffer() = 0;
// open a file with the specified filename, read it into memory, and return a pointer
- static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length);
- static std::error_condition load(std::string_view filename, std::vector<uint8_t> &data);
+ static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length) noexcept;
+ static std::error_condition load(std::string_view filename, std::vector<uint8_t> &data) noexcept;
// ----- file write -----
- // standard binary write to a file
- virtual std::uint32_t write(const void *buffer, std::uint32_t length) = 0;
-
// write a line of text to the file
virtual int puts(std::string_view s) = 0;
@@ -113,13 +99,6 @@ public:
// file truncation
virtual std::error_condition truncate(std::uint64_t offset) = 0;
-
- // flush file buffers
- virtual std::error_condition flush() = 0;
-
-
-protected:
- core_file();
};
} // namespace util
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp
index 09c2d77496f..880df3bc505 100644
--- a/src/lib/util/delegate.cpp
+++ b/src/lib/util/delegate.cpp
@@ -87,11 +87,11 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
// apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta));
object = reinterpret_cast<delegate_generic_class *>(
- reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> (MAME_DELEGATE_ITANIUM_ARM ? 1 : 0)));
+ reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0)));
LOG("Calculated this=%p ", reinterpret_cast<void const *>(object));
// test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant
- if (MAME_DELEGATE_ITANIUM_ARM ? !(m_this_delta & 1) : !(m_function & 1))
+ if ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? !(m_this_delta & 1) : !(m_function & 1))
{
// conventional function pointer
LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
@@ -100,7 +100,7 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
else
{
// byte index into the vtable to the function
- std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - (MAME_DELEGATE_ITANIUM_ARM ? 0 : 1);
+ std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1);
delegate_generic_function result;
if (MAME_DELEGATE_VT_DESCRIPTOR)
result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
@@ -159,7 +159,7 @@ delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generi
{
// relative jump with 32-bit displacement (typically a resolved PLT entry)
LOG("Found relative jump at %p ", func);
- func += 5 + *reinterpret_cast<std::int32_t const *>(func + 1);
+ func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
LOG("redirecting to %p\n", func);
continue;
}
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index 609b6667bfc..df1d6ebcba3 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -114,6 +114,8 @@
#pragma once
+#include "abi.h"
+
#include <any>
#include <cassert>
#include <cstddef>
@@ -143,35 +145,21 @@
#if defined(__MINGW32__) && !defined(__x86_64__) && defined(__i386__)
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
//#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
- //#define MAME_DELEGATE_MEMBER_ABI __thiscall
//#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 1
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#else
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
- #define MAME_DELEGATE_MEMBER_ABI
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#endif
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
- #define MAME_DELEGATE_MEMBER_ABI
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_MSVC
#else
#define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#endif
-#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__)
- #define MAME_DELEGATE_ITANIUM_ARM 1
-#elif defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64)
- #define MAME_DELEGATE_ITANIUM_ARM 1
-#elif defined(__EMSCRIPTEN__)
- #define MAME_DELEGATE_ITANIUM_ARM 1
-#else
- #define MAME_DELEGATE_ITANIUM_ARM 0
-#endif
-
#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
- #define MAME_DELEGATE_MEMBER_ABI
#define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#endif
@@ -359,7 +347,7 @@ ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object,
///
/// Supports the two most popular pointer to member function
/// implementations described in the Itanium C++ ABI. Both of these
-/// consist of a pointer followed by a pointer followed by a ptrdiff_t.
+/// consist of a pointer followed by a ptrdiff_t.
///
/// The first variant is used when member the least significant bit of a
/// member function pointer need never be set and vtable entry offsets
@@ -367,8 +355,8 @@ ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object,
/// it is a conventional function pointer to the member function. If
/// the pointer is odd, it is a byte offset into the vtable plus one.
/// The ptrdiff_t is a byte offset to add to the this pointer. A null
-/// member function pointer is represented by setting the pointer to
-/// a null pointer.
+/// member function pointer is represented by setting the pointer to a
+/// null pointer.
///
/// The second variant is used when the least significant bit of a
/// pointer to a member function may need to be set or it may not be
@@ -408,7 +396,7 @@ public:
bool isnull() const
{
- if (MAME_DELEGATE_ITANIUM_ARM)
+ if (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM)
return !reinterpret_cast<void (*)()>(m_function) && !(m_this_delta & 1);
else
return !reinterpret_cast<void (*)()>(m_function);
@@ -690,7 +678,7 @@ public:
// define our traits
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
- typedef MAME_DELEGATE_MEMBER_ABI generic_static_func generic_member_func;
+ typedef MAME_ABI_CXX_MEMBER_CALL generic_static_func generic_member_func;
// generic constructor
delegate_base() = default;
diff --git a/src/lib/util/dynamicclass.cpp b/src/lib/util/dynamicclass.cpp
new file mode 100644
index 00000000000..fc9924c09cf
--- /dev/null
+++ b/src/lib/util/dynamicclass.cpp
@@ -0,0 +1,229 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#include "dynamicclass.ipp"
+
+#include <locale>
+#include <new>
+#include <sstream>
+
+
+namespace util {
+
+namespace detail {
+
+/// \brief Complete object locator equivalent structure
+///
+/// Structure used for locating the complete object and type information
+/// for the MSVC C++ ABI on 64-bit targets. A pointer to this structure
+/// appears immediately before the first virtual member function entry
+/// in the virtual function table.
+struct dynamic_derived_class_base::msvc_complete_object_locator_equiv
+{
+ unsigned long signature; ///< Magic number, always 1
+ unsigned long offset; ///< Offset from complete object to current object
+ unsigned long ctor_disp_offset; ///< For avoiding extra constructor virtual tables for virtual inheritance
+ int type_info_offs; ///< Offset to type info
+ int class_info_offs; ///< Offset to class hierarchy info
+ int self_offs; ///< Offset to this structure for calculating module base
+};
+
+
+/// \brief Type info equivalent structure
+///
+/// Structure equivalent to the implementation of std::type_info for the
+/// MSVC C++ ABI. The structure is followed immediately by the
+/// decorated name. The pointer to the undecorated name is normally
+/// populated lazily when the \c name member function is called.
+struct dynamic_derived_class_base::msvc_type_info_equiv
+{
+ void const *vptr; ///< Pointer to virtual table
+ char const *undecorated; ///< Pointer to the undecorated name
+ char decorated[1]; ///< First character of the decorated name
+};
+
+
+/// \brief Construct dynamic derived class base
+///
+/// Creates type info for a class with a single base class using the
+/// specified name. If the name contains multiple components separated
+/// by \c :: separators, it is interpreted as a nested class name.
+/// \param [in] name The name for the dynamic class. Components must
+/// start with an alphabetic character or an underscore, and may
+/// contain only alphanumeric characters and underscores.
+/// \exception std::invalid_argument Thrown if the class name is invalid
+/// or unsupported.
+/// \exception std::bad_alloc Thrown if allocating memory for the type
+/// info fails.
+dynamic_derived_class_base::dynamic_derived_class_base(std::string_view name) :
+ m_base_vtable(nullptr)
+{
+ assert(!reinterpret_cast<void *>(std::uintptr_t(static_cast<void (*)()>(nullptr))));
+ assert(!reinterpret_cast<void (*)()>(std::uintptr_t(static_cast<void *>(nullptr))));
+
+ std::locale const &clocale(std::locale::classic());
+ if (name.empty() || (('_' != name[0]) && !std::isalpha(name[0], clocale)) || (std::find_if_not(name.begin(), name.end(), [&clocale] (char c) { return (':' == c) || ('_' == c) || std::isalnum(c, clocale); }) != name.end()))
+ throw std::invalid_argument("Invalid class name");
+
+ std::ostringstream str;
+ str.imbue(clocale);
+
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ str << "class " << name;
+ m_name = std::move(str).str();
+ str.str(std::string());
+
+ str << ".?AV";
+ std::string_view::size_type found = name.find(':');
+ while (std::string_view::npos != found)
+ {
+ if (((found + 2) >= name.length()) || (name[found + 1] != ':') || (('_' != name[found + 2]) && !std::isalpha(name[found + 2], clocale)))
+ throw std::invalid_argument("Invalid class name");
+
+ str.write(&name[0], found);
+ str << '@';
+ name.remove_prefix(found + 2);
+ found = name.find(':');
+ }
+ str << name << "@@";
+ std::string const mangled = std::move(str).str();
+
+ m_type_info = reinterpret_cast<msvc_type_info_equiv *>(
+ operator new (
+ offsetof(msvc_type_info_equiv, decorated) + mangled.length() + 1,
+ std::align_val_t(alignof(msvc_type_info_equiv))));
+ m_type_info->vptr = *reinterpret_cast<void const *const *>(&typeid(dynamic_derived_class_base));
+ m_type_info->undecorated = m_name.c_str();
+ std::copy_n(mangled.c_str(), mangled.length() + 1, m_type_info->decorated);
+#else
+ class base { };
+ class derived : base { };
+
+ m_type_info.vptr = *reinterpret_cast<void const *const *>(&typeid(derived));
+
+ std::string_view::size_type found = name.find(':');
+ bool const qualified = std::string::npos != found;
+ if (qualified)
+ str << 'N';
+ while (std::string_view::npos != found)
+ {
+ if (((found + 2) >= name.length()) || (name[found + 1] != ':') || (('_' != name[found + 2]) && !std::isalpha(name[found + 2], clocale)))
+ throw std::invalid_argument("Invalid class name");
+
+ str << found;
+ str.write(&name[0], found);
+ name.remove_prefix(found + 2);
+ found = name.find(':');
+ }
+ str << name.length() << name;
+ if (qualified)
+ str << 'E';
+ m_name = std::move(str).str();
+
+ m_type_info.name = m_name.c_str();
+#endif
+}
+
+
+dynamic_derived_class_base::~dynamic_derived_class_base()
+{
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ operator delete (m_type_info, std::align_val_t(alignof(msvc_type_info_equiv)));
+#endif
+}
+
+
+/// \brief Get virtual table index for member function
+///
+/// Gets the virtual table index represented by a pointer to a virtual
+/// member function. The \p slot argument must refer to a virtual
+/// member function returning a supported type that does not require
+/// \c this pointer adjustment.
+/// \param [in] slot Internal representation of pointer to a virtual
+/// member function. May be modified.
+/// \param [in] size Size of the member function pointer type for the
+/// \p slot argument.
+/// \return The virtual table index of the member function, in terms of
+/// the size of a virtual member function in the virtual table.
+/// \exception std::invalid_argument Thrown if the \p slot argument is
+/// not a supported virtual member function.
+std::size_t dynamic_derived_class_base::resolve_virtual_member_slot(
+ member_function_pointer_equiv &slot,
+ std::size_t size)
+{
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ if ((sizeof(msvc_mi_member_function_pointer_equiv) <= size) && slot.adj)
+ throw std::invalid_argument("Member function requires this pointer adjustment");
+#if defined(__x86_64__) || defined(_M_X64)
+ std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(slot.ptr);
+ while (0xe9 == func[0]) // relative jump with 32-bit displacement (typically a resolved PLT entry)
+ func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
+ if ((0x48 == func[0]) && (0x8b == func[1]) && (0x01 == func[2]))
+ {
+ if ((0xff == func[3]) && ((0x20 == func[4]) || (0x60 == func[4]) || (0xa0 == func[4])))
+ {
+ // MSVC virtual function call thunk - mov rax,QWORD PTR [rcx] ; jmp QWORD PTR [rax+...]
+ if (0x20 == func[4]) // no displacement
+ {
+ return 0;
+ }
+ else if (0x60 == func[4]) // 8-bit displacement
+ {
+ auto const index = *reinterpret_cast<std::int8_t const *>(func + 5);
+ if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
+ throw std::invalid_argument("Invalid member function virtual table index");
+ return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
+ }
+ else // 32-bit displacement
+ {
+ auto const index = *reinterpret_cast<std::int32_t const *>(func + 5);
+ if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
+ throw std::invalid_argument("Invalid member function virtual table index");
+ return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
+ }
+ }
+ else if ((0x48 == func[3]) && (0x8b == func[4]))
+ {
+ // clang virtual function call thunk - mov rax,QWORD PTR [rcx] ; mov rax,QWORD PTR [rax+...] ; jmp rax
+ if ((0x00 == func[5]) && (0x48 == func[6]) && (0xff == func[7]) && (0xe0 == func[8]))
+ {
+ // no displacement
+ return 0;
+ }
+ else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9]))
+ {
+ // 8-bit displacement
+ auto const index = *reinterpret_cast<std::int8_t const *>(func + 6);
+ if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
+ throw std::invalid_argument("Invalid member function virtual table index");
+ return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
+ }
+ else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12]))
+ {
+ // 32-bit displacement
+ auto const index = *reinterpret_cast<std::int32_t const *>(func + 6);
+ if (index % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
+ throw std::invalid_argument("Invalid member function virtual table index");
+ return index / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
+ }
+ }
+ }
+ throw std::invalid_argument("Not a supported pointer to a virtual member function");
+#else
+ throw std::runtime_error("Unsupported architecture");
+#endif
+#else
+ if (!slot.is_virtual())
+ throw std::invalid_argument("Not a pointer to a virtual member function");
+ if (slot.this_pointer_offset())
+ throw std::invalid_argument("Member function requires this pointer adjustment");
+ if (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_STANDARD)
+ slot.ptr -= 1;
+ if (slot.ptr % (sizeof(std::uintptr_t) * MEMBER_FUNCTION_SIZE))
+ throw std::invalid_argument("Invalid member function virtual table index");
+ return slot.ptr / sizeof(std::uintptr_t) / MEMBER_FUNCTION_SIZE;
+#endif
+}
+
+} // namespace detail
+
+} // namespace util
diff --git a/src/lib/util/dynamicclass.h b/src/lib/util/dynamicclass.h
new file mode 100644
index 00000000000..3eaf60d954c
--- /dev/null
+++ b/src/lib/util/dynamicclass.h
@@ -0,0 +1,488 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/// \file
+/// \brief Dynamic derived class creator
+///
+/// Allows on-the-fly creation of derived classes that override selected
+/// virtual member functions of the base class. The overridden base
+/// class member functions can be changed even while instances exist.
+/// This is particularly useful in conjunction with code generation.
+///
+/// Only implemented for configurations where it is possible to define a
+/// non-member function that uses the same calling convention as a
+/// non-static member function, and subject to a number of limitations
+/// on classes that can be used as base classes.
+///
+/// Itanium C++ ABI support is reasonably complete.
+///
+/// MSVC C++ABI support is subject to additional limitations:
+/// * Instances of dynamic derived classes appear to be instance of
+/// their respective base classes when subject to RTTI introspection.
+/// * Member functions returning class, structure or union types cannot
+/// be overridden.
+/// * Member functions returning scalar types that are not returned in
+/// registers cannot be overridden.
+/// * Member functions cannot be overridden for classes built with clang
+/// with optimisation disabled.
+/// * Base class implementations of member functions can only be
+/// resolved if they are virtual member functions that can be
+/// overridden.
+/// * Overriding member functions and resolving base class
+/// implementations of member functions is substantially more
+/// expensive than for the Itanium C++ ABI.
+/// * Only x86-64 targets are supported.
+///
+/// It goes without saying that this is not something you are supposed
+/// to do. This depends heavily on implementation details, and careful
+/// use by the developer. Use it at your own risk.
+#ifndef MAME_LIB_UTIL_DYNAMICCLASS_H
+#define MAME_LIB_UTIL_DYNAMICCLASS_H
+
+#pragma once
+
+#include "abi.h"
+
+#include <algorithm>
+#include <array>
+#include <bitset>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <tuple>
+#include <type_traits>
+#include <typeinfo>
+#include <utility>
+
+
+namespace util {
+
+namespace detail {
+
+/// \brief Dynamic derived class base
+///
+/// Provides common members for dynamic derived classes that do not
+/// depend on the size of the virtual table.
+class dynamic_derived_class_base
+{
+protected:
+ /// \brief Size of a member function virtual table entry
+ ///
+ /// The size of an entry representing a virtual member function in a
+ /// virtual table, as a multiple of the size of a pointer.
+ static constexpr std::size_t MEMBER_FUNCTION_SIZE = MAME_ABI_CXX_VTABLE_FNDESC ? MAME_ABI_FNDESC_SIZE : 1;
+
+ /// \brief Virtual table entries before first member function
+ ///
+ /// The number of pointer-sized entries before the first entry that
+ /// is part of a member function representation.
+ ///
+ /// For the MSVC C++ ABI, only one of these is an ABI requirement
+ /// (the pointer to the complete object locator). The other isused
+ /// internally to allow the base virtual table pointer to be
+ /// recovered.
+ static constexpr std::size_t VTABLE_PREFIX_ENTRIES = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? (1 + 1) : 2;
+
+ /// \brief Virtual table entries generated for a virtual destructor
+ ///
+ /// The number of member function entries generated in the virtual
+ /// table for a virtual destructor.
+ static constexpr std::size_t VTABLE_DESTRUCTOR_ENTRIES = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? 1 : 2;
+
+ /// \brief Virtual table index for base virtual table recovery
+ ///
+ /// Index of the virtual table entry used for recovering the base
+ /// class virtual table pointer, relative to the location an
+ /// instance virtual table pointer points to.
+ static constexpr std::ptrdiff_t VTABLE_BASE_RECOVERY_INDEX = (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC) ? -2 : -1;
+
+ /// \brief Single inheritance class type info equivalent structure
+ ///
+ /// Structure equivalent to the implementation of std::type_info for
+ /// a class with a single direct base class for the Itanium C++ ABI.
+ struct itanium_si_class_type_info_equiv
+ {
+ void const *vptr; ///< Pointer to single inheritance class type info virtual table
+ char const *name; ///< Mangled name of the class
+ std::type_info const *base_type; ///< Pointer to type info for the base class
+ };
+
+ /// \brief Member function pointer equivalent structure
+ ///
+ /// Structure equivalent to the representation of a pointer to a
+ /// member function for the Itanium C++ ABI.
+ struct itanium_member_function_pointer_equiv
+ {
+ uintptr_t ptr; ///< Function pointer or virtual table index
+ ptrdiff_t adj; ///< Offset to add to \c this pointer before call
+
+ constexpr bool is_virtual() const
+ {
+ return (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? bool(adj & 1) : bool(ptr & 1);
+ }
+
+ constexpr uintptr_t function_pointer() const
+ {
+ //assert(!is_virtual()); not constexpr
+ return ptr;
+ }
+
+ constexpr uintptr_t virtual_table_entry_offset() const
+ {
+ //assert(is_virtual()); not constexpr
+ return ptr - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_STANDARD) ? 1 : 0);
+ }
+
+ constexpr ptrdiff_t this_pointer_offset() const
+ {
+ return adj >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0);
+ }
+ };
+
+ struct msvc_complete_object_locator_equiv;
+ struct msvc_type_info_equiv;
+
+ /// \brief Single inheritance member function pointer equivalent
+ ///
+ /// Structure equivalent to the representation of a pointer to a
+ /// member function of a single inheritance class for the MSVC C++
+ /// ABI.
+ struct msvc_si_member_function_pointer_equiv
+ {
+ uintptr_t ptr; ///< Pointer to function, PLT entry, or virtual member call thunk
+ };
+
+ /// \brief Multiple inheritance member function pointer equivalent
+ ///
+ /// Structure equivalent to the representation of a pointer to a
+ /// member function of a Multiple inheritance class for the MSVC C++
+ /// ABI.
+ struct msvc_mi_member_function_pointer_equiv : msvc_si_member_function_pointer_equiv
+ {
+ int adj; ///< Offset to \c this pointer to add before call
+ };
+
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ using member_function_pointer_equiv = msvc_mi_member_function_pointer_equiv;
+
+ template <typename T>
+ using supported_return_type = std::bool_constant<std::is_void_v<T> || std::is_scalar_v<T> || std::is_reference_v<T> >;
+#else
+ using member_function_pointer_equiv = itanium_member_function_pointer_equiv;
+
+ template <typename T>
+ using supported_return_type = std::true_type;
+#endif
+
+ template <typename T>
+ struct member_function_pointer_pun
+ {
+ static_assert(
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ (sizeof(T) == sizeof(msvc_si_member_function_pointer_equiv)) || (sizeof(T) == sizeof(msvc_mi_member_function_pointer_equiv)),
+#else
+ sizeof(T) == sizeof(itanium_member_function_pointer_equiv),
+#endif
+ "Unsupported member function pointer representation");
+
+ union type
+ {
+ T ptr;
+ member_function_pointer_equiv equiv;
+ };
+ };
+
+ template <typename T>
+ using member_function_pointer_pun_t = typename member_function_pointer_pun<T>::type;
+
+ template <class Base, typename Extra>
+ class value_type
+ {
+ private:
+ template <typename... T, typename... U, std::size_t... N, std::size_t... O>
+ value_type(
+ std::tuple<T...> &a,
+ std::tuple<U...> &b,
+ std::integer_sequence<std::size_t, N...>,
+ std::integer_sequence<std::size_t, O...>) :
+ base(std::forward<T>(std::get<N>(a))...),
+ extra(std::forward<U>(std::get<O>(b))...)
+ {
+ }
+
+ public:
+ template <typename... T>
+ value_type(T &&... a) :
+ base(std::forward<T>(a)...)
+ {
+ }
+
+ template <typename... T, typename... U>
+ value_type(std::piecewise_construct_t, std::tuple<T...> a, std::tuple<U...> b) :
+ value_type(a, b, std::make_integer_sequence<std::size_t, sizeof...(T)>(), std::make_integer_sequence<std::size_t, sizeof...(U)>())
+ {
+ }
+
+ template <typename R, typename... T>
+ std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
+ R (Base::*func)(T...))
+ {
+ return dynamic_derived_class_base::resolve_base_member_function(base, func);
+ }
+
+ template <typename R, typename... T>
+ std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
+ R (Base::*func)(T...) const) const
+ {
+ return dynamic_derived_class_base::resolve_base_member_function(base, func);
+ }
+
+ template <typename R, typename... T>
+ R call_base_member_function(R (Base::*func)(T...), T... args)
+ {
+ auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
+ return resolved.first(resolved.second, std::forward<T>(args)...);
+ }
+
+ template <typename R, typename... T>
+ R call_base_member_function(R (Base::*func)(T...) const, T... args) const
+ {
+ auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
+ return resolved.first(resolved.second, std::forward<T>(args)...);
+ }
+
+ Base base;
+ Extra extra;
+ };
+
+ template <class Base>
+ class value_type<Base, void>
+ {
+ public:
+ template <typename... T> value_type(T &&... args) : base(std::forward<T>(args)...) { }
+
+ template <typename R, typename... T>
+ std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
+ R (Base::*func)(T...))
+ {
+ return dynamic_derived_class_base::resolve_base_member_function(base, func);
+ }
+
+ template <typename R, typename... T>
+ std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
+ R (Base::*func)(T...) const) const
+ {
+ return dynamic_derived_class_base::resolve_base_member_function(base, func);
+ }
+
+ template <typename R, typename... T>
+ R call_base_member_function(R (Base::*func)(T...), T... args)
+ {
+ auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
+ return resolved.first(resolved.second, std::forward<T>(args)...);
+ }
+
+ template <typename R, typename... T>
+ R call_base_member_function(R (Base::*func)(T...) const, T... args) const
+ {
+ auto const resolved = dynamic_derived_class_base::resolve_base_member_function(base, func);
+ return resolved.first(resolved.second, std::forward<T>(args)...);
+ }
+
+ Base base;
+ };
+
+ template <class Base, typename Extra, typename Enable = void>
+ struct destroyer;
+
+ /// \brief Destroyer for base classes with virtual destructors
+ ///
+ /// Provides implementations of the complete object destructor and
+ /// deleting destructor required to allow deleting instances of
+ /// dynamic derived classes through pointers to the base class type.
+ /// \tparam Base The base class type.
+ /// \tparam Extra The extra data type.
+ template <class Base, typename Extra>
+ struct destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >
+ {
+ using pointer_type = std::unique_ptr<Base>;
+
+ static void MAME_ABI_CXX_MEMBER_CALL complete_object_destructor(
+ value_type<Base, Extra> &object);
+
+ static void MAME_ABI_CXX_MEMBER_CALL deleting_destructor(
+ value_type<Base, Extra> *object);
+
+ static void *MAME_ABI_CXX_MEMBER_CALL scalar_deleting_destructor(
+ value_type<Base, Extra> *object,
+ unsigned int flags);
+ };
+
+ /// \brief Destroyer for base classes without virtual destructors
+ ///
+ /// Used as the deleter type allowing a \c std::unique_ptr to
+ /// correctly delete instances of a dynamic derived class when the
+ /// the base class type does not have a virtual destructor.
+ /// \tparam Base The base class type.
+ /// \tparam Extra The extra data type.
+ template <class Base, typename Extra>
+ struct destroyer<Base, Extra, std::enable_if_t<!std::has_virtual_destructor_v<Base> > >
+ {
+ static_assert(sizeof(Base) == sizeof(value_type<Base, Extra>), "Value type does not have expected layout");
+
+ using pointer_type = std::unique_ptr<Base, destroyer>;
+
+ void operator()(Base *object) const;
+ };
+
+ dynamic_derived_class_base(std::string_view name);
+ ~dynamic_derived_class_base();
+
+ static std::size_t resolve_virtual_member_slot(member_function_pointer_equiv &slot, std::size_t size);
+
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ msvc_type_info_equiv *m_type_info;
+#else
+ itanium_si_class_type_info_equiv m_type_info; ///< Type info for the dynamic derived class
+#endif
+ std::string m_name; ///< Storage for the class name (mangled for Itanium, undecorated for MSVC)
+ void const *m_base_vtable; ///< Saved base class virtual table pointer
+
+private:
+ static std::ptrdiff_t base_vtable_offset();
+
+ template <typename Base>
+ static std::uintptr_t const *get_base_vptr(Base const &object);
+
+ template <typename Base>
+ static void restore_base_vptr(Base &object);
+
+ template <typename Base, typename R, typename... T>
+ static std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> resolve_base_member_function(
+ Base &object,
+ R (Base::*func)(T...));
+
+ template <typename Base, typename R, typename... T>
+ static std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> resolve_base_member_function(
+ Base const &object,
+ R (Base::*func)(T...) const);
+};
+
+} // namespace detail
+
+
+
+/// \brief Dynamic derived class
+///
+/// Allows dynamically creating classes derived from a supplied base
+/// class and overriding virtual member functions. A class must meet a
+/// number of requirements to be suitable for use as a base class. Most
+/// of these requirements cannot be checked automatically. It is the
+/// developer's responsibility to ensure the requirements are met.
+///
+/// The dynamic derived class object must not be destroyed until after
+/// all instances of the class have been destroyed.
+///
+/// When destroying an instance of the dynamic derived class, the base
+/// class vtable is restored before the extra data destructor is called.
+/// This allows the extra data type to hold a smart pointer to the
+/// dynamic derived class so it can be cleaned up automatically when all
+/// instances are destroyed. However, it means you must keep in mind
+/// that the base class implementation of all virtual member functions
+/// will be restored before the extra data destructor is called.
+/// \tparam Base Base class for the dynamic derived class. Must meet
+/// the following requirements:
+/// * Must be a concrete class with at least one public constructor
+/// and a public destructor.
+/// * Must have at least one virtual member function.
+/// * Must not have any direct or indirect virtual base classes.
+/// * Must have no secondary virtual tables. This means the class and
+/// all of its direct and indirect base classes may only inherit
+/// virtual member functions from one base class at most.
+/// * If the class has a virtual destructor, it must be declared
+/// declared before any other virtual member functions in the least
+/// derived base class containing virtual member functions.
+/// \tparam Extra Extra data type, or \c void if not required. Must be
+/// a concrete type with at least one public constructor and a public
+/// destructor.
+/// \tparam VirtualCount The total number of virtual member functions of
+/// the base class, excluding the virtual destructor if present. This
+/// must be correct, and cannot be checked automatically. It is the
+/// developer's responsibility to ensure the value is correct. This
+/// potentially includes additional entries for overridden member
+/// functions with covariant return types and implicitly-declared
+/// assignment operators.
+template <class Base, typename Extra, std::size_t VirtualCount>
+class dynamic_derived_class : private detail::dynamic_derived_class_base
+{
+public:
+ /// \brief Type used to store base class and extra data
+ ///
+ /// Has a member \c base of the base class type, and a member
+ /// \c extra of the extra data type if it is not \c void.
+ ///
+ /// Provides \c resolve_base_member_function and
+ /// \c call_base_member_function member functions to assist with
+ /// calling the base implementation of overridden virtual member
+ /// functions.
+ using type = value_type<Base, Extra>;
+
+ /// \brief Smart pointer to instance
+ ///
+ /// A unique pointer type suitable for taking ownership of an
+ /// instance of the dynamic derived class.
+ using pointer = typename destroyer<Base, Extra>::pointer_type;
+
+ dynamic_derived_class(dynamic_derived_class const &) = delete;
+ dynamic_derived_class &operator=(dynamic_derived_class const &) = delete;
+
+ dynamic_derived_class(std::string_view name);
+ dynamic_derived_class(dynamic_derived_class const &prototype, std::string_view name);
+
+ /// \brief Get type info for dynamic derived class
+ ///
+ /// Gets a reference to the type info for the dynamic derived class.
+ /// The reference remains valid and unique as long as the dynamic
+ /// derived class is not destroyed.
+ /// \return A reference to an object equivalent to \c std::type_info.
+ std::type_info const &type_info() const
+ {
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ return *reinterpret_cast<std::type_info const *>(m_type_info);
+#else
+ return *reinterpret_cast<std::type_info const *>(&m_type_info);
+#endif
+ }
+
+ template <typename R, typename... T>
+ void override_member_function(R (Base::*slot)(T...), R MAME_ABI_CXX_MEMBER_CALL (*func)(type &, T...));
+
+ template <typename R, typename... T>
+ void override_member_function(R (Base::*slot)(T...) const, R MAME_ABI_CXX_MEMBER_CALL (*func)(type const &, T...));
+
+ template <typename R, typename... T>
+ void restore_base_member_function(R (Base::*slot)(T...));
+
+ template <typename... T>
+ pointer instantiate(type *&object, T &&... args);
+
+private:
+ static_assert(sizeof(std::uintptr_t) == sizeof(std::ptrdiff_t), "Pointer and pointer difference must be the same size");
+ static_assert(sizeof(void *) == sizeof(void (*)()), "Code and data pointers must be the same size");
+ static_assert(std::is_polymorphic_v<Base>, "Base class must be polymorphic");
+
+ static constexpr std::size_t FIRST_OVERRIDABLE_MEMBER_OFFSET = std::has_virtual_destructor_v<Base> ? VTABLE_DESTRUCTOR_ENTRIES : 0;
+ static constexpr std::size_t VIRTUAL_MEMBER_FUNCTION_COUNT = VirtualCount + FIRST_OVERRIDABLE_MEMBER_OFFSET;
+ static constexpr std::size_t VTABLE_SIZE = VTABLE_PREFIX_ENTRIES + (VIRTUAL_MEMBER_FUNCTION_COUNT * MEMBER_FUNCTION_SIZE);
+
+ void override_member_function(member_function_pointer_equiv &slot, std::uintptr_t func, std::size_t size);
+
+ std::array<std::uintptr_t, VTABLE_SIZE> m_vtable;
+ std::bitset<VirtualCount> m_overridden;
+};
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_DYNAMICCLASS_H
diff --git a/src/lib/util/dynamicclass.ipp b/src/lib/util/dynamicclass.ipp
new file mode 100644
index 00000000000..d44c5e50e7d
--- /dev/null
+++ b/src/lib/util/dynamicclass.ipp
@@ -0,0 +1,505 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#ifndef MAME_LIB_UTIL_DYNAMICCLASS_IPP
+#define MAME_LIB_UTIL_DYNAMICCLASS_IPP
+
+#pragma once
+
+#include "dynamicclass.h"
+
+
+namespace util {
+
+namespace detail {
+
+/// \brief Get offset to saved base virtual table pointer from type info
+///
+/// Gets the offset to the saved base class virtual table pointer from
+/// the location the dynamic derived class virtual table entry used for
+/// recovery points to.
+/// \return Offset from the type info to saved base class virtual table
+/// pointer in bytes.
+inline std::ptrdiff_t dynamic_derived_class_base::base_vtable_offset()
+{
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ return 0;
+#else
+ return
+ reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_base_vtable) -
+ reinterpret_cast<std::uint8_t *>(&reinterpret_cast<dynamic_derived_class_base *>(std::uintptr_t(0))->m_type_info);
+#endif
+}
+
+
+/// \brief Get base class virtual table pointer
+///
+/// Gets the base class virtual pointer for an instance of a dynamic
+/// derived class.
+/// \tparam Base The base class type (usually determined automatically).
+/// \param [in] object Base class member of dynamic derived class
+/// instance.
+/// \return Pointer to first virtual member function entry in base class
+/// virtual table.
+template <class Base>
+inline std::uintptr_t const *dynamic_derived_class_base::get_base_vptr(
+ Base const &object)
+{
+ auto const vptr = *reinterpret_cast<std::uintptr_t const *>(&object);
+ auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
+ return *reinterpret_cast<std::uintptr_t const *const *>(recovery + base_vtable_offset());
+}
+
+
+/// \brief Restore base class virtual table pointer
+///
+/// Restores the base class virtual pointer in an instance of a dynamic
+/// derived class. Must be called before calling the base class
+/// destructor.
+/// \tparam Base The base class type (usually determined automatically).
+/// \param [in,out] object Base class member of dynamic derived class
+/// instance.
+template <class Base>
+inline void dynamic_derived_class_base::restore_base_vptr(
+ Base &object)
+{
+ auto &vptr = *reinterpret_cast<std::uintptr_t *>(&object);
+ auto const recovery = reinterpret_cast<std::uintptr_t const *>(vptr)[VTABLE_BASE_RECOVERY_INDEX];
+ vptr = *reinterpret_cast<std::uintptr_t const *>(recovery + base_vtable_offset());
+ assert(reinterpret_cast<void const *>(vptr));
+}
+
+
+/// \brief Resolve pointer to base class member function
+///
+/// Given an instance and pointer to a base class member function, gets
+/// the adjusted \c this pointer and conventional function pointer.
+/// \tparam Base The base class type (usually determined automatically).
+/// \tparam R Return type of member function (usually determined
+/// automatically).
+/// \tparam T Parameter types expected by the member function (usually
+/// determined automatically).
+/// \param [in] object Base class member of dynamic derived class
+/// instance.
+/// \param [in] func Pointer to member function of base class.
+/// \return A \c std::pair containing the conventional function pointer
+/// and adjusted \c this pointer.
+/// \exception std::invalid_argument Thrown if the \p func argument is
+/// not a supported member function. This includes non-virtual member
+/// functions and virtual member functions that aren't supported for
+/// overriding when using the MSVC C++ ABI.
+template <typename Base, typename R, typename... T>
+inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...), void *> dynamic_derived_class_base::resolve_base_member_function(
+ Base &object,
+ R (Base::*func)(T...))
+{
+ static_assert(supported_return_type<R>::value, "Unsupported member function return type");
+ member_function_pointer_pun_t<decltype(func)> thunk;
+ thunk.ptr = func;
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
+ auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
+ std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
+ return std::make_pair(
+ MAME_ABI_CXX_VTABLE_FNDESC
+ ? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
+ : reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
+ &object);
+#else
+ if (thunk.equiv.is_virtual())
+ {
+ assert(!thunk.equiv.this_pointer_offset());
+ auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
+ auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
+ return std::make_pair(
+ MAME_ABI_CXX_VTABLE_FNDESC
+ ? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(std::uintptr_t(entryptr))
+ : reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(*entryptr),
+ &object);
+ }
+ else
+ {
+ return std::make_pair(
+ reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void *, T...)>(thunk.equiv.function_pointer()),
+ reinterpret_cast<std::uint8_t *>(&object) + thunk.equiv.this_pointer_offset());
+ }
+#endif
+}
+
+template <typename Base, typename R, typename... T>
+inline std::pair<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...), void const *> dynamic_derived_class_base::resolve_base_member_function(
+ Base const &object,
+ R (Base::*func)(T...) const)
+{
+ static_assert(supported_return_type<R>::value, "Unsupported member function return type");
+ member_function_pointer_pun_t<decltype(func)> thunk;
+ thunk.ptr = func;
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(func));
+ auto const vptr = reinterpret_cast<std::uintptr_t const *>(get_base_vptr(object));
+ std::uintptr_t const* const entryptr = vptr + (index * MEMBER_FUNCTION_SIZE);
+ return std::make_pair(
+ MAME_ABI_CXX_VTABLE_FNDESC
+ ? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
+ : reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
+ &object);
+#else
+ if (thunk.equiv.is_virtual())
+ {
+ assert(!thunk.equiv.this_pointer_offset());
+ auto const vptr = reinterpret_cast<std::uint8_t const *>(get_base_vptr(object));
+ auto const entryptr = reinterpret_cast<std::uintptr_t const *>(vptr + thunk.equiv.virtual_table_entry_offset());
+ return std::make_pair(
+ MAME_ABI_CXX_VTABLE_FNDESC
+ ? reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(std::uintptr_t(entryptr))
+ : reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(*entryptr),
+ &object);
+ }
+ else
+ {
+ return std::make_pair(
+ reinterpret_cast<R MAME_ABI_CXX_MEMBER_CALL (*)(void const *, T...)>(thunk.equiv.function_pointer()),
+ reinterpret_cast<std::uint8_t const *>(&object) + thunk.equiv.this_pointer_offset());
+ }
+#endif
+}
+
+
+/// \brief Complete object destructor for dynamic derived class
+///
+/// Restores the base class virtual table pointer, calls the extra data
+/// and base class destructors, but does not free the memory occupied by
+/// the object. Used when the base class type has a virtual destructor
+/// to allow deleting instances of a dynamic derived class through
+/// pointers to the base class type.
+///
+/// Only used for the Itanium C++ ABI.
+/// \param [in] object Reference to the object to destroy.
+template <class Base, typename Extra>
+void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::complete_object_destructor(
+ value_type<Base, Extra> &object)
+{
+ restore_base_vptr(object.base);
+ object.~value_type();
+}
+
+
+/// \brief Deleting destructor for dynamic derived class
+///
+/// Restores the base class virtual table pointer, calls the extra data
+/// and base class destructors, and frees the memory occupied by the
+/// object. Used when the base class type has a virtual destructor to
+/// allow deleting instances of a dynamic derived class through pointers
+/// to the base class type.
+///
+/// Only used for the Itanium C++ ABI.
+/// \param [in] object Pointer to the object to destroy.
+template <class Base, typename Extra>
+void MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::deleting_destructor(
+ value_type<Base, Extra> *object)
+{
+ restore_base_vptr(object->base);
+ delete object;
+}
+
+
+/// \brief Destructor for dynamic derived class
+///
+/// Restores the base class virtual table pointer, calls the extra data
+/// and base class destructors, and optionally frees the memory occupied
+/// by the object. Used when the base class type has a virtual
+/// destructor to allow deleting instances of a dynamic derived class
+/// through pointers to the base class type.
+///
+/// Only used for the MSVC C++ ABI.
+/// \param [in] object Pointer to the object to destroy.
+/// \param [in] flags If bit 0 is set, the memory occupied by the object
+/// will be freed.
+/// \return The supplied object pointer.
+template <class Base, typename Extra>
+void *MAME_ABI_CXX_MEMBER_CALL dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<std::has_virtual_destructor_v<Base> > >::scalar_deleting_destructor(
+ value_type<Base, Extra> *object,
+ unsigned int flags)
+{
+ restore_base_vptr(object->base);
+ object->~value_type();
+ if (flags & 1)
+ operator delete (static_cast<void *>(object));
+ return object;
+}
+
+
+/// \brief Deleter for dynamic derived classes
+///
+/// Restores the base class virtual table pointer, calls the extra data
+/// and base class destructors, and frees the memory occupied by the
+/// object. Used to delete instances of a dynamic derived class when
+/// the base class type does not have a virtual destructor.
+/// \param [in] object Pointer to the object to destroy.
+template <class Base, typename Extra>
+void dynamic_derived_class_base::destroyer<Base, Extra, std::enable_if_t<!std::has_virtual_destructor_v<Base> > >::operator()(
+ Base *object) const
+{
+ restore_base_vptr(*object);
+ delete reinterpret_cast<value_type<Base, Extra> *>(object);
+}
+
+} // namespace detail
+
+
+/// \brief Create a dynamic derived class
+///
+/// Creates a new dynamic derived class. No base member functions are
+/// overridden initially.
+/// \param [in] name The unmangled name for the new dynamic derived
+/// class. This will be mangled for use in the generated type info.
+template <class Base, typename Extra, std::size_t VirtualCount>
+dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
+ std::string_view name) :
+ detail::dynamic_derived_class_base(name)
+{
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
+#else
+ m_type_info.base_type = &typeid(Base);
+ m_vtable[0] = 0; // offset to top
+ m_vtable[1] = std::uintptr_t(&m_type_info); // type info
+#endif
+ if constexpr (std::has_virtual_destructor_v<Base>)
+ {
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ {
+ if (MAME_ABI_CXX_VTABLE_FNDESC)
+ {
+ std::copy_n(
+ reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor)),
+ MEMBER_FUNCTION_SIZE,
+ &m_vtable[VTABLE_PREFIX_ENTRIES]);
+ }
+ else
+ {
+ m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::scalar_deleting_destructor);
+ }
+ }
+ else
+ {
+ if (MAME_ABI_CXX_VTABLE_FNDESC)
+ {
+ std::copy_n(
+ reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor)),
+ MEMBER_FUNCTION_SIZE,
+ &m_vtable[VTABLE_PREFIX_ENTRIES]);
+ std::copy_n(
+ reinterpret_cast<std::uintptr_t const *>(std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor)),
+ MEMBER_FUNCTION_SIZE,
+ &m_vtable[VTABLE_PREFIX_ENTRIES + MEMBER_FUNCTION_SIZE]);
+ }
+ else
+ {
+ m_vtable[VTABLE_PREFIX_ENTRIES] = std::uintptr_t(&destroyer<Base, Extra>::complete_object_destructor);
+ m_vtable[VTABLE_PREFIX_ENTRIES + 1] = std::uintptr_t(&destroyer<Base, Extra>::deleting_destructor);
+ }
+ }
+ }
+ std::fill(
+ std::next(m_vtable.begin(), VTABLE_PREFIX_ENTRIES + (FIRST_OVERRIDABLE_MEMBER_OFFSET * MEMBER_FUNCTION_SIZE)),
+ m_vtable.end(),
+ std::uintptr_t(static_cast<void *>(nullptr)));
+}
+
+
+/// \brief Create a dynamic derived class using a prototype
+///
+/// Creates a new dynamic derived class using an existing dynamic
+/// derived class as a prototype. Overridden base class member
+/// functions are inherited from the current state of the prototype.
+/// The new dynamic derived class is not affected by any future
+/// modifications to the prototype. The prototype may be destroyed
+/// safely before the new dynamic derived class is destroyed (provided
+/// all its instances are destroyed first).
+/// \param [in] prototype The dynamic derived class to use as a
+/// prototype.
+/// \param [in] name The unmangled name for the new dynamic derived
+/// class. This will be mangled for use in the generated type info.
+template <class Base, typename Extra, std::size_t VirtualCount>
+dynamic_derived_class<Base, Extra, VirtualCount>::dynamic_derived_class(
+ dynamic_derived_class const &prototype,
+ std::string_view name) :
+ detail::dynamic_derived_class_base(name),
+ m_vtable(prototype.m_vtable),
+ m_overridden(prototype.m_overridden)
+{
+ m_base_vtable = prototype.m_base_vtable;
+#if MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC
+ m_vtable[0] = std::uintptr_t(&m_base_vtable); // for restoring the base vtable
+#else
+ m_type_info.base_type = &typeid(Base);
+ m_vtable[1] = std::uintptr_t(&m_type_info); // type info
+#endif
+}
+
+
+/// \brief Override a virtual member function
+///
+/// Replace the virtual table entry for the specified base member
+/// function with the supplied function. This applies to existing
+/// instances as well as newly created instances. Note that if you are
+/// using some technique to resolve pointers to virtual member functions
+/// in advance, resolved pointers may not reflect the change.
+/// \tparam R Return type of member function to override (usually
+/// determined automatically).
+/// \tparam T Parameter types expected by the member function to
+/// override (usually determined automatically).
+/// \param [in] slot A pointer to the base class member function to
+/// override. Must be a pointer to a virtual member function.
+/// \param [in] func A pointer to the function to use to override the
+/// base class member function.
+/// \exception std::invalid_argument Thrown if the \p slot argument is
+/// not a supported virtual member function.
+/// \sa restore_base_member_function
+template <class Base, typename Extra, std::size_t VirtualCount>
+template <typename R, typename... T>
+void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
+ R (Base::*slot)(T...),
+ R MAME_ABI_CXX_MEMBER_CALL (*func)(type &, T...))
+{
+ static_assert(supported_return_type<R>::value, "Unsupported member function return type");
+ member_function_pointer_pun_t<decltype(slot)> thunk;
+ thunk.ptr = slot;
+ override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
+}
+
+template <class Base, typename Extra, std::size_t VirtualCount>
+template <typename R, typename... T>
+void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
+ R (Base::*slot)(T...) const,
+ R MAME_ABI_CXX_MEMBER_CALL (*func)(type const &, T...))
+{
+ static_assert(supported_return_type<R>::value, "Unsupported member function return type");
+ member_function_pointer_pun_t<decltype(slot)> thunk;
+ thunk.ptr = slot;
+ override_member_function(thunk.equiv, std::uintptr_t(func), sizeof(func));
+}
+
+
+/// \brief Restore the base implementation of a member function
+///
+/// If the specified virtual member function of the base class has been
+/// overridden, restore the base class implementation. This applies to
+/// existing instances as well as newly created instances. Note that if
+/// you are using some technique to resolve pointers to virtual member
+/// functions in advance, resolved pointers may not reflect the change.
+/// \tparam R Return type of member function to restore (usually
+/// determined automatically).
+/// \tparam T Parameter types expected by the member function to
+/// to restore (usually determined automatically).
+/// \param [in] slot A pointer to the base class member function to
+/// restore. Must be a pointer to a virtual member function.
+/// \exception std::invalid_argument Thrown if the \p slot argument is
+/// not a supported virtual member function.
+/// \sa override_member_function
+template <class Base, typename Extra, std::size_t VirtualCount>
+template <typename R, typename... T>
+void dynamic_derived_class<Base, Extra, VirtualCount>::restore_base_member_function(
+ R (Base::*slot)(T...))
+{
+ member_function_pointer_pun_t<decltype(slot)> thunk;
+ thunk.ptr = slot;
+ std::size_t const index = resolve_virtual_member_slot(thunk.equiv, sizeof(slot));
+ assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
+ assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
+ if (m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] && m_base_vtable)
+ {
+ std::copy_n(
+ reinterpret_cast<std::uintptr_t const *>(m_base_vtable) + (index * MEMBER_FUNCTION_SIZE),
+ MEMBER_FUNCTION_SIZE,
+ &m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
+ }
+ m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = false;
+}
+
+
+/// \brief Create a new instance
+///
+/// Creates a new instance of the dynamic derived class constructed with
+/// the supplied arguments.
+/// \tparam T Constructor argument types (usually determined
+/// automatically).
+/// \param [out] object Receives an pointer to the object storing the
+/// base type and extra data.
+/// \param [in] args Constructor arguments for the object to be
+/// instantiated. If the first argument is
+/// \c std::piecewise_construct, it must be followed by two tuples
+/// containing the arguments to forward to the base class and extra
+/// data constructors, respectively. If the first argument is not
+/// \c std::piecewise_construct, all the arguments are forwarded to
+/// the base class constructor.
+/// \return A unique pointer to the new instance.
+template <class Base, typename Extra, std::size_t VirtualCount>
+template <typename... T>
+typename dynamic_derived_class<Base, Extra, VirtualCount>::pointer dynamic_derived_class<Base, Extra, VirtualCount>::instantiate(
+ type *&object,
+ T &&... args)
+{
+ std::unique_ptr<type> result(new type(std::forward<T>(args)...));
+ auto &vptr = *reinterpret_cast<std::uintptr_t const **>(&result->base);
+ if (!m_base_vtable)
+ {
+ assert(std::uintptr_t(result.get()) == std::uintptr_t(&result->base));
+ m_base_vtable = vptr;
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ m_vtable[1] = vptr[-1]; // use the base class complete object locator - too hard to fake
+ for (std::size_t i = 0; VirtualCount > i; ++i)
+ {
+ if (!m_overridden[i])
+ {
+ std::size_t const offset = (i + FIRST_OVERRIDABLE_MEMBER_OFFSET) * MEMBER_FUNCTION_SIZE;
+ std::copy_n(vptr + offset, MEMBER_FUNCTION_SIZE, &m_vtable[VTABLE_PREFIX_ENTRIES + offset]);
+ }
+ }
+ }
+ vptr = &m_vtable[VTABLE_PREFIX_ENTRIES];
+ object = result.get();
+ return pointer(&result.release()->base);
+}
+
+
+/// \brief Replace member function in virtual table
+///
+/// Does the actual work involved in replacing a virtual table entry to
+/// override a virtual member function of the base class, avoiding
+/// duplication between overloads.
+/// \param [in] slot Internal representation of pointer to a virtual
+/// member function of the base class. May be modified.
+/// \param [in] func A pointer to the function to use to override the
+/// base class member function reinterpreted as an unsigned integer of
+/// equivalent size.
+/// \param [in] size Size of the member function pointer type for the
+/// \p slot argument.
+/// \exception std::invalid_argument Thrown if the \p slot argument is
+/// not a supported virtual member function.
+template <class Base, typename Extra, std::size_t VirtualCount>
+void dynamic_derived_class<Base, Extra, VirtualCount>::override_member_function(
+ member_function_pointer_equiv &slot,
+ std::uintptr_t func,
+ std::size_t size)
+{
+ std::size_t const index = resolve_virtual_member_slot(slot, size);
+ assert(index < VIRTUAL_MEMBER_FUNCTION_COUNT);
+ assert(FIRST_OVERRIDABLE_MEMBER_OFFSET <= index);
+ m_overridden[index - FIRST_OVERRIDABLE_MEMBER_OFFSET] = true;
+ if (MAME_ABI_CXX_VTABLE_FNDESC)
+ {
+ std::copy_n(
+ reinterpret_cast<std::uintptr_t const *>(func),
+ MEMBER_FUNCTION_SIZE,
+ &m_vtable[VTABLE_PREFIX_ENTRIES + (index * MEMBER_FUNCTION_SIZE)]);
+ }
+ else
+ {
+ m_vtable[VTABLE_PREFIX_ENTRIES + index] = func;
+ }
+}
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_DYNAMICCLASS_IPP
diff --git a/src/lib/util/flac.cpp b/src/lib/util/flac.cpp
index 13bf7aed8eb..9fb0c4c46dc 100644
--- a/src/lib/util/flac.cpp
+++ b/src/lib/util/flac.cpp
@@ -10,9 +10,10 @@
#include "flac.h"
-#include "corefile.h"
+#include "ioprocs.h"
#include <cassert>
+#include <cstring>
#include <new>
@@ -37,7 +38,7 @@ flac_encoder::flac_encoder(void *buffer, uint32_t buflength)
}
-flac_encoder::flac_encoder(util::core_file &file)
+flac_encoder::flac_encoder(util::random_write &file)
{
init_common();
reset(file);
@@ -102,7 +103,7 @@ bool flac_encoder::reset(void *buffer, uint32_t buflength)
// reset - reset state with new file parameters
//-------------------------------------------------
-bool flac_encoder::reset(util::core_file &file)
+bool flac_encoder::reset(util::random_write &file)
{
// configure the output
m_compressed_start = nullptr;
@@ -187,7 +188,16 @@ uint32_t flac_encoder::finish()
{
// process the data and return the amount written
FLAC__stream_encoder_finish(m_encoder);
- return (m_file != nullptr) ? m_file->tell() : m_compressed_offset;
+ if (m_file)
+ {
+ std::uint64_t result = 0;
+ m_file->tell(result); // TODO: check for error result, consider this may be too big for uint32_t
+ return result;
+ }
+ else
+ {
+ return m_compressed_offset;
+ }
}
@@ -232,29 +242,30 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
size_t offset = 0;
while (offset < bytes)
{
- // if we're ignoring, continue to do so
if (m_ignore_bytes != 0)
{
+ // if we're ignoring, continue to do so
size_t ignore = std::min(bytes - offset, size_t(m_ignore_bytes));
offset += ignore;
m_ignore_bytes -= ignore;
}
-
- // if we haven't hit the end of metadata, process a new piece
else if (!m_found_audio)
{
+ // if we haven't hit the end of metadata, process a new piece
assert(bytes - offset >= 4);
m_found_audio = ((buffer[offset] & 0x80) != 0);
m_ignore_bytes = (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3];
offset += 4;
}
-
- // otherwise process as audio data and copy to the output
else
{
+ // otherwise process as audio data and copy to the output
int count = bytes - offset;
- if (m_file != nullptr)
- m_file->write(buffer, count);
+ if (m_file)
+ {
+ size_t actual;
+ m_file->write(buffer, count, actual); // TODO: check for errors
+ }
else
{
if (m_compressed_offset + count <= m_compressed_length)
@@ -316,7 +327,7 @@ flac_decoder::flac_decoder(const void *buffer, uint32_t length, const void *buff
// flac_decoder - constructor
//-------------------------------------------------
-flac_decoder::flac_decoder(util::core_file &file)
+flac_decoder::flac_decoder(util::read_stream &file)
: m_decoder(FLAC__stream_decoder_new()),
m_file(&file),
m_compressed_offset(0),
@@ -422,7 +433,7 @@ bool flac_decoder::reset(uint32_t sample_rate, uint8_t num_channels, uint32_t bl
// reset - reset state with new file parameter
//-------------------------------------------------
-bool flac_decoder::reset(util::core_file &file)
+bool flac_decoder::reset(util::read_stream &file)
{
m_file = &file;
m_compressed_start = nullptr;
@@ -517,12 +528,11 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s
{
uint32_t expected = *bytes;
- // if a file, just read
- if (m_file != nullptr)
- *bytes = m_file->read(buffer, expected);
-
- // otherwise, copy from memory
- else
+ if (m_file) // if a file, just read
+ {
+ m_file->read(buffer, expected, *bytes); // TODO: check for errors
+ }
+ else // otherwise, copy from memory
{
// copy from primary buffer first
uint32_t outputpos = 0;
diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h
index ffcb7904722..226afef95e6 100644
--- a/src/lib/util/flac.h
+++ b/src/lib/util/flac.h
@@ -13,6 +13,8 @@
#pragma once
+#include "utilfwd.h"
+
#include <FLAC/all.h>
#include <cstdint>
@@ -22,8 +24,6 @@
// TYPE DEFINITIONS
//**************************************************************************
-namespace util { class core_file; }
-
// ======================> flac_encoder
class flac_encoder
@@ -32,7 +32,7 @@ public:
// construction/destruction
flac_encoder();
flac_encoder(void *buffer, uint32_t buflength);
- flac_encoder(util::core_file &file);
+ flac_encoder(util::random_write &file);
~flac_encoder();
// configuration
@@ -48,7 +48,7 @@ public:
// reset
bool reset();
bool reset(void *buffer, uint32_t buflength);
- bool reset(util::core_file &file);
+ bool reset(util::random_write &file);
// encode a buffer
bool encode_interleaved(const int16_t *samples, uint32_t samples_per_channel, bool swap_endian = false);
@@ -65,7 +65,7 @@ private:
// internal state
FLAC__StreamEncoder * m_encoder; // actual encoder
- util::core_file * m_file; // output file
+ util::random_write * m_file; // output file
uint32_t m_compressed_offset; // current offset with the compressed stream
FLAC__byte * m_compressed_start; // start of compressed data
uint32_t m_compressed_length; // length of the compressed stream
@@ -90,7 +90,7 @@ public:
// construction/destruction
flac_decoder();
flac_decoder(const void *buffer, uint32_t length, const void *buffer2 = nullptr, uint32_t length2 = 0);
- flac_decoder(util::core_file &file);
+ flac_decoder(util::read_stream &file);
~flac_decoder();
// getters (valid after reset)
@@ -105,7 +105,7 @@ public:
bool reset();
bool reset(const void *buffer, uint32_t length, const void *buffer2 = nullptr, uint32_t length2 = 0);
bool reset(uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length);
- bool reset(util::core_file &file);
+ bool reset(util::read_stream &file);
// decode to a buffer; num_samples must be a multiple of the block size
bool decode_interleaved(int16_t *samples, uint32_t num_samples, bool swap_endian = false);
@@ -125,8 +125,8 @@ private:
static void error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
// output state
- FLAC__StreamDecoder * m_decoder; // actual encoder
- util::core_file * m_file; // output file
+ FLAC__StreamDecoder * m_decoder; // actual decoder
+ util::read_stream * m_file; // input file
uint32_t m_sample_rate; // decoded sample rate
uint8_t m_channels; // decoded number of channels
uint8_t m_bits_per_sample; // decoded bits per sample
diff --git a/src/lib/util/harddisk.cpp b/src/lib/util/harddisk.cpp
index 66d98582bb6..ad0108092c8 100644
--- a/src/lib/util/harddisk.cpp
+++ b/src/lib/util/harddisk.cpp
@@ -10,7 +10,7 @@
#include "harddisk.h"
-#include "corefile.h"
+#include "ioprocs.h"
#include <cstdlib>
@@ -21,9 +21,9 @@
struct hard_disk_file
{
- chd_file * chd; /* CHD file */
- util::core_file *fhandle; /* core_file if not a CHD */
- hard_disk_info info; /* hard disk info */
+ chd_file * chd; // CHD file
+ util::random_read_write * fhandle; // file if not a CHD
+ hard_disk_info info; // hard disk info
};
@@ -73,11 +73,16 @@ hard_disk_file *hard_disk_open(chd_file *chd)
return file;
}
-hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
+hard_disk_file *hard_disk_open(util::random_read_write &corefile, uint32_t skipoffs)
{
+ // bail if getting the file length fails
+ std::uint64_t length;
+ if (corefile.length(length))
+ return nullptr;
+
hard_disk_file *file;
- /* allocate memory for the hard disk file */
+ // allocate memory for the hard disk file
file = (hard_disk_file *)malloc(sizeof(hard_disk_file));
if (file == nullptr)
return nullptr;
@@ -91,7 +96,7 @@ hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
file->info.fileoffset = skipoffs;
// attempt to guess geometry in case this is an ATA situation
- for (uint32_t totalsectors = (corefile.size() - skipoffs) / file->info.sectorbytes; ; totalsectors++)
+ for (uint32_t totalsectors = (length - skipoffs) / file->info.sectorbytes; ; totalsectors++)
for (uint32_t cursectors = 63; cursectors > 1; cursectors--)
if (totalsectors % cursectors == 0)
{
@@ -118,9 +123,7 @@ hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
void hard_disk_close(hard_disk_file *file)
{
if (file->fhandle)
- {
file->fhandle->flush();
- }
free(file);
}
@@ -164,7 +167,7 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file)
-------------------------------------------------*/
/**
- * @fn uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
+ * @fn bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
*
* @brief Hard disk read.
*
@@ -172,22 +175,24 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file)
* @param lbasector The sector number (Linear Block Address) to read.
* @param buffer The buffer where the hard disk data will be placed.
*
- * @return An uint32_t.
+ * @return True if the operation succeeded
*/
-uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
+bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
{
+ std::error_condition err;
if (file->chd)
{
- std::error_condition err = file->chd->read_units(lbasector, buffer);
+ err = file->chd->read_units(lbasector, buffer);
return !err;
}
else
{
- uint32_t actual = 0;
- file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
- actual = file->fhandle->read(buffer, file->info.sectorbytes);
- return (actual == file->info.sectorbytes);
+ size_t actual = 0;
+ err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
+ if (!err)
+ err = file->fhandle->read(buffer, file->info.sectorbytes, actual);
+ return !err && (actual == file->info.sectorbytes);
}
}
@@ -198,7 +203,7 @@ uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
-------------------------------------------------*/
/**
- * @fn uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
+ * @fn bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
*
* @brief Hard disk write.
*
@@ -206,25 +211,28 @@ uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
* @param lbasector The sector number (Linear Block Address) to write.
* @param buffer The buffer containing the data to write.
*
- * @return An uint32_t.
+ * @return True if the operation succeeded
*/
-uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
+bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
{
+ std::error_condition err;
if (file->chd)
{
- std::error_condition err = file->chd->write_units(lbasector, buffer);
+ err = file->chd->write_units(lbasector, buffer);
return !err;
}
else
{
- uint32_t actual = 0;
- file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
- actual = file->fhandle->write(buffer, file->info.sectorbytes);
- return (actual == file->info.sectorbytes);
+ size_t actual = 0;
+ err = file->fhandle->seek(file->info.fileoffset + (lbasector * file->info.sectorbytes), SEEK_SET);
+ if (!err)
+ err = file->fhandle->write(buffer, file->info.sectorbytes, actual);
+ return !err && (actual == file->info.sectorbytes);
}
}
+
/*-------------------------------------------------
hard_disk_set_block_size - sets the block size
for a non-CHD-backed hard disk (a bare file).
@@ -259,6 +267,6 @@ bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize)
else
{
file->info.sectorbytes = blocksize;
+ return true;
}
- return true;
}
diff --git a/src/lib/util/harddisk.h b/src/lib/util/harddisk.h
index f6dd39acd70..a586df0f4e0 100644
--- a/src/lib/util/harddisk.h
+++ b/src/lib/util/harddisk.h
@@ -41,7 +41,7 @@ struct hard_disk_info
***************************************************************************/
hard_disk_file *hard_disk_open(chd_file *chd);
-hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs);
+hard_disk_file *hard_disk_open(util::random_read_write &corefile, uint32_t skipoffs);
void hard_disk_close(hard_disk_file *file);
@@ -50,7 +50,7 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file);
bool hard_disk_set_block_size(hard_disk_file *file, uint32_t blocksize);
-uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer);
-uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer);
+bool hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer);
+bool hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer);
#endif // MAME_LIB_UTIL_HARDDISK_H
diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp
index 1f52a3daf97..0dcea206571 100644
--- a/src/lib/util/ioprocs.cpp
+++ b/src/lib/util/ioprocs.cpp
@@ -736,296 +736,6 @@ public:
}
};
-
-// helper class for holding a core_file and closing it (or not) as necessary
-
-class core_file_adapter_base : public virtual random_access
-{
-public:
- virtual ~core_file_adapter_base()
- {
- if (m_close)
- delete m_file;
- }
-
- virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
- {
- if (!m_file->seek(offset, whence))
- return std::error_condition();
- else
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-
- virtual std::error_condition tell(std::uint64_t &result) noexcept override
- {
- // no error reporting
- result = m_file->tell();
- return std::error_condition();
- }
-
- virtual std::error_condition length(std::uint64_t &result) noexcept override
- {
- // no error reporting
- result = m_file->size();
- return std::error_condition();
- }
-
-protected:
- core_file_adapter_base(core_file::ptr &&file) noexcept : m_file(file.release()), m_close(true)
- {
- assert(m_file);
- }
-
- core_file_adapter_base(core_file &file) noexcept : m_file(&file), m_close(false)
- {
- }
-
- core_file &file() noexcept
- {
- return *m_file;
- }
-
-private:
- core_file *const m_file;
- bool const m_close;
-};
-
-
-// core_file read implementation
-
-class core_file_read_adapter : public core_file_adapter_base, public virtual random_read
-{
-public:
- core_file_read_adapter(core_file::ptr &&file) noexcept : core_file_adapter_base(std::move(file))
- {
- }
-
- core_file_read_adapter(core_file &file) noexcept : core_file_adapter_base(file)
- {
- }
-
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- // TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
- if (std::numeric_limits<std::uint32_t>::max() < length)
- {
- actual = 0U;
- return std::errc::invalid_argument;
- }
-
- // no way to distinguish between EOF and error
- actual = file().read(buffer, std::uint32_t(length));
- return std::error_condition();
- }
-
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- actual = 0U;
-
- // TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
- if (std::numeric_limits<std::uint32_t>::max() < length)
- return std::errc::invalid_argument;
-
- // no error reporting for tell
- std::uint64_t const oldpos = file().tell();
- if (file().seek(offset, SEEK_SET))
- {
- file().seek(oldpos, SEEK_SET);
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-
- // no way to distinguish between EOF and error
- actual = file().read(buffer, std::uint32_t(length));
-
- if (!file().seek(oldpos, SEEK_SET))
- return std::error_condition();
- else
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-};
-
-
-// core_file read/write implementation
-
-class core_file_read_write_adapter : public core_file_read_adapter, public random_read_write
-{
-public:
- using core_file_read_adapter::core_file_read_adapter;
-
- virtual std::error_condition finalize() noexcept override
- {
- return std::error_condition();
- }
-
- virtual std::error_condition flush() noexcept override
- {
- return file().flush();
- }
-
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- actual = 0U;
- while (length)
- {
- std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
- std::uint32_t const written = file().write(buffer, chunk);
- actual += written;
- if (written < chunk)
- return std::errc::io_error; // TODO: better error reporting for core_file
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
- length -= written;
- }
- return std::error_condition();
- }
-
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- actual = 0U;
-
- // no error reporting for tell
- std::uint64_t const oldpos = file().tell();
- if (file().seek(offset, SEEK_SET))
- {
- file().seek(oldpos, SEEK_SET);
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-
- std::error_condition err;
- while (!err && length)
- {
- std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
- std::uint32_t const written = file().write(buffer, chunk);
- actual += written;
- if (written < chunk)
- {
- err = std::errc::io_error; // TODO: better error reporting for core_file
- }
- else
- {
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
- length -= written;
- }
- }
-
- if (!file().seek(oldpos, SEEK_SET) || err)
- return err;
- else
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-};
-
-
-// core_file helper that fills space when writing past the end-of-file
-
-class core_file_read_write_filler : public random_read_fill_wrapper<core_file_read_write_adapter>
-{
-public:
- core_file_read_write_filler(core_file::ptr &&file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(std::move(file))
- {
- set_filler(fill);
- }
-
- core_file_read_write_filler(core_file &file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(file)
- {
- set_filler(fill);
- }
-
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- // no error reporting for tell or size
- std::uint64_t const offset = file().tell();
- std::uint64_t endpos = file().size();
- if (endpos < offset)
- {
- if (file().seek(endpos, SEEK_SET))
- {
- file().seek(offset, SEEK_SET);
- actual = 0U;
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
- std::uint8_t block[1024];
- std::fill_n(
- block,
- std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos),
- get_filler());
- do
- {
- std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos);
- std::uint32_t const filled = file().write(block, chunk);
- endpos += filled;
- if (chunk != filled)
- {
- file().seek(offset, SEEK_SET);
- actual = 0U;
- return std::errc::io_error; // TODO: better error reporting for core_file
- }
- }
- while (endpos < offset);
- }
-
- return core_file_read_write_adapter::write(buffer, length, actual);
- }
-
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
- {
- actual = 0U;
-
- // no error reporting for tell
- std::uint64_t const oldpos = file().tell();
- if (file().seek(0, SEEK_END))
- {
- file().seek(oldpos, SEEK_SET);
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
- std::uint64_t endpos = file().tell();
-
- std::error_condition err;
- if (endpos > offset)
- {
- if (file().seek(offset, SEEK_SET))
- err = std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
- else if (endpos < offset)
- {
- std::uint8_t block[1024];
- std::fill_n(
- block,
- std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos),
- get_filler());
- do
- {
- std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos);
- std::uint32_t const filled = file().write(block, chunk);
- endpos += filled;
- if (chunk != filled)
- err = std::errc::io_error; // TODO: better error reporting for core_file
- }
- while (!err && (endpos < offset));
- }
-
- while (!err && length)
- {
- std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
- std::uint32_t const written = file().write(buffer, chunk);
- actual += written;
- if (written < chunk)
- {
- err = std::errc::io_error; // TODO: better error reporting for core_file
- }
- else
- {
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
- length -= written;
- }
- }
-
- if (!file().seek(oldpos, SEEK_SET) || err)
- return err;
- else
- return std::errc::invalid_argument; // TODO: better error reporting for core_file
- }
-};
-
} // anonymous namespace
@@ -1181,68 +891,4 @@ random_read_write::ptr osd_file_read_write(osd_file &file) noexcept
return random_read_write::ptr(new (std::nothrow) osd_file_read_write_adapter(file));
}
-
-// creating core_file read adapters
-
-random_read::ptr core_file_read(core_file::ptr &&file) noexcept
-{
- random_read::ptr result;
- if (file)
- result.reset(new (std::nothrow) core_file_read_adapter(std::move(file)));
- return result;
-}
-
-random_read::ptr core_file_read(core_file::ptr &&file, std::uint8_t filler) noexcept
-{
- std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result;
- if (file)
- result.reset(new (std::nothrow) decltype(result)::element_type(std::move(file)));
- if (result)
- result->set_filler(filler);
- return result;
-}
-
-random_read::ptr core_file_read(core_file &file) noexcept
-{
- return random_read::ptr(new (std::nothrow) core_file_read_adapter(file));
-}
-
-random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept
-{
- std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result;
- result.reset(new (std::nothrow) decltype(result)::element_type(file));
- if (result)
- result->set_filler(filler);
- return result;
-}
-
-
-// creating core_file read/write adapters
-
-random_read_write::ptr core_file_read_write(core_file::ptr &&file) noexcept
-{
- random_read_write::ptr result;
- if (file)
- result.reset(new (std::nothrow) core_file_read_write_adapter(std::move(file)));
- return result;
-}
-
-random_read_write::ptr core_file_read_write(core_file::ptr &&file, std::uint8_t filler) noexcept
-{
- random_read_write::ptr result;
- if (file)
- result.reset(new (std::nothrow) core_file_read_write_filler(std::move(file), filler));
- return result;
-}
-
-random_read_write::ptr core_file_read_write(core_file &file) noexcept
-{
- return random_read_write::ptr(new (std::nothrow) core_file_read_write_adapter(file));
-}
-
-random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept
-{
- return random_read_write::ptr(new (std::nothrow) core_file_read_write_filler(file, filler));
-}
-
} // namespace util
diff --git a/src/lib/util/ioprocs.h b/src/lib/util/ioprocs.h
index f2826273c5d..efe0a231a11 100644
--- a/src/lib/util/ioprocs.h
+++ b/src/lib/util/ioprocs.h
@@ -253,16 +253,6 @@ random_read::ptr osd_file_read(osd_file &file) noexcept;
random_read_write::ptr osd_file_read_write(std::unique_ptr<osd_file> &&file) noexcept;
random_read_write::ptr osd_file_read_write(osd_file &file) noexcept;
-random_read::ptr core_file_read(std::unique_ptr<core_file> &&file) noexcept;
-random_read::ptr core_file_read(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept;
-random_read::ptr core_file_read(core_file &file) noexcept;
-random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept;
-
-random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file) noexcept;
-random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept;
-random_read_write::ptr core_file_read_write(core_file &file) noexcept;
-random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept;
-
} // namespace util
#endif // MAME_LIB_UTIL_IOPROCS_H
diff --git a/src/lib/util/ioprocsfilter.cpp b/src/lib/util/ioprocsfilter.cpp
index b25c0493ab1..68b9c90d018 100644
--- a/src/lib/util/ioprocsfilter.cpp
+++ b/src/lib/util/ioprocsfilter.cpp
@@ -11,6 +11,7 @@
#include "ioprocsfilter.h"
#include "ioprocs.h"
+#include "ioprocsfill.h"
#include <zlib.h>
@@ -335,7 +336,7 @@ private:
template <typename T>
class filter_base
{
-protected:
+public:
filter_base(std::unique_ptr<T> &&object) noexcept : m_object(object.release()), m_owned(true)
{
assert(m_object);
@@ -345,6 +346,7 @@ protected:
{
}
+protected:
~filter_base()
{
if (m_owned)
@@ -362,6 +364,111 @@ private:
};
+// helper for forwarding to a read stream
+
+template <typename T>
+class read_stream_proxy : public virtual read_stream, public T
+{
+public:
+ using T::T;
+
+ virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().read(buffer, length, actual);
+ }
+};
+
+
+// helper for forwarding to a write stream
+
+template <typename T>
+class write_stream_proxy : public virtual write_stream, public T
+{
+public:
+ using T::T;
+
+ virtual std::error_condition finalize() noexcept override
+ {
+ return this->object().finalize();
+ }
+
+ virtual std::error_condition flush() noexcept override
+ {
+ return this->object().flush();
+ }
+
+ virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().write(buffer, length, actual);
+ }
+};
+
+
+// helper for forwarding to random-access storage
+
+template <typename T>
+class random_access_proxy : public virtual random_access, public T
+{
+public:
+ using T::T;
+
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
+ {
+ return this->object().seek(offset, whence);
+ }
+
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override
+ {
+ return this->object().tell(result);
+ }
+
+ virtual std::error_condition length(std::uint64_t &result) noexcept override
+ {
+ return this->object().length(result);
+ }
+};
+
+
+// helper for forwarding to random-access read storage
+
+template <typename T>
+class random_read_proxy : public virtual random_read, public read_stream_proxy<T>
+{
+public:
+ using read_stream_proxy<T>::read_stream_proxy;
+
+ virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().read_at(offset, buffer, length, actual);
+ }
+};
+
+
+// helper for forwarding to random-access write storage
+
+template <typename T>
+class random_write_proxy : public virtual random_write, public write_stream_proxy<T>
+{
+public:
+ using write_stream_proxy<T>::write_stream_proxy;
+
+ virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().write_at(offset, buffer, length, actual);
+ }
+};
+
+
+// helper for forwarding to random-access read/write storage
+
+template <typename T>
+class random_read_write_proxy : public random_read_write, protected random_write_proxy<random_read_proxy<T> >
+{
+public:
+ using random_write_proxy<random_read_proxy<T> >::random_write_proxy;
+};
+
+
// filter for decompressing deflated data
template <typename Stream>
@@ -553,6 +660,91 @@ private:
} // anonymous namespace
+// creating filters that fill unread space
+
+read_stream::ptr read_stream_fill(read_stream::ptr &&stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<read_stream_fill_wrapper<read_stream_proxy<filter_base<read_stream> > > > result;
+ if (stream)
+ result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_read::ptr random_read_fill(random_read::ptr &&stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<random_read_proxy<random_access_proxy<filter_base<random_read> > > > > result;
+ if (stream)
+ result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+read_stream::ptr read_stream_fill(read_stream &stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<read_stream_fill_wrapper<read_stream_proxy<filter_base<read_stream> > > > result;
+ result.reset(new (std::nothrow) decltype(result)::element_type(stream));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_read::ptr random_read_fill(random_read &stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<random_read_proxy<random_access_proxy<filter_base<random_read> > > > > result;
+ result.reset(new (std::nothrow) decltype(result)::element_type(stream));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+
+// creating filters that fill unwritten space
+
+random_write::ptr random_write_fill(random_write::ptr &&stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_write_fill_wrapper<random_write_proxy<random_access_proxy<filter_base<random_write> > > > > result;
+ if (stream)
+ result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_write::ptr random_write_fill(random_write &stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_write_fill_wrapper<random_write_proxy<random_access_proxy<filter_base<random_write> > > > > result;
+ result.reset(new (std::nothrow) decltype(result)::element_type(stream));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+
+// creating filters that fill unread/unwritten space
+
+random_read_write::ptr random_read_write_fill(random_read_write::ptr &&stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_write_fill_wrapper<random_read_write_proxy<random_access_proxy<filter_base<random_read_write> > > > > result;
+ if (stream)
+ result.reset(new (std::nothrow) decltype(result)::element_type(std::move(stream)));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_read_write::ptr random_read_write_fill(random_read_write &stream, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_write_fill_wrapper<random_read_write_proxy<random_access_proxy<filter_base<random_read_write> > > > > result;
+ result.reset(new (std::nothrow) decltype(result)::element_type(stream));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+
// creating decompressing filters
read_stream::ptr zlib_read(read_stream::ptr &&stream, std::size_t read_chunk) noexcept
diff --git a/src/lib/util/ioprocsfilter.h b/src/lib/util/ioprocsfilter.h
index a146dd737ce..137dda9ce95 100644
--- a/src/lib/util/ioprocsfilter.h
+++ b/src/lib/util/ioprocsfilter.h
@@ -18,6 +18,17 @@
namespace util {
+std::unique_ptr<read_stream> read_stream_fill(std::unique_ptr<read_stream> &&stream, std::uint8_t filler) noexcept;
+std::unique_ptr<random_read> random_read_fill(std::unique_ptr<random_read> &&stream, std::uint8_t filler) noexcept;
+std::unique_ptr<read_stream> read_stream_fill(read_stream &stream, std::uint8_t filler) noexcept;
+std::unique_ptr<random_read> random_read_fill(random_read &stream, std::uint8_t filler) noexcept;
+
+std::unique_ptr<random_write> random_write_fill(std::unique_ptr<random_write> &&stream, std::uint8_t filler) noexcept;
+std::unique_ptr<random_write> random_write_fill(random_write &stream, std::uint8_t filler) noexcept;
+
+std::unique_ptr<random_read_write> random_read_write_fill(std::unique_ptr<random_read_write> &&stream, std::uint8_t filler) noexcept;
+std::unique_ptr<random_read_write> random_read_write_fill(random_read_write &stream, std::uint8_t filler) noexcept;
+
std::unique_ptr<read_stream> zlib_read(std::unique_ptr<read_stream> &&stream, std::size_t read_chunk) noexcept;
std::unique_ptr<read_stream> zlib_read(std::unique_ptr<random_read> &&stream, std::size_t read_chunk) noexcept;
std::unique_ptr<read_stream> zlib_read(read_stream &stream, std::size_t read_chunk) noexcept;
diff --git a/src/lib/util/msdib.cpp b/src/lib/util/msdib.cpp
index 58d7429fce1..3120e8cb68c 100644
--- a/src/lib/util/msdib.cpp
+++ b/src/lib/util/msdib.cpp
@@ -11,6 +11,8 @@
#include "msdib.h"
#include "coretmpl.h"
+#include "ioprocs.h"
+
#include "eminline.h"
#include "osdcore.h"
@@ -91,7 +93,7 @@ union bitmap_headers
};
-bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits)
+bool dib_parse_mask(std::uint32_t mask, unsigned &shift, unsigned &bits)
{
shift = count_leading_zeros_32(mask);
mask <<= shift;
@@ -113,7 +115,7 @@ void dib_truncate_channel(unsigned &shift, unsigned &bits)
}
-uint8_t dib_splat_sample(uint8_t val, unsigned bits)
+std::uint8_t dib_splat_sample(std::uint8_t val, unsigned bits)
{
assert(8U >= bits);
for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1)
@@ -122,11 +124,13 @@ uint8_t dib_splat_sample(uint8_t val, unsigned bits)
}
-msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen)
+msdib_error dib_read_file_header(read_stream &fp, std::uint32_t &filelen)
{
+ std::size_t actual;
+
// the bitmap file header doesn't use natural alignment
bitmap_file_header file_header;
- if (fp.read(file_header, sizeof(file_header)) != sizeof(file_header))
+ if (fp.read(file_header, sizeof(file_header), actual) || (sizeof(file_header) != actual))
{
LOG("Error reading DIB file header\n");
return msdib_error::FILE_TRUNCATED;
@@ -161,7 +165,7 @@ msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen)
msdib_error dib_read_bitmap_header(
- core_file &fp,
+ random_read &fp,
bitmap_headers &header,
unsigned &palette_bytes,
bool &indexed,
@@ -170,6 +174,8 @@ msdib_error dib_read_bitmap_header(
std::size_t &row_bytes,
std::uint32_t length)
{
+ std::size_t actual;
+
// check that these things haven't been padded somehow
static_assert(sizeof(bitmap_core_header) == 12U, "compiler has applied padding to bitmap_core_header");
static_assert(sizeof(bitmap_info_header) == 56U, "compiler has applied padding to bitmap_info_header");
@@ -179,7 +185,7 @@ msdib_error dib_read_bitmap_header(
if (sizeof(header.core) > length)
return msdib_error::FILE_TRUNCATED;
std::memset(&header, 0, sizeof(header));
- if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size))
+ if (fp.read(&header.core.size, sizeof(header.core.size), actual) || (sizeof(header.core.size) != actual))
{
LOG("Error reading DIB header size (length %u)\n", length);
return msdib_error::FILE_TRUNCATED;
@@ -209,12 +215,18 @@ msdib_error dib_read_bitmap_header(
{
palette_bytes = 3U;
std::uint32_t const header_read(std::min<std::uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
- if (fp.read(&header.core.width, header_read) != header_read)
+ if (fp.read(&header.core.width, header_read, actual) || (header_read != actual))
{
LOG("Error reading DIB core header from image data (%u bytes)\n", length);
return msdib_error::FILE_TRUNCATED;
}
- fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR);
+ if (fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR))
+ {
+ LOG(
+ "Error seeking past additional DIB header data (%u bytes)\n",
+ header.core.size - sizeof(header.core.size) - header_read);
+ return msdib_error::FILE_ERROR;
+ }
header.core.width = little_endianize_int16(header.core.width);
header.core.height = little_endianize_int16(header.core.height);
header.core.planes = little_endianize_int16(header.core.planes);
@@ -256,12 +268,18 @@ msdib_error dib_read_bitmap_header(
{
palette_bytes = 4U;
std::uint32_t const header_read(std::min<std::uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
- if (fp.read(&header.info.width, header_read) != header_read)
+ if (fp.read(&header.info.width, header_read, actual) || (header_read != actual))
{
LOG("Error reading DIB info header from image data (%u bytes)\n", length);
return msdib_error::FILE_TRUNCATED;
}
- fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR);
+ if (fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR))
+ {
+ LOG(
+ "Error seeking past additional DIB header data (%u bytes)\n",
+ header.info.size - sizeof(header.info.size) - header_read);
+ return msdib_error::FILE_ERROR;
+ }
header.info.width = little_endianize_int32(header.info.width);
header.info.height = little_endianize_int32(header.info.height);
header.info.planes = little_endianize_int16(header.info.planes);
@@ -379,7 +397,7 @@ msdib_error dib_read_bitmap_header(
-msdib_error msdib_verify_header(core_file &fp)
+msdib_error msdib_verify_header(random_read &fp)
{
msdib_error err;
@@ -420,7 +438,7 @@ msdib_error msdib_verify_header(core_file &fp)
}
-msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
+msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap)
{
std::uint32_t file_length;
msdib_error const headerr(dib_read_file_header(fp, file_length));
@@ -431,9 +449,10 @@ msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
}
-msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight)
+msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight)
{
// read the bitmap header
+ std::size_t actual;
bitmap_headers header;
unsigned palette_bytes;
bool indexed;
@@ -477,17 +496,18 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
if (indexed)
{
// read palette and convert
- std::unique_ptr<std::uint8_t []> palette_data;
- try { palette_data.reset(new std::uint8_t [palette_size]); }
- catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
- if (fp.read(palette_data.get(), palette_size) != palette_size)
+ std::unique_ptr<std::uint8_t []> palette_data(new (std::nothrow) std::uint8_t [palette_size]);
+ if (!palette_data)
+ return msdib_error::OUT_OF_MEMORY;
+ if (fp.read(palette_data.get(), palette_size, actual) || (palette_size != actual))
{
LOG("Error reading palette from DIB image data (%u bytes)\n", length);
return msdib_error::FILE_TRUNCATED;
}
std::size_t const palette_usable(std::min<std::size_t>(palette_entries, std::size_t(1) << header.info.bpp));
- try { palette.reset(new rgb_t [palette_usable]); }
- catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
+ palette.reset(new (std::nothrow) rgb_t [palette_usable]);
+ if (!palette)
+ return msdib_error::OUT_OF_MEMORY;
std::uint8_t const *ptr(palette_data.get());
for (std::size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes)
palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]);
@@ -496,7 +516,13 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
{
// skip over the palette if necessary
if (palette_entries)
- fp.seek(palette_bytes * palette_entries, SEEK_CUR);
+ {
+ if (fp.seek(palette_bytes * palette_entries, SEEK_CUR))
+ {
+ LOG("Error seeking past DIB palette data (%u bytes)\n", palette_bytes * palette_entries);
+ return msdib_error::FILE_ERROR;
+ }
+ }
// convert masks to shifts
bool const masks_contiguous(
@@ -545,14 +571,14 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
}
// allocate the bitmap and process row data
- std::unique_ptr<std::uint8_t []> row_data;
- try {row_data.reset(new std::uint8_t [row_bytes]); }
- catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
+ std::unique_ptr<std::uint8_t []> row_data(new (std::nothrow) std::uint8_t [row_bytes]);
+ if (!row_data)
+ return msdib_error::OUT_OF_MEMORY;
bitmap.allocate(header.info.width, header.info.height);
int const y_inc(top_down ? 1 : -1);
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
{
- if (fp.read(row_data.get(), row_bytes) != row_bytes)
+ if (fp.read(row_data.get(), row_bytes, actual) || (row_bytes != actual))
{
LOG("Error reading DIB row %d data from image data\n", i);
return msdib_error::FILE_TRUNCATED;
@@ -615,7 +641,7 @@ msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::ui
{
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
{
- if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes)
+ if (fp.read(row_data.get(), mask_row_bytes, actual) || (mask_row_bytes != actual))
{
LOG("Error reading DIB mask row %d data from image data\n", i);
return msdib_error::FILE_TRUNCATED;
diff --git a/src/lib/util/msdib.h b/src/lib/util/msdib.h
index 79c1bb6e796..8d8a9078c5b 100644
--- a/src/lib/util/msdib.h
+++ b/src/lib/util/msdib.h
@@ -13,7 +13,7 @@
#pragma once
#include "bitmap.h"
-#include "corefile.h"
+#include "utilfwd.h"
#include <cstdint>
@@ -36,9 +36,9 @@ enum class msdib_error
UNSUPPORTED_FORMAT
};
-msdib_error msdib_verify_header(core_file &fp);
-msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
-msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U);
+msdib_error msdib_verify_header(random_read &fp);
+msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap);
+msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U);
} // namespace util
diff --git a/src/lib/util/path.cpp b/src/lib/util/path.cpp
index e69de29bb2d..1b8a2f424dd 100644
--- a/src/lib/util/path.cpp
+++ b/src/lib/util/path.cpp
@@ -0,0 +1,3 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#include "path.h"
diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp
index 0605206bbf1..e2fc9aecfe7 100644
--- a/src/lib/util/png.cpp
+++ b/src/lib/util/png.cpp
@@ -10,13 +10,16 @@
#include "png.h"
-#include "osdcomm.h"
+#include "ioprocs.h"
#include "unicode.h"
+#include "osdcomm.h"
+
#include <zlib.h>
#include <algorithm>
#include <cassert>
+#include <cerrno>
#include <cmath>
#include <cstdlib>
#include <cstring>
@@ -102,6 +105,38 @@ inline void put_16bit(uint8_t *v, uint16_t data) { *reinterpret_cast<uint16_t *>
inline void put_32bit(uint8_t *v, uint32_t data) { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); }
+/***************************************************************************
+ TYPE DEFINITIONS
+***************************************************************************/
+
+class png_category_impl : public std::error_category
+{
+public:
+ virtual char const *name() const noexcept override { return "png"; }
+
+ virtual std::string message(int condition) const override
+ {
+ using namespace std::literals;
+ static std::string_view const s_messages[] = {
+ "No error"sv,
+ "Unknown filter algorithm"sv,
+ "Bad file signature"sv,
+ "Decompression error"sv,
+ "Image file truncated"sv,
+ "Image file corrupt"sv,
+ "Unknown chunk type"sv,
+ "Compression error"sv,
+ "Unsupported image format"sv };
+ if ((0 <= condition) && (std::size(s_messages) > condition))
+ return std::string(s_messages[condition]);
+ else
+ return "Unknown error"s;
+ }
+};
+
+png_category_impl const f_png_category_instance;
+
+
class png_private
{
private:
@@ -120,7 +155,7 @@ private:
std::unique_ptr<std::uint8_t []> data;
};
- png_error process(std::list<image_data_chunk> const &idata)
+ std::error_condition process(std::list<image_data_chunk> const &idata)
{
// do some basic checks for unsupported images
if (!pnginfo.bit_depth || (std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
@@ -137,14 +172,15 @@ private:
pass_offset[pass + 1] = pass_offset[pass] + get_pass_bytes(pass);
// allocate memory for the filtered image
- try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ pnginfo.image.reset(new (std::nothrow) std::uint8_t [pass_offset[pass_count]]);
+ if (!pnginfo.image)
+ return std::errc::not_enough_memory;
// decompress image data
- png_error error = png_error::NONE;
+ std::error_condition error;
error = decompress(idata, pass_offset[pass_count]);
std::uint32_t const bpp(get_bytes_per_pixel());
- for (unsigned pass = 0; (pass_count > pass) && (png_error::NONE == error); ++pass)
+ for (unsigned pass = 0; (pass_count > pass) && !error; ++pass)
{
// compute some basic parameters
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
@@ -153,7 +189,7 @@ private:
// we de-filter in place, stripping the filter bytes off the rows
uint8_t *dst(&pnginfo.image[pass_offset[pass]]);
uint8_t const *src(dst);
- for (std::uint32_t y = 0; (dimensions.second > y) && (png_error::NONE == error); ++y)
+ for (std::uint32_t y = 0; (dimensions.second > y) && !error; ++y)
{
// first byte of each row is the filter type
uint8_t const filter(*src++);
@@ -164,13 +200,13 @@ private:
}
// if we errored, free the image data
- if (error != png_error::NONE)
+ if (error)
pnginfo.image.reset();
return error;
}
- png_error decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected)
+ std::error_condition decompress(std::list<image_data_chunk> const &idata, std::uint32_t expected)
{
// only deflate is permitted
if (0 != pnginfo.compression_method)
@@ -186,7 +222,11 @@ private:
stream.avail_in = 0;
stream.next_in = Z_NULL;
zerr = inflateInit(&stream);
- if (Z_OK != zerr)
+ if (Z_ERRNO == zerr)
+ return std::error_condition(errno, std::generic_category());
+ else if (Z_MEM_ERROR == zerr)
+ return std::errc::not_enough_memory;
+ else if (Z_OK != zerr)
return png_error::DECOMPRESS_ERROR;
// decompress IDAT blocks
@@ -209,12 +249,12 @@ private:
// it's all good if we got end-of-stream or we have with no data remaining
if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in)))
- return png_error::NONE;
+ return std::error_condition();
else
- return png_error::DECOMPRESS_ERROR;
+ return png_error::DECOMPRESS_ERROR; // TODO: refactor this function for more fine-grained error reporting?
}
- png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
+ std::error_condition unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
{
if (0 != pnginfo.filter_method)
return png_error::UNKNOWN_FILTER;
@@ -223,14 +263,14 @@ private:
{
case PNG_PF_None: // no filter, just copy
std::copy_n(src, rowbytes, dst);
- return png_error::NONE;
+ return std::error_condition();
case PNG_PF_Sub: // SUB = previous pixel
dst = std::copy_n(src, bpp, dst);
src += bpp;
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
*dst = *src + dst[-bpp];
- return png_error::NONE;
+ return std::error_condition();
case PNG_PF_Up: // UP = pixel above
if (dstprev)
@@ -242,7 +282,7 @@ private:
{
std::copy_n(src, rowbytes, dst);
}
- return png_error::NONE;
+ return std::error_condition();
case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel
if (dstprev)
@@ -259,7 +299,7 @@ private:
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
*dst = *src + (dst[-bpp] >> 1);
}
- return png_error::NONE;
+ return std::error_condition();
case PNG_PF_Paeth: // PAETH = special filter
for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst)
@@ -273,14 +313,14 @@ private:
int32_t const dc(std::abs(prediction - pc));
*dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc);
}
- return png_error::NONE;
+ return std::error_condition();
default: // unknown filter type
return png_error::UNKNOWN_FILTER;
}
}
- png_error process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
+ std::error_condition process_chunk(std::list<image_data_chunk> &idata, std::unique_ptr<std::uint8_t []> &&data, uint32_t type, uint32_t length)
{
switch (type)
{
@@ -312,7 +352,7 @@ private:
case PNG_CN_IDAT: // image data
try { idata.emplace_back(length, std::move(data)); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
break;
case PNG_CN_gAMA: // gamma
@@ -353,7 +393,7 @@ private:
}
catch (std::bad_alloc const &)
{
- return png_error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
break;
@@ -363,7 +403,7 @@ private:
return png_error::UNKNOWN_CHUNK;
break;
}
- return png_error::NONE;
+ return std::error_condition();
}
unsigned get_pass_count() const
@@ -405,23 +445,31 @@ private:
return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3;
}
- static png_error read_chunk(core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
+ static std::error_condition read_chunk(read_stream &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
{
+ std::error_condition err;
+ std::size_t actual;
std::uint8_t tempbuff[4];
/* fetch the length of this chunk */
- if (fp.read(tempbuff, 4) != 4)
+ err = fp.read(tempbuff, 4, actual);
+ if (err)
+ return err;
+ else if (4 != actual)
return png_error::FILE_TRUNCATED;
length = fetch_32bit(tempbuff);
/* fetch the type of this chunk */
- if (fp.read(tempbuff, 4) != 4)
+ err = fp.read(tempbuff, 4, actual);
+ if (err)
+ return err;
+ else if (4 != actual)
return png_error::FILE_TRUNCATED;
type = fetch_32bit(tempbuff);
/* stop when we hit an IEND chunk */
if (type == PNG_CN_IEND)
- return png_error::NONE;
+ return std::error_condition();
/* start the CRC with the chunk type (but not the length) */
std::uint32_t crc = crc32(0, tempbuff, 4);
@@ -430,11 +478,18 @@ private:
if (length)
{
/* allocate memory for this chunk */
- try { data.reset(new std::uint8_t [length]); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ data.reset(new (std::nothrow) std::uint8_t [length]);
+ if (!data)
+ return std::errc::not_enough_memory;
/* read the data from the file */
- if (fp.read(data.get(), length) != length)
+ err = fp.read(data.get(), length, actual);
+ if (err)
+ {
+ data.reset();
+ return err;
+ }
+ else if (length != actual)
{
data.reset();
return png_error::FILE_TRUNCATED;
@@ -445,7 +500,13 @@ private:
}
/* read the CRC */
- if (fp.read(tempbuff, 4) != 4)
+ err = fp.read(tempbuff, 4, actual);
+ if (err)
+ {
+ data.reset();
+ return err;
+ }
+ else if (4 != actual)
{
data.reset();
return png_error::FILE_TRUNCATED;
@@ -459,7 +520,7 @@ private:
return png_error::FILE_CORRUPT;
}
- return png_error::NONE;
+ return std::error_condition();
}
png_info & pnginfo;
@@ -469,7 +530,7 @@ public:
{
}
- png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
+ std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
{
// do some basic checks for unsupported images
if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8))
@@ -595,14 +656,14 @@ public:
// set hasalpha flag and return
hasalpha = 0xffU != accumalpha;
- return png_error::NONE;
+ return std::error_condition();
}
- png_error expand_buffer_8bit()
+ std::error_condition expand_buffer_8bit()
{
// nothing to do if we're at 8 or greater already
if (pnginfo.bit_depth >= 8)
- return png_error::NONE;
+ return std::error_condition();
// do some basic checks for unsupported images
if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth))
@@ -623,9 +684,9 @@ public:
}
// allocate a new buffer at 8-bit
- std::unique_ptr<std::uint8_t []> outbuf;
- try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ std::unique_ptr<std::uint8_t []> outbuf(new (std::nothrow) std::uint8_t [outp_offset[pass_count]]);
+ if (!outbuf)
+ return std::errc::not_enough_memory;
// upsample bitmap
std::uint8_t const bytesamples(8 / pnginfo.bit_depth);
@@ -679,13 +740,13 @@ public:
pnginfo.image = std::move(outbuf);
pnginfo.bit_depth = 8;
- return png_error::NONE;
+ return std::error_condition();
}
- png_error read_file(core_file &fp)
+ std::error_condition read_file(read_stream &fp)
{
// initialize the data structures
- png_error error = png_error::NONE;
+ std::error_condition error;
pnginfo.reset();
std::list<image_data_chunk> idata;
@@ -693,13 +754,13 @@ public:
error = verify_header(fp);
// loop until we hit an IEND chunk
- while (png_error::NONE == error)
+ while (!error)
{
// read a chunk
std::unique_ptr<std::uint8_t []> chunk_data;
std::uint32_t chunk_type = 0, chunk_length;
error = read_chunk(fp, chunk_data, chunk_type, chunk_length);
- if (png_error::NONE == error)
+ if (!error)
{
if (chunk_type == PNG_CN_IEND)
break; // stop when we hit an IEND chunk
@@ -709,29 +770,33 @@ public:
}
// finish processing the image
- if (png_error::NONE == error)
+ if (!error)
error = process(idata);
// if we have an error, free all the output data
- if (error != png_error::NONE)
+ if (error)
pnginfo.reset();
return error;
}
- static png_error verify_header(core_file &fp)
+ static std::error_condition verify_header(read_stream &fp)
{
std::uint8_t signature[sizeof(PNG_SIGNATURE)];
// read 8 bytes
- if (fp.read(signature, sizeof(signature)) != sizeof(signature))
+ std::size_t actual;
+ std::error_condition err = fp.read(signature, sizeof(signature), actual);
+ if (err)
+ return err;
+ else if (sizeof(signature) != actual)
return png_error::FILE_TRUNCATED;
// return an error if we don't match
if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE)))
return png_error::BAD_SIGNATURE;
- return png_error::NONE;
+ return std::error_condition();
}
};
@@ -752,7 +817,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7];
core stream
-------------------------------------------------*/
-png_error png_info::verify_header(core_file &fp)
+std::error_condition png_info::verify_header(read_stream &fp)
{
return png_private::verify_header(fp);
}
@@ -762,7 +827,7 @@ png_error png_info::verify_header(core_file &fp)
read_file - read a PNG from a core stream
-------------------------------------------------*/
-png_error png_info::read_file(core_file &fp)
+std::error_condition png_info::read_file(read_stream &fp)
{
return png_private(*this).read_file(fp);
}
@@ -773,20 +838,20 @@ png_error png_info::read_file(core_file &fp)
bitmap
-------------------------------------------------*/
-png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
+std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap)
{
- png_error result;
+ std::error_condition result;
png_info pnginfo;
png_private png(pnginfo);
// read the PNG data
result = png.read_file(fp);
- if (png_error::NONE != result)
+ if (result)
return result;
// resample to 8bpp if necessary
result = png.expand_buffer_8bit();
- if (png_error::NONE != result)
+ if (result)
{
pnginfo.free_data();
return result;
@@ -803,7 +868,7 @@ png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
bitmap
-------------------------------------------------*/
-png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
+std::error_condition png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
{
return png_private(*this).copy_to_bitmap(bitmap, hasalpha);
}
@@ -814,7 +879,7 @@ png_error png_info::copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha)
sub 8-bit to 8-bit
-------------------------------------------------*/
-png_error png_info::expand_buffer_8bit()
+std::error_condition png_info::expand_buffer_8bit()
{
return png_private(*this).expand_buffer_8bit();
}
@@ -829,7 +894,7 @@ png_error png_info::expand_buffer_8bit()
add_text - add a text entry to the png_info
-------------------------------------------------*/
-png_error png_info::add_text(std::string_view keyword, std::string_view text)
+std::error_condition png_info::add_text(std::string_view keyword, std::string_view text)
{
// apply rules to keyword
char32_t prev(0);
@@ -859,8 +924,8 @@ png_error png_info::add_text(std::string_view keyword, std::string_view text)
// allocate a new text element
try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword), std::forward_as_tuple(text)); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
- return png_error::NONE;
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ return std::error_condition();
}
@@ -869,10 +934,12 @@ png_error png_info::add_text(std::string_view keyword, std::string_view text)
the given file
-------------------------------------------------*/
-static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type, uint32_t length)
+static std::error_condition write_chunk(write_stream &fp, const uint8_t *data, uint32_t type, uint32_t length)
{
- uint8_t tempbuff[8];
- uint32_t crc;
+ std::error_condition err;
+ std::size_t written;
+ std::uint8_t tempbuff[8];
+ std::uint32_t crc;
/* stuff the length/type into the buffer */
put_32bit(tempbuff + 0, length);
@@ -880,23 +947,32 @@ static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type,
crc = crc32(0, tempbuff + 4, 4);
/* write that data */
- if (fp.write(tempbuff, 8) != 8)
- return png_error::FILE_ERROR;
+ err = fp.write(tempbuff, 8, written);
+ if (err)
+ return err;
+ else if (8 != written)
+ return std::errc::io_error;
/* append the actual data */
if (length > 0)
{
- if (fp.write(data, length) != length)
- return png_error::FILE_ERROR;
+ err = fp.write(data, length, written);
+ if (err)
+ return err;
+ else if (length != written)
+ return std::errc::io_error;
crc = crc32(crc, data, length);
}
/* write the CRC */
put_32bit(tempbuff, crc);
- if (fp.write(tempbuff, 4) != 4)
- return png_error::FILE_ERROR;
+ err = fp.write(tempbuff, 4, written);
+ if (err)
+ return err;
+ else if (4 != written)
+ return std::errc::io_error;
- return png_error::NONE;
+ return std::error_condition();
}
@@ -905,13 +981,19 @@ static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type,
chunk to the given file by deflating it
-------------------------------------------------*/
-static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t type, uint32_t length)
+static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data, uint32_t type, uint32_t length)
{
- uint64_t lengthpos = fp.tell();
- uint8_t tempbuff[8192];
- uint32_t zlength = 0;
+ std::error_condition err;
+ std::uint64_t lengthpos;
+ err = fp.tell(lengthpos);
+ if (err)
+ return err;
+
+ std::size_t written;
+ std::uint8_t tempbuff[8192];
+ std::uint32_t zlength = 0;
z_stream stream;
- uint32_t crc;
+ std::uint32_t crc;
int zerr;
/* stuff the length/type into the buffer */
@@ -920,15 +1002,22 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
crc = crc32(0, tempbuff + 4, 4);
/* write that data */
- if (fp.write(tempbuff, 8) != 8)
- return png_error::FILE_ERROR;
+ err = fp.write(tempbuff, 8, written);
+ if (err)
+ return err;
+ else if (8 != written)
+ return std::errc::io_error;
/* initialize the stream */
memset(&stream, 0, sizeof(stream));
stream.next_in = data;
stream.avail_in = length;
zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
- if (zerr != Z_OK)
+ if (Z_ERRNO == zerr)
+ return std::error_condition(errno, std::generic_category());
+ else if (Z_MEM_ERROR == zerr)
+ return std::errc::not_enough_memory;
+ else if (Z_OK != zerr)
return png_error::COMPRESS_ERROR;
/* now loop until we run out of data */
@@ -943,10 +1032,16 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
if (stream.avail_out < sizeof(tempbuff))
{
int bytes = sizeof(tempbuff) - stream.avail_out;
- if (fp.write(tempbuff, bytes) != bytes)
+ err = fp.write(tempbuff, bytes, written);
+ if (err)
{
deflateEnd(&stream);
- return png_error::FILE_ERROR;
+ return err;
+ }
+ else if (bytes != written)
+ {
+ deflateEnd(&stream);
+ return std::errc::io_error;
}
crc = crc32(crc, tempbuff, bytes);
zlength += bytes;
@@ -960,29 +1055,45 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
if (zerr != Z_OK)
{
deflateEnd(&stream);
- return png_error::COMPRESS_ERROR;
+ if (Z_ERRNO == zerr)
+ return std::error_condition(errno, std::generic_category());
+ else if (Z_MEM_ERROR == zerr)
+ return std::errc::not_enough_memory;
+ else
+ return png_error::COMPRESS_ERROR;
}
}
/* clean up deflater(maus) */
zerr = deflateEnd(&stream);
- if (zerr != Z_OK)
+ if (Z_ERRNO == zerr)
+ return std::error_condition(errno, std::generic_category());
+ else if (Z_MEM_ERROR == zerr)
+ return std::errc::not_enough_memory;
+ else if (Z_OK != zerr)
return png_error::COMPRESS_ERROR;
/* write the CRC */
put_32bit(tempbuff, crc);
- if (fp.write(tempbuff, 4) != 4)
- return png_error::FILE_ERROR;
+ err = fp.write(tempbuff, 4, written);
+ if (err)
+ return err;
+ else if (4 != written)
+ return std::errc::io_error;
/* seek back and update the length */
- fp.seek(lengthpos, SEEK_SET);
+ err = fp.seek(lengthpos, SEEK_SET);
+ if (err)
+ return err;
put_32bit(tempbuff + 0, zlength);
- if (fp.write(tempbuff, 4) != 4)
- return png_error::FILE_ERROR;
+ err = fp.write(tempbuff, 4, written);
+ if (err)
+ return err;
+ else if (4 != written)
+ return std::errc::io_error;
/* return to the end */
- fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
- return png_error::NONE;
+ return fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
}
@@ -991,26 +1102,24 @@ static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t typ
bitmap to a palettized image
-------------------------------------------------*/
-static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
+static std::error_condition convert_bitmap_to_image_palette(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
{
- int rowbytes;
- int x, y;
-
/* set the common info */
pnginfo.width = bitmap.width();
pnginfo.height = bitmap.height();
pnginfo.bit_depth = 8;
pnginfo.color_type = 3;
pnginfo.num_palette = 256;
- rowbytes = pnginfo.width;
+ int const rowbytes = pnginfo.width;
/* allocate memory for the palette */
- try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ pnginfo.palette.reset(new (std::nothrow) std::uint8_t [3 * 256]);
+ if (!pnginfo.palette)
+ return std::errc::not_enough_memory;
/* build the palette */
std::fill_n(pnginfo.palette.get(), 3 * 256, 0);
- for (x = 0; x < palette_length; x++)
+ for (int x = 0; x < palette_length; x++)
{
rgb_t color = palette[x];
pnginfo.palette[3 * x + 0] = color.r();
@@ -1019,29 +1128,26 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
}
/* allocate memory for the image */
- try
- {
- pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]);
- }
- catch (std::bad_alloc const &)
+ pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]);
+ if (!pnginfo.image)
{
pnginfo.palette.reset();
- return png_error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
/* copy in the pixels, specifying a nullptr filter */
- for (y = 0; y < pnginfo.height; y++)
+ for (int y = 0; y < pnginfo.height; y++)
{
uint16_t const *src = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
/* store the filter byte, then copy the data */
*dst++ = 0;
- for (x = 0; x < pnginfo.width; x++)
+ for (int x = 0; x < pnginfo.width; x++)
*dst++ = *src++;
}
- return png_error::NONE;
+ return std::error_condition();
}
@@ -1050,77 +1156,75 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
bitmap to an RGB image
-------------------------------------------------*/
-static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
+static std::error_condition convert_bitmap_to_image_rgb(png_info &pnginfo, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
{
- int alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
- int rowbytes;
- int x, y;
+ bool const alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
/* set the common info */
pnginfo.width = bitmap.width();
pnginfo.height = bitmap.height();
pnginfo.bit_depth = 8;
pnginfo.color_type = alpha ? 6 : 2;
- rowbytes = pnginfo.width * (alpha ? 4 : 3);
+ int const rowbytes = pnginfo.width * (alpha ? 4 : 3);
/* allocate memory for the image */
- try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]);
+ if (!pnginfo.image)
+ return std::errc::not_enough_memory;
/* copy in the pixels, specifying a nullptr filter */
- for (y = 0; y < pnginfo.height; y++)
+ for (int y = 0; y < pnginfo.height; y++)
{
uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
/* store the filter byte, then copy the data */
*dst++ = 0;
- /* 16bpp palettized format */
if (bitmap.format() == BITMAP_FORMAT_IND16)
{
+ /* 16bpp palettized format */
uint16_t const *src16 = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ for (int x = 0; x < pnginfo.width; x++)
{
- rgb_t color = palette[*src16++];
+ rgb_t const color = palette[*src16++];
*dst++ = color.r();
*dst++ = color.g();
*dst++ = color.b();
}
}
-
- /* 32-bit RGB direct */
else if (bitmap.format() == BITMAP_FORMAT_RGB32)
{
+ /* 32-bit RGB direct */
uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ for (int x = 0; x < pnginfo.width; x++)
{
- rgb_t raw = *src32++;
+ rgb_t const raw = *src32++;
*dst++ = raw.r();
*dst++ = raw.g();
*dst++ = raw.b();
}
}
-
- /* 32-bit ARGB direct */
else if (bitmap.format() == BITMAP_FORMAT_ARGB32)
{
+ /* 32-bit ARGB direct */
uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ for (int x = 0; x < pnginfo.width; x++)
{
- rgb_t raw = *src32++;
+ rgb_t const raw = *src32++;
*dst++ = raw.r();
*dst++ = raw.g();
*dst++ = raw.b();
*dst++ = raw.a();
}
}
-
- /* unsupported format */
else
+ {
+ /* unsupported format */
return png_error::UNSUPPORTED_FORMAT;
+ }
}
- return png_error::NONE;
+ return std::error_condition();
}
@@ -1129,17 +1233,17 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &
chunks to the given file
-------------------------------------------------*/
-static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
+static std::error_condition write_png_stream(random_write &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
{
uint8_t tempbuff[16];
- png_error error;
+ std::error_condition error;
// create an unfiltered image in either palette or RGB form
if (bitmap.format() == BITMAP_FORMAT_IND16 && palette_length <= 256)
error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette);
else
error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette);
- if (error != png_error::NONE)
+ if (error)
return error;
// if we wanted to get clever and do filtering, we would do it here
@@ -1153,18 +1257,18 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
put_8bit(tempbuff + 11, pnginfo.filter_method);
put_8bit(tempbuff + 12, pnginfo.interlace_method);
error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13);
- if (error != png_error::NONE)
+ if (error)
return error;
// write the PLTE chunk
if (pnginfo.num_palette > 0)
error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3);
- if (error != png_error::NONE)
+ if (error)
return error;
// write a single IDAT chunk
error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1));
- if (error != png_error::NONE)
+ if (error)
return error;
// write TEXT chunks
@@ -1172,7 +1276,7 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
for (png_info::png_text const &text : pnginfo.textlist)
{
try { textbuf.resize(text.first.length() + 1 + text.second.length()); }
- catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
std::uint8_t *dst(&textbuf[0]);
// convert keyword to ISO-8859-1
@@ -1201,7 +1305,7 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
}
error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]);
- if (error != png_error::NONE)
+ if (error)
return error;
}
@@ -1210,16 +1314,20 @@ static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap
}
-png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
+std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
{
// use a dummy pnginfo if none passed to us
png_info pnginfo;
- if (info == nullptr)
+ if (!info)
info = &pnginfo;
// write the PNG signature
- if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE))
- return png_error::FILE_ERROR;
+ std::size_t written;
+ std::error_condition err = fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE), written);
+ if (err)
+ return err;
+ else if (sizeof(PNG_SIGNATURE) != written)
+ return std::errc::io_error;
/* write the rest of the PNG data */
return write_png_stream(fp, *info, bitmap, palette_length, palette);
@@ -1233,25 +1341,16 @@ png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap
********************************************************************************/
-/**
- * @fn png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
- *
- * @brief Mng capture start.
- *
- * @param [in,out] fp If non-null, the fp.
- * @param [in,out] bitmap The bitmap.
- * @param rate The framerate.
- *
- * @return A png_error.
- */
-
-png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
+std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate)
{
- uint8_t mhdr[28];
-
- if (fp.write(MNG_Signature, 8) != 8)
- return png_error::FILE_ERROR;
+ std::size_t written;
+ std::error_condition err = fp.write(MNG_Signature, 8, written);
+ if (err)
+ return err;
+ else if (8 != written)
+ return std::errc::io_error;
+ uint8_t mhdr[28];
memset(mhdr, 0, 28);
put_32bit(mhdr + 0, bitmap.width());
put_32bit(mhdr + 4, bitmap.height());
@@ -1260,38 +1359,27 @@ png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
return write_chunk(fp, mhdr, MNG_CN_MHDR, 28);
}
-/**
- * @fn png_error mng_capture_frame(core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette)
- *
- * @brief Mng capture frame.
- *
- * @param [in,out] fp If non-null, the fp.
- * @param [in,out] info If non-null, the information.
- * @param [in,out] bitmap The bitmap.
- * @param palette_length Length of the palette.
- * @param palette The palette.
- *
- * @return A png_error.
- */
-
-png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
+
+std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette)
{
return write_png_stream(fp, info, bitmap, palette_length, palette);
}
-/**
- * @fn png_error mng_capture_stop(core_file &fp)
- *
- * @brief Mng capture stop.
- *
- * @param [in,out] fp If non-null, the fp.
- *
- * @return A png_error.
- */
-
-png_error mng_capture_stop(core_file &fp)
+
+std::error_condition mng_capture_stop(random_write &fp)
{
return write_chunk(fp, nullptr, MNG_CN_MEND, 0);
}
+
+/*-------------------------------------------------
+ png_category - gets the PNG error category
+ instance
+-------------------------------------------------*/
+
+std::error_category const &png_category() noexcept
+{
+ return f_png_category_instance;
+}
+
} // namespace util
diff --git a/src/lib/util/png.h b/src/lib/util/png.h
index 8c80f0aa5ea..d802c1a5a82 100644
--- a/src/lib/util/png.h
+++ b/src/lib/util/png.h
@@ -14,13 +14,14 @@
#pragma once
#include "bitmap.h"
-#include "corefile.h"
+#include "utilfwd.h"
#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <string_view>
+#include <system_error>
#include <utility>
@@ -31,12 +32,9 @@ namespace util {
***************************************************************************/
/* Error types */
-enum class png_error
+enum class png_error : int
{
- NONE,
- OUT_OF_MEMORY,
- UNKNOWN_FILTER,
- FILE_ERROR,
+ UNKNOWN_FILTER = 1,
BAD_SIGNATURE,
DECOMPRESS_ERROR,
FILE_TRUNCATED,
@@ -46,6 +44,9 @@ enum class png_error
UNSUPPORTED_FORMAT
};
+std::error_category const &png_category() noexcept;
+inline std::error_condition make_error_condition(png_error err) noexcept { return std::error_condition(int(err), png_category()); }
+
/***************************************************************************
@@ -59,16 +60,16 @@ public:
~png_info() { free_data(); }
- png_error read_file(core_file &fp);
- png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha);
- png_error expand_buffer_8bit();
+ std::error_condition read_file(read_stream &fp);
+ std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha);
+ std::error_condition expand_buffer_8bit();
- png_error add_text(std::string_view keyword, std::string_view text);
+ std::error_condition add_text(std::string_view keyword, std::string_view text);
void free_data();
void reset() { free_data(); operator=(png_info()); }
- static png_error verify_header(core_file &fp);
+ static std::error_condition verify_header(read_stream &fp);
std::unique_ptr<std::uint8_t []> image;
std::uint32_t width, height;
@@ -101,14 +102,21 @@ private:
FUNCTION PROTOTYPES
***************************************************************************/
-png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
+std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap);
-png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
+std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
-png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate);
-png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
-png_error mng_capture_stop(core_file &fp);
+std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate);
+std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette);
+std::error_condition mng_capture_stop(random_write &fp);
} // namespace util
+
+namespace std {
+
+template <> struct is_error_condition_enum<util::png_error> : public std::true_type { };
+
+} // namespace std
+
#endif // MAME_LIB_UTIL_PNG_H
diff --git a/src/lib/util/xmlfile.cpp b/src/lib/util/xmlfile.cpp
index adcf56a15c7..b290f6e5381 100644
--- a/src/lib/util/xmlfile.cpp
+++ b/src/lib/util/xmlfile.cpp
@@ -47,7 +47,8 @@ void write_escaped(core_file &file, std::string const &str)
std::string::size_type const found = str.find_first_of("\"&<>", pos);
if (found != std::string::npos)
{
- file.write(&str[pos], found - pos);
+ std::size_t written;
+ file.write(&str[pos], found - pos, written);
switch (str[found])
{
case '"': file.puts("&quot;"); pos = found + 1; break;
@@ -59,7 +60,8 @@ void write_escaped(core_file &file, std::string const &str)
}
else
{
- file.write(&str[pos], str.size() - pos);
+ std::size_t written;
+ file.write(&str[pos], str.size() - pos, written);
pos = found;
}
}
@@ -118,28 +120,28 @@ file::ptr file::create()
// read - parse an XML file into its nodes
//-------------------------------------------------
-file::ptr file::read(util::core_file &file, parse_options const *opts)
+file::ptr file::read(read_stream &file, parse_options const *opts)
{
- parse_info info;
- int done;
-
// set up the parser
+ parse_info info;
if (!expat_setup_parser(info, opts))
return ptr();
// loop through the file and parse it
+ bool done;
do
{
char tempbuf[TEMP_BUFFER_SIZE];
// read as much as we can
- int bytes = file.read(tempbuf, sizeof(tempbuf));
- done = file.eof();
+ size_t bytes;
+ file.read(tempbuf, sizeof(tempbuf), bytes); // TODO: better error handling
+ done = !bytes;
// parse the data
if (XML_Parse(info.parser, tempbuf, bytes, done) == XML_STATUS_ERROR)
{
- if (opts != nullptr && opts->error != nullptr)
+ if (opts && opts->error)
{
opts->error->error_message = XML_ErrorString(XML_GetErrorCode(info.parser));
opts->error->error_line = XML_GetCurrentLineNumber(info.parser);
diff --git a/src/lib/util/xmlfile.h b/src/lib/util/xmlfile.h
index ba3a1176e25..8d0f2b8c36c 100644
--- a/src/lib/util/xmlfile.h
+++ b/src/lib/util/xmlfile.h
@@ -223,7 +223,7 @@ public:
static ptr create();
// parse an XML file into its nodes
- static ptr read(util::core_file &file, parse_options const *opts);
+ static ptr read(read_stream &file, parse_options const *opts);
// parse an XML string into its nodes
static ptr string_read(const char *string, parse_options const *opts);