summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/abi.h117
-rw-r--r--src/lib/util/avhuff.cpp127
-rw-r--r--src/lib/util/avhuff.h66
-rw-r--r--src/lib/util/aviio.cpp1485
-rw-r--r--src/lib/util/aviio.h33
-rw-r--r--src/lib/util/bitmap.cpp233
-rw-r--r--src/lib/util/bitmap.h246
-rw-r--r--src/lib/util/bitstream.h70
-rw-r--r--src/lib/util/cdrom.cpp2791
-rw-r--r--src/lib/util/cdrom.h450
-rw-r--r--src/lib/util/chd.cpp2086
-rw-r--r--src/lib/util/chd.h345
-rw-r--r--src/lib/util/chdcd.cpp1333
-rw-r--r--src/lib/util/chdcd.h38
-rw-r--r--src/lib/util/chdcodec.cpp616
-rw-r--r--src/lib/util/chdcodec.h106
-rw-r--r--src/lib/util/client_ws.hpp59
-rw-r--r--src/lib/util/client_wss.hpp2
-rw-r--r--src/lib/util/corealloc.cpp34
-rw-r--r--src/lib/util/corealloc.h85
-rw-r--r--src/lib/util/corefile.cpp1278
-rw-r--r--src/lib/util/corefile.h94
-rw-r--r--src/lib/util/corestr.cpp219
-rw-r--r--src/lib/util/corestr.h44
-rw-r--r--src/lib/util/coretmpl.h673
-rw-r--r--src/lib/util/coreutil.cpp14
-rw-r--r--src/lib/util/coreutil.h9
-rw-r--r--src/lib/util/crypto.hpp35
-rw-r--r--src/lib/util/delegate.cpp114
-rw-r--r--src/lib/util/delegate.h892
-rw-r--r--src/lib/util/disasmintf.cpp14
-rw-r--r--src/lib/util/disasmintf.h8
-rw-r--r--src/lib/util/dvdrom.cpp105
-rw-r--r--src/lib/util/dvdrom.h41
-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/endianness.h161
-rw-r--r--src/lib/util/flac.cpp183
-rw-r--r--src/lib/util/flac.h43
-rw-r--r--src/lib/util/harddisk.cpp245
-rw-r--r--src/lib/util/harddisk.h60
-rw-r--r--src/lib/util/hash.cpp226
-rw-r--r--src/lib/util/hash.h52
-rw-r--r--src/lib/util/hashing.cpp336
-rw-r--r--src/lib/util/hashing.h173
-rw-r--r--src/lib/util/huffman.cpp6
-rw-r--r--src/lib/util/huffman.h2
-rw-r--r--src/lib/util/ioprocs.cpp982
-rw-r--r--src/lib/util/ioprocs.h377
-rw-r--r--src/lib/util/ioprocsfill.h190
-rw-r--r--src/lib/util/ioprocsfilter.cpp792
-rw-r--r--src/lib/util/ioprocsfilter.h283
-rw-r--r--src/lib/util/ioprocsvec.h212
-rw-r--r--src/lib/util/jedparse.cpp169
-rw-r--r--src/lib/util/jedparse.h9
-rw-r--r--src/lib/util/language.cpp220
-rw-r--r--src/lib/util/language.h43
-rw-r--r--src/lib/util/lrucache.h408
-rw-r--r--src/lib/util/md5.cpp5
-rw-r--r--src/lib/util/mfpresolve.cpp431
-rw-r--r--src/lib/util/mfpresolve.h151
-rw-r--r--src/lib/util/msdib.cpp673
-rw-r--r--src/lib/util/msdib.h45
-rw-r--r--src/lib/util/multibyte.h301
-rw-r--r--src/lib/util/nanosvg.cpp6
-rw-r--r--src/lib/util/nanosvg.h33
-rw-r--r--src/lib/util/notifier.h240
-rw-r--r--src/lib/util/opresolv.cpp15
-rw-r--r--src/lib/util/opresolv.h4
-rw-r--r--src/lib/util/options.cpp538
-rw-r--r--src/lib/util/options.h126
-rw-r--r--src/lib/util/palette.cpp45
-rw-r--r--src/lib/util/palette.h92
-rw-r--r--src/lib/util/path.cpp82
-rw-r--r--src/lib/util/path.h100
-rw-r--r--src/lib/util/plaparse.cpp352
-rw-r--r--src/lib/util/plaparse.h7
-rw-r--r--src/lib/util/png.cpp710
-rw-r--r--src/lib/util/png.h64
-rw-r--r--src/lib/util/pool.cpp621
-rw-r--r--src/lib/util/pool.h118
-rw-r--r--src/lib/util/server_http_impl.hpp8
-rw-r--r--src/lib/util/server_ws_impl.hpp11
-rw-r--r--src/lib/util/sha1.cpp443
-rw-r--r--src/lib/util/sha1.h63
-rw-r--r--src/lib/util/sha1.hpp181
-rw-r--r--src/lib/util/simh_tape_file.cpp622
-rw-r--r--src/lib/util/simh_tape_file.h65
-rw-r--r--src/lib/util/strformat.cpp173
-rw-r--r--src/lib/util/strformat.h988
-rw-r--r--src/lib/util/tape_file_interface.h58
-rw-r--r--src/lib/util/timeconv.cpp52
-rw-r--r--src/lib/util/timeconv.h74
-rw-r--r--src/lib/util/un7z.cpp495
-rw-r--r--src/lib/util/unicode.cpp68
-rw-r--r--src/lib/util/unicode.h57
-rw-r--r--src/lib/util/unzip.cpp1266
-rw-r--r--src/lib/util/unzip.h60
-rw-r--r--src/lib/util/utf8.h20
-rw-r--r--src/lib/util/utilfwd.h52
-rw-r--r--src/lib/util/vbiparse.cpp38
-rw-r--r--src/lib/util/vbiparse.h2
-rw-r--r--src/lib/util/wavwrite.cpp224
-rw-r--r--src/lib/util/wavwrite.h33
-rw-r--r--src/lib/util/xmlfile.cpp659
-rw-r--r--src/lib/util/xmlfile.h29
-rw-r--r--src/lib/util/zippath.cpp349
-rw-r--r--src/lib/util/zippath.h13
109 files changed, 19554 insertions, 11564 deletions
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/avhuff.cpp b/src/lib/util/avhuff.cpp
index 9975287f4c1..cb78eac5209 100644
--- a/src/lib/util/avhuff.cpp
+++ b/src/lib/util/avhuff.cpp
@@ -58,14 +58,14 @@
***************************************************************************/
-#include <assert.h>
-
#include "avhuff.h"
-#include "huffman.h"
+
#include "chd.h"
+#include "huffman.h"
+#include "multibyte.h"
-#include <math.h>
-#include <stdlib.h>
+#include <cmath>
+#include <cstdlib>
#include <new>
@@ -211,20 +211,17 @@ avhuff_error avhuff_encoder::encode_data(const uint8_t *source, uint8_t *dest, u
// extract info from the header
uint32_t metasize = source[4];
uint32_t channels = source[5];
- uint32_t samples = (source[6] << 8) + source[7];
- uint32_t width = (source[8] << 8) + source[9];
- uint32_t height = (source[10] << 8) + source[11];
+ uint32_t samples = get_u16be(&source[6]);
+ uint32_t width = get_u16be(&source[8]);
+ uint32_t height = get_u16be(&source[10]);
source += 12;
// write the basics to the new header
dest[0] = metasize;
dest[1] = channels;
- dest[2] = samples >> 8;
- dest[3] = samples;
- dest[4] = width >> 8;
- dest[5] = width;
- dest[6] = height >> 8;
- dest[7] = height;
+ put_u16be(&dest[2], samples);
+ put_u16be(&dest[4], width);
+ put_u16be(&dest[6], height);
// starting offsets
uint32_t dstoffs = 10 + 2 * channels;
@@ -247,11 +244,11 @@ avhuff_error avhuff_encoder::encode_data(const uint8_t *source, uint8_t *dest, u
return err;
// advance the pointers past the data
- uint16_t treesize = (dest[8] << 8) + dest[9];
+ uint16_t treesize = get_u16be(&dest[8]);
if (treesize != 0xffff)
dstoffs += treesize;
for (int chnum = 0; chnum < channels; chnum++)
- dstoffs += (dest[10 + 2 * chnum] << 8) + dest[11 + 2 * chnum];
+ dstoffs += get_u16be(&dest[10 + 2 * chnum]);
}
else
{
@@ -299,10 +296,10 @@ uint32_t avhuff_encoder::raw_data_size(const uint8_t *data)
size = 12 + data[4];
// add in channels * samples
- size += 2 * data[5] * ((data[6] << 8) + data[7]);
+ size += 2 * data[5] * get_u16be(&data[6]);
// add in 2 * width * height
- size += 2 * ((data[8] << 8) + data[9]) * (((data[10] << 8) + data[11]) & 0x7fff);
+ size += 2 * get_u16be(&data[8]) * (get_u16be(&data[10]) & 0x7fff);
}
return size;
}
@@ -344,12 +341,12 @@ avhuff_error avhuff_encoder::assemble_data(std::vector<uint8_t> &buffer, bitmap_
*dest++ = 'v';
*dest++ = metadatasize;
*dest++ = channels;
- *dest++ = numsamples >> 8;
- *dest++ = numsamples & 0xff;
- *dest++ = bitmap.width() >> 8;
- *dest++ = bitmap.width() & 0xff;
- *dest++ = bitmap.height() >> 8;
- *dest++ = bitmap.height() & 0xff;
+ put_u16be(dest, numsamples);
+ dest += 2;
+ put_u16be(dest, bitmap.width());
+ dest += 2;
+ put_u16be(dest, bitmap.height());
+ dest += 2;
// copy the metadata
if (metadatasize > 0)
@@ -360,8 +357,8 @@ avhuff_error avhuff_encoder::assemble_data(std::vector<uint8_t> &buffer, bitmap_
for (uint8_t curchan = 0; curchan < channels; curchan++)
for (uint32_t cursamp = 0; cursamp < numsamples; cursamp++)
{
- *dest++ = samples[curchan][cursamp] >> 8;
- *dest++ = samples[curchan][cursamp] & 0xff;
+ put_u16be(dest, samples[curchan][cursamp]);
+ dest += 2;
}
// copy the video data
@@ -370,8 +367,8 @@ avhuff_error avhuff_encoder::assemble_data(std::vector<uint8_t> &buffer, bitmap_
uint16_t *src = &bitmap.pix(y);
for (int32_t x = 0; x < bitmap.width(); x++)
{
- *dest++ = src[x] >> 8;
- *dest++ = src[x] & 0xff;
+ put_u16be(dest, src[x]);
+ dest += 2;
}
}
return AVHERR_NONE;
@@ -417,8 +414,7 @@ avhuff_error avhuff_encoder::encode_audio(const uint8_t *source, int channels, i
// set the size for this channel
uint32_t cursize = m_flac_encoder.finish();
- sizes[chnum * 2 + 2] = cursize >> 8;
- sizes[chnum * 2 + 3] = cursize;
+ put_u16be(&sizes[chnum * 2 + 2], cursize);
dest += cursize;
}
@@ -437,7 +433,7 @@ avhuff_error avhuff_encoder::encode_audio(const uint8_t *source, int channels, i
int16_t prevsample = 0;
for (int sampnum = 0; sampnum < samples; sampnum++)
{
- int16_t newsample = (source[0] << 8) | source[1];
+ int16_t newsample = get_s16be(source);
source += 2;
int16_t delta = newsample - prevsample;
@@ -467,8 +463,7 @@ avhuff_error avhuff_encoder::encode_audio(const uint8_t *source, int channels, i
// note the size of the two trees
uint32_t huffsize = bitbuf.flush();
- sizes[0] = huffsize >> 8;
- sizes[1] = huffsize;
+ put_u16be(&sizes[0], huffsize);
// iterate over channels
uint32_t totalsize = huffsize;
@@ -488,8 +483,7 @@ avhuff_error avhuff_encoder::encode_audio(const uint8_t *source, int channels, i
totalsize += cursize;
if (totalsize >= channels * samples * 2)
break;
- sizes[chnum * 2 + 2] = cursize >> 8;
- sizes[chnum * 2 + 3] = cursize;
+ put_u16be(&sizes[chnum * 2 + 2], cursize);
}
// if we ran out of room, throw it all away and just store raw
@@ -499,10 +493,7 @@ avhuff_error avhuff_encoder::encode_audio(const uint8_t *source, int channels, i
uint32_t size = samples * 2;
sizes[0] = sizes[1] = 0;
for (chnum = 0; chnum < channels; chnum++)
- {
- sizes[chnum * 2 + 2] = size >> 8;
- sizes[chnum * 2 + 3] = size;
- }
+ put_u16be(&sizes[chnum * 2 + 2], size);
}
#endif
@@ -691,24 +682,19 @@ avhuff_decoder::avhuff_decoder()
}
/**
- * @fn void avhuff_decoder::configure(const avhuff_decompress_config &config)
+ * @fn void avhuff_decoder::configure(const config &cfg)
*
* @brief -------------------------------------------------
* configure - configure decompression parameters
* -------------------------------------------------.
*
- * @param config The configuration.
+ * @param cfg The configuration.
*/
-void avhuff_decoder::configure(const avhuff_decompress_config &config)
+void avhuff_decoder::configure(const config &cfg)
{
- m_config.video.wrap(config.video, config.video.cliprect());
- m_config.maxsamples = config.maxsamples;
- m_config.actsamples = config.actsamples;
- memcpy(m_config.audio, config.audio, sizeof(m_config.audio));
- m_config.maxmetalength = config.maxmetalength;
- m_config.actmetalength = config.actmetalength;
- m_config.metadata = config.metadata;
+ m_video.wrap(*cfg.video, cfg.video->cliprect());
+ m_config = cfg;
}
/**
@@ -732,19 +718,19 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
return AVHERR_INVALID_DATA;
uint32_t metasize = source[0];
uint32_t channels = source[1];
- uint32_t samples = (source[2] << 8) + source[3];
- uint32_t width = (source[4] << 8) + source[5];
- uint32_t height = (source[6] << 8) + source[7];
+ uint32_t samples = get_u16be(&source[2]);
+ uint32_t width = get_u16be(&source[4]);
+ uint32_t height = get_u16be(&source[6]);
// validate that the sizes make sense
if (complength < 10 + 2 * channels)
return AVHERR_INVALID_DATA;
uint32_t totalsize = 10 + 2 * channels;
- uint32_t treesize = (source[8] << 8) | source[9];
+ uint32_t treesize = get_u16be(&source[8]);
if (treesize != 0xffff)
totalsize += treesize;
for (int chnum = 0; chnum < channels; chnum++)
- totalsize += (source[10 + 2 * chnum] << 8) | source[11 + 2 * chnum];
+ totalsize += get_u16be(&source[10 + 2 * chnum]);
if (totalsize >= complength)
return AVHERR_INVALID_DATA;
@@ -763,12 +749,9 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
dest[3] = 'v';
dest[4] = metasize;
dest[5] = channels;
- dest[6] = samples >> 8;
- dest[7] = samples;
- dest[8] = width >> 8;
- dest[9] = width;
- dest[10] = height >> 8;
- dest[11] = height;
+ put_u16be(&dest[6], samples);
+ put_u16be(&dest[8], width);
+ put_u16be(&dest[10], height);
dest += 12;
// determine the start of each piece of data
@@ -792,9 +775,9 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
// determine the start of each piece of data
metastart = m_config.metadata;
for (int chnum = 0; chnum < channels; chnum++)
- audiostart[chnum] = (uint8_t *)m_config.audio[chnum];
- videostart = (m_config.video.valid()) ? reinterpret_cast<uint8_t *>(&m_config.video.pix(0)) : nullptr;
- videostride = (m_config.video.valid()) ? m_config.video.rowpixels() * 2 : 0;
+ audiostart[chnum] = reinterpret_cast<uint8_t *>(m_config.audio[chnum]);
+ videostart = m_video.valid() ? reinterpret_cast<uint8_t *>(&m_video.pix(0)) : nullptr;
+ videostride = m_video.valid() ? m_video.rowpixels() * 2 : 0;
// data is assumed to be native-endian
uint16_t betest = 0;
@@ -802,7 +785,7 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
audioxor = videoxor = (betest == 1) ? 1 : 0;
// verify against sizes
- if (m_config.video.valid() && (m_config.video.width() < width || m_config.video.height() < height))
+ if (m_video.valid() && (m_video.width() < width || m_video.height() < height))
return AVHERR_VIDEO_TOO_LARGE;
for (int chnum = 0; chnum < channels; chnum++)
if (m_config.audio[chnum] != nullptr && m_config.maxsamples < samples)
@@ -834,11 +817,11 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
return err;
// advance the pointers past the data
- treesize = (source[8] << 8) + source[9];
+ treesize = get_u16be(&source[8]);
if (treesize != 0xffff)
srcoffs += treesize;
for (int chnum = 0; chnum < channels; chnum++)
- srcoffs += (source[10 + 2 * chnum] << 8) + source[11 + 2 * chnum];
+ srcoffs += get_u16be(&source[10 + 2 * chnum]);
}
// decode the video data
@@ -875,7 +858,7 @@ avhuff_error avhuff_decoder::decode_data(const uint8_t *source, uint32_t complen
avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8_t *source, uint8_t **dest, uint32_t dxor, const uint8_t *sizes)
{
// extract the huffman trees
- uint16_t treesize = (sizes[0] << 8) | sizes[1];
+ uint16_t treesize = get_u16be(&sizes[0]);
#if AVHUFF_USE_FLAC
@@ -893,7 +876,7 @@ avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8
for (int chnum = 0; chnum < channels; chnum++)
{
// extract the size of this channel
- uint16_t size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
+ uint16_t size = get_u16be(&sizes[chnum * 2 + 2]);
// only process if the data is requested
uint8_t *curdest = dest[chnum];
@@ -901,9 +884,9 @@ avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8
{
// reset and decode
if (!m_flac_decoder.reset(48000, 1, samples, source, size))
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
if (!m_flac_decoder.decode_interleaved(reinterpret_cast<int16_t *>(curdest), samples, swap_endian))
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// finish up
m_flac_decoder.finish();
@@ -937,7 +920,7 @@ avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8
for (int chnum = 0; chnum < channels; chnum++)
{
// extract the size of this channel
- uint16_t size = (sizes[chnum * 2 + 2] << 8) | sizes[chnum * 2 + 3];
+ uint16_t size = get_u16be(&sizes[chnum * 2 + 2]);
// only process if the data is requested
uint8_t *curdest = dest[chnum];
@@ -951,7 +934,7 @@ avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8
const uint8_t *cursource = source;
for (int sampnum = 0; sampnum < samples; sampnum++)
{
- int16_t delta = (cursource[0] << 8) | cursource[1];
+ int16_t delta = get_s16be(cursource);
cursource += 2;
int16_t newsample = prevsample + delta;
diff --git a/src/lib/util/avhuff.h b/src/lib/util/avhuff.h
index 90ef43e977f..1462bad3c77 100644
--- a/src/lib/util/avhuff.h
+++ b/src/lib/util/avhuff.h
@@ -13,12 +13,12 @@
#pragma once
-#include "osdcore.h"
-#include "coretmpl.h"
#include "bitmap.h"
#include "huffman.h"
#include "flac.h"
+#include <algorithm>
+
//**************************************************************************
// CONSTANTS
@@ -49,32 +49,6 @@ enum avhuff_error
// TYPE DEFINITIONS
//**************************************************************************
-// ======================> av_codec_decompress_config
-
-// decompression configuration
-class avhuff_decompress_config
-{
-public:
- avhuff_decompress_config()
- : maxsamples(0),
- actsamples(nullptr),
- maxmetalength(0),
- actmetalength(nullptr),
- metadata(nullptr)
- {
- memset(audio, 0, sizeof(audio));
- }
-
- bitmap_yuy16 video; // pointer to video bitmap
- uint32_t maxsamples; // maximum number of samples per channel
- uint32_t * actsamples; // actual number of samples per channel
- int16_t * audio[16]; // pointer to individual audio channels
- uint32_t maxmetalength; // maximum length of metadata
- uint32_t * actmetalength; // actual length of metadata
- uint8_t * metadata; // pointer to metadata buffer
-};
-
-
// ======================> avhuff_encoder
// core state for the codec
@@ -98,8 +72,7 @@ private:
{
public:
// construction/destruction
- deltarle_encoder()
- : m_rlecount(0) { }
+ deltarle_encoder() { }
// histogramming
uint16_t *rle_and_histo_bitmap(const uint8_t *source, uint32_t items_per_row, uint32_t item_advance, uint32_t row_count);
@@ -111,7 +84,7 @@ private:
private:
// internal state
- int m_rlecount;
+ int m_rlecount = 0;
huffman_encoder<256 + 16> m_encoder;
std::vector<uint16_t> m_rlebuffer;
};
@@ -143,11 +116,30 @@ private:
class avhuff_decoder
{
public:
+ // decompression configuration
+ class config
+ {
+ public:
+ config()
+ {
+ std::fill(std::begin(audio), std::end(audio), nullptr);
+ }
+
+ bitmap_yuy16 * video = nullptr; // pointer to video bitmap
+ uint32_t maxsamples = 0; // maximum number of samples per channel
+ uint32_t * actsamples = nullptr; // actual number of samples per channel
+ int16_t * audio[16]; // pointer to individual audio channels
+ uint32_t maxmetalength = 0; // maximum length of metadata
+ uint32_t * actmetalength = nullptr; // actual length of metadata
+ uint8_t * metadata = nullptr; // pointer to metadata buffer
+ };
+
+
// construction/destruction
avhuff_decoder();
// configuration
- void configure(const avhuff_decompress_config &config);
+ void configure(const config &cfg);
// encode/decode
avhuff_error decode_data(const uint8_t *source, uint32_t complength, uint8_t *dest);
@@ -158,8 +150,7 @@ private:
{
public:
// construction/destruction
- deltarle_decoder()
- : m_rlecount(0), m_prevdata(0) { }
+ deltarle_decoder() { }
// general
void reset() { m_rlecount = m_prevdata = 0; }
@@ -171,8 +162,8 @@ private:
private:
// internal state
- int m_rlecount;
- uint8_t m_prevdata;
+ int m_rlecount = 0;
+ uint8_t m_prevdata = 0;
huffman_decoder<256 + 16> m_decoder;
};
@@ -183,7 +174,8 @@ private:
avhuff_error decode_video_lossless(int width, int height, const uint8_t *source, uint32_t complength, uint8_t *dest, uint32_t dstride, uint32_t dxor);
// internal state
- avhuff_decompress_config m_config;
+ bitmap_yuy16 m_video;
+ config m_config;
// video decoding contexts
deltarle_decoder m_ycontext;
diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp
index bd93cbe1847..7b5e8d00d87 100644
--- a/src/lib/util/aviio.cpp
+++ b/src/lib/util/aviio.cpp
@@ -8,228 +8,63 @@
***************************************************************************/
+#include "aviio.h"
+
+#include "multibyte.h"
+#include "strformat.h"
+
+#include "osdcomm.h"
+#include "osdfile.h"
+
+#include <algorithm>
#include <array>
#include <cassert>
#include <cstdlib>
#include <cstring>
-
-#include "aviio.h"
+#include <iostream>
+#include <utility>
/***************************************************************************
CONSTANTS
***************************************************************************/
-/**
- * @def FILETYPE_READ
- *
- * @brief A macro that defines filetype read.
- */
-
-#define FILETYPE_READ 1
-
-/**
- * @def FILETYPE_CREATE
- *
- * @brief A macro that defines filetype create.
- */
-
-#define FILETYPE_CREATE 2
-
/** @brief Size of the maximum riff. */
-#define MAX_RIFF_SIZE (2UL * 1024 * 1024 * 1024 - 1024) /* just under 2GB */
+static constexpr std::uint64_t MAX_RIFF_SIZE = 2UL * 1024 * 1024 * 1024 - 1024; // just under 2GB
/** @brief The maximum avi size in gigabytes. */
-#define MAX_AVI_SIZE_IN_GB (256)
+static constexpr unsigned MAX_AVI_SIZE_IN_GB = 1024;
/**
* @def FOUR_GB
*
- * @brief A macro that defines four gigabytes.
+ * @brief A constant that defines four gigabytes.
*/
-#define FOUR_GB (std::uint64_t(1) << 32)
+static constexpr std::uint64_t FOUR_GB = std::uint64_t(1) << 32;
/**
* @def MAX_SOUND_CHANNELS
*
- * @brief A macro that defines maximum sound channels.
+ * @brief A constant that defines maximum sound channels.
*/
-#define MAX_SOUND_CHANNELS 2
+static constexpr unsigned MAX_SOUND_CHANNELS = 16;
/**
* @def SOUND_BUFFER_MSEC
*
- * @brief A macro that defines sound buffer msec.
- */
-
-#define SOUND_BUFFER_MSEC 2000 /* milliseconds of sound buffering */
-
-/** @brief The chunktype riff. */
-#define CHUNKTYPE_RIFF AVI_FOURCC('R','I','F','F')
-/** @brief List of chunktypes. */
-#define CHUNKTYPE_LIST AVI_FOURCC('L','I','S','T')
-/** @brief The chunktype junk. */
-#define CHUNKTYPE_JUNK AVI_FOURCC('J','U','N','K')
-/** @brief The chunktype avih. */
-#define CHUNKTYPE_AVIH AVI_FOURCC('a','v','i','h')
-/** @brief The chunktype strh. */
-#define CHUNKTYPE_STRH AVI_FOURCC('s','t','r','h')
-/** @brief The chunktype strf. */
-#define CHUNKTYPE_STRF AVI_FOURCC('s','t','r','f')
-/** @brief The first chunktype index. */
-#define CHUNKTYPE_IDX1 AVI_FOURCC('i','d','x','1')
-/** @brief The chunktype indx. */
-#define CHUNKTYPE_INDX AVI_FOURCC('i','n','d','x')
-/** @brief The chunktype xxdb. */
-#define CHUNKTYPE_XXDB AVI_FOURCC(0x00,0x00,'d','b')
-/** @brief The chunktype xxdc. */
-#define CHUNKTYPE_XXDC AVI_FOURCC(0x00,0x00,'d','c')
-/** @brief The chunktype xxwb. */
-#define CHUNKTYPE_XXWB AVI_FOURCC(0x00,0x00,'w','b')
-/** @brief The chunktype ixxx. */
-#define CHUNKTYPE_IXXX AVI_FOURCC('i','x',0x00,0x00)
-/** @brief The chunktype xx mask. */
-#define CHUNKTYPE_XX_MASK AVI_FOURCC(0x00,0x00,0xff,0xff)
-
-/** @brief The listtype avi. */
-#define LISTTYPE_AVI AVI_FOURCC('A','V','I',' ')
-/** @brief The listtype avix. */
-#define LISTTYPE_AVIX AVI_FOURCC('A','V','I','X')
-/** @brief The listtype hdrl. */
-#define LISTTYPE_HDRL AVI_FOURCC('h','d','r','l')
-/** @brief The listtype strl. */
-#define LISTTYPE_STRL AVI_FOURCC('s','t','r','l')
-/** @brief The listtype movi. */
-#define LISTTYPE_MOVI AVI_FOURCC('m','o','v','i')
-
-/** @brief The streamtype vids. */
-#define STREAMTYPE_VIDS AVI_FOURCC('v','i','d','s')
-/** @brief The streamtype auds. */
-#define STREAMTYPE_AUDS AVI_FOURCC('a','u','d','s')
-
-/** @brief The handler bitmap. */
-#define HANDLER_DIB AVI_FOURCC('D','I','B',' ')
-/** @brief The handler hfyu. */
-#define HANDLER_HFYU AVI_FOURCC('h','f','y','u')
-
-/* main AVI header files */
-
-/**
- * @def AVIF_HASINDEX
- *
- * @brief A macro that defines avif hasindex.
- */
-
-#define AVIF_HASINDEX 0x00000010
-
-/**
- * @def AVIF_MUSTUSEINDEX
- *
- * @brief A macro that defines avif mustuseindex.
- */
-
-#define AVIF_MUSTUSEINDEX 0x00000020
-
-/**
- * @def AVIF_ISINTERLEAVED
- *
- * @brief A macro that defines avif isinterleaved.
- */
-
-#define AVIF_ISINTERLEAVED 0x00000100
-
-/**
- * @def AVIF_COPYRIGHTED
- *
- * @brief A macro that defines avif copyrighted.
+ * @brief A constant that defines sound buffer msec.
*/
-#define AVIF_COPYRIGHTED 0x00010000
+static constexpr int SOUND_BUFFER_MSEC = 2000; // milliseconds of sound buffering
/**
- * @def AVIF_WASCAPTUREFILE
+ * @def AVI_INTEGRAL_MULTIPLE
*
- * @brief A macro that defines avif wascapturefile.
+ * @brief Ensures the integral multiple of the video dimension, because most video players are not capable to playback a video stream with a lower multiple.
*/
-#define AVIF_WASCAPTUREFILE 0x00020000
-
-/* index definitions */
-
-/**
- * @def AVI_INDEX_OF_INDEXES
- *
- * @brief A macro that defines avi index of indexes.
- */
-
-#define AVI_INDEX_OF_INDEXES 0x00
-
-/**
- * @def AVI_INDEX_OF_CHUNKS
- *
- * @brief A macro that defines avi index of chunks.
- */
-
-#define AVI_INDEX_OF_CHUNKS 0x01
-
-/**
- * @def AVI_INDEX_IS_DATA
- *
- * @brief A macro that defines avi index is data.
- */
-
-#define AVI_INDEX_IS_DATA 0x80
-
-/**
- * @def AVI_INDEX_2FIELD
- *
- * @brief A macro that defines avi index 2 field.
- */
-
-#define AVI_INDEX_2FIELD 0x01
-
- /**
- * @def AVI_INTEGRAL_MULTIPLE
- *
- * @brief Ensures the integral multiple of the video dimension, because most video players are not capable to playback a video stream with a lower multiple.
- */
-
-#define AVI_INTEGRAL_MULTIPLE 4
-
-/* HuffYUV definitions */
-
-/**
- * @def HUFFYUV_PREDICT_LEFT
- *
- * @brief A macro that defines huffyuv predict left.
- */
-
-#define HUFFYUV_PREDICT_LEFT 0
-
-/**
- * @def HUFFYUV_PREDICT_GRADIENT
- *
- * @brief A macro that defines huffyuv predict gradient.
- */
-
-#define HUFFYUV_PREDICT_GRADIENT 1
-
-/**
- * @def HUFFYUV_PREDICT_MEDIAN
- *
- * @brief A macro that defines huffyuv predict median.
- */
-
-#define HUFFYUV_PREDICT_MEDIAN 2
-
-/**
- * @def HUFFYUV_PREDICT_DECORR
- *
- * @brief A macro that defines huffyuv predict decorr.
- */
-
-#define HUFFYUV_PREDICT_DECORR 0x40
+static constexpr int AVI_INTEGRAL_MULTIPLE = 4;
namespace {
@@ -247,10 +82,10 @@ struct avi_chunk
{
avi_chunk() : offset(0), size(0), type(0), listtype(0) { }
- std::uint64_t offset; /* file offset of chunk header */
- std::uint64_t size; /* size of this chunk */
- std::uint32_t type; /* type of this chunk */
- std::uint32_t listtype; /* type of this list (if we are a list) */
+ std::uint64_t offset; // file offset of chunk header
+ std::uint64_t size; // size of this chunk
+ std::uint32_t type; // type of this chunk
+ std::uint32_t listtype; // type of this list (if we are a list)
};
/**
@@ -261,8 +96,8 @@ struct avi_chunk
struct avi_chunk_list
{
- std::uint64_t offset; /* offset in the file of header */
- std::uint32_t length; /* length of the chunk including header */
+ std::uint64_t offset; // offset in the file of header
+ std::uint32_t length; // length of the chunk including header
};
/**
@@ -274,6 +109,12 @@ struct avi_chunk_list
class avi_stream
{
public:
+ enum : std::uint32_t
+ {
+ STREAMTYPE_VIDS = AVI_FOURCC('v','i','d','s'),
+ STREAMTYPE_AUDS = AVI_FOURCC('a','u','d','s')
+ };
+
avi_stream()
: m_type(0)
, m_format(0)
@@ -298,7 +139,7 @@ public:
std::uint32_t width = info.video_width;
std::uint32_t height = info.video_height;
- std::uint32_t integal_multiple = std::uint32_t(AVI_INTEGRAL_MULTIPLE);
+ auto integal_multiple = std::uint32_t(AVI_INTEGRAL_MULTIPLE);
if (integal_multiple > 1)
{
width = width - (width % integal_multiple);
@@ -370,44 +211,52 @@ public:
avi_file::error uncompressed_yuv420p_to_argb32(const std::uint8_t *data, std::uint32_t numbytes, bitmap_argb32 &bitmap) const;
private:
+ enum : std::uint8_t
+ {
+ HUFFYUV_PREDICT_LEFT = 0,
+ HUFFYUV_PREDICT_GRADIENT = 1,
+ HUFFYUV_PREDICT_MEDIAN = 2,
+ HUFFYUV_PREDICT_DECORR = 0x40
+ };
+
struct huffyuv_table
{
- std::uint8_t shift[256]; /* bit shift amounts */
- std::uint32_t bits[256]; /* bit match values */
- std::uint32_t mask[256]; /* bit mask values */
- std::uint16_t baselookup[65536]; /* base lookup table */
- std::vector<std::uint16_t> extralookup; /* extra lookup tables */
+ std::uint8_t shift[256]; // bit shift amounts
+ std::uint32_t bits[256]; // bit match values
+ std::uint32_t mask[256]; // bit mask values
+ std::uint16_t baselookup[65536]; // base lookup table
+ std::vector<std::uint16_t> extralookup; // extra lookup tables
};
struct huffyuv_data
{
- std::uint8_t predictor; /* predictor */
- huffyuv_table table[3]; /* array of tables */
+ std::uint8_t predictor; // predictor
+ huffyuv_table table[3]; // array of tables
};
avi_file::error huffyuv_extract_tables(const std::uint8_t *chunkdata, std::uint32_t size);
- std::uint32_t m_type; /* subtype of stream */
- std::uint32_t m_format; /* format of stream data */
+ std::uint32_t m_type; // subtype of stream
+ std::uint32_t m_format; // format of stream data
- std::uint32_t m_rate; /* timescale for stream */
- std::uint32_t m_scale; /* duration of one sample in the stream */
- std::uint32_t m_samples; /* number of samples */
+ std::uint32_t m_rate; // timescale for stream
+ std::uint32_t m_scale; // duration of one sample in the stream
+ std::uint32_t m_samples; // number of samples
- std::vector<avi_chunk_list> m_chunks; /* list of chunks */
+ std::vector<avi_chunk_list> m_chunks; // list of chunks
- std::uint32_t m_width; /* width of video */
- std::uint32_t m_height; /* height of video */
- std::uint32_t m_depth; /* depth of video */
- std::unique_ptr<huffyuv_data const> m_huffyuv; /* huffyuv decompression data */
+ std::uint32_t m_width; // width of video
+ std::uint32_t m_height; // height of video
+ std::uint32_t m_depth; // depth of video
+ std::unique_ptr<huffyuv_data const> m_huffyuv; // huffyuv decompression data
- std::uint16_t m_channels; /* audio channels */
- std::uint16_t m_samplebits; /* audio bits per sample */
- std::uint32_t m_samplerate; /* audio sample rate */
+ std::uint16_t m_channels; // audio channels
+ std::uint16_t m_samplebits; // audio bits per sample
+ std::uint32_t m_samplerate; // audio sample rate
- /* only used when creating */
- std::uint64_t m_saved_strh_offset; /* writeoffset of strh chunk */
- std::uint64_t m_saved_indx_offset; /* writeoffset of indx chunk */
+ // only used when creating
+ std::uint64_t m_saved_strh_offset; // writeoffset of strh chunk
+ std::uint64_t m_saved_indx_offset; // writeoffset of indx chunk
};
/**
@@ -418,6 +267,8 @@ private:
class avi_file_impl : public avi_file
{
+ friend class avi_stream;
+
public:
avi_file_impl(osd_file::ptr &&file, std::uint64_t length) : avi_file_impl()
{
@@ -455,7 +306,7 @@ public:
virtual ~avi_file_impl() override;
- virtual void printf_chunks() override;
+ virtual void display_chunks() override;
virtual movie_info const &get_movie_info() const override;
virtual std::uint32_t first_sample_in_frame(std::uint32_t framenum) const override;
@@ -473,6 +324,61 @@ public:
error soundbuf_initialize();
private:
+ enum : std::uint32_t
+ {
+ FILETYPE_READ = 1,
+ FILETYPE_CREATE = 2
+ };
+
+ enum : std::uint32_t
+ {
+ CHUNKTYPE_RIFF = AVI_FOURCC('R','I','F','F'),
+ CHUNKTYPE_LIST = AVI_FOURCC('L','I','S','T'),
+ CHUNKTYPE_JUNK = AVI_FOURCC('J','U','N','K'),
+ CHUNKTYPE_AVIH = AVI_FOURCC('a','v','i','h'),
+ CHUNKTYPE_STRH = AVI_FOURCC('s','t','r','h'),
+ CHUNKTYPE_STRF = AVI_FOURCC('s','t','r','f'),
+ CHUNKTYPE_IDX1 = AVI_FOURCC('i','d','x','1'),
+ CHUNKTYPE_INDX = AVI_FOURCC('i','n','d','x'),
+ CHUNKTYPE_XXDB = AVI_FOURCC(0x00,0x00,'d','b'),
+ CHUNKTYPE_XXDC = AVI_FOURCC(0x00,0x00,'d','c'),
+ CHUNKTYPE_XXWB = AVI_FOURCC(0x00,0x00,'w','b'),
+ CHUNKTYPE_IXXX = AVI_FOURCC('i','x',0x00,0x00),
+ CHUNKTYPE_XX_MASK = 0xffff0000
+ };
+
+ enum : std::uint32_t
+ {
+ LISTTYPE_AVI = AVI_FOURCC('A','V','I',' '),
+ LISTTYPE_AVIX = AVI_FOURCC('A','V','I','X'),
+ LISTTYPE_HDRL = AVI_FOURCC('h','d','r','l'),
+ LISTTYPE_STRL = AVI_FOURCC('s','t','r','l'),
+ LISTTYPE_MOVI = AVI_FOURCC('m','o','v','i')
+ };
+
+ enum : std::uint32_t
+ {
+ HANDLER_DIB = AVI_FOURCC('D','I','B',' '),
+ HANDLER_HFYU = AVI_FOURCC('h','f','y','u')
+ };
+
+ enum : std::uint32_t
+ {
+ AVIF_HASINDEX = 0x00000010,
+ AVIF_MUSTUSEINDEX = 0x00000020,
+ AVIF_ISINTERLEAVED = 0x00000100,
+ AVIF_COPYRIGHTED = 0x00010000,
+ AVIF_WASCAPTUREFILE = 0x00020000
+ };
+
+ enum : std::uint8_t
+ {
+ AVI_INDEX_OF_INDEXES = 0x00,
+ AVI_INDEX_OF_CHUNKS = 0x01,
+ AVI_INDEX_IS_DATA = 0x80,
+ AVI_INDEX_2FIELD = 0x01
+ };
+
avi_file_impl()
: m_file()
, m_type(0)
@@ -537,33 +443,33 @@ private:
error soundbuf_flush(bool only_flush_full);
// debugging
- void printf_chunk_recursive(avi_chunk const *chunk, int indent);
+ void display_chunk_recursive(avi_chunk const *chunk, int indent);
- /* shared data */
- osd_file::ptr m_file; /* pointer to open file */
- int m_type; /* type of access (read/create) */
- movie_info m_info; /* movie info structure */
- std::vector<std::uint8_t> m_tempbuffer; /* temporary buffer */
+ // shared data
+ osd_file::ptr m_file; // pointer to open file
+ int m_type; // type of access (read/create)
+ movie_info m_info; // movie info structure
+ std::vector<std::uint8_t> m_tempbuffer; // temporary buffer
- /* only used when reading */
- std::vector<avi_stream> m_streams; /* allocated array of stream information */
- avi_chunk m_rootchunk; /* dummy root chunk that wraps the whole file */
+ // only used when reading
+ std::vector<avi_stream> m_streams; // allocated array of stream information
+ avi_chunk m_rootchunk; // dummy root chunk that wraps the whole file
- /* only used when creating */
- std::uint64_t m_writeoffs; /* current file write offset */
- std::uint64_t m_riffbase; /* base of the current RIFF */
+ // only used when creating
+ std::uint64_t m_writeoffs; // current file write offset
+ std::uint64_t m_riffbase; // base of the current RIFF
- std::array<avi_chunk, 8> m_chunkstack; /* stack of chunks we are writing */
- int m_chunksp; /* stack pointer for the current chunk */
+ std::array<avi_chunk, 8> m_chunkstack; // stack of chunks we are writing
+ int m_chunksp; // stack pointer for the current chunk
- std::uint64_t m_saved_movi_offset; /* writeoffset of movi list */
- std::uint64_t m_saved_avih_offset; /* writeoffset of avih chunk */
+ std::uint64_t m_saved_movi_offset; // writeoffset of movi list
+ std::uint64_t m_saved_avih_offset; // writeoffset of avih chunk
- std::vector<std::int16_t> m_soundbuf; /* buffer for sound data */
- std::uint32_t m_soundbuf_samples; /* length of sound buffer in samples */
- std::uint32_t m_soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */
- std::uint32_t m_soundbuf_chunks; /* number of chunks completed so far */
- std::uint32_t m_soundbuf_frames; /* number of frames ahead of the video */
+ std::vector<std::int16_t> m_soundbuf; // buffer for sound data
+ std::uint32_t m_soundbuf_samples; // length of sound buffer in samples
+ std::uint32_t m_soundbuf_chansamples[MAX_SOUND_CHANNELS]; // samples in buffer for each channel
+ std::uint32_t m_soundbuf_chunks; // number of chunks completed so far
+ std::uint32_t m_soundbuf_frames; // number of frames ahead of the video
};
@@ -573,163 +479,6 @@ private:
***************************************************************************/
/*-------------------------------------------------
- fetch_16bits - read 16 bits in LSB order
- from the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline std::uint16_t fetch_16bits(const std::uint8_t *data)
- *
- * @brief Fetches the 16bits.
- *
- * @param data The data.
- *
- * @return The 16bits.
- */
-
-inline std::uint16_t fetch_16bits(const std::uint8_t *data)
-{
- return data[0] | (data[1] << 8);
-}
-
-
-/*-------------------------------------------------
- fetch_32bits - read 32 bits in LSB order
- from the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline std::uint32_t fetch_32bits(const std::uint8_t *data)
- *
- * @brief Fetches the 32bits.
- *
- * @param data The data.
- *
- * @return The 32bits.
- */
-
-inline std::uint32_t fetch_32bits(const std::uint8_t *data)
-{
- return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
-}
-
-
-/*-------------------------------------------------
- fetch_64bits - read 64 bits in LSB order
- from the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline std::uint64_t fetch_64bits(const std::uint8_t *data)
- *
- * @brief Fetches the 64bits.
- *
- * @param data The data.
- *
- * @return The 64bits.
- */
-
-inline std::uint64_t fetch_64bits(const std::uint8_t *data)
-{
- return std::uint64_t(data[0]) | (std::uint64_t(data[1]) << 8) |
- (std::uint64_t(data[2]) << 16) | (std::uint64_t(data[3]) << 24) |
- (std::uint64_t(data[4]) << 32) | (std::uint64_t(data[5]) << 40) |
- (std::uint64_t(data[6]) << 48) | (std::uint64_t(data[7]) << 56);
-}
-
-
-/*-------------------------------------------------
- put_16bits - write 16 bits in LSB order
- to the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline void put_16bits(std::uint8_t *data, std::uint16_t value)
- *
- * @brief Puts the 16bits.
- *
- * @param [in,out] data If non-null, the data.
- * @param value The value.
- */
-
-inline void put_16bits(std::uint8_t *data, std::uint16_t value)
-{
- data[0] = value >> 0;
- data[1] = value >> 8;
-}
-
-
-/*-------------------------------------------------
- put_32bits - write 32 bits in LSB order
- to the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline void put_32bits(std::uint8_t *data, std::uint32_t value)
- *
- * @brief Puts the 32bits.
- *
- * @param [in,out] data If non-null, the data.
- * @param value The value.
- */
-
-inline void put_32bits(std::uint8_t *data, std::uint32_t value)
-{
- data[0] = value >> 0;
- data[1] = value >> 8;
- data[2] = value >> 16;
- data[3] = value >> 24;
-}
-
-
-/*-------------------------------------------------
- put_64bits - write 64 bits in LSB order
- to the given buffer
--------------------------------------------------*/
-
-/**
- * @fn static inline void put_64bits(std::uint8_t *data, std::uint64_t value)
- *
- * @brief Puts the 64bits.
- *
- * @param [in,out] data If non-null, the data.
- * @param value The value.
- */
-
-inline void put_64bits(std::uint8_t *data, std::uint64_t value)
-{
- data[0] = value >> 0;
- data[1] = value >> 8;
- data[2] = value >> 16;
- data[3] = value >> 24;
- data[4] = value >> 32;
- data[5] = value >> 40;
- data[6] = value >> 48;
- data[7] = value >> 56;
-}
-
-
-/**
- * @fn static void u64toa(std::uint64_t val, char *output)
- *
- * @brief 64toas.
- *
- * @param val The value.
- * @param [in,out] output If non-null, the output.
- */
-
-inline void u64toa(std::uint64_t val, char *output)
-{
- std::uint32_t lo = std::uint32_t(val & 0xffffffff);
- std::uint32_t hi = std::uint32_t(val >> 32);
- if (hi != 0)
- sprintf(output, "%X%08X", hi, lo);
- else
- sprintf(output, "%X", lo);
-}
-
-
-/*-------------------------------------------------
set_stream_chunk_info - set the chunk info
for a given chunk within a stream
-------------------------------------------------*/
@@ -749,17 +498,17 @@ inline void u64toa(std::uint64_t val, char *output)
inline avi_file::error avi_stream::set_chunk_info(std::uint32_t index, std::uint64_t offset, std::uint32_t length)
{
- /* if we need to allocate more, allocate more */
+ // if we need to allocate more, allocate more
if (index >= m_chunks.capacity())
{
try { m_chunks.reserve((std::max<std::size_t>)(index, m_chunks.capacity() + 1000)); }
catch (...) { return avi_file::error::NO_MEMORY; }
}
- /* update the number of chunks */
+ // update the number of chunks
m_chunks.resize((std::max<std::size_t>)(m_chunks.size(), index + 1));
- /* set the data */
+ // set the data
m_chunks[index].offset = offset;
m_chunks[index].length = length;
@@ -769,26 +518,26 @@ inline avi_file::error avi_stream::set_chunk_info(std::uint32_t index, std::uint
inline avi_file::error avi_stream::set_strh_data(std::uint8_t const *data, std::uint32_t size)
{
- m_type = fetch_32bits(&data[0]);
- m_scale = fetch_32bits(&data[20]);
- m_rate = fetch_32bits(&data[24]);
- m_samples = fetch_32bits(&data[32]);
+ m_type = get_u32le(&data[0]);
+ m_scale = get_u32le(&data[20]);
+ m_rate = get_u32le(&data[24]);
+ m_samples = get_u32le(&data[32]);
return avi_file::error::NONE;
}
inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::uint32_t size)
{
- /* audio and video streams have differing headers */
+ // audio and video streams have differing headers
if (m_type == STREAMTYPE_VIDS)
{
- m_width = fetch_32bits(&data[4]);
- m_height = fetch_32bits(&data[8]);
- m_depth = fetch_16bits(&data[14]);
- m_format = fetch_32bits(&data[16]);
+ m_width = get_u32le(&data[4]);
+ m_height = get_u32le(&data[8]);
+ m_depth = get_u16le(&data[14]);
+ m_format = get_u32le(&data[16]);
- /* extra extraction for HuffYUV data */
- if ((m_format == FORMAT_HFYU) && (size >= 56))
+ // extra extraction for HuffYUV data
+ if ((m_format == avi_file::FORMAT_HFYU) && (size >= 56))
{
avi_file::error const avierr = huffyuv_extract_tables(data, size);
if (avierr != avi_file::error::NONE)
@@ -797,9 +546,9 @@ inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::
}
else if (m_type == STREAMTYPE_AUDS)
{
- m_channels = fetch_16bits(&data[2]);
- m_samplebits = fetch_16bits(&data[14]);
- m_samplerate = fetch_32bits(&data[4]);
+ m_channels = get_u16le(&data[2]);
+ m_samplebits = get_u16le(&data[14]);
+ m_samplerate = get_u32le(&data[4]);
}
return avi_file::error::NONE;
}
@@ -821,7 +570,7 @@ inline avi_file::error avi_stream::set_strf_data(std::uint8_t const *data, std::
inline avi_stream *avi_file_impl::get_video_stream()
{
for (int streamnum = 0; streamnum < m_streams.size(); streamnum++)
- if (m_streams[streamnum].type() == STREAMTYPE_VIDS)
+ if (m_streams[streamnum].type() == avi_stream::STREAMTYPE_VIDS)
return &m_streams[streamnum];
return nullptr;
@@ -847,9 +596,9 @@ inline avi_stream *avi_file_impl::get_video_stream()
inline avi_stream *avi_file_impl::get_audio_stream(int channel, int &offset)
{
- /* find the audios stream */
+ // find the audios stream
for (int streamnum = 0; streamnum < m_streams.size(); streamnum++)
- if (m_streams[streamnum].type() == STREAMTYPE_AUDS)
+ if (m_streams[streamnum].type() == avi_stream::STREAMTYPE_AUDS)
{
if (channel < m_streams[streamnum].channels())
{
@@ -882,7 +631,7 @@ inline std::uint32_t avi_file_impl::compute_idx1_size() const
{
int chunks = 0;
- /* count chunks in streams */
+ // count chunks in streams
for (int strnum = 0; strnum < m_streams.size(); strnum++)
chunks += m_streams[strnum].chunks();
@@ -911,9 +660,9 @@ inline std::uint32_t avi_file_impl::get_chunkid_for_stream(const avi_stream *str
std::uint32_t chunkid;
chunkid = AVI_FOURCC('0' + (stream - &m_streams[0]) / 10, '0' + (stream - &m_streams[0]) % 10, 0, 0);
- if (stream->type() == STREAMTYPE_VIDS)
+ if (stream->type() == avi_stream::STREAMTYPE_VIDS)
chunkid |= (stream->format() == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC;
- else if (stream->type() == STREAMTYPE_AUDS)
+ else if (stream->type() == avi_stream::STREAMTYPE_AUDS)
chunkid |= CHUNKTYPE_XXWB;
return chunkid;
@@ -961,7 +710,7 @@ inline std::uint32_t avi_file_impl::framenum_to_samplenum(std::uint32_t framenum
inline avi_file::error avi_file_impl::expand_tempbuffer(std::uint32_t length)
{
- /* expand the tempbuffer to hold the data if necessary */
+ // expand the tempbuffer to hold the data if necessary
if (length > m_tempbuffer.size())
{
try { m_tempbuffer.resize(2 * length); }
@@ -996,10 +745,10 @@ avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, st
std::uint8_t *const dataend = data + numbytes;
int x, y;
- /* compressed video */
+ // compressed video
for (y = 0; y < height; y++)
{
- const std::uint32_t *source = &bitmap.pix32(y);
+ const std::uint32_t *source = &bitmap.pix(y);
std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3;
for (x = 0; x < width && dest < dataend; x++)
@@ -1010,7 +759,7 @@ avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, st
*dest++ = pix.r();
}
- /* fill in any blank space on the right */
+ // fill in any blank space on the right
for ( ; x < m_width && dest < dataend; x++)
{
*dest++ = 0;
@@ -1019,7 +768,7 @@ avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, st
}
}
- /* fill in any blank space on the bottom */
+ // fill in any blank space on the bottom
for ( ; y < m_height; y++)
{
std::uint8_t *dest = data + (m_height - 1 - y) * m_width * 3;
@@ -1055,30 +804,26 @@ avi_file::error avi_stream::rgb32_compress_to_rgb(const bitmap_rgb32 &bitmap, st
avi_file::error avi_stream::yuv_decompress_to_yuy16(const std::uint8_t *data, std::uint32_t numbytes, bitmap_yuy16 &bitmap) const
{
- std::uint16_t const *const dataend = reinterpret_cast<const std::uint16_t *>(data + numbytes);
- int x, y;
+ auto const *const dataend = reinterpret_cast<const std::uint16_t *>(data + numbytes);
- /* compressed video */
- for (y = 0; y < m_height; y++)
+ // compressed video
+ for (int y = 0; y < m_height; y++)
{
const std::uint16_t *source = reinterpret_cast<const std::uint16_t *>(data) + y * m_width;
- std::uint16_t *dest = &bitmap.pix16(y);
+ std::uint16_t *dest = &bitmap.pix(y);
- /* switch off the compression */
+ // switch off the compression
switch (m_format)
{
- case FORMAT_UYVY:
- for (x = 0; x < m_width && source < dataend; x++)
+ case avi_file::FORMAT_UYVY:
+ for (int x = 0; x < m_width && source < dataend; x++)
*dest++ = *source++;
break;
- case FORMAT_VYUY:
- case FORMAT_YUY2:
- for (x = 0; x < m_width && source < dataend; x++)
- {
- std::uint16_t pix = *source++;
- *dest++ = (pix >> 8) | (pix << 8);
- }
+ case avi_file::FORMAT_VYUY:
+ case avi_file::FORMAT_YUY2:
+ for (int x = 0; x < m_width && source < dataend; x++)
+ *dest++ = swapendian_int16(*source++);
break;
}
}
@@ -1107,30 +852,26 @@ avi_file::error avi_stream::yuv_decompress_to_yuy16(const std::uint8_t *data, st
avi_file::error avi_stream::yuy16_compress_to_yuy(const bitmap_yuy16 &bitmap, std::uint8_t *data, std::uint32_t numbytes) const
{
- std::uint16_t *const dataend = reinterpret_cast<std::uint16_t *>(data + numbytes);
- int x, y;
+ auto *const dataend = reinterpret_cast<std::uint16_t *>(data + numbytes);
- /* compressed video */
- for (y = 0; y < m_height; y++)
+ // compressed video
+ for (int y = 0; y < m_height; y++)
{
- const std::uint16_t *source = &bitmap.pix16(y);
+ const std::uint16_t *source = &bitmap.pix(y);
std::uint16_t *dest = reinterpret_cast<std::uint16_t *>(data) + y * m_width;
- /* switch off the compression */
+ // switch off the compression
switch (m_format)
{
- case FORMAT_UYVY:
- for (x = 0; x < m_width && dest < dataend; x++)
+ case avi_file::FORMAT_UYVY:
+ for (int x = 0; x < m_width && dest < dataend; x++)
*dest++ = *source++;
break;
- case FORMAT_VYUY:
- case FORMAT_YUY2:
- for (x = 0; x < m_width && dest < dataend; x++)
- {
- std::uint16_t pix = *source++;
- *dest++ = (pix >> 8) | (pix << 8);
- }
+ case avi_file::FORMAT_VYUY:
+ case avi_file::FORMAT_YUY2:
+ for (int x = 0; x < m_width && dest < dataend; x++)
+ *dest++ = swapendian_int16(*source++);
break;
}
}
@@ -1160,26 +901,26 @@ avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata
{
const std::uint8_t *const chunkend = chunkdata + size;
- /* allocate memory for the data */
+ // allocate memory for the data
std::unique_ptr<huffyuv_data> huffyuv;
try { huffyuv = std::make_unique<huffyuv_data>(); }
catch (...) { return avi_file::error::NO_MEMORY; }
- /* extract predictor information */
+ // extract predictor information
if (&chunkdata[40] >= chunkend)
return avi_file::error::INVALID_DATA;
huffyuv->predictor = chunkdata[40];
- /* make sure it's the left predictor */
+ // make sure it's the left predictor
if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT)
return avi_file::error::UNSUPPORTED_VIDEO_FORMAT;
- /* make sure it's 16bpp YUV data */
+ // make sure it's 16bpp YUV data
if (chunkdata[41] != 16)
return avi_file::error::UNSUPPORTED_VIDEO_FORMAT;
chunkdata += 44;
- /* loop over tables */
+ // loop over tables
for (int tabnum = 0; tabnum < 3; tabnum++)
{
huffyuv_table &table = huffyuv->table[tabnum];
@@ -1187,19 +928,19 @@ avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata
std::uint16_t bitsat16 = 0;
int offset = 0, bits;
- /* loop until we populate the whole table */
+ // loop until we populate the whole table
while (offset < 256)
{
int data, shift, count, i;
- /* extract the next run */
+ // extract the next run
if (chunkdata >= chunkend)
return avi_file::error::INVALID_DATA;
data = *chunkdata++;
shift = data & 0x1f;
count = data >> 5;
- /* zero count means next whole byte is a count */
+ // zero count means next whole byte is a count
if (count == 0)
{
if (chunkdata >= chunkend)
@@ -1210,17 +951,17 @@ avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata
table.shift[offset++] = shift;
}
- /* now determine bit patterns and masks */
+ // now determine bit patterns and masks
curbits = 0;
for (bits = 31; bits >= 0; bits--)
{
bitadd = 1 << (32 - bits);
- /* make sure we've cleared out all the bits below */
+ // make sure we've cleared out all the bits below
if ((curbits & (bitadd - 1)) != 0)
return avi_file::error::INVALID_DATA;
- /* find all entries with this shift count and assign them */
+ // find all entries with this shift count and assign them
for (offset = 0; offset < 256; offset++)
if (table.shift[offset] == bits)
{
@@ -1229,12 +970,12 @@ avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata
curbits += bitadd;
}
- /* remember the bit pattern when we complete all the 17-bit codes */
+ // remember the bit pattern when we complete all the 17-bit codes
if (bits == 17)
bitsat16 = curbits >> 16;
}
- /* allocate the number of extra lookup tables we need */
+ // allocate the number of extra lookup tables we need
if (bitsat16 > 0)
{
try { table.extralookup.resize(bitsat16 * 65536); }
@@ -1243,7 +984,7 @@ avi_file::error avi_stream::huffyuv_extract_tables(const std::uint8_t *chunkdata
table.baselookup[offset] = (offset << 8) | 0;
}
- /* then create lookup tables */
+ // then create lookup tables
for (offset = 0; offset < 256; offset++)
if (table.shift[offset] > 16)
{
@@ -1293,18 +1034,17 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
std::uint8_t bitsinbuffer = 0;
std::uint32_t bitbuffer = 0;
std::uint32_t dataoffs = 0;
- int x, y;
- /* compressed video */
- for (y = 0; y < m_height; y++)
+ // compressed video
+ for (int y = 0; y < m_height; y++)
{
- std::uint16_t *dest = &bitmap.pix16(y);
+ std::uint16_t *dest = &bitmap.pix(y);
- /* handle the first four bytes independently */
- x = 0;
+ // handle the first four bytes independently
+ int x = 0;
if (y == 0)
{
- /* first DWORD is stored as YUY2 */
+ // first DWORD is stored as YUY2
lasty = data[dataoffs++];
lastcb = data[dataoffs++];
*dest++ = (lasty << 8) | lastcb;
@@ -1314,7 +1054,7 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
x = 2;
}
- /* loop over pixels */
+ // loop over pixels
for ( ; x < m_width; x++)
{
huffyuv_table const &ytable = m_huffyuv->table[0];
@@ -1322,14 +1062,14 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
std::uint16_t huffdata;
int shift;
- /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
+ // fill up the buffer; they store little-endian DWORDs, so we XOR with 3
while (bitsinbuffer <= 24 && dataoffs < numbytes)
{
bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
bitsinbuffer += 8;
}
- /* look up the Y component */
+ // look up the Y component
huffdata = ytable.baselookup[bitbuffer >> 16];
shift = huffdata & 0xff;
if (shift == 0)
@@ -1337,7 +1077,7 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
bitsinbuffer -= 16;
bitbuffer <<= 16;
- /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
+ // fill up the buffer; they store little-endian DWORDs, so we XOR with 3
while (bitsinbuffer <= 24 && dataoffs < numbytes)
{
bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
@@ -1351,14 +1091,14 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
bitbuffer <<= shift;
std::uint16_t const pixel = huffdata & 0xff00;
- /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
+ // fill up the buffer; they store little-endian DWORDs, so we XOR with 3
while (bitsinbuffer <= 24 && dataoffs < numbytes)
{
bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
bitsinbuffer += 8;
}
- /* look up the Cb/Cr component */
+ // look up the Cb/Cr component
huffdata = ctable.baselookup[bitbuffer >> 16];
shift = huffdata & 0xff;
if (shift == 0)
@@ -1366,7 +1106,7 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
bitsinbuffer -= 16;
bitbuffer <<= 16;
- /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */
+ // fill up the buffer; they store little-endian DWORDs, so we XOR with 3
while (bitsinbuffer <= 24 && dataoffs < numbytes)
{
bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer);
@@ -1382,26 +1122,26 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
}
}
- /* apply deltas */
+ // apply deltas
lastprevy = lastprevcb = lastprevcr = 0;
- for (y = 0; y < m_height; y++)
+ for (int y = 0; y < m_height; y++)
{
- std::uint16_t *prevrow = &bitmap.pix16(y - prevlines);
- std::uint16_t *dest = &bitmap.pix16(y);
+ std::uint16_t *prevrow = &bitmap.pix(y - prevlines);
+ std::uint16_t *dest = &bitmap.pix(y);
- /* handle the first four bytes independently */
- x = 0;
+ // handle the first four bytes independently
+ int x = 0;
if (y == 0)
{
- /* lasty, lastcr, lastcb are set up previously */
+ // lasty, lastcr, lastcb are set up previously
x = 2;
}
- /* left predict or gradient predict */
+ // left predict or gradient predict
if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT ||
((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT))
{
- /* first do left deltas */
+ // first do left deltas
for ( ; x < m_width; x += 2)
{
std::uint16_t pixel0 = dest[x + 0];
@@ -1416,7 +1156,7 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
dest[x + 1] = (lasty << 8) | lastcr;
}
- /* for gradient, we then add in the previous row */
+ // for gradient, we then add in the previous row
if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines)
for (x = 0; x < m_width; x++)
{
@@ -1428,7 +1168,7 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
}
}
- /* median predict on rows > 0 */
+ // median predict on rows > 0
else if ((m_huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines)
{
for ( ; x < m_width; x += 2)
@@ -1439,45 +1179,45 @@ avi_file::error avi_stream::huffyuv_decompress_to_yuy16(const std::uint8_t *data
std::uint16_t pixel1 = dest[x + 1];
std::uint8_t a, b, c;
- /* compute previous, above, and (prev + above - above-left) */
+ // compute previous, above, and (prev + above - above-left)
a = lasty;
b = prevpixel0 >> 8;
c = lastprevy;
lastprevy = b;
- if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
- if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
- if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
+ if (a > b) std::swap(a, b);
+ if (a > c) std::swap(a, c);
+ if (b > c) std::swap(b, c);
lasty = (pixel0 >> 8) + b;
- /* compute previous, above, and (prev + above - above-left) */
+ // compute previous, above, and (prev + above - above-left)
a = lastcb;
b = prevpixel0 & 0xff;
c = lastprevcb;
lastprevcb = b;
- if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
- if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
- if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
+ if (a > b) std::swap(a, b);
+ if (a > c) std::swap(a, c);
+ if (b > c) std::swap(b, c);
lastcb = (pixel0 & 0xff) + b;
dest[x + 0] = (lasty << 8) | lastcb;
- /* compute previous, above, and (prev + above - above-left) */
+ // compute previous, above, and (prev + above - above-left)
a = lasty;
b = prevpixel1 >> 8;
c = lastprevy;
lastprevy = b;
- if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
- if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
- if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
+ if (a > b) std::swap(a, b);
+ if (a > c) std::swap(a, c);
+ if (b > c) std::swap(b, c);
lasty = (pixel1 >> 8) + b;
- /* compute previous, above, and (prev + above - above-left) */
+ // compute previous, above, and (prev + above - above-left)
a = lastcr;
b = prevpixel1 & 0xff;
c = lastprevcr;
lastprevcr = b;
- if (a > b) { std::uint8_t tmp = a; a = b; b = tmp; }
- if (a > c) { std::uint8_t tmp = a; a = c; c = tmp; }
- if (b > c) { std::uint8_t tmp = b; b = c; c = tmp; }
+ if (a > b) std::swap(a, b);
+ if (a > c) std::swap(a, c);
+ if (b > c) std::swap(b, c);
lastcr = (pixel1 & 0xff) + b;
dest[x + 1] = (lasty << 8) | lastcr;
}
@@ -1497,12 +1237,12 @@ avi_file::error avi_stream::uncompressed_rgb24_to_argb32(const std::uint8_t *dat
{
std::uint32_t dataoffs = 0;
- /* uncompressed video */
+ // uncompressed video
for (int y = 0; y < m_height; y++)
{
- std::uint32_t *dest = &bitmap.pix32(y);
+ std::uint32_t *dest = &bitmap.pix(y);
- /* loop over pixels */
+ // loop over pixels
for (int x = 0; x < m_width; x++)
{
const uint8_t b = data[dataoffs++];
@@ -1527,12 +1267,12 @@ avi_file::error avi_stream::uncompressed_yuv420p_to_argb32(const std::uint8_t *d
const int height = bitmap.height();
const int size_total = width * height;
- /* uncompressed video */
+ // uncompressed video
for (int y = 0; y < m_height; y++)
{
- std::uint32_t *dest = &bitmap.pix32(y);
+ std::uint32_t *dest = &bitmap.pix(y);
- /* loop over pixels */
+ // loop over pixels
for (int x = 0; x < m_width; x++)
{
const uint8_t luma = data[y * width + x];
@@ -1543,9 +1283,9 @@ avi_file::error avi_stream::uncompressed_yuv420p_to_argb32(const std::uint8_t *d
int g = luma - (0.698001f * (v - 0x80)) - (0.337633f * (u - 0x80));
int b = luma + (1.732446f * (u - 0x80));
- r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
- g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
- b = (b < 0) ? 0 : ((b > 255) ? 255 : b);
+ r = std::clamp(r, 0, 255);
+ g = std::clamp(g, 0, 255);
+ b = std::clamp(b, 0, 255);
*dest++ = 0xff << 24 | r << 16 | g << 8 | b;
}
@@ -1573,21 +1313,21 @@ avi_file_impl::~avi_file_impl()
{
error avierr = error::NONE;
- /* if we're creating a new file, finalize it by writing out the non-media chunks */
+ // if we're creating a new file, finalize it by writing out the non-media chunks
if (m_type == FILETYPE_CREATE)
{
- /* flush any pending sound data */
+ // flush any pending sound data
avierr = soundbuf_flush(false);
- /* close the movi chunk */
+ // close the movi chunk
if (avierr == error::NONE)
avierr = chunk_close();
- /* if this is the first RIFF chunk, write an idx1 */
+ // if this is the first RIFF chunk, write an idx1
if (avierr == error::NONE && m_riffbase == 0)
avierr = write_idx1_chunk();
- /* update the strh and indx chunks for each stream */
+ // update the strh and indx chunks for each stream
for (int strnum = 0; strnum < m_streams.size(); strnum++)
{
if (avierr == error::NONE)
@@ -1596,16 +1336,16 @@ avi_file_impl::~avi_file_impl()
avierr = write_indx_chunk(m_streams[strnum], false);
}
- /* update the avih chunk */
+ // update the avih chunk
if (avierr == error::NONE)
avierr = write_avih_chunk(false);
- /* close the RIFF chunk */
+ // close the RIFF chunk
if (avierr == error::NONE)
avierr = chunk_close();
}
- /* close the file */
+ // close the file
m_file.reset();
//return avierr;
@@ -1613,20 +1353,20 @@ avi_file_impl::~avi_file_impl()
/*-------------------------------------------------
- avi_printf_chunks - print the chunks in a file
+ display_chunks - print the chunks in a file
-------------------------------------------------*/
/**
- * @fn void avi_printf_chunks(avi_file *file)
+ * @fn void display_chunks(avi_file *file)
*
* @brief Avi printf chunks.
*
* @param [in,out] file If non-null, the file.
*/
-void avi_file_impl::printf_chunks()
+void avi_file_impl::display_chunks()
{
- printf_chunk_recursive(&m_rootchunk, 0);
+ display_chunk_recursive(&m_rootchunk, 0);
}
@@ -1681,7 +1421,7 @@ std::uint32_t avi_file_impl::first_sample_in_frame(std::uint32_t framenum) const
avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t framenum, bitmap_argb32 &bitmap)
{
- /* get the video stream */
+ // get the video stream
avi_stream *const stream = get_video_stream();
if (!stream)
return error::INVALID_STREAM;
@@ -1692,31 +1432,31 @@ avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t frame
if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
bitmap.resize(stream->width(), stream->height());
- /* assume one chunk == one frame */
+ // assume one chunk == one frame
if (framenum >= stream->chunks())
return error::INVALID_FRAME;
- /* we only support YUY-style bitmaps (16bpp) */
+ // we only support YUY-style bitmaps (16bpp)
if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
return error::INVALID_BITMAP;
- /* expand the tempbuffer to hold the data if necessary */
+ // expand the tempbuffer to hold the data if necessary
error avierr = error::NONE;
avierr = expand_tempbuffer(stream->chunk(framenum).length);
if (avierr != error::NONE)
return avierr;
- /* read in the data */
+ // read in the data
std::uint32_t bytes_read;
- osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
- if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length)
+ std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
+ if (filerr || bytes_read != stream->chunk(framenum).length)
return error::READ_ERROR;
- /* validate this is good data */
- std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]);
+ // validate this is good data
+ std::uint32_t const chunkid = get_u32le(&m_tempbuffer[0]);
if (chunkid == get_chunkid_for_stream(stream))
{
- /* uncompressed YUV420p */
+ // uncompressed YUV420p
if (stream->format() == FORMAT_I420)
avierr = stream->uncompressed_yuv420p_to_argb32(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
else
@@ -1738,44 +1478,44 @@ avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t frame
avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_yuy16 &bitmap)
{
- /* get the video stream */
+ // get the video stream
avi_stream *const stream = get_video_stream();
if (!stream)
return error::INVALID_STREAM;
- /* validate our ability to handle the data */
+ // validate our ability to handle the data
if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU)
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* assume one chunk == one frame */
+ // assume one chunk == one frame
if (framenum >= stream->chunks())
return error::INVALID_FRAME;
- /* we only support YUY-style bitmaps (16bpp) */
+ // we only support YUY-style bitmaps (16bpp)
if (bitmap.width() < stream->width() || bitmap.height() < stream->height())
return error::INVALID_BITMAP;
- /* expand the tempbuffer to hold the data if necessary */
+ // expand the tempbuffer to hold the data if necessary
error avierr = error::NONE;
avierr = expand_tempbuffer(stream->chunk(framenum).length);
if (avierr != error::NONE)
return avierr;
- /* read in the data */
+ // read in the data
std::uint32_t bytes_read;
- osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
- if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length)
+ std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read);
+ if (filerr || bytes_read != stream->chunk(framenum).length)
return error::READ_ERROR;
- /* validate this is good data */
- std::uint32_t const chunkid = fetch_32bits(&m_tempbuffer[0]);
+ // validate this is good data
+ std::uint32_t const chunkid = get_u32le(&m_tempbuffer[0]);
if (chunkid == get_chunkid_for_stream(stream))
{
- /* HuffYUV-compressed */
+ // HuffYUV-compressed
if (stream->format() == FORMAT_HFYU)
avierr = stream->huffyuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
- /* other YUV-compressed */
+ // other YUV-compressed
else
avierr = stream->yuv_decompress_to_yuy16(&m_tempbuffer[8], stream->chunk(framenum).length - 8, bitmap);
}
@@ -1807,33 +1547,33 @@ avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_y
avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t firstsample, std::uint32_t numsamples, std::int16_t *output)
{
- /* get the audio stream */
+ // get the audio stream
int offset = 0;
avi_stream *const stream = get_audio_stream(channel, offset);
if (!stream)
return error::INVALID_STREAM;
- /* validate our ability to handle the data */
+ // validate our ability to handle the data
if (stream->format() != 0 || (stream->samplebits() != 8 && stream->samplebits() != 16))
return error::UNSUPPORTED_AUDIO_FORMAT;
- /* verify we are in range */
+ // verify we are in range
if (firstsample >= stream->samples())
return error::INVALID_FRAME;
if (firstsample + numsamples > stream->samples())
numsamples = stream->samples() - firstsample;
- /* determine bytes per sample */
+ // determine bytes per sample
std::uint32_t const bytes_per_sample = stream->bytes_per_sample();
- /* loop until all samples have been extracted */
+ // loop until all samples have been extracted
while (numsamples > 0)
{
std::uint32_t chunkbase = 0, chunkend = 0, chunkid;
std::uint32_t bytes_read, samples_this_chunk;
int chunknum, sampnum;
- /* locate the chunk with the first sample */
+ // locate the chunk with the first sample
for (chunknum = 0; chunknum < stream->chunks(); chunknum++)
{
chunkend = chunkbase + (stream->chunk(chunknum).length - 8) / bytes_per_sample;
@@ -1842,36 +1582,36 @@ avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t fir
chunkbase = chunkend;
}
- /* if we hit the end, fill the rest with silence */
+ // if we hit the end, fill the rest with silence
if (chunknum == stream->chunks())
{
std::memset(output, 0, numsamples * 2);
break;
}
- /* expand the tempbuffer to hold the data if necessary */
+ // expand the tempbuffer to hold the data if necessary
error avierr = expand_tempbuffer(stream->chunk(chunknum).length);
if (avierr != error::NONE)
return avierr;
- /* read in the data */
- auto const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read);
- if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(chunknum).length)
+ // read in the data
+ std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read);
+ if (filerr || bytes_read != stream->chunk(chunknum).length)
return error::READ_ERROR;
- /* validate this is good data */
- chunkid = fetch_32bits(&m_tempbuffer[0]);
+ // validate this is good data
+ chunkid = get_u32le(&m_tempbuffer[0]);
if (chunkid != get_chunkid_for_stream(stream))
return error::INVALID_DATA;
- /* determine how many samples to copy */
+ // determine how many samples to copy
samples_this_chunk = chunkend - firstsample;
samples_this_chunk = (std::min)(samples_this_chunk, numsamples);
- /* extract 16-bit samples from the chunk */
+ // extract 16-bit samples from the chunk
if (stream->samplebits() == 16)
{
- const std::int16_t *base = reinterpret_cast<const std::int16_t *>(&m_tempbuffer[8]);
+ const auto *base = reinterpret_cast<const std::int16_t *>(&m_tempbuffer[8]);
base += stream->channels() * (firstsample - chunkbase) + offset;
for (sampnum = 0; sampnum < samples_this_chunk; sampnum++)
{
@@ -1880,7 +1620,7 @@ avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t fir
}
}
- /* extract 8-bit samples from the chunk */
+ // extract 8-bit samples from the chunk
else if (stream->samplebits() == 8)
{
const std::uint8_t *base = &m_tempbuffer[8];
@@ -1892,7 +1632,7 @@ avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t fir
}
}
- /* update our counters */
+ // update our counters
firstsample += samples_this_chunk;
numsamples -= samples_this_chunk;
}
@@ -1923,32 +1663,32 @@ avi_file::error avi_file_impl::append_video_frame(bitmap_yuy16 &bitmap)
error avierr;
std::uint32_t maxlength;
- /* validate our ability to handle the data */
+ // validate our ability to handle the data
if (stream->format() != FORMAT_UYVY && stream->format() != FORMAT_VYUY && stream->format() != FORMAT_YUY2 && stream->format() != FORMAT_HFYU)
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* write out any sound data first */
+ // write out any sound data first
avierr = soundbuf_write_chunk(stream->chunks());
if (avierr != error::NONE)
return avierr;
- /* make sure we have enough room */
+ // make sure we have enough room
maxlength = 2 * stream->width() * stream->height();
avierr = expand_tempbuffer(maxlength);
if (avierr != error::NONE)
return avierr;
- /* now compress the data */
+ // now compress the data
avierr = stream->yuy16_compress_to_yuy(bitmap, &m_tempbuffer[0], maxlength);
if (avierr != error::NONE)
return avierr;
- /* write the data */
+ // write the data
avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength);
if (avierr != error::NONE)
return avierr;
- /* set the info for this new chunk */
+ // set the info for this new chunk
avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8);
if (avierr != error::NONE)
return avierr;
@@ -1981,36 +1721,36 @@ avi_file::error avi_file_impl::append_video_frame(bitmap_rgb32 &bitmap)
error avierr;
std::uint32_t maxlength;
- /* validate our ability to handle the data */
+ // validate our ability to handle the data
if (stream->format() != 0)
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* depth must be 24 */
+ // depth must be 24
if (stream->depth() != 24)
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* write out any sound data first */
+ // write out any sound data first
avierr = soundbuf_write_chunk(stream->chunks());
if (avierr != error::NONE)
return avierr;
- /* make sure we have enough room */
+ // make sure we have enough room
maxlength = 3 * stream->width() * stream->height();
avierr = expand_tempbuffer(maxlength);
if (avierr != error::NONE)
return avierr;
- /* copy the RGB data to the destination */
+ // copy the RGB data to the destination
avierr = stream->rgb32_compress_to_rgb(bitmap, &m_tempbuffer[0], maxlength);
if (avierr != error::NONE)
return avierr;
- /* write the data */
+ // write the data
avierr = chunk_write(get_chunkid_for_stream(stream), &m_tempbuffer[0], maxlength);
if (avierr != error::NONE)
return avierr;
- /* set the info for this new chunk */
+ // set the info for this new chunk
avierr = stream->set_chunk_info(stream->chunks(), m_writeoffs - maxlength - 8, maxlength + 8);
if (avierr != error::NONE)
return avierr;
@@ -2045,11 +1785,11 @@ avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int1
std::uint32_t sampoffset = m_soundbuf_chansamples[channel];
std::uint32_t sampnum;
- /* see if we have enough room in the buffer */
+ // see if we have enough room in the buffer
if (sampoffset + numsamples > m_soundbuf_samples)
return error::EXCEEDED_SOUND_BUFFER;
- /* append samples to the buffer in little-endian format */
+ // append samples to the buffer in little-endian format
for (sampnum = 0; sampnum < numsamples; sampnum++)
{
std::int16_t data = *samples++;
@@ -2059,7 +1799,7 @@ avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int1
}
m_soundbuf_chansamples[channel] = sampoffset;
- /* flush any full sound chunks to disk */
+ // flush any full sound chunks to disk
return soundbuf_flush(true);
}
@@ -2083,14 +1823,15 @@ avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int1
avi_file::error avi_file_impl::read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer)
{
- /* allocate memory for the data */
- try { buffer.reset(new std::uint8_t[chunk.size]); }
- catch (...) { return error::NO_MEMORY; }
+ // allocate memory for the data
+ buffer.reset(new (std::nothrow) std::uint8_t[chunk.size]);
+ if (!buffer)
+ return error::NO_MEMORY;
- /* read from the file */
+ // read from the file
std::uint32_t bytes_read;
- osd_file::error const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read);
- if (filerr != osd_file::error::NONE || bytes_read != chunk.size)
+ std::error_condition const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read);
+ if (filerr || bytes_read != chunk.size)
{
buffer.reset();
return error::READ_ERROR;
@@ -2290,37 +2031,35 @@ avi_file::error avi_file_impl::find_next_list(std::uint32_t findme, const avi_ch
avi_file::error avi_file_impl::get_next_chunk_internal(const avi_chunk *parent, avi_chunk &newchunk, std::uint64_t offset)
{
- osd_file::error filerr;
- std::uint8_t buffer[12];
- std::uint32_t bytesread;
-
- /* nullptr parent implies the root */
- if (parent == nullptr)
+ // nullptr parent implies the root
+ if (!parent)
parent = &m_rootchunk;
- /* start at the current offset */
+ // start at the current offset
newchunk.offset = offset;
- /* if we're past the bounds of the parent, bail */
+ // if we're past the bounds of the parent, bail
if (newchunk.offset + 8 >= parent->offset + 8 + parent->size)
return error::END;
- /* read the header */
- filerr = m_file->read(buffer, newchunk.offset, 8, bytesread);
- if (filerr != osd_file::error::NONE || bytesread != 8)
+ // read the header
+ std::uint8_t buffer[12];
+ std::uint32_t bytesread;
+ std::error_condition filerr = m_file->read(buffer, newchunk.offset, 8, bytesread);
+ if (filerr || bytesread != 8)
return error::INVALID_DATA;
- /* fill in the new chunk */
- newchunk.type = fetch_32bits(&buffer[0]);
- newchunk.size = fetch_32bits(&buffer[4]);
+ // fill in the new chunk
+ newchunk.type = get_u32le(&buffer[0]);
+ newchunk.size = get_u32le(&buffer[4]);
- /* if we are a list, fetch the list type */
+ // if we are a list, fetch the list type
if (newchunk.type == CHUNKTYPE_LIST || newchunk.type == CHUNKTYPE_RIFF)
{
filerr = m_file->read(&buffer[8], newchunk.offset + 8, 4, bytesread);
- if (filerr != osd_file::error::NONE || bytesread != 4)
+ if (filerr || bytesread != 4)
return error::INVALID_DATA;
- newchunk.listtype = fetch_32bits(&buffer[8]);
+ newchunk.listtype = get_u32le(&buffer[8]);
}
return error::NONE;
@@ -2346,89 +2085,89 @@ avi_file::error avi_file_impl::read_movie_data()
avi_chunk riff, hdrl, avih, strl, strh, strf, indx, movi, idx1;
error avierr;
- /* find the RIFF chunk */
+ // find the RIFF chunk
avierr = find_first_chunk(CHUNKTYPE_RIFF, nullptr, riff);
if (avierr != error::NONE)
return avierr;
- /* verify that the RIFF type is AVI */
+ // verify that the RIFF type is AVI
if (riff.listtype != LISTTYPE_AVI)
{
avierr = error::INVALID_DATA;
return avierr;
}
- /* find the hdrl LIST chunk within the RIFF */
+ // find the hdrl LIST chunk within the RIFF
avierr = find_first_list(LISTTYPE_HDRL, &riff, hdrl);
if (avierr != error::NONE)
return avierr;
- /* find the avih chunk */
+ // find the avih chunk
avierr = find_first_chunk(CHUNKTYPE_AVIH, &hdrl, avih);
if (avierr != error::NONE)
return avierr;
- /* parse the avih chunk */
+ // parse the avih chunk
avierr = parse_avih_chunk(avih);
if (avierr != error::NONE)
return avierr;
- /* loop over strl LIST chunks */
+ // loop over strl LIST chunks
int strindex = 0;
for (avierr = find_first_list(LISTTYPE_STRL, &hdrl, strl); avierr == error::NONE; avierr = find_next_list(LISTTYPE_STRL, &hdrl, strl))
{
- /* if we have too many, it's a bad file */
+ // if we have too many, it's a bad file
if (strindex >= m_streams.size())
return avierr;
- /* find the strh chunk */
+ // find the strh chunk
avierr = find_first_chunk(CHUNKTYPE_STRH, &strl, strh);
if (avierr != error::NONE)
return avierr;
- /* parse the data */
+ // parse the data
avierr = parse_strh_chunk(m_streams[strindex], strh);
if (avierr != error::NONE)
return avierr;
- /* find the strf chunk */
+ // find the strf chunk
avierr = find_first_chunk(CHUNKTYPE_STRF, &strl, strf);
if (avierr != error::NONE)
return avierr;
- /* parse the data */
+ // parse the data
avierr = parse_strf_chunk(m_streams[strindex], strf);
if (avierr != error::NONE)
return avierr;
- /* find the indx chunk, if present */
+ // find the indx chunk, if present
avierr = find_first_chunk(CHUNKTYPE_INDX, &strl, indx);
if (avierr == error::NONE)
avierr = parse_indx_chunk(m_streams[strindex], indx);
- /* next stream */
+ // next stream
strindex++;
}
- /* normalize the error after parsing the stream headers */
+ // normalize the error after parsing the stream headers
if (avierr == error::END)
avierr = error::NONE;
if (avierr != error::NONE)
return avierr;
- /* find the base of the movi data */
+ // find the base of the movi data
avierr = find_first_list(LISTTYPE_MOVI, &riff, movi);
if (avierr != error::NONE)
return avierr;
- /* find and parse the idx1 chunk within the RIFF (if present) */
+ // find and parse the idx1 chunk within the RIFF (if present)
avierr = find_first_chunk(CHUNKTYPE_IDX1, &riff, idx1);
if (avierr == error::NONE)
avierr = parse_idx1_chunk(movi.offset + 8, idx1);
if (avierr != error::NONE)
return avierr;
- /* now extract the movie info */
+ // now extract the movie info
avierr = extract_movie_info();
return avierr;
}
@@ -2454,11 +2193,11 @@ avi_file::error avi_file_impl::extract_movie_info()
avi_stream *stream;
int offset;
- /* get the video stream */
+ // get the video stream
stream = get_video_stream();
if (stream != nullptr)
{
- /* fill in the info */
+ // fill in the info
m_info.video_format = stream->format();
m_info.video_timescale = stream->rate();
m_info.video_sampletime = stream->scale();
@@ -2467,11 +2206,11 @@ avi_file::error avi_file_impl::extract_movie_info()
m_info.video_height = stream->height();
}
- /* get the first audio stream */
+ // get the first audio stream
stream = get_audio_stream(0, offset);
if (stream != nullptr)
{
- /* fill in the info */
+ // fill in the info
m_info.audio_format = stream->format();
m_info.audio_timescale = stream->rate();
m_info.audio_sampletime = stream->scale();
@@ -2481,13 +2220,13 @@ avi_file::error avi_file_impl::extract_movie_info()
m_info.audio_samplerate = stream->samplerate();
}
- /* now make sure all other audio streams are valid */
+ // now make sure all other audio streams are valid
while (nullptr != (stream = get_audio_stream(m_info.audio_channels, offset)))
{
- /* get the stream info */
+ // get the stream info
m_info.audio_channels++;
- /* verify compatibility */
+ // verify compatibility
if (m_info.audio_format != stream->format() ||
m_info.audio_timescale != stream->rate() ||
m_info.audio_sampletime != stream->scale() ||
@@ -2519,16 +2258,16 @@ avi_file::error avi_file_impl::extract_movie_info()
avi_file::error avi_file_impl::parse_avih_chunk(avi_chunk const &avih)
{
- /* read the data */
+ // read the data
std::unique_ptr<std::uint8_t []> chunkdata;
error avierr = read_chunk_data(avih, chunkdata);
if (avierr != error::NONE)
return avierr;
- /* extract the data */
- std::uint32_t const streams = fetch_32bits(&chunkdata[24]);
+ // extract the data
+ std::uint32_t const streams = get_u32le(&chunkdata[24]);
- /* allocate memory for the streams */
+ // allocate memory for the streams
m_streams.clear();
m_streams.resize(streams);
@@ -2555,13 +2294,13 @@ avi_file::error avi_file_impl::parse_avih_chunk(avi_chunk const &avih)
avi_file::error avi_file_impl::parse_strh_chunk(avi_stream &stream, avi_chunk const &strh)
{
- /* read the data */
+ // read the data
std::unique_ptr<std::uint8_t []> chunkdata;
error const avierr = read_chunk_data(strh, chunkdata);
if (avierr != error::NONE)
return avierr;
- /* extract the data */
+ // extract the data
return stream.set_strh_data(&chunkdata[0], strh.size);
}
@@ -2587,7 +2326,7 @@ avi_file::error avi_file_impl::parse_strf_chunk(avi_stream &stream, avi_chunk co
{
error avierr;
- /* read the data */
+ // read the data
std::unique_ptr<std::uint8_t []> chunkdata;
avierr = read_chunk_data(strf, chunkdata);
if (avierr != error::NONE)
@@ -2617,71 +2356,70 @@ avi_file::error avi_file_impl::parse_indx_chunk(avi_stream &stream, avi_chunk co
{
error avierr;
- /* read the data */
+ // read the data
std::unique_ptr<std::uint8_t []> chunkdata;
avierr = read_chunk_data(strf, chunkdata);
if (avierr != error::NONE)
return avierr;
- /* extract the data */
- std::uint16_t const longs_per_entry = fetch_16bits(&chunkdata[0]);
+ // extract the data
+ std::uint16_t const longs_per_entry = get_u16le(&chunkdata[0]);
//subtype = chunkdata[2];
std::uint8_t const type = chunkdata[3];
- std::uint32_t const entries = fetch_32bits(&chunkdata[4]);
- //id = fetch_32bits(&chunkdata[8]);
- std::uint64_t const baseoffset = fetch_64bits(&chunkdata[12]);
+ std::uint32_t const entries = get_u32le(&chunkdata[4]);
+ //id = get_u32le(&chunkdata[8]);
+ std::uint64_t const baseoffset = get_u64le(&chunkdata[12]);
- /* if this is a superindex, loop over entries and call ourselves recursively */
+ // if this is a superindex, loop over entries and call ourselves recursively
if (type == AVI_INDEX_OF_INDEXES)
{
- /* validate the size of each entry */
+ // validate the size of each entry
if (longs_per_entry != 4)
return error::INVALID_DATA;
- /* loop over entries and create subchunks for each */
+ // loop over entries and create subchunks for each
for (std::uint32_t entry = 0; entry < entries; entry++)
{
- const std::uint8_t *base = &chunkdata[24 + entry * 16];
- osd_file::error filerr;
+ std::uint8_t const *const base = &chunkdata[24 + entry * 16];
avi_chunk subchunk;
+
+ // go read the subchunk
+ subchunk.offset = get_u64le(&base[0]);
std::uint32_t bytes_read;
std::uint8_t buffer[8];
-
- /* go read the subchunk */
- subchunk.offset = fetch_64bits(&base[0]);
- filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read);
- if (filerr != osd_file::error::NONE || bytes_read != sizeof(buffer))
+ std::error_condition const filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read);
+ if (filerr || bytes_read != sizeof(buffer))
{
avierr = error::READ_ERROR;
break;
}
- /* parse the data */
- subchunk.type = fetch_32bits(&buffer[0]);
- subchunk.size = fetch_32bits(&buffer[4]);
+ // parse the data
+ subchunk.type = get_u32le(&buffer[0]);
+ subchunk.size = get_u32le(&buffer[4]);
- /* recursively parse each referenced chunk; stop if we hit an error */
+ // recursively parse each referenced chunk; stop if we hit an error
avierr = parse_indx_chunk(stream, subchunk);
if (avierr != error::NONE)
break;
}
}
- /* otherwise, this is a standard index */
+ // otherwise, this is a standard index
else if (type == AVI_INDEX_OF_CHUNKS)
{
- /* validate the size of each entry */
+ // validate the size of each entry
if (longs_per_entry != 2 && longs_per_entry != 3)
return error::INVALID_DATA;
- /* loop over entries and parse out the data */
+ // loop over entries and parse out the data
for (std::uint32_t entry = 0; entry < entries; entry++)
{
const std::uint8_t *base = &chunkdata[24 + entry * 4 * longs_per_entry];
- std::uint32_t offset = fetch_32bits(&base[0]);
- std::uint32_t size = fetch_32bits(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe
+ std::uint32_t offset = get_u32le(&base[0]);
+ std::uint32_t size = get_u32le(&base[4]) & 0x7fffffff; // bit 31 == NOT a keyframe
- /* set the info for this chunk and advance */
+ // set the info for this chunk and advance
avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset - 8, size + 8);
if (avierr != error::NONE)
break;
@@ -2712,30 +2450,30 @@ avi_file::error avi_file_impl::parse_idx1_chunk(std::uint64_t baseoffset, avi_ch
{
error avierr;
- /* read the data */
+ // read the data
std::unique_ptr<std::uint8_t []> chunkdata;
avierr = read_chunk_data(idx1, chunkdata);
if (avierr != error::NONE)
return avierr;
- /* loop over entries */
+ // loop over entries
std::uint32_t const entries = idx1.size / 16;
for (std::uint32_t entry = 0; entry < entries; entry++)
{
std::uint8_t const *const base = &chunkdata[entry * 16];
- std::uint32_t const chunkid = fetch_32bits(&base[0]);
- std::uint32_t const offset = fetch_32bits(&base[8]);
- std::uint32_t const size = fetch_32bits(&base[12]);
+ std::uint32_t const chunkid = get_u32le(&base[0]);
+ std::uint32_t const offset = get_u32le(&base[8]);
+ std::uint32_t const size = get_u32le(&base[12]);
int streamnum;
- /* determine the stream index */
+ // determine the stream index
streamnum = ((chunkid >> 8) & 0xff) - '0';
streamnum += 10 * ((chunkid & 0xff) - '0');
if (streamnum >= m_streams.size())
return error::INVALID_DATA;
avi_stream &stream = m_streams[streamnum];
- /* set the appropriate entry */
+ // set the appropriate entry
avierr = stream.set_chunk_info(stream.chunks(), baseoffset + offset, size + 8);
if (avierr != error::NONE)
return avierr;
@@ -2764,48 +2502,48 @@ avi_file::error avi_file_impl::parse_idx1_chunk(std::uint64_t baseoffset, avi_ch
avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t listtype, std::uint32_t estlength)
{
- /* if we're out of stack entries, bail */
+ // if we're out of stack entries, bail
if (m_chunksp >= m_chunkstack.size())
return error::STACK_TOO_DEEP;
avi_chunk &chunk = m_chunkstack[m_chunksp++];
- /* set up the chunk information */
+ // set up the chunk information
chunk.offset = m_writeoffs;
chunk.size = estlength;
chunk.type = type;
chunk.listtype = listtype;
- /* non-list types */
+ // non-list types
if (type != CHUNKTYPE_RIFF && type != CHUNKTYPE_LIST)
{
std::uint8_t buffer[8];
- /* populate the header */
- put_32bits(&buffer[0], chunk.type);
- put_32bits(&buffer[4], chunk.size);
+ // populate the header
+ put_u32le(&buffer[0], chunk.type);
+ put_u32le(&buffer[4], chunk.size);
- /* write the header */
+ // write the header
std::uint32_t written;
- osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
- if (filerr != osd_file::error::NONE || written != sizeof(buffer))
+ std::error_condition const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
+ if (filerr || written != sizeof(buffer))
return error::WRITE_ERROR;
m_writeoffs += written;
}
- /* list types */
+ // list types
else
{
std::uint8_t buffer[12];
- /* populate the header */
- put_32bits(&buffer[0], chunk.type);
- put_32bits(&buffer[4], chunk.size);
- put_32bits(&buffer[8], chunk.listtype);
+ // populate the header
+ put_u32le(&buffer[0], chunk.type);
+ put_u32le(&buffer[4], chunk.size);
+ put_u32le(&buffer[8], chunk.listtype);
- /* write the header */
+ // write the header
std::uint32_t written;
- osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
- if (filerr != osd_file::error::NONE || written != sizeof(buffer))
+ std::error_condition const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written);
+ if (filerr || written != sizeof(buffer))
return error::WRITE_ERROR;
m_writeoffs += written;
}
@@ -2833,23 +2571,23 @@ avi_file::error avi_file_impl::chunk_close()
avi_chunk const &chunk = m_chunkstack[--m_chunksp];
std::uint64_t const chunksize = m_writeoffs - (chunk.offset + 8);
- /* error if we don't fit into 32 bits */
+ // error if we don't fit into 32 bits
if (chunksize != std::uint32_t(chunksize))
return error::INVALID_DATA;
- /* write the final size if it is different from the guess */
+ // write the final size if it is different from the guess
if (chunk.size != chunksize)
{
std::uint8_t buffer[4];
- put_32bits(&buffer[0], std::uint32_t(chunksize));
+ put_u32le(&buffer[0], std::uint32_t(chunksize));
std::uint32_t written;
- osd_file::error const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written);
- if (filerr != osd_file::error::NONE || written != sizeof(buffer))
+ std::error_condition const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written);
+ if (filerr || written != sizeof(buffer))
return error::WRITE_ERROR;
}
- /* round up to the next word */
+ // round up to the next word
m_writeoffs += chunksize & 1;
return error::NONE;
@@ -2877,20 +2615,20 @@ avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data,
{
error avierr;
- /* if we are the first RIFF, we must reserve enough space for the IDX chunk */
+ // if we are the first RIFF, we must reserve enough space for the IDX chunk
std::uint32_t const idxreserve = (m_riffbase == 0 && type != CHUNKTYPE_IDX1) ? compute_idx1_size() : 0;
- /* if we are getting too big, split the RIFF */
- /* note that we ignore writes before the current RIFF base, as those are assumed to be
- overwrites of a chunk from the previous RIFF */
+ // if we are getting too big, split the RIFF
+ // note that we ignore writes before the current RIFF base, as those are assumed to be
+ // overwrites of a chunk from the previous RIFF
if ((m_writeoffs >= m_riffbase) && ((m_writeoffs + length + idxreserve - m_riffbase) >= MAX_RIFF_SIZE))
{
- /* close the movi list */
+ // close the movi list
avierr = chunk_close();
if (avierr != error::NONE)
return avierr;
- /* write the idx1 chunk if this is the first */
+ // write the idx1 chunk if this is the first
if (m_riffbase == 0)
{
avierr = write_idx1_chunk();
@@ -2898,37 +2636,37 @@ avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data,
return avierr;
}
- /* close the RIFF */
+ // close the RIFF
avierr = chunk_close();
if (avierr != error::NONE)
return avierr;
- /* open a new RIFF */
+ // open a new RIFF
m_riffbase = m_writeoffs;
avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0);
if (avierr != error::NONE)
return avierr;
- /* open a nested movi list */
+ // open a nested movi list
m_saved_movi_offset = m_writeoffs;
avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0);
if (avierr != error::NONE)
return avierr;
}
- /* open the chunk */
+ // open the chunk
avierr = chunk_open(type, 0, length);
if (avierr != error::NONE)
return avierr;
- /* write the data */
+ // write the data
std::uint32_t written;
- osd_file::error const filerr = m_file->write(data, m_writeoffs, length, written);
- if (filerr != osd_file::error::NONE || written != length)
+ std::error_condition const filerr = m_file->write(data, m_writeoffs, length, written);
+ if (filerr || written != length)
return error::WRITE_ERROR;
m_writeoffs += written;
- /* close the chunk */
+ // close the chunk
return chunk_close();
}
@@ -2959,21 +2697,21 @@ avi_file::error avi_file_impl::chunk_overwrite(std::uint32_t type, const void *d
{
std::uint64_t savedoffset = 0;
- /* if this is our initial write, save the offset */
+ // if this is our initial write, save the offset
if (initial_write)
offset = m_writeoffs;
- /* otherwise, remember the current write offset and replace it with the original */
+ // otherwise, remember the current write offset and replace it with the original
else
{
savedoffset = m_writeoffs;
m_writeoffs = offset;
}
- /* write the chunk */
+ // write the chunk
error const avierr = chunk_write(type, data, length);
- /* if this isn't the initial write, restore the previous offset */
+ // if this isn't the initial write, restore the previous offset
if (!initial_write)
m_writeoffs = savedoffset;
@@ -3000,59 +2738,59 @@ avi_file::error avi_file_impl::write_initial_headers()
{
error avierr;
- /* reset the write pointer */
+ // reset the write pointer
m_writeoffs = 0;
- /* open a RIFF chunk */
+ // open a RIFF chunk
avierr = chunk_open(CHUNKTYPE_RIFF, LISTTYPE_AVI, 0);
if (avierr != error::NONE)
return avierr;
- /* open a hdlr LIST */
+ // open a hdlr LIST
avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_HDRL, 0);
if (avierr != error::NONE)
return avierr;
- /* write an avih chunk */
+ // write an avih chunk
avierr = write_avih_chunk(true);
if (avierr != error::NONE)
return avierr;
- /* for each stream, write a strl LIST */
+ // for each stream, write a strl LIST
for (int strnum = 0; strnum < m_streams.size(); strnum++)
{
- /* open a strl LIST */
+ // open a strl LIST
avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_STRL, 0);
if (avierr != error::NONE)
return avierr;
- /* write the strh chunk */
+ // write the strh chunk
avierr = write_strh_chunk(m_streams[strnum], true);
if (avierr != error::NONE)
return avierr;
- /* write the strf chunk */
+ // write the strf chunk
avierr = write_strf_chunk(m_streams[strnum]);
if (avierr != error::NONE)
return avierr;
- /* write the indx chunk */
+ // write the indx chunk
avierr = write_indx_chunk(m_streams[strnum], true);
if (avierr != error::NONE)
return avierr;
- /* close the strl LIST */
+ // close the strl LIST
avierr = chunk_close();
if (avierr != error::NONE)
return avierr;
}
- /* close the hdlr LIST */
+ // close the hdlr LIST
avierr = chunk_close();
if (avierr != error::NONE)
return avierr;
- /* open a movi LIST */
+ // open a movi LIST
m_saved_movi_offset = m_writeoffs;
avierr = chunk_open(CHUNKTYPE_LIST, LISTTYPE_MOVI, 0);
if (avierr != error::NONE)
@@ -3083,17 +2821,17 @@ avi_file::error avi_file_impl::write_avih_chunk(bool initial_write)
avi_stream *video = get_video_stream();
std::uint8_t buffer[56];
- /* reset the buffer */
+ // reset the buffer
std::memset(buffer, 0, sizeof(buffer));
- put_32bits(&buffer[0], 1000000 * std::int64_t(video->scale()) / video->rate()); /* dwMicroSecPerFrame */
- put_32bits(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* dwFlags */
- put_32bits(&buffer[16], video->samples()); /* dwTotalFrames */
- put_32bits(&buffer[24], m_streams.size()); /* dwStreams */
- put_32bits(&buffer[32], video->width()); /* dwWidth */
- put_32bits(&buffer[36], video->height()); /* dwHeight */
+ put_u32le(&buffer[0], 1000000 * std::int64_t(video->scale()) / video->rate()); // dwMicroSecPerFrame
+ put_u32le(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); // dwFlags
+ put_u32le(&buffer[16], video->samples()); // dwTotalFrames
+ put_u32le(&buffer[24], m_streams.size()); // dwStreams
+ put_u32le(&buffer[32], video->width()); // dwWidth
+ put_u32le(&buffer[36], video->height()); // dwHeight
- /* (over)write the chunk */
+ // (over)write the chunk
return chunk_overwrite(CHUNKTYPE_AVIH, buffer, sizeof(buffer), m_saved_avih_offset, initial_write);
}
@@ -3119,36 +2857,36 @@ avi_file::error avi_file_impl::write_strh_chunk(avi_stream &stream, bool initial
{
std::uint8_t buffer[56];
- /* reset the buffer */
+ // reset the buffer
std::memset(buffer, 0, sizeof(buffer));
- put_32bits(&buffer[0], stream.type()); /* fccType */
- put_32bits(&buffer[20], stream.scale()); /* dwScale */
- put_32bits(&buffer[24], stream.rate()); /* dwRate */
- put_32bits(&buffer[32], stream.samples()); /* dwLength */
- put_32bits(&buffer[40], 10000); /* dwQuality */
+ put_u32le(&buffer[0], stream.type()); // fccType
+ put_u32le(&buffer[20], stream.scale()); // dwScale
+ put_u32le(&buffer[24], stream.rate()); // dwRate
+ put_u32le(&buffer[32], stream.samples()); // dwLength
+ put_u32le(&buffer[40], 10000); // dwQuality
- /* video-stream specific data */
- if (stream.type() == STREAMTYPE_VIDS)
+ // video-stream specific data
+ if (stream.type() == avi_stream::STREAMTYPE_VIDS)
{
- put_32bits(&buffer[4], /* fccHandler */
+ put_u32le(&buffer[4], // fccHandler
(stream.format() == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB);
- put_32bits(&buffer[36], /* dwSuggestedBufferSize */
+ put_u32le(&buffer[36], // dwSuggestedBufferSize
stream.width() * stream.height() * 4);
- put_16bits(&buffer[52], stream.width()); /* rcFrame.right */
- put_16bits(&buffer[54], stream.height()); /* rcFrame.bottom */
+ put_u16le(&buffer[52], stream.width()); // rcFrame.right
+ put_u16le(&buffer[54], stream.height()); // rcFrame.bottom
}
- /* audio-stream specific data */
- if (stream.type() == STREAMTYPE_AUDS)
+ // audio-stream specific data
+ if (stream.type() == avi_stream::STREAMTYPE_AUDS)
{
- put_32bits(&buffer[36], /* dwSuggestedBufferSize */
+ put_u32le(&buffer[36], // dwSuggestedBufferSize
stream.samplerate() * stream.bytes_per_sample());
- put_32bits(&buffer[44], /* dwSampleSize */
+ put_u32le(&buffer[44], // dwSampleSize
stream.bytes_per_sample());
}
- /* write the chunk */
+ // write the chunk
return chunk_overwrite(CHUNKTYPE_STRH, buffer, sizeof(buffer), stream.saved_strh_offset(), initial_write);
}
@@ -3171,45 +2909,45 @@ avi_file::error avi_file_impl::write_strh_chunk(avi_stream &stream, bool initial
avi_file::error avi_file_impl::write_strf_chunk(avi_stream const &stream)
{
- /* video stream */
- if (stream.type() == STREAMTYPE_VIDS)
+ // video stream
+ if (stream.type() == avi_stream::STREAMTYPE_VIDS)
{
std::uint8_t buffer[40];
- /* reset the buffer */
+ // reset the buffer
std::memset(buffer, 0, sizeof(buffer));
- put_32bits(&buffer[0], sizeof(buffer)); /* biSize */
- put_32bits(&buffer[4], stream.width()); /* biWidth */
- put_32bits(&buffer[8], stream.height()); /* biHeight */
- put_16bits(&buffer[12], 1); /* biPlanes */
- put_16bits(&buffer[14], stream.depth()); /* biBitCount */
- put_32bits(&buffer[16], stream.format()); /* biCompression */
- put_32bits(&buffer[20], /* biSizeImage */
+ put_u32le(&buffer[0], sizeof(buffer)); // biSize
+ put_u32le(&buffer[4], stream.width()); // biWidth
+ put_u32le(&buffer[8], stream.height()); // biHeight
+ put_u16le(&buffer[12], 1); // biPlanes
+ put_u16le(&buffer[14], stream.depth()); // biBitCount
+ put_u32le(&buffer[16], stream.format()); // biCompression
+ put_u32le(&buffer[20], // biSizeImage
stream.width() * stream.height() * (stream.depth() + 7) / 8);
- /* write the chunk */
+ // write the chunk
return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer));
}
- /* audio stream */
- if (stream.type() == STREAMTYPE_AUDS)
+ // audio stream
+ if (stream.type() == avi_stream::STREAMTYPE_AUDS)
{
std::uint8_t buffer[16];
- /* reset the buffer */
+ // reset the buffer
std::memset(buffer, 0, sizeof(buffer));
- put_16bits(&buffer[0], 1); /* wFormatTag */
- put_16bits(&buffer[2], stream.channels()); /* nChannels */
- put_32bits(&buffer[4], stream.samplerate()); /* nSamplesPerSec */
- put_32bits(&buffer[8], /* nAvgBytesPerSec */
+ put_u16le(&buffer[0], 1); // wFormatTag
+ put_u16le(&buffer[2], stream.channels()); // nChannels
+ put_u32le(&buffer[4], stream.samplerate()); // nSamplesPerSec
+ put_u32le(&buffer[8], // nAvgBytesPerSec
stream.samplerate() * stream.bytes_per_sample());
- put_16bits(&buffer[12], /* nBlockAlign */
+ put_u16le(&buffer[12], // nBlockAlign
stream.bytes_per_sample());
- put_16bits(&buffer[14], stream.samplebits()); /* wBitsPerSample */
+ put_u16le(&buffer[14], stream.samplebits()); // wBitsPerSample
- /* write the chunk */
+ // write the chunk
return chunk_write(CHUNKTYPE_STRF, buffer, sizeof(buffer));
}
@@ -3239,14 +2977,14 @@ avi_file::error avi_file_impl::write_indx_chunk(avi_stream &stream, bool initial
std::uint8_t buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4];
std::uint32_t master_entries = 0;
- /* reset the buffer */
+ // reset the buffer
std::memset(buffer, 0, sizeof(buffer));
- /* construct the chunk ID and index chunk ID */
+ // construct the chunk ID and index chunk ID
std::uint32_t const chunkid = get_chunkid_for_stream(&stream);
std::uint32_t const indexchunkid = AVI_FOURCC('i', 'x', '0' + (&stream - &m_streams[0]) / 10, '0' + (&stream - &m_streams[0]) % 10);
- /* loop over chunks of 4GB and write out indexes for them first */
+ // loop over chunks of 4GB and write out indexes for them first
if (!initial_write && m_riffbase != 0)
{
for (std::uint64_t currentbase = 0; currentbase < m_writeoffs; currentbase += FOUR_GB)
@@ -3255,67 +2993,70 @@ avi_file::error avi_file_impl::write_indx_chunk(avi_stream &stream, bool initial
std::uint32_t chunks_this_index = 0;
std::uint32_t bytes_this_index = 0;
- /* count chunks that are in this region */
+ // count chunks that are in this region
for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++)
if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend)
chunks_this_index++;
- /* if no chunks, skip */
+ // if no chunks, skip
if (chunks_this_index == 0)
continue;
- /* allocate memory */
+ if (master_entries >= MAX_AVI_SIZE_IN_GB / 4)
+ return error::WRITE_ERROR;
+
+ // allocate memory
std::unique_ptr<std::uint8_t []> tempbuf;
try { tempbuf.reset(new std::uint8_t[24 + 8 * chunks_this_index]); }
catch (...) { return error::NO_MEMORY; }
std::memset(&tempbuf[0], 0, 24 + 8 * chunks_this_index);
- /* make a regular index */
- put_16bits(&tempbuf[0], 2); /* wLongsPerEntry */
- tempbuf[2] = 0; /* bIndexSubType */
- tempbuf[3] = AVI_INDEX_OF_CHUNKS; /* bIndexType */
- put_32bits(&tempbuf[4], chunks_this_index); /* nEntriesInUse */
- put_32bits(&tempbuf[8], chunkid); /* dwChunkId */
- put_64bits(&tempbuf[12], currentbase); /* qwBaseOffset */
+ // make a regular index
+ put_u16le(&tempbuf[0], 2); // wLongsPerEntry
+ tempbuf[2] = 0; // bIndexSubType
+ tempbuf[3] = AVI_INDEX_OF_CHUNKS; // bIndexType
+ put_u32le(&tempbuf[4], chunks_this_index); // nEntriesInUse
+ put_u32le(&tempbuf[8], chunkid); // dwChunkId
+ put_u64le(&tempbuf[12], currentbase); // qwBaseOffset
- /* now fill in the indexes */
+ // now fill in the indexes
chunks_this_index = 0;
for (std::uint32_t chunknum = 0; chunknum < stream.chunks(); chunknum++)
if (stream.chunk(chunknum).offset >= currentbase && stream.chunk(chunknum).offset < currentend)
{
- put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream.chunk(chunknum).offset + 8 - currentbase);
- put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream.chunk(chunknum).length - 8);
+ put_u32le(&tempbuf[24 + 8 * chunks_this_index + 0], stream.chunk(chunknum).offset + 8 - currentbase);
+ put_u32le(&tempbuf[24 + 8 * chunks_this_index + 4], stream.chunk(chunknum).length - 8);
bytes_this_index += stream.chunk(chunknum).length;
chunks_this_index++;
}
- /* write the offset of this index to the master table */
- put_64bits(&buffer[24 + 16 * master_entries + 0], m_writeoffs);
- put_32bits(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8);
- if (stream.type() == STREAMTYPE_VIDS)
- put_32bits(&buffer[24 + 16 * master_entries + 12], chunks_this_index);
- else if (stream.type() == STREAMTYPE_AUDS)
- put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / stream.bytes_per_sample());
+ // write the offset of this index to the master table
+ put_u64le(&buffer[24 + 16 * master_entries + 0], m_writeoffs);
+ put_u32le(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8);
+ if (stream.type() == avi_stream::STREAMTYPE_VIDS)
+ put_u32le(&buffer[24 + 16 * master_entries + 12], chunks_this_index);
+ else if (stream.type() == avi_stream::STREAMTYPE_AUDS)
+ put_u32le(&buffer[24 + 16 * master_entries + 12], bytes_this_index / stream.bytes_per_sample());
master_entries++;
- /* write the index */
+ // write the index
error const avierr = chunk_write(indexchunkid, &tempbuf[0], 24 + 8 * chunks_this_index);
if (avierr != error::NONE)
return avierr;
}
}
- /* build up the master index */
+ // build up the master index
if (master_entries != 0)
{
- put_16bits(&buffer[0], 4); /* wLongsPerEntry */
- buffer[2] = 0; /* bIndexSubType */
- buffer[3] = AVI_INDEX_OF_INDEXES; /* bIndexType */
- put_32bits(&buffer[4], master_entries); /* nEntriesInUse */
- put_32bits(&buffer[8], chunkid); /* dwChunkId */
+ put_u16le(&buffer[0], 4); // wLongsPerEntry
+ buffer[2] = 0; // bIndexSubType
+ buffer[3] = AVI_INDEX_OF_INDEXES; // bIndexType
+ put_u32le(&buffer[4], master_entries); // nEntriesInUse
+ put_u32le(&buffer[8], chunkid); // dwChunkId
}
- /* (over)write the chunk */
+ // (over)write the chunk
return chunk_overwrite((master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), stream.saved_indx_offset(), initial_write);
}
@@ -3339,18 +3080,18 @@ avi_file::error avi_file_impl::write_idx1_chunk()
std::uint32_t const tempbuflength = compute_idx1_size() - 8;
std::uint32_t curchunk[2] = { 0 };
- /* allocate a temporary buffer */
+ // allocate a temporary buffer
std::unique_ptr<std::uint8_t []> tempbuf;
try { tempbuf.reset(new std::uint8_t[tempbuflength]); }
catch (...) { return error::NO_MEMORY; }
- /* fill it in */
+ // fill it in
for (std::uint32_t curoffset = 0; curoffset < tempbuflength; curoffset += 16)
{
std::uint64_t minoffset = ~std::uint64_t(0);
int minstr = 0;
- /* determine which stream has the next chunk */
+ // determine which stream has the next chunk
for (int strnum = 0; strnum < m_streams.size(); strnum++)
if (curchunk[strnum] < m_streams[strnum].chunks() && m_streams[strnum].chunk(curchunk[strnum]).offset < minoffset)
{
@@ -3358,17 +3099,17 @@ avi_file::error avi_file_impl::write_idx1_chunk()
minstr = strnum;
}
- /* make an entry for this index */
- put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(&m_streams[minstr]));
- put_32bits(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */);
- put_32bits(&tempbuf[curoffset + 8], minoffset - (m_saved_movi_offset + 8));
- put_32bits(&tempbuf[curoffset + 12], m_streams[minstr].chunk(curchunk[minstr]).length - 8);
+ // make an entry for this index
+ put_u32le(&tempbuf[curoffset + 0], get_chunkid_for_stream(&m_streams[minstr]));
+ put_u32le(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */);
+ put_u32le(&tempbuf[curoffset + 8], minoffset - (m_saved_movi_offset + 8));
+ put_u32le(&tempbuf[curoffset + 12], m_streams[minstr].chunk(curchunk[minstr]).length - 8);
- /* advance the chunk counter for this stream */
+ // advance the chunk counter for this stream
curchunk[minstr]++;
}
- /* write the chunk */
+ // write the chunk
return chunk_write(CHUNKTYPE_IDX1, &tempbuf[0], tempbuflength);
}
@@ -3394,23 +3135,23 @@ avi_file::error avi_file_impl::soundbuf_initialize()
avi_stream *const audio = get_audio_stream(0, offset);
avi_stream *const video = get_video_stream();
- /* we require a video stream */
+ // we require a video stream
if (video == nullptr)
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* skip if no audio stream */
+ // skip if no audio stream
if (audio == nullptr)
return error::NONE;
- /* determine the number of samples we want in our buffer; 2 seconds should be enough */
+ // determine the number of samples we want in our buffer; 2 seconds should be enough
m_soundbuf_samples = m_info.audio_samplerate * SOUND_BUFFER_MSEC / 1000;
- /* allocate a buffer */
+ // allocate a buffer
m_soundbuf.clear();
try { m_soundbuf.resize(m_soundbuf_samples * m_info.audio_channels, 0); }
catch (...) { return error::NO_MEMORY; }
- /* determine the number of frames to be ahead (0.75secs) */
+ // determine the number of frames to be ahead (0.75secs)
m_soundbuf_frames = (std::uint64_t(video->rate()) * 75) / (std::uint64_t(video->scale()) * 100) + 1;
return error::NONE;
}
@@ -3438,23 +3179,23 @@ avi_file::error avi_file_impl::soundbuf_write_chunk(std::uint32_t framenum)
avi_stream *const stream = get_audio_stream(0, offset);
std::uint32_t length;
- /* skip if no audio stream */
+ // skip if no audio stream
if (stream == nullptr)
return error::NONE;
- /* determine the length of this chunk */
+ // determine the length of this chunk
if (framenum == 0)
length = framenum_to_samplenum(m_soundbuf_frames);
else
length = framenum_to_samplenum(framenum + 1 + m_soundbuf_frames) - framenum_to_samplenum(framenum + m_soundbuf_frames);
length *= stream->channels() * sizeof(std::int16_t);
- /* then do the initial write */
+ // then do the initial write
error const avierr = chunk_write(get_chunkid_for_stream(stream), &m_soundbuf[0], length);
if (avierr != error::NONE)
return avierr;
- /* set the info for this new chunk */
+ // set the info for this new chunk
return stream->set_chunk_info(stream->chunks(), m_writeoffs - length - 8, length + 8);
}
@@ -3486,30 +3227,30 @@ avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full)
std::uint32_t chunknum;
std::uint32_t chunkid;
- /* skip if no stream */
+ // skip if no stream
if (stream == nullptr)
return error::NONE;
- /* get the chunk ID for this stream */
+ // get the chunk ID for this stream
chunkid = get_chunkid_for_stream(stream);
std::uint32_t const bytes_per_sample = stream->channels() * sizeof(std::int16_t);
finalchunks = stream->chunks();
- /* find out how many samples we've accumulated */
+ // find out how many samples we've accumulated
for (int channel = 0; channel < stream->channels(); channel++)
channelsamples = (std::min<std::int32_t>)(channelsamples, m_soundbuf_chansamples[channel]);
- /* loop over pending sound chunks */
+ // loop over pending sound chunks
for (chunknum = m_soundbuf_chunks; chunknum < stream->chunks(); chunknum++)
{
avi_chunk_list &chunk = stream->chunk(chunknum);
std::uint32_t const chunksamples = (chunk.length - 8) / bytes_per_sample;
- /* stop if we don't have enough to satisfy this chunk */
+ // stop if we don't have enough to satisfy this chunk
if (only_flush_full && channelsamples < chunksamples)
break;
- /* if we don't have all the samples we need, pad with 0's */
+ // if we don't have all the samples we need, pad with 0's
if (channelsamples > 0 && channelsamples < chunksamples)
{
if (processedsamples + chunksamples > m_soundbuf_samples)
@@ -3517,7 +3258,7 @@ avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full)
std::memset(&m_soundbuf[(processedsamples + channelsamples) * stream->channels()], 0, (chunksamples - channelsamples) * bytes_per_sample);
}
- /* if we're completely out of samples, clear the buffer entirely and use the end */
+ // if we're completely out of samples, clear the buffer entirely and use the end
else if (channelsamples <= 0)
{
processedsamples = m_soundbuf_samples - chunksamples;
@@ -3526,49 +3267,50 @@ avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full)
finalchunks--;
}
- /* copy the sample data in */
+ // copy the sample data in
avierr = chunk_overwrite(chunkid, &m_soundbuf[processedsamples * stream->channels()], chunk.length - 8, chunk.offset, false);
if (avierr != error::NONE)
return avierr;
- /* add up the samples */
+ // add up the samples
if (channelsamples > chunksamples)
m_info.audio_numsamples = stream->add_samples(chunksamples);
else if (channelsamples > 0)
m_info.audio_numsamples = stream->add_samples(channelsamples);
- /* advance past those */
+ // advance past those
processedsamples += chunksamples;
channelsamples -= chunksamples;
channelsamples = (std::max)(0, channelsamples);
}
- /* if we have a non-zero offset, shift everything down */
+ // if we have a non-zero offset, shift everything down
if (processedsamples > 0)
{
- /* first account for the samples we processed */
- std::memmove(&m_soundbuf[0], &m_soundbuf[processedsamples * stream->channels()], (m_soundbuf_samples - processedsamples) * bytes_per_sample);
+ // first account for the samples we processed
+ if (m_soundbuf_samples > processedsamples)
+ std::memmove(&m_soundbuf[0], &m_soundbuf[processedsamples * stream->channels()], (m_soundbuf_samples - processedsamples) * bytes_per_sample);
for (int channel = 0; channel < stream->channels(); channel++)
m_soundbuf_chansamples[channel] -= processedsamples;
}
- /* update the final chunk count */
+ // update the final chunk count
if (!only_flush_full)
stream->set_chunks(finalchunks);
- /* account for flushed chunks */
+ // account for flushed chunks
m_soundbuf_chunks = chunknum;
return error::NONE;
}
/*-------------------------------------------------
- printf_chunk_recursive - print information
+ display_chunk_recursive - print information
about a chunk recursively
-------------------------------------------------*/
/**
- * @fn static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent)
+ * @fn static void display_chunk_recursive(avi_file *file, avi_chunk *container, int indent)
*
* @brief Printf chunk recursive.
*
@@ -3577,34 +3319,33 @@ avi_file::error avi_file_impl::soundbuf_flush(bool only_flush_full)
* @param indent The indent.
*/
-void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int indent)
+void avi_file_impl::display_chunk_recursive(avi_chunk const *container, int indent)
{
- char size[20], offset[20];
avi_chunk curchunk;
error avierr;
- /* iterate over chunks in this container */
+ // iterate over chunks in this container
for (avierr = get_first_chunk(container, curchunk); avierr == error::NONE; avierr = get_next_chunk(container, curchunk))
{
std::uint32_t chunksize = curchunk.size;
bool recurse = false;
- u64toa(curchunk.size, size);
- u64toa(curchunk.offset, offset);
- printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "",
+ util::stream_format(std::cout, "%*schunk = %c%c%c%c, size=%X (%X)\n",
+ indent, "",
std::uint8_t(curchunk.type >> 0),
std::uint8_t(curchunk.type >> 8),
std::uint8_t(curchunk.type >> 16),
std::uint8_t(curchunk.type >> 24),
- size, offset);
+ curchunk.size,
+ curchunk.offset);
- /* certain chunks are just containers; recurse into them */
+ // certain chunks are just containers; recurse into them
switch (curchunk.type)
{
- /* basic containers */
+ // basic containers
case CHUNKTYPE_RIFF:
case CHUNKTYPE_LIST:
- printf("%*stype = %c%c%c%c\n", indent, "",
+ util::stream_format(std::cout, "%*stype = %c%c%c%c\n", indent, "",
std::uint8_t(curchunk.listtype >> 0),
std::uint8_t(curchunk.listtype >> 8),
std::uint8_t(curchunk.listtype >> 16),
@@ -3614,34 +3355,34 @@ void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int inden
break;
}
- /* print data within the chunk */
+ // print data within the chunk
if (chunksize > 0 && curchunk.size < 1024 * 1024)
{
std::unique_ptr<std::uint8_t []> data;
- /* read the data for a chunk */
+ // read the data for a chunk
avierr = read_chunk_data(curchunk, data);
if (avierr == error::NONE)
{
- std::uint32_t const bytes = (std::min)(std::uint32_t(512), chunksize);
+ auto bytes = std::min<std::uint32_t>(512, chunksize);
for (std::uint32_t i = 0; i < bytes; i++)
{
- if (i % 16 == 0) printf("%*s ", indent, "");
- printf("%02X ", data[i]);
- if (i % 16 == 15) printf("\n");
+ if (i % 16 == 0) util::stream_format(std::cout, "%*s ", indent, "");
+ util::stream_format(std::cout, "%02X ", data[i]);
+ if (i % 16 == 15) std::cout << "\n";
}
- if (chunksize % 16 != 0) printf("\n");
+ if (chunksize % 16 != 0) std::cout << "\n";
}
}
- /* if we're recursing, dive down */
+ // if we're recursing, dive down
if (recurse)
- printf_chunk_recursive(&curchunk, indent + 4);
+ display_chunk_recursive(&curchunk, indent + 4);
}
- /* if we didn't get a legitimate error, indicate that */
+ // if we didn't get a legitimate error, indicate that
if (avierr != error::END)
- printf("[chunk error %d]\n", int(avierr));
+ std::cout << "[chunk error " << int(avierr) << "]\n";
}
} // anonymous namespace
@@ -3669,18 +3410,18 @@ void avi_file_impl::printf_chunk_recursive(avi_chunk const *container, int inden
avi_file::error avi_file::open(std::string const &filename, ptr &file)
{
- /* open the file */
+ // open the file
osd_file::ptr f;
std::uint64_t length;
- if (osd_file::open(filename, OPEN_FLAG_READ, f, length) != osd_file::error::NONE)
+ if (osd_file::open(filename, OPEN_FLAG_READ, f, length))
return error::CANT_OPEN_FILE;
- /* allocate the file */
+ // allocate the file
std::unique_ptr<avi_file_impl> newfile;
try { newfile = std::make_unique<avi_file_impl>(std::move(f), length); }
catch (...) { return error::NO_MEMORY; }
- /* parse the data */
+ // parse the data
error const avierr = newfile->read_movie_data();
if (avierr != error::NONE)
return avierr;
@@ -3708,7 +3449,7 @@ avi_file::error avi_file::open(std::string const &filename, ptr &file)
avi_file::error avi_file::create(std::string const &filename, movie_info const &info, ptr &file)
{
- /* validate video info */
+ // validate video info
if ((info.video_format != 0 && info.video_format != FORMAT_UYVY && info.video_format != FORMAT_VYUY && info.video_format != FORMAT_YUY2) ||
(info.video_width == 0) ||
(info.video_height == 0) ||
@@ -3716,20 +3457,20 @@ avi_file::error avi_file::create(std::string const &filename, movie_info const &
(info.video_depth % 8 != 0))
return error::UNSUPPORTED_VIDEO_FORMAT;
- /* validate audio info */
+ // validate audio info
if (info.audio_format != 0 ||
info.audio_channels > MAX_SOUND_CHANNELS ||
info.audio_samplebits != 16)
return error::UNSUPPORTED_AUDIO_FORMAT;
- /* open the file */
+ // open the file
osd_file::ptr f;
std::uint64_t length;
- osd_file::error const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length);
- if (filerr != osd_file::error::NONE)
+ std::error_condition const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length);
+ if (filerr)
return error::CANT_OPEN_FILE;
- /* allocate the file */
+ // allocate the file
error avierr;
std::unique_ptr<avi_file_impl> newfile;
try { newfile = std::make_unique<avi_file_impl>(std::move(f), info); }
@@ -3739,12 +3480,12 @@ avi_file::error avi_file::create(std::string const &filename, movie_info const &
goto error;
}
- /* initialize the sound buffering */
+ // initialize the sound buffering
avierr = newfile->soundbuf_initialize();
if (avierr != error::NONE)
goto error;
- /* write the initial headers */
+ // write the initial headers
avierr = newfile->write_initial_headers();
if (avierr != error::NONE)
goto error;
diff --git a/src/lib/util/aviio.h b/src/lib/util/aviio.h
index c951f71b22f..f7ac7bccff8 100644
--- a/src/lib/util/aviio.h
+++ b/src/lib/util/aviio.h
@@ -11,7 +11,6 @@
#ifndef MAME_LIB_UTIL_AVIIO_H
#define MAME_LIB_UTIL_AVIIO_H
-#include "osdcore.h"
#include "bitmap.h"
#include <cstdint>
@@ -20,20 +19,13 @@
/***************************************************************************
- MACROS
+ INLINE FUNCTIONS
***************************************************************************/
-#define AVI_FOURCC(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
-
-#define FORMAT_UYVY AVI_FOURCC('U','Y','V','Y')
-#define FORMAT_VYUY AVI_FOURCC('V','Y','U','Y')
-#define FORMAT_YUY2 AVI_FOURCC('Y','U','Y','2')
-#define FORMAT_HFYU AVI_FOURCC('H','F','Y','U')
-#define FORMAT_I420 AVI_FOURCC('I','4','2','0')
-#define FORMAT_DIB AVI_FOURCC('D','I','B',' ')
-#define FORMAT_RGB AVI_FOURCC('R','G','B',' ')
-#define FORMAT_RAW AVI_FOURCC('R','A','W',' ')
-#define FORMAT_UNCOMPRESSED 0x00000000
+inline constexpr std::uint32_t AVI_FOURCC(char a, char b, char c, char d)
+{
+ return std::uint32_t(a) | (std::uint32_t(b) << 8) | (std::uint32_t(c) << 16) | (std::uint32_t(d) << 24);
+}
class avi_file
@@ -44,6 +36,19 @@ public:
CONSTANTS
***********************************************************************/
+ enum : std::uint32_t
+ {
+ FORMAT_UYVY = AVI_FOURCC('U','Y','V','Y'),
+ FORMAT_VYUY = AVI_FOURCC('V','Y','U','Y'),
+ FORMAT_YUY2 = AVI_FOURCC('Y','U','Y','2'),
+ FORMAT_HFYU = AVI_FOURCC('H','F','Y','U'),
+ FORMAT_I420 = AVI_FOURCC('I','4','2','0'),
+ FORMAT_DIB = AVI_FOURCC('D','I','B',' '),
+ FORMAT_RGB = AVI_FOURCC('R','G','B',' '),
+ FORMAT_RAW = AVI_FOURCC('R','A','W',' '),
+ FORMAT_UNCOMPRESSED = 0x00000000
+ };
+
enum class error
{
NONE = 0,
@@ -113,7 +118,7 @@ public:
static error create(std::string const &filename, movie_info const &info, ptr &file);
virtual ~avi_file();
- virtual void printf_chunks() = 0;
+ virtual void display_chunks() = 0;
static const char *error_string(error err);
virtual movie_info const &get_movie_info() const = 0;
diff --git a/src/lib/util/bitmap.cpp b/src/lib/util/bitmap.cpp
index 87cf8a27b76..8287009311d 100644
--- a/src/lib/util/bitmap.cpp
+++ b/src/lib/util/bitmap.cpp
@@ -8,14 +8,13 @@
***************************************************************************/
-#include <assert.h>
-
#include "bitmap.h"
+#include <cassert>
+#include <cstring>
#include <new>
-
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
@@ -24,7 +23,7 @@
// compute_rowpixels - compute a rowpixels value
//-------------------------------------------------
-inline int32_t bitmap_t::compute_rowpixels(int width, int xslop)
+inline int32_t bitmap_t::compute_rowpixels(int width, int xslop) noexcept
{
return width + 2 * xslop;
}
@@ -35,9 +34,9 @@ inline int32_t bitmap_t::compute_rowpixels(int width, int xslop)
// with the given slop values
//-------------------------------------------------
-inline void bitmap_t::compute_base(int xslop, int yslop)
+inline void bitmap_t::compute_base(int xslop, int yslop) noexcept
{
- m_base = m_alloc.get() + (m_rowpixels * yslop + xslop) * (m_bpp / 8);
+ m_base = &m_alloc[(m_rowpixels * yslop + xslop) * (m_bpp / 8)];
}
@@ -46,7 +45,7 @@ inline void bitmap_t::compute_base(int xslop, int yslop)
// is valid and agrees with the BPP
//-------------------------------------------------
-inline bool bitmap_t::valid_format() const
+inline bool bitmap_t::valid_format() const noexcept
{
switch (m_format)
{
@@ -82,7 +81,7 @@ inline bool bitmap_t::valid_format() const
// BITMAP ALLOCATION/CONFIGURATION
//**************************************************************************
-bitmap_t::bitmap_t(bitmap_t &&that)
+bitmap_t::bitmap_t(bitmap_t &&that) noexcept
: m_alloc(std::move(that.m_alloc))
, m_allocbytes(that.m_allocbytes)
, m_base(that.m_base)
@@ -113,7 +112,7 @@ bitmap_t::bitmap_t(bitmap_t &&that)
* @param yslop The yslop.
*/
-bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, int width, int height, int xslop, int yslop)
+bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, int width, int height, int xslop, int yslop) noexcept
: m_alloc()
, m_allocbytes(0)
, m_format(format)
@@ -139,7 +138,7 @@ bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, int width, int height, int
* @param rowpixels The rowpixels.
*/
-bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, void *base, int width, int height, int rowpixels)
+bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, void *base, int width, int height, int rowpixels) noexcept
: m_alloc()
, m_allocbytes(0)
, m_base(base)
@@ -165,7 +164,7 @@ bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, void *base, int width, int
* @param subrect The subrect.
*/
-bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, bitmap_t &source, const rectangle &subrect)
+bitmap_t::bitmap_t(bitmap_format format, uint8_t bpp, bitmap_t &source, const rectangle &subrect) noexcept
: m_alloc()
, m_allocbytes(0)
, m_base(source.raw_pixptr(subrect.top(), subrect.left()))
@@ -196,7 +195,7 @@ bitmap_t::~bitmap_t()
reset();
}
-bitmap_t &bitmap_t::operator=(bitmap_t &&that)
+bitmap_t &bitmap_t::operator=(bitmap_t &&that) noexcept
{
m_alloc = std::move(that.m_alloc);
m_allocbytes = that.m_allocbytes;
@@ -226,7 +225,7 @@ bitmap_t &bitmap_t::operator=(bitmap_t &&that)
* @param yslop The yslop.
*/
-void bitmap_t::allocate(int width, int height, int xslop, int yslop)
+void bitmap_t::allocate(int width, int height, int xslop, int yslop) noexcept
{
assert(m_format != BITMAP_FORMAT_INVALID);
assert(m_bpp == 8 || m_bpp == 16 || m_bpp == 32 || m_bpp == 64);
@@ -235,24 +234,28 @@ void bitmap_t::allocate(int width, int height, int xslop, int yslop)
reset();
// handle empty requests cleanly
- if (width <= 0 || height <= 0)
- return;
-
- // initialize fields
- m_rowpixels = compute_rowpixels(width, xslop);
- m_width = width;
- m_height = height;
- m_cliprect.set(0, width - 1, 0, height - 1);
-
- // allocate memory for the bitmap itself
- m_allocbytes = m_rowpixels * (m_height + 2 * yslop) * m_bpp / 8;
- m_alloc.reset(new uint8_t[m_allocbytes]);
-
- // clear to 0 by default
- memset(m_alloc.get(), 0, m_allocbytes);
-
- // compute the base
- compute_base(xslop, yslop);
+ if ((0 < width) && (0 < height))
+ {
+ // allocate memory for the bitmap itself
+ int32_t const new_rowpixels = compute_rowpixels(width, xslop);
+ uint32_t const new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
+ m_alloc.reset(new (std::nothrow) uint8_t[new_allocbytes]);
+ if (m_alloc)
+ {
+ // initialize fields
+ m_allocbytes = new_allocbytes;
+ m_rowpixels = new_rowpixels;
+ m_width = width;
+ m_height = height;
+ m_cliprect.set(0, width - 1, 0, height - 1);
+
+ // clear to 0 by default
+ memset(m_alloc.get(), 0, m_allocbytes);
+
+ // compute the base
+ compute_base(xslop, yslop);
+ }
+ }
}
/**
@@ -269,36 +272,41 @@ void bitmap_t::allocate(int width, int height, int xslop, int yslop)
* @param yslop The yslop.
*/
-void bitmap_t::resize(int width, int height, int xslop, int yslop)
+void bitmap_t::resize(int width, int height, int xslop, int yslop) noexcept
{
assert(m_format != BITMAP_FORMAT_INVALID);
assert(m_bpp == 8 || m_bpp == 16 || m_bpp == 32 || m_bpp == 64);
// handle empty requests cleanly
- if (width <= 0 || height <= 0)
+ if ((width <= 0) || (height <= 0))
width = height = 0;
// determine how much memory we need for the new bitmap
- int new_rowpixels = compute_rowpixels(width, xslop);
- uint32_t new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
+ int32_t const new_rowpixels = compute_rowpixels(width, xslop);
+ uint32_t const new_allocbytes = new_rowpixels * (height + 2 * yslop) * m_bpp / 8;
- // if we need more memory, just realloc
if (new_allocbytes > m_allocbytes)
{
- palette_t *palette = m_palette;
+ // if we need more memory, just realloc
+ palette_t *const palette = m_palette;
+ if (palette)
+ palette->ref();
allocate(width, height, xslop, yslop);
set_palette(palette);
- return;
+ if (palette)
+ palette->deref();
+ }
+ else
+ {
+ // otherwise, reconfigure
+ m_rowpixels = new_rowpixels;
+ m_width = width;
+ m_height = height;
+ m_cliprect.set(0, width - 1, 0, height - 1);
+
+ // re-compute the base
+ compute_base(xslop, yslop);
}
-
- // otherwise, reconfigure
- m_rowpixels = new_rowpixels;
- m_width = width;
- m_height = height;
- m_cliprect.set(0, width - 1, 0, height - 1);
-
- // re-compute the base
- compute_base(xslop, yslop);
}
/**
@@ -309,7 +317,7 @@ void bitmap_t::resize(int width, int height, int xslop, int yslop)
* -------------------------------------------------.
*/
-void bitmap_t::reset()
+void bitmap_t::reset() noexcept
{
// delete any existing stuff
set_palette(nullptr);
@@ -317,6 +325,7 @@ void bitmap_t::reset()
m_base = nullptr;
// reset all fields
+ m_allocbytes = 0;
m_rowpixels = 0;
m_width = 0;
m_height = 0;
@@ -336,8 +345,11 @@ void bitmap_t::reset()
* @param rowpixels The rowpixels.
*/
-void bitmap_t::wrap(void *base, int width, int height, int rowpixels)
+void bitmap_t::wrap(void *base, int width, int height, int rowpixels) noexcept
{
+ assert(base || (!width && !height));
+ assert(!m_alloc || (&m_alloc[0] > base) || (&m_alloc[m_allocbytes] <= base));
+
// delete any existing stuff
reset();
@@ -361,8 +373,9 @@ void bitmap_t::wrap(void *base, int width, int height, int rowpixels)
* @param subrect The subrect.
*/
-void bitmap_t::wrap(const bitmap_t &source, const rectangle &subrect)
+void bitmap_t::wrap(bitmap_t &source, const rectangle &subrect) noexcept
{
+ assert(&source != this);
assert(m_format == source.m_format);
assert(m_bpp == source.m_bpp);
assert(source.cliprect().contains(subrect));
@@ -389,121 +402,69 @@ void bitmap_t::wrap(const bitmap_t &source, const rectangle &subrect)
* @param [in,out] palette If non-null, the palette.
*/
-void bitmap_t::set_palette(palette_t *palette)
+void bitmap_t::set_palette(palette_t *palette) noexcept
{
- // first dereference any existing palette
- if (m_palette != nullptr)
- {
+ // first reference the new palette
+ if (palette)
+ palette->ref();
+
+ // then dereference any existing palette
+ if (m_palette)
m_palette->deref();
- m_palette = nullptr;
- }
- // then reference any new palette
- if (palette != nullptr)
- {
- palette->ref();
- m_palette = palette;
- }
+ m_palette = palette;
}
/**
- * @fn void bitmap_t::fill(uint32_t color, const rectangle &cliprect)
+ * @fn void bitmap_t::fill(uint64_t color, const rectangle &bounds)
*
* @brief -------------------------------------------------
* fill -- fill a bitmap with a solid color
* -------------------------------------------------.
*
* @param color The color.
- * @param cliprect The cliprect.
+ * @param bounds The bounds.
*/
-void bitmap_t::fill(uint32_t color, const rectangle &cliprect)
+void bitmap_t::fill(uint64_t color, const rectangle &bounds) noexcept
{
// if we have a cliprect, intersect with that
- rectangle fill = cliprect;
+ rectangle fill(bounds);
fill &= m_cliprect;
- if (fill.empty())
- return;
-
- // based on the bpp go from there
- switch (m_bpp)
+ if (!fill.empty())
{
+ // based on the bpp go from there
+ switch (m_bpp)
+ {
case 8:
- // 8bpp always uses memset
for (int32_t y = fill.top(); y <= fill.bottom(); y++)
- memset(raw_pixptr(y, fill.left()), uint8_t(color), fill.width());
+ std::fill_n(&pixt<uint8_t>(y, fill.left()), fill.width(), uint8_t(color));
break;
case 16:
- // 16bpp can use memset if the bytes are equal
- if (uint8_t(color >> 8) == uint8_t(color))
- {
- for (int32_t y = fill.top(); y <= fill.bottom(); y++)
- memset(raw_pixptr(y, fill.left()), uint8_t(color), fill.width() * 2);
- }
- else
- {
- // Fill the first line the hard way
- uint16_t *destrow = &pixt<uint16_t>(fill.top());
- for (int32_t x = fill.left(); x <= fill.right(); x++)
- destrow[x] = uint16_t(color);
-
- // For the other lines, just copy the first one
- void *destrow0 = &pixt<uint16_t>(fill.top(), fill.left());
- for (int32_t y = fill.top() + 1; y <= fill.bottom(); y++)
- {
- destrow = &pixt<uint16_t>(y, fill.left());
- memcpy(destrow, destrow0, fill.width() * 2);
- }
- }
+ for (int32_t y = fill.top(); y <= fill.bottom(); ++y)
+ std::fill_n(&pixt<uint16_t>(y, fill.left()), fill.width(), uint16_t(color));
break;
case 32:
- // 32bpp can use memset if the bytes are equal
- if ((uint8_t)(color >> 8) == (uint8_t)color && (uint16_t)(color >> 16) == (uint16_t)color)
- {
- for (int32_t y = fill.top(); y <= fill.bottom(); y++)
- memset(&pixt<uint32_t>(y, fill.left()), uint8_t(color), fill.width() * 4);
- }
- else
- {
- // Fill the first line the hard way
- uint32_t *destrow = &pixt<uint32_t>(fill.top());
- for (int32_t x = fill.left(); x <= fill.right(); x++)
- destrow[x] = uint32_t(color);
-
- // For the other lines, just copy the first one
- uint32_t *destrow0 = &pixt<uint32_t>(fill.top(), fill.left());
- for (int32_t y = fill.top() + 1; y <= fill.bottom(); y++)
- {
- destrow = &pixt<uint32_t>(y, fill.left());
- memcpy(destrow, destrow0, fill.width() * 4);
- }
- }
+ for (int32_t y = fill.top(); y <= fill.bottom(); ++y)
+ std::fill_n(&pixt<uint32_t>(y, fill.left()), fill.width(), uint32_t(color));
break;
case 64:
- // 64bpp can use memset if the bytes are equal
- if (uint8_t(color >> 8) == uint8_t(color) && uint16_t(color >> 16) == uint16_t(color)) // FIXME: really? wat about the upper bits that would be zeroed when done the "hard way"?
- {
- for (int32_t y = fill.top(); y <= fill.bottom(); y++)
- memset(&pixt<uint64_t>(y, fill.left()), uint8_t(color), fill.width() * 8);
- }
- else
- {
- // Fill the first line the hard way
- uint64_t *destrow = &pixt<uint64_t>(fill.top());
- for (int32_t x = fill.left(); x <= fill.right(); x++)
- destrow[x] = uint64_t(color);
-
- // For the other lines, just copy the first one
- uint64_t *destrow0 = &pixt<uint64_t>(fill.top(), fill.left());
- for (int32_t y = fill.top() + 1; y <= fill.bottom(); y++)
- {
- destrow = &pixt<uint64_t>(y, fill.left());
- memcpy(destrow, destrow0, fill.width() * 8);
- }
- }
+ for (int32_t y = fill.top(); y <= fill.bottom(); ++y)
+ std::fill_n(&pixt<uint64_t>(y, fill.left()), fill.width(), uint64_t(color));
break;
+ }
}
}
+
+
+//**************************************************************************
+// EXPLICIT TEMPLATE INSTANTIATIONS
+//**************************************************************************
+
+template class bitmap_specific<uint8_t>;
+template class bitmap_specific<uint16_t>;
+template class bitmap_specific<uint32_t>;
+template class bitmap_specific<uint64_t>;
diff --git a/src/lib/util/bitmap.h b/src/lib/util/bitmap.h
index ed6a5382a61..56c4328d5ed 100644
--- a/src/lib/util/bitmap.h
+++ b/src/lib/util/bitmap.h
@@ -13,9 +13,11 @@
#pragma once
-#include "osdcore.h"
#include "palette.h"
+#include <algorithm>
+#include <memory>
+
//**************************************************************************
// TYPE DEFINITIONS
@@ -24,7 +26,7 @@
// bitmap_format describes the various bitmap formats we use
enum bitmap_format
{
- BITMAP_FORMAT_INVALID = 0, // invalid forma
+ BITMAP_FORMAT_INVALID = 0, // invalid format
BITMAP_FORMAT_IND8, // 8bpp indexed
BITMAP_FORMAT_IND16, // 16bpp indexed
BITMAP_FORMAT_IND32, // 32bpp indexed
@@ -73,6 +75,20 @@ public:
return *this;
}
+ rectangle operator&(const rectangle &b)
+ {
+ rectangle a(*this);
+ a &= b;
+ return a;
+ }
+
+ rectangle operator|(const rectangle &b)
+ {
+ rectangle a(*this);
+ a |= b;
+ return a;
+ }
+
// comparisons
constexpr bool operator==(const rectangle &rhs) const { return min_x == rhs.min_x && max_x == rhs.max_x && min_y == rhs.min_y && max_y == rhs.max_y; }
constexpr bool operator!=(const rectangle &rhs) const { return min_x != rhs.min_x || max_x != rhs.max_x || min_y != rhs.min_y || max_y != rhs.max_y; }
@@ -120,60 +136,60 @@ class bitmap_t
protected:
// construction/destruction -- subclasses only to ensure type correctness
bitmap_t(const bitmap_t &) = delete;
- bitmap_t(bitmap_t &&that);
- bitmap_t(bitmap_format format, uint8_t bpp, int width = 0, int height = 0, int xslop = 0, int yslop = 0);
- bitmap_t(bitmap_format format, uint8_t bpp, void *base, int width, int height, int rowpixels);
- bitmap_t(bitmap_format format, uint8_t bpp, bitmap_t &source, const rectangle &subrect);
+ bitmap_t(bitmap_t &&that) noexcept;
+ bitmap_t(bitmap_format format, uint8_t bpp, int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept;
+ bitmap_t(bitmap_format format, uint8_t bpp, void *base, int width, int height, int rowpixels) noexcept;
+ bitmap_t(bitmap_format format, uint8_t bpp, bitmap_t &source, const rectangle &subrect) noexcept;
virtual ~bitmap_t();
// prevent implicit copying
bitmap_t &operator=(const bitmap_t &) = delete;
- bitmap_t &operator=(bitmap_t &&that);
+ bitmap_t &operator=(bitmap_t &&that) noexcept;
public:
// allocation/deallocation
- void reset();
+ void reset() noexcept;
// getters
- int32_t width() const { return m_width; }
- int32_t height() const { return m_height; }
- int32_t rowpixels() const { return m_rowpixels; }
- int32_t rowbytes() const { return m_rowpixels * m_bpp / 8; }
- uint8_t bpp() const { return m_bpp; }
- bitmap_format format() const { return m_format; }
- bool valid() const { return (m_base != nullptr); }
- palette_t *palette() const { return m_palette; }
- const rectangle &cliprect() const { return m_cliprect; }
-
- // allocation/sizing
- void allocate(int width, int height, int xslop = 0, int yslop = 0);
- void resize(int width, int height, int xslop = 0, int yslop = 0);
+ int32_t width() const noexcept { return m_width; }
+ int32_t height() const noexcept { return m_height; }
+ int32_t rowpixels() const noexcept { return m_rowpixels; }
+ int32_t rowbytes() const noexcept { return m_rowpixels * m_bpp / 8; }
+ uint8_t bpp() const noexcept { return m_bpp; }
+ bitmap_format format() const noexcept { return m_format; }
+ bool valid() const noexcept { return (m_base != nullptr); }
+ palette_t *palette() const noexcept { return m_palette; }
+ const rectangle &cliprect() const noexcept { return m_cliprect; }
+
+ // allocation/sizing (bitmap will become invalid if allocation fails)
+ void allocate(int width, int height, int xslop = 0, int yslop = 0) noexcept;
+ void resize(int width, int height, int xslop = 0, int yslop = 0) noexcept;
// operations
- void set_palette(palette_t *palette);
- void fill(uint32_t color) { fill(color, m_cliprect); }
- void fill(uint32_t color, const rectangle &cliprect);
- void plot_box(int x, int y, int width, int height, uint32_t color)
+ void set_palette(palette_t *palette) noexcept;
+ void fill(uint64_t color) noexcept { fill(color, m_cliprect); }
+ void fill(uint64_t color, const rectangle &bounds) noexcept;
+ void plot_box(int32_t x, int32_t y, int32_t width, int32_t height, uint64_t color) noexcept
{
- rectangle clip(x, x + width - 1, y, y + height - 1);
- fill(color, clip);
+ fill(color, rectangle(x, x + width - 1, y, y + height - 1));
}
// pixel access
- template<typename PixelType>
- PixelType &pixt(int32_t y, int32_t x = 0) const { return *(reinterpret_cast<PixelType *>(m_base) + y * m_rowpixels + x); }
- void *raw_pixptr(int32_t y, int32_t x = 0) const { return reinterpret_cast<uint8_t *>(m_base) + (y * m_rowpixels + x) * m_bpp / 8; }
+ void *raw_pixptr(int32_t y, int32_t x = 0) noexcept { return reinterpret_cast<uint8_t *>(m_base) + (y * m_rowpixels + x) * m_bpp / 8; }
+ void const *raw_pixptr(int32_t y, int32_t x = 0) const noexcept { return reinterpret_cast<uint8_t *>(m_base) + (y * m_rowpixels + x) * m_bpp / 8; }
protected:
// for use by subclasses only to ensure type correctness
- void wrap(void *base, int width, int height, int rowpixels);
- void wrap(const bitmap_t &source, const rectangle &subrect);
+ template <typename PixelType> PixelType &pixt(int32_t y, int32_t x = 0) noexcept { return *(reinterpret_cast<PixelType *>(m_base) + y * m_rowpixels + x); }
+ template <typename PixelType> PixelType const &pixt(int32_t y, int32_t x = 0) const noexcept { return *(reinterpret_cast<PixelType *>(m_base) + y * m_rowpixels + x); }
+ void wrap(void *base, int width, int height, int rowpixels) noexcept;
+ void wrap(bitmap_t &source, const rectangle &subrect) noexcept;
private:
// internal helpers
- int32_t compute_rowpixels(int width, int xslop);
- void compute_base(int xslop, int yslop);
- bool valid_format() const;
+ static int32_t compute_rowpixels(int width, int xslop) noexcept;
+ void compute_base(int xslop, int yslop) noexcept;
+ bool valid_format() const noexcept;
// internal state
std::unique_ptr<uint8_t []> m_alloc; // pointer to allocated pixel memory
@@ -191,45 +207,64 @@ private:
// ======================> bitmap_specific, bitmap8_t, bitmap16_t, bitmap32_t, bitmap64_t
-template<typename PixelType>
+template <typename PixelType>
class bitmap_specific : public bitmap_t
{
- static constexpr int PixelBits = 8 * sizeof(PixelType);
+ static constexpr int PIXEL_BITS = 8 * sizeof(PixelType);
protected:
// construction/destruction -- subclasses only
- bitmap_specific(bitmap_specific<PixelType> &&) = default;
- bitmap_specific(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap_t(format, PixelBits, width, height, xslop, yslop) { }
- bitmap_specific(bitmap_format format, PixelType *base, int width, int height, int rowpixels) : bitmap_t(format, PixelBits, base, width, height, rowpixels) { }
- bitmap_specific(bitmap_format format, bitmap_specific<PixelType> &source, const rectangle &subrect) : bitmap_t(format, PixelBits, source, subrect) { }
+ bitmap_specific(bitmap_specific<PixelType> &&) noexcept = default;
+ bitmap_specific(bitmap_format format, int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap_t(format, PIXEL_BITS, width, height, xslop, yslop) { }
+ bitmap_specific(bitmap_format format, PixelType *base, int width, int height, int rowpixels) noexcept : bitmap_t(format, PIXEL_BITS, base, width, height, rowpixels) { }
+ bitmap_specific(bitmap_format format, bitmap_specific<PixelType> &source, const rectangle &subrect) noexcept : bitmap_t(format, PIXEL_BITS, source, subrect) { }
- bitmap_specific<PixelType> &operator=(bitmap_specific<PixelType> &&) = default;
+ bitmap_specific<PixelType> &operator=(bitmap_specific<PixelType> &&) noexcept = default;
public:
using pixel_t = PixelType;
// getters
- uint8_t bpp() const { return PixelBits; }
+ uint8_t bpp() const noexcept { return PIXEL_BITS; }
// pixel accessors
- PixelType &pix(int32_t y, int32_t x = 0) const { return pixt<PixelType>(y, x); }
- PixelType &pix8(int32_t y, int32_t x = 0) const { static_assert(PixelBits == 8, "must be 8bpp"); return pixt<PixelType>(y, x); }
- PixelType &pix16(int32_t y, int32_t x = 0) const { static_assert(PixelBits == 16, "must be 16bpp"); return pixt<PixelType>(y, x); }
- PixelType &pix32(int32_t y, int32_t x = 0) const { static_assert(PixelBits == 32, "must be 32bpp"); return pixt<PixelType>(y, x); }
- PixelType &pix64(int32_t y, int32_t x = 0) const { static_assert(PixelBits == 64, "must be 64bpp"); return pixt<PixelType>(y, x); }
+ PixelType &pix(int32_t y, int32_t x = 0) noexcept { return pixt<PixelType>(y, x); }
+ PixelType const &pix(int32_t y, int32_t x = 0) const noexcept { return pixt<PixelType>(y, x); }
+
+ // operations
+ void fill(PixelType color) noexcept { fill(color, cliprect()); }
+ void fill(PixelType color, const rectangle &bounds) noexcept
+ {
+ // if we have a cliprect, intersect with that
+ rectangle fill(bounds);
+ fill &= cliprect();
+ if (!fill.empty())
+ {
+ for (int32_t y = fill.top(); y <= fill.bottom(); y++)
+ std::fill_n(&pix(y, fill.left()), fill.width(), color);
+ }
+ }
+ void plot_box(int32_t x, int32_t y, int32_t width, int32_t height, PixelType color) noexcept
+ {
+ fill(color, rectangle(x, x + width - 1, y, y + height - 1));
+ }
};
// 8bpp bitmaps
using bitmap8_t = bitmap_specific<uint8_t>;
+extern template class bitmap_specific<uint8_t>;
// 16bpp bitmaps
using bitmap16_t = bitmap_specific<uint16_t>;
+extern template class bitmap_specific<uint16_t>;
// 32bpp bitmaps
using bitmap32_t = bitmap_specific<uint32_t>;
+extern template class bitmap_specific<uint32_t>;
// 64bpp bitmaps
using bitmap64_t = bitmap_specific<uint64_t>;
+extern template class bitmap_specific<uint64_t>;
// ======================> bitmap_ind8, bitmap_ind16, bitmap_ind32, bitmap_ind64
@@ -241,17 +276,17 @@ class bitmap_ind8 : public bitmap8_t
public:
// construction/destruction
- bitmap_ind8(bitmap_ind8 &&) = default;
- bitmap_ind8(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap8_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_ind8(uint8_t *base, int width, int height, int rowpixels) : bitmap8_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_ind8(bitmap_ind8 &source, const rectangle &subrect) : bitmap8_t(k_bitmap_format, source, subrect) { }
- void wrap(uint8_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_ind8(bitmap_ind8 &&) noexcept = default;
+ bitmap_ind8(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap8_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_ind8(uint8_t *base, int width, int height, int rowpixels) noexcept : bitmap8_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_ind8(bitmap_ind8 &source, const rectangle &subrect) noexcept : bitmap8_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint8_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_ind8 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_ind8 &operator=(bitmap_ind8 &&) = default;
+ bitmap_ind8 &operator=(bitmap_ind8 &&) noexcept = default;
};
// BITMAP_FORMAT_IND16 bitmaps
@@ -261,17 +296,17 @@ class bitmap_ind16 : public bitmap16_t
public:
// construction/destruction
- bitmap_ind16(bitmap_ind16 &&) = default;
- bitmap_ind16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_ind16(uint16_t *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_ind16(bitmap_ind16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
- void wrap(uint16_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_ind16(bitmap_ind16 &&) noexcept = default;
+ bitmap_ind16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_ind16(uint16_t *base, int width, int height, int rowpixels) noexcept : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_ind16(bitmap_ind16 &source, const rectangle &subrect) noexcept : bitmap16_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint16_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_ind16 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_ind16 &operator=(bitmap_ind16 &&) = default;
+ bitmap_ind16 &operator=(bitmap_ind16 &&) noexcept = default;
};
// BITMAP_FORMAT_IND32 bitmaps
@@ -281,17 +316,17 @@ class bitmap_ind32 : public bitmap32_t
public:
// construction/destruction
- bitmap_ind32(bitmap_ind32 &&) = default;
- bitmap_ind32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_ind32(uint32_t *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_ind32(bitmap_ind32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
- void wrap(uint32_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_ind32(bitmap_ind32 &&) noexcept = default;
+ bitmap_ind32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_ind32(uint32_t *base, int width, int height, int rowpixels) noexcept : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_ind32(bitmap_ind32 &source, const rectangle &subrect) noexcept : bitmap32_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint32_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_ind32 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_ind32 &operator=(bitmap_ind32 &&) = default;
+ bitmap_ind32 &operator=(bitmap_ind32 &&) noexcept = default;
};
// BITMAP_FORMAT_IND64 bitmaps
@@ -301,17 +336,17 @@ class bitmap_ind64 : public bitmap64_t
public:
// construction/destruction
- bitmap_ind64(bitmap_ind64 &&) = default;
- bitmap_ind64(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap64_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_ind64(uint64_t *base, int width, int height, int rowpixels) : bitmap64_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_ind64(bitmap_ind64 &source, const rectangle &subrect) : bitmap64_t(k_bitmap_format, source, subrect) { }
- void wrap(uint64_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_ind8 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_ind64(bitmap_ind64 &&) noexcept = default;
+ bitmap_ind64(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap64_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_ind64(uint64_t *base, int width, int height, int rowpixels) noexcept : bitmap64_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_ind64(bitmap_ind64 &source, const rectangle &subrect) noexcept : bitmap64_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint64_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_ind64 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_ind64 &operator=(bitmap_ind64 &&) = default;
+ bitmap_ind64 &operator=(bitmap_ind64 &&) noexcept = default;
};
@@ -324,17 +359,17 @@ class bitmap_yuy16 : public bitmap16_t
public:
// construction/destruction
- bitmap_yuy16(bitmap_yuy16 &&) = default;
- bitmap_yuy16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_yuy16(uint16_t *base, int width, int height, int rowpixels) : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_yuy16(bitmap_yuy16 &source, const rectangle &subrect) : bitmap16_t(k_bitmap_format, source, subrect) { }
- void wrap(uint16_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_yuy16 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_yuy16(bitmap_yuy16 &&) noexcept = default;
+ bitmap_yuy16(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap16_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_yuy16(uint16_t *base, int width, int height, int rowpixels) noexcept : bitmap16_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_yuy16(bitmap_yuy16 &source, const rectangle &subrect) noexcept : bitmap16_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint16_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_yuy16 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_yuy16 &operator=(bitmap_yuy16 &&) = default;
+ bitmap_yuy16 &operator=(bitmap_yuy16 &&) noexcept = default;
};
// BITMAP_FORMAT_RGB32 bitmaps
@@ -344,17 +379,17 @@ class bitmap_rgb32 : public bitmap32_t
public:
// construction/destruction
- bitmap_rgb32(bitmap_rgb32 &&) = default;
- bitmap_rgb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_rgb32(uint32_t *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_rgb32(bitmap_rgb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
- void wrap(uint32_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_rgb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_rgb32(bitmap_rgb32 &&) noexcept = default;
+ bitmap_rgb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_rgb32(uint32_t *base, int width, int height, int rowpixels) noexcept : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_rgb32(bitmap_rgb32 &source, const rectangle &subrect) noexcept : bitmap32_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint32_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_rgb32 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_rgb32 &operator=(bitmap_rgb32 &&) = default;
+ bitmap_rgb32 &operator=(bitmap_rgb32 &&) noexcept = default;
};
// BITMAP_FORMAT_ARGB32 bitmaps
@@ -364,18 +399,17 @@ class bitmap_argb32 : public bitmap32_t
public:
// construction/destruction
- bitmap_argb32(bitmap_argb32 &&) = default;
- bitmap_argb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
- bitmap_argb32(uint32_t *base, int width, int height, int rowpixels) : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
- bitmap_argb32(bitmap_argb32 &source, const rectangle &subrect) : bitmap32_t(k_bitmap_format, source, subrect) { }
- void wrap(uint32_t *base, int width, int height, int rowpixels) { bitmap_t::wrap(base, width, height, rowpixels); }
- void wrap(const bitmap_argb32 &source, const rectangle &subrect) { bitmap_t::wrap(static_cast<const bitmap_t &>(source), subrect); }
+ bitmap_argb32(bitmap_argb32 &&) noexcept = default;
+ bitmap_argb32(int width = 0, int height = 0, int xslop = 0, int yslop = 0) noexcept : bitmap32_t(k_bitmap_format, width, height, xslop, yslop) { }
+ bitmap_argb32(uint32_t *base, int width, int height, int rowpixels) noexcept : bitmap32_t(k_bitmap_format, base, width, height, rowpixels) { }
+ bitmap_argb32(bitmap_argb32 &source, const rectangle &subrect) noexcept : bitmap32_t(k_bitmap_format, source, subrect) { }
+ void wrap(uint32_t *base, int width, int height, int rowpixels) noexcept { bitmap_t::wrap(base, width, height, rowpixels); }
+ void wrap(bitmap_argb32 &source, const rectangle &subrect) noexcept { bitmap_t::wrap(static_cast<bitmap_t &>(source), subrect); }
// getters
- bitmap_format format() const { return k_bitmap_format; }
+ bitmap_format format() const noexcept { return k_bitmap_format; }
- bitmap_argb32 &operator=(bitmap_argb32 &&) = default;
+ bitmap_argb32 &operator=(bitmap_argb32 &&) noexcept = default;
};
-
-#endif // MAME_UTIL_BITMAP_H
+#endif // MAME_UTIL_BITMAP_H
diff --git a/src/lib/util/bitstream.h b/src/lib/util/bitstream.h
index 3da6ec2c1cf..eb8b2005329 100644
--- a/src/lib/util/bitstream.h
+++ b/src/lib/util/bitstream.h
@@ -13,7 +13,7 @@
#pragma once
-#include "osdcore.h"
+#include <cstdint>
//**************************************************************************
@@ -40,10 +40,11 @@ public:
private:
// internal state
uint32_t m_buffer; // current bit accumulator
- int m_bits; // number of bits in the accumulator
+ int m_bits; // number of bits in the accumulator
const uint8_t * m_read; // read pointer
uint32_t m_doffset; // byte offset within the data
uint32_t m_dlength; // length of the data
+ int m_dbitoffs; // bit offset within current read pointer
};
@@ -64,7 +65,7 @@ public:
private:
// internal state
uint32_t m_buffer; // current bit accumulator
- int m_bits; // number of bits in the accumulator
+ int m_bits; // number of bits in the accumulator
uint8_t * m_write; // write pointer
uint32_t m_doffset; // byte offset within the data
uint32_t m_dlength; // length of the data
@@ -85,7 +86,8 @@ inline bitstream_in::bitstream_in(const void *src, uint32_t srclength)
m_bits(0),
m_read(reinterpret_cast<const uint8_t *>(src)),
m_doffset(0),
- m_dlength(srclength)
+ m_dlength(srclength),
+ m_dbitoffs(0)
{
}
@@ -103,12 +105,31 @@ inline uint32_t bitstream_in::peek(int numbits)
// fetch data if we need more
if (numbits > m_bits)
{
- while (m_bits <= 24)
+ while (m_bits < 32)
{
+ uint32_t newbits = 0;
+
if (m_doffset < m_dlength)
- m_buffer |= m_read[m_doffset] << (24 - m_bits);
- m_doffset++;
- m_bits += 8;
+ {
+ // adjust current data to discard any previously read partial bits
+ newbits = (m_read[m_doffset] << m_dbitoffs) & 0xff;
+ }
+
+ if (m_bits + 8 > 32)
+ {
+ // take only what can be used to fill out the rest of the buffer
+ m_dbitoffs = 32 - m_bits;
+ newbits >>= 8 - m_dbitoffs;
+ m_buffer |= newbits;
+ m_bits += m_dbitoffs;
+ }
+ else
+ {
+ m_buffer |= newbits << (24 - m_bits);
+ m_bits += 8 - m_dbitoffs;
+ m_dbitoffs = 0;
+ m_doffset++;
+ }
}
}
@@ -154,6 +175,10 @@ inline uint32_t bitstream_in::read_offset() const
result--;
bits -= 8;
}
+
+ if (m_dbitoffs > bits)
+ result++;
+
return result;
}
@@ -169,7 +194,11 @@ inline uint32_t bitstream_in::flush()
m_doffset--;
m_bits -= 8;
}
- m_bits = m_buffer = 0;
+
+ if (m_dbitoffs > m_bits)
+ m_doffset++;
+
+ m_bits = m_buffer = m_dbitoffs = 0;
return m_doffset;
}
@@ -196,8 +225,11 @@ inline bitstream_out::bitstream_out(void *dest, uint32_t destlength)
inline void bitstream_out::write(uint32_t newbits, int numbits)
{
+ newbits <<= 32 - numbits;
+
// flush the buffer if we're going to overflow it
- if (m_bits + numbits > 32)
+ while (m_bits + numbits >= 32 && numbits > 0)
+ {
while (m_bits >= 8)
{
if (m_doffset < m_dlength)
@@ -207,11 +239,19 @@ inline void bitstream_out::write(uint32_t newbits, int numbits)
m_bits -= 8;
}
- // shift the bits to the top
- if (numbits == 0)
- newbits = 0;
- else
- newbits <<= 32 - numbits;
+ // offload more bits if it'll still overflow the buffer
+ if (m_bits + numbits >= 32)
+ {
+ const int rem = std::min(32 - m_bits, numbits);
+ m_buffer |= newbits >> m_bits;
+ m_bits += rem;
+ newbits <<= rem;
+ numbits -= rem;
+ }
+ }
+
+ if (numbits <= 0)
+ return;
// now shift it down to account for the number of bits we already have and OR them in
m_buffer |= newbits >> m_bits;
diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp
index c2cded9f9aa..ab227dce1eb 100644
--- a/src/lib/util/cdrom.cpp
+++ b/src/lib/util/cdrom.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles,R. Belmont
/***************************************************************************
cdrom.c
@@ -16,12 +16,17 @@
***************************************************************************/
-#include <assert.h>
-
#include "cdrom.h"
-#include <stdlib.h>
-#include "chdcd.h"
+#include "corestr.h"
+#include "multibyte.h"
+#include "osdfile.h"
+#include "path.h"
+#include "strformat.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <tuple>
/***************************************************************************
@@ -30,7 +35,7 @@
/** @brief The verbose. */
#define VERBOSE (0)
-#if VERBOSE
+#define EXTRA_VERBOSE (0)
/**
* @def LOG(x) do
@@ -40,83 +45,7 @@
* @param x The void to process.
*/
-#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
-
-/**
- * @fn void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
- *
- * @brief Logerrors the given text.
- *
- * @param text The text.
- *
- * @return A CLIB_DECL.
- */
-
-void CLIB_DECL logerror(const char *text, ...) ATTR_PRINTF(1,2);
-#else
-
-/**
- * @def LOG(x);
- *
- * @brief A macro that defines log.
- *
- * @param x The void to process.
- */
-
-#define LOG(x)
-#endif
-
-
-
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-/** @brief offset within sector. */
-const int SYNC_OFFSET = 0x000;
-/** @brief 12 bytes. */
-const int SYNC_NUM_BYTES = 12;
-
-/** @brief offset within sector. */
-const int MODE_OFFSET = 0x00f;
-
-/** @brief offset within sector. */
-const int ECC_P_OFFSET = 0x81c;
-/** @brief 2 lots of 86. */
-const int ECC_P_NUM_BYTES = 86;
-/** @brief 24 bytes each. */
-const int ECC_P_COMP = 24;
-
-/** @brief The ECC q offset. */
-const int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES;
-/** @brief 2 lots of 52. */
-const int ECC_Q_NUM_BYTES = 52;
-/** @brief 43 bytes each. */
-const int ECC_Q_COMP = 43;
-
-
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-/**
- * @struct cdrom_file
- *
- * @brief A cdrom file.
- */
-
-struct cdrom_file
-{
- /** @brief The chd. */
- chd_file * chd; /* CHD file */
- /** @brief The cdtoc. */
- cdrom_toc cdtoc; /* TOC for the CD */
- /** @brief Information describing the track. */
- chdcd_track_input_info track_info; /* track info */
- /** @brief The fhandle[ CD maximum tracks]. */
- util::core_file::ptr fhandle[CD_MAX_TRACKS];/* file handle */
-};
+#define LOG(x) do { if (VERBOSE) { osd_printf_info x; } } while (0)
@@ -130,27 +59,23 @@ struct cdrom_file
-------------------------------------------------*/
/**
- * @fn static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, uint32_t &tracknum)
+ * @fn static inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum)
*
* @brief Physical to chd lba.
*
- * @param [in,out] file If non-null, the file.
* @param physlba The physlba.
* @param [in,out] tracknum The tracknum.
*
* @return An uint32_t.
*/
-static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, uint32_t &tracknum)
+uint32_t cdrom_file::physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const
{
- uint32_t chdlba;
- int track;
-
- /* loop until our current LBA is less than the start LBA of the next track */
- for (track = 0; track < file->cdtoc.numtrks; track++)
- if (physlba < file->cdtoc.tracks[track + 1].physframeofs)
+ // loop until our current LBA is less than the start LBA of the next track
+ for (int track = 0; track < cdtoc.numtrks; track++)
+ if (physlba < cdtoc.tracks[track + 1].physframeofs)
{
- chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
+ uint32_t chdlba = physlba - cdtoc.tracks[track].physframeofs + cdtoc.tracks[track].chdframeofs;
tracknum = track;
return chdlba;
}
@@ -164,36 +89,26 @@ static inline uint32_t physical_to_chd_lba(cdrom_file *file, uint32_t physlba, u
-------------------------------------------------*/
/**
- * @fn static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uint32_t &tracknum)
+ * @fn uint32_t logical_to_chd_lba(uint32_t loglba, uint32_t &tracknum)
*
* @brief Logical to chd lba.
*
- * @param [in,out] file If non-null, the file.
* @param loglba The loglba.
* @param [in,out] tracknum The tracknum.
*
* @return An uint32_t.
*/
-static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uint32_t &tracknum)
+uint32_t cdrom_file::logical_to_chd_lba(uint32_t loglba, uint32_t &tracknum) const
{
- uint32_t chdlba, physlba;
- int track;
-
- /* loop until our current LBA is less than the start LBA of the next track */
- for (track = 0; track < file->cdtoc.numtrks; track++)
+ // loop until our current LBA is less than the start LBA of the next track
+ for (int track = 0; track < cdtoc.numtrks; track++)
{
- if (loglba < file->cdtoc.tracks[track + 1].logframeofs)
+ if (loglba < cdtoc.tracks[track + 1].logframeofs)
{
- // is this a no-pregap-data track? compensate for the logical offset pointing to the "wrong" sector.
- if ((file->cdtoc.tracks[track].pgdatasize == 0) && (loglba > file->cdtoc.tracks[track].pregap))
- {
- loglba -= file->cdtoc.tracks[track].pregap;
- }
-
// convert to physical and proceed
- physlba = file->cdtoc.tracks[track].physframeofs + (loglba - file->cdtoc.tracks[track].logframeofs);
- chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs;
+ uint32_t physlba = cdtoc.tracks[track].physframeofs + (loglba - cdtoc.tracks[track].logframeofs);
+ uint32_t chdlba = physlba - cdtoc.tracks[track].physframeofs + cdtoc.tracks[track].chdframeofs;
tracknum = track;
return chdlba;
}
@@ -208,98 +123,114 @@ static inline uint32_t logical_to_chd_lba(cdrom_file *file, uint32_t loglba, uin
***************************************************************************/
/**
- * @fn cdrom_file *cdrom_open(const char *inputfile)
+ * @fn constructor
*
- * @brief Queries if a given cdrom open.
+ * @brief Open a cdrom for a file.
*
* @param inputfile The inputfile.
- *
- * @return null if it fails, else a cdrom_file*.
*/
-cdrom_file *cdrom_open(const char *inputfile)
+cdrom_file::cdrom_file(std::string_view inputfile)
{
- int i;
- cdrom_file *file;
- uint32_t physofs, logofs;
-
- /* allocate memory for the CD-ROM file */
- file = new (std::nothrow) cdrom_file();
- if (file == nullptr)
- return nullptr;
-
- /* setup the CDROM module and get the disc info */
- chd_error err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info);
- if (err != CHDERR_NONE)
+ // set up the CD-ROM module and get the disc info
+ std::error_condition err = parse_toc(inputfile, cdtoc, cdtrack_info);
+ if (err)
{
- fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err));
- delete file;
- return nullptr;
+ osd_printf_error("Error reading input file: %s\n", err.message());
+ throw nullptr;
}
- /* fill in the data */
- file->chd = nullptr;
+ // fill in the data
+ chd = nullptr;
- LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
+ LOG(("CD has %d tracks\n", cdtoc.numtrks));
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- osd_file::error filerr = util::core_file::open(file->track_info.track[i].fname, OPEN_FLAG_READ, file->fhandle[i]);
- if (filerr != osd_file::error::NONE)
+ osd_file::ptr file;
+ std::uint64_t length;
+ std::error_condition const filerr = osd_file::open(cdtrack_info.track[i].fname, OPEN_FLAG_READ, file, length);
+ if (filerr)
{
- fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.c_str());
- cdrom_close(file);
- return nullptr;
+ osd_printf_error("Unable to open file: %s\n", cdtrack_info.track[i].fname);
+ throw nullptr;
+ }
+ fhandle[i] = util::osd_file_read(std::move(file));
+ if (!fhandle[i])
+ {
+ osd_printf_error("Unable to open file: %s\n", cdtrack_info.track[i].fname);
+ throw nullptr;
}
}
+
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit 4-frame size boundries
*/
- physofs = logofs = 0;
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ uint32_t physofs = 0, logofs = 0;
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].chdframeofs = 0;
- file->cdtoc.tracks[i].logframeofs = logofs;
+ track_info &track = cdtoc.tracks[i];
+ track.logframeofs = 0;
- // if the pregap sectors aren't in the track, add them to the track's logical length
- if (file->cdtoc.tracks[i].pgdatasize == 0)
+ if (track.pgdatasize == 0)
+ {
+ logofs += track.pregap;
+ }
+ else
{
- logofs += file->cdtoc.tracks[i].pregap;
+ track.logframeofs = track.pregap;
}
+ if ((cdtoc.flags & CD_FLAG_MULTISESSION) && (cdtrack_info.track[i].leadin != -1))
+ logofs += cdtrack_info.track[i].leadin;
+
+ track.physframeofs = physofs;
+ track.chdframeofs = 0;
+ track.logframeofs += logofs;
+ track.logframes = track.frames - track.pregap;
+
// postgap adds to the track length
- logofs += file->cdtoc.tracks[i].postgap;
-
- physofs += file->cdtoc.tracks[i].frames;
- logofs += file->cdtoc.tracks[i].frames;
-
-/* printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d\n", i+1,
- file->cdtoc.tracks[i].trktype,
- file->cdtoc.tracks[i].subtype,
- file->cdtoc.tracks[i].datasize,
- file->cdtoc.tracks[i].subsize,
- file->cdtoc.tracks[i].frames,
- file->cdtoc.tracks[i].extraframes,
- file->cdtoc.tracks[i].pregap,
- file->cdtoc.tracks[i].pgtype,
- file->cdtoc.tracks[i].pgdatasize,
- file->cdtoc.tracks[i].postgap,
- file->cdtoc.tracks[i].logframeofs,
- file->cdtoc.tracks[i].physframeofs,
- file->cdtoc.tracks[i].chdframeofs);*/
- }
-
- /* fill out dummy entries for the last track to help our search */
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
- file->cdtoc.tracks[i].chdframeofs = 0;
-
- return file;
+ logofs += track.postgap;
+
+ physofs += track.frames;
+ logofs += track.frames;
+
+ if ((cdtoc.flags & CD_FLAG_MULTISESSION) && cdtrack_info.track[i].leadout != -1)
+ logofs += cdtrack_info.track[i].leadout;
+
+ if (EXTRA_VERBOSE)
+ {
+ osd_printf_verbose("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n",
+ track.session + 1,
+ i + 1,
+ track.trktype,
+ track.subtype,
+ track.datasize,
+ track.subsize,
+ track.frames,
+ track.extraframes,
+ track.pregap,
+ track.pgtype,
+ track.pgdatasize,
+ track.postgap,
+ track.logframeofs,
+ track.physframeofs,
+ track.chdframeofs,
+ track.logframes,
+ track.padframes);
+ }
+ }
+
+ // fill out dummy entries for the last track to help our search
+ track_info &track = cdtoc.tracks[cdtoc.numtrks];
+ track.physframeofs = physofs;
+ track.logframeofs = logofs;
+ track.chdframeofs = 0;
+ track.logframes = 0;
}
/*-------------------------------------------------
- cdrom_open - "open" a CD-ROM file from an
+ constructor - "open" a CD-ROM file from an
already-opened CHD file
-------------------------------------------------*/
@@ -313,116 +244,107 @@ cdrom_file *cdrom_open(const char *inputfile)
* @return null if it fails, else a cdrom_file*.
*/
-cdrom_file *cdrom_open(chd_file *chd)
+cdrom_file::cdrom_file(chd_file *_chd)
{
- int i;
- cdrom_file *file;
- uint32_t physofs, chdofs, logofs;
- chd_error err;
-
- /* punt if no CHD */
- if (!chd)
- return nullptr;
+ chd = _chd;
/* validate the CHD information */
- if (chd->hunk_bytes() % CD_FRAME_SIZE != 0)
- return nullptr;
- if (chd->unit_bytes() != CD_FRAME_SIZE)
- return nullptr;
-
- /* allocate memory for the CD-ROM file */
- file = new (std::nothrow) cdrom_file();
- if (file == nullptr)
- return nullptr;
-
- /* fill in the data */
- file->chd = chd;
+ if (chd->hunk_bytes() % FRAME_SIZE != 0)
+ throw nullptr;
+ if (chd->unit_bytes() != FRAME_SIZE)
+ throw nullptr;
/* read the CD-ROM metadata */
- err = cdrom_parse_metadata(chd, &file->cdtoc);
- if (err != CHDERR_NONE)
- {
- delete file;
- return nullptr;
- }
+ std::error_condition err = parse_metadata(chd, cdtoc);
+ if (err)
+ throw nullptr;
- LOG(("CD has %d tracks\n", file->cdtoc.numtrks));
+ LOG(("CD has %d tracks\n", cdtoc.numtrks));
/* calculate the starting frame for each track, keeping in mind that CHDMAN
pads tracks out with extra frames to fit 4-frame size boundries
*/
- physofs = chdofs = logofs = 0;
- for (i = 0; i < file->cdtoc.numtrks; i++)
+ uint32_t physofs = 0, chdofs = 0, logofs = 0;
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].chdframeofs = chdofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
+ track_info &track = cdtoc.tracks[i];
+ track.logframeofs = 0;
- // if the pregap sectors aren't in the track, add them to the track's logical length
- if (file->cdtoc.tracks[i].pgdatasize == 0)
+ if (track.pgdatasize == 0)
+ {
+ // Anything that isn't cue.
+ // toc (cdrdao): Pregap data seems to be included at the end of previous track.
+ // START/PREGAP is only issued in special cases, for instance alongside ZERO commands.
+ // ZERO and SILENCE commands are supposed to generate additional data that's not included
+ // in the image directly, so the total logofs value must be offset to point to index 1.
+ logofs += track.pregap;
+ }
+ else
{
- logofs += file->cdtoc.tracks[i].pregap;
+ // cues: Pregap is the difference between index 0 and index 1 unless PREGAP is specified.
+ // The data is assumed to be in the bin and not generated separately, so the pregap should
+ // only be added to the current track's lba to offset it to index 1.
+ track.logframeofs = track.pregap;
}
+ track.physframeofs = physofs;
+ track.chdframeofs = chdofs;
+ track.logframeofs += logofs;
+ track.logframes = track.frames - track.pregap;
+
// postgap counts against the next track
- logofs += file->cdtoc.tracks[i].postgap;
-
- physofs += file->cdtoc.tracks[i].frames;
- chdofs += file->cdtoc.tracks[i].frames;
- chdofs += file->cdtoc.tracks[i].extraframes;
- logofs += file->cdtoc.tracks[i].frames;
-/*
- printf("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d\n", i+1,
- file->cdtoc.tracks[i].trktype,
- file->cdtoc.tracks[i].subtype,
- file->cdtoc.tracks[i].datasize,
- file->cdtoc.tracks[i].subsize,
- file->cdtoc.tracks[i].frames,
- file->cdtoc.tracks[i].extraframes,
- file->cdtoc.tracks[i].pregap,
- file->cdtoc.tracks[i].pgtype,
- file->cdtoc.tracks[i].pgdatasize,
- file->cdtoc.tracks[i].postgap,
- file->cdtoc.tracks[i].logframeofs,
- file->cdtoc.tracks[i].physframeofs,
- file->cdtoc.tracks[i].chdframeofs);*/
- }
-
- /* fill out dummy entries for the last track to help our search */
- file->cdtoc.tracks[i].physframeofs = physofs;
- file->cdtoc.tracks[i].logframeofs = logofs;
- file->cdtoc.tracks[i].chdframeofs = chdofs;
-
- return file;
+ logofs += track.postgap;
+
+ physofs += track.frames;
+ chdofs += track.frames;
+ chdofs += track.extraframes;
+ logofs += track.frames;
+
+ if (EXTRA_VERBOSE)
+ {
+ osd_printf_verbose("session %d track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d pregap %d pgmode %d presize %d postgap %d logofs %d physofs %d chdofs %d logframes %d pad %d\n",
+ track.session + 1,
+ i + 1,
+ track.trktype,
+ track.subtype,
+ track.datasize,
+ track.subsize,
+ track.frames,
+ track.extraframes,
+ track.pregap,
+ track.pgtype,
+ track.pgdatasize,
+ track.postgap,
+ track.logframeofs,
+ track.physframeofs,
+ track.chdframeofs,
+ track.logframes,
+ track.padframes);
+ }
+ }
+
+ // fill out dummy entries for the last track to help our search
+ track_info &track = cdtoc.tracks[cdtoc.numtrks];
+ track.physframeofs = physofs;
+ track.logframeofs = logofs;
+ track.chdframeofs = chdofs;
+ track.logframes = 0;
}
/*-------------------------------------------------
- cdrom_close - "close" a CD-ROM file
+ destructor - "close" a CD-ROM file
-------------------------------------------------*/
-/**
- * @fn void cdrom_close(cdrom_file *file)
- *
- * @brief Cdrom close.
- *
- * @param [in,out] file If non-null, the file.
- */
-
-void cdrom_close(cdrom_file *file)
+cdrom_file::~cdrom_file()
{
- if (file == nullptr)
- return;
-
- if (file->chd == nullptr)
+ if (chd == nullptr)
{
- for (int i = 0; i < file->cdtoc.numtrks; i++)
+ for (int i = 0; i < cdtoc.numtrks; i++)
{
- file->fhandle[i].reset();
+ fhandle[i].reset();
}
}
-
- delete file;
}
@@ -432,71 +354,86 @@ void cdrom_close(cdrom_file *file)
***************************************************************************/
/**
- * @fn chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length)
+ * @fn std::error_condition read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys)
*
* @brief Reads partial sector.
*
- * @param [in,out] file If non-null, the file.
* @param [in,out] dest If non-null, destination for the.
* @param lbasector The lbasector.
* @param chdsector The chdsector.
* @param tracknum The tracknum.
* @param startoffs The startoffs.
* @param length The length.
+ * @param phys true to physical.
*
* @return The partial sector.
*/
-chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys=false)
+std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys)
{
- chd_error result = CHDERR_NONE;
+ std::error_condition result;
bool needswap = false;
// if this is pregap info that isn't actually in the file, just return blank data
if (!phys)
{
- if ((file->cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < (file->cdtoc.tracks[tracknum].logframeofs + file->cdtoc.tracks[tracknum].pregap)))
+ if ((cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < cdtoc.tracks[tracknum].logframeofs))
{
- //printf("PG missing sector: LBA %d, trklog %d\n", lbasector, file->cdtoc.tracks[tracknum].logframeofs);
+ if (EXTRA_VERBOSE)
+ osd_printf_verbose("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs);
memset(dest, 0, length);
return result;
}
}
// if a CHD, just read
- if (file->chd != nullptr)
+ if (chd != nullptr)
{
- result = file->chd->read_bytes(uint64_t(chdsector) * uint64_t(CD_FRAME_SIZE) + startoffs, dest, length);
- /* swap CDDA in the case of LE GDROMs */
- if ((file->cdtoc.flags & CD_FLAG_GDROMLE) && (file->cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO))
+ if (!phys && cdtoc.tracks[tracknum].pgdatasize != 0)
+ {
+ // chdman (phys=true) relies on chdframeofs to point to index 0 instead of index 1 for extractcd.
+ // Actually playing CDs requires it to point to index 1 instead of index 0, so adjust the offset when phys=false.
+ chdsector += cdtoc.tracks[tracknum].pregap;
+ }
+
+ result = chd->read_bytes(uint64_t(chdsector) * uint64_t(FRAME_SIZE) + startoffs, dest, length);
+
+ // swap CDDA in the case of LE GDROMs
+ if ((cdtoc.flags & CD_FLAG_GDROMLE) && (cdtoc.tracks[tracknum].trktype == CD_TRACK_AUDIO))
needswap = true;
}
else
{
// else read from the appropriate file
- util::core_file &srcfile = *file->fhandle[tracknum];
+ util::random_read &srcfile = *fhandle[tracknum];
- uint64_t sourcefileoffset = file->track_info.track[tracknum].offset;
- int bytespersector = file->cdtoc.tracks[tracknum].datasize + file->cdtoc.tracks[tracknum].subsize;
+ int bytespersector = cdtoc.tracks[tracknum].datasize + cdtoc.tracks[tracknum].subsize;
+ uint64_t sourcefileoffset = cdtrack_info.track[tracknum].offset;
+
+ if (cdtoc.tracks[tracknum].pgdatasize != 0)
+ chdsector += cdtoc.tracks[tracknum].pregap;
sourcefileoffset += chdsector * bytespersector + startoffs;
- // printf("Reading sector %d from track %d at offset %lld\n", chdsector, tracknum, sourcefileoffset);
+ if (EXTRA_VERBOSE)
+ osd_printf_verbose("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset);
- srcfile.seek(sourcefileoffset, SEEK_SET);
- srcfile.read(dest, length);
+ result = srcfile.seek(sourcefileoffset, SEEK_SET);
+ size_t actual;
+ if (!result)
+ std::tie(result, actual) = read(srcfile, dest, length);
+ // FIXME: if (!result && (actual < length)) report error
- needswap = file->track_info.track[tracknum].swap;
+ needswap = cdtrack_info.track[tracknum].swap;
}
if (needswap)
{
uint8_t *buffer = (uint8_t *)dest - startoffs;
- for (int swapindex = startoffs; swapindex < 2352; swapindex += 2 )
+ for (int swapindex = startoffs; swapindex < 2352; swapindex += 2)
{
- int swaptemp = buffer[ swapindex ];
- buffer[ swapindex ] = buffer[ swapindex + 1 ];
- buffer[ swapindex + 1 ] = swaptemp;
+ using std::swap;
+ swap(buffer[ swapindex ], buffer[ swapindex + 1 ]);
}
}
return result;
@@ -509,11 +446,10 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector,
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
+ * @fn bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
*
* @brief Cdrom read data.
*
- * @param [in,out] file If non-null, the file.
* @param lbasector The lbasector.
* @param [in,out] buffer If non-null, the buffer.
* @param datatype The datatype.
@@ -522,71 +458,67 @@ chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector,
* @return An uint32_t.
*/
-uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
+bool cdrom_file::read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys)
{
- if (file == nullptr)
- return 0;
-
// compute CHD sector and tracknumber
uint32_t tracknum = 0;
uint32_t chdsector;
if (phys)
{
- chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = physical_to_chd_lba(lbasector, tracknum);
}
else
{
- chdsector = logical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = logical_to_chd_lba(lbasector, tracknum);
}
- /* copy out the requested sector */
- uint32_t tracktype = file->cdtoc.tracks[tracknum].trktype;
+ // copy out the requested sector
+ uint32_t tracktype = cdtoc.tracks[tracknum].trktype;
if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize, phys) == CHDERR_NONE);
+ assert(cdtoc.tracks[tracknum].datasize != 0);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 0, cdtoc.tracks[tracknum].datasize, phys);
}
else
{
- /* return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector */
+ // return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector
if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 16, 2048, phys);
}
- /* return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data */
+ // return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data
if ((datatype == CD_TRACK_MODE1_RAW) && (tracktype == CD_TRACK_MODE1))
{
- uint8_t *bufptr = (uint8_t *)buffer;
+ auto *bufptr = (uint8_t *)buffer;
uint32_t msf = lba_to_msf(lbasector);
static const uint8_t syncbytes[12] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
memcpy(bufptr, syncbytes, 12);
- bufptr[12] = msf>>16;
- bufptr[13] = msf>>8;
- bufptr[14] = msf&0xff;
+ put_u24be(&bufptr[12], msf);
bufptr[15] = 1; // mode 1
LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n"));
- return (read_partial_sector(file, bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys);
}
- /* return 2048 bytes of mode 1 data from a mode2 form1 or raw sector */
+ // return 2048 bytes of mode 1 data from a mode2 form1 or raw sector
if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW)))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 24, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 24, 2048, phys);
}
- /* return 2048 bytes of mode 1 data from a mode2 form2 or XA sector */
+ // return 2048 bytes of mode 1 data from a mode2 form2 or XA sector
if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE2_FORM_MIX))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 8, 2048, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 8, 2048, phys);
}
- /* return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) */
+ // return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header)
if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW)))
{
- return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336, phys) == CHDERR_NONE);
+ return !read_partial_sector(buffer, lbasector, chdsector, tracknum, 16, 2336, phys);
}
LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype));
@@ -596,47 +528,43 @@ uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uin
/*-------------------------------------------------
- cdrom_read_subcode - read subcode data for
+ read_subcode - read subcode data for
a sector
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, bool phys)
+ * @fn bool read_subcode(uint32_t lbasector, void *buffer, bool phys)
*
* @brief Cdrom read subcode.
*
- * @param [in,out] file If non-null, the file.
* @param lbasector The lbasector.
* @param [in,out] buffer If non-null, the buffer.
* @param phys true to physical.
*
- * @return An uint32_t.
+ * @return false on failure.
*/
-uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, bool phys)
+bool cdrom_file::read_subcode(uint32_t lbasector, void *buffer, bool phys)
{
- if (file == nullptr)
- return ~0;
-
// compute CHD sector and tracknumber
uint32_t tracknum = 0;
uint32_t chdsector;
if (phys)
{
- chdsector = physical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = physical_to_chd_lba(lbasector, tracknum);
}
else
{
- chdsector = logical_to_chd_lba(file, lbasector, tracknum);
+ chdsector = logical_to_chd_lba(lbasector, tracknum);
}
- if (file->cdtoc.tracks[tracknum].subsize == 0)
- return 0;
+ if (cdtoc.tracks[tracknum].subsize == 0)
+ return false;
// read the data
- chd_error err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize);
- return (err == CHDERR_NONE);
+ std::error_condition err = read_partial_sector(buffer, lbasector, chdsector, tracknum, cdtoc.tracks[tracknum].datasize, cdtoc.tracks[tracknum].subsize, phys);
+ return !err;
}
@@ -646,220 +574,64 @@ uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer,
***************************************************************************/
/*-------------------------------------------------
- cdrom_get_track - get the track number
+ get_track - get the track number
for a physical frame number
-------------------------------------------------*/
/**
- * @fn uint32_t cdrom_get_track(cdrom_file *file, uint32_t frame)
+ * @fn uint32_t get_track(uint32_t frame)
*
* @brief Cdrom get track.
*
- * @param [in,out] file If non-null, the file.
* @param frame The frame.
*
* @return An uint32_t.
*/
-uint32_t cdrom_get_track(cdrom_file *file, uint32_t frame)
+uint32_t cdrom_file::get_track(uint32_t frame) const
{
uint32_t track = 0;
- if (file == nullptr)
- return ~0;
-
/* convert to a CHD sector offset and get track information */
- logical_to_chd_lba(file, frame, track);
+ logical_to_chd_lba(frame, track);
return track;
}
-
-/*-------------------------------------------------
- cdrom_get_track_start - get the frame number
- that a track starts at
--------------------------------------------------*/
-
-/**
- * @fn uint32_t cdrom_get_track_start(cdrom_file *file, uint32_t track)
- *
- * @brief Cdrom get track start.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An uint32_t.
- */
-
-uint32_t cdrom_get_track_start(cdrom_file *file, uint32_t track)
+uint32_t cdrom_file::get_track_index(uint32_t frame) const
{
- if (file == nullptr)
- return ~0;
-
- /* handle lead-out specially */
- if (track == 0xaa)
- track = file->cdtoc.numtrks;
-
- return file->cdtoc.tracks[track].logframeofs;
-}
-
-/*-------------------------------------------------
- cdrom_get_track_start_phys - get the
- physical frame number that a track starts at
--------------------------------------------------*/
+ const uint32_t track = get_track(frame);
+ const uint32_t track_start = get_track_start(track);
+ const uint32_t index_offset = frame - track_start;
+ int index = 0;
-/**
- * @fn uint32_t cdrom_get_track_start_phys(cdrom_file *file, uint32_t track)
- *
- * @brief Cdrom get track start physical.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An uint32_t.
- */
-
-uint32_t cdrom_get_track_start_phys(cdrom_file *file, uint32_t track)
-{
- if (file == nullptr)
- return ~0;
-
- /* handle lead-out specially */
- if (track == 0xaa)
- track = file->cdtoc.numtrks;
-
- return file->cdtoc.tracks[track].physframeofs;
-}
-
-/*-------------------------------------------------
- cdrom_get_chd - get a handle to a CHD
- from a cdrom
--------------------------------------------------*/
-
-chd_file *cdrom_get_chd(cdrom_file *file)
-{
- return file->chd;
-}
-
-/***************************************************************************
- TOC UTILITIES
-***************************************************************************/
-
-/*-------------------------------------------------
- cdrom_get_last_track - returns the last track
- number
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_last_track(cdrom_file *file)
- *
- * @brief Cdrom get last track.
- *
- * @param [in,out] file If non-null, the file.
- *
- * @return An int.
- */
-
-int cdrom_get_last_track(cdrom_file *file)
-{
- if (file == nullptr)
- return -1;
-
- return file->cdtoc.numtrks;
-}
-
-
-/*-------------------------------------------------
- cdrom_get_adr_control - get the ADR | CONTROL
- for a track
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_adr_control(cdrom_file *file, int track)
- *
- * @brief Cdrom get address control.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An int.
- */
-
-int cdrom_get_adr_control(cdrom_file *file, int track)
-{
- if (file == nullptr)
- return -1;
-
- if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO)
+ for (int i = 0; i < std::size(cdtrack_info.track[track].idx); i++)
{
- return 0x10; // audio track, subchannel is position
+ if (index_offset >= cdtrack_info.track[track].idx[i])
+ index = i;
+ else
+ break;
}
- return 0x14; // data track, subchannel is position
-}
-
-
-/*-------------------------------------------------
- cdrom_get_track_type - return the track type
--------------------------------------------------*/
-
-/**
- * @fn int cdrom_get_track_type(cdrom_file *file, int track)
- *
- * @brief Cdrom get track type.
- *
- * @param [in,out] file If non-null, the file.
- * @param track The track.
- *
- * @return An int.
- */
-
-int cdrom_get_track_type(cdrom_file *file, int track)
-{
- if (file == nullptr)
- return -1;
+ if (cdtrack_info.track[track].idx[index] == -1)
+ index = 1; // valid index not found, default to index 1
- return file->cdtoc.tracks[track].trktype;
+ return index;
}
-/*-------------------------------------------------
- cdrom_get_toc - return the TOC data for a
- CD-ROM
--------------------------------------------------*/
-
-/**
- * @fn const cdrom_toc *cdrom_get_toc(cdrom_file *file)
- *
- * @brief Cdrom get TOC.
- *
- * @param [in,out] file If non-null, the file.
- *
- * @return null if it fails, else a cdrom_toc*.
- */
-
-const cdrom_toc *cdrom_get_toc(cdrom_file *file)
-{
- if (file == nullptr)
- return nullptr;
-
- return &file->cdtoc;
-}
-
-
-
/***************************************************************************
EXTRA UTILITIES
***************************************************************************/
/*-------------------------------------------------
- cdrom_get_info_from_type_string
+ get_info_from_type_string
take a string and convert it into track type
and track data size
-------------------------------------------------*/
/**
- * @fn static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
+ * @fn static void get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
*
* @brief Cdrom get information from type string.
*
@@ -868,7 +640,7 @@ const cdrom_toc *cdrom_get_toc(cdrom_file *file)
* @param [in,out] datasize If non-null, the datasize.
*/
-static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
+void cdrom_file::get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize)
{
if (!strcmp(typestring, "MODE1"))
{
@@ -925,11 +697,6 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
*trktype = CD_TRACK_MODE2_FORM_MIX;
*datasize = 2336;
}
- else if (!strcmp(typestring, "MODE2/2336"))
- {
- *trktype = CD_TRACK_MODE2_FORM_MIX;
- *datasize = 2336;
- }
else if (!strcmp(typestring, "MODE2_RAW"))
{
*trktype = CD_TRACK_MODE2_RAW;
@@ -940,6 +707,11 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
*trktype = CD_TRACK_MODE2_RAW;
*datasize = 2352;
}
+ else if (!strcmp(typestring, "CDI/2352"))
+ {
+ *trktype = CD_TRACK_MODE2_RAW;
+ *datasize = 2352;
+ }
else if (!strcmp(typestring, "AUDIO"))
{
*trktype = CD_TRACK_AUDIO;
@@ -948,61 +720,61 @@ static void cdrom_get_info_from_type_string(const char *typestring, uint32_t *tr
}
/*-------------------------------------------------
- cdrom_convert_type_string_to_track_info -
+ convert_type_string_to_track_info -
take a string and convert it into track type
and track data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_type_string_to_track_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert type string to track information.
+ * @brief Convert type string to track information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_type_string_to_track_info(const char *typestring, track_info *info)
{
- cdrom_get_info_from_type_string(typestring, &info->trktype, &info->datasize);
+ get_info_from_type_string(typestring, &info->trktype, &info->datasize);
}
/*-------------------------------------------------
- cdrom_convert_type_string_to_pregap_info -
+ convert_type_string_to_pregap_info -
take a string and convert it into pregap type
and pregap data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_type_string_to_pregap_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert type string to pregap information.
+ * @brief Convert type string to pregap information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_type_string_to_pregap_info(const char *typestring, track_info *info)
{
- cdrom_get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize);
+ get_info_from_type_string(typestring, &info->pgtype, &info->pgdatasize);
}
/*-------------------------------------------------
- cdrom_convert_subtype_string_to_track_info -
+ convert_subtype_string_to_track_info -
take a string and convert it into track subtype
and track subcode data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_subtype_string_to_track_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert subtype string to track information.
+ * @brief Convert subtype string to track information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_subtype_string_to_track_info(const char *typestring, track_info *info)
{
if (!strcmp(typestring, "RW"))
{
@@ -1017,21 +789,21 @@ void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_tr
}
/*-------------------------------------------------
- cdrom_convert_subtype_string_to_pregap_info -
+ convert_subtype_string_to_pregap_info -
take a string and convert it into track subtype
and track subcode data size
-------------------------------------------------*/
/**
- * @fn void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+ * @fn void convert_subtype_string_to_pregap_info(const char *typestring, track_info *info)
*
- * @brief Cdrom convert subtype string to pregap information.
+ * @brief Convert subtype string to pregap information.
*
* @param typestring The typestring.
* @param [in,out] info If non-null, the information.
*/
-void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info)
+void cdrom_file::convert_subtype_string_to_pregap_info(const char *typestring, track_info *info)
{
if (!strcmp(typestring, "RW"))
{
@@ -1046,21 +818,21 @@ void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_t
}
/*-------------------------------------------------
- cdrom_get_type_string - get the string
+ get_type_string - get the string
associated with the given type
-------------------------------------------------*/
/**
- * @fn const char *cdrom_get_type_string(uint32_t trktype)
+ * @fn const char *get_type_string(uint32_t trktype)
*
- * @brief Cdrom get type string.
+ * @brief Get type string.
*
* @param trktype The trktype.
*
* @return null if it fails, else a char*.
*/
-const char *cdrom_get_type_string(uint32_t trktype)
+const char *cdrom_file::get_type_string(uint32_t trktype)
{
switch (trktype)
{
@@ -1078,21 +850,21 @@ const char *cdrom_get_type_string(uint32_t trktype)
/*-------------------------------------------------
- cdrom_get_subtype_string - get the string
+ get_subtype_string - get the string
associated with the given subcode type
-------------------------------------------------*/
/**
- * @fn const char *cdrom_get_subtype_string(uint32_t subtype)
+ * @fn const char *get_subtype_string(uint32_t subtype)
*
- * @brief Cdrom get subtype string.
+ * @brief Get subtype string.
*
* @param subtype The subtype.
*
* @return null if it fails, else a char*.
*/
-const char *cdrom_get_subtype_string(uint32_t subtype)
+const char *cdrom_file::get_subtype_string(uint32_t subtype)
{
switch (subtype)
{
@@ -1109,112 +881,98 @@ const char *cdrom_get_subtype_string(uint32_t subtype)
***************************************************************************/
/*-------------------------------------------------
- cdrom_parse_metadata - parse metadata into the
+ parse_metadata - parse metadata into the
TOC structure
-------------------------------------------------*/
/**
- * @fn chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
+ * @fn std::error_condition cdrom_parse_metadata(chd_file *chd, toc *toc)
*
- * @brief Cdrom parse metadata.
+ * @brief Parse metadata.
*
* @param [in,out] chd If non-null, the chd.
* @param [in,out] toc If non-null, the TOC.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
+std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
{
std::string metadata;
- chd_error err;
- int i;
+ std::error_condition err;
- toc->flags = 0;
+ /* clear structures */
+ memset(&toc, 0, sizeof(toc));
+
+ toc.numsessions = 1;
/* start with no tracks */
- for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++)
+ for (toc.numtrks = 0; toc.numtrks < MAX_TRACKS; toc.numtrks++)
{
- int tracknum = -1, frames = 0, pregap, postgap, padframes;
+ int tracknum, frames, pregap, postgap, padframes;
char type[16], subtype[16], pgtype[16], pgsub[16];
- cdrom_track_info *track;
+ track_info *track;
- pregap = postgap = padframes = 0;
+ tracknum = -1;
+ frames = pregap = postgap = padframes = 0;
+ std::fill(std::begin(type), std::end(type), 0);
+ std::fill(std::begin(subtype), std::end(subtype), 0);
+ std::fill(std::begin(pgtype), std::end(pgtype), 0);
+ std::fill(std::begin(pgsub), std::end(pgsub), 0);
- /* fetch the metadata for this track */
- err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata);
- if (err == CHDERR_NONE)
+ // fetch the metadata for this track
+ if (!chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc.numtrks, metadata))
{
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pgtype[0] = pgsub[0] = 0;
if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4)
- return CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_INVALID_DATA;
- track = &toc->tracks[tracknum - 1];
+ return chd_file::error::INVALID_DATA;
+ }
+ else if (!chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc.numtrks, metadata))
+ {
+ if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
+ return chd_file::error::INVALID_DATA;
}
else
{
- err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata);
- if (err == CHDERR_NONE)
- {
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pregap = postgap = 0;
- if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8)
- return CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_INVALID_DATA;
- track = &toc->tracks[tracknum - 1];
- }
+ // fall through to GD-ROM detection
+ err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
+ if (!err)
+ toc.flags |= CD_FLAG_GDROMLE; // legacy GDROM track was detected
else
- {
- err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc->numtrks, metadata);
- if (err == CHDERR_NONE)
- /* legacy GDROM track was detected */
- toc->flags |= CD_FLAG_GDROMLE;
- else
- err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata);
+ err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc.numtrks, metadata);
- if (err == CHDERR_NONE)
- {
- /* parse the metadata */
- type[0] = subtype[0] = 0;
- pregap = postgap = 0;
- if (sscanf(metadata.c_str(), GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9)
- return CHDERR_INVALID_DATA;
- if (tracknum == 0 || tracknum > CD_MAX_TRACKS)
- return CHDERR_INVALID_DATA;
- track = &toc->tracks[tracknum - 1];
- toc->flags |= CD_FLAG_GDROM;
- }
- else
- {
- break;
- }
- }
+ if (err)
+ break;
+
+ if (sscanf(metadata.c_str(), GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9)
+ return chd_file::error::INVALID_DATA;
+
+ toc.flags |= CD_FLAG_GDROM;
}
- /* extract the track type and determine the data size */
+ if (tracknum == 0 || tracknum > MAX_TRACKS)
+ return chd_file::error::INVALID_DATA;
+
+ track = &toc.tracks[tracknum - 1];
+
+ // extract the track type and determine the data size
track->trktype = CD_TRACK_MODE1;
track->datasize = 0;
- cdrom_convert_type_string_to_track_info(type, track);
+ convert_type_string_to_track_info(type, track);
if (track->datasize == 0)
- return CHDERR_INVALID_DATA;
+ return chd_file::error::INVALID_DATA;
- /* extract the subtype and determine the subcode data size */
+ // extract the subtype and determine the subcode data size
track->subtype = CD_SUB_NONE;
track->subsize = 0;
- cdrom_convert_subtype_string_to_track_info(subtype, track);
+ convert_subtype_string_to_track_info(subtype, track);
- /* set the frames and extra frames data */
+ // set the frames and extra frames data
track->frames = frames;
track->padframes = padframes;
- int padded = (frames + CD_TRACK_PADDING - 1) / CD_TRACK_PADDING;
- track->extraframes = padded * CD_TRACK_PADDING - frames;
+ int padded = (frames + TRACK_PADDING - 1) / TRACK_PADDING;
+ track->extraframes = padded * TRACK_PADDING - frames;
- /* set the pregap info */
+ // set the pregap info
track->pregap = pregap;
track->pgtype = CD_TRACK_MODE1;
track->pgsub = CD_SUB_NONE;
@@ -1224,10 +982,10 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
{
if (pgtype[0] == 'V')
{
- cdrom_convert_type_string_to_pregap_info(&pgtype[1], track);
+ convert_type_string_to_pregap_info(&pgtype[1], track);
}
- cdrom_convert_subtype_string_to_pregap_info(pgsub, track);
+ convert_subtype_string_to_pregap_info(pgsub, track);
}
/* set the postgap info */
@@ -1235,113 +993,116 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc)
}
/* if we got any tracks this way, we're done */
- if (toc->numtrks > 0)
- return CHDERR_NONE;
+ if (toc.numtrks > 0)
+ return std::error_condition();
- printf("toc->numtrks = %u?!\n", toc->numtrks);
+ osd_printf_info("toc.numtrks = %u?!\n", toc.numtrks);
/* look for old-style metadata */
std::vector<uint8_t> oldmetadata;
err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata);
- if (err != CHDERR_NONE)
+ if (err)
return err;
/* reconstruct the TOC from it */
- uint32_t *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
- toc->numtrks = *mrp++;
+ auto *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
+ toc.numtrks = *mrp++;
- for (i = 0; i < CD_MAX_TRACKS; i++)
+ toc.numsessions = 1;
+
+ for (int i = 0; i < MAX_TRACKS; i++)
{
- toc->tracks[i].trktype = *mrp++;
- toc->tracks[i].subtype = *mrp++;
- toc->tracks[i].datasize = *mrp++;
- toc->tracks[i].subsize = *mrp++;
- toc->tracks[i].frames = *mrp++;
- toc->tracks[i].extraframes = *mrp++;
- toc->tracks[i].pregap = 0;
- toc->tracks[i].postgap = 0;
- toc->tracks[i].pgtype = 0;
- toc->tracks[i].pgsub = 0;
- toc->tracks[i].pgdatasize = 0;
- toc->tracks[i].pgsubsize = 0;
+ toc.tracks[i].session = 0;
+ toc.tracks[i].trktype = *mrp++;
+ toc.tracks[i].subtype = *mrp++;
+ toc.tracks[i].datasize = *mrp++;
+ toc.tracks[i].subsize = *mrp++;
+ toc.tracks[i].frames = *mrp++;
+ toc.tracks[i].extraframes = *mrp++;
+ toc.tracks[i].pregap = 0;
+ toc.tracks[i].postgap = 0;
+ toc.tracks[i].pgtype = 0;
+ toc.tracks[i].pgsub = 0;
+ toc.tracks[i].pgdatasize = 0;
+ toc.tracks[i].pgsubsize = 0;
}
/* TODO: I don't know why sometimes the data is one endian and sometimes another */
- if (toc->numtrks > CD_MAX_TRACKS)
+ if (toc.numtrks > MAX_TRACKS)
{
- toc->numtrks = swapendian_int32(toc->numtrks);
- for (i = 0; i < CD_MAX_TRACKS; i++)
+ toc.numtrks = swapendian_int32(toc.numtrks);
+ for (int i = 0; i < MAX_TRACKS; i++)
{
- toc->tracks[i].trktype = swapendian_int32(toc->tracks[i].trktype);
- toc->tracks[i].subtype = swapendian_int32(toc->tracks[i].subtype);
- toc->tracks[i].datasize = swapendian_int32(toc->tracks[i].datasize);
- toc->tracks[i].subsize = swapendian_int32(toc->tracks[i].subsize);
- toc->tracks[i].frames = swapendian_int32(toc->tracks[i].frames);
- toc->tracks[i].padframes = swapendian_int32(toc->tracks[i].padframes);
- toc->tracks[i].extraframes = swapendian_int32(toc->tracks[i].extraframes);
+ toc.tracks[i].trktype = swapendian_int32(toc.tracks[i].trktype);
+ toc.tracks[i].subtype = swapendian_int32(toc.tracks[i].subtype);
+ toc.tracks[i].datasize = swapendian_int32(toc.tracks[i].datasize);
+ toc.tracks[i].subsize = swapendian_int32(toc.tracks[i].subsize);
+ toc.tracks[i].frames = swapendian_int32(toc.tracks[i].frames);
+ toc.tracks[i].padframes = swapendian_int32(toc.tracks[i].padframes);
+ toc.tracks[i].extraframes = swapendian_int32(toc.tracks[i].extraframes);
}
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/*-------------------------------------------------
- cdrom_write_metadata - write metadata
+ write_metadata - write metadata
-------------------------------------------------*/
/**
- * @fn chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
+ * @fn std::error_condition write_metadata(chd_file *chd, const toc *toc)
*
- * @brief Cdrom write metadata.
+ * @brief Write metadata.
*
* @param [in,out] chd If non-null, the chd.
* @param toc The TOC.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
+std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
{
- chd_error err;
+ std::error_condition err;
/* write the metadata */
- for (int i = 0; i < toc->numtrks; i++)
+ for (int i = 0; i < toc.numtrks; i++)
{
std::string metadata;
- if (!(toc->flags & CD_FLAG_GDROM))
+ if (toc.flags & CD_FLAG_GDROM)
+ {
+ metadata = util::string_format(GDROM_TRACK_METADATA_FORMAT, i + 1, get_type_string(toc.tracks[i].trktype),
+ get_subtype_string(toc.tracks[i].subtype), toc.tracks[i].frames, toc.tracks[i].padframes,
+ toc.tracks[i].pregap, get_type_string(toc.tracks[i].pgtype),
+ get_subtype_string(toc.tracks[i].pgsub), toc.tracks[i].postgap);
+
+ err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
+ }
+ else
{
char submode[32];
- if (toc->tracks[i].pgdatasize > 0)
+ if (toc.tracks[i].pgdatasize > 0)
{
- strcpy(&submode[1], cdrom_get_type_string(toc->tracks[i].pgtype));
+ strcpy(&submode[1], get_type_string(toc.tracks[i].pgtype));
submode[0] = 'V'; // indicate valid submode
}
else
{
- strcpy(submode, cdrom_get_type_string(toc->tracks[i].pgtype));
+ strcpy(submode, get_type_string(toc.tracks[i].pgtype));
}
- metadata = string_format(CDROM_TRACK_METADATA2_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
- cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].pregap,
- submode, cdrom_get_subtype_string(toc->tracks[i].pgsub),
- toc->tracks[i].postgap);
+ metadata = util::string_format(CDROM_TRACK_METADATA2_FORMAT, i + 1, get_type_string(toc.tracks[i].trktype),
+ get_subtype_string(toc.tracks[i].subtype), toc.tracks[i].frames, toc.tracks[i].pregap,
+ submode, get_subtype_string(toc.tracks[i].pgsub),
+ toc.tracks[i].postgap);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
}
- else
- {
- metadata = string_format(GDROM_TRACK_METADATA_FORMAT, i + 1, cdrom_get_type_string(toc->tracks[i].trktype),
- cdrom_get_subtype_string(toc->tracks[i].subtype), toc->tracks[i].frames, toc->tracks[i].padframes,
- toc->tracks[i].pregap, cdrom_get_type_string(toc->tracks[i].pgtype),
- cdrom_get_subtype_string(toc->tracks[i].pgsub), toc->tracks[i].postgap);
-
- err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata);
- }
- if (err != CHDERR_NONE)
+ if (err)
return err;
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/**
@@ -1350,7 +1111,7 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc)
* -------------------------------------------------.
*/
-static const uint8_t ecclow[256] =
+const uint8_t cdrom_file::ecclow[256] =
{
0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e, 0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
@@ -1371,7 +1132,7 @@ static const uint8_t ecclow[256] =
};
/** @brief The ecchigh[ 256]. */
-static const uint8_t ecchigh[256] =
+const uint8_t cdrom_file::ecchigh[256] =
{
0x00, 0xf4, 0xf5, 0x01, 0xf7, 0x03, 0x02, 0xf6, 0xf3, 0x07, 0x06, 0xf2, 0x04, 0xf0, 0xf1, 0x05,
0xfb, 0x0f, 0x0e, 0xfa, 0x0c, 0xf8, 0xf9, 0x0d, 0x08, 0xfc, 0xfd, 0x09, 0xff, 0x0b, 0x0a, 0xfe,
@@ -1398,7 +1159,7 @@ static const uint8_t ecchigh[256] =
* -------------------------------------------------.
*/
-static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
+const uint16_t cdrom_file::poffsets[cdrom_file::ECC_P_NUM_BYTES][cdrom_file::ECC_P_COMP] =
{
{ 0x000,0x056,0x0ac,0x102,0x158,0x1ae,0x204,0x25a,0x2b0,0x306,0x35c,0x3b2,0x408,0x45e,0x4b4,0x50a,0x560,0x5b6,0x60c,0x662,0x6b8,0x70e,0x764,0x7ba },
{ 0x001,0x057,0x0ad,0x103,0x159,0x1af,0x205,0x25b,0x2b1,0x307,0x35d,0x3b3,0x409,0x45f,0x4b5,0x50b,0x561,0x5b7,0x60d,0x663,0x6b9,0x70f,0x765,0x7bb },
@@ -1495,7 +1256,7 @@ static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP] =
* -------------------------------------------------.
*/
-static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
+const uint16_t cdrom_file::qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
{
{ 0x000,0x058,0x0b0,0x108,0x160,0x1b8,0x210,0x268,0x2c0,0x318,0x370,0x3c8,0x420,0x478,0x4d0,0x528,0x580,0x5d8,0x630,0x688,0x6e0,0x738,0x790,0x7e8,0x840,0x898,0x034,0x08c,0x0e4,0x13c,0x194,0x1ec,0x244,0x29c,0x2f4,0x34c,0x3a4,0x3fc,0x454,0x4ac,0x504,0x55c,0x5b4 },
{ 0x001,0x059,0x0b1,0x109,0x161,0x1b9,0x211,0x269,0x2c1,0x319,0x371,0x3c9,0x421,0x479,0x4d1,0x529,0x581,0x5d9,0x631,0x689,0x6e1,0x739,0x791,0x7e9,0x841,0x899,0x035,0x08d,0x0e5,0x13d,0x195,0x1ed,0x245,0x29d,0x2f5,0x34d,0x3a5,0x3fd,0x455,0x4ad,0x505,0x55d,0x5b5 },
@@ -1558,7 +1319,7 @@ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP] =
// particular to a mode
//-------------------------------------------------
-inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
+uint8_t cdrom_file::ecc_source_byte(const uint8_t *sector, uint32_t offset)
{
// in mode 2 always treat these as 0 bytes
return (sector[MODE_OFFSET] == 2 && offset < 4) ? 0x00 : sector[SYNC_OFFSET + SYNC_NUM_BYTES + offset];
@@ -1578,7 +1339,7 @@ inline uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset)
* @param [in,out] val2 The second value.
*/
-void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
+void cdrom_file::ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2)
{
val1 = val2 = 0;
for (int component = 0; component < rowlen; component++)
@@ -1603,7 +1364,7 @@ void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, u
* @return true if it succeeds, false if it fails.
*/
-bool ecc_verify(const uint8_t *sector)
+bool cdrom_file::ecc_verify(const uint8_t *sector)
{
// first verify P bytes
for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++)
@@ -1636,7 +1397,7 @@ bool ecc_verify(const uint8_t *sector)
* @param [in,out] sector If non-null, the sector.
*/
-void ecc_generate(uint8_t *sector)
+void cdrom_file::ecc_generate(uint8_t *sector)
{
// first verify P bytes
for (int byte = 0; byte < ECC_P_NUM_BYTES; byte++)
@@ -1657,8 +1418,1720 @@ void ecc_generate(uint8_t *sector)
* @param [in,out] sector If non-null, the sector.
*/
-void ecc_clear(uint8_t *sector)
+void cdrom_file::ecc_clear(uint8_t *sector)
{
memset(&sector[ECC_P_OFFSET], 0, 2 * ECC_P_NUM_BYTES);
memset(&sector[ECC_Q_OFFSET], 0, 2 * ECC_Q_NUM_BYTES);
}
+
+/***************************************************************************
+
+ TOC parser
+ Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi
+
+***************************************************************************/
+
+/***************************************************************************
+ CONSTANTS & DEFINES
+***************************************************************************/
+
+/**
+ * @def TOKENIZE();
+ *
+ * @brief A macro that defines tokenize.
+ *
+ * @param linebuffer The linebuffer.
+ * @param i Zero-based index of the.
+ * @param std::size(linebuffer) The std::size(linebuffer)
+ * @param token The token.
+ * @param std::size(token) The std::size(token)
+ */
+
+#define TOKENIZE i = tokenize( linebuffer, i, std::size(linebuffer), token, std::size(token) );
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+/**
+ * @fn std::string get_file_path(std::string &path)
+ *
+ * @brief Gets file path.
+ *
+ * @param [in,out] path Full pathname of the file.
+ *
+ * @return The file path.
+ */
+
+std::string cdrom_file::get_file_path(std::string &path)
+{
+ int pos = path.find_last_of('\\');
+ if (pos!=-1)
+ {
+ path = path.substr(0,pos+1);
+ }
+ else
+ {
+ pos = path.find_last_of('/');
+ path = path.substr(0,pos+1);
+ }
+ return path;
+}
+/*-------------------------------------------------
+ get_file_size - get the size of a file
+-------------------------------------------------*/
+
+/**
+ * @fn static uint64_t get_file_size(std::string_view filename)
+ *
+ * @brief Gets file size.
+ *
+ * @param filename Filename of the file.
+ *
+ * @return The file size.
+ */
+
+uint64_t cdrom_file::get_file_size(std::string_view filename)
+{
+ osd_file::ptr file;
+ std::uint64_t filesize = 0;
+
+ osd_file::open(std::string(filename), OPEN_FLAG_READ, file, filesize); // FIXME: allow osd_file to accept std::string_view
+
+ return filesize;
+}
+
+
+/*-------------------------------------------------
+ tokenize - get a token from the line buffer
+-------------------------------------------------*/
+
+/**
+ * @fn static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
+ *
+ * @brief Tokenizes.
+ *
+ * @param linebuffer The linebuffer.
+ * @param i Zero-based index of the.
+ * @param linebuffersize The linebuffersize.
+ * @param [in,out] token If non-null, the token.
+ * @param tokensize The tokensize.
+ *
+ * @return An int.
+ */
+
+int cdrom_file::tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize)
+{
+ int j = 0;
+ bool singlequote = false;
+ bool doublequote = false;
+
+ while ((i < linebuffersize) && isspace((uint8_t)linebuffer[i]))
+ {
+ i++;
+ }
+
+ while ((i < linebuffersize) && (j < tokensize) && (linebuffer[i] != '\0'))
+ {
+ if (!singlequote && linebuffer[i] == '"')
+ {
+ doublequote = !doublequote;
+ }
+ else if (!doublequote && linebuffer[i] == '\'')
+ {
+ singlequote = !singlequote;
+ }
+ else if (!singlequote && !doublequote && isspace((uint8_t)linebuffer[i]))
+ {
+ break;
+ }
+ else
+ {
+ token[j] = linebuffer[i];
+ j++;
+ }
+
+ i++;
+ }
+
+ token[j] = '\0';
+
+ return i;
+}
+
+
+/*-------------------------------------------------
+ msf_to_frames - convert m:s:f into a number of frames
+-------------------------------------------------*/
+
+/**
+ * @fn static int msf_to_frames( char *token )
+ *
+ * @brief Msf to frames.
+ *
+ * @param [in,out] token If non-null, the token.
+ *
+ * @return An int.
+ */
+
+int cdrom_file::msf_to_frames(const char *token)
+{
+ int m = 0;
+ int s = 0;
+ int f = 0;
+
+ if (sscanf(token, "%d:%d:%d", &m, &s, &f) == 1)
+ {
+ f = m;
+ }
+ else
+ {
+ // convert to just frames
+ s += (m * 60);
+ f += (s * 75);
+ }
+
+ return f;
+}
+
+/*-------------------------------------------------
+ parse_wav_sample - takes a .WAV file, verifies
+ that the file is 16 bits, and returns the
+ length in bytes of the data and the offset in
+ bytes to where the data starts in the file.
+-------------------------------------------------*/
+
+/**
+ * @fn static uint32_t parse_wav_sample(std::string_view filename, uint32_t *dataoffs)
+ *
+ * @brief Parse WAV sample.
+ *
+ * @param filename Filename of the file.
+ * @param [in,out] dataoffs If non-null, the dataoffs.
+ *
+ * @return An uint32_t.
+ */
+
+uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *dataoffs)
+{
+ unsigned long offset = 0;
+ uint32_t length, rate, filesize;
+ uint16_t bits, temp16;
+ char buf[32];
+ osd_file::ptr file;
+ uint64_t fsize = 0;
+ std::uint32_t actual;
+
+ std::string fname = std::string(filename);
+ std::error_condition const filerr = osd_file::open(fname, OPEN_FLAG_READ, file, fsize);
+ if (filerr)
+ {
+ osd_printf_error("ERROR: could not open (%s)\n", fname);
+ return 0;
+ }
+
+ /* read the core header and make sure it's a WAVE file */
+ file->read(buf, 0, 4, actual);
+ offset += actual;
+ if (offset < 4)
+ {
+ osd_printf_error("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ if (memcmp(&buf[0], "RIFF", 4) != 0)
+ {
+ osd_printf_error("ERROR: could not find RIFF header (%s)\n", fname);
+ return 0;
+ }
+
+ /* get the total size */
+ file->read(&filesize, offset, 4, actual);
+ offset += actual;
+ if (offset < 8)
+ {
+ osd_printf_error("ERROR: unexpected size offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ filesize = little_endianize_int32(filesize);
+
+ /* read the RIFF file type and make sure it's a WAVE file */
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ if (offset < 12)
+ {
+ osd_printf_error("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname);
+ return 0;
+ }
+ if (memcmp(&buf[0], "WAVE", 4) != 0)
+ {
+ osd_printf_error("ERROR: could not find WAVE header (%s)\n", fname);
+ return 0;
+ }
+
+ /* seek until we find a format tag */
+ while (true)
+ {
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ file->read(&length, offset, 4, actual);
+ offset += actual;
+ length = little_endianize_int32(length);
+ if (memcmp(&buf[0], "fmt ", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_error("ERROR: could not find fmt tag (%s)\n", fname);
+ return 0;
+ }
+ }
+
+ /* read the format -- make sure it is PCM */
+ file->read(&temp16, offset, 2, actual);
+ offset += actual;
+ temp16 = little_endianize_int16(temp16);
+ if (temp16 != 1)
+ {
+ osd_printf_error("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname);
+ return 0;
+ }
+
+ /* number of channels -- only stereo is supported */
+ file->read(&temp16, offset, 2, actual);
+ offset += actual;
+ temp16 = little_endianize_int16(temp16);
+ if (temp16 != 2)
+ {
+ osd_printf_error("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname);
+ return 0;
+ }
+
+ /* sample rate */
+ file->read(&rate, offset, 4, actual);
+ offset += actual;
+ rate = little_endianize_int32(rate);
+ if (rate != 44100)
+ {
+ osd_printf_error("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname);
+ return 0;
+ }
+
+ /* bytes/second and block alignment are ignored */
+ file->read(buf, offset, 6, actual);
+ offset += actual;
+
+ /* bits/sample */
+ file->read(&bits, offset, 2, actual);
+ offset += actual;
+ bits = little_endianize_int16(bits);
+ if (bits != 16)
+ {
+ osd_printf_error("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname);
+ return 0;
+ }
+
+ /* seek past any extra data */
+ offset += length - 16;
+
+ /* seek until we find a data tag */
+ while (true)
+ {
+ file->read(buf, offset, 4, actual);
+ offset += actual;
+ file->read(&length, offset, 4, actual);
+ offset += actual;
+ length = little_endianize_int32(length);
+ if (memcmp(&buf[0], "data", 4) == 0)
+ break;
+
+ /* seek to the next block */
+ offset += length;
+ if (offset >= filesize)
+ {
+ osd_printf_error("ERROR: could not find data tag (%s)\n", fname);
+ return 0;
+ }
+ }
+
+ /* if there was a 0 length data block, we're done */
+ if (length == 0)
+ {
+ osd_printf_error("ERROR: empty data block (%s)\n", fname);
+ return 0;
+ }
+
+ *dataoffs = offset;
+
+ return length;
+}
+
+/**
+ * @fn uint16_t read_uint16(FILE *infile)
+ *
+ * @brief Reads uint 16.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 16.
+ */
+
+uint16_t cdrom_file::read_uint16(FILE *infile)
+{
+ unsigned char buffer[2];
+
+ fread(buffer, 2, 1, infile);
+
+ return get_u16be(buffer);
+}
+
+/**
+ * @fn uint32_t read_uint32(FILE *infile)
+ *
+ * @brief Reads uint 32.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 32.
+ */
+
+uint32_t cdrom_file::read_uint32(FILE *infile)
+{
+ unsigned char buffer[4];
+
+ fread(buffer, 4, 1, infile);
+
+ return get_u32be(buffer);
+}
+
+/**
+ * @fn uint64_t read_uint64(FILE *infile)
+ *
+ * @brief Reads uint 64.
+ *
+ * @param [in,out] infile If non-null, the infile.
+ *
+ * @return The uint 64.
+ */
+
+uint64_t cdrom_file::read_uint64(FILE *infile)
+{
+ unsigned char buffer[8];
+
+ fread(buffer, 8, 1, infile);
+
+ return get_u64be(buffer);
+}
+
+/*-------------------------------------------------
+ parse_nero - parse a Nero .NRG file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse nero.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ unsigned char buffer[12];
+ uint32_t chain_offs, chunk_size;
+ int done = 0;
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rb");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ outtoc.numsessions = 1;
+
+ // seek to 12 bytes before the end
+ fseek(infile, -12, SEEK_END);
+ fread(buffer, 12, 1, infile);
+
+ if (memcmp(buffer, "NER5", 4))
+ {
+ osd_printf_error("ERROR: Not a Nero 5.5 or later image!\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ chain_offs = get_u32be(&buffer[8]);
+
+ if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0))
+ {
+ osd_printf_error("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+// printf("NER5 detected, chain offset: %x\n", chain_offs);
+
+ while (!done)
+ {
+ fseek(infile, chain_offs, SEEK_SET);
+ fread(buffer, 8, 1, infile);
+
+ chunk_size = get_u32be(&buffer[4]);
+
+// printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size);
+
+ // we want the DAOX chunk, which has the TOC information
+ if (!memcmp(buffer, "DAOX", 4))
+ {
+ // skip second chunk size and UPC code
+ fseek(infile, 20, SEEK_CUR);
+
+ uint8_t start, end;
+ fread(&start, 1, 1, infile);
+ fread(&end, 1, 1, infile);
+
+// printf("Start track %d End track: %d\n", start, end);
+
+ outtoc.numtrks = (end-start) + 1;
+
+ uint32_t offset = 0;
+ for (int track = start; track <= end; track++)
+ {
+ uint32_t size, mode;
+ uint64_t index0, index1, track_end;
+
+ fseek(infile, 12, SEEK_CUR); // skip ISRC code
+ size = read_uint16(infile);
+ mode = read_uint16(infile);
+ fseek(infile, 2, SEEK_CUR);
+ index0 = read_uint64(infile);
+ index1 = read_uint64(infile);
+ track_end = read_uint64(infile);
+
+// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx track_end %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, track_end, (uint32_t)(index1-index0)/size, (uint32_t)(track_end-index1)/size);
+ outinfo.track[track-1].fname.assign(tocfname);
+ outinfo.track[track-1].offset = offset + (uint32_t)(index1-index0);
+ outinfo.track[track-1].idx[0] = outinfo.track[track-1].idx[1] = 0;
+
+ switch (mode)
+ {
+ case 0x0000: // 2048 byte data
+ outtoc.tracks[track-1].trktype = CD_TRACK_MODE1;
+ outinfo.track[track-1].swap = false;
+ break;
+
+ case 0x0300: // Mode 2 Form 1
+ osd_printf_error("ERROR: Mode 2 Form 1 tracks not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x0500: // raw data
+ osd_printf_error("ERROR: Raw data tracks not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x0600: // 2352 byte mode 2 raw
+ outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
+ outinfo.track[track-1].swap = false;
+ break;
+
+ case 0x0700: // 2352 byte audio
+ outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO;
+ outinfo.track[track-1].swap = true;
+ break;
+
+ case 0x0f00: // raw data with sub-channel
+ osd_printf_error("ERROR: Raw data tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x1000: // audio with sub-channel
+ osd_printf_error("ERROR: Audio tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ case 0x1100: // raw Mode 2 Form 1 with sub-channel
+ osd_printf_error("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n");
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+
+ default:
+ osd_printf_error("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
+ fclose(infile);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ outtoc.tracks[track-1].datasize = size;
+
+ outtoc.tracks[track-1].subtype = CD_SUB_NONE;
+ outtoc.tracks[track-1].subsize = 0;
+
+ outtoc.tracks[track-1].pregap = (uint32_t)(index1-index0)/size;
+ outtoc.tracks[track-1].frames = (uint32_t)(track_end-index1)/size;
+ outtoc.tracks[track-1].postgap = 0;
+ outtoc.tracks[track-1].pgtype = 0;
+ outtoc.tracks[track-1].pgsub = CD_SUB_NONE;
+ outtoc.tracks[track-1].pgdatasize = 0;
+ outtoc.tracks[track-1].pgsubsize = 0;
+ outtoc.tracks[track-1].padframes = 0;
+
+ offset += (uint32_t)track_end-index1;
+ }
+ }
+
+ if (!memcmp(buffer, "END!", 4))
+ {
+ done = 1;
+ }
+ else
+ {
+ chain_offs += chunk_size + 8;
+ }
+ }
+
+ fclose(infile);
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_iso - parse a .ISO file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Parse ISO.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rb");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ uint64_t size = get_file_size(tocfname);
+ fclose(infile);
+
+
+ outtoc.numtrks = 1;
+ outtoc.numsessions = 1;
+
+ outinfo.track[0].fname = tocfname;
+ outinfo.track[0].offset = 0;
+ outinfo.track[0].idx[0] = outinfo.track[0].idx[1] = 0;
+
+ if ((size % 2048) == 0)
+ {
+ outtoc.tracks[0].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[0].frames = size / 2048;
+ outtoc.tracks[0].datasize = 2048;
+ outinfo.track[0].swap = false;
+ }
+ else if ((size % 2336) == 0)
+ {
+ // 2352 byte mode 2
+ outtoc.tracks[0].trktype = CD_TRACK_MODE2;
+ outtoc.tracks[0].frames = size / 2336;
+ outtoc.tracks[0].datasize = 2336;
+ outinfo.track[0].swap = false;
+ }
+ else if ((size % 2352) == 0)
+ {
+ // 2352 byte mode 2 raw
+ outtoc.tracks[0].trktype = CD_TRACK_MODE2_RAW;
+ outtoc.tracks[0].frames = size / 2352;
+ outtoc.tracks[0].datasize = 2352;
+ outinfo.track[0].swap = false;
+ }
+ else
+ {
+ osd_printf_error("ERROR: Unrecognized track type\n");
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ outtoc.tracks[0].subtype = CD_SUB_NONE;
+ outtoc.tracks[0].subsize = 0;
+
+ outtoc.tracks[0].pregap = 0;
+
+ outtoc.tracks[0].postgap = 0;
+ outtoc.tracks[0].pgtype = 0;
+ outtoc.tracks[0].pgsub = CD_SUB_NONE;
+ outtoc.tracks[0].pgdatasize = 0;
+ outtoc.tracks[0].pgsubsize = 0;
+ outtoc.tracks[0].padframes = 0;
+
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_gdi - parse a Sega GD-ROM rip
+-------------------------------------------------*/
+
+/**
+ * @fn static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse GDI.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ char token[512];
+ int i = 0;
+ int trackcnt = 0;
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ outtoc.flags = CD_FLAG_GDROM;
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ if (!fgets(linebuffer,511,infile))
+ {
+ osd_printf_error("GDI doesn't have track count (blank file?)\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ i = 0;
+ TOKENIZE
+
+ const int numtracks = atoi(token);
+
+ if (numtracks > 0 && numtracks - 1 > std::size(outinfo.track))
+ {
+ osd_printf_error("GDI expects too many tracks. Expected %d tracks but only up to %zu tracks allowed\n", numtracks, std::size(outinfo.track) + 1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (numtracks == 0)
+ {
+ osd_printf_error("GDI header specifies no tracks\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ while (!feof(infile))
+ {
+ int paramcnt = 0;
+
+ if (!fgets(linebuffer,511,infile))
+ break;
+
+ i = 0;
+ TOKENIZE
+
+ // Ignore empty lines so they're not countered toward the total track count
+ if (!token[0])
+ continue;
+
+ paramcnt++;
+ const int trknum = atoi(token) - 1;
+
+ if (trknum < 0 || trknum > std::size(outinfo.track) || trknum + 1 > numtracks)
+ {
+ osd_printf_error("Track %d is out of expected range of 1 to %d\n", trknum + 1, numtracks);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (outtoc.tracks[trknum].datasize != 0)
+ osd_printf_warning("Track %d defined multiple times?\n", trknum + 1);
+ else
+ trackcnt++;
+
+ outinfo.track[trknum].swap = false;
+ outinfo.track[trknum].offset = 0;
+
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+ outtoc.tracks[trknum].subsize = 0;
+ outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ outtoc.tracks[trknum].physframeofs = atoi(token);
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trktype = atoi(token);
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trksize = atoi(token);
+
+ if (trktype == 4 && trksize == 2352)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1_RAW;
+ outtoc.tracks[trknum].datasize = 2352;
+ }
+ else if (trktype == 4 && trksize == 2048)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[trknum].datasize = 2048;
+ }
+ else if (trktype == 0)
+ {
+ outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
+ outtoc.tracks[trknum].datasize = 2352;
+ outinfo.track[trknum].swap = true;
+ }
+ else
+ {
+ osd_printf_error("Unknown track type %d and track size %d combination encountered\n", trktype, trksize);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ // skip to start of next token
+ int pi = i;
+ while (pi < std::size(linebuffer) && isspace((uint8_t)linebuffer[pi]))
+ pi++;
+
+ if (linebuffer[pi] == '"' && strchr(linebuffer + pi + 1, '"') == nullptr)
+ {
+ osd_printf_error("Track %d filename does not having closing quotation mark: '%s'\n", trknum + 1, linebuffer + pi);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+
+ outinfo.track[trknum].fname.assign(path).append(token);
+
+ const uint64_t sz = get_file_size(outinfo.track[trknum].fname);
+ outtoc.tracks[trknum].frames = sz / trksize;
+ outtoc.tracks[trknum].padframes = 0;
+
+ if (trknum != 0)
+ {
+ const int dif = outtoc.tracks[trknum].physframeofs - (outtoc.tracks[trknum-1].frames + outtoc.tracks[trknum-1].physframeofs);
+ outtoc.tracks[trknum-1].frames += dif;
+ outtoc.tracks[trknum-1].padframes = dif;
+ }
+
+ TOKENIZE
+ // offset parameter, not used
+ if (token[0])
+ paramcnt++;
+
+ // check if there are any extra parameters that shouldn't be there
+ while (token[0])
+ {
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ }
+
+ if (paramcnt != 6)
+ {
+ osd_printf_error("GDI track entry should have 6 parameters, found %d\n", paramcnt);
+ return chd_file::error::INVALID_DATA;
+ }
+ }
+
+ bool missing_tracks = trackcnt != numtracks;
+ for (int i = 0; i < numtracks; i++)
+ {
+ if (outtoc.tracks[i].datasize == 0)
+ {
+ osd_printf_warning("Could not find track %d\n", i + 1);
+ missing_tracks = true;
+ }
+ }
+
+ if (missing_tracks)
+ {
+ osd_printf_error("GDI is missing tracks\n");
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (EXTRA_VERBOSE)
+ for (int i = 0; i < numtracks; i++)
+ {
+ osd_printf_verbose("'%s' %d %d %d (true %d)\n", outinfo.track[i].fname, outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes);
+ }
+
+ /* close the input TOC */
+ fclose(infile);
+
+ /* store the number of tracks found */
+ outtoc.numtrks = numtracks;
+ outtoc.numsessions = 1;
+
+ return std::error_condition();
+}
+
+/*-------------------------------------------------
+ parse_cue - parse a .CUE file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse cue.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ *
+ * Redump multi-CUE for Dreamcast GDI:
+ * Dreamcast discs have two images on a single disc. The first image is SINGLE-DENSITY and the second image
+ * is HIGH-DENSITY. The SINGLE-DENSITY area starts 0 LBA and HIGH-DENSITY area starts 45000 LBA.
+ */
+
+std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ int i, trknum, sessionnum, session_pregap;
+ char token[512];
+ std::string lastfname;
+ uint32_t wavlen, wavoffs;
+ std::string path = std::string(tocfname);
+ const bool is_gdrom = is_gdicue(tocfname);
+ enum gdi_area current_area = SINGLE_DENSITY;
+ bool is_multibin = false;
+ int leadin = -1;
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ trknum = -1;
+ wavoffs = wavlen = 0;
+ sessionnum = 0;
+ session_pregap = 0;
+
+ if (is_gdrom)
+ {
+ outtoc.flags = CD_FLAG_GDROM;
+ }
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ /* get the next line */
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ i = 0;
+
+ TOKENIZE
+
+ if (!strcmp(token, "REM"))
+ {
+ /* skip to actual data of REM command */
+ while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
+ i++;
+
+ if (!strncmp(linebuffer+i, "SESSION", 7))
+ {
+ /* IsoBuster extension */
+ TOKENIZE
+
+ /* get the session number */
+ TOKENIZE
+
+ sessionnum = strtoul(token, nullptr, 10) - 1;
+
+ if (sessionnum >= 1) /* don't consider it a multisession CD unless there's actually more than 1 session */
+ outtoc.flags |= CD_FLAG_MULTISESSION;
+ }
+ else if ((outtoc.flags & CD_FLAG_MULTISESSION) && !strncmp(linebuffer+i, "PREGAP", 6))
+ {
+ /*
+ Redump extension? PREGAP associated with the session instead of the track
+
+ DiscImageCreator - Older versions would write a bogus value here (and maybe session lead-in and lead-out).
+ These should be considered bad dumps and will not be supported.
+ */
+ TOKENIZE
+
+ /* get pregap time */
+ TOKENIZE
+ session_pregap = msf_to_frames(token);
+ }
+ else if (!strncmp(linebuffer+i, "LEAD-OUT", 8))
+ {
+ /*
+ IsoBuster and ImgBurn (single bin file) - Lead-out time is the start of the lead-out
+ lead-out time - MSF of last track of session = size of last track
+
+ Redump and DiscImageCreator (multiple bins) - Lead-out time is the duration of just the lead-out
+ */
+ TOKENIZE
+
+ /* get lead-out time */
+ TOKENIZE
+ int leadout_offset = msf_to_frames(token);
+ outinfo.track[trknum].leadout = leadout_offset;
+ }
+ else if (!strncmp(linebuffer+i, "LEAD-IN", 7))
+ {
+ /*
+ IsoBuster and ImgBurn (single bin file) - Not used?
+ Redump and DiscImageCreator (multiple bins) - Lead-in time is the duration of just the lead-in
+ */
+ TOKENIZE
+
+ /* get lead-in time */
+ TOKENIZE
+ leadin = msf_to_frames(token);
+ }
+ else if (is_gdrom && !strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
+ {
+ /* single-density area starts LBA = 0 */
+ current_area = SINGLE_DENSITY;
+ }
+ else if (is_gdrom && !strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
+ {
+ /* high-density area starts LBA = 45000 */
+ current_area = HIGH_DENSITY;
+ }
+ }
+ else if (!strcmp(token, "FILE"))
+ {
+ /* found the data file for a track */
+ TOKENIZE
+
+ /* keep the filename */
+ if (!is_multibin)
+ {
+ std::string prevfname(std::move(lastfname));
+ lastfname.assign(path).append(token);
+ is_multibin = !prevfname.empty() && lastfname != prevfname;
+ }
+ else
+ {
+ lastfname.assign(path).append(token);
+ }
+
+ /* get the file type */
+ TOKENIZE
+
+ if (!strcmp(token, "BINARY"))
+ {
+ outinfo.track[trknum+1].swap = false;
+ }
+ else if (!strcmp(token, "MOTOROLA"))
+ {
+ outinfo.track[trknum+1].swap = true;
+ }
+ else if (!strcmp(token, "WAVE"))
+ {
+ wavlen = parse_wav_sample(lastfname, &wavoffs);
+ if (!wavlen)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname);
+ return chd_file::error::INVALID_DATA;
+ }
+ }
+ else
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unhandled track type %s\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+ }
+ else if (!strcmp(token, "TRACK"))
+ {
+ /* get the track number */
+ TOKENIZE
+ trknum = strtoul(token, nullptr, 10) - 1;
+
+ /* next token on the line is the track type */
+ TOKENIZE
+
+ outtoc.tracks[trknum].session = sessionnum;
+ outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+ outtoc.tracks[trknum].subsize = 0;
+ outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+ outtoc.tracks[trknum].pregap = 0;
+ outtoc.tracks[trknum].padframes = 0;
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].multicuearea = is_gdrom ? current_area : 0;
+ outinfo.track[trknum].offset = 0;
+ std::fill(std::begin(outinfo.track[trknum].idx), std::end(outinfo.track[trknum].idx), -1);
+
+ outinfo.track[trknum].leadout = -1;
+ outinfo.track[trknum].leadin = leadin; /* use previously saved lead-in value */
+ leadin = -1;
+
+ if (session_pregap != 0)
+ {
+ /*
+ associated the pregap from the session transition with the lead-in to simplify things for now.
+ setting it as the proper pregap for the track causes logframeofs of the last dummy entry in the TOC
+ to become 2s later than it should. this might be an issue with how pgdatasize = 0 pregaps are handled.
+ */
+ if (outinfo.track[trknum].leadin == -1)
+ outinfo.track[trknum].leadin = session_pregap;
+ else
+ outinfo.track[trknum].leadin += session_pregap;
+ session_pregap = 0;
+ }
+
+ if (wavlen != 0)
+ {
+ outtoc.tracks[trknum].frames = wavlen/2352;
+ outinfo.track[trknum].offset = wavoffs;
+ wavoffs = wavlen = 0;
+ }
+
+ outinfo.track[trknum].fname.assign(lastfname); /* default filename to the last one */
+
+ if (EXTRA_VERBOSE)
+ {
+ if (is_gdrom)
+ {
+ osd_printf_verbose("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea);
+ }
+ else
+ {
+ osd_printf_verbose("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset);
+ }
+ }
+
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ /* next (optional) token on the line is the subcode type */
+ TOKENIZE
+
+ convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ }
+ else if (!strcmp(token, "INDEX"))
+ {
+ int idx, frames;
+
+ /* get index number */
+ TOKENIZE
+ idx = strtoul(token, nullptr, 10);
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ if (idx < 0 || idx > MAX_INDEX)
+ {
+ osd_printf_error("ERROR: encountered invalid index %d\n", idx);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ outinfo.track[trknum].idx[idx] = frames;
+
+ if (idx == 1)
+ {
+ if (outtoc.tracks[trknum].pregap == 0 && outinfo.track[trknum].idx[0] != -1)
+ {
+ outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx[0];
+ outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
+ outtoc.tracks[trknum].pgdatasize = outtoc.tracks[trknum].datasize;
+ }
+ else if (outinfo.track[trknum].idx[0] == -1) /* pregap sectors not in file, but we're always using idx 0 for track length calc now */
+ {
+ outinfo.track[trknum].idx[0] = frames;
+ }
+ }
+ }
+ else if (!strcmp(token, "PREGAP"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ outtoc.tracks[trknum].pregap = frames;
+ }
+ else if (!strcmp(token, "POSTGAP"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ outtoc.tracks[trknum].postgap = frames;
+ }
+ else if (!strcmp(token, "FLAGS"))
+ {
+ outtoc.tracks[trknum].control_flags = 0;
+
+ /* keep looping over remaining tokens in FLAGS line until there's no more to read */
+ while (i < std::size(linebuffer))
+ {
+ int last_idx = i;
+
+ TOKENIZE
+
+ if (i == last_idx)
+ break;
+
+ if (!strcmp(token, "DCP"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ else if (!strcmp(token, "4CH"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_4CH;
+ else if (!strcmp(token, "PRE"))
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ }
+ }
+
+ /* close the input CUE */
+ fclose(infile);
+
+ /* store the number of tracks found */
+ outtoc.numtrks = trknum + 1;
+ outtoc.numsessions = sessionnum + 1;
+
+ /* now go over the files again and set the lengths */
+ for (trknum = 0; trknum < outtoc.numtrks; trknum++)
+ {
+ uint64_t tlen = 0;
+
+ if (outinfo.track[trknum].idx[1] == -1)
+ {
+ /* index 1 should always be set */
+ osd_printf_error("ERROR: track %d is missing INDEX 01 marker\n", trknum+1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ /* this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian */
+ if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
+ {
+ outinfo.track[trknum].swap = true;
+ }
+
+ /* don't do this for .WAV tracks, we already have their length and offset filled out */
+ if (outinfo.track[trknum].offset != 0)
+ continue;
+
+ if (trknum+1 >= outtoc.numtrks && trknum > 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname) == 0))
+ {
+ /* if the last track's filename is the same as the previous track */
+ tlen = get_file_size(outinfo.track[trknum].fname);
+ if (tlen == 0)
+ {
+ osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname);
+ return std::errc::no_such_file_or_directory;
+ }
+
+ outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+ outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ }
+ else if (trknum+1 < outtoc.numtrks && outinfo.track[trknum].fname.compare(outinfo.track[trknum+1].fname) == 0)
+ {
+ /* if the current filename is the same as the next track */
+ outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx[0] - outinfo.track[trknum].idx[0];
+
+ if (outtoc.tracks[trknum].frames == 0)
+ {
+ osd_printf_error("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
+ return chd_file::error::INVALID_DATA;
+ }
+
+ if (trknum > 0)
+ {
+ const uint32_t previous_track_raw_size = outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
+ outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + previous_track_raw_size;
+ }
+ }
+ else if (outtoc.tracks[trknum].frames == 0)
+ {
+ /* if the filenames between tracks are different */
+ tlen = get_file_size(outinfo.track[trknum].fname);
+ if (tlen == 0)
+ {
+ osd_printf_error("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname);
+ return std::errc::no_such_file_or_directory;
+ }
+
+ outtoc.tracks[trknum].frames = tlen / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ outinfo.track[trknum].offset = 0;
+ }
+
+ if (outtoc.flags & CD_FLAG_MULTISESSION)
+ {
+ if (is_multibin)
+ {
+ if (outinfo.track[trknum].leadout == -1 && trknum + 1 < outtoc.numtrks && outtoc.tracks[trknum].session != outtoc.tracks[trknum+1].session)
+ {
+ /* add a standard lead-out to the last track before changing sessions */
+ outinfo.track[trknum].leadout = outtoc.tracks[trknum].session == 0 ? 6750 : 2250; /* first session lead-out (1m30s0f) is longer than the rest (0m30s0f) */
+ }
+
+ if (outinfo.track[trknum].leadin == -1 && trknum > 0 && outtoc.tracks[trknum].session != outtoc.tracks[trknum-1].session)
+ {
+ /* add a standard lead-in to the first track of a new session */
+ outinfo.track[trknum].leadin = 4500; /* lead-in (1m0s0f) */
+ }
+ }
+ else
+ {
+ if (outinfo.track[trknum].leadout != -1)
+ {
+ /*
+ if a lead-out time is specified in a multisession CD then the size of the previous track needs to be trimmed
+ to use the lead-out time instead of the idx 0 of the next track
+ */
+ const int endframes = outinfo.track[trknum].leadout - outinfo.track[trknum].idx[0];
+ if (outtoc.tracks[trknum].frames >= endframes)
+ {
+ outtoc.tracks[trknum].frames = endframes; /* trim track length */
+
+ if (trknum + 1 < outtoc.numtrks)
+ {
+ /* lead-out value becomes just the duration between the lead-out to the pre-gap of the next track */
+ outinfo.track[trknum].leadout = outinfo.track[trknum+1].idx[0] - outinfo.track[trknum].leadout;
+ }
+ }
+ }
+
+ if (trknum > 0 && outinfo.track[trknum-1].leadout != -1)
+ {
+ /*
+ ImgBurn bin/cue have dummy data to pad between the lead-out and the start of the next track.
+ DiscImageCreator img/cue does not have any data between the lead-out and the start of the next track.
+
+ Detecting by extension is an awful way to handle this but there's no other way to determine what format
+ the data will be in since we don't know the exact length of the last track just from the cue.
+ */
+ if (!core_filename_ends_with(outinfo.track[trknum-1].fname, ".img"))
+ {
+ outtoc.tracks[trknum-1].padframes += outinfo.track[trknum-1].leadout;
+ outtoc.tracks[trknum].frames -= outinfo.track[trknum-1].leadout;
+ outinfo.track[trknum].offset += outinfo.track[trknum-1].leadout * (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ }
+ }
+ }
+ }
+ }
+
+ if (is_gdrom)
+ {
+ /*
+ * Strip pregaps from Redump tracks and adjust the LBA offset to match TOSEC layout
+ */
+ for (trknum = 1; trknum < outtoc.numtrks; trknum++)
+ {
+ uint32_t this_pregap = outtoc.tracks[trknum].pregap;
+ uint32_t this_offset = this_pregap * (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+
+ outtoc.tracks[trknum-1].frames += this_pregap;
+ outtoc.tracks[trknum-1].splitframes += this_pregap;
+
+ outinfo.track[trknum].offset += this_offset;
+ outtoc.tracks[trknum].frames -= this_pregap;
+ outinfo.track[trknum].idx[1] -= this_pregap;
+
+ outtoc.tracks[trknum].pregap = 0;
+ outtoc.tracks[trknum].pgtype = 0;
+ }
+
+ /*
+ * TOC now matches TOSEC layout, set LBA for every track with HIGH-DENSITY area @ LBA 45000
+ */
+ for (trknum = 1; trknum < outtoc.numtrks; trknum++)
+ {
+ if (outtoc.tracks[trknum].multicuearea == HIGH_DENSITY && outtoc.tracks[trknum-1].multicuearea == SINGLE_DENSITY)
+ {
+ outtoc.tracks[trknum].physframeofs = 45000;
+ int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
+ outtoc.tracks[trknum-1].frames += dif;
+ outtoc.tracks[trknum-1].padframes = dif;
+ }
+ else
+ {
+ outtoc.tracks[trknum].physframeofs = outtoc.tracks[trknum-1].physframeofs + outtoc.tracks[trknum-1].frames;
+ }
+ }
+ }
+
+ if (EXTRA_VERBOSE)
+ {
+ for (trknum = 0; trknum < outtoc.numtrks; trknum++)
+ {
+ osd_printf_verbose("session %d trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, pgdatasize=%d, idx0=%d, idx1=%d, dataframes=%d\n",
+ outtoc.tracks[trknum].session + 1,
+ trknum + 1,
+ outtoc.tracks[trknum].frames,
+ outinfo.track[trknum].offset,
+ outtoc.tracks[trknum].padframes,
+ outtoc.tracks[trknum].splitframes,
+ outtoc.tracks[trknum].multicuearea,
+ outtoc.tracks[trknum].physframeofs,
+ outtoc.tracks[trknum].pregap,
+ outtoc.tracks[trknum].pgtype,
+ outtoc.tracks[trknum].pgdatasize,
+ outinfo.track[trknum].idx[0],
+ outinfo.track[trknum].idx[1],
+ outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
+ }
+ }
+
+ return std::error_condition();
+}
+
+/*---------------------------------------------------------------------------------------
+ is_gdicue - determine if CUE contains Redump multi-CUE format for Dreamcast GDI
+----------------------------------------------------------------------------------------*/
+
+/**
+ * Dreamcast GDI has two images on one disc, SINGLE-DENSITY and HIGH-DENSITY.
+ *
+ * Redump stores both images in a single .cue with a REM comment separating the images.
+ * This multi-cue format replaces the old flawed .gdi format.
+ *
+ * http://forum.redump.org/topic/19969/done-sega-dreamcast-multicue-gdi/
+ *
+ * This function looks for strings "REM SINGLE-DENSITY AREA" & "REM HIGH-DENSITY AREA"
+ * indicating the Redump multi-cue format and therefore a Dreamcast GDI disc.
+ */
+
+bool cdrom_file::is_gdicue(std::string_view tocfname)
+{
+ char token[512];
+ bool has_rem_singledensity = false;
+ bool has_rem_highdensity = false;
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return false;
+ }
+
+ path = get_file_path(path);
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ int i = 0;
+
+ TOKENIZE
+
+ if (!strcmp(token, "REM"))
+ {
+ /* skip to actual data of REM command */
+ while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
+ i++;
+
+ if (!strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
+ has_rem_singledensity = true;
+ else if (!strncmp(linebuffer+i, "HIGH-DENSITY AREA", 17))
+ has_rem_highdensity = true;
+ }
+ }
+
+ fclose(infile);
+
+ return has_rem_singledensity && has_rem_highdensity;
+}
+
+/*-------------------------------------------------
+ parse_toc - parse a CDRDAO format TOC file
+-------------------------------------------------*/
+
+/**
+ * @fn std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+ *
+ * @brief Chdcd parse TOC.
+ *
+ * @param tocfname The tocfname.
+ * @param [in,out] outtoc The outtoc.
+ * @param [in,out] outinfo The outinfo.
+ *
+ * @return A std::error_condition.
+ */
+
+std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
+{
+ char token[512];
+
+ auto pos = tocfname.rfind('.');
+ std::string tocfext = pos == std::string_view::npos ? std::string() : strmakelower(tocfname.substr(pos + 1));
+
+ if (tocfext == "gdi")
+ {
+ return parse_gdi(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "cue")
+ {
+ return parse_cue(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "nrg")
+ {
+ return parse_nero(tocfname, outtoc, outinfo);
+ }
+
+ if (tocfext == "iso" || tocfext == "cdr" || tocfext == "toast")
+ {
+ return parse_iso(tocfname, outtoc, outinfo);
+ }
+
+ std::string path = std::string(tocfname);
+
+ FILE *infile = fopen(path.c_str(), "rt");
+ if (!infile)
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ path = get_file_path(path);
+
+ /* clear structures */
+ memset(&outtoc, 0, sizeof(outtoc));
+ outinfo.reset();
+
+ int trknum = -1;
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
+ {
+ /* get the next line */
+ if (!fgets(linebuffer, 511, infile))
+ break;
+
+ int i = 0;
+
+ TOKENIZE
+
+ /*
+ Samples: https://github.com/cdrdao/cdrdao/tree/master/testtocs
+
+ Unimplemented:
+ CD_TEXT
+ SILENCE
+ ZERO
+ FIFO
+ PREGAP
+ CATALOG
+ ISRC
+ */
+ if (!strcmp(token, "NO"))
+ {
+ TOKENIZE
+ if (!strcmp(token, "COPY"))
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ else if (!strcmp(token, "PRE_EMPHASIS"))
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ else if (!strcmp(token, "COPY"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED;
+ }
+ else if (!strcmp(token, "PRE_EMPHASIS"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_PREEMPHASIS;
+ }
+ else if (!strcmp(token, "TWO_CHANNEL_AUDIO"))
+ {
+ outtoc.tracks[trknum].control_flags &= ~CD_FLAG_CONTROL_4CH;
+ }
+ else if (!strcmp(token, "FOUR_CHANNEL_AUDIO"))
+ {
+ outtoc.tracks[trknum].control_flags |= CD_FLAG_CONTROL_4CH;
+ }
+ else if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
+ {
+ int f;
+
+ /* found the data file for a track */
+ TOKENIZE
+
+ /* keep the filename */
+ outinfo.track[trknum].fname.assign(path).append(token);
+
+ /* get either the offset or the length */
+ TOKENIZE
+
+ if (!strcmp(token, "SWAP"))
+ {
+ TOKENIZE
+
+ outinfo.track[trknum].swap = true;
+ }
+ else
+ {
+ outinfo.track[trknum].swap = false;
+ }
+
+ if (token[0] == '#')
+ {
+ /* it's a decimal offset, use it */
+ f = strtoul(&token[1], nullptr, 10);
+ }
+ else if (isdigit((uint8_t)token[0]))
+ {
+ /* convert the time to an offset */
+ f = msf_to_frames(token);
+
+ f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ }
+ else
+ {
+ f = 0;
+ }
+
+ outinfo.track[trknum].offset = f;
+
+ TOKENIZE
+
+ if (isdigit((uint8_t)token[0]))
+ {
+ // this could be the length or an offset from the previous field.
+ f = msf_to_frames(token);
+
+ TOKENIZE
+
+ if (isdigit((uint8_t)token[0]))
+ {
+ // it was an offset.
+ f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+
+ outinfo.track[trknum].offset += f;
+
+ // this is the length.
+ f = msf_to_frames(token);
+ }
+ }
+ else if (trknum == 0 && outinfo.track[trknum].offset != 0)
+ {
+ /* the 1st track might have a length with no offset */
+ f = outinfo.track[trknum].offset / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
+ outinfo.track[trknum].offset = 0;
+ }
+ else
+ {
+ /* guesstimate the track length? */
+ f = 0;
+ }
+
+ outtoc.tracks[trknum].frames = f;
+ }
+ else if (!strcmp(token, "TRACK"))
+ {
+ trknum++;
+
+ /* next token on the line is the track type */
+ TOKENIZE
+
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[trknum].datasize = 0;
+ outtoc.tracks[trknum].subtype = CD_SUB_NONE;
+ outtoc.tracks[trknum].subsize = 0;
+ outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
+ outtoc.tracks[trknum].padframes = 0;
+
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
+ {
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
+
+ /* next (optional) token on the line is the subcode type */
+ TOKENIZE
+
+ convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ }
+ else if (!strcmp(token, "START"))
+ {
+ int frames;
+
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
+
+ outtoc.tracks[trknum].pregap = frames;
+ }
+ }
+
+ /* close the input TOC */
+ fclose(infile);
+
+ /* store the number of tracks found */
+ outtoc.numtrks = trknum + 1;
+ outtoc.numsessions = 1;
+
+ return std::error_condition();
+}
diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h
index 50b762d5009..b11ecb5b3ba 100644
--- a/src/lib/util/cdrom.h
+++ b/src/lib/util/cdrom.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles,R. Belmont
/***************************************************************************
cdrom.h
@@ -7,176 +7,294 @@
Generic MAME cd-rom implementation
***************************************************************************/
-
-#ifndef MAME_UTIL_CDROM_H
-#define MAME_UTIL_CDROM_H
+#ifndef MAME_LIB_UTIL_CDROM_H
+#define MAME_LIB_UTIL_CDROM_H
#pragma once
-#include "osdcore.h"
#include "chd.h"
+#include "ioprocs.h"
+#include "osdcore.h"
-
-
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-// tracks are padded to a multiple of this many frames
-const uint32_t CD_TRACK_PADDING = 4;
-
-#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */
-#define CD_MAX_SECTOR_DATA (2352)
-#define CD_MAX_SUBCODE_DATA (96)
-
-#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA)
-#define CD_FRAMES_PER_HUNK (8)
-
-#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6))
-
-enum
-{
- CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */
- CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */
- CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */
- CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */
- CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */
- CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */
- CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */
- CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */
-
- CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */
-};
-
-enum
-{
- CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
- CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
- CD_SUB_NONE /* no subcode data stored */
-};
-
-#define CD_FLAG_GDROM 0x00000001 // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
-#define CD_FLAG_GDROMLE 0x00000002 // legacy GD-ROM, with little-endian CDDA data
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-struct cdrom_file;
-
-struct cdrom_track_info
-{
- /* fields used by CHDMAN and in MAME */
- uint32_t trktype; /* track type */
- uint32_t subtype; /* subcode data type */
- uint32_t datasize; /* size of data in each sector of this track */
- uint32_t subsize; /* size of subchannel data in each sector of this track */
- uint32_t frames; /* number of frames in this track */
- uint32_t extraframes; /* number of "spillage" frames in this track */
- uint32_t pregap; /* number of pregap frames */
- uint32_t postgap; /* number of postgap frames */
- uint32_t pgtype; /* type of sectors in pregap */
- uint32_t pgsub; /* type of subchannel data in pregap */
- uint32_t pgdatasize; /* size of data in each sector of the pregap */
- uint32_t pgsubsize; /* size of subchannel data in each sector of the pregap */
-
- /* fields used in CHDMAN only */
- uint32_t padframes; /* number of frames of padding to add to the end of the track; needed for GDI */
-
- /* fields used in MAME/MESS only */
- uint32_t logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */
- uint32_t physframeofs; /* physical frame of actual track data in CHD data */
- uint32_t chdframeofs; /* frame number this track starts at on the CHD */
-};
-
-
-struct cdrom_toc
-{
- uint32_t numtrks; /* number of tracks */
- uint32_t flags; /* see FLAG_ above */
- cdrom_track_info tracks[CD_MAX_TRACKS];
-};
-
-
-
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
-
-/* base functionality */
-cdrom_file *cdrom_open(chd_file *chd);
-void cdrom_close(cdrom_file *file);
-
-cdrom_file *cdrom_open(const char *inputfile);
-
-/* core read access */
-uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uint32_t datatype, bool phys=false);
-uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, bool phys=false);
-
-/* handy utilities */
-uint32_t cdrom_get_track(cdrom_file *file, uint32_t frame);
-uint32_t cdrom_get_track_start(cdrom_file *file, uint32_t track);
-uint32_t cdrom_get_track_start_phys(cdrom_file *file, uint32_t track);
-chd_file *cdrom_get_chd(cdrom_file *file);
-
-/* TOC utilities */
-int cdrom_get_last_track(cdrom_file *file);
-int cdrom_get_adr_control(cdrom_file *file, int track);
-int cdrom_get_track_type(cdrom_file *file, int track);
-const cdrom_toc *cdrom_get_toc(cdrom_file *file);
-
-/* extra utilities */
-void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info);
-void cdrom_convert_type_string_to_pregap_info(const char *typestring, cdrom_track_info *info);
-void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info);
-void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info);
-const char *cdrom_get_type_string(uint32_t trktype);
-const char *cdrom_get_subtype_string(uint32_t subtype);
-chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc);
-chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc);
-
-// ECC utilities
-bool ecc_verify(const uint8_t *sector);
-void ecc_generate(uint8_t *sector);
-void ecc_clear(uint8_t *sector);
-
-
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-static inline uint32_t msf_to_lba(uint32_t msf)
-{
- return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
-}
-
-static inline uint32_t lba_to_msf(uint32_t lba)
-{
- uint8_t m, s, f;
-
- m = lba / (60 * 75);
- lba -= m * (60 * 75);
- s = lba / 75;
- f = lba % 75;
-
- return ((m / 10) << 20) | ((m % 10) << 16) |
+#include <algorithm>
+#include <string>
+#include <string_view>
+#include <system_error>
+
+
+class cdrom_file {
+public:
+ // tracks are padded to a multiple of this many frames
+ static constexpr uint32_t TRACK_PADDING = 4;
+
+ static constexpr uint32_t MAX_TRACKS = 99; /* AFAIK the theoretical limit */
+ static constexpr uint32_t MAX_SECTOR_DATA = 2352;
+ static constexpr uint32_t MAX_SUBCODE_DATA = 96;
+ static constexpr uint32_t MAX_INDEX = 99;
+
+ static constexpr uint32_t FRAME_SIZE = MAX_SECTOR_DATA + MAX_SUBCODE_DATA;
+ static constexpr uint32_t FRAMES_PER_HUNK = 8;
+
+ static constexpr uint32_t METADATA_WORDS = 1 + MAX_TRACKS * 6;
+
+ enum
+ {
+ CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */
+ CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */
+ CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */
+ CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */
+ CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */
+ CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */
+ CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes/sector */
+ CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */
+
+ CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */
+ };
+
+ enum
+ {
+ CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */
+ CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */
+ CD_SUB_NONE /* no subcode data stored */
+ };
+
+ enum
+ {
+ CD_FLAG_GDROM = 0x00000001, // disc is a GD-ROM, all tracks should be stored with GD-ROM metadata
+ CD_FLAG_GDROMLE = 0x00000002, // legacy GD-ROM, with little-endian CDDA data
+ CD_FLAG_MULTISESSION = 0x00000004, // multisession CD-ROM
+ };
+
+ enum
+ {
+ CD_FLAG_CONTROL_PREEMPHASIS = 1,
+ CD_FLAG_CONTROL_DIGITAL_COPY_PERMITTED = 2,
+ CD_FLAG_CONTROL_DATA_TRACK = 4,
+ CD_FLAG_CONTROL_4CH = 8,
+ };
+
+ enum
+ {
+ CD_FLAG_ADR_START_TIME = 1,
+ CD_FLAG_ADR_CATALOG_CODE,
+ CD_FLAG_ADR_ISRC_CODE,
+ };
+
+ struct track_info
+ {
+ /* fields used by CHDMAN and in MAME */
+ uint32_t trktype; /* track type */
+ uint32_t subtype; /* subcode data type */
+ uint32_t datasize; /* size of data in each sector of this track */
+ uint32_t subsize; /* size of subchannel data in each sector of this track */
+ uint32_t frames; /* number of frames in this track */
+ uint32_t extraframes; /* number of "spillage" frames in this track */
+ uint32_t pregap; /* number of pregap frames */
+ uint32_t postgap; /* number of postgap frames */
+ uint32_t pgtype; /* type of sectors in pregap */
+ uint32_t pgsub; /* type of subchannel data in pregap */
+ uint32_t pgdatasize; /* size of data in each sector of the pregap */
+ uint32_t pgsubsize; /* size of subchannel data in each sector of the pregap */
+ uint32_t control_flags; /* metadata flags associated with each track */
+ uint32_t session; /* session number */
+
+ /* fields used in CHDMAN only */
+ uint32_t padframes; /* number of frames of padding to add to the end of the track; needed for GDI */
+ uint32_t splitframes; /* number of frames from the next file to add to the end of the current track after padding; needed for Redump split-bin GDI */
+
+ /* fields used in MAME/MESS only */
+ uint32_t logframeofs; /* logical frame of actual track data - offset by pregap size if pregap not physically present */
+ uint32_t physframeofs; /* physical frame of actual track data in CHD data */
+ uint32_t chdframeofs; /* frame number this track starts at on the CHD */
+ uint32_t logframes; /* number of frames from logframeofs until end of track data */
+
+ /* fields used in multi-cue GDI */
+ uint32_t multicuearea;
+ };
+
+
+ struct toc
+ {
+ uint32_t numtrks; /* number of tracks */
+ uint32_t numsessions; /* number of sessions */
+ uint32_t flags; /* see FLAG_ above */
+ track_info tracks[MAX_TRACKS + 1];
+ };
+
+ struct track_input_entry
+ {
+ track_input_entry() { reset(); }
+ void reset() { fname.clear(); offset = 0; leadin = leadout = -1; swap = false; std::fill(std::begin(idx), std::end(idx), -1); }
+
+ std::string fname; // filename for each track
+ uint32_t offset; // offset in the data file for each track
+ bool swap; // data needs to be byte swapped
+ int32_t idx[MAX_INDEX + 1];
+ int32_t leadin, leadout; // TODO: these should probably be their own tracks entirely
+ };
+
+ struct track_input_info
+ {
+ void reset() { for (auto & elem : track) elem.reset(); }
+
+ track_input_entry track[MAX_TRACKS];
+ };
+
+
+ cdrom_file(chd_file *chd);
+ cdrom_file(std::string_view inputfile);
+ ~cdrom_file();
+
+
+ /* core read access */
+ bool read_data(uint32_t lbasector, void *buffer, uint32_t datatype, bool phys=false);
+ bool read_subcode(uint32_t lbasector, void *buffer, bool phys=false);
+
+ /* handy utilities */
+ uint32_t get_track(uint32_t frame) const;
+ uint32_t get_track_start(uint32_t track) const {return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].logframeofs; }
+ uint32_t get_track_start_phys(uint32_t track) const { return cdtoc.tracks[track == 0xaa ? cdtoc.numtrks : track].physframeofs; }
+ uint32_t get_track_index(uint32_t frame) const;
+
+ /* TOC utilities */
+ static std::error_condition parse_nero(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ static std::error_condition parse_iso(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ static std::error_condition parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ static std::error_condition parse_cue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ static bool is_gdicue(std::string_view tocfname);
+ static std::error_condition parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
+ int get_last_session() const { return cdtoc.numsessions ? cdtoc.numsessions : 1; }
+ int get_last_track() const { return cdtoc.numtrks; }
+ int get_adr_control(int track) const
+ {
+ if (track == 0xaa)
+ track = get_last_track() - 1; // use last track's flags
+ int adrctl = (CD_FLAG_ADR_START_TIME << 4) | (cdtoc.tracks[track].control_flags & 0x0f);
+ if (cdtoc.tracks[track].trktype != CD_TRACK_AUDIO)
+ adrctl |= CD_FLAG_CONTROL_DATA_TRACK;
+ return adrctl;
+ }
+ int get_track_type(int track) const { return cdtoc.tracks[track].trktype; }
+ const toc &get_toc() const { return cdtoc; }
+
+ /* extra utilities */
+ static void convert_type_string_to_track_info(const char *typestring, track_info *info);
+ static void convert_type_string_to_pregap_info(const char *typestring, track_info *info);
+ static void convert_subtype_string_to_track_info(const char *typestring, track_info *info);
+ static void convert_subtype_string_to_pregap_info(const char *typestring, track_info *info);
+ static const char *get_type_string(uint32_t trktype);
+ static const char *get_subtype_string(uint32_t subtype);
+ static std::error_condition parse_metadata(chd_file *chd, toc &toc);
+ static std::error_condition write_metadata(chd_file *chd, const toc &toc);
+ bool is_gdrom() const { return cdtoc.flags & (CD_FLAG_GDROM|CD_FLAG_GDROMLE); }
+
+ // ECC utilities
+ static bool ecc_verify(const uint8_t *sector);
+ static void ecc_generate(uint8_t *sector);
+ static void ecc_clear(uint8_t *sector);
+
+
+
+ static inline uint32_t msf_to_lba(uint32_t msf)
+ {
+ return ( ((msf&0x00ff0000)>>16) * 60 * 75) + (((msf&0x0000ff00)>>8) * 75) + ((msf&0x000000ff)>>0);
+ }
+
+ static inline uint32_t lba_to_msf(uint32_t lba)
+ {
+ uint8_t m, s, f;
+
+ m = lba / (60 * 75);
+ lba -= m * (60 * 75);
+ s = lba / 75;
+ f = lba % 75;
+
+ return ((m / 10) << 20) | ((m % 10) << 16) |
((s / 10) << 12) | ((s % 10) << 8) |
((f / 10) << 4) | ((f % 10) << 0);
-}
-
-// segacd needs it like this.. investigate
-// Angelo also says PCE tracks often start playing at the
-// wrong address.. related?
-static inline uint32_t lba_to_msf_alt(int lba)
-{
- uint32_t ret = 0;
-
- ret |= ((lba / (60 * 75))&0xff)<<16;
- ret |= (((lba / 75) % 60)&0xff)<<8;
- ret |= ((lba % 75)&0xff)<<0;
-
- return ret;
-}
+ }
+
+ // segacd needs it like this.. investigate
+ // Angelo also says PCE tracks often start playing at the
+ // wrong address.. related?
+ static inline uint32_t lba_to_msf_alt(int lba)
+ {
+ uint32_t ret = 0;
+
+ ret |= ((lba / (60 * 75))&0xff)<<16;
+ ret |= (((lba / 75) % 60)&0xff)<<8;
+ ret |= ((lba % 75)&0xff)<<0;
+
+ return ret;
+ }
+
+private:
+ enum gdi_area {
+ SINGLE_DENSITY,
+ HIGH_DENSITY
+ };
+
+ enum gdi_pattern {
+ TYPE_UNKNOWN = 0,
+ TYPE_I,
+ TYPE_II,
+ TYPE_III,
+ TYPE_III_SPLIT
+ };
+
+ /** @brief offset within sector. */
+ static constexpr int SYNC_OFFSET = 0x000;
+ /** @brief 12 bytes. */
+ static constexpr int SYNC_NUM_BYTES = 12;
+
+ /** @brief offset within sector. */
+ static constexpr int MODE_OFFSET = 0x00f;
+
+ /** @brief offset within sector. */
+ static constexpr int ECC_P_OFFSET = 0x81c;
+ /** @brief 2 lots of 86. */
+ static constexpr int ECC_P_NUM_BYTES = 86;
+ /** @brief 24 bytes each. */
+ static constexpr int ECC_P_COMP = 24;
+
+ /** @brief The ECC q offset. */
+ static constexpr int ECC_Q_OFFSET = ECC_P_OFFSET + 2 * ECC_P_NUM_BYTES;
+ /** @brief 2 lots of 52. */
+ static constexpr int ECC_Q_NUM_BYTES = 52;
+ /** @brief 43 bytes each. */
+ static constexpr int ECC_Q_COMP = 43;
+
+ // ECC tables
+ static const uint8_t ecclow[256];
+ static const uint8_t ecchigh[256];
+ static const uint16_t poffsets[ECC_P_NUM_BYTES][ECC_P_COMP];
+ static const uint16_t qoffsets[ECC_Q_NUM_BYTES][ECC_Q_COMP];
+
+ /** @brief The chd. */
+ chd_file * chd; /* CHD file */
+ /** @brief The cdtoc. */
+ toc cdtoc; /* TOC for the CD */
+ /** @brief Information describing the track. */
+ track_input_info cdtrack_info; /* track info */
+ /** @brief The fhandle[ CD maximum tracks]. */
+ util::random_read::ptr fhandle[MAX_TRACKS];/* file handle */
+
+ inline uint32_t physical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const;
+ inline uint32_t logical_to_chd_lba(uint32_t physlba, uint32_t &tracknum) const;
+
+ static void get_info_from_type_string(const char *typestring, uint32_t *trktype, uint32_t *datasize);
+ static uint8_t ecc_source_byte(const uint8_t *sector, uint32_t offset);
+ static void ecc_compute_bytes(const uint8_t *sector, const uint16_t *row, int rowlen, uint8_t &val1, uint8_t &val2);
+ std::error_condition read_partial_sector(void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys);
+
+ static std::string get_file_path(std::string &path);
+ static uint64_t get_file_size(std::string_view filename);
+ static int tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize);
+ static int msf_to_frames(const char *token);
+ static uint32_t parse_wav_sample(std::string_view filename, uint32_t *dataoffs);
+ static uint16_t read_uint16(FILE *infile);
+ static uint32_t read_uint32(FILE *infile);
+ static uint64_t read_uint64(FILE *infile);
+};
-#endif // MAME_UTIL_CDROM_H
+#endif // MAME_LIB_UTIL_CDROM_H
diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp
index 3b61d044cd3..c05977a84eb 100644
--- a/src/lib/util/chd.cpp
+++ b/src/lib/util/chd.cpp
@@ -2,26 +2,31 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chd.c
-
MAME Compressed Hunks of Data file format
***************************************************************************/
-#include <assert.h>
-
#include "chd.h"
+
#include "avhuff.h"
-#include "hashing.h"
-#include "flac.h"
#include "cdrom.h"
+#include "corefile.h"
#include "coretmpl.h"
+#include "flac.h"
+#include "hashing.h"
+#include "multibyte.h"
+
+#include "eminline.h"
+
#include <zlib.h>
-#include <time.h>
-#include <stddef.h>
-#include <stdlib.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdlib>
+#include <ctime>
#include <new>
-#include "eminline.h"
+#include <tuple>
//**************************************************************************
@@ -101,12 +106,12 @@ enum
// description of where a metadata entry lives within the file
struct chd_file::metadata_entry
{
- uint64_t offset; // offset within the file of the header
- uint64_t next; // offset within the file of the next header
- uint64_t prev; // offset within the file of the previous header
- uint32_t length; // length of the metadata
- uint32_t metatag; // metadata tag
- uint8_t flags; // flag bits
+ uint64_t offset; // offset within the file of the header
+ uint64_t next; // offset within the file of the next header
+ uint64_t prev; // offset within the file of the previous header
+ uint32_t length; // length of the metadata
+ uint32_t metatag; // metadata tag
+ uint8_t flags; // flag bits
};
@@ -114,7 +119,7 @@ struct chd_file::metadata_entry
struct chd_file::metadata_hash
{
- uint8_t tag[4]; // tag of the metadata in big-endian
+ uint8_t tag[4]; // tag of the metadata in big-endian
util::sha1_t sha1; // hash data
};
@@ -125,41 +130,11 @@ struct chd_file::metadata_hash
//**************************************************************************
//-------------------------------------------------
-// be_read - extract a big-endian number from
-// a byte buffer
-//-------------------------------------------------
-
-inline uint64_t chd_file::be_read(const uint8_t *base, int numbytes)
-{
- uint64_t result = 0;
- while (numbytes--)
- result = (result << 8) | *base++;
- return result;
-}
-
-
-//-------------------------------------------------
-// be_write - write a big-endian number to a byte
-// buffer
-//-------------------------------------------------
-
-inline void chd_file::be_write(uint8_t *base, uint64_t value, int numbytes)
-{
- base += numbytes;
- while (numbytes--)
- {
- *--base = value;
- value >>= 8;
- }
-}
-
-
-//-------------------------------------------------
// be_read_sha1 - fetch a sha1_t from a data
// stream in bigendian order
//-------------------------------------------------
-inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base)
+inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base) const noexcept
{
util::sha1_t result;
memcpy(&result.m_raw[0], base, sizeof(result.m_raw));
@@ -172,7 +147,7 @@ inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base)
// stream in bigendian order
//-------------------------------------------------
-inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value)
+inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) noexcept
{
memcpy(base, &value.m_raw[0], sizeof(value.m_raw));
}
@@ -180,39 +155,45 @@ inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value)
//-------------------------------------------------
// file_read - read from the file at the given
-// offset; on failure throw an error
+// offset.
//-------------------------------------------------
-inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length)
+inline std::error_condition chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const noexcept
{
// no file = failure
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
// seek and read
- m_file->seek(offset, SEEK_SET);
- uint32_t count = m_file->read(dest, length);
- if (count != length)
- throw CHDERR_READ_ERROR;
+ std::error_condition err;
+ err = m_file->seek(offset, SEEK_SET);
+ if (UNEXPECTED(err))
+ return err;
+ size_t count;
+ std::tie(err, count) = read(*m_file, dest, length);
+ if (UNEXPECTED(!err && (count != length)))
+ return std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is truncated)
+ return err;
}
//-------------------------------------------------
// file_write - write to the file at the given
-// offset; on failure throw an error
+// offset.
//-------------------------------------------------
-inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t length)
+inline std::error_condition chd_file::file_write(uint64_t offset, const void *source, uint32_t length) noexcept
{
// no file = failure
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
// seek and write
- m_file->seek(offset, SEEK_SET);
- uint32_t count = m_file->write(source, length);
- if (count != length)
- throw CHDERR_WRITE_ERROR;
+ std::error_condition err;
+ err = m_file->seek(offset, SEEK_SET);
+ if (UNEXPECTED(err))
+ return err;
+ return write(*m_file, source, length).first;
}
@@ -225,14 +206,20 @@ inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t l
inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint32_t alignment)
{
// no file = failure
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
+ if (UNEXPECTED(!m_file))
+ throw std::error_condition(error::NOT_OPEN);
// seek to the end and align if necessary
- m_file->seek(0, SEEK_END);
+ std::error_condition err;
+ err = m_file->seek(0, SEEK_END);
+ if (UNEXPECTED(err))
+ throw err;
if (alignment != 0)
{
- uint64_t offset = m_file->tell();
+ uint64_t offset;
+ err = m_file->tell(offset);
+ if (UNEXPECTED(err))
+ throw err;
uint32_t delta = offset % alignment;
if (delta != 0)
{
@@ -242,20 +229,24 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3
delta = alignment - delta;
while (delta != 0)
{
- uint32_t bytes_to_write = (std::min<std::size_t>)(sizeof(buffer), delta);
- uint32_t count = m_file->write(buffer, bytes_to_write);
- if (count != bytes_to_write)
- throw CHDERR_WRITE_ERROR;
- delta -= bytes_to_write;
+ uint32_t bytes_to_write = std::min<std::size_t>(sizeof(buffer), delta);
+ size_t count;
+ std::tie(err, count) = write(*m_file, buffer, bytes_to_write);
+ if (UNEXPECTED(err))
+ throw err;
+ delta -= count;
}
}
}
// write the real data
- uint64_t offset = m_file->tell();
- uint32_t count = m_file->write(source, length);
- if (count != length)
- throw CHDERR_READ_ERROR;
+ uint64_t offset;
+ err = m_file->tell(offset);
+ if (UNEXPECTED(err))
+ throw err;
+ std::tie(err, std::ignore) = write(*m_file, source, length);
+ if (UNEXPECTED(err))
+ throw err;
return offset;
}
@@ -265,11 +256,14 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3
// necessary to represent all numbers 0..value
//-------------------------------------------------
-inline uint8_t chd_file::bits_for_value(uint64_t value)
+inline uint8_t chd_file::bits_for_value(uint64_t value) noexcept
{
uint8_t result = 0;
while (value != 0)
- value >>= 1, result++;
+ {
+ value >>= 1;
+ result++;
+ }
return result;
}
@@ -288,11 +282,8 @@ inline uint8_t chd_file::bits_for_value(uint64_t value)
*/
chd_file::chd_file()
- : m_file(nullptr),
- m_owns_file(false)
{
// reset state
- memset(m_decompressor, 0, sizeof(m_decompressor));
close();
}
@@ -311,6 +302,32 @@ chd_file::~chd_file()
}
/**
+ * @fn util::random_read chd_file::file()
+ *
+ * @brief -------------------------------------------------
+ * file - return our underlying file
+ * -------------------------------------------------.
+ *
+ * @return A random_read.
+ */
+
+util::random_read &chd_file::file()
+{
+ assert(m_file);
+ return *m_file;
+}
+
+bool chd_file::parent_missing() const noexcept
+{
+ if (m_parent_missing)
+ return true;
+ else if (!m_parent)
+ return false;
+ else
+ return m_parent->parent_missing();
+}
+
+/**
* @fn util::sha1_t chd_file::sha1()
*
* @brief -------------------------------------------------
@@ -320,20 +337,14 @@ chd_file::~chd_file()
* @return A sha1_t.
*/
-util::sha1_t chd_file::sha1()
+util::sha1_t chd_file::sha1() const noexcept
{
- try
- {
- // read the big-endian version
- uint8_t rawbuf[sizeof(util::sha1_t)];
- file_read(m_sha1_offset, rawbuf, sizeof(rawbuf));
- return be_read_sha1(rawbuf);
- }
- catch (chd_error &)
- {
- // on failure, return nullptr
- return util::sha1_t::null;
- }
+ // read the big-endian version
+ uint8_t rawbuf[sizeof(util::sha1_t)];
+ std::error_condition err = file_read(m_sha1_offset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(err))
+ return util::sha1_t::null; // on failure, return null
+ return be_read_sha1(rawbuf);
}
/**
@@ -349,22 +360,24 @@ util::sha1_t chd_file::sha1()
* @return A sha1_t.
*/
-util::sha1_t chd_file::raw_sha1()
+util::sha1_t chd_file::raw_sha1() const noexcept
{
try
{
// determine offset within the file for data-only
- if (m_rawsha1_offset == 0)
- throw CHDERR_UNSUPPORTED_VERSION;
+ if (UNEXPECTED(!m_rawsha1_offset))
+ throw std::error_condition(error::UNSUPPORTED_VERSION);
// read the big-endian version
uint8_t rawbuf[sizeof(util::sha1_t)];
- file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf));
+ std::error_condition err = file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(err))
+ throw err;
return be_read_sha1(rawbuf);
}
- catch (chd_error &)
+ catch (std::error_condition const &)
{
- // on failure, return nullptr
+ // on failure, return null
return util::sha1_t::null;
}
}
@@ -382,20 +395,22 @@ util::sha1_t chd_file::raw_sha1()
* @return A sha1_t.
*/
-util::sha1_t chd_file::parent_sha1()
+util::sha1_t chd_file::parent_sha1() const noexcept
{
try
{
// determine offset within the file
- if (m_parentsha1_offset == 0)
- throw CHDERR_UNSUPPORTED_VERSION;
+ if (UNEXPECTED(!m_parentsha1_offset))
+ throw std::error_condition(error::UNSUPPORTED_VERSION);
// read the big-endian version
uint8_t rawbuf[sizeof(util::sha1_t)];
- file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
+ std::error_condition err = file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(err))
+ throw err;
return be_read_sha1(rawbuf);
}
- catch (chd_error &)
+ catch (std::error_condition const &)
{
// on failure, return nullptr
return util::sha1_t::null;
@@ -403,7 +418,7 @@ util::sha1_t chd_file::parent_sha1()
}
/**
- * @fn chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes)
+ * @fn std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes)
*
* @brief -------------------------------------------------
* hunk_info - return information about this hunk
@@ -413,60 +428,62 @@ util::sha1_t chd_file::parent_sha1()
* @param [in,out] compressor The compressor.
* @param [in,out] compbytes The compbytes.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes)
+std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes)
{
// error if invalid
if (hunknum >= m_hunkcount)
- return CHDERR_HUNK_OUT_OF_RANGE;
+ return std::error_condition(error::HUNK_OUT_OF_RANGE);
// get the map pointer
- uint8_t *rawmap;
switch (m_version)
{
- // v3/v4 map entries
- case 3:
- case 4:
- rawmap = &m_rawmap[16 * hunknum];
+ // v3/v4 map entries
+ case 3:
+ case 4:
+ {
+ uint8_t const *const rawmap = &m_rawmap[16 * hunknum];
switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
{
- case V34_MAP_ENTRY_TYPE_COMPRESSED:
- compressor = CHD_CODEC_ZLIB;
- compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16);
- break;
+ case V34_MAP_ENTRY_TYPE_COMPRESSED:
+ compressor = CHD_CODEC_ZLIB;
+ compbytes = get_u16be(&rawmap[12]) + (rawmap[14] << 16);
+ break;
- case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
- compressor = CHD_CODEC_NONE;
- compbytes = m_hunkbytes;
- break;
+ case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
+ compressor = CHD_CODEC_NONE;
+ compbytes = m_hunkbytes;
+ break;
- case V34_MAP_ENTRY_TYPE_MINI:
- compressor = CHD_CODEC_MINI;
- compbytes = 0;
- break;
+ case V34_MAP_ENTRY_TYPE_MINI:
+ compressor = CHD_CODEC_MINI;
+ compbytes = 0;
+ break;
- case V34_MAP_ENTRY_TYPE_SELF_HUNK:
- compressor = CHD_CODEC_SELF;
- compbytes = 0;
- break;
+ case V34_MAP_ENTRY_TYPE_SELF_HUNK:
+ compressor = CHD_CODEC_SELF;
+ compbytes = 0;
+ break;
- case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
- compressor = CHD_CODEC_PARENT;
- compbytes = 0;
- break;
+ case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
+ compressor = CHD_CODEC_PARENT;
+ compbytes = 0;
+ break;
}
- break;
+ }
+ break;
- // v5 map entries
- case 5:
- rawmap = &m_rawmap[m_mapentrybytes * hunknum];
+ // v5 map entries
+ case 5:
+ {
+ uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum];
- // uncompressed case
if (!compressed())
{
- if (be_read(&rawmap[0], 4) == 0)
+ // uncompressed case
+ if (get_u32be(&rawmap[0]) == 0)
{
compressor = CHD_CODEC_PARENT;
compbytes = 0;
@@ -476,18 +493,18 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint
compressor = CHD_CODEC_NONE;
compbytes = m_hunkbytes;
}
- break;
}
-
- // compressed case
- switch (rawmap[0])
+ else
{
+ // compressed case
+ switch (rawmap[0])
+ {
case COMPRESSION_TYPE_0:
case COMPRESSION_TYPE_1:
case COMPRESSION_TYPE_2:
case COMPRESSION_TYPE_3:
compressor = m_compression[rawmap[0]];
- compbytes = be_read(&rawmap[1], 3);
+ compbytes = get_u24be(&rawmap[1]);
break;
case COMPRESSION_NONE:
@@ -506,11 +523,14 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint
break;
default:
- return CHDERR_UNKNOWN_COMPRESSION;
+ return error::UNKNOWN_COMPRESSION;
+ }
}
- break;
+ }
+ break;
}
- return CHDERR_NONE;
+
+ return std::error_condition();
}
/**
@@ -523,20 +543,36 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint
* @param rawdata The rawdata.
*/
-void chd_file::set_raw_sha1(util::sha1_t rawdata)
+std::error_condition chd_file::set_raw_sha1(util::sha1_t rawdata) noexcept
{
+ uint64_t const offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset;
+ assert(offset != 0);
+
// create a big-endian version
uint8_t rawbuf[sizeof(util::sha1_t)];
be_write_sha1(rawbuf, rawdata);
// write to the header
- uint64_t offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset;
- assert(offset != 0);
- file_write(offset, rawbuf, sizeof(rawbuf));
+ std::error_condition err = file_write(offset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(err))
+ return err;
- // if we have a separate rawsha1_offset, update the full sha1 as well
- if (m_rawsha1_offset != 0)
- metadata_update_hash();
+ try
+ {
+ // if we have a separate rawsha1_offset, update the full sha1 as well
+ if (m_rawsha1_offset != 0)
+ metadata_update_hash();
+ }
+ catch (std::error_condition const &err)
+ {
+ return err;
+ }
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
+
+ return std::error_condition();
}
/**
@@ -551,23 +587,24 @@ void chd_file::set_raw_sha1(util::sha1_t rawdata)
* @param parent The parent.
*/
-void chd_file::set_parent_sha1(util::sha1_t parent)
+std::error_condition chd_file::set_parent_sha1(util::sha1_t parent) noexcept
{
// if no file, fail
- if (m_file == nullptr)
- throw CHDERR_INVALID_FILE;
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::INVALID_FILE);
+
+ assert(m_parentsha1_offset != 0);
// create a big-endian version
uint8_t rawbuf[sizeof(util::sha1_t)];
be_write_sha1(rawbuf, parent);
// write to the header
- assert(m_parentsha1_offset != 0);
- file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
+ return file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf));
}
/**
- * @fn chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4])
+ * @fn std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4])
*
* @brief -------------------------------------------------
* create - create a new file with no parent using an existing opened file handle
@@ -579,30 +616,36 @@ void chd_file::set_parent_sha1(util::sha1_t parent)
* @param unitbytes The unitbytes.
* @param compression The compression.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4])
+std::error_condition chd_file::create(
+ util::random_read_write::ptr &&file,
+ uint64_t logicalbytes,
+ uint32_t hunkbytes,
+ uint32_t unitbytes,
+ const chd_codec_type (&compression)[4])
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
+ else if (UNEXPECTED(!file))
+ return std::errc::invalid_argument;
// set the header parameters
m_logicalbytes = logicalbytes;
m_hunkbytes = hunkbytes;
m_unitbytes = unitbytes;
memcpy(m_compression, compression, sizeof(m_compression));
- m_parent = nullptr;
+ m_parent.reset();
// take ownership of the file
- m_file = &file;
- m_owns_file = false;
+ m_file = std::move(file);
return create_common();
}
/**
- * @fn chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent)
+ * @fn std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent)
*
* @brief -------------------------------------------------
* create - create a new file with a parent using an existing opened file handle
@@ -614,30 +657,36 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_
* @param compression The compression.
* @param [in,out] parent The parent.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent)
+std::error_condition chd_file::create(
+ util::random_read_write::ptr &&file,
+ uint64_t logicalbytes,
+ uint32_t hunkbytes,
+ const chd_codec_type (&compression)[4],
+ chd_file &parent)
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
+ else if (UNEXPECTED(!file))
+ return std::errc::invalid_argument;
// set the header parameters
m_logicalbytes = logicalbytes;
m_hunkbytes = hunkbytes;
m_unitbytes = parent.unit_bytes();
memcpy(m_compression, compression, sizeof(m_compression));
- m_parent = &parent;
+ m_parent = std::shared_ptr<chd_file>(std::shared_ptr<chd_file>(), &parent);
// take ownership of the file
- m_file = &file;
- m_owns_file = false;
+ m_file = std::move(file);
return create_common();
}
/**
- * @fn chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4])
+ * @fn std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4])
*
* @brief -------------------------------------------------
* create - create a new file with no parent using a filename
@@ -649,40 +698,40 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_
* @param unitbytes The unitbytes.
* @param compression The compression.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4])
+std::error_condition chd_file::create(
+ std::string_view filename,
+ uint64_t logicalbytes,
+ uint32_t hunkbytes,
+ uint32_t unitbytes,
+ const chd_codec_type (&compression)[4])
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
// create the new file
util::core_file::ptr file;
- const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
- if (filerr != osd_file::error::NONE)
- return CHDERR_FILE_NOT_FOUND;
+ std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
+ if (UNEXPECTED(filerr))
+ return filerr;
// create the file normally, then claim the file
- const chd_error chderr = create(*file, logicalbytes, hunkbytes, unitbytes, compression);
- m_owns_file = true;
+ std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression);
// if an error happened, close and delete the file
- if (chderr != CHDERR_NONE)
+ if (UNEXPECTED(chderr))
{
file.reset();
- osd_file::remove(filename);
- }
- else
- {
- file.release();
+ osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
}
return chderr;
}
/**
- * @fn chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent)
+ * @fn std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent)
*
* @brief -------------------------------------------------
* create - create a new file with a parent using a filename
@@ -694,40 +743,40 @@ chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t
* @param compression The compression.
* @param [in,out] parent The parent.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent)
+std::error_condition chd_file::create(
+ std::string_view filename,
+ uint64_t logicalbytes,
+ uint32_t hunkbytes,
+ const chd_codec_type (&compression)[4],
+ chd_file &parent)
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
// create the new file
util::core_file::ptr file;
- const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
- if (filerr != osd_file::error::NONE)
- return CHDERR_FILE_NOT_FOUND;
+ std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
+ if (UNEXPECTED(filerr))
+ return filerr;
// create the file normally, then claim the file
- const chd_error chderr = create(*file, logicalbytes, hunkbytes, compression, parent);
- m_owns_file = true;
+ std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent);
// if an error happened, close and delete the file
- if (chderr != CHDERR_NONE)
+ if (UNEXPECTED(chderr))
{
file.reset();
- osd_file::remove(filename);
- }
- else
- {
- file.release();
+ osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
}
return chderr;
}
/**
- * @fn chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent)
+ * @fn std::error_condition chd_file::open(std::string_view filename, bool writeable, chd_file *parent)
*
* @brief -------------------------------------------------
* open - open an existing file for read or read/write
@@ -737,35 +786,32 @@ chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t
* @param writeable true if writeable.
* @param [in,out] parent If non-null, the parent.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent)
+std::error_condition chd_file::open(
+ std::string_view filename,
+ bool writeable,
+ chd_file *parent,
+ const open_parent_func &open_parent)
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
// open the file
const uint32_t openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ;
util::core_file::ptr file;
- const osd_file::error filerr = util::core_file::open(filename, openflags, file);
- if (filerr != osd_file::error::NONE)
- return CHDERR_FILE_NOT_FOUND;
+ std::error_condition filerr = util::core_file::open(filename, openflags, file);
+ if (UNEXPECTED(filerr))
+ return filerr;
// now open the CHD
- chd_error err = open(*file, writeable, parent);
- if (err != CHDERR_NONE)
- return err;
-
- // we now own this file
- file.release();
- m_owns_file = true;
- return err;
+ return open(std::move(file), writeable, parent, open_parent);
}
/**
- * @fn chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent)
+ * @fn std::error_condition chd_file::open(util::random_read_write::ptr &&file, bool writeable, chd_file *parent)
*
* @brief -------------------------------------------------
* open - open an existing file for read or read/write
@@ -775,21 +821,26 @@ chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent)
* @param writeable true if writeable.
* @param [in,out] parent If non-null, the parent.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent)
+std::error_condition chd_file::open(
+ util::random_read_write::ptr &&file,
+ bool writeable,
+ chd_file *parent,
+ const open_parent_func &open_parent)
{
// make sure we don't already have a file open
- if (m_file != nullptr)
- return CHDERR_ALREADY_OPEN;
+ if (UNEXPECTED(m_file))
+ return error::ALREADY_OPEN;
+ else if (UNEXPECTED(!file))
+ return std::errc::invalid_argument;
// open the file
- m_file = &file;
- m_owns_file = false;
- m_parent = parent;
+ m_file = std::move(file);
+ m_parent = std::shared_ptr<chd_file>(std::shared_ptr<chd_file>(), parent);
m_cachehunk = ~0;
- return open_common(writeable);
+ return open_common(writeable, open_parent);
}
/**
@@ -803,10 +854,7 @@ chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent
void chd_file::close()
{
// reset file characteristics
- if (m_owns_file && m_file)
- delete m_file;
- m_file = nullptr;
- m_owns_file = false;
+ m_file.reset();
m_allow_reads = false;
m_allow_writes = false;
@@ -819,8 +867,8 @@ void chd_file::close()
m_hunkcount = 0;
m_unitbytes = 0;
m_unitcount = 0;
- memset(m_compression, 0, sizeof(m_compression));
- m_parent = nullptr;
+ std::fill(std::begin(m_compression), std::end(m_compression), 0);
+ m_parent.reset();
m_parent_missing = false;
// reset key offsets within the header
@@ -836,10 +884,7 @@ void chd_file::close()
// reset compression management
for (auto & elem : m_decompressor)
- {
- delete elem;
- elem = nullptr;
- }
+ elem.reset();
m_compressed.clear();
// reset caching
@@ -847,8 +892,107 @@ void chd_file::close()
m_cachehunk = ~0;
}
+std::error_condition chd_file::codec_process_hunk(uint32_t hunknum)
+{
+ // punt if no file
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
+
+ // return an error if out of range
+ if (UNEXPECTED(hunknum >= m_hunkcount))
+ return std::error_condition(error::HUNK_OUT_OF_RANGE);
+
+ // wrap this for clean reporting
+ try
+ {
+ // get a pointer to the map entry
+ switch (m_version)
+ {
+ // v3/v4 map entries
+ case 3:
+ case 4:
+ {
+ uint8_t const *const rawmap = &m_rawmap[16 * hunknum];
+ uint64_t const blockoffs = get_u64be(&rawmap[0]);
+ switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
+ {
+ case V34_MAP_ENTRY_TYPE_COMPRESSED:
+ {
+ uint32_t const blocklen = get_u16be(&rawmap[12]) | (uint32_t(rawmap[14]) << 16);
+ std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen);
+ if (UNEXPECTED(err))
+ return err;
+ m_decompressor[0]->process(&m_compressed[0], blocklen);
+ return std::error_condition();
+ }
+
+ case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
+ case V34_MAP_ENTRY_TYPE_MINI:
+ return std::error_condition(error::UNSUPPORTED_FORMAT);
+
+ case V34_MAP_ENTRY_TYPE_SELF_HUNK:
+ return codec_process_hunk(blockoffs);
+
+ case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
+ if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
+ return m_parent->codec_process_hunk(blockoffs);
+ }
+ }
+ break;
+
+ // v5 map entries
+ case 5:
+ {
+ if (UNEXPECTED(!compressed()))
+ return std::error_condition(error::UNSUPPORTED_FORMAT);
+
+ // compressed case
+ uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum];
+ uint32_t const blocklen = get_u24be(&rawmap[1]);
+ uint64_t const blockoffs = get_u48be(&rawmap[4]);
+ switch (rawmap[0])
+ {
+ case COMPRESSION_TYPE_0:
+ case COMPRESSION_TYPE_1:
+ case COMPRESSION_TYPE_2:
+ case COMPRESSION_TYPE_3:
+ {
+ std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen);
+ if (UNEXPECTED(err))
+ return err;
+ auto &decompressor = *m_decompressor[rawmap[0]];
+ decompressor.process(&m_compressed[0], blocklen);
+ return std::error_condition();
+ }
+
+ case COMPRESSION_NONE:
+ return std::error_condition(error::UNSUPPORTED_FORMAT);
+
+ case COMPRESSION_SELF:
+ return codec_process_hunk(blockoffs);
+
+ case COMPRESSION_PARENT:
+ if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
+ return m_parent->codec_process_hunk(blockoffs / (m_parent->hunk_bytes() / m_parent->unit_bytes()));
+ }
+ break;
+ }
+ }
+
+ // if we get here, the map contained an unsupported block type
+ return std::error_condition(error::INVALID_DATA);
+ }
+ catch (std::error_condition const &err)
+ {
+ // just return errors
+ return err;
+ }
+}
+
/**
- * @fn chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer)
+ * @fn std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer)
*
* @brief -------------------------------------------------
* read - read a single hunk from the CHD file
@@ -867,137 +1011,158 @@ void chd_file::close()
* @param hunknum The hunknum.
* @param [in,out] buffer If non-null, the buffer.
*
- * @return The hunk.
+ * @return An error condition.
*/
-chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer)
+std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer)
{
+ // punt if no file
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
+
+ // return an error if out of range
+ if (UNEXPECTED(hunknum >= m_hunkcount))
+ return std::error_condition(error::HUNK_OUT_OF_RANGE);
+
+ auto *const dest = reinterpret_cast<uint8_t *>(buffer);
+
// wrap this for clean reporting
try
{
- // punt if no file
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
-
- // return an error if out of range
- if (hunknum >= m_hunkcount)
- throw CHDERR_HUNK_OUT_OF_RANGE;
-
// get a pointer to the map entry
- uint64_t blockoffs;
- uint32_t blocklen;
- util::crc32_t blockcrc;
- uint8_t *rawmap;
- uint8_t *dest = reinterpret_cast<uint8_t *>(buffer);
switch (m_version)
{
- // v3/v4 map entries
- case 3:
- case 4:
- rawmap = &m_rawmap[16 * hunknum];
- blockoffs = be_read(&rawmap[0], 8);
- blockcrc = be_read(&rawmap[8], 4);
+ // v3/v4 map entries
+ case 3:
+ case 4:
+ {
+ uint8_t const *const rawmap = &m_rawmap[16 * hunknum];
+ uint64_t const blockoffs = get_u64be(&rawmap[0]);
+ util::crc32_t const blockcrc = get_u32be(&rawmap[8]);
+ bool const nocrc = rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC;
switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK)
{
- case V34_MAP_ENTRY_TYPE_COMPRESSED:
- blocklen = be_read(&rawmap[12], 2) + (rawmap[14] << 16);
- file_read(blockoffs, &m_compressed[0], blocklen);
+ case V34_MAP_ENTRY_TYPE_COMPRESSED:
+ {
+ uint32_t const blocklen = get_u16be(&rawmap[12]) | (uint32_t(rawmap[14]) << 16);
+ std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen);
+ if (UNEXPECTED(err))
+ return err;
m_decompressor[0]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes);
- if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && dest != nullptr && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- return CHDERR_NONE;
-
- case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
- file_read(blockoffs, dest, m_hunkbytes);
- if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- return CHDERR_NONE;
-
- case V34_MAP_ENTRY_TYPE_MINI:
- be_write(dest, blockoffs, 8);
- for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++)
- dest[bytes] = dest[bytes - 8];
- if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- return CHDERR_NONE;
-
- case V34_MAP_ENTRY_TYPE_SELF_HUNK:
- return read_hunk(blockoffs, dest);
+ if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)))
+ return std::error_condition(error::DECOMPRESSION_ERROR);
+ return std::error_condition();
+ }
- case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
- if (m_parent_missing)
- throw CHDERR_REQUIRES_PARENT;
- return m_parent->read_hunk(blockoffs, dest);
+ case V34_MAP_ENTRY_TYPE_UNCOMPRESSED:
+ {
+ std::error_condition err = file_read(blockoffs, dest, m_hunkbytes);
+ if (UNEXPECTED(err))
+ return err;
+ if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)))
+ return std::error_condition(error::DECOMPRESSION_ERROR);
+ return std::error_condition();
+ }
+
+ case V34_MAP_ENTRY_TYPE_MINI:
+ put_u64be(dest, blockoffs);
+ for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++)
+ dest[bytes] = dest[bytes - 8];
+ if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)))
+ return std::error_condition(error::DECOMPRESSION_ERROR);
+ return std::error_condition();
+
+ case V34_MAP_ENTRY_TYPE_SELF_HUNK:
+ return read_hunk(blockoffs, dest);
+
+ case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
+ if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
+ return m_parent->read_hunk(blockoffs, dest);
}
- break;
+ }
+ break;
- // v5 map entries
- case 5:
- rawmap = &m_rawmap[m_mapentrybytes * hunknum];
+ // v5 map entries
+ case 5:
+ {
+ uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum];
- // uncompressed case
if (!compressed())
{
- blockoffs = uint64_t(be_read(rawmap, 4)) * uint64_t(m_hunkbytes);
+ // uncompressed case
+ uint64_t const blockoffs = mulu_32x32(get_u32be(rawmap), m_hunkbytes);
if (blockoffs != 0)
- file_read(blockoffs, dest, m_hunkbytes);
- else if (m_parent_missing)
- throw CHDERR_REQUIRES_PARENT;
- else if (m_parent != nullptr)
- m_parent->read_hunk(hunknum, dest);
+ return file_read(blockoffs, dest, m_hunkbytes);
+ else if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
+ else if (m_parent)
+ return m_parent->read_hunk(hunknum, dest);
else
memset(dest, 0, m_hunkbytes);
- return CHDERR_NONE;
+ return std::error_condition();
}
-
- // compressed case
- blocklen = be_read(&rawmap[1], 3);
- blockoffs = be_read(&rawmap[4], 6);
- blockcrc = be_read(&rawmap[10], 2);
- switch (rawmap[0])
+ else
{
+ // compressed case
+ uint32_t const blocklen = get_u24be(&rawmap[1]);
+ uint64_t const blockoffs = get_u48be(&rawmap[4]);
+ util::crc16_t const blockcrc = get_u16be(&rawmap[10]);
+ switch (rawmap[0])
+ {
case COMPRESSION_TYPE_0:
case COMPRESSION_TYPE_1:
case COMPRESSION_TYPE_2:
case COMPRESSION_TYPE_3:
- file_read(blockoffs, &m_compressed[0], blocklen);
- m_decompressor[rawmap[0]]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes);
- if (!m_decompressor[rawmap[0]]->lossy() && dest != nullptr && util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- if (m_decompressor[rawmap[0]]->lossy() && util::crc16_creator::simple(&m_compressed[0], blocklen) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- return CHDERR_NONE;
+ {
+ std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen);
+ if (UNEXPECTED(err))
+ return err;
+ auto &decompressor = *m_decompressor[rawmap[0]];
+ decompressor.decompress(&m_compressed[0], blocklen, dest, m_hunkbytes);
+ util::crc16_t const calculated = !decompressor.lossy()
+ ? util::crc16_creator::simple(dest, m_hunkbytes)
+ : util::crc16_creator::simple(&m_compressed[0], blocklen);
+ if (UNEXPECTED(calculated != blockcrc))
+ return std::error_condition(error::DECOMPRESSION_ERROR);
+ return std::error_condition();
+ }
case COMPRESSION_NONE:
- file_read(blockoffs, dest, m_hunkbytes);
- if (util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
- return CHDERR_NONE;
+ {
+ std::error_condition err = file_read(blockoffs, dest, m_hunkbytes);
+ if (UNEXPECTED(err))
+ return err;
+ if (UNEXPECTED(util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc))
+ return std::error_condition(error::DECOMPRESSION_ERROR);
+ return std::error_condition();
+ }
case COMPRESSION_SELF:
return read_hunk(blockoffs, dest);
case COMPRESSION_PARENT:
- if (m_parent_missing)
- throw CHDERR_REQUIRES_PARENT;
- return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes);
+ if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
+ return m_parent->read_bytes(blockoffs * m_parent->unit_bytes(), dest, m_hunkbytes);
+ }
}
break;
+ }
}
- // if we get here, something was wrong
- throw CHDERR_READ_ERROR;
+ // if we get here, the map contained an unsupported block type
+ return std::error_condition(error::INVALID_DATA);
}
-
- // just return errors
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
+ // just return errors
return err;
}
}
/**
- * @fn chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer)
+ * @fn std::error_condition chd_file::write_hunk(uint32_t hunknum, const void *buffer)
*
* @brief -------------------------------------------------
* write - write a single hunk to the CHD file
@@ -1012,78 +1177,83 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer)
* @param hunknum The hunknum.
* @param buffer The buffer.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer)
+std::error_condition chd_file::write_hunk(uint32_t hunknum, const void *buffer)
{
- // wrap this for clean reporting
- try
- {
- // punt if no file
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
+ // punt if no file
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
- // return an error if out of range
- if (hunknum >= m_hunkcount)
- throw CHDERR_HUNK_OUT_OF_RANGE;
+ // return an error if out of range
+ if (UNEXPECTED(hunknum >= m_hunkcount))
+ return std::error_condition(error::HUNK_OUT_OF_RANGE);
- // if not writeable, fail
- if (!m_allow_writes)
- throw CHDERR_FILE_NOT_WRITEABLE;
+ // if not writeable, fail
+ if (UNEXPECTED(!m_allow_writes))
+ return std::error_condition(error::FILE_NOT_WRITEABLE);
- // uncompressed writes only via this interface
- if (compressed())
- throw CHDERR_FILE_NOT_WRITEABLE;
+ // uncompressed writes only via this interface
+ if (UNEXPECTED(compressed()))
+ return std::error_condition(error::FILE_NOT_WRITEABLE);
- // see if we have allocated the space on disk for this hunk
- uint8_t *rawmap = &m_rawmap[hunknum * 4];
- uint32_t rawentry = be_read(rawmap, 4);
+ // see if we have allocated the space on disk for this hunk
+ uint8_t *const rawmap = &m_rawmap[hunknum * 4];
+ uint32_t rawentry = get_u32be(rawmap);
- // if not, allocate one now
- if (rawentry == 0)
+ // if not, allocate one now
+ if (rawentry == 0)
+ {
+ // first make sure we need to allocate it
+ bool all_zeros = true;
+ const auto *scan = reinterpret_cast<const uint32_t *>(buffer);
+ for (uint32_t index = 0; index < m_hunkbytes / 4; index++)
{
- // first make sure we need to allocate it
- bool all_zeros = true;
- const uint32_t *scan = reinterpret_cast<const uint32_t *>(buffer);
- for (uint32_t index = 0; index < m_hunkbytes / 4; index++)
- if (scan[index] != 0)
- {
- all_zeros = false;
- break;
- }
+ if (scan[index] != 0)
+ {
+ all_zeros = false;
+ break;
+ }
+ }
- // if it's all zeros, do nothing more
- if (all_zeros)
- return CHDERR_NONE;
+ // if it's all zeros, do nothing more
+ if (all_zeros)
+ return std::error_condition();
+ // wrap this for clean reporting
+ try
+ {
// append new data to the end of the file, aligning the first chunk
rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes;
+ }
+ catch (std::error_condition const &err)
+ {
+ // just return errors
+ return err;
+ }
- // write the map entry back
- be_write(rawmap, rawentry, 4);
- file_write(m_mapoffset + hunknum * 4, rawmap, 4);
+ // write the map entry back
+ put_u32be(rawmap, rawentry);
+ std::error_condition err = file_write(m_mapoffset + hunknum * 4, rawmap, 4);
+ if (UNEXPECTED(err))
+ return err;
- // update the cached hunk if we just wrote it
- if (hunknum == m_cachehunk && buffer != &m_cache[0])
- memcpy(&m_cache[0], buffer, m_hunkbytes);
- }
+ // update the cached hunk if we just wrote it
+ if (hunknum == m_cachehunk && buffer != &m_cache[0])
+ memcpy(&m_cache[0], buffer, m_hunkbytes);
- // otherwise, just overwrite
- else
- file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes);
- return CHDERR_NONE;
+ return std::error_condition();
}
-
- // just return errors
- catch (chd_error &err)
+ else
{
- return err;
+ // otherwise, just overwrite
+ return file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes);
}
}
/**
- * @fn chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count)
+ * @fn std::error_condition chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count)
*
* @brief -------------------------------------------------
* read_units - read the given number of units from the CHD
@@ -1096,13 +1266,13 @@ chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer)
* @return The units.
*/
-chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count)
+std::error_condition chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count)
{
return read_bytes(unitnum * uint64_t(m_unitbytes), buffer, count * m_unitbytes);
}
/**
- * @fn chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count)
+ * @fn std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count)
*
* @brief -------------------------------------------------
* write_units - write the given number of units to the CHD
@@ -1112,16 +1282,16 @@ chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count)
* @param buffer The buffer.
* @param count Number of.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count)
+std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count)
{
return write_bytes(unitnum * uint64_t(m_unitbytes), buffer, count * m_unitbytes);
}
/**
- * @fn chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes)
+ * @fn std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes)
*
* @brief -------------------------------------------------
* read_bytes - read from the CHD at a byte level, using the cache to handle partial
@@ -1135,46 +1305,46 @@ chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t c
* @return The bytes.
*/
-chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes)
+std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes)
{
// iterate over hunks
- uint32_t first_hunk = offset / m_hunkbytes;
- uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes;
- uint8_t *dest = reinterpret_cast<uint8_t *>(buffer);
+ uint32_t const first_hunk = offset / m_hunkbytes;
+ uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes;
+ auto *dest = reinterpret_cast<uint8_t *>(buffer);
for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++)
{
// determine start/end boundaries
- uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0;
- uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1);
+ uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0;
+ uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1);
- // if it's a full block, just read directly from disk unless it's the cached hunk
- chd_error err = CHDERR_NONE;
- if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
- err = read_hunk(curhunk, dest);
-
- // otherwise, read from the cache
+ if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk))
+ {
+ // if it's a full block, just read directly from disk unless it's the cached hunk
+ std::error_condition err = read_hunk(curhunk, dest);
+ if (UNEXPECTED(err))
+ return err;
+ }
else
{
+ // otherwise, read from the cache
if (curhunk != m_cachehunk)
{
- err = read_hunk(curhunk, &m_cache[0]);
- if (err != CHDERR_NONE)
+ std::error_condition err = read_hunk(curhunk, &m_cache[0]);
+ if (UNEXPECTED(err))
return err;
m_cachehunk = curhunk;
}
memcpy(dest, &m_cache[startoffs], endoffs + 1 - startoffs);
}
- // handle errors and advance
- if (err != CHDERR_NONE)
- return err;
+ // advance
dest += endoffs + 1 - startoffs;
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/**
- * @fn chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes)
+ * @fn std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes)
*
* @brief -------------------------------------------------
* write_bytes - write to the CHD at a byte level, using the cache to handle partial
@@ -1185,33 +1355,34 @@ chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes)
* @param buffer The buffer.
* @param bytes The bytes.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes)
+std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes)
{
// iterate over hunks
- uint32_t first_hunk = offset / m_hunkbytes;
- uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes;
- const uint8_t *source = reinterpret_cast<const uint8_t *>(buffer);
+ uint32_t const first_hunk = offset / m_hunkbytes;
+ uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes;
+ auto const *source = reinterpret_cast<uint8_t const *>(buffer);
for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++)
{
// determine start/end boundaries
- uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0;
- uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1);
+ uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0;
+ uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1);
- // if it's a full block, just write directly to disk unless it's the cached hunk
- chd_error err = CHDERR_NONE;
- if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
+ std::error_condition err;
+ if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk))
+ {
+ // if it's a full block, just write directly to disk unless it's the cached hunk
err = write_hunk(curhunk, source);
-
- // otherwise, write from the cache
+ }
else
{
+ // otherwise, write from the cache
if (curhunk != m_cachehunk)
{
err = read_hunk(curhunk, &m_cache[0]);
- if (err != CHDERR_NONE)
+ if (UNEXPECTED(err))
return err;
m_cachehunk = curhunk;
}
@@ -1220,15 +1391,15 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by
}
// handle errors and advance
- if (err != CHDERR_NONE)
+ if (UNEXPECTED(err))
return err;
source += endoffs + 1 - startoffs;
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/**
- * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output)
+ * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output)
*
* @brief -------------------------------------------------
* read_metadata - read the indexed metadata of the given type
@@ -1241,34 +1412,24 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by
* @param searchindex The searchindex.
* @param [in,out] output The output.
*
- * @return The metadata.
+ * @return An error condition.
*/
-chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output)
+std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output)
{
- // wrap this for clean reporting
- try
- {
- // if we didn't find it, just return
- metadata_entry metaentry;
- if (!metadata_find(searchtag, searchindex, metaentry))
- throw CHDERR_METADATA_NOT_FOUND;
-
- // read the metadata
- output.assign(metaentry.length, '\0');
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
- return CHDERR_NONE;
- }
-
- // just return errors
- catch (chd_error &err)
- {
+ // if we didn't find it, just return
+ metadata_entry metaentry;
+ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry))
return err;
- }
+
+ // read the metadata
+ try { output.assign(metaentry.length, '\0'); }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
}
/**
- * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output)
+ * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output)
*
* @brief Reads a metadata.
*
@@ -1279,34 +1440,24 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind
* @param searchindex The searchindex.
* @param [in,out] output The output.
*
- * @return The metadata.
+ * @return An error condition.
*/
-chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output)
+std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output)
{
- // wrap this for clean reporting
- try
- {
- // if we didn't find it, just return
- metadata_entry metaentry;
- if (!metadata_find(searchtag, searchindex, metaentry))
- throw CHDERR_METADATA_NOT_FOUND;
-
- // read the metadata
- output.resize(metaentry.length);
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
- return CHDERR_NONE;
- }
-
- // just return errors
- catch (chd_error &err)
- {
+ // if we didn't find it, just return
+ metadata_entry metaentry;
+ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry))
return err;
- }
+
+ // read the metadata
+ try { output.resize(metaentry.length); }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
}
/**
- * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen)
+ * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen)
*
* @brief Reads a metadata.
*
@@ -1319,34 +1470,23 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind
* @param outputlen The outputlen.
* @param [in,out] resultlen The resultlen.
*
- * @return The metadata.
+ * @return An error condition.
*/
-chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen)
+std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen)
{
- // wrap this for clean reporting
- try
- {
- // if we didn't find it, just return
- metadata_entry metaentry;
- if (!metadata_find(searchtag, searchindex, metaentry))
- throw CHDERR_METADATA_NOT_FOUND;
-
- // read the metadata
- resultlen = metaentry.length;
- file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen));
- return CHDERR_NONE;
- }
-
- // just return errors
- catch (chd_error &err)
- {
+ // if we didn't find it, just return
+ metadata_entry metaentry;
+ if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry))
return err;
- }
+
+ // read the metadata
+ resultlen = metaentry.length;
+ return file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen));
}
/**
- * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags)
+ * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags)
*
* @brief Reads a metadata.
*
@@ -1359,36 +1499,32 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind
* @param [in,out] resulttag The resulttag.
* @param [in,out] resultflags The resultflags.
*
- * @return The metadata.
+ * @return An error condition.
*/
-chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags)
+std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags)
{
- // wrap this for clean reporting
- try
- {
- // if we didn't find it, just return
- metadata_entry metaentry;
- if (!metadata_find(searchtag, searchindex, metaentry))
- throw CHDERR_METADATA_NOT_FOUND;
-
- // read the metadata
- output.resize(metaentry.length);
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
- resulttag = metaentry.metatag;
- resultflags = metaentry.flags;
- return CHDERR_NONE;
- }
+ std::error_condition err;
- // just return errors
- catch (chd_error &err)
- {
+ // if we didn't find it, just return
+ metadata_entry metaentry;
+ err = metadata_find(searchtag, searchindex, metaentry);
+ if (err)
return err;
- }
+
+ // read the metadata
+ try { output.resize(metaentry.length); }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
+ if (UNEXPECTED(err))
+ return err;
+ resulttag = metaentry.metatag;
+ resultflags = metaentry.flags;
+ return std::error_condition();
}
/**
- * @fn chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags)
+ * @fn std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags)
*
* @brief -------------------------------------------------
* write_metadata - write the indexed metadata of the given type
@@ -1400,77 +1536,94 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind
* @param inputlen The inputlen.
* @param flags The flags.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags)
+std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags)
{
- // wrap this for clean reporting
- try
+ // must write at least 1 byte and no more than 16MB
+ if (UNEXPECTED((inputlen < 1) || (inputlen >= 16 * 1024 * 1024)))
+ return std::error_condition(std::errc::invalid_argument);
+
+ // find the entry if it already exists
+ metadata_entry metaentry;
+ bool finished = false;
+ std::error_condition err = metadata_find(metatag, metaindex, metaentry);
+ if (!err)
{
- // must write at least 1 byte and no more than 16MB
- if (inputlen < 1 || inputlen >= 16 * 1024 * 1024)
- return CHDERR_INVALID_PARAMETER;
-
- // find the entry if it already exists
- metadata_entry metaentry;
- bool finished = false;
- if (metadata_find(metatag, metaindex, metaentry))
+ if (inputlen <= metaentry.length)
{
// if the new data fits over the old data, just overwrite
- if (inputlen <= metaentry.length)
- {
- file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen);
-
- // if the lengths don't match, we need to update the length in our header
- if (inputlen != metaentry.length)
- {
- uint8_t length[3];
- be_write(length, inputlen, 3);
- file_write(metaentry.offset + 5, length, sizeof(length));
- }
+ err = file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen);
+ if (UNEXPECTED(err))
+ return err;
- // indicate we did everything
- finished = true;
+ // if the lengths don't match, we need to update the length in our header
+ if (inputlen != metaentry.length)
+ {
+ uint8_t length[3];
+ put_u24be(length, inputlen);
+ err = file_write(metaentry.offset + 5, length, sizeof(length));
+ if (UNEXPECTED(err))
+ return err;
}
+ // indicate we did everything
+ finished = true;
+ }
+ else
+ {
// if it doesn't fit, unlink the current entry
- else
- metadata_set_previous_next(metaentry.prev, metaentry.next);
+ err = metadata_set_previous_next(metaentry.prev, metaentry.next);
+ if (UNEXPECTED(err))
+ return err;
}
+ }
+ else if (UNEXPECTED(err != error::METADATA_NOT_FOUND))
+ {
+ return err;
+ }
+ // wrap this for clean reporting
+ try
+ {
// if not yet done, create a new entry and append
if (!finished)
{
// now build us a new entry
uint8_t raw_meta_header[METADATA_HEADER_SIZE];
- be_write(&raw_meta_header[0], metatag, 4);
+ put_u32be(&raw_meta_header[0], metatag);
raw_meta_header[4] = flags;
- be_write(&raw_meta_header[5], (inputlen & 0x00ffffff) | (flags << 24), 3);
- be_write(&raw_meta_header[8], 0, 8);
+ put_u24be(&raw_meta_header[5], inputlen & 0x00ffffff);
+ put_u64be(&raw_meta_header[8], 0);
// append the new header, then the data
uint64_t offset = file_append(raw_meta_header, sizeof(raw_meta_header));
file_append(inputbuf, inputlen);
// set the previous entry to point to us
- metadata_set_previous_next(metaentry.prev, offset);
+ err = metadata_set_previous_next(metaentry.prev, offset);
+ if (UNEXPECTED(err))
+ return err;
}
// update the hash
metadata_update_hash();
- return CHDERR_NONE;
+ return std::error_condition();
}
-
- // return any errors
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
+ // return any errors
return err;
}
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
}
/**
- * @fn chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex)
+ * @fn std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex)
*
* @brief -------------------------------------------------
* delete_metadata - remove the given metadata from the list
@@ -1482,33 +1635,22 @@ chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex,
* @param metatag The metatag.
* @param metaindex The metaindex.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex)
+std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex)
{
- // wrap this for clean reporting
- try
- {
- // find the entry
- metadata_entry metaentry;
- if (!metadata_find(metatag, metaindex, metaentry))
- throw CHDERR_METADATA_NOT_FOUND;
-
- // point the previous to the next, unlinking us
- metadata_set_previous_next(metaentry.prev, metaentry.next);
- return CHDERR_NONE;
- }
-
- // return any errors
- catch (chd_error &err)
- {
+ // find the entry
+ metadata_entry metaentry;
+ if (std::error_condition err = metadata_find(metatag, metaindex, metaentry))
return err;
- }
+
+ // point the previous to the next, unlinking us
+ return metadata_set_previous_next(metaentry.prev, metaentry.next);
}
/**
- * @fn chd_error chd_file::clone_all_metadata(chd_file &source)
+ * @fn std::error_condition chd_file::clone_all_metadata(chd_file &source)
*
* @brief -------------------------------------------------
* clone_all_metadata - clone the metadata from one CHD to a second
@@ -1518,40 +1660,37 @@ chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex
*
* @param [in,out] source Another instance to copy.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::clone_all_metadata(chd_file &source)
+std::error_condition chd_file::clone_all_metadata(chd_file &source)
{
- // wrap this for clean reporting
- try
- {
- // iterate over metadata entries in the source
- std::vector<uint8_t> filedata;
- metadata_entry metaentry;
- metaentry.metatag = 0;
- metaentry.length = 0;
- metaentry.next = 0;
- metaentry.flags = 0;
- for (bool has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
- {
- // read the metadata item
- filedata.resize(metaentry.length);
- source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length);
-
- // write it to the destination
- chd_error err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags);
- if (err != CHDERR_NONE)
- throw err;
- }
- return CHDERR_NONE;
- }
+ // iterate over metadata entries in the source
+ std::vector<uint8_t> filedata;
+ metadata_entry metaentry;
+ metaentry.metatag = 0;
+ metaentry.length = 0;
+ metaentry.next = 0;
+ metaentry.flags = 0;
+ std::error_condition err;
+ for (err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
+ {
+ // read the metadata item
+ try { filedata.resize(metaentry.length); }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ err = source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length);
+ if (UNEXPECTED(err))
+ return err;
- // return any errors
- catch (chd_error &err)
- {
- return err;
+ // write it to the destination
+ err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags);
+ if (UNEXPECTED(err))
+ return err;
}
+ if (err == error::METADATA_NOT_FOUND)
+ return std::error_condition();
+ else
+ return err;
}
/**
@@ -1577,22 +1716,27 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1)
std::vector<uint8_t> filedata;
std::vector<metadata_hash> hasharray;
metadata_entry metaentry;
- for (bool has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
+ std::error_condition err;
+ for (err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
{
// if not checksumming, continue
- if ((metaentry.flags & CHD_MDFLAGS_CHECKSUM) == 0)
+ if (!(metaentry.flags & CHD_MDFLAGS_CHECKSUM))
continue;
// allocate memory and read the data
filedata.resize(metaentry.length);
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length);
+ err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length);
+ if (UNEXPECTED(err))
+ throw err;
// create an entry for this metadata and add it
metadata_hash hashentry;
- be_write(hashentry.tag, metaentry.metatag, 4);
+ put_u32be(hashentry.tag, metaentry.metatag);
hashentry.sha1 = util::sha1_creator::simple(&filedata[0], metaentry.length);
hasharray.push_back(hashentry);
}
+ if (err != error::METADATA_NOT_FOUND)
+ throw err;
// sort the array
if (!hasharray.empty())
@@ -1607,7 +1751,7 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1)
}
/**
- * @fn chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config)
+ * @fn std::error_condition chd_file::codec_configure(chd_codec_type codec, int param, void *config)
*
* @brief -------------------------------------------------
* codec_config - set internal codec parameters
@@ -1617,27 +1761,26 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1)
* @param param The parameter.
* @param [in,out] config If non-null, the configuration.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config)
+std::error_condition chd_file::codec_configure(chd_codec_type codec, int param, void *config)
{
// wrap this for clean reporting
try
{
// find the codec and call its configuration
- for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++)
+ for (int codecnum = 0; codecnum < std::size(m_compression); codecnum++)
if (m_compression[codecnum] == codec)
{
m_decompressor[codecnum]->configure(param, config);
- return CHDERR_NONE;
+ return std::error_condition();
}
- return CHDERR_INVALID_PARAMETER;
+ return std::errc::invalid_argument;
}
-
- // return any errors
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
+ // return any errors
return err;
}
}
@@ -1654,44 +1797,49 @@ chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *confi
* @return null if it fails, else a char*.
*/
-const char *chd_file::error_string(chd_error err)
+std::error_category const &chd_category() noexcept
{
- switch (err)
+ class chd_category_impl : public std::error_category
{
- case CHDERR_NONE: return "no error";
- case CHDERR_NO_INTERFACE: return "no drive interface";
- case CHDERR_OUT_OF_MEMORY: return "out of memory";
- case CHDERR_NOT_OPEN: return "file not open";
- case CHDERR_ALREADY_OPEN: return "file already open";
- case CHDERR_INVALID_FILE: return "invalid file";
- case CHDERR_INVALID_PARAMETER: return "invalid parameter";
- case CHDERR_INVALID_DATA: return "invalid data";
- case CHDERR_FILE_NOT_FOUND: return "file not found";
- case CHDERR_REQUIRES_PARENT: return "requires parent";
- case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable";
- case CHDERR_READ_ERROR: return "read error";
- case CHDERR_WRITE_ERROR: return "write error";
- case CHDERR_CODEC_ERROR: return "codec error";
- case CHDERR_INVALID_PARENT: return "invalid parent";
- case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range";
- case CHDERR_DECOMPRESSION_ERROR: return "decompression error";
- case CHDERR_COMPRESSION_ERROR: return "compression error";
- case CHDERR_CANT_CREATE_FILE: return "can't create file";
- case CHDERR_CANT_VERIFY: return "can't verify file";
- case CHDERR_NOT_SUPPORTED: return "operation not supported";
- case CHDERR_METADATA_NOT_FOUND: return "can't find metadata";
- case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size";
- case CHDERR_UNSUPPORTED_VERSION: return "mismatched DIFF and CHD or unsupported CHD version";
- case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify";
- case CHDERR_INVALID_METADATA: return "invalid metadata";
- case CHDERR_INVALID_STATE: return "invalid state";
- case CHDERR_OPERATION_PENDING: return "operation pending";
- case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format";
- case CHDERR_UNKNOWN_COMPRESSION: return "unknown compression type";
- case CHDERR_WALKING_PARENT: return "currently examining parent";
- case CHDERR_COMPRESSING: return "currently compressing";
- default: return "undocumented error";
- }
+ virtual char const *name() const noexcept override { return "chd"; }
+
+ virtual std::string message(int condition) const override
+ {
+ using namespace std::literals;
+ static std::string_view const s_messages[] = {
+ "No error"sv,
+ "No drive interface"sv,
+ "File not open"sv,
+ "File already open"sv,
+ "Invalid file"sv,
+ "Invalid data"sv,
+ "Requires parent"sv,
+ "File not writeable"sv,
+ "Codec error"sv,
+ "Invalid parent"sv,
+ "Hunk out of range"sv,
+ "Decompression error"sv,
+ "Compression error"sv,
+ "Can't verify file"sv,
+ "Can't find metadata"sv,
+ "Invalid metadata size"sv,
+ "Mismatched DIFF and CHD or unsupported CHD version"sv,
+ "Incomplete verify"sv,
+ "Invalid metadata"sv,
+ "Invalid state"sv,
+ "Operation pending"sv,
+ "Unsupported format"sv,
+ "Unknown compression type"sv,
+ "Currently examining parent"sv,
+ "Currently compressing"sv };
+ if ((0 <= condition) && (std::size(s_messages) > condition))
+ return std::string(s_messages[condition]);
+ else
+ return "Unknown error"s;
+ }
+ };
+ static chd_category_impl const s_chd_category_instance;
+ return s_chd_category_instance;
}
@@ -1716,16 +1864,16 @@ uint32_t chd_file::guess_unitbytes()
// look for hard disk metadata; if found, then the unit size == sector size
std::string metadata;
int i0, i1, i2, i3;
- if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4)
+ if (!read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) && sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4)
return i3;
// look for CD-ROM metadata; if found, then the unit size == CD frame size
- if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE ||
- read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE ||
- read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE)
- return CD_FRAME_SIZE;
+ if (!read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) ||
+ !read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) ||
+ !read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) ||
+ !read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) ||
+ !read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata))
+ return cdrom_file::FRAME_SIZE;
// otherwise, just map 1:1 with the hunk size
return m_hunkbytes;
@@ -1750,28 +1898,28 @@ uint32_t chd_file::guess_unitbytes()
void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1)
{
// verify header length
- if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE)
- throw CHDERR_INVALID_FILE;
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V3_HEADER_SIZE))
+ throw std::error_condition(error::INVALID_FILE);
// extract core info
- m_logicalbytes = be_read(&rawheader[28], 8);
+ m_logicalbytes = get_u64be(&rawheader[28]);
m_mapoffset = 120;
- m_metaoffset = be_read(&rawheader[36], 8);
- m_hunkbytes = be_read(&rawheader[76], 4);
- m_hunkcount = be_read(&rawheader[24], 4);
+ m_metaoffset = get_u64be(&rawheader[36]);
+ m_hunkbytes = get_u32be(&rawheader[76]);
+ m_hunkcount = get_u32be(&rawheader[24]);
// extract parent SHA-1
- uint32_t flags = be_read(&rawheader[16], 4);
+ uint32_t flags = get_u32be(&rawheader[16]);
m_allow_writes = (flags & 2) == 0;
// determine compression
- switch (be_read(&rawheader[20], 4))
+ switch (get_u32be(&rawheader[20]))
{
case 0: m_compression[0] = CHD_CODEC_NONE; break;
case 1: m_compression[0] = CHD_CODEC_ZLIB; break;
case 2: m_compression[0] = CHD_CODEC_ZLIB; break;
case 3: m_compression[0] = CHD_CODEC_AVHUFF; break;
- default: throw CHDERR_UNKNOWN_COMPRESSION;
+ default: throw std::error_condition(error::UNKNOWN_COMPRESSION);
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE;
@@ -1813,28 +1961,28 @@ void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1)
void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1)
{
// verify header length
- if (be_read(&rawheader[8], 4) != V4_HEADER_SIZE)
- throw CHDERR_INVALID_FILE;
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V4_HEADER_SIZE))
+ throw std::error_condition(error::INVALID_FILE);
// extract core info
- m_logicalbytes = be_read(&rawheader[28], 8);
+ m_logicalbytes = get_u64be(&rawheader[28]);
m_mapoffset = 108;
- m_metaoffset = be_read(&rawheader[36], 8);
- m_hunkbytes = be_read(&rawheader[44], 4);
- m_hunkcount = be_read(&rawheader[24], 4);
+ m_metaoffset = get_u64be(&rawheader[36]);
+ m_hunkbytes = get_u32be(&rawheader[44]);
+ m_hunkcount = get_u32be(&rawheader[24]);
// extract parent SHA-1
- uint32_t flags = be_read(&rawheader[16], 4);
+ uint32_t flags = get_u32be(&rawheader[16]);
m_allow_writes = (flags & 2) == 0;
// determine compression
- switch (be_read(&rawheader[20], 4))
+ switch (get_u32be(&rawheader[20]))
{
case 0: m_compression[0] = CHD_CODEC_NONE; break;
case 1: m_compression[0] = CHD_CODEC_ZLIB; break;
case 2: m_compression[0] = CHD_CODEC_ZLIB; break;
case 3: m_compression[0] = CHD_CODEC_AVHUFF; break;
- default: throw CHDERR_UNKNOWN_COMPRESSION;
+ default: throw std::error_condition(error::UNKNOWN_COMPRESSION);
}
m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE;
@@ -1873,23 +2021,23 @@ void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1)
void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1)
{
// verify header length
- if (be_read(&rawheader[8], 4) != V5_HEADER_SIZE)
- throw CHDERR_INVALID_FILE;
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V5_HEADER_SIZE))
+ throw std::error_condition(error::INVALID_FILE);
// extract core info
- m_logicalbytes = be_read(&rawheader[32], 8);
- m_mapoffset = be_read(&rawheader[40], 8);
- m_metaoffset = be_read(&rawheader[48], 8);
- m_hunkbytes = be_read(&rawheader[56], 4);
+ m_logicalbytes = get_u64be(&rawheader[32]);
+ m_mapoffset = get_u64be(&rawheader[40]);
+ m_metaoffset = get_u64be(&rawheader[48]);
+ m_hunkbytes = get_u32be(&rawheader[56]);
m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
- m_unitbytes = be_read(&rawheader[60], 4);
+ m_unitbytes = get_u32be(&rawheader[60]);
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
// determine compression
- m_compression[0] = be_read(&rawheader[16], 4);
- m_compression[1] = be_read(&rawheader[20], 4);
- m_compression[2] = be_read(&rawheader[24], 4);
- m_compression[3] = be_read(&rawheader[28], 4);
+ m_compression[0] = get_u32be(&rawheader[16]);
+ m_compression[1] = get_u32be(&rawheader[20]);
+ m_compression[2] = get_u32be(&rawheader[24]);
+ m_compression[3] = get_u32be(&rawheader[28]);
m_allow_writes = !compressed();
@@ -1908,7 +2056,7 @@ void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1)
}
/**
- * @fn chd_error chd_file::compress_v5_map()
+ * @fn std::error_condition chd_file::compress_v5_map()
*
* @brief -------------------------------------------------
* compress_v5_map - compress the v5 map and write it to the end of the file
@@ -1917,10 +2065,10 @@ void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1)
* @exception CHDERR_COMPRESSION_ERROR Thrown when a chderr compression error error
* condition occurs.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::compress_v5_map()
+std::error_condition chd_file::compress_v5_map()
{
try
{
@@ -1943,14 +2091,14 @@ chd_error chd_file::compress_v5_map()
uint32_t max_complen = 0;
uint8_t lastcomp = 0;
int count = 0;
- for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+ for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++)
{
uint8_t curcomp = m_rawmap[hunknum * 12 + 0];
- // promote self block references to more compact forms
if (curcomp == COMPRESSION_SELF)
{
- uint32_t refhunk = be_read(&m_rawmap[hunknum * 12 + 4], 6);
+ // promote self block references to more compact forms
+ uint32_t refhunk = get_u48be(&m_rawmap[hunknum * 12 + 4]);
if (refhunk == last_self)
curcomp = COMPRESSION_SELF_0;
else if (refhunk == last_self + 1)
@@ -1959,12 +2107,11 @@ chd_error chd_file::compress_v5_map()
max_self = std::max(max_self, refhunk);
last_self = refhunk;
}
-
- // promote parent block references to more compact forms
else if (curcomp == COMPRESSION_PARENT)
{
- uint32_t refunit = be_read(&m_rawmap[hunknum * 12 + 4], 6);
- if (refunit == (uint64_t(hunknum) * uint64_t(m_hunkbytes)) / m_unitbytes)
+ // promote parent block references to more compact forms
+ uint32_t refunit = get_u48be(&m_rawmap[hunknum * 12 + 4]);
+ if (refunit == mulu_32x32(hunknum, m_hunkbytes) / m_unitbytes)
curcomp = COMPRESSION_PARENT_SELF;
else if (refunit == last_parent)
curcomp = COMPRESSION_PARENT_0;
@@ -1977,7 +2124,7 @@ chd_error chd_file::compress_v5_map()
// track maximum compressed length
else //if (curcomp >= COMPRESSION_TYPE_0 && curcomp <= COMPRESSION_TYPE_3)
- max_complen = std::max(max_complen, uint32_t(be_read(&m_rawmap[hunknum * 12 + 1], 3)));
+ max_complen = std::max<uint32_t>(max_complen, get_u24be(&m_rawmap[hunknum * 12 + 1]));
// track repeats
if (curcomp == lastcomp)
@@ -2010,37 +2157,48 @@ chd_error chd_file::compress_v5_map()
}
}
- // compute a tree and export it to the buffer
- std::vector<uint8_t> compressed(m_hunkcount * 6);
+ // determine the number of bits we need to hold the a length and a hunk index
+ const uint8_t lengthbits = bits_for_value(max_complen);
+ const uint8_t selfbits = bits_for_value(max_self);
+ const uint8_t parentbits = bits_for_value(max_parent);
+
+ // determine the needed size of the output buffer
+ // 16 bytes is required for the header
+ // max len per entry given to huffman encoder at instantiation is 8 bits
+ // this corresponds to worst-case max 12 bits per entry when RLE encoded.
+ // max additional bits per entry after RLE encoded tree is
+ // for COMPRESSION_TYPE_0-3: lengthbits+16
+ // for COMPRESSION_NONE: 16
+ // for COMPRESSION_SELF: selfbits
+ // for COMPRESSION_PARENT: parentbits
+ // the overall size is clamped later with bitbuf.flush()
+ int nbits_needed = (8*16) + (12 + std::max<int>({lengthbits+16, selfbits, parentbits}))*m_hunkcount;
+ std::vector<uint8_t> compressed(nbits_needed / 8 + 1);
bitstream_out bitbuf(&compressed[16], compressed.size() - 16);
+
+ // compute a tree and export it to the buffer
huffman_error err = encoder.compute_tree_from_histo();
- if (err != HUFFERR_NONE)
- throw CHDERR_COMPRESSION_ERROR;
+ if (UNEXPECTED(err != HUFFERR_NONE))
+ throw std::error_condition(error::COMPRESSION_ERROR);
err = encoder.export_tree_rle(bitbuf);
- if (err != HUFFERR_NONE)
- throw CHDERR_COMPRESSION_ERROR;
+ if (UNEXPECTED(err != HUFFERR_NONE))
+ throw std::error_condition(error::COMPRESSION_ERROR);
// encode the data
for (uint8_t *src = &compression_rle[0]; src < dest; src++)
encoder.encode_one(bitbuf, *src);
- // determine the number of bits we need to hold the a length
- // and a hunk index
- uint8_t lengthbits = bits_for_value(max_complen);
- uint8_t selfbits = bits_for_value(max_self);
- uint8_t parentbits = bits_for_value(max_parent);
-
// for each compression type, output the relevant data
lastcomp = 0;
count = 0;
uint8_t *src = &compression_rle[0];
uint64_t firstoffs = 0;
- for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+ for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++)
{
uint8_t *rawmap = &m_rawmap[hunknum * 12];
- uint32_t length = be_read(&rawmap[1], 3);
- uint64_t offset = be_read(&rawmap[4], 6);
- uint16_t crc = be_read(&rawmap[10], 2);
+ uint32_t length = get_u24be(&rawmap[1]);
+ uint64_t offset = get_u48be(&rawmap[4]);
+ uint16_t crc = get_u16be(&rawmap[10]);
// if no count remaining, fetch the next entry
if (count == 0)
@@ -2098,9 +2256,9 @@ chd_error chd_file::compress_v5_map()
// write the map header
uint32_t complen = bitbuf.flush();
assert(!bitbuf.overflow());
- be_write(&compressed[0], complen, 4);
- be_write(&compressed[4], firstoffs, 6);
- be_write(&compressed[10], mapcrc, 2);
+ put_u32be(&compressed[0], complen);
+ put_u48be(&compressed[4], firstoffs);
+ put_u16be(&compressed[10], mapcrc);
compressed[12] = lengthbits;
compressed[13] = selfbits;
compressed[14] = parentbits;
@@ -2111,14 +2269,17 @@ chd_error chd_file::compress_v5_map()
// then write the map offset
uint8_t rawbuf[sizeof(uint64_t)];
- be_write(rawbuf, m_mapoffset, 8);
- file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf));
- return CHDERR_NONE;
+ put_u64be(rawbuf, m_mapoffset);
+ return file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf));
}
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
return err;
}
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
}
/**
@@ -2143,27 +2304,32 @@ void chd_file::decompress_v5_map()
// read the reader
uint8_t rawbuf[16];
- file_read(m_mapoffset, rawbuf, sizeof(rawbuf));
- uint32_t const mapbytes = be_read(&rawbuf[0], 4);
- uint64_t const firstoffs = be_read(&rawbuf[4], 6);
- util::crc16_t const mapcrc = be_read(&rawbuf[10], 2);
+ std::error_condition ioerr;
+ ioerr = file_read(m_mapoffset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(ioerr))
+ throw ioerr;
+ uint32_t const mapbytes = get_u32be(&rawbuf[0]);
+ uint64_t const firstoffs = get_u48be(&rawbuf[4]);
+ util::crc16_t const mapcrc = get_u16be(&rawbuf[10]);
uint8_t const lengthbits = rawbuf[12];
uint8_t const selfbits = rawbuf[13];
uint8_t const parentbits = rawbuf[14];
// now read the map
std::vector<uint8_t> compressed(mapbytes);
- file_read(m_mapoffset + 16, &compressed[0], mapbytes);
+ ioerr = file_read(m_mapoffset + 16, &compressed[0], mapbytes);
+ if (UNEXPECTED(ioerr))
+ throw ioerr;
bitstream_in bitbuf(&compressed[0], compressed.size());
// first decode the compression types
huffman_decoder<16, 8> decoder;
- huffman_error err = decoder.import_tree_rle(bitbuf);
- if (err != HUFFERR_NONE)
- throw CHDERR_DECOMPRESSION_ERROR;
+ huffman_error const huferr = decoder.import_tree_rle(bitbuf);
+ if (UNEXPECTED(huferr != HUFFERR_NONE))
+ throw std::error_condition(error::DECOMPRESSION_ERROR);
uint8_t lastcomp = 0;
int repcount = 0;
- for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+ for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++)
{
uint8_t *rawmap = &m_rawmap[hunknum * 12];
if (repcount > 0)
@@ -2184,7 +2350,7 @@ void chd_file::decompress_v5_map()
uint64_t curoffset = firstoffs;
uint32_t last_self = 0;
uint64_t last_parent = 0;
- for (int hunknum = 0; hunknum < m_hunkcount; hunknum++)
+ for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++)
{
uint8_t *rawmap = &m_rawmap[hunknum * 12];
uint64_t offset = curoffset;
@@ -2218,6 +2384,7 @@ void chd_file::decompress_v5_map()
// pseudo-types; convert into base types
case COMPRESSION_SELF_1:
last_self++;
+ [[fallthrough]];
case COMPRESSION_SELF_0:
rawmap[0] = COMPRESSION_SELF;
offset = last_self;
@@ -2225,28 +2392,29 @@ void chd_file::decompress_v5_map()
case COMPRESSION_PARENT_SELF:
rawmap[0] = COMPRESSION_PARENT;
- last_parent = offset = (uint64_t(hunknum) * uint64_t(m_hunkbytes)) / m_unitbytes;
+ last_parent = offset = mulu_32x32(hunknum, m_hunkbytes) / m_unitbytes;
break;
case COMPRESSION_PARENT_1:
last_parent += m_hunkbytes / m_unitbytes;
+ [[fallthrough]];
case COMPRESSION_PARENT_0:
rawmap[0] = COMPRESSION_PARENT;
offset = last_parent;
break;
}
- be_write(&rawmap[1], length, 3);
- be_write(&rawmap[4], offset, 6);
- be_write(&rawmap[10], crc, 2);
+ put_u24be(&rawmap[1], length);
+ put_u48be(&rawmap[4], offset);
+ put_u16be(&rawmap[10], crc);
}
// verify the final CRC
- if (util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc)
- throw CHDERR_DECOMPRESSION_ERROR;
+ if (UNEXPECTED(util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc))
+ throw std::error_condition(error::DECOMPRESSION_ERROR);
}
/**
- * @fn chd_error chd_file::create_common()
+ * @fn std::error_condition chd_file::create_common()
*
* @brief -------------------------------------------------
* create_common - command path when creating a new CHD file
@@ -2262,7 +2430,7 @@ void chd_file::decompress_v5_map()
* @return The new common.
*/
-chd_error chd_file::create_common()
+std::error_condition chd_file::create_common()
{
// wrap in try for proper error handling
try
@@ -2271,14 +2439,14 @@ chd_error chd_file::create_common()
m_metaoffset = 0;
// if we have a parent, it must be V3 or later
- if (m_parent != nullptr && m_parent->version() < 3)
- throw CHDERR_UNSUPPORTED_VERSION;
+ if (UNEXPECTED(m_parent && m_parent->version() < 3))
+ throw std::error_condition(error::UNSUPPORTED_VERSION);
// must be an even number of units per hunk
- if (m_hunkbytes % m_unitbytes != 0)
- throw CHDERR_INVALID_PARAMETER;
- if (m_parent != nullptr && m_unitbytes != m_parent->unit_bytes())
- throw CHDERR_INVALID_PARAMETER;
+ if (UNEXPECTED(m_hunkbytes % m_unitbytes != 0))
+ throw std::error_condition(std::errc::invalid_argument);
+ if (UNEXPECTED(m_parent && m_unitbytes != m_parent->unit_bytes()))
+ throw std::error_condition(std::errc::invalid_argument);
// verify the compression types
bool found_zero = false;
@@ -2288,31 +2456,33 @@ chd_error chd_file::create_common()
if (elem == CHD_CODEC_NONE)
found_zero = true;
else if (found_zero)
- throw CHDERR_INVALID_PARAMETER;
+ throw std::error_condition(std::errc::invalid_argument);
else if (!chd_codec_list::codec_exists(elem))
- throw CHDERR_UNKNOWN_COMPRESSION;
+ throw std::error_condition(error::UNKNOWN_COMPRESSION);
}
// create our V5 header
uint8_t rawheader[V5_HEADER_SIZE];
memcpy(&rawheader[0], "MComprHD", 8);
- be_write(&rawheader[8], V5_HEADER_SIZE, 4);
- be_write(&rawheader[12], m_version, 4);
- be_write(&rawheader[16], m_compression[0], 4);
- be_write(&rawheader[20], m_compression[1], 4);
- be_write(&rawheader[24], m_compression[2], 4);
- be_write(&rawheader[28], m_compression[3], 4);
- be_write(&rawheader[32], m_logicalbytes, 8);
- be_write(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE, 8);
- be_write(&rawheader[48], m_metaoffset, 8);
- be_write(&rawheader[56], m_hunkbytes, 4);
- be_write(&rawheader[60], m_unitbytes, 4);
+ put_u32be(&rawheader[8], V5_HEADER_SIZE);
+ put_u32be(&rawheader[12], m_version);
+ put_u32be(&rawheader[16], m_compression[0]);
+ put_u32be(&rawheader[20], m_compression[1]);
+ put_u32be(&rawheader[24], m_compression[2]);
+ put_u32be(&rawheader[28], m_compression[3]);
+ put_u64be(&rawheader[32], m_logicalbytes);
+ put_u64be(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE);
+ put_u64be(&rawheader[48], m_metaoffset);
+ put_u32be(&rawheader[56], m_hunkbytes);
+ put_u32be(&rawheader[60], m_unitbytes);
be_write_sha1(&rawheader[64], util::sha1_t::null);
be_write_sha1(&rawheader[84], util::sha1_t::null);
- be_write_sha1(&rawheader[104], (m_parent != nullptr) ? m_parent->sha1() : util::sha1_t::null);
+ be_write_sha1(&rawheader[104], m_parent ? m_parent->sha1() : util::sha1_t::null);
// write the resulting header
- file_write(0, rawheader, sizeof(rawheader));
+ std::error_condition err = file_write(0, rawheader, sizeof(rawheader));
+ if (UNEXPECTED(err))
+ throw err;
// parse it back out to set up fields appropriately
util::sha1_t parentsha1;
@@ -2330,8 +2500,10 @@ chd_error chd_file::create_common()
uint64_t offset = m_mapoffset;
while (mapsize != 0)
{
- uint32_t bytes_to_write = (std::min<size_t>)(mapsize, sizeof(buffer));
- file_write(offset, buffer, bytes_to_write);
+ uint32_t const bytes_to_write = std::min<size_t>(mapsize, sizeof(buffer));
+ err = file_write(offset, buffer, bytes_to_write);
+ if (UNEXPECTED(err))
+ throw err;
offset += bytes_to_write;
mapsize -= bytes_to_write;
}
@@ -2340,10 +2512,9 @@ chd_error chd_file::create_common()
// finish opening the file
create_open_common();
}
-
- // handle errors by closing ourself
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
+ // handle errors by closing ourself
close();
return err;
}
@@ -2352,11 +2523,11 @@ chd_error chd_file::create_common()
close();
throw;
}
- return CHDERR_NONE;
+ return std::error_condition();
}
/**
- * @fn chd_error chd_file::open_common(bool writeable)
+ * @fn std::error_condition chd_file::open_common(bool writeable)
*
* @brief -------------------------------------------------
* open_common - common path when opening an existing CHD file for input
@@ -2375,10 +2546,10 @@ chd_error chd_file::create_common()
*
* @param writeable true if writeable.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file::open_common(bool writeable)
+std::error_condition chd_file::open_common(bool writeable, const open_parent_func &open_parent)
{
// wrap in try for proper error handling
try
@@ -2388,13 +2559,15 @@ chd_error chd_file::open_common(bool writeable)
// read the raw header
uint8_t rawheader[MAX_HEADER_SIZE];
- file_read(0, rawheader, sizeof(rawheader));
+ std::error_condition err = file_read(0, rawheader, sizeof(rawheader));
+ if (UNEXPECTED(err))
+ throw err;
// verify the signature
- if (memcmp(rawheader, "MComprHD", 8) != 0)
- throw CHDERR_INVALID_FILE;
+ if (UNEXPECTED(memcmp(rawheader, "MComprHD", 8) != 0))
+ throw std::error_condition(error::INVALID_FILE);
- m_version = be_read(&rawheader[12], 4);
+ m_version = get_u32be(&rawheader[12]);
// read the header if we support it
util::sha1_t parentsha1 = util::sha1_t::null;
@@ -2403,35 +2576,39 @@ chd_error chd_file::open_common(bool writeable)
case 3: parse_v3_header(rawheader, parentsha1); break;
case 4: parse_v4_header(rawheader, parentsha1); break;
case 5: parse_v5_header(rawheader, parentsha1); break;
- default: throw CHDERR_UNSUPPORTED_VERSION;
+ default: throw std::error_condition(error::UNSUPPORTED_VERSION);
}
// only allow writes to the most recent version
if (m_version < HEADER_VERSION)
m_allow_writes = false;
- if (writeable && !m_allow_writes)
- throw CHDERR_FILE_NOT_WRITEABLE;
+ if (UNEXPECTED(writeable && !m_allow_writes))
+ throw std::error_condition(error::FILE_NOT_WRITEABLE);
// make sure we have a parent if we need one (and don't if we don't)
if (parentsha1 != util::sha1_t::null)
{
- if (m_parent == nullptr)
+ if (!m_parent && open_parent)
+ m_parent = open_parent(parentsha1);
+
+ if (!m_parent)
m_parent_missing = true;
else if (m_parent->sha1() != parentsha1)
- throw CHDERR_INVALID_PARENT;
+ throw std::error_condition(error::INVALID_PARENT);
+ }
+ else if (UNEXPECTED(m_parent))
+ {
+ throw std::error_condition(std::errc::invalid_argument);
}
- else if (m_parent != nullptr)
- throw CHDERR_INVALID_PARAMETER;
// finish opening the file
create_open_common();
- return CHDERR_NONE;
+ return std::error_condition();
}
-
- // handle errors by closing ourself
- catch (chd_error &err)
+ catch (std::error_condition const &err)
{
+ // handle errors by closing ourself
close();
return err;
}
@@ -2451,19 +2628,25 @@ chd_error chd_file::open_common(bool writeable)
void chd_file::create_open_common()
{
// verify the compression types and initialize the codecs
- for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++)
+ for (int decompnum = 0; decompnum < std::size(m_compression); decompnum++)
{
m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this);
- if (m_decompressor[decompnum] == nullptr && m_compression[decompnum] != 0)
- throw CHDERR_UNKNOWN_COMPRESSION;
+ if (UNEXPECTED(!m_decompressor[decompnum] && (m_compression[decompnum] != 0)))
+ throw std::error_condition(error::UNKNOWN_COMPRESSION);
}
// read the map; v5+ compressed drives need to read and decompress their map
m_rawmap.resize(m_hunkcount * m_mapentrybytes);
if (m_version >= 5 && compressed())
+ {
decompress_v5_map();
+ }
else
- file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size());
+ {
+ std::error_condition err = file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size());
+ if (UNEXPECTED(err))
+ throw err;
+ }
// allocate the temporary compressed buffer and a buffer for caching
m_compressed.resize(m_hunkbytes);
@@ -2478,44 +2661,37 @@ void chd_file::create_open_common()
* for appending to a compressed CHD
* -------------------------------------------------.
*
- * @exception CHDERR_NOT_OPEN Thrown when a chderr not open error condition occurs.
- * @exception CHDERR_HUNK_OUT_OF_RANGE Thrown when a chderr hunk out of range error
- * condition occurs.
- * @exception CHDERR_FILE_NOT_WRITEABLE Thrown when a chderr file not writeable error
- * condition occurs.
- * @exception CHDERR_COMPRESSION_ERROR Thrown when a chderr compression error error
- * condition occurs.
- *
* @param hunknum The hunknum.
*/
-void chd_file::verify_proper_compression_append(uint32_t hunknum)
+std::error_condition chd_file::verify_proper_compression_append(uint32_t hunknum) const noexcept
{
// punt if no file
- if (m_file == nullptr)
- throw CHDERR_NOT_OPEN;
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
// return an error if out of range
- if (hunknum >= m_hunkcount)
- throw CHDERR_HUNK_OUT_OF_RANGE;
+ if (UNEXPECTED(hunknum >= m_hunkcount))
+ return std::error_condition(error::HUNK_OUT_OF_RANGE);
// if not writeable, fail
- if (!m_allow_writes)
- throw CHDERR_FILE_NOT_WRITEABLE;
+ if (UNEXPECTED(!m_allow_writes))
+ return std::error_condition(error::FILE_NOT_WRITEABLE);
// compressed writes only via this interface
- if (!compressed())
- throw CHDERR_FILE_NOT_WRITEABLE;
+ if (UNEXPECTED(!compressed()))
+ return std::error_condition(error::FILE_NOT_WRITEABLE);
// only permitted to write new blocks
- uint8_t *rawmap = &m_rawmap[hunknum * 12];
- if (rawmap[0] != 0xff)
- throw CHDERR_COMPRESSION_ERROR;
+ uint8_t const *const rawmap = &m_rawmap[hunknum * 12];
+ if (UNEXPECTED(rawmap[0] != 0xff))
+ return std::error_condition(error::COMPRESSION_ERROR);
+
+ // if this isn't the first block, only permitted to write immediately after the previous one
+ if (UNEXPECTED((hunknum != 0) && (rawmap[-12] == 0xff)))
+ return std::error_condition(error::COMPRESSION_ERROR);
- // if this isn't the first block, only permitted to write immediately
- // after the previous one
- if (hunknum != 0 && rawmap[-12] == 0xff)
- throw CHDERR_COMPRESSION_ERROR;
+ return std::error_condition();
}
/**
@@ -2536,7 +2712,9 @@ void chd_file::verify_proper_compression_append(uint32_t hunknum)
void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16)
{
// verify that we are appending properly to a compressed file
- verify_proper_compression_append(hunknum);
+ std::error_condition err = verify_proper_compression_append(hunknum);
+ if (UNEXPECTED(err))
+ throw err;
// write the final result
uint64_t offset = file_append(compressed, complength);
@@ -2544,9 +2722,9 @@ void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const
// update the map entry
uint8_t *rawmap = &m_rawmap[hunknum * 12];
rawmap[0] = (compression == -1) ? COMPRESSION_NONE : compression;
- be_write(&rawmap[1], complength, 3);
- be_write(&rawmap[4], offset, 6);
- be_write(&rawmap[10], crc16, 2);
+ put_u24be(&rawmap[1], complength);
+ put_u48be(&rawmap[4], offset);
+ put_u16be(&rawmap[10], crc16);
}
/**
@@ -2566,18 +2744,20 @@ void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const
void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk)
{
// verify that we are appending properly to a compressed file
- verify_proper_compression_append(hunknum);
+ std::error_condition err = verify_proper_compression_append(hunknum);
+ if (UNEXPECTED(err))
+ throw err;
// only permitted to reference prior hunks
- if (otherhunk >= hunknum)
- throw CHDERR_INVALID_PARAMETER;
+ if (UNEXPECTED(otherhunk >= hunknum))
+ throw std::error_condition(error::HUNK_OUT_OF_RANGE);
// update the map entry
uint8_t *rawmap = &m_rawmap[hunknum * 12];
rawmap[0] = COMPRESSION_SELF;
- be_write(&rawmap[1], 0, 3);
- be_write(&rawmap[4], otherhunk, 6);
- be_write(&rawmap[10], 0, 2);
+ put_u24be(&rawmap[1], 0);
+ put_u48be(&rawmap[4], otherhunk);
+ put_u16be(&rawmap[10], 0);
}
/**
@@ -2594,18 +2774,20 @@ void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk)
void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit)
{
// verify that we are appending properly to a compressed file
- verify_proper_compression_append(hunknum);
+ std::error_condition err = verify_proper_compression_append(hunknum);
+ if (UNEXPECTED(err))
+ throw err;
// update the map entry
- uint8_t *rawmap = &m_rawmap[hunknum * 12];
+ uint8_t *const rawmap = &m_rawmap[hunknum * 12];
rawmap[0] = COMPRESSION_PARENT;
- be_write(&rawmap[1], 0, 3);
- be_write(&rawmap[4], parentunit, 6);
- be_write(&rawmap[10], 0, 2);
+ put_u24be(&rawmap[1], 0);
+ put_u48be(&rawmap[4], parentunit);
+ put_u16be(&rawmap[10], 0);
}
/**
- * @fn bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume)
+ * @fn std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume)
*
* @brief -------------------------------------------------
* metadata_find - find a metadata entry
@@ -2616,10 +2798,10 @@ void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit)
* @param [in,out] metaentry The metaentry.
* @param resume true to resume.
*
- * @return true if it succeeds, false if it fails.
+ * @return A std::error_condition (error::METADATA_NOT_FOUND if the search fails).
*/
-bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume)
+std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) const noexcept
{
// start at the beginning unless we're resuming a previous search
if (!resume)
@@ -2638,18 +2820,20 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada
{
// read the raw header
uint8_t raw_meta_header[METADATA_HEADER_SIZE];
- file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header));
+ std::error_condition err = file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header));
+ if (UNEXPECTED(err))
+ return err;
// extract the data
- metaentry.metatag = be_read(&raw_meta_header[0], 4);
+ metaentry.metatag = get_u32be(&raw_meta_header[0]);
metaentry.flags = raw_meta_header[4];
- metaentry.length = be_read(&raw_meta_header[5], 3);
- metaentry.next = be_read(&raw_meta_header[8], 8);
+ metaentry.length = get_u24be(&raw_meta_header[5]);
+ metaentry.next = get_u64be(&raw_meta_header[8]);
// if we got a match, proceed
if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag)
if (metaindex-- == 0)
- return true;
+ return std::error_condition();
// no match, fetch the next link
metaentry.prev = metaentry.offset;
@@ -2657,7 +2841,7 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada
}
// if we get here, we didn't find it
- return false;
+ return error::METADATA_NOT_FOUND;
}
/**
@@ -2671,27 +2855,28 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada
* @param nextoffset The nextoffset.
*/
-void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset)
+std::error_condition chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) noexcept
{
uint64_t offset = 0;
- // if we were the first entry, make the next entry the first
if (prevoffset == 0)
{
+ // if we were the first entry, make the next entry the first
offset = m_metaoffset_offset;
m_metaoffset = nextoffset;
}
-
- // otherwise, update the link in the previous header
else
+ {
+ // otherwise, update the link in the previous header
offset = prevoffset + 8;
+ }
// create a big-endian version
uint8_t rawbuf[sizeof(uint64_t)];
- be_write(rawbuf, nextoffset, 8);
+ put_u64be(rawbuf, nextoffset);
// write to the header and update our local copy
- file_write(offset, rawbuf, sizeof(rawbuf));
+ return file_write(offset, rawbuf, sizeof(rawbuf));
}
/**
@@ -2705,7 +2890,7 @@ void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffs
void chd_file::metadata_update_hash()
{
// only works for V4 and above, and only for compressed CHDs
- if (m_version < 4 || !compressed())
+ if ((m_version < 4) || !compressed())
return;
// compute the new overall hash
@@ -2716,7 +2901,9 @@ void chd_file::metadata_update_hash()
be_write_sha1(&rawbuf[0], fullsha1);
// write to the header
- file_write(m_sha1_offset, rawbuf, sizeof(rawbuf));
+ std::error_condition err = file_write(m_sha1_offset, rawbuf, sizeof(rawbuf));
+ if (UNEXPECTED(err))
+ throw err;
}
/**
@@ -2751,19 +2938,18 @@ int CLIB_DECL chd_file::metadata_hash_compare(const void *elem1, const void *ele
* -------------------------------------------------.
*/
-chd_file_compressor::chd_file_compressor()
- : m_walking_parent(false),
- m_total_in(0),
- m_total_out(0),
- m_read_queue(nullptr),
- m_read_queue_offset(0),
- m_read_done_offset(0),
- m_read_error(false),
- m_work_queue(nullptr),
- m_write_hunk(0)
+chd_file_compressor::chd_file_compressor() :
+ m_walking_parent(false),
+ m_total_in(0),
+ m_total_out(0),
+ m_read_queue(nullptr),
+ m_read_queue_offset(0),
+ m_read_done_offset(0),
+ m_work_queue(nullptr),
+ m_write_hunk(0)
{
// zap arrays
- memset(m_codecs, 0, sizeof(m_codecs));
+ std::fill(std::begin(m_codecs), std::end(m_codecs), nullptr);
// allocate work queues
m_read_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO);
@@ -2800,7 +2986,7 @@ chd_file_compressor::~chd_file_compressor()
void chd_file_compressor::compress_begin()
{
// reset state
- m_walking_parent = (m_parent != nullptr);
+ m_walking_parent = bool(m_parent);
m_total_in = 0;
m_total_out = 0;
m_compsha1.reset();
@@ -2812,7 +2998,7 @@ void chd_file_compressor::compress_begin()
// reset read state
m_read_queue_offset = 0;
m_read_done_offset = 0;
- m_read_error = false;
+ m_read_error.clear();
// reset work item state
m_work_buffer.resize(hunk_bytes() * (WORK_BUFFER_HUNKS + 1));
@@ -2839,7 +3025,7 @@ void chd_file_compressor::compress_begin()
}
/**
- * @fn chd_error chd_file_compressor::compress_continue(double &progress, double &ratio)
+ * @fn std::error_condition chd_file_compressor::compress_continue(double &progress, double &ratio)
*
* @brief -------------------------------------------------
* compress_continue - continue compression
@@ -2848,25 +3034,24 @@ void chd_file_compressor::compress_begin()
* @param [in,out] progress The progress.
* @param [in,out] ratio The ratio.
*
- * @return A chd_error.
+ * @return A std::error_condition.
*/
-chd_error chd_file_compressor::compress_continue(double &progress, double &ratio)
+std::error_condition chd_file_compressor::compress_continue(double &progress, double &ratio)
{
- // if we got an error, return an error
- if (m_read_error)
- return CHDERR_READ_ERROR;
+ // if we got an error, return the error
+ if (UNEXPECTED(m_read_error))
+ return m_read_error;
// if done reading, queue some more
while (m_read_queue_offset < m_logicalbytes && osd_work_queue_items(m_read_queue) < 2)
{
// see if we have enough free work items to read the next half of a buffer
- uint32_t startitem = m_read_queue_offset / hunk_bytes();
- uint32_t enditem = startitem + WORK_BUFFER_HUNKS / 2;
- uint32_t curitem;
- for (curitem = startitem; curitem < enditem; curitem++)
- if (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY)
- break;
+ uint32_t const startitem = m_read_queue_offset / hunk_bytes();
+ uint32_t const enditem = startitem + WORK_BUFFER_HUNKS / 2;
+ uint32_t curitem = startitem;
+ while ((curitem < enditem) && (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status == WS_READY))
+ ++curitem;
// if it's not all clear, defer
if (curitem != enditem)
@@ -2890,64 +3075,60 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio
work_item &item = m_work_item[m_write_hunk % WORK_BUFFER_HUNKS];
// free any OSD work item
- if (item.m_osd != nullptr)
+ if (item.m_osd)
+ {
osd_work_item_release(item.m_osd);
- item.m_osd = nullptr;
+ item.m_osd = nullptr;
+ }
- // for parent walking, just add to the hashmap
if (m_walking_parent)
{
- uint32_t uph = hunk_bytes() / unit_bytes();
+ // for parent walking, just add to the hashmap
+ uint32_t const uph = hunk_bytes() / unit_bytes();
uint32_t units = uph;
if (item.m_hunknum == hunk_count() - 1 || !compressed())
units = 1;
for (uint32_t unit = 0; unit < units; unit++)
+ {
if (m_parent_map.find(item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1) == hashmap::NOT_FOUND)
m_parent_map.add(item.m_hunknum * uph + unit, item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1);
+ }
}
-
- // if we're uncompressed, use regular writes
else if (!compressed())
{
- chd_error err = write_hunk(item.m_hunknum, item.m_data);
- if (err != CHDERR_NONE)
+ // if we're uncompressed, use regular writes
+ std::error_condition err = write_hunk(item.m_hunknum, item.m_data);
+ if (UNEXPECTED(err))
return err;
// writes of all-0 data don't actually take space, so see if we count this
chd_codec_type codec = CHD_CODEC_NONE;
uint32_t complen;
- hunk_info(item.m_hunknum, codec, complen);
- if (codec == CHD_CODEC_NONE)
+ err = hunk_info(item.m_hunknum, codec, complen);
+ if (!err && codec == CHD_CODEC_NONE) // TODO: report error?
m_total_out += m_hunkbytes;
}
-
- // for compressing, process the result
- else do
+ else if (uint64_t const selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); selfhunk != hashmap::NOT_FOUND)
+ {
+ // the hunk is in the self map
+ hunk_copy_from_self(item.m_hunknum, selfhunk);
+ }
+ else
{
- // first see if the hunk is in the parent or self maps
- uint64_t selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
- if (selfhunk != hashmap::NOT_FOUND)
+ // if not, see if it's in the parent map
+ uint64_t const parentunit = m_parent ? m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) : hashmap::NOT_FOUND;
+ if (parentunit != hashmap::NOT_FOUND)
{
- hunk_copy_from_self(item.m_hunknum, selfhunk);
- break;
+ hunk_copy_from_parent(item.m_hunknum, parentunit);
}
-
- // if not, see if it's in the parent map
- if (m_parent != nullptr)
+ else
{
- uint64_t parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
- if (parentunit != hashmap::NOT_FOUND)
- {
- hunk_copy_from_parent(item.m_hunknum, parentunit);
- break;
- }
+ // otherwise, append it compressed and add to the self map
+ hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16);
+ m_total_out += item.m_complen;
+ m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
}
-
- // otherwise, append it compressed and add to the self map
- hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16);
- m_total_out += item.m_complen;
- m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1);
- } while (0);
+ }
// reset the item and advance
item.m_status = WS_READY;
@@ -2956,23 +3137,24 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio
// if we hit the end, finalize
if (m_write_hunk == m_hunkcount)
{
- // if this is just walking the parent, reset and get ready for compression
if (m_walking_parent)
{
+ // if this is just walking the parent, reset and get ready for compression
m_walking_parent = false;
m_read_queue_offset = m_read_done_offset = 0;
m_write_hunk = 0;
- for (auto & elem : m_work_item)
+ for (auto &elem : m_work_item)
elem.m_status = WS_READY;
}
-
- // wait for all reads to finish and if we're compressed, write the final SHA1 and map
else
{
+ // wait for all reads to finish and if we're compressed, write the final SHA1 and map
osd_work_queue_wait(m_read_queue, 30 * osd_ticks_per_second());
if (!compressed())
- return CHDERR_NONE;
- set_raw_sha1(m_compsha1.finish());
+ return std::error_condition();
+ std::error_condition err = set_raw_sha1(m_compsha1.finish());
+ if (UNEXPECTED(err))
+ return err;
return compress_v5_map();
}
}
@@ -2987,12 +3169,12 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio
// if we're waiting for work, wait
// sometimes code can get here with .m_status == WS_READY and .m_osd != nullptr, TODO find out why this happens
- while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY &&
- m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE &&
- m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != nullptr)
+ while ((m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY) &&
+ (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE) &&
+ m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd)
osd_work_item_wait(m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd, osd_ticks_per_second());
- return m_walking_parent ? CHDERR_WALKING_PARENT : CHDERR_COMPRESSING;
+ return m_walking_parent ? error::WALKING_PARENT : error::COMPRESSING;
}
/**
@@ -3010,7 +3192,7 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio
void *chd_file_compressor::async_walk_parent_static(void *param, int threadid)
{
- work_item *item = reinterpret_cast<work_item *>(param);
+ auto *const item = reinterpret_cast<work_item *>(param);
item->m_compressor->async_walk_parent(*item);
return nullptr;
}
@@ -3052,7 +3234,7 @@ void chd_file_compressor::async_walk_parent(work_item &item)
void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid)
{
- work_item *item = reinterpret_cast<work_item *>(param);
+ auto *const item = reinterpret_cast<work_item *>(param);
item->m_compressor->async_compress_hunk(*item, threadid);
return nullptr;
}
@@ -3069,7 +3251,7 @@ void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid)
void chd_file_compressor::async_compress_hunk(work_item &item, int threadid)
{
// use our thread's codec
- assert(threadid < ARRAY_LENGTH(m_codecs));
+ assert(threadid < std::size(m_codecs));
item.m_codecs = m_codecs[threadid];
// compute CRC-16 and SHA-1 hashes
@@ -3079,8 +3261,8 @@ void chd_file_compressor::async_compress_hunk(work_item &item, int threadid)
// find the best compression scheme, unless we already have a self or parent match
// (note we may miss a self match from blocks not yet added, but this just results in extra work)
// TODO: data race
- if (m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND &&
- m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND)
+ if ((m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND) &&
+ (m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND))
item.m_compression = item.m_codecs->find_best_compressor(item.m_data, item.m_compressed, item.m_complen);
// mark us complete
@@ -3115,37 +3297,45 @@ void *chd_file_compressor::async_read_static(void *param, int threadid)
void chd_file_compressor::async_read()
{
// if in the error or complete state, stop
- if (m_read_error)
+ if (UNEXPECTED(m_read_error))
return;
// determine parameters for the read
- uint32_t work_buffer_bytes = WORK_BUFFER_HUNKS * hunk_bytes();
+ uint32_t const work_buffer_bytes = WORK_BUFFER_HUNKS * hunk_bytes();
uint32_t numbytes = work_buffer_bytes / 2;
- if (m_read_done_offset + numbytes > logical_bytes())
+ if ((m_read_done_offset + numbytes) > logical_bytes())
numbytes = logical_bytes() - m_read_done_offset;
+ uint8_t *const dest = &m_work_buffer[0] + (m_read_done_offset % work_buffer_bytes);
+ assert((&m_work_buffer[0] == dest) || (&m_work_buffer[work_buffer_bytes / 2] == dest));
+ assert(!(m_read_done_offset % hunk_bytes()));
+ uint64_t const end_offset = m_read_done_offset + numbytes;
+
// catch any exceptions coming out of here
try
{
// do the read
- uint8_t *dest = &m_work_buffer[0] + (m_read_done_offset % work_buffer_bytes);
- assert(dest == &m_work_buffer[0] || dest == &m_work_buffer[work_buffer_bytes/2]);
- uint64_t end_offset = m_read_done_offset + numbytes;
-
- // if walking the parent, read in hunks from the parent CHD
if (m_walking_parent)
{
+ // if walking the parent, read in hunks from the parent CHD
+ uint64_t curoffs = m_read_done_offset;
uint8_t *curdest = dest;
- for (uint64_t curoffs = m_read_done_offset; curoffs < end_offset + 1; curoffs += hunk_bytes())
+ uint32_t curhunk = m_read_done_offset / hunk_bytes();
+ while (curoffs < end_offset + 1)
{
- m_parent->read_hunk(curoffs / hunk_bytes(), curdest);
+ std::error_condition err = m_parent->read_hunk(curhunk, curdest);
+ if (err && (error::HUNK_OUT_OF_RANGE != err)) // FIXME: fix the code so it doesn't depend on trying to read past the end of the parent CHD
+ throw err;
+ curoffs += hunk_bytes();
curdest += hunk_bytes();
+ ++curhunk;
}
}
-
- // otherwise, call the virtual function
else
+ {
+ // otherwise, call the virtual function
read_data(dest, m_read_done_offset, numbytes);
+ }
// spawn off work for each hunk
for (uint64_t curoffs = m_read_done_offset; curoffs < end_offset; curoffs += hunk_bytes())
@@ -3169,15 +3359,15 @@ void chd_file_compressor::async_read()
// advance the read pointer
m_read_done_offset += numbytes;
}
- catch (chd_error& err)
+ catch (std::error_condition const &err)
{
- fprintf(stderr, "CHD error occurred: %s\n", chd_file::error_string(err));
- m_read_error = true;
+ fprintf(stderr, "CHD error occurred: %s\n", err.message().c_str());
+ m_read_error = err;
}
- catch (std::exception& ex)
+ catch (std::exception const &ex)
{
fprintf(stderr, "exception occurred: %s\n", ex.what());
- m_read_error = true;
+ m_read_error = std::errc::io_error; // TODO: revisit this error code
}
}
@@ -3195,8 +3385,8 @@ void chd_file_compressor::async_read()
* -------------------------------------------------.
*/
-chd_file_compressor::hashmap::hashmap()
- : m_block_list(new entry_block(nullptr))
+chd_file_compressor::hashmap::hashmap() :
+ m_block_list(new entry_block(nullptr))
{
// initialize the map to empty
memset(m_map, 0, sizeof(m_map));
@@ -3252,10 +3442,10 @@ void chd_file_compressor::hashmap::reset()
* @return An uint64_t.
*/
-uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sha1)
+uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sha1) const noexcept
{
// look up the entry in the map
- for (entry_t *entry = m_map[crc16]; entry != nullptr; entry = entry->m_next)
+ for (entry_t *entry = m_map[crc16]; entry; entry = entry->m_next)
if (entry->m_sha1 == sha1)
return entry->m_itemnum;
return NOT_FOUND;
@@ -3276,7 +3466,7 @@ uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sh
void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, util::sha1_t sha1)
{
// add to the appropriate map
- if (m_block_list->m_nextalloc == ARRAY_LENGTH(m_block_list->m_array))
+ if (m_block_list->m_nextalloc == std::size(m_block_list->m_array))
m_block_list = new entry_block(m_block_list);
entry_t *entry = &m_block_list->m_array[m_block_list->m_nextalloc++];
entry->m_itemnum = itemnum;
@@ -3284,3 +3474,41 @@ void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, ut
entry->m_next = m_map[crc16];
m_map[crc16] = entry;
}
+
+std::error_condition chd_file::check_is_hd() const noexcept
+{
+ metadata_entry metaentry;
+ return metadata_find(HARD_DISK_METADATA_TAG, 0, metaentry);
+}
+
+std::error_condition chd_file::check_is_cd() const noexcept
+{
+ metadata_entry metaentry;
+ std::error_condition err = metadata_find(CDROM_OLD_METADATA_TAG, 0, metaentry);
+ if (err == error::METADATA_NOT_FOUND)
+ err = metadata_find(CDROM_TRACK_METADATA_TAG, 0, metaentry);
+ if (err == error::METADATA_NOT_FOUND)
+ err = metadata_find(CDROM_TRACK_METADATA2_TAG, 0, metaentry);
+ return err;
+}
+
+std::error_condition chd_file::check_is_gd() const noexcept
+{
+ metadata_entry metaentry;
+ std::error_condition err = metadata_find(GDROM_OLD_METADATA_TAG, 0, metaentry);
+ if (err == error::METADATA_NOT_FOUND)
+ err = metadata_find(GDROM_TRACK_METADATA_TAG, 0, metaentry);
+ return err;
+}
+
+std::error_condition chd_file::check_is_dvd() const noexcept
+{
+ metadata_entry metaentry;
+ return metadata_find(DVD_METADATA_TAG, 0, metaentry);
+}
+
+std::error_condition chd_file::check_is_av() const noexcept
+{
+ metadata_entry metaentry;
+ return metadata_find(AV_METADATA_TAG, 0, metaentry);
+}
diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h
index 254a99daae1..671f4bdbd01 100644
--- a/src/lib/util/chd.h
+++ b/src/lib/util/chd.h
@@ -2,26 +2,27 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chd.h
-
MAME Compressed Hunks of Data file format
***************************************************************************/
-
-#ifndef MAME_UTIL_CHD_H
-#define MAME_UTIL_CHD_H
+#ifndef MAME_LIB_UTIL_CHD_H
+#define MAME_LIB_UTIL_CHD_H
#pragma once
-#include "osdcore.h"
-#include "coretmpl.h"
-#include "corestr.h"
-#include <string>
-#include "bitmap.h"
-#include "corefile.h"
-#include "hashing.h"
#include "chdcodec.h"
+#include "hashing.h"
+#include "ioprocs.h"
+
+#include "osdcore.h"
+
#include <atomic>
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <system_error>
+
/***************************************************************************
@@ -193,86 +194,52 @@
//**************************************************************************
// pseudo-codecs returned by hunk_info
-const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk
-const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk
-const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat
+constexpr chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk
+constexpr chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk
+constexpr chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat
// core types
typedef uint32_t chd_metadata_tag;
// metadata parameters
-const chd_metadata_tag CHDMETATAG_WILDCARD = 0;
-const uint32_t CHDMETAINDEX_APPEND = ~0;
+constexpr chd_metadata_tag CHDMETATAG_WILDCARD = 0;
+constexpr uint32_t CHDMETAINDEX_APPEND = ~0;
// metadata flags
-const uint8_t CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed
+constexpr uint8_t CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed
// standard hard disk metadata
-const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D');
+constexpr chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D');
extern const char *HARD_DISK_METADATA_FORMAT;
// hard disk identify information
-const chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T');
+constexpr chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T');
// hard disk key information
-const chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' ');
+constexpr chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' ');
// pcmcia CIS information
-const chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' ');
+constexpr chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' ');
// standard CD-ROM metadata
-const chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D');
-const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R');
+constexpr chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D');
+constexpr chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R');
extern const char *CDROM_TRACK_METADATA_FORMAT;
-const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2');
+constexpr chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2');
extern const char *CDROM_TRACK_METADATA2_FORMAT;
-const chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T');
-const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D');
+constexpr chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T');
+constexpr chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D');
extern const char *GDROM_TRACK_METADATA_FORMAT;
+// standard DVD metafata
+constexpr chd_metadata_tag DVD_METADATA_TAG = CHD_MAKE_TAG('D', 'V', 'D', ' ');
+
// standard A/V metadata
-const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V');
+constexpr chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V');
extern const char *AV_METADATA_FORMAT;
// A/V laserdisc frame metadata
-const chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D');
-
-// error types
-enum chd_error
-{
- CHDERR_NONE,
- CHDERR_NO_INTERFACE,
- CHDERR_OUT_OF_MEMORY,
- CHDERR_NOT_OPEN,
- CHDERR_ALREADY_OPEN,
- CHDERR_INVALID_FILE,
- CHDERR_INVALID_PARAMETER,
- CHDERR_INVALID_DATA,
- CHDERR_FILE_NOT_FOUND,
- CHDERR_REQUIRES_PARENT,
- CHDERR_FILE_NOT_WRITEABLE,
- CHDERR_READ_ERROR,
- CHDERR_WRITE_ERROR,
- CHDERR_CODEC_ERROR,
- CHDERR_INVALID_PARENT,
- CHDERR_HUNK_OUT_OF_RANGE,
- CHDERR_DECOMPRESSION_ERROR,
- CHDERR_COMPRESSION_ERROR,
- CHDERR_CANT_CREATE_FILE,
- CHDERR_CANT_VERIFY,
- CHDERR_NOT_SUPPORTED,
- CHDERR_METADATA_NOT_FOUND,
- CHDERR_INVALID_METADATA_SIZE,
- CHDERR_UNSUPPORTED_VERSION,
- CHDERR_VERIFY_INCOMPLETE,
- CHDERR_INVALID_METADATA,
- CHDERR_INVALID_STATE,
- CHDERR_OPERATION_PENDING,
- CHDERR_UNSUPPORTED_FORMAT,
- CHDERR_UNKNOWN_COMPRESSION,
- CHDERR_WALKING_PARENT,
- CHDERR_COMPRESSING
-};
+constexpr chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D');
@@ -280,9 +247,6 @@ enum chd_error
// TYPE DEFINITIONS
//**************************************************************************
-class chd_codec;
-
-
// ======================> chd_file
// core file class
@@ -292,151 +256,183 @@ class chd_file
friend class chd_verifier;
// constants
- static const uint32_t HEADER_VERSION = 5;
- static const uint32_t V3_HEADER_SIZE = 120;
- static const uint32_t V4_HEADER_SIZE = 108;
- static const uint32_t V5_HEADER_SIZE = 124;
- static const uint32_t MAX_HEADER_SIZE = V5_HEADER_SIZE;
+ static constexpr uint32_t HEADER_VERSION = 5;
+ static constexpr uint32_t V3_HEADER_SIZE = 120;
+ static constexpr uint32_t V4_HEADER_SIZE = 108;
+ static constexpr uint32_t V5_HEADER_SIZE = 124;
+ static constexpr uint32_t MAX_HEADER_SIZE = V5_HEADER_SIZE;
public:
+ // error types
+ enum class error
+ {
+ NO_INTERFACE = 1,
+ NOT_OPEN,
+ ALREADY_OPEN,
+ INVALID_FILE,
+ INVALID_DATA,
+ REQUIRES_PARENT,
+ FILE_NOT_WRITEABLE,
+ CODEC_ERROR,
+ INVALID_PARENT,
+ HUNK_OUT_OF_RANGE,
+ DECOMPRESSION_ERROR,
+ COMPRESSION_ERROR,
+ CANT_VERIFY,
+ METADATA_NOT_FOUND,
+ INVALID_METADATA_SIZE,
+ UNSUPPORTED_VERSION,
+ VERIFY_INCOMPLETE,
+ INVALID_METADATA,
+ INVALID_STATE,
+ OPERATION_PENDING,
+ UNSUPPORTED_FORMAT,
+ UNKNOWN_COMPRESSION,
+ WALKING_PARENT,
+ COMPRESSING
+ };
+
+ using open_parent_func = std::function<std::unique_ptr<chd_file> (util::sha1_t const &)>;
+
// construction/destruction
chd_file();
virtual ~chd_file();
- // operators
- operator util::core_file &() { return *m_file; }
-
// getters
- bool opened() const { return (m_file != nullptr); }
- uint32_t version() const { return m_version; }
- uint64_t logical_bytes() const { return m_logicalbytes; }
- uint32_t hunk_bytes() const { return m_hunkbytes; }
- uint32_t hunk_count() const { return m_hunkcount; }
- uint32_t unit_bytes() const { return m_unitbytes; }
- uint64_t unit_count() const { return m_unitcount; }
- bool compressed() const { return (m_compression[0] != CHD_CODEC_NONE); }
- chd_codec_type compression(int index) const { return m_compression[index]; }
- chd_file *parent() const { return m_parent; }
- util::sha1_t sha1();
- util::sha1_t raw_sha1();
- util::sha1_t parent_sha1();
- chd_error hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes);
+ util::random_read &file();
+ bool opened() const noexcept { return bool(m_file); }
+ uint32_t version() const noexcept { return m_version; }
+ uint64_t logical_bytes() const noexcept { return m_logicalbytes; }
+ uint32_t hunk_bytes() const noexcept { return m_hunkbytes; }
+ uint32_t hunk_count() const noexcept { return m_hunkcount; }
+ uint32_t unit_bytes() const noexcept { return m_unitbytes; }
+ uint64_t unit_count() const noexcept { return m_unitcount; }
+ bool compressed() const noexcept { return (m_compression[0] != CHD_CODEC_NONE); }
+ chd_codec_type compression(int index) const noexcept { return m_compression[index]; }
+ chd_file *parent() const noexcept { return m_parent.get(); }
+ bool parent_missing() const noexcept;
+ util::sha1_t sha1() const noexcept;
+ util::sha1_t raw_sha1() const noexcept;
+ util::sha1_t parent_sha1() const noexcept;
+ std::error_condition hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes);
// setters
- void set_raw_sha1(util::sha1_t rawdata);
- void set_parent_sha1(util::sha1_t parent);
+ std::error_condition set_raw_sha1(util::sha1_t rawdata) noexcept;
+ std::error_condition set_parent_sha1(util::sha1_t parent) noexcept;
// file create
- chd_error create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]);
- chd_error create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]);
- chd_error create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent);
- chd_error create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent);
+ std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4]);
+ std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4]);
+ std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent);
+ std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent);
// file open
- chd_error open(const char *filename, bool writeable = false, chd_file *parent = nullptr);
- chd_error open(util::core_file &file, bool writeable = false, chd_file *parent = nullptr);
+ std::error_condition open(std::string_view filename, bool writeable = false, chd_file *parent = nullptr, const open_parent_func &open_parent = nullptr);
+ std::error_condition open(util::random_read_write::ptr &&file, bool writeable = false, chd_file *parent = nullptr, const open_parent_func &open_parent = nullptr);
// file close
void close();
// read/write
- chd_error read_hunk(uint32_t hunknum, void *buffer);
- chd_error write_hunk(uint32_t hunknum, const void *buffer);
- chd_error read_units(uint64_t unitnum, void *buffer, uint32_t count = 1);
- chd_error write_units(uint64_t unitnum, const void *buffer, uint32_t count = 1);
- chd_error read_bytes(uint64_t offset, void *buffer, uint32_t bytes);
- chd_error write_bytes(uint64_t offset, const void *buffer, uint32_t bytes);
+ std::error_condition codec_process_hunk(uint32_t hunknum);
+ std::error_condition read_hunk(uint32_t hunknum, void *buffer);
+ std::error_condition write_hunk(uint32_t hunknum, const void *buffer);
+ std::error_condition read_units(uint64_t unitnum, void *buffer, uint32_t count = 1);
+ std::error_condition write_units(uint64_t unitnum, const void *buffer, uint32_t count = 1);
+ std::error_condition read_bytes(uint64_t offset, void *buffer, uint32_t bytes);
+ std::error_condition write_bytes(uint64_t offset, const void *buffer, uint32_t bytes);
// metadata management
- chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output);
- chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output);
- chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen);
- chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags);
- chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags = CHD_MDFLAGS_CHECKSUM);
- chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::string &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.c_str(), input.length() + 1, flags); }
- chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::vector<uint8_t> &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, &input[0], input.size(), flags); }
- chd_error delete_metadata(chd_metadata_tag metatag, uint32_t metaindex);
- chd_error clone_all_metadata(chd_file &source);
+ std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output);
+ std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output);
+ std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen);
+ std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags);
+ std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags = CHD_MDFLAGS_CHECKSUM);
+ std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::string &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.c_str(), input.length() + 1, flags); }
+ std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::vector<uint8_t> &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, &input[0], input.size(), flags); }
+ std::error_condition delete_metadata(chd_metadata_tag metatag, uint32_t metaindex);
+ std::error_condition clone_all_metadata(chd_file &source);
// hashing helper
util::sha1_t compute_overall_sha1(util::sha1_t rawsha1);
// codec interfaces
- chd_error codec_configure(chd_codec_type codec, int param, void *config);
+ std::error_condition codec_configure(chd_codec_type codec, int param, void *config);
- // static helpers
- static const char *error_string(chd_error err);
+ // typing
+ std::error_condition check_is_hd() const noexcept;
+ std::error_condition check_is_cd() const noexcept;
+ std::error_condition check_is_gd() const noexcept;
+ std::error_condition check_is_dvd() const noexcept;
+ std::error_condition check_is_av() const noexcept;
private:
struct metadata_entry;
struct metadata_hash;
// inline helpers
- uint64_t be_read(const uint8_t *base, int numbytes);
- void be_write(uint8_t *base, uint64_t value, int numbytes);
- util::sha1_t be_read_sha1(const uint8_t *base);
- void be_write_sha1(uint8_t *base, util::sha1_t value);
- void file_read(uint64_t offset, void *dest, uint32_t length);
- void file_write(uint64_t offset, const void *source, uint32_t length);
+ util::sha1_t be_read_sha1(const uint8_t *base) const noexcept;
+ void be_write_sha1(uint8_t *base, util::sha1_t value) noexcept;
+ std::error_condition file_read(uint64_t offset, void *dest, uint32_t length) const noexcept;
+ std::error_condition file_write(uint64_t offset, const void *source, uint32_t length) noexcept;
uint64_t file_append(const void *source, uint32_t length, uint32_t alignment = 0);
- uint8_t bits_for_value(uint64_t value);
+ static uint8_t bits_for_value(uint64_t value) noexcept;
// internal helpers
uint32_t guess_unitbytes();
void parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1);
void parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1);
void parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1);
- chd_error compress_v5_map();
+ std::error_condition compress_v5_map();
void decompress_v5_map();
- chd_error create_common();
- chd_error open_common(bool writeable);
+ std::error_condition create_common();
+ std::error_condition open_common(bool writeable, const open_parent_func &open_parent);
void create_open_common();
- void verify_proper_compression_append(uint32_t hunknum);
+ std::error_condition verify_proper_compression_append(uint32_t hunknum) const noexcept;
void hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16);
void hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk);
void hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit);
- bool metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume = false);
- void metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset);
+ std::error_condition metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume = false) const noexcept;
+ std::error_condition metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) noexcept;
void metadata_update_hash();
static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2);
// file characteristics
- util::core_file * m_file; // handle to the open core file
- bool m_owns_file; // flag indicating if this file should be closed on chd_close()
+ util::random_read_write::ptr m_file; // handle to the open core file
bool m_allow_reads; // permit reads from this CHD?
bool m_allow_writes; // permit writes to this CHD?
// core parameters from the header
- uint32_t m_version; // version of the header
- uint64_t m_logicalbytes; // logical size of the raw CHD data in bytes
- uint64_t m_mapoffset; // offset of map
- uint64_t m_metaoffset; // offset to first metadata bit
- uint32_t m_hunkbytes; // size of each raw hunk in bytes
- uint32_t m_hunkcount; // number of hunks represented
- uint32_t m_unitbytes; // size of each unit in bytes
- uint64_t m_unitcount; // number of units represented
+ uint32_t m_version; // version of the header
+ uint64_t m_logicalbytes; // logical size of the raw CHD data in bytes
+ uint64_t m_mapoffset; // offset of map
+ uint64_t m_metaoffset; // offset to first metadata bit
+ uint32_t m_hunkbytes; // size of each raw hunk in bytes
+ uint32_t m_hunkcount; // number of hunks represented
+ uint32_t m_unitbytes; // size of each unit in bytes
+ uint64_t m_unitcount; // number of units represented
chd_codec_type m_compression[4]; // array of compression types used
- chd_file * m_parent; // pointer to parent file, or nullptr if none
+ std::shared_ptr<chd_file> m_parent; // pointer to parent file, or nullptr if none
bool m_parent_missing; // are we missing our parent?
// key offsets within the header
- uint64_t m_mapoffset_offset; // offset of map offset field
- uint64_t m_metaoffset_offset;// offset of metaoffset field
- uint64_t m_sha1_offset; // offset of SHA1 field
- uint64_t m_rawsha1_offset; // offset of raw SHA1 field
- uint64_t m_parentsha1_offset;// offset of paren SHA1 field
+ uint64_t m_mapoffset_offset; // offset of map offset field
+ uint64_t m_metaoffset_offset;// offset of metaoffset field
+ uint64_t m_sha1_offset; // offset of SHA1 field
+ uint64_t m_rawsha1_offset; // offset of raw SHA1 field
+ uint64_t m_parentsha1_offset;// offset of paren SHA1 field
// map information
- uint32_t m_mapentrybytes; // length of each entry in a map
- std::vector<uint8_t> m_rawmap; // raw map data
+ uint32_t m_mapentrybytes; // length of each entry in a map
+ std::vector<uint8_t> m_rawmap; // raw map data
// compression management
- chd_decompressor * m_decompressor[4]; // array of decompression codecs
- std::vector<uint8_t> m_compressed; // temporary buffer for compressed data
+ chd_decompressor::ptr m_decompressor[4]; // array of decompression codecs
+ std::vector<uint8_t> m_compressed; // temporary buffer for compressed data
// caching
- std::vector<uint8_t> m_cache; // single-hunk cache for partial reads/writes
- uint32_t m_cachehunk; // which hunk is in the cache?
+ std::vector<uint8_t> m_cache; // single-hunk cache for partial reads/writes
+ uint32_t m_cachehunk; // which hunk is in the cache?
};
@@ -452,7 +448,7 @@ public:
// compression management
void compress_begin();
- chd_error compress_continue(double &progress, double &ratio);
+ std::error_condition compress_continue(double &progress, double &ratio);
protected:
// required override: read more data
@@ -469,28 +465,28 @@ private:
// operations
void reset();
- uint64_t find(util::crc16_t crc16, util::sha1_t sha1);
+ uint64_t find(util::crc16_t crc16, util::sha1_t sha1) const noexcept;
void add(uint64_t itemnum, util::crc16_t crc16, util::sha1_t sha1);
// constants
- static const uint64_t NOT_FOUND = ~uint64_t(0);
+ static constexpr uint64_t NOT_FOUND = ~uint64_t(0);
+
private:
// internal entry
struct entry_t
{
entry_t * m_next; // next entry in list
- uint64_t m_itemnum; // item number
+ uint64_t m_itemnum; // item number
util::sha1_t m_sha1; // SHA-1 of the block
};
// block of entries
struct entry_block
{
- entry_block(entry_block *prev)
- : m_next(prev), m_nextalloc(0) { }
+ entry_block(entry_block *prev) : m_next(prev), m_nextalloc(0) { }
entry_block * m_next; // next block in list
- uint32_t m_nextalloc; // next to be allocated
+ uint32_t m_nextalloc; // next to be allocated
entry_t m_array[16384]; // array of entries
};
@@ -554,8 +550,8 @@ private:
// current compression status
bool m_walking_parent; // are we building the parent map?
- uint64_t m_total_in; // total bytes in
- uint64_t m_total_out; // total bytes out
+ uint64_t m_total_in; // total bytes in
+ uint64_t m_total_out; // total bytes out
util::sha1_creator m_compsha1; // running SHA-1 on raw data
// hash lookup maps
@@ -564,20 +560,31 @@ private:
// read I/O thread
osd_work_queue * m_read_queue; // work queue for reading
- uint64_t m_read_queue_offset;// next offset to enqueue
- uint64_t m_read_done_offset; // next offset that will complete
- bool m_read_error; // error during reading?
+ uint64_t m_read_queue_offset;// next offset to enqueue
+ uint64_t m_read_done_offset; // next offset that will complete
+ std::error_condition m_read_error; // error during reading, if any
// work item thread
- static const int WORK_BUFFER_HUNKS = 256;
+ static constexpr int WORK_BUFFER_HUNKS = 256;
osd_work_queue * m_work_queue; // queue for doing work on other threads
- std::vector<uint8_t> m_work_buffer; // buffer containing hunk data to work on
- std::vector<uint8_t> m_compressed_buffer;// buffer containing compressed data
+ std::vector<uint8_t> m_work_buffer; // buffer containing hunk data to work on
+ std::vector<uint8_t> m_compressed_buffer;// buffer containing compressed data
work_item m_work_item[WORK_BUFFER_HUNKS]; // status of each hunk
chd_compressor_group * m_codecs[WORK_MAX_THREADS]; // codecs to use
// output state
- uint32_t m_write_hunk; // next hunk to write
+ uint32_t m_write_hunk; // next hunk to write
};
-#endif // MAME_UTIL_CHD_H
+
+// error category for CHD errors
+std::error_category const &chd_category() noexcept;
+inline std::error_condition make_error_condition(chd_file::error err) noexcept { return std::error_condition(int(err), chd_category()); }
+
+namespace std {
+
+template <> struct is_error_condition_enum<chd_file::error> : public std::true_type { };
+
+} // namespace std
+
+#endif // MAME_LIB_UTIL_CHD_H
diff --git a/src/lib/util/chdcd.cpp b/src/lib/util/chdcd.cpp
deleted file mode 100644
index 85bd7cdad27..00000000000
--- a/src/lib/util/chdcd.cpp
+++ /dev/null
@@ -1,1333 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:R. Belmont
-/***************************************************************************
-
- TOC parser for CHD compression frontend
- Handles CDRDAO .toc, CDRWIN .cue, Nero .nrg, and Sega GDROM .gdi
-
-***************************************************************************/
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <assert.h>
-#include "osdcore.h"
-#include "chd.h"
-#include "chdcd.h"
-#include "corefile.h"
-
-
-
-/***************************************************************************
- CONSTANTS & DEFINES
-***************************************************************************/
-
-/**
- * @def TOKENIZE();
- *
- * @brief A macro that defines tokenize.
- *
- * @param linebuffer The linebuffer.
- * @param i Zero-based index of the.
- * @param sizeof(linebuffer) The sizeof(linebuffer)
- * @param token The token.
- * @param sizeof(token) The sizeof(token)
- */
-
-#define TOKENIZE i = tokenize( linebuffer, i, sizeof(linebuffer), token, sizeof(token) );
-
-
-
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
-
-/** @brief The linebuffer[ 512]. */
-static char linebuffer[512];
-
-
-
-/***************************************************************************
- IMPLEMENTATION
-***************************************************************************/
-
-/**
- * @fn static std::string get_file_path(std::string &path)
- *
- * @brief Gets file path.
- *
- * @param [in,out] path Full pathname of the file.
- *
- * @return The file path.
- */
-
-static std::string get_file_path(std::string &path)
-{
- int pos = path.find_last_of('\\');
- if (pos!=-1) {
- path = path.substr(0,pos+1);
- } else {
- pos = path.find_last_of('/');
- path = path.substr(0,pos+1);
- }
- return path;
-}
-/*-------------------------------------------------
- get_file_size - get the size of a file
--------------------------------------------------*/
-
-/**
- * @fn static uint64_t get_file_size(const char *filename)
- *
- * @brief Gets file size.
- *
- * @param filename Filename of the file.
- *
- * @return The file size.
- */
-
-static uint64_t get_file_size(const char *filename)
-{
- osd_file::ptr file;
- std::uint64_t filesize = 0;
-
- osd_file::open(filename, OPEN_FLAG_READ, file, filesize);
-
- return filesize;
-}
-
-
-/*-------------------------------------------------
- tokenize - get a token from the line buffer
--------------------------------------------------*/
-
-/**
- * @fn static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
- *
- * @brief Tokenizes.
- *
- * @param linebuffer The linebuffer.
- * @param i Zero-based index of the.
- * @param linebuffersize The linebuffersize.
- * @param [in,out] token If non-null, the token.
- * @param tokensize The tokensize.
- *
- * @return An int.
- */
-
-static int tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
-{
- int j = 0;
- int singlequote = 0;
- int doublequote = 0;
-
- while ((i < linebuffersize) && isspace((uint8_t)linebuffer[i]))
- {
- i++;
- }
-
- while ((i < linebuffersize) && (j < tokensize))
- {
- if (!singlequote && linebuffer[i] == '"' )
- {
- doublequote = !doublequote;
- }
- else if (!doublequote && linebuffer[i] == '\'')
- {
- singlequote = !singlequote;
- }
- else if (!singlequote && !doublequote && isspace((uint8_t)linebuffer[i]))
- {
- break;
- }
- else
- {
- token[j] = linebuffer[i];
- j++;
- }
-
- i++;
- }
-
- token[j] = '\0';
-
- return i;
-}
-
-
-/*-------------------------------------------------
- msf_to_frames - convert m:s:f into a number of frames
--------------------------------------------------*/
-
-/**
- * @fn static int msf_to_frames( char *token )
- *
- * @brief Msf to frames.
- *
- * @param [in,out] token If non-null, the token.
- *
- * @return An int.
- */
-
-static int msf_to_frames( char *token )
-{
- int m = 0;
- int s = 0;
- int f = 0;
-
- if( sscanf( token, "%d:%d:%d", &m, &s, &f ) == 1 )
- {
- f = m;
- }
- else
- {
- /* convert to just frames */
- s += (m * 60);
- f += (s * 75);
- }
-
- return f;
-}
-
-/*-------------------------------------------------
- parse_wav_sample - takes a .WAV file, verifies
- that the file is 16 bits, and returns the
- length in bytes of the data and the offset in
- bytes to where the data starts in the file.
--------------------------------------------------*/
-
-/**
- * @fn static uint32_t parse_wav_sample(const char *filename, uint32_t *dataoffs)
- *
- * @brief Parse WAV sample.
- *
- * @param filename Filename of the file.
- * @param [in,out] dataoffs If non-null, the dataoffs.
- *
- * @return An uint32_t.
- */
-
-static uint32_t parse_wav_sample(const char *filename, uint32_t *dataoffs)
-{
- unsigned long offset = 0;
- uint32_t length, rate, filesize;
- uint16_t bits, temp16;
- char buf[32];
- osd_file::ptr file;
- uint64_t fsize = 0;
- std::uint32_t actual;
-
- osd_file::error filerr = osd_file::open(filename, OPEN_FLAG_READ, file, fsize);
- if (filerr != osd_file::error::NONE)
- {
- printf("ERROR: could not open (%s)\n", filename);
- return 0;
- }
-
- /* read the core header and make sure it's a WAVE file */
- file->read(buf, 0, 4, actual);
- offset += actual;
- if (offset < 4)
- {
- printf("ERROR: unexpected RIFF offset %lu (%s)\n", offset, filename);
- return 0;
- }
- if (memcmp(&buf[0], "RIFF", 4) != 0)
- {
- printf("ERROR: could not find RIFF header (%s)\n", filename);
- return 0;
- }
-
- /* get the total size */
- file->read(&filesize, offset, 4, actual);
- offset += actual;
- if (offset < 8)
- {
- printf("ERROR: unexpected size offset %lu (%s)\n", offset, filename);
- return 0;
- }
- filesize = little_endianize_int32(filesize);
-
- /* read the RIFF file type and make sure it's a WAVE file */
- file->read(buf, offset, 4, actual);
- offset += actual;
- if (offset < 12)
- {
- printf("ERROR: unexpected WAVE offset %lu (%s)\n", offset, filename);
- return 0;
- }
- if (memcmp(&buf[0], "WAVE", 4) != 0)
- {
- printf("ERROR: could not find WAVE header (%s)\n", filename);
- return 0;
- }
-
- /* seek until we find a format tag */
- while (1)
- {
- file->read(buf, offset, 4, actual);
- offset += actual;
- file->read(&length, offset, 4, actual);
- offset += actual;
- length = little_endianize_int32(length);
- if (memcmp(&buf[0], "fmt ", 4) == 0)
- break;
-
- /* seek to the next block */
- offset += length;
- if (offset >= filesize)
- {
- printf("ERROR: could not find fmt tag (%s)\n", filename);
- return 0;
- }
- }
-
- /* read the format -- make sure it is PCM */
- file->read(&temp16, offset, 2, actual);
- offset += actual;
- temp16 = little_endianize_int16(temp16);
- if (temp16 != 1)
- {
- printf("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, filename);
- return 0;
- }
-
- /* number of channels -- only stereo is supported */
- file->read(&temp16, offset, 2, actual);
- offset += actual;
- temp16 = little_endianize_int16(temp16);
- if (temp16 != 2)
- {
- printf("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, filename);
- return 0;
- }
-
- /* sample rate */
- file->read(&rate, offset, 4, actual);
- offset += actual;
- rate = little_endianize_int32(rate);
- if (rate != 44100)
- {
- printf("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, filename);
- return 0;
- }
-
- /* bytes/second and block alignment are ignored */
- file->read(buf, offset, 6, actual);
- offset += actual;
-
- /* bits/sample */
- file->read(&bits, offset, 2, actual);
- offset += actual;
- bits = little_endianize_int16(bits);
- if (bits != 16)
- {
- printf("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, filename);
- return 0;
- }
-
- /* seek past any extra data */
- offset += length - 16;
-
- /* seek until we find a data tag */
- while (1)
- {
- file->read(buf, offset, 4, actual);
- offset += actual;
- file->read(&length, offset, 4, actual);
- offset += actual;
- length = little_endianize_int32(length);
- if (memcmp(&buf[0], "data", 4) == 0)
- break;
-
- /* seek to the next block */
- offset += length;
- if (offset >= filesize)
- {
- printf("ERROR: could not find data tag (%s)\n", filename);
- return 0;
- }
- }
-
- /* if there was a 0 length data block, we're done */
- if (length == 0)
- {
- printf("ERROR: empty data block (%s)\n", filename);
- return 0;
- }
-
- *dataoffs = offset;
-
- return length;
-}
-
-/**
- * @fn uint16_t read_uint16(FILE *infile)
- *
- * @brief Reads uint 16.
- *
- * @param [in,out] infile If non-null, the infile.
- *
- * @return The uint 16.
- */
-
-uint16_t read_uint16(FILE *infile)
-{
- uint16_t res = 0;
- unsigned char buffer[2];
-
- fread(buffer, 2, 1, infile);
-
- res = buffer[1] | buffer[0]<<8;
-
- return res;
-}
-
-/**
- * @fn uint32_t read_uint32(FILE *infile)
- *
- * @brief Reads uint 32.
- *
- * @param [in,out] infile If non-null, the infile.
- *
- * @return The uint 32.
- */
-
-uint32_t read_uint32(FILE *infile)
-{
- uint32_t res = 0;
- unsigned char buffer[4];
-
- fread(buffer, 4, 1, infile);
-
- res = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
-
- return res;
-}
-
-/**
- * @fn uint64_t read_uint64(FILE *infile)
- *
- * @brief Reads uint 64.
- *
- * @param [in,out] infile If non-null, the infile.
- *
- * @return The uint 64.
- */
-
-uint64_t read_uint64(FILE *infile)
-{
- uint64_t res0(0), res1(0);
- uint64_t res;
- unsigned char buffer[8];
-
- fread(buffer, 8, 1, infile);
-
- res0 = buffer[3] | buffer[2]<<8 | buffer[1]<<16 | buffer[0]<<24;
- res1 = buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24;
-
- res = res0<<32 | res1;
-
- return res;
-}
-
-/*-------------------------------------------------
- chdcd_parse_nero - parse a Nero .NRG file
--------------------------------------------------*/
-
-/**
- * @fn chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
- *
- * @brief Chdcd parse nero.
- *
- * @param tocfname The tocfname.
- * @param [in,out] outtoc The outtoc.
- * @param [in,out] outinfo The outinfo.
- *
- * @return A chd_error.
- */
-
-chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
-{
- FILE *infile;
- unsigned char buffer[12];
- uint32_t chain_offs, chunk_size;
- int done = 0;
-
- std::string path = std::string(tocfname);
-
- infile = fopen(tocfname, "rb");
- path = get_file_path(path);
-
- if (infile == (FILE *)nullptr)
- {
- return CHDERR_FILE_NOT_FOUND;
- }
-
- /* clear structures */
- memset(&outtoc, 0, sizeof(outtoc));
- outinfo.reset();
-
- // seek to 12 bytes before the end
- fseek(infile, -12, SEEK_END);
- fread(buffer, 12, 1, infile);
-
- if (memcmp(buffer, "NER5", 4))
- {
- printf("ERROR: Not a Nero 5.5 or later image!\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_VERSION;
- }
-
- chain_offs = buffer[11] | (buffer[10]<<8) | (buffer[9]<<16) | (buffer[8]<<24);
-
- if ((buffer[7] != 0) || (buffer[6] != 0) || (buffer[5] != 0) || (buffer[4] != 0))
- {
- printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
- }
-
-// printf("NER5 detected, chain offset: %x\n", chain_offs);
-
- while (!done)
- {
- uint32_t offset;
- uint8_t start, end;
- int track;
-
- fseek(infile, chain_offs, SEEK_SET);
- fread(buffer, 8, 1, infile);
-
- chunk_size = (buffer[7] | buffer[6]<<8 | buffer[5]<<16 | buffer[4]<<24);
-
-// printf("Chunk type: %c%c%c%c, size %x\n", buffer[0], buffer[1], buffer[2], buffer[3], chunk_size);
-
- // we want the DAOX chunk, which has the TOC information
- if (!memcmp(buffer, "DAOX", 4))
- {
- // skip second chunk size and UPC code
- fseek(infile, 20, SEEK_CUR);
-
- fread(&start, 1, 1, infile);
- fread(&end, 1, 1, infile);
-
-// printf("Start track %d End track: %d\n", start, end);
-
- outtoc.numtrks = (end-start) + 1;
-
- offset = 0;
- for (track = start; track <= end; track++)
- {
- uint32_t size, mode;
- uint64_t index0, index1, track_end;
-
- fseek(infile, 12, SEEK_CUR); // skip ISRC code
- size = read_uint16(infile);
- mode = read_uint16(infile);
- fseek(infile, 2, SEEK_CUR);
- index0 = read_uint64(infile);
- index1 = read_uint64(infile);
- track_end = read_uint64(infile);
-
-// printf("Track %d: sector size %d mode %x index0 %llx index1 %llx track_end %llx (pregap %d sectors, length %d sectors)\n", track, size, mode, index0, index1, track_end, (uint32_t)(index1-index0)/size, (uint32_t)(track_end-index1)/size);
- outinfo.track[track-1].fname.assign(tocfname);
- outinfo.track[track-1].offset = offset + (uint32_t)(index1-index0);
- outinfo.track[track-1].idx0offs = 0;
- outinfo.track[track-1].idx1offs = 0;
-
- switch (mode)
- {
- case 0x0000: // 2048 byte data
- outtoc.tracks[track-1].trktype = CD_TRACK_MODE1;
- outinfo.track[track-1].swap = false;
- break;
-
- case 0x0300: // Mode 2 Form 1
- printf("ERROR: Mode 2 Form 1 tracks not supported\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
-
- case 0x0500: // raw data
- printf("ERROR: Raw data tracks not supported\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
-
- case 0x0600: // 2352 byte mode 2 raw
- outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW;
- outinfo.track[track-1].swap = false;
- break;
-
- case 0x0700: // 2352 byte audio
- outtoc.tracks[track-1].trktype = CD_TRACK_AUDIO;
- outinfo.track[track-1].swap = true;
- break;
-
- case 0x0f00: // raw data with sub-channel
- printf("ERROR: Raw data tracks with sub-channel not supported\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
-
- case 0x1000: // audio with sub-channel
- printf("ERROR: Audio tracks with sub-channel not supported\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
-
- case 0x1100: // raw Mode 2 Form 1 with sub-channel
- printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n");
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
-
- default:
- printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
- fclose(infile);
- return CHDERR_UNSUPPORTED_FORMAT;
- }
-
- outtoc.tracks[track-1].datasize = size;
-
- outtoc.tracks[track-1].subtype = CD_SUB_NONE;
- outtoc.tracks[track-1].subsize = 0;
-
- outtoc.tracks[track-1].pregap = (uint32_t)(index1-index0)/size;
- outtoc.tracks[track-1].frames = (uint32_t)(track_end-index1)/size;
- outtoc.tracks[track-1].postgap = 0;
- outtoc.tracks[track-1].pgtype = 0;
- outtoc.tracks[track-1].pgsub = CD_SUB_NONE;
- outtoc.tracks[track-1].pgdatasize = 0;
- outtoc.tracks[track-1].pgsubsize = 0;
- outtoc.tracks[track-1].padframes = 0;
-
- offset += (uint32_t)track_end-index1;
- }
- }
-
- if (!memcmp(buffer, "END!", 4))
- {
- done = 1;
- }
- else
- {
- chain_offs += chunk_size + 8;
- }
- }
-
- fclose(infile);
-
- return CHDERR_NONE;
-}
-
-/*-------------------------------------------------
- chdcd_parse_iso - parse a .ISO file
--------------------------------------------------*/
-
-/**
- * @fn chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
- *
- * @brief Chdcd parse ISO.
- *
- * @param tocfname The tocfname.
- * @param [in,out] outtoc The outtoc.
- * @param [in,out] outinfo The outinfo.
- *
- * @return A chd_error.
- */
-
-chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
-{
- FILE *infile;
- std::string path = std::string(tocfname);
-
- infile = fopen(tocfname, "rb");
- path = get_file_path(path);
-
- if (infile == (FILE *)nullptr)
- {
- return CHDERR_FILE_NOT_FOUND;
- }
-
- /* clear structures */
- memset(&outtoc, 0, sizeof(outtoc));
- outinfo.reset();
-
- uint64_t size = get_file_size(tocfname);
- fclose(infile);
-
-
- outtoc.numtrks = 1;
-
- outinfo.track[0].fname = tocfname;
- outinfo.track[0].offset = 0;
- outinfo.track[0].idx0offs = 0;
- outinfo.track[0].idx1offs = 0;
-
- if ((size % 2048)==0 ) {
- outtoc.tracks[0].trktype = CD_TRACK_MODE1;
- outtoc.tracks[0].frames = size / 2048;
- outtoc.tracks[0].datasize = 2048;
- outinfo.track[0].swap = false;
- } else if ((size % 2336)==0 ) {
- // 2352 byte mode 2
- outtoc.tracks[0].trktype = CD_TRACK_MODE2;
- outtoc.tracks[0].frames = size / 2336;
- outtoc.tracks[0].datasize = 2336;
- outinfo.track[0].swap = false;
- } else if ((size % 2352)==0 ) {
- // 2352 byte mode 2 raw
- outtoc.tracks[0].trktype = CD_TRACK_MODE2_RAW;
- outtoc.tracks[0].frames = size / 2352;
- outtoc.tracks[0].datasize = 2352;
- outinfo.track[0].swap = false;
- } else {
- printf("ERROR: Unrecognized track type\n");
- return CHDERR_UNSUPPORTED_FORMAT;
- }
-
- outtoc.tracks[0].subtype = CD_SUB_NONE;
- outtoc.tracks[0].subsize = 0;
-
- outtoc.tracks[0].pregap = 0;
-
- outtoc.tracks[0].postgap = 0;
- outtoc.tracks[0].pgtype = 0;
- outtoc.tracks[0].pgsub = CD_SUB_NONE;
- outtoc.tracks[0].pgdatasize = 0;
- outtoc.tracks[0].pgsubsize = 0;
- outtoc.tracks[0].padframes = 0;
-
-
- return CHDERR_NONE;
-}
-
-/*-------------------------------------------------
- chdcd_parse_gdi - parse a Sega GD-ROM rip
--------------------------------------------------*/
-
-/**
- * @fn static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
- *
- * @brief Chdcd parse GDI.
- *
- * @param tocfname The tocfname.
- * @param [in,out] outtoc The outtoc.
- * @param [in,out] outinfo The outinfo.
- *
- * @return A chd_error.
- */
-
-static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
-{
- FILE *infile;
- int i, numtracks;
-
- std::string path = std::string(tocfname);
-
- infile = fopen(tocfname, "rt");
- path = get_file_path(path);
-
- if (infile == (FILE *)nullptr)
- {
- return CHDERR_FILE_NOT_FOUND;
- }
-
- /* clear structures */
- memset(&outtoc, 0, sizeof(outtoc));
- outinfo.reset();
-
- outtoc.flags = CD_FLAG_GDROM;
-
- fgets(linebuffer,511,infile);
- numtracks=atoi(linebuffer);
-
- for(i=0;i<numtracks;++i)
- {
- char *tok;
- int trknum;
- int trksize,trktype;
- int sz;
-
- fgets(linebuffer,511,infile);
-
- tok=strtok(linebuffer," ");
-
- trknum=atoi(tok)-1;
-
- outinfo.track[trknum].swap=false;
- outinfo.track[trknum].offset=0;
-
- outtoc.tracks[trknum].datasize = 0;
- outtoc.tracks[trknum].subtype = CD_SUB_NONE;
- outtoc.tracks[trknum].subsize = 0;
- outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
-
- tok=strtok(nullptr," ");
- outtoc.tracks[trknum].physframeofs=atoi(tok);
-
- tok=strtok(nullptr," ");
- trktype=atoi(tok);
-
- tok=strtok(nullptr," ");
- trksize=atoi(tok);
-
- if(trktype==4 && trksize==2352)
- {
- outtoc.tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
- outtoc.tracks[trknum].datasize=2352;
- }
- if(trktype==4 && trksize==2048)
- {
- outtoc.tracks[trknum].trktype=CD_TRACK_MODE1;
- outtoc.tracks[trknum].datasize=2048;
- }
- if(trktype==0)
- {
- outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO;
- outtoc.tracks[trknum].datasize=2352;
- outinfo.track[trknum].swap = true;
- }
-
- std::string name;
-
- tok=strtok(nullptr," ");
- name = tok;
- if (tok[0]=='"') {
- do {
- tok=strtok(nullptr," ");
- if (tok!=nullptr) {
- name += " ";
- name += tok;
- }
- } while(tok!=nullptr && (strrchr(tok,'"')-tok !=(strlen(tok)-1)));
- strdelchr(name,'"');
- }
- outinfo.track[trknum].fname.assign(path).append(name);
-
- sz = get_file_size(outinfo.track[trknum].fname.c_str());
-
- outtoc.tracks[trknum].frames = sz/trksize;
- outtoc.tracks[trknum].padframes = 0;
-
- if (trknum != 0)
- {
- int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
- outtoc.tracks[trknum-1].frames += dif;
- outtoc.tracks[trknum-1].padframes = dif;
- }
- }
-
- #if 0
- for(i=0; i < numtracks; i++)
- {
- printf("%s %d %d %d (true %d)\n", outinfo.track[i].fname.c_str(), outtoc.tracks[i].frames, outtoc.tracks[i].padframes, outtoc.tracks[i].physframeofs, outtoc.tracks[i].frames - outtoc.tracks[i].padframes);
- }
- #endif
-
- /* close the input TOC */
- fclose(infile);
-
- /* store the number of tracks found */
- outtoc.numtrks = numtracks;
-
- return CHDERR_NONE;
-}
-
-/*-------------------------------------------------
- chdcd_parse_cue - parse a CDRWin format CUE file
--------------------------------------------------*/
-
-/**
- * @fn chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
- *
- * @brief Chdcd parse cue.
- *
- * @param tocfname The tocfname.
- * @param [in,out] outtoc The outtoc.
- * @param [in,out] outinfo The outinfo.
- *
- * @return A chd_error.
- */
-
-chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
-{
- FILE *infile;
- int i, trknum;
- static char token[512];
- std::string lastfname;
- uint32_t wavlen, wavoffs;
- std::string path = std::string(tocfname);
-
- infile = fopen(tocfname, "rt");
- path = get_file_path(path);
- if (infile == (FILE *)nullptr)
- {
- return CHDERR_FILE_NOT_FOUND;
- }
-
- /* clear structures */
- memset(&outtoc, 0, sizeof(outtoc));
- outinfo.reset();
-
- trknum = -1;
- wavoffs = wavlen = 0;
-
- while (!feof(infile))
- {
- /* get the next line */
- fgets(linebuffer, 511, infile);
-
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
- {
- i = 0;
-
- TOKENIZE
-
- if (!strcmp(token, "FILE"))
- {
- /* found the data file for a track */
- TOKENIZE
-
- /* keep the filename */
- lastfname.assign(path).append(token);
-
- /* get the file type */
- TOKENIZE
-
- if (!strcmp(token, "BINARY"))
- {
- outinfo.track[trknum+1].swap = false;
- }
- else if (!strcmp(token, "MOTOROLA"))
- {
- outinfo.track[trknum+1].swap = true;
- }
- else if (!strcmp(token, "WAVE"))
- {
- wavlen = parse_wav_sample(lastfname.c_str(), &wavoffs);
- if (!wavlen)
- {
- fclose(infile);
- printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str());
- return CHDERR_INVALID_DATA;
- }
- }
- else
- {
- fclose(infile);
- printf("ERROR: Unhandled track type %s\n", token);
- return CHDERR_UNSUPPORTED_FORMAT;
- }
- }
- else if (!strcmp(token, "TRACK"))
- {
- /* get the track number */
- TOKENIZE
- trknum = strtoul(token, nullptr, 10) - 1;
-
- /* next token on the line is the track type */
- TOKENIZE
-
- if (wavlen != 0)
- {
- outtoc.tracks[trknum].trktype = CD_TRACK_AUDIO;
- outtoc.tracks[trknum].frames = wavlen/2352;
- outinfo.track[trknum].offset = wavoffs;
- wavoffs = wavlen = 0;
- }
- else
- {
- outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
- outtoc.tracks[trknum].datasize = 0;
- outinfo.track[trknum].offset = 0;
- }
- outtoc.tracks[trknum].subtype = CD_SUB_NONE;
- outtoc.tracks[trknum].subsize = 0;
- outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
- outtoc.tracks[trknum].pregap = 0;
- outtoc.tracks[trknum].padframes = 0;
- outinfo.track[trknum].idx0offs = -1;
- outinfo.track[trknum].idx1offs = 0;
-
- outinfo.track[trknum].fname.assign(lastfname); // default filename to the last one
-
-// printf("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset);
-
- cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
- if (outtoc.tracks[trknum].datasize == 0)
- {
- fclose(infile);
- printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
- return CHDERR_UNSUPPORTED_FORMAT;
- }
-
- /* next (optional) token on the line is the subcode type */
- TOKENIZE
-
- cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
- }
- else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
- {
- int idx, frames;
-
- /* get index number */
- TOKENIZE
- idx = strtoul(token, nullptr, 10);
-
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
-
- if (idx == 0)
- {
- outinfo.track[trknum].idx0offs = frames;
- }
- else if (idx == 1)
- {
- outinfo.track[trknum].idx1offs = frames;
- if ((outtoc.tracks[trknum].pregap == 0) && (outinfo.track[trknum].idx0offs != -1))
- {
- outtoc.tracks[trknum].pregap = frames - outinfo.track[trknum].idx0offs;
- outtoc.tracks[trknum].pgtype = outtoc.tracks[trknum].trktype;
- switch (outtoc.tracks[trknum].pgtype)
- {
- case CD_TRACK_MODE1:
- case CD_TRACK_MODE2_FORM1:
- outtoc.tracks[trknum].pgdatasize = 2048;
- break;
-
- case CD_TRACK_MODE1_RAW:
- case CD_TRACK_MODE2_RAW:
- case CD_TRACK_AUDIO:
- outtoc.tracks[trknum].pgdatasize = 2352;
- break;
-
- case CD_TRACK_MODE2:
- case CD_TRACK_MODE2_FORM_MIX:
- outtoc.tracks[trknum].pgdatasize = 2336;
- break;
-
- case CD_TRACK_MODE2_FORM2:
- outtoc.tracks[trknum].pgdatasize = 2324;
- break;
- }
- }
- else // pregap sectors not in file, but we're always using idx0ofs for track length calc now
- {
- outinfo.track[trknum].idx0offs = frames;
- }
- }
- }
- else if (!strcmp(token, "PREGAP"))
- {
- int frames;
-
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
-
- outtoc.tracks[trknum].pregap = frames;
- }
- else if (!strcmp(token, "POSTGAP"))
- {
- int frames;
-
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
-
- outtoc.tracks[trknum].postgap = frames;
- }
- }
- }
-
- /* close the input CUE */
- fclose(infile);
-
- /* store the number of tracks found */
- outtoc.numtrks = trknum + 1;
-
- /* now go over the files again and set the lengths */
- for (trknum = 0; trknum < outtoc.numtrks; trknum++)
- {
- uint64_t tlen = 0;
-
- // this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
- if (outtoc.tracks[trknum].trktype == CD_TRACK_AUDIO)
- {
- outinfo.track[trknum].swap = true;
- }
-
- // don't do this for .WAV tracks, we already have their length and offset filled out
- if (outinfo.track[trknum].offset == 0)
- {
- // is this the last track?
- if (trknum == (outtoc.numtrks-1))
- {
- /* if we have the same filename as the last track, do it that way */
- if (trknum != 0 && (outinfo.track[trknum].fname.compare(outinfo.track[trknum-1].fname)==0))
- {
- tlen = get_file_size(outinfo.track[trknum].fname.c_str());
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return CHDERR_FILE_NOT_FOUND;
- }
- outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
- outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- }
- else /* data files are different */
- {
- tlen = get_file_size(outinfo.track[trknum].fname.c_str());
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return CHDERR_FILE_NOT_FOUND;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
- }
- }
- else
- {
- /* if we have the same filename as the next track, do it that way */
- if (outinfo.track[trknum].fname.compare(outinfo.track[trknum+1].fname)==0)
- {
- outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx0offs - outinfo.track[trknum].idx0offs;
-
- if (trknum == 0) // track 0 offset is 0
- {
- outinfo.track[trknum].offset = 0;
- }
- else
- {
- outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize);
- }
-
- if (!outtoc.tracks[trknum].frames)
- {
- printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
- return CHDERR_INVALID_DATA;
- }
- }
- else /* data files are different */
- {
- tlen = get_file_size(outinfo.track[trknum].fname.c_str());
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str());
- return CHDERR_FILE_NOT_FOUND;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
- }
- }
- }
- //printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset);
- }
-
- return CHDERR_NONE;
-}
-
-/*-------------------------------------------------
- chdcd_parse_toc - parse a CDRDAO format TOC file
--------------------------------------------------*/
-
-/**
- * @fn chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
- *
- * @brief Chdcd parse TOC.
- *
- * @param tocfname The tocfname.
- * @param [in,out] outtoc The outtoc.
- * @param [in,out] outinfo The outinfo.
- *
- * @return A chd_error.
- */
-
-chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo)
-{
- FILE *infile;
- int i, trknum;
- static char token[512];
- char tocftemp[512];
-
- strcpy(tocftemp, tocfname);
- for (i = 0; i < strlen(tocfname); i++)
- {
- tocftemp[i] = tolower(tocftemp[i]);
- }
-
- if (strstr(tocftemp,".gdi"))
- {
- return chdcd_parse_gdi(tocfname, outtoc, outinfo);
- }
-
- if (strstr(tocftemp,".cue"))
- {
- return chdcd_parse_cue(tocfname, outtoc, outinfo);
- }
-
- if (strstr(tocftemp,".nrg"))
- {
- return chdcd_parse_nero(tocfname, outtoc, outinfo);
- }
-
- if (strstr(tocftemp,".iso") || strstr(tocftemp,".cdr") || strstr(tocftemp,".toast"))
- {
- return chdcd_parse_iso(tocfname, outtoc, outinfo);
- }
-
- std::string path = std::string(tocfname);
-
- infile = fopen(tocfname, "rt");
- path = get_file_path(path);
-
- if (infile == (FILE *)nullptr)
- {
- return CHDERR_FILE_NOT_FOUND;
- }
-
- /* clear structures */
- memset(&outtoc, 0, sizeof(outtoc));
- outinfo.reset();
-
- trknum = -1;
-
- while (!feof(infile))
- {
- /* get the next line */
- fgets(linebuffer, 511, infile);
-
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
- {
- i = 0;
-
- TOKENIZE
-
- if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
- {
- int f;
-
- /* found the data file for a track */
- TOKENIZE
-
- /* keep the filename */
- outinfo.track[trknum].fname.assign(path).append(token);
-
- /* get either the offset or the length */
- TOKENIZE
-
- if (!strcmp(token, "SWAP"))
- {
- TOKENIZE
-
- outinfo.track[trknum].swap = true;
- }
- else
- {
- outinfo.track[trknum].swap = false;
- }
-
- if (token[0] == '#')
- {
- /* it's a decimal offset, use it */
- f = strtoul(&token[1], nullptr, 10);
- }
- else if (isdigit((uint8_t)token[0]))
- {
- /* convert the time to an offset */
- f = msf_to_frames( token );
-
- f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- }
- else
- {
- f = 0;
- }
-
- outinfo.track[trknum].offset = f;
-
- TOKENIZE
-
- if (isdigit((uint8_t)token[0]))
- {
- // this could be the length or an offset from the previous field.
- f = msf_to_frames( token );
-
- TOKENIZE
-
- if (isdigit((uint8_t)token[0]))
- {
- // it was an offset.
- f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
-
- outinfo.track[trknum].offset += f;
-
- // this is the length.
- f = msf_to_frames( token );
- }
- }
- else if( trknum == 0 && outinfo.track[trknum].offset != 0 )
- {
- /* the 1st track might have a length with no offset */
- f = outinfo.track[trknum].offset / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outinfo.track[trknum].offset = 0;
- }
- else
- {
- /* guesstimate the track length? */
- f = 0;
- }
-
- outtoc.tracks[trknum].frames = f;
- }
- else if (!strcmp(token, "TRACK"))
- {
- trknum++;
-
- /* next token on the line is the track type */
- TOKENIZE
-
- outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
- outtoc.tracks[trknum].datasize = 0;
- outtoc.tracks[trknum].subtype = CD_SUB_NONE;
- outtoc.tracks[trknum].subsize = 0;
- outtoc.tracks[trknum].pgsub = CD_SUB_NONE;
- outtoc.tracks[trknum].padframes = 0;
-
- cdrom_convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
- if (outtoc.tracks[trknum].datasize == 0)
- {
- fclose(infile);
- printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
- return CHDERR_UNSUPPORTED_FORMAT;
- }
-
- /* next (optional) token on the line is the subcode type */
- TOKENIZE
-
- cdrom_convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
- }
- else if (!strcmp(token, "START"))
- {
- int frames;
-
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
-
- outtoc.tracks[trknum].pregap = frames;
- }
- }
- }
-
- /* close the input TOC */
- fclose(infile);
-
- /* store the number of tracks found */
- outtoc.numtrks = trknum + 1;
-
- return CHDERR_NONE;
-}
diff --git a/src/lib/util/chdcd.h b/src/lib/util/chdcd.h
deleted file mode 100644
index 65e4b943964..00000000000
--- a/src/lib/util/chdcd.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:R. Belmont
-/***************************************************************************
-
- CDRDAO TOC parser for CHD compression frontend
-
-***************************************************************************/
-
-#ifndef MAME_UTIL_CHDCD_H
-#define MAME_UTIL_CHDCD_H
-
-#pragma once
-
-#include "cdrom.h"
-
-struct chdcd_track_input_entry
-{
- chdcd_track_input_entry() { reset(); }
- void reset() { fname.clear(); offset = idx0offs = idx1offs = 0; swap = false; }
-
- std::string fname; // filename for each track
- uint32_t offset; // offset in the data file for each track
- bool swap; // data needs to be byte swapped
- uint32_t idx0offs;
- uint32_t idx1offs;
-};
-
-struct chdcd_track_input_info
-{
- void reset() { for (auto & elem : track) elem.reset(); }
-
- chdcd_track_input_entry track[CD_MAX_TRACKS];
-};
-
-
-chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo);
-
-#endif // MAME_UTIL_CHDCD_H
diff --git a/src/lib/util/chdcodec.cpp b/src/lib/util/chdcodec.cpp
index 71763163a87..c8a1f573bd0 100644
--- a/src/lib/util/chdcodec.cpp
+++ b/src/lib/util/chdcodec.cpp
@@ -2,30 +2,35 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chdcodec.c
-
Codecs used by the CHD format
***************************************************************************/
-#include <assert.h>
+#include "chdcodec.h"
-#include "chd.h"
-#include "hashing.h"
#include "avhuff.h"
-#include "flac.h"
#include "cdrom.h"
-#include <zlib.h>
-#include "lzma/C/LzmaEnc.h"
+#include "chd.h"
+#include "flac.h"
+#include "hashing.h"
+#include "multibyte.h"
+
#include "lzma/C/LzmaDec.h"
+#include "lzma/C/LzmaEnc.h"
+
+#include <zlib.h>
+#include <zstd.h>
+
#include <new>
+namespace {
+
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
-static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };
+constexpr uint8_t f_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };
@@ -35,7 +40,7 @@ static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff
// ======================> chd_zlib_allocator
-// allocation helper clas for zlib
+// allocation helper class for zlib
class chd_zlib_allocator
{
public:
@@ -51,7 +56,8 @@ private:
static voidpf fast_alloc(voidpf opaque, uInt items, uInt size);
static void fast_free(voidpf opaque, voidpf address);
- static const int MAX_ZLIB_ALLOCS = 64;
+ static constexpr int MAX_ZLIB_ALLOCS = 64;
+
uint32_t * m_allocptr[MAX_ZLIB_ALLOCS];
};
@@ -96,6 +102,44 @@ private:
};
+// ======================> chd_zstd_compressor
+
+// Zstandard compressor
+class chd_zstd_compressor : public chd_compressor
+{
+public:
+ // construction/destruction
+ chd_zstd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy);
+ ~chd_zstd_compressor();
+
+ // core functionality
+ virtual uint32_t compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) override;
+
+private:
+ // internal state
+ ZSTD_CStream * m_stream;
+};
+
+
+// ======================> chd_zstd_decompressor
+
+// Zstandard decompressor
+class chd_zstd_decompressor : public chd_decompressor
+{
+public:
+ // construction/destruction
+ chd_zstd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy);
+ ~chd_zstd_decompressor();
+
+ // core functionality
+ virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) override;
+
+private:
+ // internal state
+ ZSTD_DStream * m_stream;
+};
+
+
// ======================> chd_lzma_allocator
// allocation helper clas for zlib
@@ -108,11 +152,11 @@ public:
private:
// internal helpers
- static void *fast_alloc(void *p, size_t size);
- static void fast_free(void *p, void *address);
+ static void *fast_alloc(ISzAllocPtr p, size_t size);
+ static void fast_free(ISzAllocPtr p, void *address);
- static const int MAX_LZMA_ALLOCS = 64;
- uint32_t * m_allocptr[MAX_LZMA_ALLOCS];
+ static constexpr int MAX_LZMA_ALLOCS = 64;
+ uint32_t *m_allocptr[MAX_LZMA_ALLOCS];
};
@@ -287,27 +331,27 @@ private:
// ======================> chd_cd_compressor
-template<class _BaseCompressor, class _SubcodeCompressor>
+template <class BaseCompressor, class SubcodeCompressor>
class chd_cd_compressor : public chd_compressor
{
public:
// construction/destruction
chd_cd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : chd_compressor(chd, hunkbytes, lossy),
- m_base_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy),
- m_subcode_compressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy),
- m_buffer(hunkbytes + (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA)
+ : chd_compressor(chd, hunkbytes, lossy)
+ , m_base_compressor(chd, (hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SECTOR_DATA, lossy)
+ , m_subcode_compressor(chd, (hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SUBCODE_DATA, lossy)
+ , m_buffer(hunkbytes + (hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SUBCODE_DATA)
{
// make sure the CHD's hunk size is an even multiple of the frame size
- if (hunkbytes % CD_FRAME_SIZE != 0)
- throw CHDERR_CODEC_ERROR;
+ if (hunkbytes % cdrom_file::FRAME_SIZE != 0)
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
// core functionality
virtual uint32_t compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) override
{
// determine header bytes
- uint32_t frames = srclen / CD_FRAME_SIZE;
+ uint32_t frames = srclen / cdrom_file::FRAME_SIZE;
uint32_t complen_bytes = (srclen < 65536) ? 2 : 3;
uint32_t ecc_bytes = (frames + 7) / 8;
uint32_t header_bytes = ecc_bytes + complen_bytes;
@@ -318,98 +362,96 @@ public:
// copy audio data followed by subcode data
for (uint32_t framenum = 0; framenum < frames; framenum++)
{
- memcpy(&m_buffer[framenum * CD_MAX_SECTOR_DATA], &src[framenum * CD_FRAME_SIZE], CD_MAX_SECTOR_DATA);
- memcpy(&m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], &src[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], CD_MAX_SUBCODE_DATA);
+ memcpy(&m_buffer[framenum * cdrom_file::MAX_SECTOR_DATA], &src[framenum * cdrom_file::FRAME_SIZE], cdrom_file::MAX_SECTOR_DATA);
+ memcpy(&m_buffer[frames * cdrom_file::MAX_SECTOR_DATA + framenum * cdrom_file::MAX_SUBCODE_DATA], &src[framenum * cdrom_file::FRAME_SIZE + cdrom_file::MAX_SECTOR_DATA], cdrom_file::MAX_SUBCODE_DATA);
// clear out ECC data if we can
- uint8_t *sector = &m_buffer[framenum * CD_MAX_SECTOR_DATA];
- if (memcmp(sector, s_cd_sync_header, sizeof(s_cd_sync_header)) == 0 && ecc_verify(sector))
+ uint8_t *sector = &m_buffer[framenum * cdrom_file::MAX_SECTOR_DATA];
+ if (memcmp(sector, f_cd_sync_header, sizeof(f_cd_sync_header)) == 0 && cdrom_file::ecc_verify(sector))
{
dest[framenum / 8] |= 1 << (framenum % 8);
- memset(sector, 0, sizeof(s_cd_sync_header));
- ecc_clear(sector);
+ memset(sector, 0, sizeof(f_cd_sync_header));
+ cdrom_file::ecc_clear(sector);
}
}
// encode the base portion
- uint32_t complen = m_base_compressor.compress(&m_buffer[0], frames * CD_MAX_SECTOR_DATA, &dest[header_bytes]);
+ uint32_t complen = m_base_compressor.compress(&m_buffer[0], frames * cdrom_file::MAX_SECTOR_DATA, &dest[header_bytes]);
if (complen >= srclen)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// write compressed length
- dest[ecc_bytes + 0] = complen >> ((complen_bytes - 1) * 8);
- dest[ecc_bytes + 1] = complen >> ((complen_bytes - 2) * 8);
if (complen_bytes > 2)
- dest[ecc_bytes + 2] = complen >> ((complen_bytes - 3) * 8);
+ put_u24be(&dest[ecc_bytes], complen);
+ else
+ put_u16be(&dest[ecc_bytes], complen);
// encode the subcode
- return header_bytes + complen + m_subcode_compressor.compress(&m_buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA, &dest[header_bytes + complen]);
+ return header_bytes + complen + m_subcode_compressor.compress(&m_buffer[frames * cdrom_file::MAX_SECTOR_DATA], frames * cdrom_file::MAX_SUBCODE_DATA, &dest[header_bytes + complen]);
}
private:
// internal state
- _BaseCompressor m_base_compressor;
- _SubcodeCompressor m_subcode_compressor;
+ BaseCompressor m_base_compressor;
+ SubcodeCompressor m_subcode_compressor;
std::vector<uint8_t> m_buffer;
};
// ======================> chd_cd_decompressor
-template<class _BaseDecompressor, class _SubcodeDecompressor>
+template <class BaseDecompressor, class SubcodeDecompressor>
class chd_cd_decompressor : public chd_decompressor
{
public:
// construction/destruction
chd_cd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : chd_decompressor(chd, hunkbytes, lossy),
- m_base_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA, lossy),
- m_subcode_decompressor(chd, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SUBCODE_DATA, lossy),
- m_buffer(hunkbytes)
+ : chd_decompressor(chd, hunkbytes, lossy)
+ , m_base_decompressor(chd, (hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SECTOR_DATA, lossy)
+ , m_subcode_decompressor(chd, (hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SUBCODE_DATA, lossy)
+ , m_buffer(hunkbytes)
{
// make sure the CHD's hunk size is an even multiple of the frame size
- if (hunkbytes % CD_FRAME_SIZE != 0)
- throw CHDERR_CODEC_ERROR;
+ if (hunkbytes % cdrom_file::FRAME_SIZE != 0)
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
// core functionality
virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) override
{
// determine header bytes
- uint32_t frames = destlen / CD_FRAME_SIZE;
+ uint32_t frames = destlen / cdrom_file::FRAME_SIZE;
uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
uint32_t ecc_bytes = (frames + 7) / 8;
uint32_t header_bytes = ecc_bytes + complen_bytes;
// extract compressed length of base
- uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
- if (complen_bytes > 2)
- complen_base = (complen_base << 8) | src[ecc_bytes + 2];
+ uint32_t complen_base = (complen_bytes > 2) ? get_u24be(&src[ecc_bytes]) : get_u16be(&src[ecc_bytes]);
// reset and decode
- m_base_decompressor.decompress(&src[header_bytes], complen_base, &m_buffer[0], frames * CD_MAX_SECTOR_DATA);
- m_subcode_decompressor.decompress(&src[header_bytes + complen_base], complen - complen_base - header_bytes, &m_buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
+ m_base_decompressor.decompress(&src[header_bytes], complen_base, &m_buffer[0], frames * cdrom_file::MAX_SECTOR_DATA);
+ m_subcode_decompressor.decompress(&src[header_bytes + complen_base], complen - complen_base - header_bytes, &m_buffer[frames * cdrom_file::MAX_SECTOR_DATA], frames * cdrom_file::MAX_SUBCODE_DATA);
// reassemble the data
for (uint32_t framenum = 0; framenum < frames; framenum++)
{
- memcpy(&dest[framenum * CD_FRAME_SIZE], &m_buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
- memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
+ memcpy(&dest[framenum * cdrom_file::FRAME_SIZE], &m_buffer[framenum * cdrom_file::MAX_SECTOR_DATA], cdrom_file::MAX_SECTOR_DATA);
+ memcpy(&dest[framenum * cdrom_file::FRAME_SIZE + cdrom_file::MAX_SECTOR_DATA], &m_buffer[frames * cdrom_file::MAX_SECTOR_DATA + framenum * cdrom_file::MAX_SUBCODE_DATA], cdrom_file::MAX_SUBCODE_DATA);
// reconstitute the ECC data and sync header
- uint8_t *sector = &dest[framenum * CD_FRAME_SIZE];
+ uint8_t *sector = &dest[framenum * cdrom_file::FRAME_SIZE];
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
{
- memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));
- ecc_generate(sector);
+ memcpy(sector, f_cd_sync_header, sizeof(f_cd_sync_header));
+ cdrom_file::ecc_generate(sector);
}
}
}
private:
// internal state
- _BaseDecompressor m_base_decompressor;
- _SubcodeDecompressor m_subcode_decompressor;
+ BaseDecompressor m_base_decompressor;
+ SubcodeDecompressor m_subcode_decompressor;
std::vector<uint8_t> m_buffer;
};
@@ -446,6 +488,7 @@ public:
chd_avhuff_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy);
// core functionality
+ virtual void process(const uint8_t *src, uint32_t complen) override;
virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) override;
virtual void configure(int param, void *config) override;
@@ -460,25 +503,67 @@ private:
// CODEC LIST
//**************************************************************************
+// an entry in the list
+struct codec_entry
+{
+ chd_codec_type m_type;
+ bool m_lossy;
+ const char * m_name;
+ chd_compressor::ptr (*m_construct_compressor)(chd_file &, uint32_t, bool);
+ chd_decompressor::ptr (*m_construct_decompressor)(chd_file &, uint32_t, bool);
+
+ template <class CompressorClass>
+ static chd_compressor::ptr construct_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
+ {
+ return std::make_unique<CompressorClass>(chd, hunkbytes, lossy);
+ }
+
+ template <class DecompressorClass>
+ static chd_decompressor::ptr construct_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
+ {
+ return std::make_unique<DecompressorClass>(chd, hunkbytes, lossy);
+ }
+};
+
+
// static list of available known codecs
-const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] =
+const codec_entry f_codec_list[] =
{
// general codecs
- { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor<chd_zlib_compressor>, &chd_codec_list::construct_decompressor<chd_zlib_decompressor> },
- { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor<chd_lzma_compressor>, &chd_codec_list::construct_decompressor<chd_lzma_decompressor> },
- { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor<chd_huffman_compressor>, &chd_codec_list::construct_decompressor<chd_huffman_decompressor> },
- { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor<chd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_flac_decompressor> },
+ { CHD_CODEC_ZLIB, false, "Deflate", &codec_entry::construct_compressor<chd_zlib_compressor>, &codec_entry::construct_decompressor<chd_zlib_decompressor> },
+ { CHD_CODEC_ZSTD, false, "Zstandard", &codec_entry::construct_compressor<chd_zstd_compressor>, &codec_entry::construct_decompressor<chd_zstd_decompressor> },
+ { CHD_CODEC_LZMA, false, "LZMA", &codec_entry::construct_compressor<chd_lzma_compressor>, &codec_entry::construct_decompressor<chd_lzma_decompressor> },
+ { CHD_CODEC_HUFFMAN, false, "Huffman", &codec_entry::construct_compressor<chd_huffman_compressor>, &codec_entry::construct_decompressor<chd_huffman_decompressor> },
+ { CHD_CODEC_FLAC, false, "FLAC", &codec_entry::construct_compressor<chd_flac_compressor>, &codec_entry::construct_decompressor<chd_flac_decompressor> },
// general codecs with CD frontend
- { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > },
- { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > },
- { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor<chd_cd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_cd_flac_decompressor> },
+ { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &codec_entry::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > },
+ { CHD_CODEC_CD_ZSTD, false, "CD Zstandard", &codec_entry::construct_compressor<chd_cd_compressor<chd_zstd_compressor, chd_zstd_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_zstd_decompressor, chd_zstd_decompressor> > },
+ { CHD_CODEC_CD_LZMA, false, "CD LZMA", &codec_entry::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > },
+ { CHD_CODEC_CD_FLAC, false, "CD FLAC", &codec_entry::construct_compressor<chd_cd_flac_compressor>, &codec_entry::construct_decompressor<chd_cd_flac_decompressor> },
// A/V codecs
- { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor<chd_avhuff_compressor>, &chd_codec_list::construct_decompressor<chd_avhuff_decompressor> },
+ { CHD_CODEC_AVHUFF, false, "A/V Huffman", &codec_entry::construct_compressor<chd_avhuff_compressor>, &codec_entry::construct_decompressor<chd_avhuff_decompressor> },
};
+//-------------------------------------------------
+// find_in_list - create a new compressor
+// instance of the given type
+//-------------------------------------------------
+
+const codec_entry *find_in_list(chd_codec_type type) noexcept
+{
+ // find in the list and construct the class
+ for (auto & elem : f_codec_list)
+ if (elem.m_type == type)
+ return &elem;
+ return nullptr;
+}
+
+} // anonymous namespace
+
+
//**************************************************************************
// CHD CODEC
@@ -489,9 +574,9 @@ const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] =
//-------------------------------------------------
chd_codec::chd_codec(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : m_chd(chd),
- m_hunkbytes(hunkbytes),
- m_lossy(lossy)
+ : m_chd(chd)
+ , m_hunkbytes(hunkbytes)
+ , m_lossy(lossy)
{
}
@@ -512,7 +597,7 @@ chd_codec::~chd_codec()
void chd_codec::configure(int param, void *config)
{
// if not overridden, it is always a failure
- throw CHDERR_INVALID_PARAMETER;
+ throw std::error_condition(std::errc::invalid_argument);
}
@@ -521,10 +606,6 @@ void chd_codec::configure(int param, void *config)
// CHD COMPRESSOR
//**************************************************************************
-//-------------------------------------------------
-// chd_compressor - constructor
-//-------------------------------------------------
-
chd_compressor::chd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
: chd_codec(chd, hunkbytes, lossy)
{
@@ -536,15 +617,16 @@ chd_compressor::chd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
// CHD DECOMPRESSOR
//**************************************************************************
-//-------------------------------------------------
-// chd_decompressor - constructor
-//-------------------------------------------------
-
chd_decompressor::chd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
: chd_codec(chd, hunkbytes, lossy)
{
}
+void chd_decompressor::process(const uint8_t *src, uint32_t complen)
+{
+ throw std::error_condition(chd_file::error::UNSUPPORTED_FORMAT);
+}
+
//**************************************************************************
@@ -556,11 +638,11 @@ chd_decompressor::chd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy
// instance of the given type
//-------------------------------------------------
-chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &chd)
+chd_compressor::ptr chd_codec_list::new_compressor(chd_codec_type type, chd_file &chd)
{
// find in the list and construct the class
- const codec_entry *entry = find_in_list(type);
- return (entry == nullptr) ? nullptr : (*entry->m_construct_compressor)(chd, chd.hunk_bytes(), entry->m_lossy);
+ codec_entry const *const entry = find_in_list(type);
+ return entry ? (*entry->m_construct_compressor)(chd, chd.hunk_bytes(), entry->m_lossy) : nullptr;
}
@@ -569,39 +651,36 @@ chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &ch
// instance of the given type
//-------------------------------------------------
-chd_decompressor *chd_codec_list::new_decompressor(chd_codec_type type, chd_file &chd)
+chd_decompressor::ptr chd_codec_list::new_decompressor(chd_codec_type type, chd_file &chd)
{
// find in the list and construct the class
const codec_entry *entry = find_in_list(type);
- return (entry == nullptr) ? nullptr : (*entry->m_construct_decompressor)(chd, chd.hunk_bytes(), entry->m_lossy);
+ return entry ? (*entry->m_construct_decompressor)(chd, chd.hunk_bytes(), entry->m_lossy) : nullptr;
}
//-------------------------------------------------
-// codec_name - return the name of the given
-// codec
+// codec_exists - determine whether a codec type
+// corresponds to a supported codec
//-------------------------------------------------
-const char *chd_codec_list::codec_name(chd_codec_type type)
+bool chd_codec_list::codec_exists(chd_codec_type type) noexcept
{
// find in the list and construct the class
- const codec_entry *entry = find_in_list(type);
- return (entry == nullptr) ? nullptr : entry->m_name;
+ return bool(find_in_list(type));
}
//-------------------------------------------------
-// find_in_list - create a new compressor
-// instance of the given type
+// codec_name - return the name of the given
+// codec
//-------------------------------------------------
-const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type type)
+const char *chd_codec_list::codec_name(chd_codec_type type) noexcept
{
// find in the list and construct the class
- for (auto & elem : s_codec_list)
- if (elem.m_type == type)
- return &elem;
- return nullptr;
+ const codec_entry *entry = find_in_list(type);
+ return entry ? entry->m_name : nullptr;
}
@@ -615,25 +694,24 @@ const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type t
//-------------------------------------------------
chd_compressor_group::chd_compressor_group(chd_file &chd, uint32_t compressor_list[4])
- : m_hunkbytes(chd.hunk_bytes()),
- m_compress_test(m_hunkbytes)
+ : m_hunkbytes(chd.hunk_bytes())
+ , m_compress_test(m_hunkbytes)
#if CHDCODEC_VERIFY_COMPRESSION
- ,m_decompressed(m_hunkbytes)
+ , m_decompressed(m_hunkbytes)
#endif
{
// verify the compression types and initialize the codecs
- for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++)
+ for (int codecnum = 0; codecnum < std::size(m_compressor); codecnum++)
{
- m_compressor[codecnum] = nullptr;
if (compressor_list[codecnum] != CHD_CODEC_NONE)
{
m_compressor[codecnum] = chd_codec_list::new_compressor(compressor_list[codecnum], chd);
- if (m_compressor[codecnum] == nullptr)
- throw CHDERR_UNKNOWN_COMPRESSION;
+ if (!m_compressor[codecnum])
+ throw std::error_condition(chd_file::error::UNKNOWN_COMPRESSION);
#if CHDCODEC_VERIFY_COMPRESSION
m_decompressor[codecnum] = chd_codec_list::new_decompressor(compressor_list[codecnum], chd);
- if (m_decompressor[codecnum] == nullptr)
- throw CHDERR_UNKNOWN_COMPRESSION;
+ if (!m_decompressor[codecnum])
+ throw std::error_condition(chd_file::error::UNKNOWN_COMPRESSION);
#endif
}
}
@@ -646,9 +724,7 @@ chd_compressor_group::chd_compressor_group(chd_file &chd, uint32_t compressor_li
chd_compressor_group::~chd_compressor_group()
{
- // delete the codecs and the test buffer
- for (auto & elem : m_compressor)
- delete elem;
+ // codecs and test buffer deleted automatically
}
@@ -663,8 +739,8 @@ int8_t chd_compressor_group::find_best_compressor(const uint8_t *src, uint8_t *c
// determine best compression technique
complen = m_hunkbytes;
int8_t compression = -1;
- for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compressor); codecnum++)
- if (m_compressor[codecnum] != nullptr)
+ for (int codecnum = 0; codecnum < std::size(m_compressor); codecnum++)
+ if (m_compressor[codecnum])
{
// attempt to compress, swallowing errors
try
@@ -702,7 +778,9 @@ printf(" codec%d=%d bytes \n", codecnum, compbytes);
memcpy(compressed, &m_compress_test[0], compbytes);
}
}
- catch (...) { }
+ catch (...)
+ {
+ }
}
// if the best is none, copy it over
@@ -729,7 +807,7 @@ chd_zlib_allocator::chd_zlib_allocator()
//-------------------------------------------------
-// ~chd_zlib_allocator - constructor
+// ~chd_zlib_allocator - destructor
//-------------------------------------------------
chd_zlib_allocator::~chd_zlib_allocator()
@@ -760,7 +838,7 @@ void chd_zlib_allocator::install(z_stream &stream)
voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size)
{
- chd_zlib_allocator *codec = reinterpret_cast<chd_zlib_allocator *>(opaque);
+ auto *codec = reinterpret_cast<chd_zlib_allocator *>(opaque);
// compute the size, rounding to the nearest 1k
size = (size * items + 0x3ff) & ~0x3ff;
@@ -778,7 +856,7 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size)
}
// alloc a new one and put it into the list
- uint32_t *ptr = reinterpret_cast<uint32_t *>(new uint8_t[size + sizeof(uint32_t)]);
+ auto *ptr = reinterpret_cast<uint32_t *>(new uint8_t[size + sizeof(uint32_t)]);
for (int scan = 0; scan < MAX_ZLIB_ALLOCS; scan++)
if (codec->m_allocptr[scan] == nullptr)
{
@@ -799,7 +877,7 @@ voidpf chd_zlib_allocator::fast_alloc(voidpf opaque, uInt items, uInt size)
void chd_zlib_allocator::fast_free(voidpf opaque, voidpf address)
{
- chd_zlib_allocator *codec = reinterpret_cast<chd_zlib_allocator *>(opaque);
+ auto *codec = reinterpret_cast<chd_zlib_allocator *>(opaque);
// find the hunk
uint32_t *ptr = reinterpret_cast<uint32_t *>(address) - 1;
@@ -835,7 +913,7 @@ chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, uint32_t hunkbytes, bool
if (zerr == Z_MEM_ERROR)
throw std::bad_alloc();
else if (zerr != Z_OK)
- throw CHDERR_CODEC_ERROR;
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
@@ -855,7 +933,7 @@ chd_zlib_compressor::~chd_zlib_compressor()
uint32_t chd_zlib_compressor::compress(const uint8_t *src, uint32_t srclen, uint8_t *dest)
{
- // reset the decompressor
+ // reset the compressor
m_deflater.next_in = const_cast<Bytef *>(src);
m_deflater.avail_in = srclen;
m_deflater.total_in = 0;
@@ -864,14 +942,14 @@ uint32_t chd_zlib_compressor::compress(const uint8_t *src, uint32_t srclen, uint
m_deflater.total_out = 0;
int zerr = deflateReset(&m_deflater);
if (zerr != Z_OK)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// do it
zerr = deflate(&m_deflater, Z_FINISH);
// if we ended up with more data than we started with, return an error
if (zerr != Z_STREAM_END || m_deflater.total_out >= srclen)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// otherwise, return the length
return m_deflater.total_out;
@@ -900,7 +978,7 @@ chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, uint32_t hunkbytes,
if (zerr == Z_MEM_ERROR)
throw std::bad_alloc();
else if (zerr != Z_OK)
- throw CHDERR_CODEC_ERROR;
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
@@ -930,14 +1008,139 @@ void chd_zlib_decompressor::decompress(const uint8_t *src, uint32_t complen, uin
m_inflater.total_out = 0;
int zerr = inflateReset(&m_inflater);
if (zerr != Z_OK)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// do it
zerr = inflate(&m_inflater, Z_FINISH);
if (zerr != Z_STREAM_END)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
if (m_inflater.total_out != destlen)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
+}
+
+
+
+//**************************************************************************
+// ZSTANDARD COMPRESSOR
+//**************************************************************************
+
+//-------------------------------------------------
+// chd_zstd_compressor - constructor
+//-------------------------------------------------
+
+chd_zstd_compressor::chd_zstd_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
+ : chd_compressor(chd, hunkbytes, lossy)
+ , m_stream(nullptr)
+{
+ // initialize the stream
+ m_stream = ZSTD_createCStream();
+
+ // convert errors
+ if (!m_stream)
+ throw std::bad_alloc();
+}
+
+
+//-------------------------------------------------
+// ~chd_zstd_compressor - destructor
+//-------------------------------------------------
+
+chd_zstd_compressor::~chd_zstd_compressor()
+{
+ ZSTD_freeCStream(m_stream);
+}
+
+
+//-------------------------------------------------
+// compress - compress data using the Zstandard
+// codec
+//-------------------------------------------------
+
+uint32_t chd_zstd_compressor::compress(const uint8_t *src, uint32_t srclen, uint8_t *dest)
+{
+ // reset the compressor
+ auto result = ZSTD_initCStream(m_stream, ZSTD_maxCLevel());
+ if (ZSTD_isError(result))
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
+
+ // do it
+ ZSTD_inBuffer input{ src, srclen, 0 };
+ ZSTD_outBuffer output = { dest, srclen, 0 };
+ while (output.pos < output.size)
+ {
+ result = ZSTD_compressStream2(m_stream, &output, &input, ZSTD_e_end);
+ if (ZSTD_isError(result))
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
+ else if (!result)
+ break;
+ }
+
+ // if we ended up with more data than we started with, return an error
+ if (output.pos == output.size)
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
+
+ // otherwise, return the length
+ return output.pos;
+}
+
+
+
+//**************************************************************************
+// ZSTANDARD DECOMPRESSOR
+//**************************************************************************
+
+//-------------------------------------------------
+// chd_zstd_compressor - constructor
+//-------------------------------------------------
+
+chd_zstd_decompressor::chd_zstd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
+ : chd_decompressor(chd, hunkbytes, lossy)
+ , m_stream(nullptr)
+{
+ // initialize the stream
+ m_stream = ZSTD_createDStream();
+
+ // convert errors
+ if (!m_stream)
+ throw std::bad_alloc();
+}
+
+
+//-------------------------------------------------
+// ~chd_zstd_decompressor - destructor
+//-------------------------------------------------
+
+chd_zstd_decompressor::~chd_zstd_decompressor()
+{
+ ZSTD_freeDStream(m_stream);
+}
+
+
+//-------------------------------------------------
+// decompress - decompress data using the
+// Zstandard codec
+//-------------------------------------------------
+
+void chd_zstd_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
+{
+ // reset the decompressor
+ auto result = ZSTD_initDStream(m_stream);
+ if (ZSTD_isError(result))
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
+
+ // do it
+ ZSTD_inBuffer input{ src, complen, 0 };
+ ZSTD_outBuffer output = { dest, destlen, 0 };
+ while ((input.pos < input.size) && (output.pos < output.size))
+ {
+ result = ZSTD_decompressStream(m_stream, &output, &input);
+ if (ZSTD_isError(result))
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
+ }
+
+ // ensure the expected amount of output was generated
+ if (output.pos != output.size)
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
}
@@ -978,9 +1181,9 @@ chd_lzma_allocator::~chd_lzma_allocator()
// allocates and frees memory frequently
//-------------------------------------------------
-void *chd_lzma_allocator::fast_alloc(void *p, size_t size)
+void *chd_lzma_allocator::fast_alloc(ISzAllocPtr p, size_t size)
{
- chd_lzma_allocator *codec = reinterpret_cast<chd_lzma_allocator *>(p);
+ auto *const codec = static_cast<chd_lzma_allocator *>(const_cast<ISzAlloc *>(p));
// compute the size, rounding to the nearest 1k
size = (size + 0x3ff) & ~0x3ff;
@@ -998,7 +1201,7 @@ void *chd_lzma_allocator::fast_alloc(void *p, size_t size)
}
// alloc a new one and put it into the list
- uint32_t *ptr = reinterpret_cast<uint32_t *>(new uint8_t[size + sizeof(uint32_t)]);
+ auto *ptr = reinterpret_cast<uint32_t *>(new uint8_t[size + sizeof(uint32_t)]);
for (int scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
if (codec->m_allocptr[scan] == nullptr)
{
@@ -1017,12 +1220,12 @@ void *chd_lzma_allocator::fast_alloc(void *p, size_t size)
// allocates and frees memory frequently
//-------------------------------------------------
-void chd_lzma_allocator::fast_free(void *p, void *address)
+void chd_lzma_allocator::fast_free(ISzAllocPtr p, void *address)
{
if (address == nullptr)
return;
- chd_lzma_allocator *codec = reinterpret_cast<chd_lzma_allocator *>(p);
+ auto *const codec = static_cast<chd_lzma_allocator *>(const_cast<ISzAlloc *>(p));
// find the hunk
uint32_t *ptr = reinterpret_cast<uint32_t *>(address) - 1;
@@ -1071,20 +1274,20 @@ uint32_t chd_lzma_compressor::compress(const uint8_t *src, uint32_t srclen, uint
// allocate the encoder
CLzmaEncHandle encoder = LzmaEnc_Create(&m_allocator);
if (encoder == nullptr)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
try
{
// configure the encoder
SRes res = LzmaEnc_SetProps(encoder, &m_props);
if (res != SZ_OK)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// run it
SizeT complen = srclen;
res = LzmaEnc_MemEncode(encoder, dest, &complen, src, srclen, 0, nullptr, &m_allocator, &m_allocator);
if (res != SZ_OK)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// clean up
LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator);
@@ -1107,7 +1310,7 @@ uint32_t chd_lzma_compressor::compress(const uint8_t *src, uint32_t srclen, uint
void chd_lzma_compressor::configure_properties(CLzmaEncProps &props, uint32_t hunkbytes)
{
LzmaEncProps_Init(&props);
- props.level = 9;
+ props.level = 8;
props.reduceSize = hunkbytes;
LzmaEncProps_Normalize(&props);
}
@@ -1140,24 +1343,24 @@ chd_lzma_decompressor::chd_lzma_decompressor(chd_file &chd, uint32_t hunkbytes,
// convert to decoder properties
CLzmaEncHandle enc = LzmaEnc_Create(&m_allocator);
if (!enc)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)
{
LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
}
Byte decoder_props[LZMA_PROPS_SIZE];
SizeT props_size = sizeof(decoder_props);
if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)
{
LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
}
LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
// do memory allocations
if (LzmaDec_Allocate(&m_decoder, decoder_props, LZMA_PROPS_SIZE, &m_allocator) != SZ_OK)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
}
@@ -1188,7 +1391,7 @@ void chd_lzma_decompressor::decompress(const uint8_t *src, uint32_t complen, uin
ELzmaStatus status;
SRes res = LzmaDec_DecodeToBuf(&m_decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
}
@@ -1216,7 +1419,7 @@ uint32_t chd_huffman_compressor::compress(const uint8_t *src, uint32_t srclen, u
{
uint32_t complen;
if (m_encoder.encode(src, srclen, dest, srclen, complen) != HUFFERR_NONE)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
return complen;
}
@@ -1244,7 +1447,7 @@ chd_huffman_decompressor::chd_huffman_decompressor(chd_file &chd, uint32_t hunkb
void chd_huffman_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
if (m_decoder.decode(src, complen, dest, destlen) != HUFFERR_NONE)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
}
@@ -1282,19 +1485,19 @@ uint32_t chd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, uint
// reset and encode big-endian
m_encoder.reset(dest + 1, hunkbytes() - 1);
if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, !m_big_endian))
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
uint32_t complen_be = m_encoder.finish();
// reset and encode little-endian
m_encoder.reset(dest + 1, hunkbytes() - 1);
if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, m_big_endian))
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
uint32_t complen_le = m_encoder.finish();
// pick the best one and add a byte
uint32_t complen = std::min(complen_le, complen_be);
if (complen + 1 >= hunkbytes())
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// if big-endian was better, re-do it
dest[0] = 'L';
@@ -1303,7 +1506,7 @@ uint32_t chd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, uint
dest[0] = 'B';
m_encoder.reset(dest + 1, hunkbytes() - 1);
if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, !m_big_endian))
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
m_encoder.finish();
}
return complen + 1;
@@ -1358,13 +1561,13 @@ void chd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, uin
else if (src[0] == 'B')
swap_endian = !m_big_endian;
else
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// reset and decode
if (!m_decoder.reset(44100, 2, chd_flac_compressor::blocksize(destlen), src + 1, complen - 1))
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
if (!m_decoder.decode_interleaved(reinterpret_cast<int16_t *>(dest), destlen / 4, swap_endian))
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// finish up
m_decoder.finish();
@@ -1381,12 +1584,12 @@ void chd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, uin
//-------------------------------------------------
chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : chd_compressor(chd, hunkbytes, lossy),
- m_buffer(hunkbytes)
+ : chd_compressor(chd, hunkbytes, lossy)
+ , m_buffer(hunkbytes)
{
// make sure the CHD's hunk size is an even multiple of the frame size
- if (hunkbytes % CD_FRAME_SIZE != 0)
- throw CHDERR_CODEC_ERROR;
+ if (hunkbytes % cdrom_file::FRAME_SIZE != 0)
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
// determine whether we want native or swapped samples
uint16_t native_endian = 0;
@@ -1396,7 +1599,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, uint32_t hunkbytes
// configure the encoder
m_encoder.set_sample_rate(44100);
m_encoder.set_num_channels(2);
- m_encoder.set_block_size(blocksize((hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA));
+ m_encoder.set_block_size(blocksize((hunkbytes / cdrom_file::FRAME_SIZE) * cdrom_file::MAX_SECTOR_DATA));
m_encoder.set_strip_metadata(true);
// initialize the deflater
@@ -1409,7 +1612,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, uint32_t hunkbytes
if (zerr == Z_MEM_ERROR)
throw std::bad_alloc();
else if (zerr != Z_OK)
- throw CHDERR_CODEC_ERROR;
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
@@ -1431,32 +1634,32 @@ chd_cd_flac_compressor::~chd_cd_flac_compressor()
uint32_t chd_cd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, uint8_t *dest)
{
// copy audio data followed by subcode data
- uint32_t frames = hunkbytes() / CD_FRAME_SIZE;
+ uint32_t frames = hunkbytes() / cdrom_file::FRAME_SIZE;
for (uint32_t framenum = 0; framenum < frames; framenum++)
{
- memcpy(&m_buffer[framenum * CD_MAX_SECTOR_DATA], &src[framenum * CD_FRAME_SIZE], CD_MAX_SECTOR_DATA);
- memcpy(&m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], &src[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], CD_MAX_SUBCODE_DATA);
+ memcpy(&m_buffer[framenum * cdrom_file::MAX_SECTOR_DATA], &src[framenum * cdrom_file::FRAME_SIZE], cdrom_file::MAX_SECTOR_DATA);
+ memcpy(&m_buffer[frames * cdrom_file::MAX_SECTOR_DATA + framenum * cdrom_file::MAX_SUBCODE_DATA], &src[framenum * cdrom_file::FRAME_SIZE + cdrom_file::MAX_SECTOR_DATA], cdrom_file::MAX_SUBCODE_DATA);
}
// reset and encode the audio portion
m_encoder.reset(dest, hunkbytes());
uint8_t *buffer = &m_buffer[0];
- if (!m_encoder.encode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian))
- throw CHDERR_COMPRESSION_ERROR;
+ if (!m_encoder.encode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * cdrom_file::MAX_SECTOR_DATA/4, m_swap_endian))
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// finish up
uint32_t complen = m_encoder.finish();
// deflate the subcode data
- m_deflater.next_in = const_cast<Bytef *>(&m_buffer[frames * CD_MAX_SECTOR_DATA]);
- m_deflater.avail_in = frames * CD_MAX_SUBCODE_DATA;
+ m_deflater.next_in = const_cast<Bytef *>(&m_buffer[frames * cdrom_file::MAX_SECTOR_DATA]);
+ m_deflater.avail_in = frames * cdrom_file::MAX_SUBCODE_DATA;
m_deflater.total_in = 0;
m_deflater.next_out = &dest[complen];
m_deflater.avail_out = hunkbytes() - complen;
m_deflater.total_out = 0;
int zerr = deflateReset(&m_deflater);
if (zerr != Z_OK)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
// do it
zerr = deflate(&m_deflater, Z_FINISH);
@@ -1464,7 +1667,7 @@ uint32_t chd_cd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, u
// if we ended up with more data than we started with, return an error
complen += m_deflater.total_out;
if (zerr != Z_STREAM_END || complen >= srclen)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
return complen;
}
@@ -1484,7 +1687,7 @@ uint32_t chd_cd_flac_compressor::blocksize(uint32_t bytes)
{
// for CDs it seems that CD_MAX_SECTOR_DATA is the right target
uint32_t blocksize = bytes / 4;
- while (blocksize > CD_MAX_SECTOR_DATA)
+ while (blocksize > cdrom_file::MAX_SECTOR_DATA)
blocksize /= 2;
return blocksize;
}
@@ -1510,12 +1713,12 @@ uint32_t chd_cd_flac_compressor::blocksize(uint32_t bytes)
*/
chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : chd_decompressor(chd, hunkbytes, lossy),
- m_buffer(hunkbytes)
+ : chd_decompressor(chd, hunkbytes, lossy)
+ , m_buffer(hunkbytes)
{
// make sure the CHD's hunk size is an even multiple of the frame size
- if (hunkbytes % CD_FRAME_SIZE != 0)
- throw CHDERR_CODEC_ERROR;
+ if (hunkbytes % cdrom_file::FRAME_SIZE != 0)
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
// determine whether we want native or swapped samples
uint16_t native_endian = 0;
@@ -1532,7 +1735,7 @@ chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, uint32_t hunkb
if (zerr == Z_MEM_ERROR)
throw std::bad_alloc();
else if (zerr != Z_OK)
- throw CHDERR_CODEC_ERROR;
+ throw std::error_condition(chd_file::error::CODEC_ERROR);
}
/**
@@ -1567,37 +1770,37 @@ chd_cd_flac_decompressor::~chd_cd_flac_decompressor()
void chd_cd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
// reset and decode
- uint32_t frames = destlen / CD_FRAME_SIZE;
- if (!m_decoder.reset(44100, 2, chd_cd_flac_compressor::blocksize(frames * CD_MAX_SECTOR_DATA), src, complen))
- throw CHDERR_DECOMPRESSION_ERROR;
+ uint32_t frames = destlen / cdrom_file::FRAME_SIZE;
+ if (!m_decoder.reset(44100, 2, chd_cd_flac_compressor::blocksize(frames * cdrom_file::MAX_SECTOR_DATA), src, complen))
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
uint8_t *buffer = &m_buffer[0];
- if (!m_decoder.decode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian))
- throw CHDERR_DECOMPRESSION_ERROR;
+ if (!m_decoder.decode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * cdrom_file::MAX_SECTOR_DATA/4, m_swap_endian))
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// inflate the subcode data
uint32_t offset = m_decoder.finish();
m_inflater.next_in = const_cast<Bytef *>(src + offset);
m_inflater.avail_in = complen - offset;
m_inflater.total_in = 0;
- m_inflater.next_out = &m_buffer[frames * CD_MAX_SECTOR_DATA];
- m_inflater.avail_out = frames * CD_MAX_SUBCODE_DATA;
+ m_inflater.next_out = &m_buffer[frames * cdrom_file::MAX_SECTOR_DATA];
+ m_inflater.avail_out = frames * cdrom_file::MAX_SUBCODE_DATA;
m_inflater.total_out = 0;
int zerr = inflateReset(&m_inflater);
if (zerr != Z_OK)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// do it
zerr = inflate(&m_inflater, Z_FINISH);
if (zerr != Z_STREAM_END)
- throw CHDERR_DECOMPRESSION_ERROR;
- if (m_inflater.total_out != frames * CD_MAX_SUBCODE_DATA)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
+ if (m_inflater.total_out != frames * cdrom_file::MAX_SUBCODE_DATA)
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// reassemble the data
for (uint32_t framenum = 0; framenum < frames; framenum++)
{
- memcpy(&dest[framenum * CD_FRAME_SIZE], &m_buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
- memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &m_buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
+ memcpy(&dest[framenum * cdrom_file::FRAME_SIZE], &m_buffer[framenum * cdrom_file::MAX_SECTOR_DATA], cdrom_file::MAX_SECTOR_DATA);
+ memcpy(&dest[framenum * cdrom_file::FRAME_SIZE + cdrom_file::MAX_SECTOR_DATA], &m_buffer[frames * cdrom_file::MAX_SECTOR_DATA + framenum * cdrom_file::MAX_SUBCODE_DATA], cdrom_file::MAX_SUBCODE_DATA);
}
}
@@ -1620,15 +1823,15 @@ void chd_cd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen,
*/
chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy)
- : chd_compressor(chd, hunkbytes, lossy),
- m_postinit(false)
+ : chd_compressor(chd, hunkbytes, lossy)
+ , m_postinit(false)
{
try
{
// attempt to do a post-init now
postinit();
}
- catch (chd_error &)
+ catch (std::error_condition const &)
{
// if we're creating a new CHD, it won't work but that's ok
}
@@ -1665,14 +1868,14 @@ uint32_t chd_avhuff_compressor::compress(const uint8_t *src, uint32_t srclen, ui
int size = avhuff_encoder::raw_data_size(src);
while (size < srclen)
if (src[size++] != 0)
- throw CHDERR_INVALID_DATA;
+ throw std::error_condition(chd_file::error::INVALID_DATA);
}
// encode the audio and video
uint32_t complen;
avhuff_error averr = m_encoder.encode_data(src, dest, complen);
if (averr != AVHERR_NONE || complen > srclen)
- throw CHDERR_COMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::COMPRESSION_ERROR);
return complen;
}
@@ -1693,21 +1896,21 @@ void chd_avhuff_compressor::postinit()
{
// get the metadata
std::string metadata;
- chd_error err = chd().read_metadata(AV_METADATA_TAG, 0, metadata);
- if (err != CHDERR_NONE)
+ std::error_condition err = chd().read_metadata(AV_METADATA_TAG, 0, metadata);
+ if (err)
throw err;
// extract the info
int fps, fpsfrac, width, height, interlaced, channels, rate;
if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7)
- throw CHDERR_INVALID_METADATA;
+ throw std::error_condition(chd_file::error::INVALID_METADATA);
// compute the bytes per frame
uint32_t fps_times_1million = fps * 1000000 + fpsfrac;
uint32_t max_samples_per_frame = (uint64_t(rate) * 1000000 + fps_times_1million - 1) / fps_times_1million;
uint32_t bytes_per_frame = 12 + channels * max_samples_per_frame * 2 + width * height * 2;
if (bytes_per_frame > hunkbytes())
- throw CHDERR_INVALID_METADATA;
+ throw std::error_condition(chd_file::error::INVALID_METADATA);
// done with post-init
m_postinit = true;
@@ -1736,36 +1939,25 @@ chd_avhuff_decompressor::chd_avhuff_decompressor(chd_file &chd, uint32_t hunkbyt
{
}
-/**
- * @fn void chd_avhuff_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
- *
- * @brief -------------------------------------------------
- * decompress - decompress data using the A/V codec
- * -------------------------------------------------.
- *
- * @exception CHDERR_DECOMPRESSION_ERROR Thrown when a chderr decompression error error
- * condition occurs.
- *
- * @param src Source for the.
- * @param complen The complen.
- * @param [in,out] dest If non-null, destination for the.
- * @param destlen The destlen.
- */
+void chd_avhuff_decompressor::process(const uint8_t *src, uint32_t complen)
+{
+ // decode the audio and video
+ avhuff_error averr = m_decoder.decode_data(src, complen, nullptr);
+ if (averr != AVHERR_NONE)
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
+}
void chd_avhuff_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
{
// decode the audio and video
avhuff_error averr = m_decoder.decode_data(src, complen, dest);
if (averr != AVHERR_NONE)
- throw CHDERR_DECOMPRESSION_ERROR;
+ throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR);
// pad short frames with 0
- if (dest != nullptr)
- {
- int size = avhuff_encoder::raw_data_size(dest);
- if (size < destlen)
- memset(dest + size, 0, destlen - size);
- }
+ auto const size = avhuff_encoder::raw_data_size(dest);
+ if (size < destlen)
+ memset(dest + size, 0, destlen - size);
}
/**
@@ -1786,9 +1978,9 @@ void chd_avhuff_decompressor::configure(int param, void *config)
{
// if we're getting the decompression configuration, apply it now
if (param == AVHUFF_CODEC_DECOMPRESS_CONFIG)
- m_decoder.configure(*reinterpret_cast<avhuff_decompress_config *>(config));
+ m_decoder.configure(*reinterpret_cast<avhuff_decoder::config const *>(config));
// anything else is invalid
else
- throw CHDERR_INVALID_PARAMETER;
+ throw std::error_condition(std::errc::invalid_argument);
}
diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h
index e688b41a108..c477333544f 100644
--- a/src/lib/util/chdcodec.h
+++ b/src/lib/util/chdcodec.h
@@ -2,39 +2,28 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chdcodec.h
-
Codecs used by the CHD format
***************************************************************************/
-
-#ifndef MAME_UTIL_CHDCODEC_H
-#define MAME_UTIL_CHDCODEC_H
+#ifndef MAME_LIB_UTIL_CHDCODEC_H
+#define MAME_LIB_UTIL_CHDCODEC_H
#pragma once
-#include "osdcore.h"
-#include "coretmpl.h"
-
-
-#define CHDCODEC_VERIFY_COMPRESSION 0
-
+#include "utilfwd.h"
-//**************************************************************************
-// MACROS
-//**************************************************************************
+#include <cstdint>
+#include <memory>
+#include <vector>
-#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
+#define CHDCODEC_VERIFY_COMPRESSION 0
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
-// forward references
-class chd_file;
-
// base types
typedef uint32_t chd_codec_type;
@@ -63,7 +52,7 @@ public:
private:
// internal state
chd_file & m_chd;
- uint32_t m_hunkbytes;
+ uint32_t m_hunkbytes;
bool m_lossy;
};
@@ -78,6 +67,8 @@ protected:
chd_compressor(chd_file &file, uint32_t hunkbytes, bool lossy);
public:
+ using ptr = std::unique_ptr<chd_compressor>;
+
// implementation
virtual uint32_t compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) = 0;
};
@@ -93,7 +84,10 @@ protected:
chd_decompressor(chd_file &file, uint32_t hunkbytes, bool lossy);
public:
+ using ptr = std::unique_ptr<chd_decompressor>;
+
// implementation
+ virtual void process(const uint8_t *src, uint32_t complen);
virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) = 0;
};
@@ -105,35 +99,12 @@ class chd_codec_list
{
public:
// create compressors or decompressors
- static chd_compressor *new_compressor(chd_codec_type type, chd_file &file);
- static chd_decompressor *new_decompressor(chd_codec_type type, chd_file &file);
+ static chd_compressor::ptr new_compressor(chd_codec_type type, chd_file &file);
+ static chd_decompressor::ptr new_decompressor(chd_codec_type type, chd_file &file);
// utilities
- static bool codec_exists(chd_codec_type type) { return (find_in_list(type) != nullptr); }
- static const char *codec_name(chd_codec_type type);
-
-private:
- // an entry in the list
- struct codec_entry
- {
- chd_codec_type m_type;
- bool m_lossy;
- const char * m_name;
- chd_compressor * (*m_construct_compressor)(chd_file &, uint32_t, bool);
- chd_decompressor * (*m_construct_decompressor)(chd_file &, uint32_t, bool);
- };
-
- // internal helper functions
- static const codec_entry *find_in_list(chd_codec_type type);
-
- template<class _CompressorClass>
- static chd_compressor *construct_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy) { return new _CompressorClass(chd, hunkbytes, lossy); }
-
- template<class _DecompressorClass>
- static chd_decompressor *construct_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy) { return new _DecompressorClass(chd, hunkbytes, lossy); }
-
- // the static list
- static const codec_entry s_codec_list[];
+ static bool codec_exists(chd_codec_type type) noexcept;
+ static const char *codec_name(chd_codec_type type) noexcept;
};
@@ -152,37 +123,50 @@ public:
private:
// internal state
- uint32_t m_hunkbytes; // number of bytes in a hunk
- chd_compressor * m_compressor[4]; // array of active codecs
- std::vector<uint8_t> m_compress_test; // test buffer for compression
+ uint32_t m_hunkbytes; // number of bytes in a hunk
+ chd_compressor::ptr m_compressor[4]; // array of active codecs
+ std::vector<uint8_t> m_compress_test; // test buffer for compression
#if CHDCODEC_VERIFY_COMPRESSION
- chd_decompressor * m_decompressor[4]; // array of active codecs
- std::vector<uint8_t> m_decompressed; // verification buffer
+ chd_decompressor::ptr m_decompressor[4]; // array of active codecs
+ std::vector<uint8_t> m_decompressed; // verification buffer
#endif
};
//**************************************************************************
+// MACROS
+//**************************************************************************
+
+constexpr chd_codec_type CHD_MAKE_TAG(char a, char b, char c, char d)
+{
+ return (uint32_t(uint8_t(a)) << 24) | uint32_t(uint8_t((b)) << 16) | uint32_t(uint8_t((c)) << 8) | uint32_t(uint8_t(d));
+}
+
+
+
+//**************************************************************************
// CONSTANTS
//**************************************************************************
// currently-defined codecs
-const chd_codec_type CHD_CODEC_NONE = 0;
+constexpr chd_codec_type CHD_CODEC_NONE = 0;
// general codecs
-const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b');
-const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a');
-const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f');
-const chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c');
+constexpr chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b');
+constexpr chd_codec_type CHD_CODEC_ZSTD = CHD_MAKE_TAG('z','s','t','d');
+constexpr chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a');
+constexpr chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f');
+constexpr chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c');
// general codecs with CD frontend
-const chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l');
-const chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z');
-const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l');
+constexpr chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l');
+constexpr chd_codec_type CHD_CODEC_CD_ZSTD = CHD_MAKE_TAG('c','d','z','s');
+constexpr chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z');
+constexpr chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l');
// A/V codecs
-const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u');
+constexpr chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u');
// A/V codec configuration parameters
enum
@@ -190,4 +174,4 @@ enum
AVHUFF_CODEC_DECOMPRESS_CONFIG = 1
};
-#endif // MAME_UTIL_CHDCODEC_H
+#endif // MAME_LIB_UTIL_CHDCODEC_H
diff --git a/src/lib/util/client_ws.hpp b/src/lib/util/client_ws.hpp
index f3c4ded6bb2..04af5026014 100644
--- a/src/lib/util/client_ws.hpp
+++ b/src/lib/util/client_ws.hpp
@@ -1,5 +1,21 @@
// license:MIT
// copyright-holders:Ole Christian Eidheim, Miodrag Milanovic
+
+/*
+ Changes to the websocket client
+
+ July 2022: (Michael Zapf, patch by Pete Eberlein [peberlein])
+ Symptom: During the initial handshake, the ws client reads the HTTP header
+ from the server and then waits for more data to come. In cases where the
+ websocket server sends data immediately after the handshake to the client,
+ these data may already be available in the streambuf without triggering
+ a new reception event, so the client may wait forever for a new reception.
+
+ Correction: When going into the async_read, the client only waits for so
+ many bytes as to satisfy the next read from the streambuf. For this end,
+ the Message::needed_for method has been added. Also, leftover bytes are
+ copied to the next message instance.
+*/
#ifndef CLIENT_WS_HPP
#define CLIENT_WS_HPP
@@ -11,6 +27,7 @@
#include <atomic>
#include <list>
#include "crypto.hpp"
+#include "base64.hpp"
#ifndef CASE_INSENSITIVE_EQUALS_AND_HASH
#define CASE_INSENSITIVE_EQUALS_AND_HASH
@@ -65,7 +82,7 @@ namespace webpp {
unsigned short remote_endpoint_port;
private:
- explicit Connection(socket_type* socket): remote_endpoint_port(0), socket(socket), strand(socket->get_io_context()), closed(false) { }
+ explicit Connection(asio::io_context &context, socket_type* socket): remote_endpoint_port(0), socket(socket), strand(context), closed(false) { }
class SendData {
public:
@@ -122,16 +139,28 @@ namespace webpp {
size_t size() const {
return length;
}
- std::string string() const {
- std::stringstream ss;
- ss << rdbuf();
- return ss.str();
+ std::string string() {
+ if (s.size() != length) {
+ asio::streambuf::const_buffers_type bufs = streambuf.data();
+ s.assign(asio::buffers_begin(bufs), asio::buffers_begin(bufs)+length);
+ streambuf.consume(length);
+ }
+ return s;
+ }
+
+ // Return the number of bytes that are missing in the streambuf
+ // before n bytes can be read from it
+ size_t needed_for(size_t n) const {
+ if (streambuf.size() >= n)
+ return 0;
+ return n - streambuf.size();
}
private:
Message(): std::istream(&streambuf), fin_rsv_opcode(0), length(0) { }
size_t length;
asio::streambuf streambuf;
+ std::string s;
};
std::function<void()> on_open;
@@ -345,10 +374,10 @@ namespace webpp {
}
void read_message(const std::shared_ptr<Message> &message) {
- asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(2),
+ asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(message->needed_for(2)),
[this, message](const std::error_code& ec, size_t bytes_transferred) {
if(!ec) {
- if(bytes_transferred==0) { //TODO: This might happen on server at least, might also happen here
+ if(message->streambuf.size()+bytes_transferred==0) { //TODO: This might happen on server at least, might also happen here
read_message(message);
return;
}
@@ -362,7 +391,7 @@ namespace webpp {
if(first_bytes[1]>=128) {
const std::string reason("message from server masked");
auto kept_connection=connection;
- send_close(1002, reason, [this, kept_connection](const std::error_code& /*ec*/) {});
+ send_close(1002, reason, [](const std::error_code& /*ec*/) {});
if(on_close)
on_close(1002, reason);
return;
@@ -372,7 +401,7 @@ namespace webpp {
if(length==126) {
//2 next bytes is the size of content
- asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(2),
+ asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(message->needed_for(2)),
[this, message]
(const std::error_code& ec, size_t /*bytes_transferred*/) {
if(!ec) {
@@ -394,7 +423,7 @@ namespace webpp {
}
else if(length==127) {
//8 next bytes is the size of content
- asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(8),
+ asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(message->needed_for(8)),
[this, message]
(const std::error_code& ec, size_t /*bytes_transferred*/) {
if(!ec) {
@@ -425,7 +454,7 @@ namespace webpp {
}
void read_message_content(const std::shared_ptr<Message> &message) {
- asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(message->length),
+ asio::async_read(*connection->socket, message->streambuf, asio::transfer_exactly(message->needed_for(message->length)),
[this, message]
(const std::error_code& ec, size_t /*bytes_transferred*/) {
if(!ec) {
@@ -440,7 +469,7 @@ namespace webpp {
auto reason=message->string();
auto kept_connection=connection;
- send_close(status, reason, [this, kept_connection](const std::error_code& /*ec*/) {});
+ send_close(status, reason, [](const std::error_code& /*ec*/) {});
if(on_close)
on_close(status, reason);
return;
@@ -457,6 +486,10 @@ namespace webpp {
//Next message
std::shared_ptr<Message> next_message(new Message());
+ //Copy any leftover data in the streambuf into the next message
+ next_message->streambuf.commit(buffer_copy(
+ next_message->streambuf.prepare(message->streambuf.size()),
+ message->streambuf.data()));
read_message(next_message);
}
else if(on_error)
@@ -488,7 +521,7 @@ namespace webpp {
resolver->async_resolve(query, [this]
(const std::error_code &ec, asio::ip::tcp::resolver::iterator it){
if(!ec) {
- connection=std::shared_ptr<Connection>(new Connection(new WS(*io_context)));
+ connection=std::shared_ptr<Connection>(new Connection(*io_context, new WS(*io_context)));
asio::async_connect(*connection->socket, it, [this]
(const std::error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){
diff --git a/src/lib/util/client_wss.hpp b/src/lib/util/client_wss.hpp
index 6ff51658f30..128394f5f4e 100644
--- a/src/lib/util/client_wss.hpp
+++ b/src/lib/util/client_wss.hpp
@@ -45,7 +45,7 @@ namespace webpp {
resolver->async_resolve(query, [this]
(const std::error_code &ec, asio::ip::tcp::resolver::iterator it){
if(!ec) {
- connection=std::shared_ptr<Connection>(new Connection(new WSS(*io_context, context)));
+ connection=std::shared_ptr<Connection>(new Connection(*io_context, new WSS(*io_context, context)));
asio::async_connect(connection->socket->lowest_layer(), it, [this]
(const std::error_code &ec, asio::ip::tcp::resolver::iterator /*it*/){
diff --git a/src/lib/util/corealloc.cpp b/src/lib/util/corealloc.cpp
new file mode 100644
index 00000000000..9d14671000f
--- /dev/null
+++ b/src/lib/util/corealloc.cpp
@@ -0,0 +1,34 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#include "corealloc.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <new>
+
+
+#ifdef MAME_DEBUG
+
+std::uint8_t g_mame_new_prefill_byte(0xcd);
+
+void *operator new(std::size_t n)
+{
+ void *const result(std::malloc(n));
+ if (result)
+ {
+ std::fill_n(reinterpret_cast<std::uint8_t *>(result), n, g_mame_new_prefill_byte);
+ return result;
+ }
+ else
+ {
+ throw std::bad_alloc();
+ }
+}
+
+void operator delete(void *ptr) noexcept
+{
+ std::free(ptr);
+}
+
+#endif // MAME_DEBUG
diff --git a/src/lib/util/corealloc.h b/src/lib/util/corealloc.h
index 12dea2fce67..c6c954fa2cf 100644
--- a/src/lib/util/corealloc.h
+++ b/src/lib/util/corealloc.h
@@ -8,93 +8,42 @@
***************************************************************************/
-#pragma once
-
#ifndef MAME_LIB_UTIL_COREALLOC_H
#define MAME_LIB_UTIL_COREALLOC_H
-#include "osdcore.h"
-
-#include <stdlib.h>
+#pragma once
+#include <cassert>
#include <cstddef>
#include <cstring>
-#include <new>
#include <memory>
+#include <new>
#include <type_traits>
-#include <utility>
-
-
-
-//**************************************************************************
-// MACROS
-//**************************************************************************
-
-// global allocation helpers -- use these instead of new and delete
-#define global_alloc(Type) new Type
-#define global_alloc_nothrow(Type) new (std::nothrow) Type
-#define global_alloc_array(Type, Num) new Type[Num]
-#define global_alloc_array_nothrow(Type, Num) new (std::nothrow) Type[Num]
-#define global_free(Ptr) do { delete Ptr; } while (0)
-#define global_free_array(Ptr) do { delete[] Ptr; } while (0)
-
-
-template<typename T, typename... Params>
-inline T* global_alloc_clear(Params &&... args)
-{
- void *const ptr = ::operator new(sizeof(T)); // allocate memory
- std::memset(ptr, 0, sizeof(T));
- return new(ptr) T(std::forward<Params>(args)...);
-}
-
-template<typename T>
-inline T* global_alloc_array_clear(std::size_t num)
-{
- auto const size = sizeof(T) * num;
- void *const ptr = new unsigned char[size]; // allocate memory
- std::memset(ptr, 0, size);
- return new(ptr) T[num]();
-}
+// global allocation helpers
+namespace util {
-template<typename Tp> struct MakeUniqClearT { typedef std::unique_ptr<Tp> single_object; };
+namespace detail {
-template<typename Tp> struct MakeUniqClearT<Tp[]> { typedef std::unique_ptr<Tp[]> array; };
+template <typename Tp> struct make_unique_clear_traits { };
+template <typename Tp> struct make_unique_clear_traits<Tp []> { using unbounded_array_ptr = std::unique_ptr<Tp []>; };
+template <typename Tp, size_t Bound> struct make_unique_clear_traits<Tp [Bound]> { };
-template<typename Tp, size_t Bound> struct MakeUniqClearT<Tp[Bound]> { struct invalid_type { }; };
-
-/// make_unique_clear for single objects
-template<typename Tp, typename... Params>
-inline typename MakeUniqClearT<Tp>::single_object make_unique_clear(Params&&... args)
-{
- void *const ptr = ::operator new(sizeof(Tp)); // allocate memory
- std::memset(ptr, 0, sizeof(Tp));
- return std::unique_ptr<Tp>(new(ptr) Tp(std::forward<Params>(args)...));
-}
+} // namespace detail
/// make_unique_clear for arrays of unknown bound
-template<typename Tp>
-inline typename MakeUniqClearT<Tp>::array make_unique_clear(size_t num)
+template <typename Tp>
+inline typename detail::make_unique_clear_traits<Tp>::unbounded_array_ptr make_unique_clear(size_t num)
{
- auto size = sizeof(std::remove_extent_t<Tp>) * num;
- unsigned char* ptr = new unsigned char[size]; // allocate memory
+ static_assert(std::is_trivially_constructible_v<std::remove_extent_t<Tp> >, "make_unique_clear is only suitable for trivially constructible types");
+ auto const size = sizeof(std::remove_extent_t<Tp>) * num;
+ unsigned char* ptr = new unsigned char [size]; // allocate memory - this assumes new expression overhead is the same for all array types
std::memset(ptr, 0, size);
- return std::unique_ptr<Tp>(new(ptr) std::remove_extent_t<Tp>[num]());
-}
-
-template<typename Tp, unsigned char F>
-inline typename MakeUniqClearT<Tp>::array make_unique_clear(size_t num)
-{
- auto size = sizeof(std::remove_extent_t<Tp>) * num;
- unsigned char* ptr = new unsigned char[size]; // allocate memory
- std::memset(ptr, F, size);
- return std::unique_ptr<Tp>(new(ptr) std::remove_extent_t<Tp>[num]());
+ return std::unique_ptr<Tp>(new (ptr) std::remove_extent_t<Tp> [num]);
}
-/// Disable make_unique_clear for arrays of known bound
-template<typename Tp, typename... Params>
-inline typename MakeUniqClearT<Tp>::invalid_type make_unique_clear(Params&&...) = delete;
+} // namespace util
#endif // MAME_LIB_UTIL_COREALLOC_H
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp
index 78a484c5511..74a742b0306 100644
--- a/src/lib/util/corefile.cpp
+++ b/src/lib/util/corefile.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
- corefile.c
+ corefile.cpp
File access functions.
@@ -10,17 +10,15 @@
#include "corefile.h"
+#include "coretmpl.h"
+#include "osdcore.h"
#include "unicode.h"
#include "vecstream.h"
-#include <zlib.h>
-
-#include <algorithm>
#include <cassert>
#include <cstring>
#include <iterator>
-
-#include <ctype.h>
+#include <limits>
namespace util {
@@ -41,128 +39,32 @@ namespace {
TYPE DEFINITIONS
***************************************************************************/
-class zlib_data
+class core_proxy_file : public core_file
{
public:
- typedef std::unique_ptr<zlib_data> ptr;
-
- static int start_compression(int level, std::uint64_t offset, ptr &data)
- {
- ptr result(new zlib_data(offset));
- result->reset_output();
- auto const zerr = deflateInit(&result->m_stream, level);
- result->m_compress = (Z_OK == zerr);
- if (result->m_compress) data = std::move(result);
- return zerr;
- }
- static int start_decompression(std::uint64_t offset, ptr &data)
- {
- ptr result(new zlib_data(offset));
- auto const zerr = inflateInit(&result->m_stream);
- result->m_decompress = (Z_OK == zerr);
- if (result->m_decompress) data = std::move(result);
- return zerr;
- }
-
- ~zlib_data()
- {
- if (m_compress) deflateEnd(&m_stream);
- else if (m_decompress) inflateEnd(&m_stream);
- }
-
- std::size_t buffer_size() const { return sizeof(m_buffer); }
- void const *buffer_data() const { return m_buffer; }
- void *buffer_data() { return m_buffer; }
-
- // general-purpose output buffer manipulation
- bool output_full() const { return 0 == m_stream.avail_out; }
- std::size_t output_space() const { return m_stream.avail_out; }
- void set_output(void *data, std::uint32_t size)
- {
- m_stream.next_out = reinterpret_cast<Bytef *>(data);
- m_stream.avail_out = size;
- }
-
- // working with output to the internal buffer
- bool has_output() const { return m_stream.avail_out != sizeof(m_buffer); }
- std::size_t output_size() const { return sizeof(m_buffer) - m_stream.avail_out; }
- void reset_output()
- {
- m_stream.next_out = m_buffer;
- m_stream.avail_out = sizeof(m_buffer);
- }
+ core_proxy_file(core_file &file) noexcept : m_file(file) { }
- // general-purpose input buffer manipulation
- bool has_input() const { return 0 != m_stream.avail_in; }
- std::size_t input_size() const { return m_stream.avail_in; }
- void set_input(void const *data, std::uint32_t size)
- {
- m_stream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(data));
- m_stream.avail_in = size;
- }
+ 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); }
- // working with input from the internal buffer
- void reset_input(std::uint32_t size)
- {
- m_stream.next_in = m_buffer;
- m_stream.avail_in = size;
- }
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_some(buffer, length, actual); }
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_some_at(offset, buffer, length, actual); }
- int compress() { assert(m_compress); return deflate(&m_stream, Z_NO_FLUSH); }
- int finalise() { assert(m_compress); return deflate(&m_stream, Z_FINISH); }
- int decompress() { assert(m_decompress); return inflate(&m_stream, Z_SYNC_FLUSH); }
+ 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_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_some(buffer, length, actual); }
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_some_at(offset, buffer, length, actual); }
- std::uint64_t realoffset() const { return m_realoffset; }
- void add_realoffset(std::uint32_t increment) { m_realoffset += increment; }
-
- bool is_nextoffset(std::uint64_t value) const { return m_nextoffset == value; }
- void add_nextoffset(std::uint32_t increment) { m_nextoffset += increment; }
-
-private:
- zlib_data(std::uint64_t offset)
- : m_compress(false)
- , m_decompress(false)
- , m_realoffset(offset)
- , m_nextoffset(offset)
- {
- m_stream.zalloc = Z_NULL;
- m_stream.zfree = Z_NULL;
- m_stream.opaque = Z_NULL;
- m_stream.avail_in = m_stream.avail_out = 0;
- }
-
- bool m_compress, m_decompress;
- z_stream m_stream;
- std::uint8_t m_buffer[1024];
- std::uint64_t m_realoffset;
- std::uint64_t m_nextoffset;
-};
-
-
-class core_proxy_file : public core_file
-{
-public:
- core_proxy_file(core_file &file) : m_file(file) { }
- virtual ~core_proxy_file() override { }
- virtual osd_file::error compress(int level) override { return m_file.compress(level); }
-
- virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); }
- virtual std::uint64_t tell() const override { return m_file.tell(); }
virtual bool eof() const override { return m_file.eof(); }
- virtual std::uint64_t size() const override { return m_file.size(); }
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override { return m_file.read(buffer, length); }
virtual int getc() override { return m_file.getc(); }
virtual int ungetc(int c) override { return m_file.ungetc(c); }
virtual char *gets(char *s, int n) override { return m_file.gets(s, n); }
- virtual const void *buffer() override { return m_file.buffer(); }
-
- virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); }
- virtual int puts(const char *s) override { return m_file.puts(s); }
- virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); }
- virtual osd_file::error truncate(std::uint64_t offset) override { return m_file.truncate(offset); }
- virtual osd_file::error flush() override { return m_file.flush(); }
+ virtual int puts(std::string_view s) override { return m_file.puts(s); }
+ virtual int vprintf(util::format_argument_pack<char> const &args) override { return m_file.vprintf(args); }
+ virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); }
private:
core_file &m_file;
@@ -185,8 +87,8 @@ public:
virtual int getc() override;
virtual int ungetc(int c) override;
virtual char *gets(char *s, int n) override;
- virtual int puts(char const *s) override;
- virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override;
+ virtual int puts(std::string_view s) override;
+ virtual int vprintf(util::format_argument_pack<char> const &args) override;
protected:
core_text_file(std::uint32_t openflags)
@@ -198,12 +100,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
@@ -215,53 +117,74 @@ private:
};
-class core_in_memory_file : public core_text_file
+class core_basic_file : public core_text_file
{
public:
- core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy)
+ 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_index; return std::error_condition(); }
+ virtual std::error_condition length(std::uint64_t &result) noexcept override { result = m_size; return std::error_condition(); }
+
+ virtual bool eof() const override;
+
+protected:
+ core_basic_file(std::uint32_t openflags, std::uint64_t length) noexcept
: core_text_file(openflags)
+ , m_index(0)
+ , m_size(length)
+ {
+ }
+
+ std::uint64_t index() const noexcept { return m_index; }
+ void add_offset(std::size_t increment) noexcept { m_index += increment; m_size = (std::max)(m_size, m_index); }
+ std::uint64_t size() const noexcept { return m_size; }
+ void set_size(std::uint64_t value) noexcept { m_size = 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) noexcept;
+
+
+private:
+ std::uint64_t m_index; // current file offset
+ std::uint64_t m_size; // total file length
+};
+
+class core_in_memory_file final : public core_basic_file
+{
+public:
+ core_in_memory_file(std::uint32_t openflags, void const *data, std::size_t length, bool copy) noexcept
+ : core_basic_file(openflags, length)
, m_data_allocated(false)
, m_data(copy ? nullptr : data)
- , m_offset(0)
- , m_length(length)
{
if (copy)
{
void *const buf = allocate();
- if (buf) std::memcpy(buf, data, length);
+ if (buf)
+ std::memcpy(buf, data, length);
}
}
~core_in_memory_file() override { purge(); }
- virtual osd_file::error compress(int level) override { return osd_file::error::INVALID_ACCESS; }
- virtual int seek(std::int64_t offset, int whence) override;
- virtual std::uint64_t tell() const override { return m_offset; }
- virtual bool eof() const override;
- virtual std::uint64_t size() const override { return m_length; }
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
- virtual void const *buffer() override { return m_data; }
+ 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_some(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_some_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 std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; }
- virtual osd_file::error truncate(std::uint64_t offset) override;
- virtual osd_file::error flush() override { clear_putback(); return osd_file::error::NONE; }
+ void const *buffer() const { return m_data; }
-protected:
- core_in_memory_file(std::uint32_t openflags, std::uint64_t length)
- : core_text_file(openflags)
- , m_data_allocated(false)
- , m_data(nullptr)
- , m_offset(0)
- , m_length(length)
- {
- }
+ virtual std::error_condition truncate(std::uint64_t offset) override;
- bool is_loaded() const { return nullptr != m_data; }
- void *allocate()
+protected:
+ void *allocate() noexcept
{
- if (m_data) return nullptr;
- void *data = malloc(m_length);
+ if (m_data || (std::numeric_limits<std::size_t>::max() < size()))
+ return nullptr;
+ void *data = malloc(size());
if (data)
{
m_data_allocated = true;
@@ -269,140 +192,124 @@ protected:
}
return data;
}
- void purge()
+
+ void purge() noexcept
{
- if (m_data && m_data_allocated) free(const_cast<void *>(m_data));
+ if (m_data && m_data_allocated)
+ free(const_cast<void *>(m_data));
m_data_allocated = false;
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); }
-
- 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);
-
private:
bool m_data_allocated; // was the data allocated by us?
void const * m_data; // file data, if RAM-based
- std::uint64_t m_offset; // current file offset
- std::uint64_t m_length; // total file length
};
-class core_osd_file : public core_in_memory_file
+class core_osd_file final : public core_basic_file
{
public:
core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length)
- : core_in_memory_file(openmode, length)
+ : core_basic_file(openmode, length)
, m_file(std::move(file))
- , m_zdata()
- , m_bufferbase(0)
- , m_bufferbytes(0)
{
}
~core_osd_file() override;
- virtual osd_file::error compress(int level) override;
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual int seek(std::int64_t offset, int whence) override;
+ virtual std::error_condition finalize() noexcept override;
+ virtual std::error_condition flush() noexcept override;
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual std::uint32_t read(void *buffer, std::uint32_t length) override;
- virtual void const *buffer() override;
-
- virtual std::uint32_t write(void const *buffer, std::uint32_t length) override;
- virtual osd_file::error truncate(std::uint64_t offset) override;
- virtual osd_file::error flush() override;
+ virtual std::error_condition truncate(std::uint64_t offset) 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::error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual);
- osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual);
-
osd_file::ptr m_file; // OSD file handle
- zlib_data::ptr m_zdata; // compression data
- std::uint64_t m_bufferbase; // base offset of internal buffer
- std::uint32_t m_bufferbytes; // bytes currently loaded into buffer
+ std::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
};
-/***************************************************************************
- core_text_file
-***************************************************************************/
+//**************************************************************************
+// core_text_file
+//**************************************************************************
-/*-------------------------------------------------
- getc - read a character from a file
--------------------------------------------------*/
+//-------------------------------------------------
+// getc - read a character from a file
+//-------------------------------------------------
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)
+ std::uint8_t bom[4];
+ auto const [err, readlen] = read(*this, bom, 4); // FIXME: check for errors
+ if (readlen == 4)
{
- 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;
+ 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];
- char32_t uchar = char32_t(~0);
+ char32_t uchar = ~char32_t(0);
switch (m_text_type)
{
default:
case text_file_type::OSD:
{
char default_buffer[16];
- auto const readlen = read(default_buffer, sizeof(default_buffer));
+ auto const [err, readlen] = read(*this, default_buffer, sizeof(default_buffer));
if (readlen > 0)
{
auto const charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0]));
@@ -414,7 +321,7 @@ 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));
+ auto const [err, readlen] = read(*this, utf8_buffer, sizeof(utf8_buffer));
if (readlen > 0)
{
auto const charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0]));
@@ -425,7 +332,7 @@ int core_text_file::getc()
case text_file_type::UTF16BE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ auto const [err, readlen] = read(*this, utf16_buffer, sizeof(utf16_buffer));
if (readlen > 0)
{
auto const charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -436,7 +343,7 @@ int core_text_file::getc()
case text_file_type::UTF16LE:
{
- auto const readlen = read(utf16_buffer, sizeof(utf16_buffer));
+ auto const [err, readlen] = read(*this, utf16_buffer, sizeof(utf16_buffer));
if (readlen > 0)
{
auto const charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0]));
@@ -446,13 +353,21 @@ 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
+ auto const [err, readlen] = read(*this, &uchar, sizeof(uchar));
+ 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
+ auto const [err, readlen] = read(*this, &uchar, sizeof(uchar));
+ if (sizeof(uchar) == readlen)
+ uchar = little_endianize_int32(uchar);
+ }
break;
}
@@ -460,40 +375,41 @@ int core_text_file::getc()
{
// place the new character in the ring buffer
m_back_char_head = 0;
- m_back_char_tail = utf8_from_uchar(m_back_chars, ARRAY_LENGTH(m_back_chars), uchar);
+ m_back_char_tail = utf8_from_uchar(m_back_chars, std::size(m_back_chars), uchar);
//assert(file->back_char_tail != -1);
}
}
// now read from the ring buffer
+ int result;
if (m_back_char_head == m_back_char_tail)
result = EOF;
else
{
result = m_back_chars[m_back_char_head++];
- m_back_char_head %= ARRAY_LENGTH(m_back_chars);
+ m_back_char_head %= std::size(m_back_chars);
}
return result;
}
-/*-------------------------------------------------
- ungetc - put back a character read from a
- file
--------------------------------------------------*/
+//-------------------------------------------------
+// ungetc - put back a character read from a
+// file
+//-------------------------------------------------
int core_text_file::ungetc(int c)
{
m_back_chars[m_back_char_tail++] = char(c);
- m_back_char_tail %= ARRAY_LENGTH(m_back_chars);
+ m_back_char_tail %= std::size(m_back_chars);
return c;
}
-/*-------------------------------------------------
- gets - read a line from a text file
--------------------------------------------------*/
+//-------------------------------------------------
+// gets - read a line from a text file
+//-------------------------------------------------
char *core_text_file::gets(char *s, int n)
{
@@ -538,28 +454,39 @@ 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(char const *s)
+int core_text_file::puts(std::string_view s)
{
+ // TODO: what to do about write errors?
+ // 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
- while (*s != '\0')
+ for (char ch : s)
{
- if (*s == '\n')
+ if (ch == '\n')
{
if (CRLF == 1) // CR only
*pconvbuf++ = 13;
@@ -572,126 +499,120 @@ int core_text_file::puts(char const *s)
}
}
else
- *pconvbuf++ = *s;
- s++;
+ *pconvbuf++ = ch;
// if we overflow, break into chunks
- if (pconvbuf >= convbuf + ARRAY_LENGTH(convbuf) - 10)
+ if (pconvbuf >= convbuf + std::size(convbuf) - 10)
{
- count += write(convbuf, pconvbuf - convbuf);
+ auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here
+ count += written;
pconvbuf = convbuf;
}
}
// final flush
if (pconvbuf != convbuf)
- count += write(convbuf, pconvbuf - convbuf);
+ {
+ auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here
+ count += written;
+ }
return count;
}
-/*-------------------------------------------------
- vprintf - vfprintf to a text file
--------------------------------------------------*/
+//-------------------------------------------------
+// vprintf - vfprintf to a text file
+//-------------------------------------------------
-int core_text_file::vprintf(util::format_argument_pack<std::ostream> const &args)
+int core_text_file::vprintf(util::format_argument_pack<char> const &args)
{
m_printf_buffer.clear();
m_printf_buffer.reserve(1024);
m_printf_buffer.seekp(0, ovectorstream::beg);
- util::stream_format<std::ostream, std::ostream>(m_printf_buffer, args);
- m_printf_buffer.put('\0');
- return puts(&m_printf_buffer.vec()[0]);
+ util::stream_format(m_printf_buffer, args);
+ return puts(buf_to_string_view(m_printf_buffer));
}
-/***************************************************************************
- core_in_memory_file
-***************************************************************************/
+//**************************************************************************
+// core_basic_file
+//**************************************************************************
-/*-------------------------------------------------
- seek - seek within a file
--------------------------------------------------*/
+//-------------------------------------------------
+// seek - seek within a file
+//-------------------------------------------------
-int core_in_memory_file::seek(std::int64_t offset, int whence)
+std::error_condition core_basic_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:
- m_offset = offset;
- break;
+ if (0 > offset)
+ return std::errc::invalid_argument;
+ m_index = offset;
+ return std::error_condition();
case SEEK_CUR:
- m_offset += offset;
- break;
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_index)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_index)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_index += offset;
+ return std::error_condition();
case SEEK_END:
- m_offset = m_length + offset;
- break;
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_size)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_index = m_size + offset;
+ return std::error_condition();
+
+ default:
+ return std::errc::invalid_argument;
}
- return 0;
}
-/*-------------------------------------------------
- eof - return 1 if we're at the end of file
--------------------------------------------------*/
+//-------------------------------------------------
+// eof - return true if we're at the end of file
+//-------------------------------------------------
-bool core_in_memory_file::eof() const
+bool core_basic_file::eof() const
{
// check for buffered chars
if (has_putback())
- return 0;
+ return false;
// if the offset == length, we're at EOF
- return (m_offset >= m_length);
+ return (m_index >= m_size);
}
-/*-------------------------------------------------
- read - read from a file
--------------------------------------------------*/
+//-------------------------------------------------
+// safe_buffer_copy - copy safely from one
+// bounded buffer to another
+//-------------------------------------------------
-std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length)
-{
- 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;
-}
-
-
-/*-------------------------------------------------
- truncate - truncate a file
--------------------------------------------------*/
-
-osd_file::error core_in_memory_file::truncate(std::uint64_t offset)
-{
- if (m_length < offset)
- return osd_file::error::FAILURE;
-
- // adjust to new length and offset
- set_length(offset);
- return osd_file::error::NONE;
-}
-
-
-/*-------------------------------------------------
- safe_buffer_copy - copy safely from one
- bounded buffer to another
--------------------------------------------------*/
-
-std::size_t core_in_memory_file::safe_buffer_copy(
+std::size_t core_basic_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;
@@ -708,605 +629,412 @@ std::size_t core_in_memory_file::safe_buffer_copy(
-/***************************************************************************
- core_osd_file
-***************************************************************************/
+//**************************************************************************
+// core_in_memory_file
+//**************************************************************************
-/*-------------------------------------------------
- closes a file
--------------------------------------------------*/
+//-------------------------------------------------
+// read - read from a file
+//-------------------------------------------------
-core_osd_file::~core_osd_file()
+std::error_condition core_in_memory_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
- // close files and free memory
- if (m_zdata)
- core_osd_file::compress(FCOMPRESS_NONE);
-}
-
+ clear_putback();
-/*-------------------------------------------------
- compress - enable/disable streaming file
- compression via zlib; level is 0 to disable
- compression, or up to 9 for max compression
--------------------------------------------------*/
+ // handle RAM-based files
+ if (index() < size())
+ actual = safe_buffer_copy(m_data, std::size_t(index()), std::size_t(size()), buffer, 0, length);
+ else
+ actual = 0U;
+ add_offset(actual);
+ return std::error_condition();
+}
-osd_file::error core_osd_file::compress(int level)
+std::error_condition core_in_memory_file::read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
{
- osd_file::error result = osd_file::error::NONE;
-
- // can only do this for read-only and write-only cases
- if (read_access() && write_access())
- return osd_file::error::INVALID_ACCESS;
+ clear_putback();
- // if we have been compressing, flush and free the data
- if (m_zdata && (level == FCOMPRESS_NONE))
- {
- int zerr = Z_OK;
+ // handle RAM-based files
+ if (offset < size())
+ actual = safe_buffer_copy(m_data, std::size_t(offset), std::size_t(size()), buffer, 0, length);
+ else
+ actual = 0U;
+ return std::error_condition();
+}
- // flush any remaining data if we are writing
- while (write_access() && (zerr != Z_STREAM_END))
- {
- // deflate some more
- zerr = m_zdata->finalise();
- if ((zerr != Z_STREAM_END) && (zerr != Z_OK))
- {
- result = osd_file::error::INVALID_DATA;
- break;
- }
- // write the data
- if (m_zdata->has_output())
- {
- std::uint32_t actualdata;
- auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata);
- if (filerr != osd_file::error::NONE)
- break;
- m_zdata->add_realoffset(actualdata);
- m_zdata->reset_output();
- }
- }
+//-------------------------------------------------
+// truncate - truncate a file
+//-------------------------------------------------
- // free memory
- m_zdata.reset();
- }
+std::error_condition core_in_memory_file::truncate(std::uint64_t offset)
+{
+ if (size() < offset)
+ return std::errc::io_error; // TODO: revisit this error code
- // if we are just starting to compress, allocate a new buffer
- if (!m_zdata && (level > FCOMPRESS_NONE))
- {
- int zerr;
+ // adjust to new length and offset
+ set_size(offset);
+ return std::error_condition();
+}
- // initialize the stream and compressor
- if (write_access())
- zerr = zlib_data::start_compression(level, offset(), m_zdata);
- else
- zerr = zlib_data::start_decompression(offset(), m_zdata);
- // on error, return an error
- if (zerr != Z_OK)
- return osd_file::error::OUT_OF_MEMORY;
+//**************************************************************************
+// core_osd_file
+//**************************************************************************
- // flush buffers
- m_bufferbytes = 0;
- }
+//-------------------------------------------------
+// closes a file
+//-------------------------------------------------
- return result;
+core_osd_file::~core_osd_file()
+{
+ // close files and free memory
}
-/*-------------------------------------------------
- seek - seek within a file
--------------------------------------------------*/
+//-------------------------------------------------
+// read - read from a file
+//-------------------------------------------------
-int core_osd_file::seek(std::int64_t offset, int whence)
+std::error_condition core_osd_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
- // error if compressing
- if (m_zdata)
- return 1;
- else
- return core_in_memory_file::seek(offset, whence);
+ // since osd_file works like pread/pwrite, implement in terms of read_at
+ // core_osd_file is declared final, so a derived class can't interfere
+ std::error_condition err = read_some_at(index(), buffer, length, actual);
+ add_offset(actual);
+ return err;
}
-/*-------------------------------------------------
- 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_some_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);
+ if (!m_file)
+ {
+ actual = 0U;
+ return std::errc::bad_file_descriptor;
+ }
// 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;
- osd_or_zlib_read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes);
-
- // do a bounded copy from the buffer to the destination
- bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length);
+ 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;
- osd_or_zlib_read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read);
- bytes_read += new_bytes_read;
+ // read the remainder directly from the file - may need to return short 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)
+ actual += bytes_read;
}
}
- // return the number of bytes read
- add_offset(bytes_read);
- return bytes_read;
+ // return any errors
+ return err;
}
-/*-------------------------------------------------
- buffer - return a pointer to the file buffer;
- if it doesn't yet exist, load the file into
- RAM first
--------------------------------------------------*/
+//-------------------------------------------------
+// write - write to a file
+//-------------------------------------------------
-void const *core_osd_file::buffer()
+std::error_condition core_osd_file::write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept
{
- // if we already have data, just return it
- if (!is_loaded() && length())
- {
- // allocate some memory
- void *buf = allocate();
- if (!buf) return nullptr;
-
- // read the file
- std::uint32_t read_length = 0;
- auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length);
- if ((filerr != osd_file::error::NONE) || (read_length != length()))
- purge();
- else
- {
- // close the file because we don't need it anymore
- m_file.reset();
- }
- }
- return core_in_memory_file::buffer();
+ // since osd_file works like pread/pwrite, implement in terms of write_at
+ // core_osd_file is declared final, so a derived class can't interfere
+ std::error_condition err = write_some_at(index(), buffer, length, actual);
+ add_offset(actual);
+ return err;
}
-/*-------------------------------------------------
- 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_some_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);
-
// 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;
- osd_or_zlib_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 return short 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);
+ std::uint32_t bytes_written;
+ std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written);
+ if (err)
+ {
+ // bytes written not valid on error
+ actual = 0U;
+ }
+ else
+ {
+ assert(chunk >= bytes_written);
+ offset += bytes_written;
+ actual = bytes_written;
+ set_size((std::max)(size(), offset));
+ }
+ return err;
}
-/*-------------------------------------------------
- truncate - truncate a file
--------------------------------------------------*/
+//-------------------------------------------------
+// truncate - truncate a file
+//-------------------------------------------------
-osd_file::error core_osd_file::truncate(std::uint64_t offset)
+std::error_condition core_osd_file::truncate(std::uint64_t offset)
{
- if (is_loaded())
- return core_in_memory_file::truncate(offset);
-
// truncate file
- auto const err = m_file->truncate(offset);
- if (err != osd_file::error::NONE)
+ std::error_condition err = m_file->truncate(offset);
+ if (err)
return err;
// and adjust to new length and offset
- set_length(offset);
- return osd_file::error::NONE;
+ set_size(offset);
+ return std::error_condition();
}
-/*-------------------------------------------------
- flush - flush file buffers
--------------------------------------------------*/
+//-------------------------------------------------
+// flush - flush file buffers
+//-------------------------------------------------
-osd_file::error core_osd_file::flush()
+std::error_condition core_osd_file::finalize() noexcept
{
- if (is_loaded())
- return core_in_memory_file::flush();
+ return std::error_condition();
+}
+
+std::error_condition core_osd_file::flush() noexcept
+{
// flush any buffered char
clear_putback();
// invalidate any buffered data
- m_bufferbytes = 0;
+ m_bufferbytes = 0U;
return m_file->flush();
}
+} // anonymous namespace
-/*-------------------------------------------------
- osd_or_zlib_read - wrapper for osd_read that
- handles zlib-compressed data
--------------------------------------------------*/
-osd_file::error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual)
-{
- // if no compression, just pass through
- if (!m_zdata)
- return m_file->read(buffer, offset, length, actual);
-
- // if the offset doesn't match the next offset, fail
- if (!m_zdata->is_nextoffset(offset))
- return osd_file::error::INVALID_ACCESS;
-
- // set up the destination
- osd_file::error filerr = osd_file::error::NONE;
- m_zdata->set_output(buffer, length);
- while (!m_zdata->output_full())
- {
- // if we didn't make progress, report an error or the end
- if (m_zdata->has_input())
- {
- auto const zerr = m_zdata->decompress();
- if (Z_OK != zerr)
- {
- if (Z_STREAM_END != zerr) filerr = osd_file::error::INVALID_DATA;
- break;
- }
- }
- // fetch more data if needed
- if (!m_zdata->has_input())
- {
- std::uint32_t actualdata = 0;
- filerr = m_file->read(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), actualdata);
- if (filerr != osd_file::error::NONE) break;
- m_zdata->add_realoffset(actualdata);
- m_zdata->reset_input(actualdata);
- if (!m_zdata->has_input()) break;
- }
- }
-
- // adjust everything
- actual = length - m_zdata->output_space();
- m_zdata->add_nextoffset(actual);
- return filerr;
-}
+//**************************************************************************
+// core_file
+//**************************************************************************
+//-------------------------------------------------
+// open - open a file for access and
+// return an error code
+//-------------------------------------------------
-/*-------------------------------------------------
- osd_or_zlib_write - wrapper for osd_write that
- handles zlib-compressed data
--------------------------------------------------*/
-
-/**
- * @fn osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual)
- *
- * @brief OSD or zlib write.
- *
- * @param buffer The buffer.
- * @param offset The offset.
- * @param length The length.
- * @param [in,out] actual The actual.
- *
- * @return A osd_file::error.
- */
-
-osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual)
+std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) noexcept
{
- // if no compression, just pass through
- if (!m_zdata)
- return m_file->write(buffer, offset, length, actual);
-
- // if the offset doesn't match the next offset, fail
- if (!m_zdata->is_nextoffset(offset))
- return osd_file::error::INVALID_ACCESS;
-
- // set up the source
- m_zdata->set_input(buffer, length);
- while (m_zdata->has_input())
- {
- // if we didn't make progress, report an error or the end
- auto const zerr = m_zdata->compress();
- if (zerr != Z_OK)
- {
- actual = length - m_zdata->input_size();
- m_zdata->add_nextoffset(actual);
- return osd_file::error::INVALID_DATA;
- }
+ // 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;
- // write more data if we are full up
- if (m_zdata->output_full())
- {
- std::uint32_t actualdata = 0;
- auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata);
- if (filerr != osd_file::error::NONE)
- return filerr;
- m_zdata->add_realoffset(actualdata);
- m_zdata->reset_output();
- }
- }
+ try { file = std::make_unique<core_osd_file>(openflags, std::move(f), length); }
+ catch (...) { return std::errc::not_enough_memory; }
- // we wrote everything
- actual = length;
- m_zdata->add_nextoffset(actual);
- return osd_file::error::NONE;
+ return std::error_condition();
}
-} // anonymous namespace
-
-
-
-/***************************************************************************
- core_file
-***************************************************************************/
-/*-------------------------------------------------
- open - open a file for access and
- return an error code
--------------------------------------------------*/
+//-------------------------------------------------
+// open_ram - open a RAM-based buffer for file-
+// like access and return an error code
+//-------------------------------------------------
-osd_file::error core_file::open(std::string const &filename, 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
{
- try
- {
- // attempt to open the file
- osd_file::ptr f;
- std::uint64_t length = 0;
- auto const filerr = osd_file::open(filename, openflags, f, length);
- if (filerr != osd_file::error::NONE)
- return filerr;
-
- file = std::make_unique<core_osd_file>(openflags, std::move(f), length);
- return osd_file::error::NONE;
- }
- catch (...)
- {
- return osd_file::error::OUT_OF_MEMORY;
- }
-}
+ // can only do this for read access
+ if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
+ return std::errc::invalid_argument;
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
-/*-------------------------------------------------
- open_ram - open a RAM-based buffer for file-
- like access and return an error code
--------------------------------------------------*/
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
-osd_file::error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file)
-{
- // can only do this for read access
- if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE))
- return osd_file::error::INVALID_ACCESS;
+ ptr result(new (std::nothrow) core_in_memory_file(openflags, data, length, false));
+ if (!result)
+ return std::errc::not_enough_memory;
- try
- {
- file.reset(new core_in_memory_file(openflags, data, length, false));
- return osd_file::error::NONE;
- }
- catch (...)
- {
- return osd_file::error::OUT_OF_MEMORY;
- }
+ file = std::move(result);
+ return std::error_condition();
}
-/*-------------------------------------------------
- open_ram_copy - open a copy of a RAM-based
- buffer for file-like access and return an
- error code
--------------------------------------------------*/
+//-------------------------------------------------
+// open_ram_copy - open a copy of a RAM-based
+// buffer for file-like access and return an
+// error code
+//-------------------------------------------------
-osd_file::error 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 osd_file::error::INVALID_ACCESS;
+ return std::errc::invalid_argument;
- try
- {
- ptr result(new core_in_memory_file(openflags, data, length, true));
- if (!result->buffer())
- return osd_file::error::OUT_OF_MEMORY;
+ // if length is non-zero, data must be non-null
+ if (length && !data)
+ return std::errc::invalid_argument;
- file = std::move(result);
- return osd_file::error::NONE;
- }
- catch (...)
- {
- return osd_file::error::OUT_OF_MEMORY;
- }
+ // platforms where size_t is larger than 64 bits are theoretically possible
+ if (std::uint64_t(length) != length)
+ return std::errc::file_too_large;
+
+ std::unique_ptr<core_in_memory_file> 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();
}
-/*-------------------------------------------------
- open_proxy - open a proxy to an existing file
- object and return an error code
--------------------------------------------------*/
+//-------------------------------------------------
+// open_proxy - open a proxy to an existing file
+// object and return an error code
+//-------------------------------------------------
-osd_file::error core_file::open_proxy(core_file &file, ptr &proxy)
+std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) noexcept
{
- try
- {
- proxy = std::make_unique<core_proxy_file>(file);
- return osd_file::error::NONE;
- }
- catch (...)
- {
- return osd_file::error::OUT_OF_MEMORY;
- }
+ ptr result(new (std::nothrow) core_proxy_file(file));
+ if (!result)
+ return std::errc::not_enough_memory;
+
+ proxy = std::move(result);
+ return std::error_condition();
}
-/*-------------------------------------------------
- closes a file
--------------------------------------------------*/
+//-------------------------------------------------
+// closes a file
+//-------------------------------------------------
core_file::~core_file()
{
}
-/*-------------------------------------------------
- load - open a file with the specified
- filename, read it into memory, and return a
- pointer
--------------------------------------------------*/
+//-------------------------------------------------
+// load - open a file with the specified
+// filename, read it into memory, and return a
+// pointer
+//-------------------------------------------------
-osd_file::error core_file::load(std::string const &filename, void **data, std::uint32_t &length)
+std::error_condition core_file::load(std::string_view filename, void **data, std::size_t &length) noexcept
{
- ptr file;
+ std::error_condition err;
// attempt to open the file
- auto const err = open(filename, OPEN_FLAG_READ, file);
- if (err != osd_file::error::NONE)
+ 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)
- return osd_file::error::OUT_OF_MEMORY;
+ 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
- *data = malloc(size);
- length = std::uint32_t(size);
+ *data = std::malloc(std::size_t(size));
+ if (!*data)
+ return std::errc::not_enough_memory;
+ length = std::size_t(size);
// read the data
- if (file->read(*data, size) != size)
+ if (size)
{
- free(*data);
- return osd_file::error::FAILURE;
+ std::size_t actual;
+ std::tie(err, actual) = read(*file, *data, std::size_t(size));
+ if (err || (size != actual))
+ {
+ std::free(*data);
+ *data = nullptr;
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us
+ }
}
// close the file and return data
- return osd_file::error::NONE;
+ return std::error_condition();
}
-osd_file::error core_file::load(std::string const &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);
- if (err != osd_file::error::NONE)
+ 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)
- return osd_file::error::OUT_OF_MEMORY;
+ 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
- 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 osd_file::error::FAILURE;
+ std::size_t actual;
+ std::tie(err, actual) = read(*file, &data[0], std::size_t(size));
+ if (err || (size != actual))
+ {
+ data.clear();
+ if (err)
+ return err;
+ else
+ return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us
+ }
}
// close the file and return data
- return osd_file::error::NONE;
-}
-
-
-/*-------------------------------------------------
- protected constructor
--------------------------------------------------*/
-
-core_file::core_file()
-{
+ return std::error_condition();
}
} // namespace util
-
-
-
-/***************************************************************************
- FILENAME UTILITIES
-***************************************************************************/
-
-// -------------------------------------------------
-// core_filename_extract_base - extract the base
-// name from a filename; note that this makes
-// assumptions about path separators
-// -------------------------------------------------
-
-std::string core_filename_extract_base(const std::string &name, bool strip_extension)
-{
- // find the start of the basename
- auto const start = std::find_if(name.rbegin(), name.rend(), &util::is_directory_separator);
-
- // find the end of the basename
- auto const chop_position = strip_extension
- ? std::find(name.rbegin(), start, '.')
- : start;
- auto const end = ((chop_position != start) && (std::next(chop_position) != start))
- ? std::next(chop_position)
- : name.rbegin();
-
- // copy the result into an string
- return std::string(start.base(), end.base());
-}
-
-
-// -------------------------------------------------
-// core_filename_extract_extension
-// -------------------------------------------------
-
-std::string core_filename_extract_extension(const std::string &filename, bool strip_period)
-{
- auto loc = filename.find_last_of('.');
- std::string result = loc != std::string::npos
- ? filename.substr(loc + (strip_period ? 1 : 0))
- : "";
- return result;
-}
-
-
-// -------------------------------------------------
-// core_filename_ends_with - does the given
-// filename end with the specified extension?
-// -------------------------------------------------
-
-bool core_filename_ends_with(const std::string &filename, const std::string &extension)
-{
- auto namelen = filename.length();
- auto extlen = extension.length();
-
- // first if the extension is bigger than the name, we definitely don't match
- bool matches = namelen >= extlen;
-
- // work backwards checking for a match
- while (matches && extlen > 0 && namelen > 0)
- {
- if (tolower((uint8_t)filename[--namelen]) != tolower((uint8_t)extension[--extlen]))
- matches = false;
- }
-
- return matches;
-}
diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h
index d286cb866ac..fda5015d8ba 100644
--- a/src/lib/util/corefile.h
+++ b/src/lib/util/corefile.h
@@ -12,14 +12,14 @@
#pragma once
-
-#include "corestr.h"
-#include "coretmpl.h"
+#include "ioprocs.h"
+#include "osdfile.h"
#include "strformat.h"
#include <cstdint>
#include <memory>
-#include <string>
+#include <string_view>
+#include <system_error>
namespace util {
@@ -30,17 +30,12 @@ namespace util {
#define OPEN_FLAG_NO_BOM 0x0100 /* don't output BOM */
-#define FCOMPRESS_NONE 0 /* no compression */
-#define FCOMPRESS_MIN 1 /* minimal compression */
-#define FCOMPRESS_MEDIUM 6 /* standard compression */
-#define FCOMPRESS_MAX 9 /* maximum compression */
-
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
-class core_file
+class core_file : public random_read_write
{
public:
typedef std::unique_ptr<core_file> ptr;
@@ -49,44 +44,29 @@ public:
// ----- file open/close -----
// open a file with the specified filename
- static osd_file::error open(std::string const &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 osd_file::error 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 osd_file::error 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 osd_file::error 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();
- // enable/disable streaming file compression via zlib; level is 0 to disable compression, or up to 9 for max compression
- virtual osd_file::error compress(int level) = 0;
-
// ----- 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;
@@ -96,74 +76,28 @@ public:
// read a full line of text from the file
virtual char *gets(char *s, int n) = 0;
- // get a pointer to a buffer that holds the full file data in RAM
- // this function may cause the full file data to be read
- virtual const void *buffer() = 0;
-
// open a file with the specified filename, read it into memory, and return a pointer
- static osd_file::error load(std::string const &filename, void **data, std::uint32_t &length);
- static osd_file::error load(std::string const &filename, std::vector<uint8_t> &data);
+ static std::error_condition load(std::string_view filename, void **data, std::size_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(const char *s) = 0;
+ virtual int puts(std::string_view s) = 0;
// printf-style text write to a file
- virtual int vprintf(util::format_argument_pack<std::ostream> const &args) = 0;
+ virtual int vprintf(util::format_argument_pack<char> const &args) = 0;
template <typename Format, typename... Params> int printf(Format &&fmt, Params &&...args)
{
return vprintf(util::make_format_argument_pack(std::forward<Format>(fmt), std::forward<Params>(args)...));
}
// file truncation
- virtual osd_file::error truncate(std::uint64_t offset) = 0;
-
- // flush file buffers
- virtual osd_file::error flush() = 0;
-
-
-protected:
- core_file();
+ virtual std::error_condition truncate(std::uint64_t offset) = 0;
};
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-// is a given character a directory separator?
-
-constexpr bool is_directory_separator(char c)
-{
-#if defined(WIN32)
- return ('\\' == c) || ('/' == c) || (':' == c);
-#else
- return '/' == c;
-#endif
-}
-
} // namespace util
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
-
-/* ----- filename utilities ----- */
-
-// extract the base part of a filename (remove extensions and paths)
-std::string core_filename_extract_base(const std::string &name, bool strip_extension = false);
-
-// extracts the file extension from a filename
-std::string core_filename_extract_extension(const std::string &filename, bool strip_period = false);
-
-// true if the given filename ends with a particular extension
-bool core_filename_ends_with(const std::string &filename, const std::string &extension);
-
-
#endif // MAME_LIB_UTIL_COREFILE_H
diff --git a/src/lib/util/corestr.cpp b/src/lib/util/corestr.cpp
index 5add3ed302f..9fa0a3990a2 100644
--- a/src/lib/util/corestr.cpp
+++ b/src/lib/util/corestr.cpp
@@ -9,27 +9,36 @@
****************************************************************************/
#include "corestr.h"
-#include "osdcore.h"
#include <algorithm>
+#include <iterator>
#include <memory>
-#include <ctype.h>
-#include <stdlib.h>
+#include <cctype>
+#include <cstdint>
+#include <cstdlib>
/*-------------------------------------------------
core_stricmp - case-insensitive string compare
-------------------------------------------------*/
-int core_stricmp(const char *s1, const char *s2)
+int core_stricmp(std::string_view s1, std::string_view s2)
{
- for (;;)
+ auto s1_iter = s1.begin();
+ auto s2_iter = s2.begin();
+ while (true)
{
- int c1 = tolower((uint8_t)*s1++);
- int c2 = tolower((uint8_t)*s2++);
- if (c1 == 0 || c1 != c2)
- return c1 - c2;
+ if (s1.end() == s1_iter)
+ return (s2.end() == s2_iter) ? 0 : -1;
+ else if (s2.end() == s2_iter)
+ return 1;
+
+ const int c1 = tolower(uint8_t(*s1_iter++));
+ const int c2 = tolower(uint8_t(*s2_iter++));
+ const int diff = c1 - c2;
+ if (diff)
+ return diff;
}
}
@@ -55,70 +64,42 @@ int core_strnicmp(const char *s1, const char *s2, size_t n)
/*-------------------------------------------------
core_strwildcmp - case-insensitive wildcard
- string compare (up to 16 characters at the
- moment)
+ string compare
-------------------------------------------------*/
-int core_strwildcmp(const char *sp1, const char *sp2)
+int core_strwildcmp(std::string_view s1, std::string_view s2)
{
- char s1[17], s2[17];
- size_t i, l1, l2;
- char *p;
-
- //assert(strlen(sp1) < 16);
- //assert(strlen(sp2) < 16);
-
- if (sp1[0] == 0) strcpy(s1, "*");
- else { strncpy(s1, sp1, 16); s1[16] = 0; }
-
- if (sp2[0] == 0) strcpy(s2, "*");
- else { strncpy(s2, sp2, 16); s2[16] = 0; }
-
- p = strchr(s1, '*');
- if (p)
- {
- for (i = p - s1; i < 16; i++) s1[i] = '?';
- s1[16] = 0;
- }
-
- p = strchr(s2, '*');
- if (p)
- {
- for (i = p - s2; i < 16; i++) s2[i] = '?';
- s2[16] = 0;
- }
-
- l1 = strlen(s1);
- if (l1 < 16)
+ // slight tweak of core_stricmp() logic
+ auto s1_iter = s1.begin();
+ auto s2_iter = s2.begin();
+ while (true)
{
- for (i = l1 + 1; i < 16; i++) s1[i] = ' ';
- s1[16] = 0;
+ if ((s1.end() != s1_iter && *s1_iter == '*')
+ || (s2.end() != s2_iter && *s2_iter == '*'))
+ return 0;
+
+ if (s1.end() == s1_iter)
+ return (s2.end() == s2_iter) ? 0 : -1;
+ else if (s2.end() == s2_iter)
+ return 1;
+
+ const int c1 = tolower(uint8_t(*s1_iter++));
+ const int c2 = tolower(uint8_t(*s2_iter++));
+ const int diff = (c1 != '?' && c2 != '?')
+ ? c1 - c2
+ : 0;
+ if (diff)
+ return diff;
}
-
- l2 = strlen(s2);
- if (l2 < 16)
- {
- for (i = l2 + 1; i < 16; i++) s2[i] = ' ';
- s2[16] = 0;
- }
-
- for (i = 0; i < 16; i++)
- {
- if (s1[i] == '?' && s2[i] != '?') s1[i] = s2[i];
- if (s2[i] == '?' && s1[i] != '?') s2[i] = s1[i];
- }
-
- return core_stricmp(s1, s2);
}
-bool core_iswildstr(const char *sp)
+bool core_iswildstr(std::string_view s)
{
- for ( ; sp && *sp; sp++)
+ auto iter = std::find_if(s.begin(), s.end(), [](char c)
{
- if (('?' == *sp) || ('*' == *sp))
- return true;
- }
- return false;
+ return c == '?' || c == '*';
+ });
+ return iter != s.end();
}
@@ -126,18 +107,6 @@ bool core_iswildstr(const char *sp)
std::string helpers
-------------------------------------------------*/
-#include <algorithm>
-
-int strcatvprintf(std::string &str, const char *format, va_list args)
-{
- char tempbuf[4096];
- int result = vsprintf(tempbuf, format, args);
-
- // set the result
- str.append(tempbuf);
- return result;
-}
-
void strdelchr(std::string& str, char chr)
{
for (size_t i = 0; i < str.length(); i++)
@@ -158,61 +127,39 @@ void strreplacechr(std::string& str, char ch, char newch)
}
}
-static std::string &internal_strtrimspace(std::string& str, bool right_only)
-{
- // identify the start
- std::string::iterator start = str.begin();
- if (!right_only)
- {
- start = std::find_if(
- str.begin(),
- str.end(),
- [](char c) { return !isspace(uint8_t(c)); });
- }
-
- // identify the end
- std::string::iterator end = std::find_if(
- str.rbegin(),
- std::string::reverse_iterator(start),
- [](char c) { return !isspace(uint8_t(c)); }).base();
-
- // extract the string
- str = end > start
- ? str.substr(start - str.begin(), end - start)
- : "";
- return str;
-}
-
-std::string &strtrimspace(std::string& str)
+std::string_view strtrimspace(std::string_view str)
{
- return internal_strtrimspace(str, false);
+ std::string_view str2 = strtrimleft(str, [] (char c) { return !isspace(uint8_t(c)); });
+ return strtrimright(str2, [] (char c) { return !isspace(uint8_t(c)); });
}
-std::string &strtrimrightspace(std::string& str)
+std::string_view strtrimrightspace(std::string_view str)
{
- return internal_strtrimspace(str, true);
+ return strtrimright(str, [] (char c) { return !isspace(uint8_t(c)); });
}
-std::string &strmakeupper(std::string& str)
+std::string strmakeupper(std::string_view str)
{
- std::transform(str.begin(), str.end(), str.begin(), ::toupper);
- return str;
+ std::string result;
+ std::transform(str.begin(), str.end(), std::back_inserter(result), ::toupper);
+ return result;
}
/**
- * @fn std::string &strmakelower(std::string& str)
+ * @fn std::string strmakelower(std::string_view str)
*
- * @brief Changes the given string to lower case.
+ * @brief Returns a lower case version of the given string.
*
- * @param [in,out] str The string to make lower case
+ * @param [in] str The string to make lower case
*
- * @return A reference to the original std::string having been changed to lower case
+ * @return A new std::string having been changed to lower case
*/
-std::string &strmakelower(std::string& str)
+std::string strmakelower(std::string_view str)
{
- std::transform(str.begin(), str.end(), str.begin(), ::tolower);
- return str;
+ std::string result;
+ std::transform(str.begin(), str.end(), std::back_inserter(result), ::tolower);
+ return result;
}
/**
@@ -244,7 +191,41 @@ int strreplace(std::string &str, const std::string& search, const std::string& r
namespace util {
/**
- * @fn double edit_distance(std::u32string const &lhs, std::u32string const &rhs)
+ * @fn bool streqlower(std::string_view str, std::string_view lcstr)
+ *
+ * @brief Tests whether a mixed-case string matches a lowercase string.
+ *
+ * @param [in] str First string to compare (may be mixed-case).
+ * @param [in] lcstr Second string to compare (must be all lowercase).
+ *
+ * @return True if the strings match regardless of case.
+ */
+
+bool streqlower(std::string_view str, std::string_view lcstr)
+{
+ return std::equal(str.begin(), str.end(), lcstr.begin(), lcstr.end(),
+ [] (unsigned char c1, unsigned char c2) { return std::tolower(c1) == c2; });
+}
+
+/**
+ * @fn bool strequpper(std::string_view str, std::string_view ucstr)
+ *
+ * @brief Tests whether a mixed-case string matches an uppercase string.
+ *
+ * @param [in] str First string to compare (may be mixed-case).
+ * @param [in] ucstr Second string to compare (must be all uppercase).
+ *
+ * @return True if the strings match regardless of case.
+ */
+
+bool strequpper(std::string_view str, std::string_view ucstr)
+{
+ return std::equal(str.begin(), str.end(), ucstr.begin(), ucstr.end(),
+ [] (unsigned char c1, unsigned char c2) { return std::toupper(c1) == c2; });
+}
+
+/**
+ * @fn double edit_distance(std::u32string_view lhs, std::u32string_view rhs)
*
* @brief Compares strings and returns prefix-weighted similarity score (smaller is more similar).
*
@@ -254,7 +235,7 @@ namespace util {
* @return Similarity score ranging from 0.0 (totally dissimilar) to 1.0 (identical).
*/
-double edit_distance(std::u32string const &lhs, std::u32string const &rhs)
+double edit_distance(std::u32string_view lhs, std::u32string_view rhs)
{
// based on Jaro-Winkler distance
// TODO: this breaks if the lengths don't fit in a long int, but that's not a big limitation
@@ -262,8 +243,8 @@ double edit_distance(std::u32string const &lhs, std::u32string const &rhs)
constexpr double PREFIX_WEIGHT(0.1);
constexpr double PREFIX_THRESHOLD(0.7);
- std::u32string const &longer((lhs.length() >= rhs.length()) ? lhs : rhs);
- std::u32string const &shorter((lhs.length() < rhs.length()) ? lhs : rhs);
+ std::u32string_view const &longer((lhs.length() >= rhs.length()) ? lhs : rhs);
+ std::u32string_view const &shorter((lhs.length() < rhs.length()) ? lhs : rhs);
// find matches
long const range((std::max)(long(longer.length() / 2) - 1, 0L));
diff --git a/src/lib/util/corestr.h b/src/lib/util/corestr.h
index c0bf4ef16d5..a9fc52531c8 100644
--- a/src/lib/util/corestr.h
+++ b/src/lib/util/corestr.h
@@ -13,12 +13,11 @@
#pragma once
-#include "osdcore.h"
-#include "strformat.h"
-
#include <string>
+#include <string_view>
-#include <string.h>
+#include <algorithm>
+#include <cstring>
/***************************************************************************
@@ -26,7 +25,7 @@
***************************************************************************/
/* since stricmp is not part of the standard, we use this instead */
-int core_stricmp(const char *s1, const char *s2);
+int core_stricmp(std::string_view s1, std::string_view s2);
/* this macro prevents people from using stricmp directly */
#undef stricmp
@@ -50,24 +49,39 @@ int core_strnicmp(const char *s1, const char *s2, size_t n);
/* additional string compare helper (up to 16 characters at the moment) */
-int core_strwildcmp(const char *sp1, const char *sp2);
-bool core_iswildstr(const char *sp);
-
-
-int strcatvprintf(std::string &str, const char *format, va_list args);
+int core_strwildcmp(std::string_view s1, std::string_view s2);
+bool core_iswildstr(std::string_view s);
+
+/* trim functions */
+template <typename TPred>
+std::string_view strtrimleft(std::string_view str, TPred &&pred)
+{
+ auto const start = std::find_if(str.begin(), str.end(), pred);
+ return str.substr(start - str.begin());
+}
+
+template <typename TPred>
+std::string_view strtrimright(std::string_view str, TPred &&pred)
+{
+ auto const end = std::find_if(str.rbegin(), str.rend(), pred);
+ return str.substr(0, str.size() - (end - str.rbegin()));
+}
void strdelchr(std::string& str, char chr);
void strreplacechr(std::string& str, char ch, char newch);
-std::string &strtrimspace(std::string& str);
-std::string &strtrimrightspace(std::string& str);
-std::string &strmakeupper(std::string& str);
-std::string &strmakelower(std::string& str);
+[[nodiscard]] std::string_view strtrimspace(std::string_view str);
+[[nodiscard]] std::string_view strtrimrightspace(std::string_view str);
+[[nodiscard]] std::string strmakeupper(std::string_view str);
+[[nodiscard]] std::string strmakelower(std::string_view str);
int strreplace(std::string &str, const std::string& search, const std::string& replace);
namespace util {
+bool strequpper(std::string_view str, std::string_view ucstr);
+bool streqlower(std::string_view str, std::string_view lcstr);
+
// based on Jaro-Winkler distance - returns value from 0.0 (totally dissimilar) to 1.0 (identical)
-double edit_distance(std::u32string const &lhs, std::u32string const &rhs);
+double edit_distance(std::u32string_view lhs, std::u32string_view rhs);
} // namespace util
diff --git a/src/lib/util/coretmpl.h b/src/lib/util/coretmpl.h
index 6c8ac39b45c..87f8cfbf09e 100644
--- a/src/lib/util/coretmpl.h
+++ b/src/lib/util/coretmpl.h
@@ -13,39 +13,22 @@
#pragma once
#include "osdcomm.h"
-#include "osdcore.h"
-#include "corealloc.h"
+#include "vecstream.h"
#include <array>
-#include <cassert>
#include <cstddef>
-#include <functional>
-#include <initializer_list>
#include <iterator>
-#include <list>
-#include <map>
-#include <memory>
-#include <set>
+#include <numeric>
#include <stdexcept>
-#include <tuple>
+#include <string_view>
#include <type_traits>
#include <utility>
-#include <vector>
-
-// Generate a N-bit mask at compile time. N better be constant.
-// Works even for signed types
-
-template<typename T> constexpr T make_bitmask(unsigned int N)
-{
- return T((N < (8 * sizeof(T)) ? (std::make_unsigned_t<T>(1) << N) : std::make_unsigned_t<T>(0)) - 1);
-}
-
// ======================> simple_list
// a simple_list is a singly-linked list whose 'next' pointer is owned
// by the object
-template<class _ElementType>
+template<class ElementType>
class simple_list final
{
public:
@@ -53,36 +36,31 @@ public:
{
public:
typedef int difference_type;
- typedef _ElementType value_type;
- typedef _ElementType *pointer;
- typedef _ElementType &reference;
+ typedef ElementType value_type;
+ typedef ElementType *pointer;
+ typedef ElementType &reference;
typedef std::forward_iterator_tag iterator_category;
// construction/destruction
auto_iterator() noexcept : m_current(nullptr) { }
- auto_iterator(_ElementType *ptr) noexcept : m_current(ptr) { }
+ auto_iterator(ElementType *ptr) noexcept : m_current(ptr) { }
// required operator overloads
bool operator==(const auto_iterator &iter) const noexcept { return m_current == iter.m_current; }
bool operator!=(const auto_iterator &iter) const noexcept { return m_current != iter.m_current; }
- _ElementType &operator*() const noexcept { return *m_current; }
- _ElementType *operator->() const noexcept { return m_current; }
- // note that _ElementType::next() must not return a const ptr
+ ElementType &operator*() const noexcept { return *m_current; }
+ ElementType *operator->() const noexcept { return m_current; }
+ // note that ElementType::next() must not return a const ptr
auto_iterator &operator++() noexcept { m_current = m_current->next(); return *this; }
auto_iterator operator++(int) noexcept { auto_iterator result(*this); m_current = m_current->next(); return result; }
private:
// private state
- _ElementType *m_current;
+ ElementType *m_current;
};
// construction/destruction
- simple_list() noexcept
- : m_head(nullptr)
- , m_tail(nullptr)
- , m_count(0)
- {
- }
+ simple_list() noexcept { }
~simple_list() noexcept { reset(); }
// we don't support deep copying
@@ -90,18 +68,19 @@ public:
simple_list &operator=(const simple_list &) = delete;
// but we do support cheap swap/move
- simple_list(simple_list &&list) : simple_list() { operator=(std::move(list)); }
+ simple_list(simple_list &&list) noexcept { operator=(std::move(list)); }
simple_list &operator=(simple_list &&list)
{
using std::swap;
swap(m_head, list.m_head);
swap(m_tail, list.m_tail);
swap(m_count, list.m_count);
+ return *this;
}
// simple getters
- _ElementType *first() const noexcept { return m_head; }
- _ElementType *last() const noexcept { return m_tail; }
+ ElementType *first() const noexcept { return m_head; }
+ ElementType *last() const noexcept { return m_tail; }
int count() const noexcept { return m_count; }
bool empty() const noexcept { return m_count == 0; }
@@ -117,7 +96,7 @@ public:
}
// add the given object to the head of the list
- _ElementType &prepend(_ElementType &object) noexcept
+ ElementType &prepend(ElementType &object) noexcept
{
object.m_next = m_head;
m_head = &object;
@@ -128,13 +107,13 @@ public:
}
// add the given list to the head of the list
- void prepend_list(simple_list<_ElementType> &list) noexcept
+ void prepend_list(simple_list<ElementType> &list) noexcept
{
int count = list.count();
if (count == 0)
return;
- _ElementType *tail = list.last();
- _ElementType *head = list.detach_all();
+ ElementType *tail = list.last();
+ ElementType *head = list.detach_all();
tail->m_next = m_head;
m_head = head;
if (m_tail == nullptr)
@@ -143,7 +122,7 @@ public:
}
// add the given object to the tail of the list
- _ElementType &append(_ElementType &object) noexcept
+ ElementType &append(ElementType &object) noexcept
{
object.m_next = nullptr;
if (m_tail != nullptr)
@@ -155,13 +134,13 @@ public:
}
// add the given list to the tail of the list
- void append_list(simple_list<_ElementType> &list) noexcept
+ void append_list(simple_list<ElementType> &list) noexcept
{
int count = list.count();
if (count == 0)
return;
- _ElementType *tail = list.last();
- _ElementType *head = list.detach_all();
+ ElementType *tail = list.last();
+ ElementType *head = list.detach_all();
if (m_tail != nullptr)
m_tail->m_next = head;
else
@@ -171,7 +150,7 @@ public:
}
// insert the given object after a particular object (nullptr means prepend)
- _ElementType &insert_after(_ElementType &object, _ElementType *insert_after) noexcept
+ ElementType &insert_after(ElementType &object, ElementType *insert_after) noexcept
{
if (insert_after == nullptr)
return prepend(object);
@@ -184,11 +163,11 @@ public:
}
// insert the given object before a particular object (nullptr means append)
- _ElementType &insert_before(_ElementType &object, _ElementType *insert_before) noexcept
+ ElementType &insert_before(ElementType &object, ElementType *insert_before) noexcept
{
if (insert_before == nullptr)
return append(object);
- for (_ElementType **curptr = &m_head; *curptr != nullptr; curptr = &(*curptr)->m_next)
+ for (ElementType **curptr = &m_head; *curptr != nullptr; curptr = &(*curptr)->m_next)
if (*curptr == insert_before)
{
object.m_next = insert_before;
@@ -202,10 +181,10 @@ public:
}
// replace an item in the list at the same location, and remove it
- _ElementType &replace_and_remove(_ElementType &object, _ElementType &toreplace) noexcept
+ ElementType &replace_and_remove(ElementType &object, ElementType &toreplace) noexcept
{
- _ElementType *prev = nullptr;
- for (_ElementType *cur = m_head; cur != nullptr; prev = cur, cur = cur->m_next)
+ ElementType *prev = nullptr;
+ for (ElementType *cur = m_head; cur != nullptr; prev = cur, cur = cur->m_next)
if (cur == &toreplace)
{
if (prev != nullptr)
@@ -215,16 +194,16 @@ public:
if (m_tail == &toreplace)
m_tail = &object;
object.m_next = toreplace.m_next;
- global_free(&toreplace);
+ delete &toreplace;
return object;
}
return append(object);
}
// detach the head item from the list, but don't free its memory
- _ElementType *detach_head() noexcept
+ ElementType *detach_head() noexcept
{
- _ElementType *result = m_head;
+ ElementType *result = m_head;
if (result != nullptr)
{
m_head = result->m_next;
@@ -236,10 +215,10 @@ public:
}
// detach the given item from the list, but don't free its memory
- _ElementType &detach(_ElementType &object) noexcept
+ ElementType &detach(ElementType &object) noexcept
{
- _ElementType *prev = nullptr;
- for (_ElementType *cur = m_head; cur != nullptr; prev = cur, cur = cur->m_next)
+ ElementType *prev = nullptr;
+ for (ElementType *cur = m_head; cur != nullptr; prev = cur, cur = cur->m_next)
if (cur == &object)
{
if (prev != nullptr)
@@ -255,34 +234,34 @@ public:
}
// detach the entire list, returning the head, but don't free memory
- _ElementType *detach_all() noexcept
+ ElementType *detach_all() noexcept
{
- _ElementType *result = m_head;
+ ElementType *result = m_head;
m_head = m_tail = nullptr;
m_count = 0;
return result;
}
// remove the given object and free its memory
- void remove(_ElementType &object) noexcept
+ void remove(ElementType &object) noexcept
{
- global_free(&detach(object));
+ delete &detach(object);
}
// find an object by index in the list
- _ElementType *find(int index) const noexcept
+ ElementType *find(int index) const noexcept
{
- for (_ElementType *cur = m_head; cur != nullptr; cur = cur->m_next)
+ for (ElementType *cur = m_head; cur != nullptr; cur = cur->m_next)
if (index-- == 0)
return cur;
return nullptr;
}
// return the index of the given object in the list
- int indexof(const _ElementType &object) const noexcept
+ int indexof(const ElementType &object) const noexcept
{
int index = 0;
- for (_ElementType *cur = m_head; cur != nullptr; cur = cur->m_next)
+ for (ElementType *cur = m_head; cur != nullptr; cur = cur->m_next)
{
if (cur == &object)
return index;
@@ -293,49 +272,16 @@ public:
private:
// internal state
- _ElementType * m_head; // head of the singly-linked list
- _ElementType * m_tail; // tail of the singly-linked list
- int m_count; // number of objects in the list
-};
-
-
-// ======================> simple_list_wrapper
-
-// a simple_list_wrapper wraps an existing object with a next pointer so it
-// can live in a simple_list without requiring the object to have a next
-// pointer
-template<class _ObjectType>
-class simple_list_wrapper
-{
-public:
- template<class U> friend class simple_list;
-
- // construction/destruction
- simple_list_wrapper(_ObjectType *object)
- : m_next(nullptr),
- m_object(object) { }
-
- // operators
- operator _ObjectType *() { return m_object; }
- operator _ObjectType *() const { return m_object; }
- _ObjectType *operator *() { return m_object; }
- _ObjectType *operator *() const { return m_object; }
-
- // getters
- simple_list_wrapper *next() const { return m_next; }
- _ObjectType *object() const { return m_object; }
-
-private:
- // internal state
- simple_list_wrapper * m_next;
- _ObjectType * m_object;
+ ElementType * m_head = nullptr; // head of the singly-linked list
+ ElementType * m_tail = nullptr; // tail of the singly-linked list
+ int m_count = 0; // number of objects in the list
};
// ======================> fixed_allocator
// a fixed_allocator is a simple class that maintains a free pool of objects
-template<class _ItemType>
+template<class ItemType>
class fixed_allocator
{
// we don't support deep copying
@@ -347,24 +293,24 @@ public:
fixed_allocator() { }
// allocate a new item, either by recycling an old one, or by allocating a new one
- _ItemType *alloc()
+ ItemType *alloc()
{
- _ItemType *result = m_freelist.detach_head();
+ ItemType *result = m_freelist.detach_head();
if (result == nullptr)
- result = global_alloc(_ItemType);
+ result = new ItemType;
return result;
}
// reclaim an item by adding it to the free list
- void reclaim(_ItemType *item) { if (item != nullptr) m_freelist.append(*item); }
- void reclaim(_ItemType &item) { m_freelist.append(item); }
+ void reclaim(ItemType *item) { if (item != nullptr) m_freelist.append(*item); }
+ void reclaim(ItemType &item) { m_freelist.append(item); }
// reclaim all items from a list
- void reclaim_all(simple_list<_ItemType> &_list) { m_freelist.append_list(_list); }
+ void reclaim_all(simple_list<ItemType> &_list) { m_freelist.append_list(_list); }
private:
// internal state
- simple_list<_ItemType> m_freelist; // list of free objects
+ simple_list<ItemType> m_freelist; // list of free objects
};
@@ -450,383 +396,6 @@ private:
};
-// LRU cache that behaves like std::map with differences:
-// * drops least-recently used items if necessary on insert to prevent size from exceeding max_size
-// * operator[], at, insert, emplace and find freshen existing entries
-// * iterates from least- to most-recently used rather than in order by key
-// * iterators to dropped items are invalidated
-// * not all map interfaces implemented
-// * copyable and swappable but not movable
-// * swap may invalidate past-the-end iterator, other iterators refer to new container
-template <typename Key, typename T, typename Compare = std::less<Key>, class Allocator = std::allocator<std::pair<Key const, T> > >
-class lru_cache_map
-{
-private:
- class iterator_compare;
- typedef std::list<std::pair<Key const, T>, Allocator> value_list;
- typedef typename std::allocator_traits<Allocator>::template rebind_alloc<typename value_list::iterator> iterator_allocator_type;
- typedef std::set<typename value_list::iterator, iterator_compare, iterator_allocator_type> iterator_set;
-
- class iterator_compare
- {
- public:
- typedef std::true_type is_transparent;
- iterator_compare(Compare const &comp) : m_comp(comp) { }
- iterator_compare(iterator_compare const &that) = default;
- iterator_compare(iterator_compare &&that) = default;
- Compare key_comp() const { return m_comp; }
- iterator_compare &operator=(iterator_compare const &that) = default;
- iterator_compare &operator=(iterator_compare &&that) = default;
- bool operator()(typename value_list::iterator const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs->first, rhs->first); }
- template <typename K> bool operator()(typename value_list::iterator const &lhs, K const &rhs) const { return m_comp(lhs->first, rhs); }
- template <typename K> bool operator()(K const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs, rhs->first); }
- private:
- Compare m_comp;
- };
-
-public:
- typedef Key key_type;
- typedef T mapped_type;
- typedef std::pair<Key const, T> value_type;
- typedef typename value_list::size_type size_type;
- typedef typename value_list::difference_type difference_type;
- typedef Compare key_compare;
- typedef Allocator allocator_type;
- typedef value_type &reference;
- typedef value_type const &const_reference;
- typedef typename std::allocator_traits<Allocator>::pointer pointer;
- typedef typename std::allocator_traits<Allocator>::const_pointer const_pointer;
- typedef typename value_list::iterator iterator;
- typedef typename value_list::const_iterator const_iterator;
- typedef typename value_list::reverse_iterator reverse_iterator;
- typedef typename value_list::const_reverse_iterator const_reverse_iterator;
-
- explicit lru_cache_map(size_type max_size)
- : lru_cache_map(max_size, key_compare())
- {
- }
- lru_cache_map(size_type max_size, key_compare const &comp, allocator_type const &alloc = allocator_type())
- : m_max_size(max_size)
- , m_size(0U)
- , m_elements(alloc)
- , m_mapping(iterator_compare(comp), iterator_allocator_type(alloc))
- {
- assert(0U < m_max_size);
- }
- lru_cache_map(lru_cache_map const &that)
- : m_max_size(that.m_max_size)
- , m_size(that.m_size)
- , m_elements(that.m_elements)
- , m_mapping(that.m_mapping.key_comp(), that.m_mapping.get_allocator())
- {
- for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
- m_mapping.insert(it);
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- }
-
- allocator_type get_allocator() const { return m_elements.get_allocator(); }
-
- iterator begin() { return m_elements.begin(); }
- const_iterator begin() const { return m_elements.cbegin(); }
- const_iterator cbegin() const { return m_elements.cbegin(); }
- iterator end() { return m_elements.end(); }
- const_iterator end() const { return m_elements.cend(); }
- const_iterator cend() const { return m_elements.cend(); }
- reverse_iterator rbegin() { return m_elements.rbegin(); }
- const_reverse_iterator rbegin() const { return m_elements.crbegin(); }
- const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
- reverse_iterator rend() { return m_elements.end(); }
- const_reverse_iterator rend() const { return m_elements.crend(); }
- const_reverse_iterator crend() const { return m_elements.crend(); }
-
- bool empty() const { return !m_size; }
- size_type size() const { return m_size; }
- size_type max_size() const { return m_max_size; }
-
- mapped_type &operator[](key_type const &key)
- {
- typename iterator_set::iterator existing(m_mapping.lower_bound(key));
- if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return (*existing)->second;
- }
- make_space(existing);
- iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()));
- m_mapping.insert(existing, inserted);
- ++m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return inserted->second;
- }
- mapped_type &operator[](key_type &&key)
- {
- typename iterator_set::iterator existing(m_mapping.lower_bound(key));
- if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return (*existing)->second;
- }
- make_space(existing);
- iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()));
- m_mapping.insert(existing, inserted);
- ++m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return inserted->second;
- }
- mapped_type &at(key_type const &key)
- {
- typename iterator_set::iterator existing(m_mapping.find(key));
- if (m_mapping.end() != existing)
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return (*existing)->second;
- }
- else
- {
- throw std::out_of_range("lru_cache_map::at");
- }
- }
- mapped_type const &at(key_type const &key) const
- {
- typename iterator_set::iterator existing(m_mapping.find(key));
- if (m_mapping.end() != existing)
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return (*existing)->second;
- }
- else
- {
- throw std::out_of_range("lru_cache_map::at");
- }
- }
-
- void clear()
- {
- m_size = 0U;
- m_elements.clear();
- m_mapping.clear();
- }
- std::pair<iterator, bool> insert(value_type const &value)
- {
- typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
- if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return std::pair<iterator, bool>(*existing, false);
- }
- make_space(existing);
- iterator const inserted(m_elements.emplace(m_elements.end(), value));
- m_mapping.insert(existing, inserted);
- ++m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return std::pair<iterator, bool>(inserted, true);
- }
- std::pair<iterator, bool> insert(value_type &&value)
- {
- typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
- if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
- {
- m_elements.splice(m_elements.cend(), m_elements, *existing);
- return std::pair<iterator, bool>(*existing, false);
- }
- make_space(existing);
- iterator const inserted(m_elements.emplace(m_elements.end(), std::move(value)));
- m_mapping.insert(existing, inserted);
- ++m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return std::pair<iterator, bool>(inserted, true);
- }
- template <typename P>
- std::enable_if_t<std::is_constructible<value_type, P>::value, std::pair<iterator, bool> > insert(P &&value)
- {
- return emplace(std::forward<P>(value));
- }
- template <typename InputIt>
- void insert(InputIt first, InputIt last)
- {
- while (first != last)
- {
- insert(*first);
- ++first;
- }
- }
- void insert(std::initializer_list<value_type> ilist)
- {
- for (value_type const &value : ilist)
- insert(value);
- }
- template <typename... Params>
- std::pair<iterator, bool> emplace(Params &&... args)
- {
- // TODO: is there a more efficient way than depending on value_type being efficiently movable?
- return insert(value_type(std::forward<Params>(args)...));
- }
- iterator erase(const_iterator pos)
- {
- m_mapping.erase(m_elements.erase(pos, pos));
- iterator const result(m_elements.erase(pos));
- --m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return result;
- }
- iterator erase(const_iterator first, const_iterator last)
- {
- iterator pos(m_elements.erase(first, first));
- while (pos != last)
- {
- m_mapping.erase(pos);
- pos = m_elements.erase(pos);
- --m_size;
- }
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return pos;
- }
- size_type erase(key_type const &key)
- {
- typename iterator_set::iterator const found(m_mapping.find(key));
- if (m_mapping.end() == found)
- {
- return 0U;
- }
- else
- {
- m_elements.erase(*found);
- m_mapping.erase(found);
- --m_size;
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- return 1U;
- }
- }
- void swap(lru_cache_map &that)
- {
- using std::swap;
- swap(m_max_size, that.m_max_size);
- swap(m_size, that.m_size);
- swap(m_elements, that.m_elements);
- swap(m_mapping, that.m_mapping);
- }
-
- size_type count(key_type const &key) const
- {
- // TODO: perhaps this should freshen an element
- return m_mapping.count(key);
- }
- template <typename K>
- size_type count(K const &x) const
- {
- // FIXME: should only enable this overload if Compare::is_transparent
- // TODO: perhaps this should freshen an element
- return m_mapping.count(x);
- }
- iterator find(key_type const &key)
- {
- typename iterator_set::const_iterator const found(m_mapping.find(key));
- if (m_mapping.end() == found)
- {
- return m_elements.end();
- }
- else
- {
- m_elements.splice(m_elements.cend(), m_elements, *found);
- return *found;
- }
- }
- iterator find(key_type const &key) const
- {
- typename iterator_set::const_iterator const found(m_mapping.find(key));
- if (m_mapping.end() == found)
- {
- return m_elements.end();
- }
- else
- {
- m_elements.splice(m_elements.cend(), m_elements, *found);
- return *found;
- }
- }
- template <typename K>
- iterator find(K const &x)
- {
- // FIXME: should only enable this overload if Compare::is_transparent
- typename iterator_set::const_iterator const found(m_mapping.find(x));
- if (m_mapping.end() == found)
- {
- return m_elements.end();
- }
- else
- {
- m_elements.splice(m_elements.cend(), m_elements, *found);
- return *found;
- }
- }
- template <typename K>
- iterator find(K const &x) const
- {
- // FIXME: should only enable this overload if Compare::is_transparent
- typename iterator_set::const_iterator const found(m_mapping.find(x));
- if (m_mapping.end() == found)
- {
- return m_elements.end();
- }
- else
- {
- m_elements.splice(m_elements.cend(), m_elements, *found);
- return *found;
- }
- }
-
- key_compare key_comp() const
- {
- return m_mapping.key_comp().key_comp();
- }
-
- lru_cache_map &operator=(lru_cache_map const &that)
- {
- m_max_size = that.m_max_size;
- m_size = that.m_size;
- m_elements = that.m_elements;
- m_mapping.clear();
- for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
- m_mapping.insert(it);
- assert(m_elements.size() == m_size);
- assert(m_mapping.size() == m_size);
- }
-
-private:
- void make_space(typename iterator_set::iterator &existing)
- {
- while (m_max_size <= m_size)
- {
- if ((m_mapping.end() != existing) && (m_elements.begin() == *existing))
- existing = m_mapping.erase(existing);
- else
- m_mapping.erase(m_elements.begin());
- m_elements.erase(m_elements.begin());
- --m_size;
- }
- }
-
- size_type m_max_size;
- size_type m_size;
- mutable value_list m_elements;
- iterator_set m_mapping;
-};
-
-template <typename Key, typename T, typename Compare, class Allocator>
-void swap(lru_cache_map<Key, T, Compare, Allocator> &lhs, lru_cache_map<Key, T, Compare, Allocator> &rhs)
-{
- lhs.swap(rhs);
-}
-
-
template <typename T, std::size_t N, bool WriteWrap = false, bool ReadWrap = WriteWrap>
class fifo : protected std::array<T, N>
{
@@ -957,6 +526,25 @@ private:
};
+// extract a string_view from an ovectorstream buffer
+template <typename CharT, typename Traits, typename Allocator>
+std::basic_string_view<CharT, Traits> buf_to_string_view(basic_ovectorstream<CharT, Traits, Allocator> &stream)
+{
+ // this works on the assumption that the value tellp returns is the same both before and after vec is called
+ return std::basic_string_view<CharT, Traits>(&stream.vec()[0], stream.tellp());
+}
+
+
+// For declaring an array of the same dimensions as another array (including multi-dimensional arrays)
+template <typename T, typename U> struct equivalent_array_or_type { typedef T type; };
+template <typename T, typename U, std::size_t N> struct equivalent_array_or_type<T, U[N]> { typedef typename equivalent_array_or_type<T, U>::type type[N]; };
+template <typename T, typename U> using equivalent_array_or_type_t = typename equivalent_array_or_type<T, U>::type;
+template <typename T, typename U> struct equivalent_array { };
+template <typename T, typename U, std::size_t N> struct equivalent_array<T, U[N]> { typedef equivalent_array_or_type_t<T, U> type[N]; };
+template <typename T, typename U> using equivalent_array_t = typename equivalent_array<T, U>::type;
+#define EQUIVALENT_ARRAY(a, T) util::equivalent_array_t<T, std::remove_reference_t<decltype(a)> >
+
+
template <typename E>
using enable_enum_t = typename std::enable_if_t<std::is_enum<E>::value, typename std::underlying_type_t<E> >;
@@ -975,24 +563,114 @@ constexpr typename std::enable_if_t<std::is_enum<E>::value && std::is_integral<T
}
-// useful functions to deal with bit shuffling
+/// \defgroup bitutils Useful functions for bit shuffling
+/// \{
+
+/// \brief Generate a right-aligned bit mask
+///
+/// Generates a right aligned mask of the specified width. Works with
+/// signed and unsigned integer types.
+/// \tparam T Desired output type.
+/// \tparam U Type of the input (generally resolved by the compiler).
+/// \param [in] n Width of the mask to generate in bits.
+/// \return Right-aligned mask of the specified width.
+
+template <typename T, typename U> constexpr T make_bitmask(U n)
+{
+ return T((n < (8 * sizeof(T)) ? (std::make_unsigned_t<T>(1) << n) : std::make_unsigned_t<T>(0)) - 1);
+}
+
+
+/// \brief Extract a single bit from an integer
+///
+/// Extracts a single bit from an integer into the least significant bit
+/// position.
+///
+/// \param [in] x The integer to extract the bit from.
+/// \param [in] n The bit to extract, where zero is the least
+/// significant bit of the input.
+/// \return Zero if the specified bit is unset, or one if it is set.
+/// \sa bitswap
template <typename T, typename U> constexpr T BIT(T x, U n) noexcept { return (x >> n) & T(1); }
-template <typename T, typename U> constexpr T bitswap(T val, U b) noexcept { return BIT(val, b) << 0U; }
+/// \brief Extract a bit field from an integer
+///
+/// Extracts and right-aligns a bit field from an integer.
+///
+/// \param [in] x The integer to extract the bit field from.
+/// \param [in] n The least significant bit position of the field to
+/// extract, where zero is the least significant bit of the input.
+/// \param [in] w The width of the field to extract in bits.
+/// \return The field [n..(n+w-1)] from the input.
+/// \sa bitswap
+template <typename T, typename U, typename V> constexpr T BIT(T x, U n, V w)
+{
+ return (x >> n) & make_bitmask<T>(w);
+}
+
+
+/// \brief Extract bits in arbitrary order
+///
+/// Extracts bits from an integer. Specify the bits in the order they
+/// should be arranged in the output, from most significant to least
+/// significant. The extracted bits will be packed into a right-aligned
+/// field in the output.
+///
+/// \param [in] val The integer to extract bits from.
+/// \param [in] b The first bit to extract from the input
+/// extract, where zero is the least significant bit of the input.
+/// This bit will appear in the most significant position of the
+/// right-aligned output field.
+/// \param [in] c The remaining bits to extract, where zero is the
+/// least significant bit of the input.
+/// \return The extracted bits packed into a right-aligned field.
template <typename T, typename U, typename... V> constexpr T bitswap(T val, U b, V... c) noexcept
{
- return (BIT(val, b) << sizeof...(c)) | bitswap(val, c...);
+ if constexpr (sizeof...(c) > 0U)
+ return (BIT(val, b) << sizeof...(c)) | bitswap(val, c...);
+ else
+ return BIT(val, b);
}
-// explicit version that checks number of bit position arguments
-template <unsigned B, typename T, typename... U> T bitswap(T val, U... b) noexcept
+
+/// \brief Extract bits in arbitrary order with explicit count
+///
+/// Extracts bits from an integer. Specify the bits in the order they
+/// should be arranged in the output, from most significant to least
+/// significant. The extracted bits will be packed into a right-aligned
+/// field in the output. The number of bits to extract must be supplied
+/// as a template argument.
+///
+/// A compile error will be generated if the number of bit positions
+/// supplied does not match the specified number of bits to extract, or
+/// if the output type is too small to hold the extracted bits. This
+/// guards against some simple errors.
+///
+/// \tparam B The number of bits to extract. Must match the number of
+/// bit positions supplied.
+/// \param [in] val The integer to extract bits from.
+/// \param [in] b Bits to extract, where zero is the least significant
+/// bit of the input. Specify bits in the order they should appear in
+/// the output field, from most significant to least significant.
+/// \return The extracted bits packed into a right-aligned field.
+template <unsigned B, typename T, typename... U> constexpr T bitswap(T val, U... b) noexcept
{
static_assert(sizeof...(b) == B, "wrong number of bits");
static_assert((sizeof(std::remove_reference_t<T>) * 8) >= B, "return type too small for result");
return bitswap(val, b...);
}
+/// \}
+
+
+// utility function for sign-extending values of arbitrary width
+template <typename T, typename U>
+constexpr std::make_signed_t<T> sext(T value, U width) noexcept
+{
+ return std::make_signed_t<T>(value << (8 * sizeof(value) - width)) >> (8 * sizeof(value) - width);
+}
+
// constexpr absolute value of an integer
template <typename T>
@@ -1002,18 +680,11 @@ constexpr std::enable_if_t<std::is_signed<T>::value, T> iabs(T v) noexcept
}
-// returns greatest common divisor of a and b using the Euclidean algorithm
-template <typename M, typename N>
-constexpr std::common_type_t<M, N> euclid_gcd(M a, N b)
-{
- return b ? euclid_gcd(b, a % b) : a;
-}
-
// reduce a fraction
template <typename M, typename N>
inline void reduce_fraction(M &num, N &den)
{
- auto const div(euclid_gcd(num, den));
+ auto const div(std::gcd(num, den));
if (div)
{
num /= div;
@@ -1021,6 +692,6 @@ inline void reduce_fraction(M &num, N &den)
}
}
-}; // namespace util
+} // namespace util
#endif // MAME_UTIL_CORETMPL_H
diff --git a/src/lib/util/coreutil.cpp b/src/lib/util/coreutil.cpp
index d973080e354..cebbceb037e 100644
--- a/src/lib/util/coreutil.cpp
+++ b/src/lib/util/coreutil.cpp
@@ -9,8 +9,7 @@
***************************************************************************/
#include "coreutil.h"
-#include <assert.h>
-#include <zlib.h>
+#include <cassert>
/***************************************************************************
@@ -55,14 +54,3 @@ uint32_t bcd_2_dec(uint32_t a)
}
return result;
}
-
-
-
-/***************************************************************************
- MISC
-***************************************************************************/
-
-uint32_t core_crc32(uint32_t crc, const uint8_t *buf, uint32_t len)
-{
- return crc32(crc, buf, len);
-}
diff --git a/src/lib/util/coreutil.h b/src/lib/util/coreutil.h
index a0166818da8..813deb27a83 100644
--- a/src/lib/util/coreutil.h
+++ b/src/lib/util/coreutil.h
@@ -13,7 +13,7 @@
#pragma once
-#include "osdcomm.h"
+#include <cstdint>
/***************************************************************************
@@ -72,11 +72,4 @@ inline int gregorian_days_in_month(int month, int year)
return result;
}
-
-/***************************************************************************
- MISC
-***************************************************************************/
-
-uint32_t core_crc32(uint32_t crc, const uint8_t *buf, uint32_t len);
-
#endif // MAME_UTIL_COREUTIL_H
diff --git a/src/lib/util/crypto.hpp b/src/lib/util/crypto.hpp
index 705aad6a375..e28f5b1f9f5 100644
--- a/src/lib/util/crypto.hpp
+++ b/src/lib/util/crypto.hpp
@@ -1,18 +1,31 @@
-// license:MIT
-// copyright-holders:Miodrag Milanovic
-#ifndef CRYPTO_HPP
-#define CRYPTO_HPP
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#ifndef MAME_UTIL_CRYPTO_HPP
+#define MAME_UTIL_CRYPTO_HPP
+
+#pragma once
+
+#include "hashing.h"
+
+#include <cstdint>
+#include <limits>
+#include <string>
-#include "base64.hpp"
-#include "sha1.hpp"
inline std::string sha1_encode(const std::string& input)
{
- char message_digest[20];
- sha1::calc(input.c_str(),input.length(),reinterpret_cast<unsigned char*>(message_digest));
-
- return std::string(message_digest, sizeof(message_digest));
+ util::sha1_creator digester;
+ const char *ptr = input.c_str();
+ std::string::size_type remain = input.length();
+ while (remain > std::numeric_limits<uint32_t>::max())
+ {
+ digester.append(ptr, std::numeric_limits<uint32_t>::max());
+ ptr += std::numeric_limits<uint32_t>::max();
+ remain -= std::numeric_limits<uint32_t>::max();
+ }
+ digester.append(ptr, uint32_t(remain));
+ return std::string(reinterpret_cast<const char *>(digester.finish().m_raw), 20);
}
-#endif /* CRYPTO_HPP */
+#endif // MAME_UTIL_CRYPTO_HPP
diff --git a/src/lib/util/delegate.cpp b/src/lib/util/delegate.cpp
index 7b4ee8df76e..fb2fd4e2091 100644
--- a/src/lib/util/delegate.cpp
+++ b/src/lib/util/delegate.cpp
@@ -1,79 +1,95 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles,Vas Crabb
/***************************************************************************
- delegate.c
+ delegate.cpp
Templates and classes to enable delegates for callbacks.
***************************************************************************/
-#include <assert.h>
-#include <cstdint>
-#include <stdio.h>
#include "delegate.h"
+#include "mfpresolve.h"
+
+#include <cstdio>
+#include <sstream>
+
//**************************************************************************
-// GLOBAL VARIABLES
+// MACROS
//**************************************************************************
-#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
+#if defined(MAME_DELEGATE_LOG_ADJ)
+ #define LOG(...) printf(__VA_ARGS__)
+#else
+ #define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
+#endif
-delegate_mfp::raw_mfp_data delegate_mfp::s_null_mfp = { {0 }};
-#endif
+//**************************************************************************
+// LATE BINDING EXCEPTION
+//**************************************************************************
+binding_type_exception::binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type)
+ : m_target_type(&target_type)
+ , m_actual_type(&actual_type)
+{
+ std::ostringstream os;
+ os << "Error performing late bind of function expecting type " << target_type.name() << " to instance of type " << actual_type.name();
+ m_what = os.str();
+}
+
+
+char const *binding_type_exception::what() const noexcept
+{
+ return m_what.c_str();
+}
+
+
+
+namespace util::detail {
//**************************************************************************
-// INTERNAL DELEGATE HELPERS
+// GLOBAL VARIABLES
//**************************************************************************
-#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
+const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp = { { 0 } };
+
+
+//**************************************************************************
+// INTERNAL DELEGATE HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// delegate_mfp_itanium::convert_to_generic -
+// given an object pointer and member function
+// pointer, apply the displacement and get the
+// actual function pointer
//-------------------------------------------------
-// delegate_convert_raw - given an object and an raw function, adjust the object base
-// and return the actual final code pointer
-//-------------------------------------------------//
-delegate_generic_function delegate_mfp::convert_to_generic(delegate_generic_class *&object) const
+delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
{
-#if defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(__EMSCRIPTEN__)
- if ((m_this_delta & 1) == 0) {
-#if defined(LOG_DELEGATES)
- printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
-#endif
- object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
- return reinterpret_cast<delegate_generic_function>(m_function);
- }
- object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object));
-
- // otherwise, it is the byte index into the vtable where the actual function lives
- std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
-#if defined(LOG_DELEGATES)
- printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1)));
-#endif
- return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function + m_this_delta - 1);
-#else
- // apply the "this" delta to the object first
- object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
+ auto const [entrypoint, adjusted] = resolve_member_function_itanium(m_function, m_this_delta, object);
+ object = reinterpret_cast<delegate_generic_class *>(adjusted);
+ return reinterpret_cast<delegate_generic_function>(entrypoint);
+}
- // if the low bit of the vtable index is clear, then it is just a raw function pointer
- if (!(m_function & 1)) {
-#if defined(LOG_DELEGATES)
- printf("Calculated Addr = %08x\n", (uintptr_t)(void*)(m_function));
-#endif
- return reinterpret_cast<delegate_generic_function>(m_function);
- }
- // otherwise, it is the byte index into the vtable where the actual function lives
- std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
-#if defined(LOG_DELEGATES)
- printf("Calculated Addr = %08x (VTAB)\n", (uintptr_t)(void*)(*reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1)));
-#endif
- return *reinterpret_cast<delegate_generic_function *>(vtable_base + m_function - 1);
-#endif
+//-------------------------------------------------
+// delegate_mfp_msvc::adjust_this_pointer - given
+// an object pointer and member function pointer,
+// apply the displacement, and walk past
+// recognisable thunks
+//-------------------------------------------------
+
+delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
+{
+ auto const [entrypoint, adjusted] = resolve_member_function_msvc(&m_function, m_size, object);
+ object = reinterpret_cast<delegate_generic_class *>(adjusted);
+ return reinterpret_cast<delegate_generic_function>(entrypoint);
}
-#endif
+} // namespace util::detail
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index 6b3101f5afa..b4681b04758 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic
+// copyright-holders:Aaron Giles,Couriersud,Miodrag Milanovic,Vas Crabb
/***************************************************************************
delegate.h
@@ -33,7 +33,7 @@
The "compatible" version of delegates is based on an implementation
from Sergey Ryazanov, found here:
- http://www.codeproject.com/KB/cpp/ImpossiblyFastCppDelegate.aspx
+ https://www.codeproject.com/Articles/11015/The-Impossibly-Fast-C-Delegates
These delegates essentially generate a templated static stub function
for each target function. The static function takes the first
@@ -52,36 +52,80 @@
--------------------------------------------------------------------
- The "internal" version of delegates makes use of the internal
+ The "Itanium" version of delegates makes use of the internal
structure of member function pointers in order to convert them at
binding time into simple static function pointers. This only works
on platforms where object->func(p1, p2) is equivalent in calling
convention to func(object, p1, p2).
- Most of the information on how this works comes from Don Clugston
- in this article:
-
- http://www.codeproject.com/KB/cpp/FastDelegate.aspx
-
Pros:
* as fast as a standard function call in static and member cases
* no stub functions or double-hops needed
Cons:
* requires internal knowledge of the member function pointer
- * only works for GCC (for now; MSVC info is also readily available)
+ * only works for two popular variants of the Itanium C++ ABI
+
+ --------------------------------------------------------------------
+
+ The "MSVC" version of delegates makes use of the internal structure
+ of member function pointers in order to convert them at binding time
+ into simple static function pointers. This only works on platforms
+ where object->func(p1, p2) is equivalent in calling convention to
+ func(object, p1, p2).
+
+ Pros:
+ * as fast as a standard function call in static and non-virtual
+ member cases
+ * no stub functions needed
+
+ Cons:
+ * requires internal knowledge of the member function pointer
+ * only works works with MSVC ABI, and not on 32-bit x86
+ * does not work for classes with virtual bases
+ * structure return does not work with member function pointers
+ * virtual member function lookup cannot be done in advance
+
+ --------------------------------------------------------------------
+
+ Further reading:
+
+ * http://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-pointers
+ Formal specification for the most common member function pointer
+ implementations.
+
+ * https://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
+ Discusses many member function pointer implementations. Based
+ on reverse-engineering, so not entirely accurate. In particular,
+ various fields are incorrectly assumed to be int-sized which is
+ not true in the general case.
+
+ * https://devblogs.microsoft.com/oldnewthing/20040209-00/?p=40713
+ Describes the MSVC implementation of pointers to member
+ functions for classes with single or multiple inheritance. Does
+ not mention the additional variants for virtual or unknown
+ inheritance. Incorrectly states that the "this" pointer
+ displacement is a size_t when in reality it is an int (important
+ for 64-bit architectures).
***************************************************************************/
-#ifndef MAME_UTIL_DELEGATE_H
-#define MAME_UTIL_DELEGATE_H
+#ifndef MAME_LIB_UTIL_DELEGATE_H
+#define MAME_LIB_UTIL_DELEGATE_H
#pragma once
-// standard C++ includes
+#include "abi.h"
+
+#include <any>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
#include <cstring>
+#include <functional>
+#include <string>
+#include <type_traits>
#include <typeinfo>
#include <utility>
-#include <functional>
//**************************************************************************
@@ -89,43 +133,72 @@
//**************************************************************************
// types of delegates supported
-#define DELEGATE_TYPE_COMPATIBLE 0
-#define DELEGATE_TYPE_INTERNAL 1
-#define DELEGATE_TYPE_MSVC 2
+#define MAME_DELEGATE_TYPE_COMPATIBLE 0
+#define MAME_DELEGATE_TYPE_ITANIUM 1
+#define MAME_DELEGATE_TYPE_MSVC 2
// select which one we will be using
-#if defined(__GNUC__)
- /* 32bit MINGW asks for different convention */
- #if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
- #define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
- #define MEMBER_ABI __thiscall
- #define HAS_DIFFERENT_ABI 1
+#if defined(MAME_DELEGATE_FORCE_COMPATIBLE)
+ #define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
+#elif defined(__GNUC__)
+ // 32bit MINGW asks for different convention
+ #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_DIFFERENT_MEMBER_ABI 1
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
- #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+ #define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#else
- #define USE_DELEGATE_TYPE DELEGATE_TYPE_INTERNAL
- #define MEMBER_ABI
- #define HAS_DIFFERENT_ABI 0
+ #define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_ITANIUM
+ #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#endif
-#elif defined(_MSC_VER) && defined (_M_X64)
-#define MEMBER_ABI
-#define HAS_DIFFERENT_ABI 0
-#define USE_DELEGATE_TYPE DELEGATE_TYPE_MSVC
+#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
+ #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
+ #define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_MSVC
#else
-#define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+ #define MAME_DELEGATE_USE_TYPE MAME_DELEGATE_TYPE_COMPATIBLE
#endif
-#if defined(FORCE_COMPATIBLE)
- #undef USE_DELEGATE_TYPE
- #undef MEMBER_ABI
- #undef HAS_DIFFERENT_ABI
- #define USE_DELEGATE_TYPE DELEGATE_TYPE_COMPATIBLE
+#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
+ #define MAME_DELEGATE_DIFFERENT_MEMBER_ABI 0
#endif
-#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
- #define MEMBER_ABI
- #define HAS_DIFFERENT_ABI 0
-#endif
+
+/// \brief Base for objects used with late binding
+///
+/// Default polymorphic class used as base for objects that can be bound
+/// to after the target function has already been set.
+class delegate_late_bind
+{
+public:
+ virtual ~delegate_late_bind() = default;
+};
+
+
+/// \brief Inappropriate late bind object error
+///
+/// Thrown as an exception if the object supplied for late binding
+/// cannot be cast to the target type for the delegate's function.
+class binding_type_exception : public std::bad_cast
+{
+public:
+ binding_type_exception(std::type_info const &target_type, std::type_info const &actual_type);
+
+ virtual char const *what() const noexcept override;
+
+ std::type_info const &target_type() const noexcept { return *m_target_type; }
+ std::type_info const &actual_type() const noexcept { return *m_actual_type; }
+
+private:
+ std::string m_what;
+ std::type_info const *m_target_type;
+ std::type_info const *m_actual_type;
+};
+
+
+
+namespace util::detail {
+
//**************************************************************************
// HELPER CLASSES
//**************************************************************************
@@ -138,38 +211,12 @@ using delegate_generic_function = void(*)();
// define a dummy generic class that is just straight single-inheritance
#ifdef _MSC_VER
-class __single_inheritance generic_class;
class delegate_generic_class { };
#else
class delegate_generic_class;
#endif
-// ======================> delegate_late_bind
-
-// simple polymorphic class that must be mixed into any object that is late-bound
-class delegate_late_bind
-{
-public:
- delegate_late_bind() { }
- virtual ~delegate_late_bind() { }
-};
-
-
-// ======================> binding_type_exception
-
-// exception that is thrown when a bind fails the dynamic_cast
-class binding_type_exception : public std::exception
-{
-public:
- binding_type_exception(const std::type_info &target_type, const std::type_info &actual_type)
- : m_target_type(target_type), m_actual_type(actual_type)
- { }
- const std::type_info &m_target_type;
- const std::type_info &m_actual_type;
-};
-
-
// ======================> delegate_traits
// delegate_traits is a meta-template that is used to provide a static function pointer
@@ -186,83 +233,69 @@ struct delegate_traits
-//**************************************************************************
-// DELEGATE MEMBER FUNCTION POINTER WRAPPERS
-//**************************************************************************
-
-#if (USE_DELEGATE_TYPE == DELEGATE_TYPE_COMPATIBLE)
-
-// ======================> delegate_mfp
-
-// delegate_mfp is a class that wraps a generic member function pointer
-// in a static buffer, and can effectively recast itself back for later use;
-// it hides some of the gross details involved in copying arbitrary member
-// function pointers around
-class delegate_mfp
+/// \brief Maximally compatible member function pointer wrapper
+///
+/// Instantiates a static member function template on construction as
+/// an adaptor thunk to call the supplied member function with the
+/// supplied object. Adds one layer of indirection to calls.
+///
+/// This implementation requires the representation of a null member
+/// function pointer to be all zeroes.
+class delegate_mfp_compatible
{
public:
// default constructor
- delegate_mfp()
+ delegate_mfp_compatible() noexcept
: m_rawdata(s_null_mfp)
, m_realobject(nullptr)
, m_stubfunction(nullptr)
{ }
// copy constructor
- delegate_mfp(const delegate_mfp &src)
- : m_rawdata(src.m_rawdata)
- , m_realobject(src.m_realobject)
- , m_stubfunction(src.m_stubfunction)
- { }
+ delegate_mfp_compatible(const delegate_mfp_compatible &src) noexcept = default;
// construct from any member function pointer
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
- delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
+ delegate_mfp_compatible(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) noexcept
: m_rawdata(s_null_mfp)
, m_realobject(nullptr)
- , m_stubfunction(make_generic<StaticFunctionType>(&delegate_mfp::method_stub<MemberFunctionClass, ReturnType>))
+ , m_stubfunction(make_generic<StaticFunctionType>(&delegate_mfp_compatible::method_stub<MemberFunctionClass, ReturnType>))
{
- assert(sizeof(mfp) <= sizeof(m_rawdata));
+ static_assert(sizeof(mfp) <= sizeof(m_rawdata), "Unsupported member function pointer size");
*reinterpret_cast<MemberFunctionType *>(&m_rawdata) = mfp;
}
// comparison helpers
- bool operator==(const delegate_mfp &rhs) const { return (m_rawdata == rhs.m_rawdata); }
- bool isnull() const { return (m_rawdata == s_null_mfp); }
+ bool operator==(const delegate_mfp_compatible &rhs) const noexcept { return m_rawdata == rhs.m_rawdata; }
+ bool isnull() const noexcept { return m_rawdata == s_null_mfp; }
// getters
- delegate_generic_class *real_object(delegate_generic_class *original) const { return m_realobject; }
-
- // binding helper
- template <typename FunctionType>
- void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
+ delegate_generic_class *real_object(delegate_generic_class *original) const noexcept
{
- m_realobject = object;
- object = reinterpret_cast<delegate_generic_class *>(this);
- funcptr = reinterpret_cast<FunctionType>(m_stubfunction);
+ return m_realobject;
}
+ // binding helpers
+ template <typename FunctionType>
+ void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object) noexcept;
+
+ template <typename FunctionType>
+ void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) noexcept;
+
private:
// helper stubs for calling encased member function pointers
template <class FunctionClass, typename ReturnType, typename... Params>
- static ReturnType method_stub(delegate_generic_class *object, Params ... args)
- {
- delegate_mfp *_this = reinterpret_cast<delegate_mfp *>(object);
- using mfptype = ReturnType(FunctionClass::*)(Params...);
- mfptype &mfp = *reinterpret_cast<mfptype *>(&_this->m_rawdata);
- return (reinterpret_cast<FunctionClass *>(_this->m_realobject)->*mfp)(std::forward<Params>(args)...);
- }
-
+ static ReturnType method_stub(delegate_generic_class *object, Params ... args);
// helper to convert a function of a given type to a generic function, forcing template
// instantiation to match the source type
template <typename SourceType>
- static delegate_generic_function make_generic(SourceType funcptr)
+ static delegate_generic_function make_generic(SourceType funcptr) noexcept
{
return reinterpret_cast<delegate_generic_function>(funcptr);
}
-
+ // FIXME: not properly aligned for storing pointers
struct raw_mfp_data
{
#if defined(__INTEL_COMPILER) && defined(_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least
@@ -270,181 +303,404 @@ private:
#else // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
int data[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
#endif
- bool operator==(const raw_mfp_data &rhs) const { return (memcmp(data, rhs.data, sizeof(data)) == 0); }
+ bool operator==(const raw_mfp_data &rhs) const noexcept { return !std::memcmp(data, rhs.data, sizeof(data)); }
};
// internal state
raw_mfp_data m_rawdata; // raw buffer to hold the copy of the function pointer
delegate_generic_class * m_realobject; // pointer to the object used for calling
delegate_generic_function m_stubfunction; // pointer to our matching stub function
- static raw_mfp_data s_null_mfp; // nullptr mfp
+
+ static const raw_mfp_data s_null_mfp; // nullptr mfp
};
-#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_INTERNAL)
-// ======================> delegate_mfp
+template <typename FunctionType>
+void delegate_mfp_compatible::update_after_bind(FunctionType &funcptr, delegate_generic_class *&object) noexcept
+{
+ m_realobject = object;
+ object = reinterpret_cast<delegate_generic_class *>(this);
+ funcptr = reinterpret_cast<FunctionType>(m_stubfunction);
+}
+
-// struct describing the contents of a member function pointer
-class delegate_mfp
+template <typename FunctionType>
+void delegate_mfp_compatible::update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) noexcept
+{
+ assert(reinterpret_cast<FunctionType>(m_stubfunction) == funcptr);
+ object = reinterpret_cast<delegate_generic_class *>(this);
+}
+
+
+template <class FunctionClass, typename ReturnType, typename... Params>
+ReturnType delegate_mfp_compatible::method_stub(delegate_generic_class *object, Params ... args)
+{
+ using mfptype = ReturnType(FunctionClass::*)(Params...);
+ delegate_mfp_compatible &_this = *reinterpret_cast<delegate_mfp_compatible *>(object);
+ mfptype &mfp = *reinterpret_cast<mfptype *>(&_this.m_rawdata);
+ return (reinterpret_cast<FunctionClass *>(_this.m_realobject)->*mfp)(std::forward<Params>(args)...);
+}
+
+
+
+/// \brief Itanium C++ ABI member function pointer wrapper
+///
+/// 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 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
+/// are guaranteed to be even numbers of bytes. If the pointer is even,
+/// 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.
+///
+/// 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
+/// possible to distinguish between a vtable offset and a null pointer.
+/// (This is the case for ARM where the least significant bit of a
+/// pointer to a function is set if the function starts in Thumb mode.)
+/// If the least significant bit of the ptrdiff_t is clear, the pointer
+/// is a conventional function pointer to the member function. If the
+/// least significant bit of the ptrdiff_t is set, the pointer is a byte
+/// offset into the vtable. The ptrdiff_t must be shifted right one bit
+/// position to make a byte offset to add to the this pointer. A null
+/// member function pointer is represented by setting the pointer to a
+/// null pointer and clearing the least significant bit of the
+/// ptrdiff_t.
+class delegate_mfp_itanium
{
public:
// default constructor
- delegate_mfp()
- : m_function(0)
- , m_this_delta(0)
- { }
+ delegate_mfp_itanium() noexcept = default;
// copy constructor
- delegate_mfp(const delegate_mfp &src)
- : m_function(src.m_function)
- , m_this_delta(src.m_this_delta)
- { }
+ delegate_mfp_itanium(const delegate_mfp_itanium &src) noexcept = default;
// construct from any member function pointer
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
- delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
+ delegate_mfp_itanium(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) noexcept
{
- assert(sizeof(mfp) == sizeof(*this));
+ static_assert(sizeof(mfp) == sizeof(*this), "Unsupported member function pointer size");
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
}
// comparison helpers
- bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function && m_this_delta == rhs.m_this_delta); }
- bool isnull() const { return (m_function == 0 && m_this_delta==0); }
+ bool operator==(const delegate_mfp_itanium &rhs) const noexcept
+ {
+ return (isnull() && rhs.isnull()) || ((m_function == rhs.m_function) && (m_this_delta == rhs.m_this_delta));
+ }
+
+ bool isnull() const noexcept
+ {
+ 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);
+ }
// getters
- static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
+ static delegate_generic_class *real_object(delegate_generic_class *original) noexcept
+ {
+ return original;
+ }
- // binding helper
+ // binding helpers
template <typename FunctionType>
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
{
funcptr = reinterpret_cast<FunctionType>(convert_to_generic(object));
}
+ template <typename FunctionType>
+ void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) noexcept
+ {
+ }
+
private:
// extract the generic function and adjust the object pointer
delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
// actual state
- uintptr_t m_function; // first item can be one of two things:
- // if even, it's a pointer to the function
- // if odd, it's the byte offset into the vtable
- int m_this_delta; // delta to apply to the 'this' pointer
+ uintptr_t m_function = reinterpret_cast<uintptr_t>(static_cast<void (*)()>(nullptr)); // function pointer or vtable offset
+ ptrdiff_t m_this_delta = 0; // delta to apply to the 'this' pointer
};
-#elif (USE_DELEGATE_TYPE == DELEGATE_TYPE_MSVC)
-// ======================> delegate_mfp
-const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (delegate_generic_class::*)());
-// struct describing the contents of a member function pointer
-class delegate_mfp
+/// \brief MSVC member function pointer wrapper
+///
+/// MSVC uses space optimisation. A member function pointer is a
+/// conventional function pointer followed by zero to three int values,
+/// depending on whether the class has single, multiple, virtual or
+/// unknown inheritance of base classes. The function pointer is always
+/// a conventional function pointer (a thunk is used to call virtual
+/// member functions through the vtable).
+///
+/// If present, the first int value is a byte offset to add to the this
+/// pointer before calling the function.
+///
+/// For the virtual inheritance case, the offset to the vtable pointer
+/// from the location the this pointer points to must be known by the
+/// compiler when the member function pointer is called. The second int
+/// value is a byte offset into the vtable to an int value containing an
+/// additional byte offset to add to the this pointer.
+///
+/// For the unknown inheritance case, the second int value is a byte
+/// offset add to the this pointer to obtain a pointer to the vtable
+/// pointer, or undefined if not required. If the third int value is
+/// not zero, it is a byte offset into the vtable to an int value
+/// containing an additional byte offset to add to the this pointer.
+///
+/// It is not possible to support the virtual inheritance case without
+/// some way of obtaining the offset to the vtable pointer.
+class delegate_mfp_msvc
{
+ struct single_base_equiv { delegate_generic_function fptr; };
+ struct multi_base_equiv { delegate_generic_function fptr; int thisdisp; };
+ struct unknown_base_equiv { delegate_generic_function fptr; int thisdisp, vptrdisp, vtdisp; };
+
public:
// default constructor
- delegate_mfp()
- : m_function(0), m_this_delta(0), m_dummy1(0), m_dummy2(0), m_size(0)
- {
- }
+ delegate_mfp_msvc() noexcept = default;
// copy constructor
- delegate_mfp(const delegate_mfp &src)
- : m_function(src.m_function), m_this_delta(0), m_dummy1(0), m_dummy2(0), m_size(0)
- {
- }
+ delegate_mfp_msvc(const delegate_mfp_msvc &src) noexcept = default;
// construct from any member function pointer
template <typename MemberFunctionType, class MemberFunctionClass, typename ReturnType, typename StaticFunctionType>
- delegate_mfp(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType)
+ delegate_mfp_msvc(MemberFunctionType mfp, MemberFunctionClass *, ReturnType *, StaticFunctionType) noexcept
{
- //assert(sizeof(mfp) == 12 || sizeof(mfp) == 16);
- m_size = sizeof(mfp);
+ // FIXME: this doesn't actually catch the unsupported virtual inheritance case on 64-bit targets
+ // alignment of the pointer means sizeof gives the same value for multiple inheritance and virtual inheritance cases
+ static_assert(
+ (sizeof(mfp) == sizeof(single_base_equiv)) || (sizeof(mfp) == sizeof(multi_base_equiv)) || (sizeof(mfp) == sizeof(unknown_base_equiv)),
+ "Unsupported member function pointer size");
+ static_assert(sizeof(mfp) <= sizeof(*this), "Member function pointer is too large to support");
*reinterpret_cast<MemberFunctionType *>(this) = mfp;
+ m_size = sizeof(mfp);
}
// comparison helpers
- bool operator==(const delegate_mfp &rhs) const { return (m_function == rhs.m_function); }
- bool isnull() const { return (m_function == 0); }
+ bool operator==(const delegate_mfp_msvc &rhs) const noexcept
+ {
+ if (m_function != rhs.m_function)
+ {
+ return false;
+ }
+ else if (sizeof(single_base_equiv) == m_size)
+ {
+ return (sizeof(single_base_equiv) == rhs.m_size) || (!rhs.m_this_delta && ((sizeof(multi_base_equiv) == rhs.m_size) || !rhs.m_vt_index));
+ }
+ else if (sizeof(multi_base_equiv) == m_size)
+ {
+ if (sizeof(unknown_base_equiv) == rhs.m_size)
+ return (m_this_delta == rhs.m_this_delta) && !rhs.m_vt_index;
+ else
+ return (sizeof(single_base_equiv) == rhs.m_size) ? !m_this_delta : (m_this_delta == rhs.m_this_delta);
+ }
+ else if (sizeof(unknown_base_equiv) == rhs.m_size)
+ {
+ return (m_this_delta == rhs.m_this_delta) && (m_vt_index == rhs.m_vt_index) && (!m_vt_index || (m_vptr_offs == rhs.m_vptr_offs));
+ }
+ else
+ {
+ return !m_vt_index && ((sizeof(multi_base_equiv) == rhs.m_size) ? (m_this_delta == rhs.m_this_delta) : !m_this_delta);
+ }
+ }
+
+ bool isnull() const noexcept
+ {
+ return !reinterpret_cast<void (*)()>(m_function);
+ }
// getters
- static delegate_generic_class *real_object(delegate_generic_class *original) { return original; }
+ static delegate_generic_class *real_object(delegate_generic_class *original) noexcept { return original; }
- // binding helper
+ // binding helpers
template <typename FunctionType>
void update_after_bind(FunctionType &funcptr, delegate_generic_class *&object)
{
- funcptr = reinterpret_cast<FunctionType>(m_function);
- if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int))
- object = reinterpret_cast<delegate_generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
+ funcptr = reinterpret_cast<FunctionType>(adjust_this_pointer(object));
+ }
+
+ template <typename FunctionType>
+ void update_after_copy(FunctionType &funcptr, delegate_generic_class *&object) noexcept
+ {
}
private:
- // extract the generic function and adjust the object pointer
- delegate_generic_function convert_to_generic(delegate_generic_class *&object) const;
+ // adjust the object pointer and bypass thunks
+ delegate_generic_function adjust_this_pointer(delegate_generic_class *&object) const;
// actual state
- uintptr_t m_function; // first item can be one of two things:
- // if even, it's a pointer to the function
- // if odd, it's the byte offset into the vtable
- int m_this_delta; // delta to apply to the 'this' pointer
+ uintptr_t m_function = 0; // pointer to function or non-virtual thunk for virtual function call
+ int m_this_delta = 0; // delta to apply to the 'this' pointer for multiple inheritance
+ int m_vptr_offs = 0; // offset to apply to this pointer to obtain pointer to vptr
+ int m_vt_index = 0; // offset into vtable to additional delta to apply to the 'this' pointer
- int m_dummy1;
- int m_dummy2;
-
- int m_size;
+ unsigned m_size = 0; // overall size of the pointer to member function representation
};
+
+
+#if MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_COMPATIBLE
+
+template <typename ReturnType>
+struct delegate_mfp { using type = delegate_mfp_compatible; };
+
+#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_ITANIUM
+
+template <typename ReturnType>
+struct delegate_mfp { using type = delegate_mfp_itanium; };
+
+#elif MAME_DELEGATE_USE_TYPE == MAME_DELEGATE_TYPE_MSVC
+
+/// \brief Determine whether a type is returned conventionally
+///
+/// Under the MSVC C++ ABI with the Microsoft calling convention for
+/// x86-64 or AArch64, the calling convention for member functions is
+/// not quite the same as a free function with the "this" pointer as the
+/// first parameter.
+///
+/// Conventionally, structure and union values can be returned in
+/// registers if they are small enough and are aggregates (trivially
+/// constructible, destructible, copyable and assignable). On x86-64,
+/// if the value cannot be returned in registers, the pointer to the
+/// area for the return value is conventionally passed in RCX and
+/// explicit parameters are shifted by one position. On AArch64, if the
+/// value cannot be returned in registers, the pointer to the area for
+/// the return value is passed in X8 (explicit parameters do not need to
+/// be shifted).
+///
+/// For member functions, structure and union types are never returned
+/// in registers, and the pointer to the area for the return value is
+/// passed differently for structures and unions. When a structure or
+/// union is to be returned, a pointer to the area for the return value
+/// is effectively passed as a second implicit parameter. On x86-64,
+/// the "this" pointer is passed in RCX and the pointer to the area for
+/// the return value is passed in RDX; on AArch64, the "this" pointer is
+/// passed in X0 and the pointer to the area for the return value is
+/// passed in X1. Explicit parameters are shifted an additional
+/// position to allow for the second implicit parameter.
+///
+/// Note that pointer types are returned conventionally from member
+/// functions even when they're too large to return in registers (e.g. a
+/// pointer to a function member of a class with unknown inheritance).
+///
+/// Because of this, we may need to use the #delegate_mfp_compatible
+/// class to generate adaptor thunks depending on the return type. This
+/// trait doesn't need to reliably be true for types that are returned
+/// conventionally from member functions; it only needs to reliably be
+/// false for types that aren't. Incorrectly yielding true will result
+/// in incorrect behaviour while incorrectly yielding false will just
+/// cause increased overhead (both compile-time and run-time).
+template <typename ReturnType>
+using delegate_mfp_conventional_return = std::bool_constant<
+ std::is_void_v<ReturnType> ||
+ std::is_scalar_v<ReturnType> ||
+ std::is_reference_v<ReturnType> >;
+
+template <typename ReturnType, typename Enable = void>
+struct delegate_mfp;
+
+template <typename ReturnType>
+struct delegate_mfp<ReturnType, std::enable_if_t<delegate_mfp_conventional_return<ReturnType>::value> > { using type = delegate_mfp_msvc; };
+
+template <typename ReturnType>
+struct delegate_mfp<ReturnType, std::enable_if_t<!delegate_mfp_conventional_return<ReturnType>::value> > { using type = delegate_mfp_compatible; };
+
#endif
+template <typename ReturnType> using delegate_mfp_t = typename delegate_mfp<ReturnType>::type;
+
+
+
+/// \brief Helper class for generating late bind functions
+///
+/// Members of this class don't depend on the delegate's signature.
+/// Keeping them here reduces the number of template instantiations as
+/// you'll only need one late bind helper for each class used for late
+/// binding, not for each class for each delegate signature.
+template <class LateBindBase>
+class delegate_late_bind_helper
+{
+public:
+ // make it default constructible and copyable
+ delegate_late_bind_helper() noexcept = default;
+ delegate_late_bind_helper(delegate_late_bind_helper const &) noexcept = default;
+ delegate_late_bind_helper(delegate_late_bind_helper &&) noexcept = default;
+ delegate_late_bind_helper &operator=(delegate_late_bind_helper const &) noexcept = default;
+ delegate_late_bind_helper &operator=(delegate_late_bind_helper &&) noexcept = default;
+
+ template <class FunctionClass>
+ delegate_late_bind_helper(FunctionClass *) noexcept
+ : m_latebinder(&delegate_late_bind_helper::late_bind_helper<FunctionClass>)
+ {
+ }
+
+ delegate_generic_class *operator()(LateBindBase &object) { return m_latebinder(object); }
+
+ explicit operator bool() const noexcept { return bool(m_latebinder); }
+
+private:
+ using late_bind_func = delegate_generic_class *(*)(LateBindBase &object);
+
+ template <class FunctionClass> static delegate_generic_class *late_bind_helper(LateBindBase &object);
+
+ late_bind_func m_latebinder = nullptr;
+};
+
+
+template <class LateBindBase>
+template <class FunctionClass>
+delegate_generic_class *delegate_late_bind_helper<LateBindBase>::late_bind_helper(LateBindBase &object)
+{
+ FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
+ if (result)
+ return reinterpret_cast<delegate_generic_class *>(result);
+ throw binding_type_exception(typeid(FunctionClass), typeid(object));
+}
+
//**************************************************************************
// COMMON DELEGATE BASE CLASS
//**************************************************************************
-// ======================> delegate_base
-
-// general delegate class template supporting up to 5 parameters
-template <typename ReturnType, typename... Params>
+template <class LateBindBase, typename ReturnType, typename... Params>
class delegate_base
{
public:
// define our traits
template <class FunctionClass> using traits = delegate_traits<FunctionClass, ReturnType, Params...>;
- using functional_type = std::function<ReturnType (Params...)>;
using generic_static_func = typename traits<delegate_generic_class>::static_func_type;
- typedef MEMBER_ABI generic_static_func generic_member_func;
+ typedef MAME_ABI_CXX_MEMBER_CALL generic_static_func generic_member_func;
// generic constructor
- delegate_base()
- : m_function(nullptr)
- , m_object(nullptr)
- , m_latebinder(nullptr)
- , m_raw_function(nullptr)
- , m_std_func(nullptr)
- { }
+ delegate_base() noexcept = default;
// copy constructor
delegate_base(const delegate_base &src)
: m_function(src.m_function)
- , m_object(nullptr)
+ , m_object(src.m_object)
, m_latebinder(src.m_latebinder)
, m_raw_function(src.m_raw_function)
, m_raw_mfp(src.m_raw_mfp)
- , m_std_func(src.m_std_func)
{
- bind(src.object());
+ if (src.object() && is_mfp())
+ m_raw_mfp.update_after_copy(m_function, m_object);
}
// copy constructor with late bind
- delegate_base(const delegate_base &src, delegate_late_bind &object)
+ delegate_base(const delegate_base &src, LateBindBase &object)
: m_function(src.m_function)
- , m_object(nullptr)
, m_latebinder(src.m_latebinder)
, m_raw_function(src.m_raw_function)
, m_raw_mfp(src.m_raw_mfp)
- , m_std_func(src.m_std_func)
{
late_bind(object);
}
@@ -452,48 +708,29 @@ public:
// construct from member function with object pointer
template <class FunctionClass>
delegate_base(typename traits<FunctionClass>::member_func_type funcptr, FunctionClass *object)
- : m_function(nullptr)
- , m_object(nullptr)
- , m_latebinder(&late_bind_helper<FunctionClass>)
- , m_raw_function(nullptr)
+ : m_latebinder(object)
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
- , m_std_func(nullptr)
{
- bind(reinterpret_cast<delegate_generic_class *>(object));
+ bind(object);
}
+ // construct from const member function with object pointer
template <class FunctionClass>
delegate_base(typename traits<FunctionClass>::const_member_func_type funcptr, FunctionClass *object)
- : m_function(nullptr)
- , m_object(nullptr)
- , m_latebinder(&late_bind_helper<FunctionClass>)
- , m_raw_function(nullptr)
+ : m_latebinder(object)
, m_raw_mfp(funcptr, object, static_cast<ReturnType *>(nullptr), static_cast<generic_static_func>(nullptr))
- , m_std_func(nullptr)
{
- bind(reinterpret_cast<delegate_generic_class *>(object));
+ bind(object);
}
// construct from static reference function with object reference
template <class FunctionClass>
delegate_base(typename traits<FunctionClass>::static_ref_func_type funcptr, FunctionClass *object)
: m_function(reinterpret_cast<generic_static_func>(funcptr))
- , m_object(nullptr)
- , m_latebinder(&late_bind_helper<FunctionClass>)
+ , m_latebinder(object)
, m_raw_function(reinterpret_cast<generic_static_func>(funcptr))
- , m_std_func(nullptr)
- {
- bind(reinterpret_cast<delegate_generic_class *>(object));
- }
-
- // construct from static reference function with object reference
- delegate_base(functional_type funcptr)
- : m_function(nullptr)
- , m_object(nullptr)
- , m_latebinder(nullptr)
- , m_raw_function(nullptr)
- , m_std_func(funcptr)
{
+ bind(object);
}
// copy operator
@@ -502,82 +739,69 @@ public:
if (this != &src)
{
m_function = src.m_function;
- m_object = nullptr;
+ m_object = src.m_object;
m_latebinder = src.m_latebinder;
m_raw_function = src.m_raw_function;
m_raw_mfp = src.m_raw_mfp;
- m_std_func = src.m_std_func;
- bind(src.object());
+ if (src.object() && is_mfp())
+ m_raw_mfp.update_after_copy(m_function, m_object);
}
return *this;
}
// comparison helper
- bool operator==(const delegate_base &rhs) const
+ bool operator==(const delegate_base &rhs) const noexcept
{
- return (m_raw_function == rhs.m_raw_function && object() == rhs.object() && m_raw_mfp == rhs.m_raw_mfp && m_std_func.target_type().name() == rhs.m_std_func.target_type().name());
+ return (m_raw_function == rhs.m_raw_function) && (object() == rhs.object()) && (m_raw_mfp == rhs.m_raw_mfp);
}
-
// call the function
ReturnType operator()(Params... args) const
{
- if (is_mfp() && (HAS_DIFFERENT_ABI))
- return (*reinterpret_cast<generic_member_func>(m_function)) (m_object, std::forward<Params>(args)...);
- else if (m_std_func)
- return m_std_func(std::forward<Params>(args)...);
+ if ((MAME_DELEGATE_DIFFERENT_MEMBER_ABI) && is_mfp())
+ return (*reinterpret_cast<generic_member_func>(m_function))(m_object, std::forward<Params>(args)...);
else
- return (*m_function) (m_object, std::forward<Params>(args)...);
+ return (*m_function)(m_object, std::forward<Params>(args)...);
}
// getters
- bool has_object() const { return (object() != nullptr) || m_std_func; }
-
- // helpers
- bool isnull() const { return (m_raw_function == nullptr && m_raw_mfp.isnull() && !m_std_func); }
- bool is_mfp() const { return !m_raw_mfp.isnull(); }
+ bool has_object() const noexcept { return object() != nullptr; }
+ bool isnull() const noexcept { return !m_raw_function && m_raw_mfp.isnull(); }
+ bool is_mfp() const noexcept { return !m_raw_mfp.isnull(); }
// late binding
- void late_bind(delegate_late_bind &object) { if(m_latebinder) bind((*m_latebinder)(object)); }
+ void late_bind(LateBindBase &object)
+ {
+ if (m_latebinder)
+ bind(m_latebinder(object));
+ }
protected:
// return the actual object (not the one we use for calling)
- delegate_generic_class *object() const { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
-
- // late binding function
- using late_bind_func = delegate_generic_class*(*)(delegate_late_bind &object);
-
- // late binding helper
- template <class FunctionClass>
- static delegate_generic_class *late_bind_helper(delegate_late_bind &object)
- {
- FunctionClass *result = dynamic_cast<FunctionClass *>(&object);
- if (!result)
- throw binding_type_exception(typeid(FunctionClass), typeid(object));
-
- return reinterpret_cast<delegate_generic_class *>(result);
- }
+ delegate_generic_class *object() const noexcept { return is_mfp() ? m_raw_mfp.real_object(m_object) : m_object; }
// bind the actual object
- void bind(delegate_generic_class *object)
+ template <typename FunctionClass>
+ void bind(FunctionClass *object)
{
- m_object = object;
+ m_object = reinterpret_cast<delegate_generic_class *>(object);
// if we're wrapping a member function pointer, handle special stuff
- if (m_object != nullptr && is_mfp())
+ if (m_object && is_mfp())
m_raw_mfp.update_after_bind(m_function, m_object);
}
// internal state
- generic_static_func m_function; // resolved static function pointer
- delegate_generic_class * m_object; // resolved object to the post-cast object
- late_bind_func m_latebinder; // late binding helper
- generic_static_func m_raw_function; // raw static function pointer
- delegate_mfp m_raw_mfp; // raw member function pointer
- functional_type m_std_func; // std::function pointer
+ generic_static_func m_function = nullptr; // resolved static function pointer
+ delegate_generic_class * m_object = nullptr; // resolved object to the post-cast object
+ delegate_late_bind_helper<LateBindBase> m_latebinder; // late binding helper
+ generic_static_func m_raw_function = nullptr; // raw static function pointer
+ delegate_mfp_t<ReturnType> m_raw_mfp; // raw member function pointer
};
+} // namespace util::detail
+
//**************************************************************************
@@ -585,14 +809,73 @@ protected:
//**************************************************************************
// declare the base template
-template <typename Signature>
-class delegate;
+template <typename Signature, class LateBindBase = delegate_late_bind> class delegate;
-template <typename ReturnType, typename... Params>
-class delegate<ReturnType (Params...)> : public delegate_base<ReturnType, Params...>
+template <class LateBindBase, typename ReturnType, typename... Params>
+class delegate<ReturnType (Params...), LateBindBase> : public util::detail::delegate_base<LateBindBase, ReturnType, Params...>
{
private:
- using basetype = delegate_base<ReturnType, Params...>;
+ using basetype = util::detail::delegate_base<LateBindBase, ReturnType, Params...>;
+ using functoid_setter = void (*)(delegate &);
+
+ template <typename T> struct functoid_type_unwrap { using type = std::remove_reference_t<T>; };
+ template <typename T> struct functoid_type_unwrap<std::reference_wrapper<T> > { using type = typename functoid_type_unwrap<T>::type; };
+ template <typename T> using unwrapped_functoid_t = typename functoid_type_unwrap<std::remove_cv_t<std::remove_reference_t<T> > >::type;
+
+ template <typename T> static constexpr bool matching_non_const_call(T &&) { return false; }
+ template <typename T> static constexpr bool matching_non_const_call(ReturnType (T::*)(Params...)) { return true; }
+ template <typename T> static constexpr bool matching_const_call(T &&) { return false; }
+ template <typename T> static constexpr bool matching_const_call(ReturnType (T::*)(Params...) const) { return true; }
+
+ template <typename T> static T *unwrap_functoid(T *functoid) { return functoid; }
+ template <typename T> static T *unwrap_functoid(std::reference_wrapper<T> *functoid) { return &functoid->get(); }
+
+ template <typename T>
+ unwrapped_functoid_t<T> *unwrap_functoid() noexcept
+ {
+ return unwrap_functoid(std::any_cast<std::remove_cv_t<std::remove_reference_t<T> > >(&m_functoid));
+ }
+
+ template <typename T>
+ static functoid_setter make_functoid_setter()
+ {
+ if constexpr (matching_non_const_call(&unwrapped_functoid_t<T>::operator()))
+ {
+ return
+ [] (delegate &obj)
+ {
+ obj.basetype::operator=(
+ basetype(
+ static_cast<ReturnType (unwrapped_functoid_t<T>::*)(Params...)>(&unwrapped_functoid_t<T>::operator()),
+ obj.unwrap_functoid<T>()));
+ };
+ }
+ else if constexpr (matching_const_call(&unwrapped_functoid_t<T>::operator()))
+ {
+ return
+ [] (delegate &obj)
+ {
+ obj.basetype::operator=(
+ basetype(
+ static_cast<ReturnType (unwrapped_functoid_t<T>::*)(Params...) const>(&unwrapped_functoid_t<T>::operator()),
+ obj.unwrap_functoid<T>()));
+ };
+ }
+ else
+ {
+ return
+ [] (delegate &obj)
+ {
+ obj.basetype::operator=(
+ basetype(
+ [] (unwrapped_functoid_t<T> &f, Params... args) { return ReturnType(f(std::forward<Params>(args)...)); },
+ obj.unwrap_functoid<T>()));
+ };
+ }
+ }
+
+ std::any m_functoid;
+ functoid_setter m_set_functoid = nullptr;
protected:
template <class FunctionClass> using traits = typename basetype::template traits<FunctionClass>;
@@ -600,16 +883,99 @@ protected:
template <class FunctionClass> using const_member_func_type = typename traits<FunctionClass>::const_member_func_type;
template <class FunctionClass> using static_ref_func_type = typename traits<FunctionClass>::static_ref_func_type;
+ template <typename T> using suitable_functoid = std::is_invocable_r<ReturnType, T, Params...>;
+
public:
- // create a standard set of constructors
- delegate() : basetype() { }
- explicit delegate(const basetype &src) : basetype(src) { }
- delegate(const basetype &src, delegate_late_bind &object) : basetype(src, object) { }
+ delegate() noexcept : basetype() { }
+
+ delegate(delegate const &src)
+ : basetype(src.m_functoid.has_value() ? static_cast<const basetype &>(basetype()) : src)
+ , m_functoid(src.m_functoid)
+ , m_set_functoid(src.m_set_functoid)
+ {
+ if (m_functoid.has_value())
+ m_set_functoid(*this);
+ }
+
+ delegate(delegate &src)
+ : delegate(const_cast<delegate const &>(src))
+ {
+ }
+
+ delegate(delegate &&src)
+ : basetype(src.m_functoid.has_value() ? basetype() : std::move(src))
+ , m_functoid(std::move(src.m_functoid))
+ , m_set_functoid(std::move(src.m_set_functoid))
+ {
+ if (m_functoid.has_value())
+ m_set_functoid(*this);
+ }
+
+ delegate(delegate const &src, LateBindBase &object)
+ : basetype(src.m_functoid.has_value() ? basetype() : basetype(src, object))
+ , m_functoid(src.m_functoid)
+ , m_set_functoid(src.m_set_functoid)
+ {
+ if (m_functoid.has_value())
+ m_set_functoid(*this);
+ }
+
template <class FunctionClass> delegate(member_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
template <class FunctionClass> delegate(const_member_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
- explicit delegate(std::function<ReturnType (Params...)> funcptr) : basetype(funcptr) { }
template <class FunctionClass> delegate(static_ref_func_type<FunctionClass> funcptr, FunctionClass *object) : basetype(funcptr, object) { }
- delegate &operator=(const basetype &src) { *static_cast<basetype *>(this) = src; return *this; }
+
+ template <typename T>
+ explicit delegate(T &&functoid, std::enable_if_t<suitable_functoid<T>::value, int> = 0)
+ : basetype()
+ , m_functoid(std::forward<T>(functoid))
+ , m_set_functoid(make_functoid_setter<T>())
+ {
+ m_set_functoid(*this);
+ }
+
+ delegate &operator=(std::nullptr_t) noexcept
+ {
+ reset();
+ return *this;
+ }
+
+ delegate &operator=(delegate const &src)
+ {
+ m_functoid = src.m_functoid;
+ m_set_functoid = src.m_set_functoid;
+ if (m_functoid.has_value())
+ m_set_functoid(*this);
+ else
+ basetype::operator=(src);
+ return *this;
+ }
+
+ delegate &operator=(delegate &&src)
+ {
+ m_functoid = std::move(src.m_functoid);
+ m_set_functoid = std::move(src.m_set_functoid);
+ if (m_functoid.has_value())
+ m_set_functoid(*this);
+ else
+ basetype::operator=(std::move(src));
+ return *this;
+ }
+
+ void reset() noexcept
+ {
+ basetype::operator=(basetype());
+ m_functoid.reset();
+ m_set_functoid = nullptr;
+ }
};
-#endif // MAME_UTIL_DELEGATE_H
+template <class FunctionClass, typename ReturnType, typename... Params>
+delegate(ReturnType (FunctionClass::*)(Params...), FunctionClass *) -> delegate<ReturnType (Params...)>;
+
+template <class FunctionClass, typename ReturnType, typename... Params>
+delegate(ReturnType (FunctionClass::*)(Params...) const, FunctionClass *) -> delegate<ReturnType (Params...)>;
+
+template <class FunctionClass, typename ReturnType, typename... Params>
+delegate(ReturnType (*)(FunctionClass &, Params...), FunctionClass *) -> delegate<ReturnType (Params...)>;
+
+#endif // MAME_LIB_UTIL_DELEGATE_H
diff --git a/src/lib/util/disasmintf.cpp b/src/lib/util/disasmintf.cpp
index 9699f68e823..c78dc6b0f85 100644
--- a/src/lib/util/disasmintf.cpp
+++ b/src/lib/util/disasmintf.cpp
@@ -1,16 +1,16 @@
#include "disasmintf.h"
-util::u32 util::disasm_interface::interface_flags() const
+util::disasm_interface::u32 util::disasm_interface::interface_flags() const
{
return 0;
}
-util::u32 util::disasm_interface::page_address_bits() const
+util::disasm_interface::u32 util::disasm_interface::page_address_bits() const
{
throw ("unimplemented page_address_bits called");
}
-util::u32 util::disasm_interface::page2_address_bits() const
+util::disasm_interface::u32 util::disasm_interface::page2_address_bits() const
{
throw ("unimplemented page2_address_bits called");
}
@@ -25,22 +25,22 @@ util::disasm_interface::offs_t util::disasm_interface::pc_real_to_linear(offs_t
throw ("unimplemented pc_real_to_linear called");
}
-util::u8 util::disasm_interface::decrypt8(u8 value, offs_t pc, bool opcode) const
+util::disasm_interface::u8 util::disasm_interface::decrypt8(u8 value, offs_t pc, bool opcode) const
{
throw ("unimplemented decrypt8 called");
}
-util::u16 util::disasm_interface::decrypt16(u16 value, offs_t pc, bool opcode) const
+util::disasm_interface::u16 util::disasm_interface::decrypt16(u16 value, offs_t pc, bool opcode) const
{
throw ("unimplemented decrypt16 called");
}
-util::u32 util::disasm_interface::decrypt32(u32 value, offs_t pc, bool opcode) const
+util::disasm_interface::u32 util::disasm_interface::decrypt32(u32 value, offs_t pc, bool opcode) const
{
throw ("unimplemented decrypt32 called");
}
-util::u64 util::disasm_interface::decrypt64(u64 value, offs_t pc, bool opcode) const
+util::disasm_interface::u64 util::disasm_interface::decrypt64(u64 value, offs_t pc, bool opcode) const
{
throw ("unimplemented decrypt64 called");
}
diff --git a/src/lib/util/disasmintf.h b/src/lib/util/disasmintf.h
index 78c77a9abb2..562ba5de00a 100644
--- a/src/lib/util/disasmintf.h
+++ b/src/lib/util/disasmintf.h
@@ -13,7 +13,8 @@
#ifndef MAME_UTIL_DISASMINTF_H
#define MAME_UTIL_DISASMINTF_H
-#include "coretmpl.h"
+#include "osdcomm.h"
+#include <ostream>
namespace util {
@@ -38,8 +39,9 @@ public:
static constexpr u32 SUPPORTED = 0x80000000; // are disassembly flags supported?
static constexpr u32 STEP_OUT = 0x40000000; // this instruction should be the end of a step out sequence
static constexpr u32 STEP_OVER = 0x20000000; // this instruction should be stepped over by setting a breakpoint afterwards
- static constexpr u32 OVERINSTMASK = 0x18000000; // number of extra instructions to skip when stepping over
- static constexpr u32 OVERINSTSHIFT = 27; // bits to shift after masking to get the value
+ static constexpr u32 STEP_COND = 0x10000000; // this instruction may conditionally result in a program transfer or short skip
+ static constexpr u32 OVERINSTMASK = 0x0c000000; // number of extra instructions to skip when stepping over
+ static constexpr u32 OVERINSTSHIFT = 26; // bits to shift after masking to get the value
static constexpr u32 LENGTHMASK = 0x0000ffff; // the low 16-bits contain the actual length
static inline u32 step_over_extra(u32 x) {
diff --git a/src/lib/util/dvdrom.cpp b/src/lib/util/dvdrom.cpp
new file mode 100644
index 00000000000..0b2379ef7e6
--- /dev/null
+++ b/src/lib/util/dvdrom.cpp
@@ -0,0 +1,105 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,R. Belmont
+/***************************************************************************
+
+ dvdrom.c
+
+ Generic MAME DVD-ROM utilties - build IDE and SCSI DVD-ROMs on top of this
+
+***************************************************************************/
+
+#include "dvdrom.h"
+
+#include "corestr.h"
+#include "osdfile.h"
+#include "strformat.h"
+
+#include <cassert>
+#include <cstdlib>
+
+
+
+/**
+ * @fn constructor
+ *
+ * @brief Open a dvdrom for a file.
+ *
+ * @param inputfile The inputfile.
+ */
+
+dvdrom_file::dvdrom_file(std::string_view inputfile)
+{
+ fhandle = nullptr;
+}
+
+/*-------------------------------------------------
+ constructor - "open" a DVD-ROM file from an
+ already-opened CHD file
+-------------------------------------------------*/
+
+/**
+ * @fn dvdrom_file *dvdrom_open(chd_file *chd)
+ *
+ * @brief Queries if a given dvdrom open.
+ *
+ * @param [in,out] chd If non-null, the chd.
+ *
+ * @return null if it fails, else a dvdrom_file*.
+ */
+
+dvdrom_file::dvdrom_file(chd_file *_chd)
+{
+ chd = _chd;
+
+ /* validate the CHD information */
+ if (chd->hunk_bytes() != 2048)
+ throw nullptr;
+ if (chd->unit_bytes() != 2048)
+ throw nullptr;
+
+ /* check it's actually a DVD-ROM */
+ if (std::error_condition err = chd->check_is_dvd())
+ throw err;
+
+ sector_count = chd->unit_count();
+}
+
+
+/*-------------------------------------------------
+ destructor - "close" a DVD-ROM file
+-------------------------------------------------*/
+
+dvdrom_file::~dvdrom_file()
+{
+}
+
+
+
+/***************************************************************************
+ CORE READ ACCESS
+***************************************************************************/
+
+
+/*-------------------------------------------------
+ dvdrom_read_data - read one 2048 bytes sector
+ from a DVD-ROM
+-------------------------------------------------*/
+
+/**
+ * @fn bool read_data(uint32_t lbasector, void *buffer)
+ *
+ * @brief Dvdrom read data.
+ *
+ * @param lbasector The lbasector.
+ * @param buffer The buffer.
+ *
+ * @return Success status.
+ */
+
+std::error_condition dvdrom_file::read_data(uint32_t lbasector, void *buffer)
+{
+ if (lbasector >= sector_count)
+ return std::error_condition(chd_file::error::HUNK_OUT_OF_RANGE);
+
+ return chd->read_hunk(lbasector, buffer);
+}
diff --git a/src/lib/util/dvdrom.h b/src/lib/util/dvdrom.h
new file mode 100644
index 00000000000..47bd6a26172
--- /dev/null
+++ b/src/lib/util/dvdrom.h
@@ -0,0 +1,41 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles,R. Belmont
+/***************************************************************************
+
+ dvdrom.h
+
+ Generic MAME dvd-rom implementation
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_DVDROM_H
+#define MAME_LIB_UTIL_DVDROM_H
+
+#pragma once
+
+#include "chd.h"
+#include "ioprocs.h"
+#include "osdcore.h"
+
+class dvdrom_file {
+public:
+ dvdrom_file(chd_file *chd);
+ dvdrom_file(std::string_view inputfile);
+ ~dvdrom_file();
+
+ uint32_t get_sector_count() const { return sector_count; }
+
+ /* core read access */
+ std::error_condition read_data(uint32_t lbasector, void *buffer);
+
+private:
+ /** @brief The chd, when there's one */
+ chd_file * chd; /* CHD file */
+
+ /** @brief The raw file, when there isn't */
+ util::random_read_write * fhandle; // file if not a CHD
+
+ /** @brief Size of the dvd image in sectors */
+ uint32_t sector_count;
+};
+
+#endif // MAME_LIB_UTIL_DVDROM_H
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/endianness.h b/src/lib/util/endianness.h
new file mode 100644
index 00000000000..b93c1ffe44f
--- /dev/null
+++ b/src/lib/util/endianness.h
@@ -0,0 +1,161 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ endianness.h
+
+ Endianness types and utility functions.
+
+***************************************************************************/
+
+#ifndef MAME_LIB_UTIL_ENDIANNESS_H
+#define MAME_LIB_UTIL_ENDIANNESS_H
+
+#pragma once
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <string_view>
+#include <type_traits>
+
+
+namespace util {
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// constants for expression endianness
+enum class endianness
+{
+ little,
+ big,
+#ifdef LSB_FIRST
+ native = little
+#else
+ native = big
+#endif
+};
+
+
+// helper for accessing data adjusted for endianness
+template <typename In, typename Out, endianness Endian>
+class offset_endian_cast
+{
+private:
+ static inline constexpr std::ptrdiff_t SWIZZLE = (sizeof(In) / sizeof(Out)) - 1;
+
+ static_assert(!(sizeof(In) % sizeof(Out)), "input size must be a multiple of output size");
+ static_assert(!((sizeof(In) / sizeof(Out)) & SWIZZLE), "ratio of input size to output size must be a power of two");
+
+ Out *m_ptr;
+ std::ptrdiff_t m_offs;
+
+public:
+ constexpr offset_endian_cast(In *ptr, std::ptrdiff_t offs) noexcept : m_ptr(reinterpret_cast<Out *>(ptr)), m_offs(offs) { }
+
+ constexpr Out &operator[](std::ptrdiff_t i) const noexcept { return m_ptr[(m_offs + i) ^ ((Endian != endianness::native) ? SWIZZLE : 0)]; }
+ constexpr Out &operator*() const noexcept { return m_ptr[m_offs ^ ((Endian != endianness::native) ? SWIZZLE : 0)]; }
+
+ constexpr offset_endian_cast operator+(std::ptrdiff_t i) const noexcept { return offset_endian_cast(*this) += i; }
+ constexpr offset_endian_cast operator-(std::ptrdiff_t i) const noexcept { return offset_endian_cast(*this) -= i; }
+
+ offset_endian_cast &operator+=(std::ptrdiff_t i) noexcept { m_offs += i; return *this; }
+ offset_endian_cast &operator-=(std::ptrdiff_t i) noexcept { m_offs -= i; return *this; }
+ offset_endian_cast &operator++() noexcept { ++m_offs; return *this; }
+ offset_endian_cast &operator--() noexcept { --m_offs; return *this; }
+ offset_endian_cast operator++(int) noexcept { offset_endian_cast result(*this); ++m_offs; return result; }
+ offset_endian_cast operator--(int) noexcept { offset_endian_cast result(*this); --m_offs; return result; }
+};
+
+
+// helper for accessing data adjusted for endianness
+template <typename In, typename Out, endianness Endian>
+class endian_cast
+{
+private:
+ static inline constexpr std::ptrdiff_t SWIZZLE = (sizeof(In) / sizeof(Out)) - 1;
+
+ static_assert(!(sizeof(In) % sizeof(Out)), "input size must be a multiple of output size");
+ static_assert(!((sizeof(In) / sizeof(Out)) & SWIZZLE), "ratio of input size to output size must be a power of two");
+
+ Out *m_ptr;
+
+public:
+ constexpr endian_cast(In *ptr) noexcept : m_ptr(reinterpret_cast<Out *>(ptr)) { }
+
+ constexpr Out &operator[](std::ptrdiff_t i) const noexcept { return m_ptr[i ^ ((Endian != endianness::native) ? SWIZZLE : 0)]; }
+
+ constexpr auto operator+(std::ptrdiff_t offs) const noexcept
+ {
+ using required_const = std::conditional_t<std::is_const_v<Out>, std::add_const_t<In>, In>;
+ using required_cv = std::conditional_t<std::is_volatile_v<Out>, std::add_volatile<required_const>, required_const>;
+ return offset_endian_cast<required_cv, Out, Endian>(reinterpret_cast<required_cv *>(m_ptr), offs);
+ }
+
+ constexpr auto operator-(std::ptrdiff_t offs) const noexcept
+ {
+ using required_const = std::conditional_t<std::is_const_v<Out>, std::add_const_t<In>, In>;
+ using required_cv = std::conditional_t<std::is_volatile_v<Out>, std::add_volatile<required_const>, required_const>;
+ return offset_endian_cast<required_cv, Out, Endian>(reinterpret_cast<required_cv *>(m_ptr), -offs);
+ }
+};
+
+
+
+//**************************************************************************
+// MACROS AND INLINE FUNCTIONS
+//**************************************************************************
+
+constexpr std::string_view endian_to_string_view(endianness e) { using namespace std::literals; return e == endianness::little ? "little"sv : "big"sv; }
+
+// endian-based value: first value is if native endianness is little-endian, second is if native is big-endian
+#define NATIVE_ENDIAN_VALUE_LE_BE(leval, beval) ((util::endianness::native == util::endianness::little) ? (leval) : (beval))
+
+
+// inline functions for accessing bytes and words within larger chunks
+
+template <typename T, typename U>
+auto big_endian_cast(U *ptr)
+{
+ using requested_const = std::conditional_t<std::is_const_v<U>, std::add_const_t<T>, T>;
+ using requested_cv = std::conditional_t<std::is_volatile_v<U>, std::add_volatile<requested_const>, requested_const>;
+ return endian_cast<U, requested_cv, endianness::big>(ptr);
+}
+
+template <typename T, typename U>
+auto little_endian_cast(U *ptr)
+{
+ using requested_const = std::conditional_t<std::is_const_v<U>, std::add_const_t<T>, T>;
+ using requested_cv = std::conditional_t<std::is_volatile_v<U>, std::add_volatile<requested_const>, requested_const>;
+ return endian_cast<U, requested_cv, endianness::little>(ptr);
+}
+
+// read/write a byte to a 16-bit space
+template <typename T> constexpr T BYTE_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(1,0); }
+template <typename T> constexpr T BYTE_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,1); }
+
+// read/write a byte to a 32-bit space
+template <typename T> constexpr T BYTE4_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(3,0); }
+template <typename T> constexpr T BYTE4_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,3); }
+
+// read/write a word to a 32-bit space
+template <typename T> constexpr T WORD_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(2,0); }
+template <typename T> constexpr T WORD_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,2); }
+
+// read/write a byte to a 64-bit space
+template <typename T> constexpr T BYTE8_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(7,0); }
+template <typename T> constexpr T BYTE8_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,7); }
+
+// read/write a word to a 64-bit space
+template <typename T> constexpr T WORD2_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(6,0); }
+template <typename T> constexpr T WORD2_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,6); }
+
+// read/write a dword to a 64-bit space
+template <typename T> constexpr T DWORD_XOR_BE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(4,0); }
+template <typename T> constexpr T DWORD_XOR_LE(T a) { return a ^ NATIVE_ENDIAN_VALUE_LE_BE(0,4); }
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_ENDIANNESS_H
diff --git a/src/lib/util/flac.cpp b/src/lib/util/flac.cpp
index 54a589ee2a2..d3110ca0d38 100644
--- a/src/lib/util/flac.cpp
+++ b/src/lib/util/flac.cpp
@@ -2,16 +2,23 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- flac.c
+ flac.cpp
FLAC compression wrappers
***************************************************************************/
-#include <assert.h>
-
#include "flac.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <iterator>
#include <new>
+#include <tuple>
//**************************************************************************
@@ -35,7 +42,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);
@@ -78,6 +85,8 @@ bool flac_encoder::reset()
FLAC__stream_encoder_set_blocksize(m_encoder, m_block_size);
// re-start processing
+ if (m_file)
+ return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, seek_callback_static, tell_callback_static, nullptr, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK);
return (FLAC__stream_encoder_init_stream(m_encoder, write_callback_static, nullptr, nullptr, nullptr, this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK);
}
@@ -100,7 +109,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;
@@ -127,7 +136,7 @@ bool flac_encoder::encode_interleaved(const int16_t *samples, uint32_t samples_p
// process in batches of 2k samples
FLAC__int32 converted_buffer[2048];
FLAC__int32 *dest = converted_buffer;
- uint32_t cur_samples = (std::min<size_t>)(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel);
+ uint32_t cur_samples = (std::min<size_t>)(std::size(converted_buffer) / num_channels, samples_per_channel);
// convert a buffers' worth
for (uint32_t sampnum = 0; sampnum < cur_samples; sampnum++)
@@ -160,7 +169,7 @@ bool flac_encoder::encode(int16_t *const *samples, uint32_t samples_per_channel,
// process in batches of 2k samples
FLAC__int32 converted_buffer[2048];
FLAC__int32 *dest = converted_buffer;
- uint32_t cur_samples = (std::min<size_t>)(ARRAY_LENGTH(converted_buffer) / num_channels, samples_per_channel);
+ uint32_t cur_samples = (std::min<size_t>)(std::size(converted_buffer) / num_channels, samples_per_channel);
// convert a buffers' worth
for (uint32_t sampnum = 0; sampnum < cur_samples; sampnum++, srcindex++)
@@ -185,7 +194,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;
+ }
}
@@ -230,29 +248,29 @@ 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];
+ m_ignore_bytes = get_u24be(&buffer[offset + 1]);
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)
+ {
+ /*auto const [err, actual] =*/ write(*m_file, buffer, count); // FIXME: check for errors
+ }
else
{
if (m_compressed_offset + count <= m_compressed_length)
@@ -265,6 +283,38 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
+FLAC__StreamEncoderSeekStatus flac_encoder::seek_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data)
+{
+ return reinterpret_cast<flac_encoder *>(client_data)->seek_callback(absolute_byte_offset);
+}
+
+FLAC__StreamEncoderSeekStatus flac_encoder::seek_callback(FLAC__uint64 absolute_byte_offset)
+{
+ if (m_file)
+ {
+ if (!m_file->seek(absolute_byte_offset, SEEK_SET))
+ return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
+ return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
+ }
+ return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED;
+}
+
+FLAC__StreamEncoderTellStatus flac_encoder::tell_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
+{
+ return reinterpret_cast<flac_encoder *>(client_data)->tell_callback(absolute_byte_offset);
+}
+
+FLAC__StreamEncoderTellStatus flac_encoder::tell_callback(FLAC__uint64 *absolute_byte_offset)
+{
+ if (m_file)
+ {
+ if (!m_file->tell(*absolute_byte_offset))
+ return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
+ return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
+ }
+ return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED;
+}
+
//**************************************************************************
@@ -280,7 +330,7 @@ flac_decoder::flac_decoder()
m_file(nullptr),
m_sample_rate(0),
m_channels(0),
- m_bits_per_sample(0),
+ m_bits_per_sample(16),
m_compressed_offset(0),
m_compressed_start(nullptr),
m_compressed_length(0),
@@ -300,6 +350,9 @@ flac_decoder::flac_decoder()
flac_decoder::flac_decoder(const void *buffer, uint32_t length, const void *buffer2, uint32_t length2)
: m_decoder(FLAC__stream_decoder_new()),
m_file(nullptr),
+ m_sample_rate(0),
+ m_channels(0),
+ m_bits_per_sample(16),
m_compressed_offset(0),
m_compressed_start(reinterpret_cast<const FLAC__byte *>(buffer)),
m_compressed_length(length),
@@ -314,9 +367,12 @@ 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_sample_rate(0),
+ m_channels(0),
+ m_bits_per_sample(16),
m_compressed_offset(0),
m_compressed_start(nullptr),
m_compressed_length(0),
@@ -400,11 +456,9 @@ bool flac_decoder::reset(uint32_t sample_rate, uint8_t num_channels, uint32_t bl
// +2A: start of stream data
};
memcpy(m_custom_header, s_header_template, sizeof(s_header_template));
- m_custom_header[0x08] = m_custom_header[0x0a] = block_size >> 8;
- m_custom_header[0x09] = m_custom_header[0x0b] = block_size & 0xff;
- m_custom_header[0x12] = sample_rate >> 12;
- m_custom_header[0x13] = sample_rate >> 4;
- m_custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
+ put_u16be(&m_custom_header[0x08], block_size);
+ put_u16be(&m_custom_header[0x0a], block_size);
+ put_u24be(&m_custom_header[0x12], (sample_rate << 4) | ((num_channels - 1) << 1));
// configure the header ahead of the provided buffer
m_file = nullptr;
@@ -420,7 +474,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;
@@ -462,7 +516,7 @@ bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_end
{
// make sure we don't have too many channels
int chans = channels();
- if (chans > ARRAY_LENGTH(m_uncompressed_start))
+ if (chans > std::size(m_uncompressed_start))
return false;
// configure the uncompressed buffer
@@ -515,12 +569,12 @@ 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
+ {
+ std::error_condition err;
+ std::tie(err, *bytes) = read(*m_file, buffer, expected); // FIXME: check for errors
+ }
+ else // otherwise, copy from memory
{
// copy from primary buffer first
uint32_t outputpos = 0;
@@ -559,9 +613,8 @@ void flac_decoder::metadata_callback_static(const FLAC__StreamDecoder *decoder,
return;
// parse out the data we care about
- flac_decoder *fldecoder = reinterpret_cast<flac_decoder *>(client_data);
+ auto *fldecoder = reinterpret_cast<flac_decoder *>(client_data);
fldecoder->m_sample_rate = metadata->data.stream_info.sample_rate;
- fldecoder->m_bits_per_sample = metadata->data.stream_info.bits_per_sample;
fldecoder->m_channels = metadata->data.stream_info.channels;
}
@@ -583,33 +636,83 @@ FLAC__StreamDecoderTellStatus flac_decoder::tell_callback_static(const FLAC__Str
// stream
//-------------------------------------------------
-FLAC__StreamDecoderWriteStatus flac_decoder::write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+FLAC__StreamDecoderWriteStatus flac_decoder::write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
{
return reinterpret_cast<flac_decoder *>(client_data)->write_callback(frame, buffer);
}
-FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[])
+FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[])
{
assert(frame->header.channels == channels());
+ if (frame->header.bits_per_sample > m_bits_per_sample)
+ {
+ if (m_uncompressed_swap)
+ return write_callback<SCALE_DOWN, true>(frame, buffer);
+ else
+ return write_callback<SCALE_DOWN, false>(frame, buffer);
+ }
+ else if (frame->header.bits_per_sample < m_bits_per_sample)
+ {
+ if (m_uncompressed_swap)
+ return write_callback<SCALE_UP, true>(frame, buffer);
+ else
+ return write_callback<SCALE_UP, false>(frame, buffer);
+ }
+ else
+ {
+ if (m_uncompressed_swap)
+ return write_callback<SCALE_SAME, true>(frame, buffer);
+ else
+ return write_callback<SCALE_SAME, false>(frame, buffer);
+ }
+}
+
+template <flac_decoder::DECODE_MODE Mode, bool SwapEndian> FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[])
+{
+ const int blocksize = frame->header.blocksize;
+ const int shift = (Mode == SCALE_DOWN) ? frame->header.bits_per_sample - m_bits_per_sample : (Mode == SCALE_UP) ? m_bits_per_sample - frame->header.bits_per_sample : 0;
+ const int low_bit = 1 << shift;
+ const int right_shift = m_bits_per_sample % frame->header.bits_per_sample;
+ int multiplier = 0;
+ for (int bits_to_set = m_bits_per_sample; bits_to_set > frame->header.bits_per_sample; bits_to_set -= frame->header.bits_per_sample)
+ {
+ multiplier = (multiplier << frame->header.bits_per_sample) | (1 << frame->header.bits_per_sample);
+ }
+
// interleaved case
- int shift = m_uncompressed_swap ? 8 : 0;
- int blocksize = frame->header.blocksize;
if (m_uncompressed_start[1] == nullptr)
{
int16_t *dest = m_uncompressed_start[0] + m_uncompressed_offset * frame->header.channels;
for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++)
for (int chan = 0; chan < frame->header.channels; chan++)
- *dest++ = int16_t((uint16_t(buffer[chan][sampnum]) << shift) | (uint16_t(buffer[chan][sampnum]) >> shift));
+ {
+ int16_t output_sample;
+ if (Mode == SCALE_DOWN)
+ output_sample = int16_t((buffer[chan][sampnum] + (buffer[chan][sampnum] & low_bit)) >> shift);
+ else if (Mode == SCALE_UP)
+ output_sample = int16_t((buffer[chan][sampnum] * multiplier) | ((buffer[chan][sampnum] >> right_shift)));
+ else
+ output_sample = int16_t(buffer[chan][sampnum]);
+ *dest++ = SwapEndian ? int16_t(swapendian_int16(uint16_t(output_sample))) : output_sample;
+ }
}
-
// non-interleaved case
else
{
for (int sampnum = 0; sampnum < blocksize && m_uncompressed_offset < m_uncompressed_length; sampnum++, m_uncompressed_offset++)
for (int chan = 0; chan < frame->header.channels; chan++)
if (m_uncompressed_start[chan] != nullptr)
- m_uncompressed_start[chan][m_uncompressed_offset] = int16_t((uint16_t(buffer[chan][sampnum]) << shift) | (uint16_t(buffer[chan][sampnum]) >> shift));
+ {
+ int16_t output_sample;
+ if (Mode == SCALE_DOWN)
+ output_sample = int16_t((buffer[chan][sampnum] + (buffer[chan][sampnum] & low_bit)) >> shift);
+ else if (Mode == SCALE_UP)
+ output_sample = int16_t((buffer[chan][sampnum] * multiplier) | ((buffer[chan][sampnum] >> right_shift)));
+ else
+ output_sample = int16_t(buffer[chan][sampnum]);
+ m_uncompressed_start[chan][m_uncompressed_offset] = SwapEndian ? int16_t(swapendian_int16(uint16_t(output_sample))) : output_sample;
+ }
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h
index 20e3f1b9378..be3a0a8ac96 100644
--- a/src/lib/util/flac.h
+++ b/src/lib/util/flac.h
@@ -13,11 +13,12 @@
#pragma once
-#include "osdcore.h"
-#include "corefile.h"
+#include "utilfwd.h"
#include <FLAC/all.h>
+#include <cstdint>
+
//**************************************************************************
// TYPE DEFINITIONS
@@ -31,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
@@ -47,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);
@@ -61,22 +62,26 @@ private:
void init_common();
static FLAC__StreamEncoderWriteStatus write_callback_static(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data);
FLAC__StreamEncoderWriteStatus write_callback(const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame);
+ static FLAC__StreamEncoderSeekStatus seek_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data);
+ FLAC__StreamEncoderSeekStatus seek_callback(FLAC__uint64 absolute_byte_offset);
+ static FLAC__StreamEncoderTellStatus tell_callback_static(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
+ FLAC__StreamEncoderTellStatus tell_callback(FLAC__uint64 *absolute_byte_offset);
// internal state
FLAC__StreamEncoder * m_encoder; // actual encoder
- util::core_file * m_file; // output file
- uint32_t m_compressed_offset; // current offset with the compressed stream
+ 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
+ uint32_t m_compressed_length; // length of the compressed stream
// parameters
- uint32_t m_sample_rate; // sample rate
- uint8_t m_channels; // number of channels
- uint32_t m_block_size; // block size
+ uint32_t m_sample_rate; // sample rate
+ uint8_t m_channels; // number of channels
+ uint32_t m_block_size; // block size
// header stripping
bool m_strip_metadata; // strip the metadata?
- uint32_t m_ignore_bytes; // how many bytes to ignore when writing
+ uint32_t m_ignore_bytes; // how many bytes to ignore when writing
bool m_found_audio; // have we hit the audio yet?
};
@@ -89,7 +94,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)
@@ -104,7 +109,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);
@@ -114,18 +119,22 @@ public:
uint32_t finish();
private:
+ enum DECODE_MODE {
+ SCALE_UP, SCALE_DOWN, SCALE_SAME
+ };
// internal helpers
static FLAC__StreamDecoderReadStatus read_callback_static(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data);
FLAC__StreamDecoderReadStatus read_callback(FLAC__byte buffer[], size_t *bytes);
static void metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data);
static FLAC__StreamDecoderTellStatus tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data);
- static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data);
- FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
+ static FLAC__StreamDecoderWriteStatus write_callback_static(const FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data);
+ FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
+ template <DECODE_MODE Mode, bool SwapEndian> FLAC__StreamDecoderWriteStatus write_callback(const ::FLAC__Frame *frame, const FLAC__int32 *const buffer[]);
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 5db97e3417b..266e5d25a2b 100644
--- a/src/lib/util/harddisk.cpp
+++ b/src/lib/util/harddisk.cpp
@@ -8,88 +8,62 @@
***************************************************************************/
-#include <assert.h>
#include "harddisk.h"
-#include "osdcore.h"
-#include <stdlib.h>
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
+#include "chd.h"
+#include "ioprocs.h"
-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 */
-};
+#include "osdcore.h"
+#include <cstdlib>
+#include <tuple>
-/***************************************************************************
- CORE IMPLEMENTATION
-***************************************************************************/
-
/*-------------------------------------------------
- hard_disk_open - open a hard disk handle,
+ constructor - open a hard disk handle,
given a chd_file
-------------------------------------------------*/
-hard_disk_file *hard_disk_open(chd_file *chd)
+hard_disk_file::hard_disk_file(chd_file *_chd)
{
- int cylinders, heads, sectors, sectorbytes;
- hard_disk_file *file;
+ chd = _chd;
+ fhandle = nullptr;
+ fileoffset = 0;
+
std::string metadata;
- chd_error err;
+ std::error_condition err;
/* punt if no CHD */
if (chd == nullptr)
- return nullptr;
+ throw nullptr;
/* read the hard disk metadata */
- err = chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
- if (err != CHDERR_NONE)
- return nullptr;
+ err = _chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata);
+ if (err)
+ throw nullptr;
/* parse the metadata */
- if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &cylinders, &heads, &sectors, &sectorbytes) != 4)
- return nullptr;
-
- /* allocate memory for the hard disk file */
- file = (hard_disk_file *)malloc(sizeof(hard_disk_file));
- if (file == nullptr)
- return nullptr;
-
- /* fill in the data */
- file->chd = chd;
- file->fhandle = nullptr;
- file->info.cylinders = cylinders;
- file->info.heads = heads;
- file->info.sectors = sectors;
- file->info.sectorbytes = sectorbytes;
- file->info.fileoffset = 0;
- return file;
+ if (sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &hdinfo.cylinders, &hdinfo.heads, &hdinfo.sectors, &hdinfo.sectorbytes) != 4)
+ throw nullptr;
}
-hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
+hard_disk_file::hard_disk_file(util::random_read_write &corefile, uint32_t skipoffs)
{
- hard_disk_file *file;
-
- /* allocate memory for the hard disk file */
- file = (hard_disk_file *)malloc(sizeof(hard_disk_file));
- if (file == nullptr)
- return nullptr;
-
- file->chd = nullptr;
- file->fhandle = &corefile;
- file->info.sectorbytes = 512;
- file->info.cylinders = 0;
- file->info.heads = 0;
- file->info.sectors = 0;
- file->info.fileoffset = skipoffs;
+ // bail if getting the file length fails
+ std::uint64_t length;
+ if (corefile.length(length))
+ throw nullptr;
+
+ chd = nullptr;
+ fhandle = &corefile;
+ hdinfo.sectorbytes = 512;
+ hdinfo.cylinders = 0;
+ hdinfo.heads = 0;
+ hdinfo.sectors = 0;
+ 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) / hdinfo.sectorbytes; ; totalsectors++)
for (uint32_t cursectors = 63; cursectors > 1; cursectors--)
if (totalsectors % cursectors == 0)
{
@@ -97,128 +71,151 @@ hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs)
for (uint32_t curheads = 16; curheads > 1; curheads--)
if (totalheads % curheads == 0)
{
- file->info.cylinders = totalheads / curheads;
- file->info.heads = curheads;
- file->info.sectors = cursectors;
- osd_printf_verbose("Guessed CHS of %d/%d/%d\n", file->info.cylinders, file->info.heads, file->info.sectors);
- return file;
+ hdinfo.cylinders = totalheads / curheads;
+ hdinfo.heads = curheads;
+ hdinfo.sectors = cursectors;
+ osd_printf_verbose("Guessed CHS of %d/%d/%d\n", hdinfo.cylinders, hdinfo.heads, hdinfo.sectors);
+ return;
}
}
-
- return file;
-}
-
-
-/*-------------------------------------------------
- hard_disk_close - close a hard disk handle
--------------------------------------------------*/
-
-void hard_disk_close(hard_disk_file *file)
-{
- if (file->fhandle)
- {
- file->fhandle->flush();
- }
-
- free(file);
}
/*-------------------------------------------------
- hard_disk_get_chd - get a handle to a CHD
- from a hard disk
+ destructor - close a hard disk handle
-------------------------------------------------*/
-chd_file *hard_disk_get_chd(hard_disk_file *file)
+hard_disk_file::~hard_disk_file()
{
- return file->chd;
+ if (fhandle)
+ fhandle->flush();
}
/*-------------------------------------------------
- hard_disk_get_info - return information about
- a hard disk
+ read - read sectors from a hard disk
-------------------------------------------------*/
/**
- * @fn hard_disk_info *hard_disk_get_info(hard_disk_file *file)
+ * @fn bool read(uint32_t lbasector, void *buffer)
*
- * @brief Hard disk get information.
+ * @brief Hard disk read.
*
- * @param [in,out] file If non-null, the file.
+ * @param lbasector The sector number (Linear Block Address) to read.
+ * @param buffer The buffer where the hard disk data will be placed.
*
- * @return null if it fails, else a hard_disk_info*.
+ * @return True if the operation succeeded
*/
-hard_disk_info *hard_disk_get_info(hard_disk_file *file)
+bool hard_disk_file::read(uint32_t lbasector, void *buffer)
{
- return &file->info;
+ if (chd)
+ {
+ std::error_condition err = chd->read_units(lbasector, buffer);
+ return !err;
+ }
+ else
+ {
+ size_t actual = 0;
+ std::error_condition err = fhandle->seek(fileoffset + (lbasector * hdinfo.sectorbytes), SEEK_SET);
+ if (!err)
+ std::tie(err, actual) = util::read(*fhandle, buffer, hdinfo.sectorbytes);
+ return !err && (actual == hdinfo.sectorbytes);
+ }
}
/*-------------------------------------------------
- hard_disk_read - read sectors from a hard
- disk
+ write - write sectors to a hard disk
-------------------------------------------------*/
/**
- * @fn uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
+ * @fn bool write(uint32_t lbasector, const void *buffer)
*
- * @brief Hard disk read.
+ * @brief Hard disk write.
*
- * @param [in,out] file If non-null, the file.
- * @param lbasector The lbasector.
- * @param [in,out] buffer If non-null, the 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_read(hard_disk_file *file, uint32_t lbasector, void *buffer)
+bool hard_disk_file::write(uint32_t lbasector, const void *buffer)
{
- if (file->chd)
+ if (chd)
{
- chd_error err = file->chd->read_units(lbasector, buffer);
- return (err == CHDERR_NONE);
+ std::error_condition err = 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->read(buffer, file->info.sectorbytes);
- return (actual == file->info.sectorbytes);
+ size_t actual = 0;
+ std::error_condition err = fhandle->seek(fileoffset + (lbasector * hdinfo.sectorbytes), SEEK_SET);
+ if (!err)
+ std::tie(err, actual) = util::write(*fhandle, buffer, hdinfo.sectorbytes);
+ return !err;
}
}
/*-------------------------------------------------
- hard_disk_write - write sectors to a hard
- disk
+ set_block_size - sets the block size
+ for a non-CHD-backed hard disk (a bare file).
-------------------------------------------------*/
/**
- * @fn uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
+ * @fn bool set_block_size(uint32_t blocksize)
*
- * @brief Hard disk write.
+ * @brief Hard disk set block size (works only for non-CHD-files)
*
- * @param [in,out] file If non-null, the file.
- * @param lbasector The lbasector.
- * @param buffer The buffer.
+ * @param blocksize The block size of this hard disk, in bytes.
*
- * @return An uint32_t.
+ * @return true on success, false on failure. Failure means a CHD is in use and the CHD
+ * block size does not match the passed in size. If a CHD is in use and the block
+ * sizes match, success is returned).
*/
-uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer)
+bool hard_disk_file::set_block_size(uint32_t blocksize)
{
- if (file->chd)
+ if (chd)
{
- chd_error err = file->chd->write_units(lbasector, buffer);
- return (err == CHDERR_NONE);
+ // if the CHD block size matches our block size, we're OK.
+ if (chd->unit_bytes() == blocksize)
+ {
+ return true;
+ }
+
+ // indicate failure, since we can't change the block size of a CHD.
+ return false;
}
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);
+ hdinfo.sectorbytes = blocksize;
+ return true;
}
}
+
+
+std::error_condition hard_disk_file::get_inquiry_data(std::vector<uint8_t> &data) const
+{
+ if(chd)
+ return chd->read_metadata(HARD_DISK_IDENT_METADATA_TAG, 0, data);
+ else
+ return std::error_condition(chd_file::error::METADATA_NOT_FOUND);
+}
+
+std::error_condition hard_disk_file::get_cis_data(std::vector<uint8_t> &data) const
+{
+ if(chd)
+ return chd->read_metadata(PCMCIA_CIS_METADATA_TAG, 0, data);
+ else
+ return std::error_condition(chd_file::error::METADATA_NOT_FOUND);
+}
+
+std::error_condition hard_disk_file::get_disk_key_data(std::vector<uint8_t> &data) const
+{
+ if(chd)
+ return chd->read_metadata(HARD_DISK_KEY_METADATA_TAG, 0, data);
+ else
+ return std::error_condition(chd_file::error::METADATA_NOT_FOUND);
+}
diff --git a/src/lib/util/harddisk.h b/src/lib/util/harddisk.h
index 2a4b727e7e7..f92c08edec5 100644
--- a/src/lib/util/harddisk.h
+++ b/src/lib/util/harddisk.h
@@ -8,45 +8,51 @@
***************************************************************************/
-#ifndef MAME_UTIL_HARDDISK_H
-#define MAME_UTIL_HARDDISK_H
+#ifndef MAME_LIB_UTIL_HARDDISK_H
+#define MAME_LIB_UTIL_HARDDISK_H
#pragma once
-#include "osdcore.h"
-#include "chd.h"
+#include "utilfwd.h"
+#include <cstdint>
+#include <system_error>
+#include <vector>
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-struct hard_disk_file;
+class chd_file;
-struct hard_disk_info
-{
- uint32_t cylinders;
- uint32_t heads;
- uint32_t sectors;
- uint32_t sectorbytes;
- uint32_t fileoffset; // offset in the file where the HDD image starts. not valid for CHDs.
-};
+class hard_disk_file {
+public:
+ struct info
+ {
+ uint32_t cylinders;
+ uint32_t heads;
+ uint32_t sectors;
+ uint32_t sectorbytes;
+ };
+ hard_disk_file(chd_file *chd);
+ hard_disk_file(util::random_read_write &corefile, uint32_t skipoffs);
+ ~hard_disk_file();
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
+ const info &get_info() const { return hdinfo; }
-hard_disk_file *hard_disk_open(chd_file *chd);
-hard_disk_file *hard_disk_open(util::core_file &corefile, uint32_t skipoffs);
+ bool set_block_size(uint32_t blocksize);
-void hard_disk_close(hard_disk_file *file);
+ bool read(uint32_t lbasector, void *buffer);
+ bool write(uint32_t lbasector, const void *buffer);
-chd_file *hard_disk_get_chd(hard_disk_file *file);
-hard_disk_info *hard_disk_get_info(hard_disk_file *file);
+ std::error_condition get_inquiry_data(std::vector<uint8_t> &data) const;
+ std::error_condition get_cis_data(std::vector<uint8_t> &data) const;
+ std::error_condition get_disk_key_data(std::vector<uint8_t> &data) const;
-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);
+private:
+ chd_file * chd; // CHD file
+ util::random_read_write * fhandle; // file if not a CHD
+ info hdinfo; // hard disk info
+ uint32_t fileoffset; // offset in the file where the HDD image starts. not valid for CHDs.
+};
-#endif // MAME_UTIL_HARDDISK_H
+#endif // MAME_LIB_UTIL_HARDDISK_H
diff --git a/src/lib/util/hash.cpp b/src/lib/util/hash.cpp
index ddb6f52ac5c..f2c528e9b22 100644
--- a/src/lib/util/hash.cpp
+++ b/src/lib/util/hash.cpp
@@ -11,68 +11,87 @@
***************************************************************************/
#include "hash.h"
-#include "hashing.h"
-#include <ctype.h>
+
+#include "ioprocs.h"
+
+#include <cassert>
+#include <cctype>
+#include <optional>
namespace util {
+
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
-char const hash_collection::HASH_CRC;
-char const hash_collection::HASH_SHA1;
-
char const *const hash_collection::HASH_TYPES_CRC = "R";
char const *const hash_collection::HASH_TYPES_CRC_SHA1 = "RS";
char const *const hash_collection::HASH_TYPES_ALL = "RS";
-char const hash_collection::FLAG_NO_DUMP;
-char const hash_collection::FLAG_BAD_DUMP;
-
-
//**************************************************************************
-// HASH COLLECTION
+// HASH CREATOR
//**************************************************************************
-//-------------------------------------------------
-// hash_collection - constructor
-//-------------------------------------------------
-
-hash_collection::hash_collection()
- : m_has_crc32(false),
- m_has_sha1(false),
- m_creator(nullptr)
+class hash_collection::hash_creator
{
-}
+public:
+ // constructor
+ hash_creator(bool doing_crc32, bool doing_sha1) noexcept
+ {
+ if (doing_crc32)
+ m_crc32_creator.emplace();
+ if (doing_sha1)
+ m_sha1_creator.emplace();
+ }
+ // add the given buffer to the hash
+ void append(void const *buffer, std::size_t length) noexcept
+ {
+ // append to each active hash
+ if (m_crc32_creator)
+ m_crc32_creator->append(buffer, length);
+ if (m_sha1_creator)
+ m_sha1_creator->append(buffer, length);
+ }
-hash_collection::hash_collection(const char *string)
- : m_has_crc32(false),
- m_has_sha1(false),
- m_creator(nullptr)
-{
- from_internal_string(string);
-}
+ // stop hashing
+ void finish(hash_collection &hashes) noexcept
+ {
+ // finish up the CRC32
+ if (m_crc32_creator)
+ {
+ hashes.add_crc(m_crc32_creator->finish());
+ m_crc32_creator.reset();
+ }
+ // finish up the SHA1
+ if (m_sha1_creator)
+ {
+ hashes.add_sha1(m_sha1_creator->finish());
+ m_sha1_creator.reset();
+ }
+ }
-hash_collection::hash_collection(const hash_collection &src)
- : m_has_crc32(false),
- m_has_sha1(false),
- m_creator(nullptr)
-{
- copyfrom(src);
-}
+private:
+ std::optional<crc32_creator> m_crc32_creator;
+ std::optional<sha1_creator> m_sha1_creator;
+};
+//**************************************************************************
+// HASH COLLECTION
+//**************************************************************************
+
//-------------------------------------------------
-// ~hash_collection - destructor
+// hash_collection - constructor
//-------------------------------------------------
-hash_collection::~hash_collection()
+hash_collection::hash_collection()
+ : m_has_crc32(false)
+ , m_has_sha1(false)
{
- delete m_creator;
}
@@ -93,15 +112,16 @@ hash_collection &hash_collection::operator=(const hash_collection &src)
// operator== - test for equality
//-------------------------------------------------
-bool hash_collection::operator==(const hash_collection &rhs) const
+bool hash_collection::operator==(const hash_collection &rhs) const noexcept
{
+ bool matches = false;
+
// match CRCs
- int matches = 0;
if (m_has_crc32 && rhs.m_has_crc32)
{
if (m_crc32 != rhs.m_crc32)
return false;
- matches++;
+ matches = true;
}
// match SHA1s
@@ -109,11 +129,11 @@ bool hash_collection::operator==(const hash_collection &rhs) const
{
if (m_sha1 != rhs.m_sha1)
return false;
- matches++;
+ matches = true;
}
- // if all shared hashes match, return true
- return (matches > 0);
+ // if at least one type is shared and matches, return true
+ return matches;
}
@@ -138,12 +158,10 @@ std::string hash_collection::hash_types() const
// set of hashes and flags
//-------------------------------------------------
-void hash_collection::reset()
+void hash_collection::reset() noexcept
{
m_flags.clear();
m_has_crc32 = m_has_sha1 = false;
- delete m_creator;
- m_creator = nullptr;
}
@@ -152,15 +170,15 @@ void hash_collection::reset()
// from a string
//-------------------------------------------------
-bool hash_collection::add_from_string(char type, const char *buffer, int length)
+bool hash_collection::add_from_string(char type, std::string_view string) noexcept
{
// handle CRCs
if (type == HASH_CRC)
- return m_has_crc32 = m_crc32.from_string(buffer, length);
+ return m_has_crc32 = m_crc32.from_string(string);
// handle SHA1s
else if (type == HASH_SHA1)
- return m_has_sha1 = m_sha1.from_string(buffer, length);
+ return m_has_sha1 = m_sha1.from_string(string);
return false;
}
@@ -170,7 +188,7 @@ bool hash_collection::add_from_string(char type, const char *buffer, int length)
// remove - remove a hash of the given type
//-------------------------------------------------
-bool hash_collection::remove(char type)
+bool hash_collection::remove(char type) noexcept
{
bool result = false;
@@ -240,7 +258,13 @@ std::string hash_collection::macro_string() const
buffer.append("NO_DUMP ");
if (flag(FLAG_BAD_DUMP))
buffer.append("BAD_DUMP ");
- strtrimspace(buffer);
+
+ // remove trailing space
+ if (!buffer.empty())
+ {
+ assert(buffer.back() == ' ');
+ buffer = buffer.substr(0, buffer.length() - 1);
+ }
return buffer;
}
@@ -267,7 +291,10 @@ std::string hash_collection::attribute_string() const
buffer.append("status=\"nodump\"");
if (flag(FLAG_BAD_DUMP))
buffer.append("status=\"baddump\"");
- strtrimspace(buffer);
+
+ // remove trailing space
+ if (!buffer.empty() && buffer.back() == ' ')
+ buffer = buffer.substr(0, buffer.length() - 1);
return buffer;
}
@@ -277,24 +304,19 @@ std::string hash_collection::attribute_string() const
// compact string to set of hashes and flags
//-------------------------------------------------
-bool hash_collection::from_internal_string(const char *string)
+bool hash_collection::from_internal_string(std::string_view string)
{
- assert(string != nullptr);
-
// start fresh
reset();
- // determine the end of the string
- const char *stringend = string + strlen(string);
- const char *ptr = string;
-
- // loop until we hit it
+ // loop until we hit the end of the string
bool errors = false;
int skip_digits = 0;
- while (ptr < stringend)
+ while (!string.empty())
{
- char c = *ptr++;
+ char c = string[0];
char uc = toupper(c);
+ string.remove_prefix(1);
// non-hex alpha values specify a hash type
if (uc >= 'G' && uc <= 'Z')
@@ -303,13 +325,13 @@ bool hash_collection::from_internal_string(const char *string)
if (uc == HASH_CRC)
{
m_has_crc32 = true;
- errors = !m_crc32.from_string(ptr, stringend - ptr);
+ errors = !m_crc32.from_string(string);
skip_digits = 2 * sizeof(crc32_t);
}
else if (uc == HASH_SHA1)
{
m_has_sha1 = true;
- errors = !m_sha1.from_string(ptr, stringend - ptr);
+ errors = !m_sha1.from_string(string);
skip_digits = 2 * sizeof(sha1_t);
}
else
@@ -336,69 +358,74 @@ bool hash_collection::from_internal_string(const char *string)
//-------------------------------------------------
-// begin - begin hashing
+// create - begin hashing
//-------------------------------------------------
-void hash_collection::begin(const char *types)
+std::unique_ptr<hash_collection::hash_creator> hash_collection::create(const char *types)
{
- // nuke previous creator and make a new one
- delete m_creator;
- m_creator = new hash_creator;
-
// by default use all types
if (types == nullptr)
- m_creator->m_doing_crc32 = m_creator->m_doing_sha1 = true;
+ return std::make_unique<hash_creator>(true, true);
// otherwise, just allocate the ones that are specified
else
- {
- m_creator->m_doing_crc32 = (strchr(types, HASH_CRC) != nullptr);
- m_creator->m_doing_sha1 = (strchr(types, HASH_SHA1) != nullptr);
- }
+ return std::make_unique<hash_creator>(strchr(types, HASH_CRC) != nullptr, strchr(types, HASH_SHA1) != nullptr);
}
//-------------------------------------------------
-// buffer - add the given buffer to the hash
+// compute - hash a block of data
//-------------------------------------------------
-void hash_collection::buffer(const uint8_t *data, uint32_t length)
+void hash_collection::compute(const uint8_t *data, uint32_t length, const char *types)
{
- assert(m_creator != nullptr);
+ // begin
+ std::unique_ptr<hash_creator> creator = create(types);
+
+ // run the hashes
+ creator->append(data, length);
- // append to each active hash
- if (m_creator->m_doing_crc32)
- m_creator->m_crc32_creator.append(data, length);
- if (m_creator->m_doing_sha1)
- m_creator->m_sha1_creator.append(data, length);
+ // end
+ creator->finish(*this);
}
//-------------------------------------------------
-// end - stop hashing
+// compute - hash data from a stream
//-------------------------------------------------
-void hash_collection::end()
+std::error_condition hash_collection::compute(random_read &stream, uint64_t offset, size_t length, size_t &actual, const char *types)
{
- assert(m_creator != nullptr);
+ // begin
+ std::unique_ptr<hash_creator> creator = create(types);
- // finish up the CRC32
- if (m_creator->m_doing_crc32)
- {
- m_has_crc32 = true;
- m_crc32 = m_creator->m_crc32_creator.finish();
- }
+ // local buffer of arbitrary size
+ uint8_t buffer[2048];
- // finish up the SHA1
- if (m_creator->m_doing_sha1)
+ // run the hashes
+ actual = 0U;
+ while (length)
{
- m_has_sha1 = true;
- m_sha1 = m_creator->m_sha1_creator.finish();
+ // determine the size of the next chunk
+ unsigned const chunk_length = std::min(length, sizeof(buffer));
+
+ // read one chunk
+ auto const [err, bytes_read] = read_at(stream, offset, buffer, chunk_length);
+ if (err)
+ return err;
+ if (!bytes_read) // EOF?
+ break;
+ offset += bytes_read;
+ length -= bytes_read;
+
+ // append the chunk
+ creator->append(buffer, bytes_read);
+ actual += bytes_read;
}
- // nuke the creator
- delete m_creator;
- m_creator = nullptr;
+ // end
+ creator->finish(*this);
+ return std::error_condition();
}
@@ -417,9 +444,6 @@ void hash_collection::copyfrom(const hash_collection &src)
m_crc32 = src.m_crc32;
m_has_sha1 = src.m_has_sha1;
m_sha1 = src.m_sha1;
-
- // don't copy creators
- m_creator = nullptr;
}
} // namespace util
diff --git a/src/lib/util/hash.h b/src/lib/util/hash.h
index 98a851bfd60..bd111224e6f 100644
--- a/src/lib/util/hash.h
+++ b/src/lib/util/hash.h
@@ -16,6 +16,10 @@
#pragma once
#include "hashing.h"
+#include "utilfwd.h"
+
+#include <memory>
+#include <system_error>
//**************************************************************************
@@ -30,6 +34,7 @@
namespace util {
+
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
@@ -40,6 +45,9 @@ namespace util {
// a collection of the various supported hashes and flags
class hash_collection
{
+ // forward declaration
+ class hash_creator;
+
public:
// hash types are identified by non-hex alpha values (G-Z)
static constexpr char HASH_CRC = 'R';
@@ -56,46 +64,44 @@ public:
// construction/destruction
hash_collection();
- hash_collection(const char *string);
- hash_collection(const hash_collection &src);
- ~hash_collection();
+ hash_collection(std::string_view string) : hash_collection() { from_internal_string(string); }
+ hash_collection(const hash_collection &src) : hash_collection() { copyfrom(src); }
// operators
hash_collection &operator=(const hash_collection &src);
- bool operator==(const hash_collection &rhs) const;
- bool operator!=(const hash_collection &rhs) const { return !(*this == rhs); }
+ bool operator==(const hash_collection &rhs) const noexcept;
+ bool operator!=(const hash_collection &rhs) const noexcept { return !(*this == rhs); }
// getters
- bool flag(char flag) const { return (m_flags.find_first_of(flag) != std::string::npos); }
+ bool flag(char flag) const noexcept { return (m_flags.find_first_of(flag) != std::string::npos); }
std::string hash_types() const;
// hash manipulators
- void reset();
- bool add_from_string(char type, const char *buffer, int length = -1);
- bool remove(char type);
+ void reset() noexcept;
+ bool add_from_string(char type, std::string_view string) noexcept;
+ bool remove(char type) noexcept;
// CRC-specific helpers
- bool crc(uint32_t &result) const { result = m_crc32; return m_has_crc32; }
- void add_crc(uint32_t crc) { m_crc32 = crc; m_has_crc32 = true; }
+ bool crc(uint32_t &result) const noexcept { result = m_crc32; return m_has_crc32; }
+ void add_crc(uint32_t crc) noexcept { m_crc32 = crc; m_has_crc32 = true; }
// SHA1-specific helpers
- bool sha1(sha1_t &result) const { result = m_sha1; return m_has_sha1; }
- void add_sha1(sha1_t sha1) { m_has_sha1 = true; m_sha1 = sha1; }
+ bool sha1(sha1_t &result) const noexcept { result = m_sha1; return m_has_sha1; }
+ void add_sha1(sha1_t sha1) noexcept { m_has_sha1 = true; m_sha1 = sha1; }
// string conversion
std::string internal_string() const;
std::string macro_string() const;
std::string attribute_string() const;
- bool from_internal_string(const char *string);
+ bool from_internal_string(std::string_view string);
// creation
- void begin(const char *types = nullptr);
- void buffer(const uint8_t *data, uint32_t length);
- void end();
- void compute(const uint8_t *data, uint32_t length, const char *types = nullptr) { begin(types); buffer(data, length); end(); }
+ void compute(const uint8_t *data, uint32_t length, const char *types = nullptr);
+ std::error_condition compute(random_read &stream, uint64_t offset, size_t length, size_t &actual, const char *types = nullptr);
private:
// internal helpers
+ std::unique_ptr<hash_creator> create(const char *types = nullptr);
void copyfrom(const hash_collection &src);
// internal state
@@ -104,16 +110,6 @@ private:
crc32_t m_crc32;
bool m_has_sha1;
sha1_t m_sha1;
-
- // creators
- struct hash_creator
- {
- bool m_doing_crc32;
- crc32_creator m_crc32_creator;
- bool m_doing_sha1;
- sha1_creator m_sha1_creator;
- };
- hash_creator * m_creator;
};
diff --git a/src/lib/util/hashing.cpp b/src/lib/util/hashing.cpp
index 448cdc5d6ec..3410389fdc8 100644
--- a/src/lib/util/hashing.cpp
+++ b/src/lib/util/hashing.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
hashing.c
@@ -9,44 +9,115 @@
***************************************************************************/
#include "hashing.h"
+
+#include "strformat.h"
+
+#include "eminline.h"
+
#include <zlib.h>
+
+#include <algorithm>
#include <iomanip>
#include <sstream>
namespace util {
-//**************************************************************************
-// CONSTANTS
-//**************************************************************************
-
-const crc16_t crc16_t::null = { 0 };
-const crc32_t crc32_t::null = { 0 };
-const md5_t md5_t::null = { { 0 } };
-const sha1_t sha1_t::null = { { 0 } };
-
-
//**************************************************************************
// INLINE FUNCTIONS
//**************************************************************************
+namespace {
+
//-------------------------------------------------
// char_to_hex - return the hex value of a
// character
//-------------------------------------------------
-inline int char_to_hex(char c)
+constexpr int char_to_hex(char c)
{
- if (c >= '0' && c <= '9')
- return c - '0';
- if (c >= 'a' && c <= 'f')
- return 10 + c - 'a';
- if (c >= 'A' && c <= 'F')
- return 10 + c - 'A';
- return -1;
+ return
+ (c >= '0' && c <= '9') ? (c - '0') :
+ (c >= 'a' && c <= 'f') ? (10 + c - 'a') :
+ (c >= 'A' && c <= 'F') ? (10 + c - 'A') :
+ -1;
}
+inline uint32_t sha1_b(uint32_t *data, unsigned i) noexcept
+{
+ uint32_t r = data[(i + 13) & 15U];
+ r ^= data[(i + 8) & 15U];
+ r ^= data[(i + 2) & 15U];
+ r ^= data[i & 15U];
+ r = rotl_32(r, 1);
+ data[i & 15U] = r;
+ return r;
+}
+
+inline void sha1_r0(const uint32_t *data, std::array<uint32_t, 5> &d, unsigned i) noexcept
+{
+ d[i % 5] = d[i % 5] + ((d[(i + 3) % 5] & (d[(i + 2) % 5] ^ d[(i + 1) % 5])) ^ d[(i + 1) % 5]) + data[i] + 0x5a827999U + rotl_32(d[(i + 4) % 5], 5);
+ d[(i + 3) % 5] = rotl_32(d[(i + 3) % 5], 30);
+}
+
+inline void sha1_r1(uint32_t *data, std::array<uint32_t, 5> &d, unsigned i) noexcept
+{
+ d[i % 5] = d[i % 5] + ((d[(i + 3) % 5] & (d[(i + 2) % 5] ^ d[(i + 1) % 5])) ^ d[(i + 1) % 5])+ sha1_b(data, i) + 0x5a827999U + rotl_32(d[(i + 4) % 5], 5);
+ d[(i + 3) % 5] = rotl_32(d[(i + 3) % 5], 30);
+}
+
+inline void sha1_r2(uint32_t *data, std::array<uint32_t, 5> &d, unsigned i) noexcept
+{
+ d[i % 5] = d[i % 5] + (d[(i + 3) % 5] ^ d[(i + 2) % 5] ^ d[(i + 1) % 5]) + sha1_b(data, i) + 0x6ed9eba1U + rotl_32(d[(i + 4) % 5], 5);
+ d[(i + 3) % 5] = rotl_32(d[(i + 3) % 5], 30);
+}
+
+inline void sha1_r3(uint32_t *data, std::array<uint32_t, 5> &d, unsigned i) noexcept
+{
+ d[i % 5] = d[i % 5] + (((d[(i + 3) % 5] | d[(i + 2) % 5]) & d[(i + 1) % 5]) | (d[(i + 3) % 5] & d[(i + 2) % 5])) + sha1_b(data, i) + 0x8f1bbcdcU + rotl_32(d[(i + 4) % 5], 5);
+ d[(i + 3) % 5] = rotl_32(d[(i + 3) % 5], 30);
+}
+
+inline void sha1_r4(uint32_t *data, std::array<uint32_t, 5> &d, unsigned i) noexcept
+{
+ d[i % 5] = d[i % 5] + (d[(i + 3) % 5] ^ d[(i + 2) % 5] ^ d[(i + 1) % 5]) + sha1_b(data, i) + 0xca62c1d6U + rotl_32(d[(i + 4) % 5], 5);
+ d[(i + 3) % 5] = rotl_32(d[(i + 3) % 5], 30);
+}
+
+inline void sha1_process(std::array<uint32_t, 5> &st, uint32_t *data) noexcept
+{
+ std::array<uint32_t, 5> d = st;
+ unsigned i = 0U;
+ while (i < 16U)
+ sha1_r0(data, d, i++);
+ while (i < 20U)
+ sha1_r1(data, d, i++);
+ while (i < 40U)
+ sha1_r2(data, d, i++);
+ while (i < 60U)
+ sha1_r3(data, d, i++);
+ while (i < 80U)
+ sha1_r4(data, d, i++);
+ for (i = 0U; i < 5U; i++)
+ st[i] += d[i];
+}
+
+} // anonymous namespace
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+const crc16_t crc16_t::null = { 0 };
+const crc32_t crc32_t::null = { 0 };
+const md5_t md5_t::null = { { 0 } };
+const sha1_t sha1_t::null = { { 0 } };
+const sum16_t sum16_t::null = { 0 };
+
+
//**************************************************************************
// SHA-1 HELPERS
@@ -56,23 +127,22 @@ inline int char_to_hex(char c)
// from_string - convert from a string
//-------------------------------------------------
-bool sha1_t::from_string(const char *string, int length)
+bool sha1_t::from_string(std::string_view string) noexcept
{
// must be at least long enough to hold everything
- memset(m_raw, 0, sizeof(m_raw));
- if (length == -1)
- length = strlen(string);
- if (length < 2 * sizeof(m_raw))
+ std::fill(std::begin(m_raw), std::end(m_raw), 0);
+ if (string.length() < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
- for (auto & elem : m_raw)
+ for (auto &elem : m_raw)
{
- int upper = char_to_hex(*string++);
- int lower = char_to_hex(*string++);
+ int const upper = char_to_hex(string[0]);
+ int const lower = char_to_hex(string[1]);
if (upper == -1 || lower == -1)
return false;
elem = (upper << 4) | lower;
+ string.remove_prefix(2);
}
return true;
}
@@ -84,15 +154,93 @@ bool sha1_t::from_string(const char *string, int length)
std::string sha1_t::as_string() const
{
- std::ostringstream buffer;
- buffer.fill('0');
- buffer << std::hex;
- for (auto & elem : m_raw)
- buffer << std::setw(2) << unsigned(elem);
- return buffer.str();
+ std::string result(2 * std::size(m_raw), ' ');
+ auto it = result.begin();
+ for (auto const &elem : m_raw)
+ {
+ auto const upper = elem >> 4;
+ auto const lower = elem & 0x0f;
+ *it++ = ((10 > upper) ? '0' : ('a' - 10)) + upper;
+ *it++ = ((10 > lower) ? '0' : ('a' - 10)) + lower;
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// reset - prepare to digest a block of data
+//-------------------------------------------------
+
+void sha1_creator::reset() noexcept
+{
+ m_cnt = 0U;
+ m_st[0] = 0xc3d2e1f0U;
+ m_st[1] = 0x10325476U;
+ m_st[2] = 0x98badcfeU;
+ m_st[3] = 0xefcdab89U;
+ m_st[4] = 0x67452301U;
+}
+
+
+//-------------------------------------------------
+// append - digest a block of data
+//-------------------------------------------------
+
+void sha1_creator::append(const void *data, uint32_t length) noexcept
+{
+#ifdef LSB_FIRST
+ constexpr unsigned swizzle = 3U;
+#else
+ constexpr unsigned swizzle = 0U;
+#endif
+ uint32_t residual = (uint32_t(m_cnt) >> 3) & 63U;
+ m_cnt += uint64_t(length) << 3;
+ uint32_t offset = 0U;
+ if (length >= (64U - residual))
+ {
+ if (residual)
+ {
+ for (offset = 0U; (offset + residual) < 64U; offset++)
+ reinterpret_cast<uint8_t *>(m_buf)[(offset + residual) ^ swizzle] = reinterpret_cast<const uint8_t *>(data)[offset];
+ sha1_process(m_st, m_buf);
+ }
+ while ((length - offset) >= 64U)
+ {
+ for (residual = 0U; residual < 64U; residual++, offset++)
+ reinterpret_cast<uint8_t *>(m_buf)[residual ^ swizzle] = reinterpret_cast<const uint8_t *>(data)[offset];
+ sha1_process(m_st, m_buf);
+ }
+ residual = 0U;
+ }
+ for ( ; offset < length; residual++, offset++)
+ reinterpret_cast<uint8_t *>(m_buf)[residual ^ swizzle] = reinterpret_cast<const uint8_t *>(data)[offset];
+}
+
+
+//-------------------------------------------------
+// finish - compute final hash
+//-------------------------------------------------
+
+sha1_t sha1_creator::finish() noexcept
+{
+ const unsigned padlen = 64U - (63U & ((unsigned(m_cnt) >> 3) + 8U));
+ uint8_t padbuf[64];
+ padbuf[0] = 0x80;
+ for (unsigned i = 1U; i < padlen; i++)
+ padbuf[i] = 0x00;
+ uint8_t lenbuf[8];
+ for (unsigned i = 0U; i < 8U; i++)
+ lenbuf[i] = uint8_t(m_cnt >> ((7U - i) << 3));
+ append(padbuf, padlen);
+ append(lenbuf, sizeof(lenbuf));
+ sha1_t result;
+ for (unsigned i = 0U; i < 20U; i++)
+ result.m_raw[i] = uint8_t(m_st[4U - (i >> 2)] >> ((3U - (i & 3)) << 3));
+ return result;
}
+
//**************************************************************************
// MD-5 HELPERS
//**************************************************************************
@@ -101,23 +249,22 @@ std::string sha1_t::as_string() const
// from_string - convert from a string
//-------------------------------------------------
-bool md5_t::from_string(const char *string, int length)
+bool md5_t::from_string(std::string_view string) noexcept
{
// must be at least long enough to hold everything
- memset(m_raw, 0, sizeof(m_raw));
- if (length == -1)
- length = strlen(string);
- if (length < 2 * sizeof(m_raw))
+ std::fill(std::begin(m_raw), std::end(m_raw), 0);
+ if (string.length() < 2 * sizeof(m_raw))
return false;
// iterate through our raw buffer
- for (auto & elem : m_raw)
+ for (auto &elem : m_raw)
{
- int upper = char_to_hex(*string++);
- int lower = char_to_hex(*string++);
+ int const upper = char_to_hex(string[0]);
+ int const lower = char_to_hex(string[1]);
if (upper == -1 || lower == -1)
return false;
elem = (upper << 4) | lower;
+ string.remove_prefix(2);
}
return true;
}
@@ -129,12 +276,16 @@ bool md5_t::from_string(const char *string, int length)
std::string md5_t::as_string() const
{
- std::ostringstream buffer;
- buffer.fill('0');
- buffer << std::hex;
- for (auto & elem : m_raw)
- buffer << std::setw(2) << unsigned(elem);
- return buffer.str();
+ std::string result(2 * std::size(m_raw), ' ');
+ auto it = result.begin();
+ for (auto const &elem : m_raw)
+ {
+ auto const upper = elem >> 4;
+ auto const lower = elem & 0x0f;
+ *it++ = ((10 > upper) ? '0' : ('a' - 10)) + upper;
+ *it++ = ((10 > lower) ? '0' : ('a' - 10)) + lower;
+ }
+ return result;
}
@@ -147,23 +298,22 @@ std::string md5_t::as_string() const
// from_string - convert from a string
//-------------------------------------------------
-bool crc32_t::from_string(const char *string, int length)
+bool crc32_t::from_string(std::string_view string) noexcept
{
// must be at least long enough to hold everything
m_raw = 0;
- if (length == -1)
- length = strlen(string);
- if (length < 2 * sizeof(m_raw))
+ if (string.length() < (2 * sizeof(m_raw)))
return false;
// iterate through our raw buffer
m_raw = 0;
for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
{
- int nibble = char_to_hex(*string++);
+ int const nibble = char_to_hex(string[0]);
if (nibble == -1)
return false;
m_raw = (m_raw << 4) | nibble;
+ string.remove_prefix(1);
}
return true;
}
@@ -184,7 +334,7 @@ std::string crc32_t::as_string() const
// the currently-accumulated value
//-------------------------------------------------
-void crc32_creator::append(const void *data, uint32_t length)
+void crc32_creator::append(const void *data, uint32_t length) noexcept
{
m_accum.m_raw = crc32(m_accum, reinterpret_cast<const Bytef *>(data), length);
}
@@ -199,23 +349,22 @@ void crc32_creator::append(const void *data, uint32_t length)
// from_string - convert from a string
//-------------------------------------------------
-bool crc16_t::from_string(const char *string, int length)
+bool crc16_t::from_string(std::string_view string) noexcept
{
// must be at least long enough to hold everything
m_raw = 0;
- if (length == -1)
- length = strlen(string);
- if (length < 2 * sizeof(m_raw))
+ if (string.length() < (2 * sizeof(m_raw)))
return false;
// iterate through our raw buffer
m_raw = 0;
for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
{
- int nibble = char_to_hex(*string++);
+ int const nibble = char_to_hex(string[0]);
if (nibble == -1)
return false;
m_raw = (m_raw << 4) | nibble;
+ string.remove_prefix(1);
}
return true;
}
@@ -246,7 +395,7 @@ std::string crc16_t::as_string() const
* @param length The length.
*/
-void crc16_creator::append(const void *data, uint32_t length)
+void crc16_creator::append(const void *data, uint32_t length) noexcept
{
static const uint16_t s_table[256] =
{
@@ -284,7 +433,7 @@ void crc16_creator::append(const void *data, uint32_t length)
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
- const uint8_t *src = reinterpret_cast<const uint8_t *>(data);
+ const auto *src = reinterpret_cast<const uint8_t *>(data);
// fetch the current value into a local and rip through the source data
uint16_t crc = m_accum.m_raw;
@@ -293,4 +442,71 @@ void crc16_creator::append(const void *data, uint32_t length)
m_accum.m_raw = crc;
}
+
+
+//**************************************************************************
+// SUM-16 HELPERS
+//**************************************************************************
+
+//-------------------------------------------------
+// from_string - convert from a string
+//-------------------------------------------------
+
+bool sum16_t::from_string(std::string_view string) noexcept
+{
+ // must be at least long enough to hold everything
+ m_raw = 0;
+ if (string.length() < (2 * sizeof(m_raw)))
+ return false;
+
+ // iterate through our raw buffer
+ m_raw = 0;
+ for (int bytenum = 0; bytenum < sizeof(m_raw) * 2; bytenum++)
+ {
+ int const nibble = char_to_hex(string[0]);
+ if (nibble == -1)
+ return false;
+ m_raw = (m_raw << 4) | nibble;
+ string.remove_prefix(1);
+ }
+ return true;
+}
+
+/**
+ * @fn std::string sum16_t::as_string() const
+ *
+ * @brief -------------------------------------------------
+ * as_string - convert to a string
+ * -------------------------------------------------.
+ *
+ * @return a std::string.
+ */
+
+std::string sum16_t::as_string() const
+{
+ return string_format("%04x", m_raw);
+}
+
+/**
+ * @fn void sum16_creator::append(const void *data, uint32_t length)
+ *
+ * @brief -------------------------------------------------
+ * append - sum a block of data, appending to the currently-accumulated value
+ * -------------------------------------------------.
+ *
+ * @param data The data.
+ * @param length The length.
+ */
+
+void sum16_creator::append(const void *data, uint32_t length) noexcept
+{
+ const auto *src = reinterpret_cast<const uint8_t *>(data);
+
+ // fetch the current value into a local and rip through the source data
+ uint16_t sum = m_accum.m_raw;
+ while (length-- != 0)
+ sum += *src++;
+ m_accum.m_raw = sum;
+}
+
} // namespace util
diff --git a/src/lib/util/hashing.h b/src/lib/util/hashing.h
index b1505c654a4..c57d403de1b 100644
--- a/src/lib/util/hashing.h
+++ b/src/lib/util/hashing.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
hashing.h
@@ -7,22 +7,23 @@
Hashing helper classes.
***************************************************************************/
-
#ifndef MAME_UTIL_HASHING_H
#define MAME_UTIL_HASHING_H
#pragma once
-#include "osdcore.h"
-#include "corestr.h"
#include "md5.h"
-#include "sha1.h"
+#include <array>
+#include <cstdint>
+#include <cstring>
#include <functional>
#include <string>
+#include <string_view>
namespace util {
+
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
@@ -33,10 +34,10 @@ namespace util {
// final digest
struct sha1_t
{
- bool operator==(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
- bool operator!=(const sha1_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
- operator uint8_t *() { return m_raw; }
- bool from_string(const char *string, int length = -1);
+ bool operator==(const sha1_t &rhs) const noexcept { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
+ bool operator!=(const sha1_t &rhs) const noexcept { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
+ operator uint8_t *() noexcept { return m_raw; }
+ bool from_string(std::string_view string) noexcept;
std::string as_string() const;
uint8_t m_raw[20];
static const sha1_t null;
@@ -47,25 +48,19 @@ class sha1_creator
{
public:
// construction/destruction
- sha1_creator() { reset(); }
+ sha1_creator() noexcept { reset(); }
// reset
- void reset() { sha1_init(&m_context); }
+ void reset() noexcept;
// append data
- void append(const void *data, uint32_t length) { sha1_update(&m_context, length, reinterpret_cast<const uint8_t *>(data)); }
+ void append(const void *data, uint32_t length) noexcept;
// finalize and compute the final digest
- sha1_t finish()
- {
- sha1_t result;
- sha1_final(&m_context);
- sha1_digest(&m_context, sizeof(result.m_raw), result.m_raw);
- return result;
- }
+ sha1_t finish() noexcept;
// static wrapper to just get the digest from a block
- static sha1_t simple(const void *data, uint32_t length)
+ static sha1_t simple(const void *data, uint32_t length) noexcept
{
sha1_creator creator;
creator.append(data, length);
@@ -73,8 +68,9 @@ public:
}
protected:
- // internal state
- struct sha1_ctx m_context; // internal context
+ uint64_t m_cnt;
+ std::array<uint32_t, 5> m_st;
+ uint32_t m_buf[16];
};
@@ -84,10 +80,10 @@ protected:
// final digest
struct md5_t
{
- bool operator==(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
- bool operator!=(const md5_t &rhs) const { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
- operator uint8_t *() { return m_raw; }
- bool from_string(const char *string, int length = -1);
+ bool operator==(const md5_t &rhs) const noexcept { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) == 0; }
+ bool operator!=(const md5_t &rhs) const noexcept { return memcmp(m_raw, rhs.m_raw, sizeof(m_raw)) != 0; }
+ operator uint8_t *() noexcept { return m_raw; }
+ bool from_string(std::string_view string) noexcept;
std::string as_string() const;
uint8_t m_raw[16];
static const md5_t null;
@@ -98,16 +94,16 @@ class md5_creator
{
public:
// construction/destruction
- md5_creator() { reset(); }
+ md5_creator() noexcept { reset(); }
// reset
- void reset() { MD5Init(&m_context); }
+ void reset() noexcept { MD5Init(&m_context); }
// append data
- void append(const void *data, uint32_t length) { MD5Update(&m_context, reinterpret_cast<const unsigned char *>(data), length); }
+ void append(const void *data, uint32_t length) noexcept { MD5Update(&m_context, reinterpret_cast<const unsigned char *>(data), length); }
// finalize and compute the final digest
- md5_t finish()
+ md5_t finish() noexcept
{
md5_t result;
MD5Final(result.m_raw, &m_context);
@@ -115,7 +111,7 @@ public:
}
// static wrapper to just get the digest from a block
- static md5_t simple(const void *data, uint32_t length)
+ static md5_t simple(const void *data, uint32_t length) noexcept
{
md5_creator creator;
creator.append(data, length);
@@ -134,19 +130,19 @@ protected:
// final digest
struct crc32_t
{
- crc32_t() { }
- constexpr crc32_t(const crc32_t &rhs) = default;
- constexpr crc32_t(const uint32_t crc) : m_raw(crc) { }
+ crc32_t() noexcept { }
+ constexpr crc32_t(const crc32_t &rhs) noexcept = default;
+ constexpr crc32_t(const uint32_t crc) noexcept : m_raw(crc) { }
- constexpr bool operator==(const crc32_t &rhs) const { return m_raw == rhs.m_raw; }
- constexpr bool operator!=(const crc32_t &rhs) const { return m_raw != rhs.m_raw; }
+ constexpr bool operator==(const crc32_t &rhs) const noexcept { return m_raw == rhs.m_raw; }
+ constexpr bool operator!=(const crc32_t &rhs) const noexcept { return m_raw != rhs.m_raw; }
- crc32_t &operator=(const crc32_t &rhs) = default;
- crc32_t &operator=(const uint32_t crc) { m_raw = crc; return *this; }
+ crc32_t &operator=(const crc32_t &rhs) noexcept = default;
+ crc32_t &operator=(const uint32_t crc) noexcept { m_raw = crc; return *this; }
- constexpr operator uint32_t() const { return m_raw; }
+ constexpr operator uint32_t() const noexcept { return m_raw; }
- bool from_string(const char *string, int length = -1);
+ bool from_string(std::string_view string) noexcept;
std::string as_string() const;
uint32_t m_raw;
@@ -159,19 +155,19 @@ class crc32_creator
{
public:
// construction/destruction
- crc32_creator() { reset(); }
+ crc32_creator() noexcept { reset(); }
// reset
- void reset() { m_accum.m_raw = 0; }
+ void reset() noexcept { m_accum.m_raw = 0; }
// append data
- void append(const void *data, uint32_t length);
+ void append(const void *data, uint32_t length) noexcept;
// finalize and compute the final digest
- crc32_t finish() { return m_accum; }
+ crc32_t finish() noexcept { return m_accum; }
// static wrapper to just get the digest from a block
- static crc32_t simple(const void *data, uint32_t length)
+ static crc32_t simple(const void *data, uint32_t length) noexcept
{
crc32_creator creator;
creator.append(data, length);
@@ -190,19 +186,19 @@ protected:
// final digest
struct crc16_t
{
- crc16_t() { }
- constexpr crc16_t(const crc16_t &rhs) = default;
- constexpr crc16_t(const uint16_t crc) : m_raw(crc) { }
+ crc16_t() noexcept { }
+ constexpr crc16_t(const crc16_t &rhs) noexcept = default;
+ constexpr crc16_t(const uint16_t crc) noexcept : m_raw(crc) { }
- constexpr bool operator==(const crc16_t &rhs) const { return m_raw == rhs.m_raw; }
- constexpr bool operator!=(const crc16_t &rhs) const { return m_raw != rhs.m_raw; }
+ constexpr bool operator==(const crc16_t &rhs) const noexcept { return m_raw == rhs.m_raw; }
+ constexpr bool operator!=(const crc16_t &rhs) const noexcept { return m_raw != rhs.m_raw; }
- crc16_t &operator=(const crc16_t &rhs) = default;
- crc16_t &operator=(const uint16_t crc) { m_raw = crc; return *this; }
+ crc16_t &operator=(const crc16_t &rhs) noexcept = default;
+ crc16_t &operator=(const uint16_t crc) noexcept { m_raw = crc; return *this; }
- constexpr operator uint16_t() const { return m_raw; }
+ constexpr operator uint16_t() const noexcept { return m_raw; }
- bool from_string(const char *string, int length = -1);
+ bool from_string(std::string_view string) noexcept;
std::string as_string() const;
uint16_t m_raw;
@@ -215,19 +211,19 @@ class crc16_creator
{
public:
// construction/destruction
- crc16_creator() { reset(); }
+ crc16_creator() noexcept { reset(); }
// reset
- void reset() { m_accum.m_raw = 0xffff; }
+ void reset() noexcept { m_accum.m_raw = 0xffff; }
// append data
- void append(const void *data, uint32_t length);
+ void append(const void *data, uint32_t length) noexcept;
// finalize and compute the final digest
- crc16_t finish() { return m_accum; }
+ crc16_t finish() noexcept { return m_accum; }
// static wrapper to just get the digest from a block
- static crc16_t simple(const void *data, uint32_t length)
+ static crc16_t simple(const void *data, uint32_t length) noexcept
{
crc16_creator creator;
creator.append(data, length);
@@ -240,6 +236,61 @@ protected:
};
+
+// ======================> SUM-16
+
+// final digest
+struct sum16_t
+{
+ sum16_t() noexcept { }
+ constexpr sum16_t(const sum16_t &rhs) noexcept = default;
+ constexpr sum16_t(const uint16_t sum) noexcept : m_raw(sum) { }
+
+ constexpr bool operator==(const sum16_t &rhs) const noexcept { return m_raw == rhs.m_raw; }
+ constexpr bool operator!=(const sum16_t &rhs) const noexcept { return m_raw != rhs.m_raw; }
+
+ sum16_t &operator=(const sum16_t &rhs) noexcept = default;
+ sum16_t &operator=(const uint16_t sum) noexcept { m_raw = sum; return *this; }
+
+ constexpr operator uint16_t() const noexcept { return m_raw; }
+
+ bool from_string(std::string_view string) noexcept;
+ std::string as_string() const;
+
+ uint16_t m_raw;
+
+ static const sum16_t null;
+};
+
+// creation helper
+class sum16_creator
+{
+public:
+ // construction/destruction
+ sum16_creator() noexcept { reset(); }
+
+ // reset
+ void reset() noexcept { m_accum.m_raw = 0; }
+
+ // append data
+ void append(const void *data, uint32_t length) noexcept;
+
+ // finalize and compute the final digest
+ sum16_t finish() noexcept { return m_accum; }
+
+ // static wrapper to just get the digest from a block
+ static sum16_t simple(const void *data, uint32_t length) noexcept
+ {
+ sum16_creator creator;
+ creator.append(data, length);
+ return creator.finish();
+ }
+
+protected:
+ // internal state
+ sum16_t m_accum; // internal accumulator
+};
+
} // namespace util
namespace std {
@@ -248,14 +299,14 @@ template <> struct hash<::util::crc32_t>
{
typedef ::util::crc32_t argument_type;
typedef std::size_t result_type;
- result_type operator()(argument_type const & s) const { return std::hash<std::uint32_t>()(s); }
+ result_type operator()(argument_type const &s) const noexcept { return std::hash<std::uint32_t>()(s); }
};
template <> struct hash<::util::crc16_t>
{
typedef ::util::crc16_t argument_type;
typedef std::size_t result_type;
- result_type operator()(argument_type const & s) const { return std::hash<std::uint16_t>()(s); }
+ result_type operator()(argument_type const &s) const noexcept { return std::hash<std::uint16_t>()(s); }
};
} // namespace std
diff --git a/src/lib/util/huffman.cpp b/src/lib/util/huffman.cpp
index d3a73315ea9..1d531fdd634 100644
--- a/src/lib/util/huffman.cpp
+++ b/src/lib/util/huffman.cpp
@@ -96,8 +96,8 @@
***************************************************************************/
-#include <stdlib.h>
-#include <assert.h>
+#include <cstdlib>
+#include <cassert>
#include "coretmpl.h"
#include "huffman.h"
@@ -431,7 +431,7 @@ huffman_error huffman_context_base::compute_tree_from_histo()
// binary search to achieve the optimum encoding
uint32_t lowerweight = 0;
uint32_t upperweight = sdatacount * 2;
- while (1)
+ while (true)
{
// build a tree using the current weight
uint32_t curweight = (upperweight + lowerweight) / 2;
diff --git a/src/lib/util/huffman.h b/src/lib/util/huffman.h
index 0eac393f380..b13fa4b1d5d 100644
--- a/src/lib/util/huffman.h
+++ b/src/lib/util/huffman.h
@@ -13,8 +13,8 @@
#pragma once
-#include "osdcore.h"
#include "bitstream.h"
+#include "osdcomm.h"
//**************************************************************************
diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp
new file mode 100644
index 00000000000..687968f85ee
--- /dev/null
+++ b/src/lib/util/ioprocs.cpp
@@ -0,0 +1,982 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocs.cpp
+
+ I/O interface implementations for RAM, C standard I/O and OSD files
+
+***************************************************************************/
+
+#include "ioprocs.h"
+
+#include "ioprocsfill.h"
+
+#include "osdfile.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <iterator>
+#include <limits>
+#include <new>
+#include <type_traits>
+
+
+namespace util {
+
+namespace {
+
+// helper for holding a block of memory and deallocating it (or not) as necessary
+
+template <typename T, bool Owned>
+class ram_adapter_base : public virtual random_access
+{
+public:
+ virtual ~ram_adapter_base()
+ {
+ if constexpr (Owned)
+ std::free(m_data);
+ }
+
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
+ {
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (0 > offset)
+ return std::errc::invalid_argument;
+ m_pointer = std::uint64_t(offset);
+ return std::error_condition();
+
+ case SEEK_CUR:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_pointer)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_pointer += offset;
+ return std::error_condition();
+
+ case SEEK_END:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_size)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_pointer = std::uint64_t(m_size) + offset;
+ return std::error_condition();
+
+ default:
+ return std::errc::invalid_argument;
+ }
+ }
+
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override
+ {
+ result = m_pointer;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition length(std::uint64_t &result) noexcept override
+ {
+ if (std::numeric_limits<uint64_t>::max() < m_size)
+ return std::errc::file_too_large;
+
+ result = m_size;
+ return std::error_condition();
+ }
+
+protected:
+ template <typename U>
+ ram_adapter_base(U *data, std::size_t size) noexcept : m_data(reinterpret_cast<T>(data)), m_size(size)
+ {
+ static_assert(sizeof(*m_data) == 1U, "Element type must be byte-sized");
+ assert(m_data || !m_size);
+ }
+
+ T m_data;
+ std::uint64_t m_pointer = 0U;
+ std::size_t m_size;
+};
+
+
+// RAM read implementation
+
+template <typename T, bool Owned>
+class ram_read_adapter : public ram_adapter_base<T, Owned>, public virtual random_read
+{
+public:
+ template <typename U>
+ ram_read_adapter(U *data, std::size_t size) noexcept : ram_adapter_base<T, Owned>(data, size)
+ {
+ }
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ do_read(this->m_pointer, buffer, length, actual);
+ this->m_pointer += actual;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ do_read(offset, buffer, length, actual);
+ return std::error_condition();
+ }
+
+private:
+ void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) const noexcept
+ {
+ if ((offset < this->m_size) && length)
+ {
+ actual = std::min(std::size_t(this->m_size - offset), length);
+ if constexpr (Owned)
+ std::memcpy(buffer, this->m_data + offset, actual);
+ else
+ std::memmove(buffer, this->m_data + offset, actual);
+ }
+ else
+ {
+ actual = 0U;
+ }
+ }
+};
+
+
+// helper for holding a stdio FILE and closing it (or not) as necessary
+
+class stdio_adapter_base : public virtual random_access
+{
+public:
+ virtual ~stdio_adapter_base()
+ {
+ if (m_close)
+ std::fclose(m_file);
+ }
+
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
+ {
+ if ((std::numeric_limits<long>::max() < offset) || (std::numeric_limits<long>::min() > offset))
+ return std::errc::invalid_argument;
+ else if (!std::fseek(m_file, long(offset), whence))
+ return std::error_condition();
+ else
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override
+ {
+ long const pos = std::ftell(m_file);
+ if (0 > pos)
+ return std::error_condition(errno, std::generic_category());
+ result = static_cast<unsigned long>(pos);
+ return std::error_condition();
+ }
+
+ virtual std::error_condition length(std::uint64_t &result) noexcept override
+ {
+ std::fpos_t oldpos;
+ if (std::fgetpos(m_file, &oldpos))
+ return std::error_condition(errno, std::generic_category());
+
+ long endpos = -1;
+ if (!std::fseek(m_file, 0, SEEK_END))
+ {
+ m_dangling_read = m_dangling_write = false;
+ endpos = std::ftell(m_file);
+ }
+ std::error_condition err;
+ if (0 > endpos)
+ err.assign(errno, std::generic_category());
+ else if (std::numeric_limits<std::uint64_t>::max() < static_cast<unsigned long>(endpos))
+ err = std::errc::file_too_large;
+ else
+ result = static_cast<unsigned long>(endpos);
+
+ if (!std::fsetpos(m_file, &oldpos))
+ m_dangling_read = m_dangling_write = false;
+ else if (!err)
+ err.assign(errno, std::generic_category());
+
+ return err;
+ }
+
+protected:
+ stdio_adapter_base(FILE *file, bool close) noexcept : m_file(file), m_close(close)
+ {
+ assert(m_file);
+ }
+
+ FILE *file() noexcept
+ {
+ return m_file;
+ }
+
+private:
+ FILE *const m_file;
+ bool const m_close;
+
+protected:
+ bool m_dangling_read = true, m_dangling_write = true;
+};
+
+
+// stdio read implementation
+
+class stdio_read_adapter : public stdio_adapter_base, public virtual random_read
+{
+public:
+ stdio_read_adapter(FILE *file, bool close) noexcept : stdio_adapter_base(file, close)
+ {
+ }
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ if (m_dangling_write)
+ {
+ if (std::fflush(file()))
+ {
+ std::clearerr(file());
+ actual = 0U;
+ return std::error_condition(errno, std::generic_category());
+ }
+ m_dangling_write = false;
+ }
+
+ actual = std::fread(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_read = true;
+ if (length != actual)
+ {
+ if (std::ferror(file()))
+ {
+ std::clearerr(file());
+ return std::error_condition(errno, std::generic_category());
+ }
+ else if (std::feof(file()))
+ {
+ m_dangling_read = false;
+ }
+ }
+
+ return std::error_condition();
+ }
+
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ actual = 0U;
+
+ if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
+ return std::errc::invalid_argument;
+
+ std::fpos_t oldpos;
+ if (std::fgetpos(file(), &oldpos))
+ return std::error_condition(errno, std::generic_category());
+
+ std::error_condition err;
+ if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
+ {
+ err.assign(errno, std::generic_category());
+ }
+ else
+ {
+ m_dangling_write = false;
+ actual = std::fread(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_read = true;
+ if (length != actual)
+ {
+ if (std::ferror(file()))
+ {
+ err.assign(errno, std::generic_category());
+ std::clearerr(file());
+ }
+ else if (std::feof(file()))
+ {
+ m_dangling_read = false;
+ }
+ }
+ }
+
+ if (!std::fsetpos(file(), &oldpos))
+ m_dangling_read = m_dangling_write = false;
+ else if (!err)
+ err.assign(errno, std::generic_category());
+
+ return err;
+ }
+};
+
+
+// stdio read/write implementation
+
+class stdio_read_write_adapter : public stdio_read_adapter, public random_read_write
+{
+public:
+ using stdio_read_adapter::stdio_read_adapter;
+
+ virtual std::error_condition finalize() noexcept override
+ {
+ return std::error_condition();
+ }
+
+ virtual std::error_condition flush() noexcept override
+ {
+ if (std::fflush(file()))
+ {
+ std::clearerr(file());
+ return std::error_condition(errno, std::generic_category());
+ }
+ m_dangling_write = false;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ if (m_dangling_read)
+ {
+ if (std::fseek(file(), 0, SEEK_CUR))
+ {
+ actual = 0U;
+ return std::error_condition(errno, std::generic_category());
+ }
+ m_dangling_read = false;
+ }
+
+ actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_write = true;
+ if (length != actual)
+ {
+ std::clearerr(file());
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ return std::error_condition();
+ }
+
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ actual = 0U;
+
+ if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
+ return std::errc::invalid_argument;
+
+ std::fpos_t oldpos;
+ if (std::fgetpos(file(), &oldpos))
+ return std::error_condition(errno, std::generic_category());
+
+ std::error_condition err;
+ if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
+ {
+ err.assign(errno, std::generic_category());
+ }
+ else
+ {
+ m_dangling_read = false;
+ actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_write = true;
+ if (length != actual)
+ {
+ err.assign(errno, std::generic_category());
+ std::clearerr(file());
+ }
+ }
+
+ if (!std::fsetpos(file(), &oldpos))
+ m_dangling_read = m_dangling_write = false;
+ else if (!err)
+ err.assign(errno, std::generic_category());
+
+ return err;
+ }
+};
+
+
+// stdio helper that fills space when writing past the end-of-file
+
+class stdio_read_write_filler : public random_read_fill_wrapper<stdio_read_write_adapter>
+{
+public:
+ stdio_read_write_filler(FILE *file, bool close, std::uint8_t fill) noexcept : random_read_fill_wrapper<stdio_read_write_adapter>(file, close)
+ {
+ set_filler(fill);
+ }
+
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ actual = 0U;
+
+ long const offset = std::ftell(file());
+ if (0 > offset)
+ return std::error_condition(errno, std::generic_category());
+
+ if (std::fseek(file(), 0, SEEK_END))
+ {
+ std::error_condition err(errno, std::generic_category());
+ if (!std::fseek(file(), offset, SEEK_SET))
+ m_dangling_read = m_dangling_write = false;
+ return err;
+ }
+ m_dangling_read = m_dangling_write = false;
+ long endpos = std::ftell(file());
+ if (0 > endpos)
+ {
+ std::error_condition err(errno, std::generic_category());
+ std::fseek(file(), offset, SEEK_SET);
+ return err;
+ }
+
+ if (offset > endpos)
+ {
+ std::uint8_t block[1024];
+ std::fill_n(
+ block,
+ std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos),
+ get_filler());
+ do
+ {
+ std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos);
+ std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file());
+ endpos += filled;
+ m_dangling_write = true;
+ if (chunk != filled)
+ {
+ std::error_condition err(errno, std::generic_category());
+ std::clearerr(file());
+ if (!std::fseek(file(), offset, SEEK_SET))
+ m_dangling_write = false;
+ return err;
+ }
+ }
+ while (static_cast<unsigned long>(endpos) < offset);
+ }
+ else if ((offset < endpos) && std::fseek(file(), offset, SEEK_SET))
+ {
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_write = true;
+ if (length != actual)
+ {
+ std::clearerr(file());
+ return std::error_condition(errno, std::generic_category());
+ }
+
+ return std::error_condition();
+ }
+
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ actual = 0U;
+
+ if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset)
+ return std::errc::invalid_argument;
+
+ std::fpos_t oldpos;
+ if (std::fgetpos(file(), &oldpos))
+ return std::error_condition(errno, std::generic_category());
+
+ std::error_condition err;
+ if (std::fseek(file(), 0, SEEK_END))
+ {
+ err.assign(errno, std::generic_category());
+ }
+ else
+ {
+ m_dangling_read = m_dangling_write = false;
+ long endpos = std::ftell(file());
+ if (0 > endpos)
+ {
+ err.assign(errno, std::generic_category());
+ }
+ else if (static_cast<unsigned long>(endpos) > offset)
+ {
+ if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET))
+ err.assign(errno, std::generic_category());
+ }
+ else if (static_cast<unsigned long>(endpos) < offset)
+ {
+ std::uint8_t block[1024];
+ std::fill_n(
+ block,
+ std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos),
+ get_filler());
+ do
+ {
+ std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos);
+ std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file());
+ endpos += filled;
+ m_dangling_write = true;
+ if (chunk != filled)
+ {
+ err.assign(errno, std::generic_category());
+ std::clearerr(file());
+ }
+ }
+ while (!err && (static_cast<unsigned long>(endpos) < offset));
+ }
+ }
+
+ if (!err)
+ {
+ actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file());
+ m_dangling_write = true;
+ if (length != actual)
+ {
+ err.assign(errno, std::generic_category());
+ std::clearerr(file());
+ }
+ }
+
+ if (!std::fsetpos(file(), &oldpos))
+ m_dangling_read = m_dangling_write = false;
+ else if (!err)
+ err.assign(errno, std::generic_category());
+
+ return err;
+ }
+};
+
+
+// helper class for holding an osd_file and closing it (or not) as necessary
+
+class osd_file_adapter_base : public virtual random_access
+{
+public:
+ virtual ~osd_file_adapter_base()
+ {
+ if (m_close)
+ delete m_file;
+ }
+
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
+ {
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (0 > offset)
+ return std::errc::invalid_argument;
+ m_pointer = std::uint64_t(offset);
+ return std::error_condition();
+
+ case SEEK_CUR:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_pointer)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_pointer += offset;
+ return std::error_condition();
+
+ // TODO: add SEEK_END when osd_file can support it - should it return a different error?
+ default:
+ return std::errc::invalid_argument;
+ }
+ }
+
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override
+ {
+ result = m_pointer;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition length(std::uint64_t &result) noexcept override
+ {
+ // not supported by osd_file
+ return std::errc::not_supported; // TODO: revisit this error code
+ }
+
+protected:
+ osd_file_adapter_base(osd_file::ptr &&file) noexcept : m_file(file.release()), m_close(true)
+ {
+ assert(m_file);
+ }
+
+ osd_file_adapter_base(osd_file &file) noexcept : m_file(&file), m_close(false)
+ {
+ }
+
+ osd_file &file() noexcept
+ {
+ return *m_file;
+ }
+
+ std::uint64_t m_pointer = 0U;
+
+private:
+ osd_file *const m_file;
+ bool const m_close;
+};
+
+
+// osd_file read implementation
+
+class osd_file_read_adapter : public osd_file_adapter_base, public virtual random_read
+{
+public:
+ osd_file_read_adapter(osd_file::ptr &&file) noexcept : osd_file_adapter_base(std::move(file))
+ {
+ }
+
+ osd_file_read_adapter(osd_file &file) noexcept : osd_file_adapter_base(file)
+ {
+ }
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // actual length 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 count;
+ std::error_condition err = file().read(buffer, m_pointer, chunk, count);
+ if (!err)
+ {
+ m_pointer += count;
+ actual = std::size_t(count);
+ }
+ else
+ {
+ actual = 0U;
+ }
+ return err;
+ }
+
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // actual length 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 count;
+ std::error_condition err = file().read(buffer, offset, chunk, count);
+ if (!err)
+ actual = std::size_t(count);
+ else
+ actual = 0U;
+ return err;
+ }
+};
+
+
+// osd_file read/write implementation
+
+class osd_file_read_write_adapter : public osd_file_read_adapter, public random_read_write
+{
+public:
+ using osd_file_read_adapter::osd_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_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // actual length 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 count;
+ std::error_condition err = file().write(buffer, m_pointer, chunk, count);
+ if (!err)
+ {
+ actual = std::size_t(count);
+ m_pointer += count;
+ }
+ else
+ {
+ actual = 0U;
+ }
+ return err;
+ }
+
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // actual length 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 count;
+ std::error_condition err = file().write(buffer, offset, chunk, count);
+ if (!err)
+ actual = std::size_t(count);
+ else
+ actual = 0U;
+ return err;
+ }
+};
+
+} // anonymous namespace
+
+
+// helper functions for common patterns
+
+std::pair<std::error_condition, std::size_t> read(read_stream &stream, void *buffer, std::size_t length) noexcept
+{
+ std::size_t actual = 0;
+ do
+ {
+ std::size_t count;
+ std::error_condition err = stream.read_some(buffer, length, count);
+ actual += count;
+ if (!err)
+ {
+ if (!count)
+ break;
+ }
+ else if (std::errc::interrupted != err)
+ {
+ return std::make_pair(err, actual);
+ }
+ buffer = reinterpret_cast<std::uint8_t *>(buffer) + count;
+ length -= count;
+ }
+ while (length);
+ return std::make_pair(std::error_condition(), actual);
+}
+
+std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read(read_stream &stream, std::size_t length) noexcept
+{
+ std::unique_ptr<std::uint8_t []> buffer(new (std::nothrow) std::uint8_t [length]);
+ if (!buffer)
+ return std::make_tuple(std::errc::not_enough_memory, std::move(buffer), std::size_t(0));
+ auto [err, actual] = read(stream, buffer.get(), length);
+ return std::make_tuple(err, std::move(buffer), actual);
+}
+
+std::pair<std::error_condition, std::size_t> read_at(random_read &stream, std::uint64_t offset, void *buffer, std::size_t length) noexcept
+{
+ std::size_t actual = 0;
+ do
+ {
+ std::size_t count;
+ std::error_condition err = stream.read_some_at(offset, buffer, length, count);
+ actual += count;
+ if (!err)
+ {
+ if (!count)
+ break;
+ }
+ else if (std::errc::interrupted != err)
+ {
+ return std::make_pair(err, actual);
+ }
+ offset += count;
+ buffer = reinterpret_cast<std::uint8_t *>(buffer) + count;
+ length -= count;
+ }
+ while (length);
+ return std::make_pair(std::error_condition(), actual);
+}
+
+std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read_at(random_read &stream, std::uint64_t offset, std::size_t length) noexcept
+{
+ std::unique_ptr<std::uint8_t []> buffer(new (std::nothrow) std::uint8_t [length]);
+ if (!buffer)
+ return std::make_tuple(std::errc::not_enough_memory, std::move(buffer), std::size_t(0));
+ auto [err, actual] = read_at(stream, offset, buffer.get(), length);
+ return std::make_tuple(err, std::move(buffer), actual);
+}
+
+std::pair<std::error_condition, std::size_t> write(write_stream &stream, void const *buffer, std::size_t length) noexcept
+{
+ std::size_t actual = 0;
+ do
+ {
+ std::size_t written;
+ std::error_condition const err = stream.write_some(buffer, length, written);
+ assert(written || err || !length);
+ actual += written;
+ if (err && (std::errc::interrupted != err))
+ return std::make_pair(err, actual);
+ buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
+ length -= written;
+ }
+ while (length);
+ return std::make_pair(std::error_condition(), actual);
+}
+
+std::pair<std::error_condition, std::size_t> write_at(random_write &stream, std::uint64_t offset, void const *buffer, std::size_t length) noexcept
+{
+ std::size_t actual = 0;
+ do
+ {
+ std::size_t written;
+ std::error_condition const err = stream.write_some_at(offset, buffer, length, written);
+ assert(written || err || !length);
+ actual += written;
+ if (err && (std::errc::interrupted != err))
+ return std::make_pair(err, actual);
+ offset += written;
+ buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
+ length -= written;
+ }
+ while (length);
+ return std::make_pair(std::error_condition(), actual);
+}
+
+
+// creating RAM read adapters
+
+random_read::ptr ram_read(void const *data, std::size_t size) noexcept
+{
+ random_read::ptr result;
+ if (data || !size)
+ result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t const *const, false>(data, size));
+ return result;
+}
+
+random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t const *const, false> > > result;
+ if (data || !size)
+ result.reset(new (std::nothrow) decltype(result)::element_type(data, size));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept
+{
+ random_read::ptr result;
+ void *const copy = size ? std::malloc(size) : nullptr;
+ if (copy)
+ std::memcpy(copy, data, size);
+ if (copy || !size)
+ result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t *const, true>(copy, size));
+ if (!result)
+ std::free(copy);
+ return result;
+}
+
+random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t *const, true> > > result;
+ void *const copy = size ? std::malloc(size) : nullptr;
+ if (copy)
+ std::memcpy(copy, data, size);
+ if (copy || !size)
+ result.reset(new (std::nothrow) decltype(result)::element_type(copy, size));
+ if (!result)
+ std::free(copy);
+ return result;
+}
+
+
+// creating stdio read adapters
+
+random_read::ptr stdio_read(FILE *file) noexcept
+{
+ random_read::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_adapter(file, true));
+ return result;
+}
+
+random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result;
+ if (file)
+ result.reset(new (std::nothrow) decltype(result)::element_type(file, true));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+random_read::ptr stdio_read_noclose(FILE *file) noexcept
+{
+ random_read::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_adapter(file, false));
+ return result;
+}
+
+random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept
+{
+ std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result;
+ if (file)
+ result.reset(new (std::nothrow) decltype(result)::element_type(file, false));
+ if (result)
+ result->set_filler(filler);
+ return result;
+}
+
+
+// creating stdio read/write adapters
+
+random_read_write::ptr stdio_read_write(FILE *file) noexcept
+{
+ random_read_write::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_write_adapter(file, true));
+ return result;
+}
+
+random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept
+{
+ random_read_write::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_write_filler(file, true, filler));
+ return result;
+}
+
+random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept
+{
+ random_read_write::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_write_adapter(file, false));
+ return result;
+}
+
+random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept
+{
+ random_read_write::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) stdio_read_write_filler(file, false, filler));
+ return result;
+}
+
+
+// creating osd_file read adapters
+
+random_read::ptr osd_file_read(osd_file::ptr &&file) noexcept
+{
+ random_read::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) osd_file_read_adapter(std::move(file)));
+ return result;
+}
+
+random_read::ptr osd_file_read(osd_file &file) noexcept
+{
+ return random_read::ptr(new (std::nothrow) osd_file_read_adapter(file));
+}
+
+
+// creating osd_file read/write adapters
+
+random_read_write::ptr osd_file_read_write(osd_file::ptr &&file) noexcept
+{
+ random_read_write::ptr result;
+ if (file)
+ result.reset(new (std::nothrow) osd_file_read_write_adapter(std::move(file)));
+ return result;
+}
+
+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));
+}
+
+} // namespace util
diff --git a/src/lib/util/ioprocs.h b/src/lib/util/ioprocs.h
new file mode 100644
index 00000000000..9e2c29d2a14
--- /dev/null
+++ b/src/lib/util/ioprocs.h
@@ -0,0 +1,377 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocs.h
+
+ I/O interfaces
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_IOPROCS_H
+#define MAME_LIB_UTIL_IOPROCS_H
+
+#pragma once
+
+#include "utilfwd.h"
+
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <memory>
+#include <system_error>
+#include <tuple>
+#include <utility>
+
+
+// FIXME: make a proper place for OSD forward declarations
+class osd_file;
+
+
+namespace util {
+
+/// \defgroup ioprocs Generic I/O interfaces
+/// \{
+
+/// \brief Interface to an input stream
+///
+/// Represents a stream producing a sequence of bytes with no further
+/// structure.
+/// \sa write_stream read_write_stream random_read
+class read_stream
+{
+public:
+ using ptr = std::unique_ptr<read_stream>;
+
+ virtual ~read_stream() = default;
+
+ /// \brief Read from the current position in the stream
+ ///
+ /// Reads up to the specified number of bytes from the stream into
+ /// the supplied buffer. May read less than the requested number of
+ /// bytes if the end of the stream is reached or an error occurs.
+ /// If the stream supports seeking, reading starts at the current
+ /// position in the stream, and the current position is incremented
+ /// by the number of bytes read.
+ /// \param [out] buffer Destination buffer. Must be large enough to
+ /// hold the requested number of bytes.
+ /// \param [in] length Maximum number of bytes to read.
+ /// \param [out] actual Number of bytes actually read. Will always
+ /// be less than or equal to the requested length.
+ /// \return An error condition if reading stopped due to an error.
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+};
+
+
+/// \brief Interface to an output stream
+///
+/// Represents a stream that accepts a sequence of bytes with no further
+/// structure.
+/// \sa read_stream read_write_stream random_write
+class write_stream
+{
+public:
+ using ptr = std::unique_ptr<write_stream>;
+
+ virtual ~write_stream() = default;
+
+ /// \brief Finish writing data
+ ///
+ /// Performs any tasks necessary to finish writing data to the
+ /// stream and guarantee that the written data can be read in its
+ /// entirety. Further writes may not be possible.
+ /// \return An error condition if the operation fails.
+ virtual std::error_condition finalize() noexcept = 0;
+
+ /// \brief Flush application-side caches
+ ///
+ /// Flushes any caches to the underlying stream. Success does not
+ /// guarantee that data has reached persistent storage.
+ /// \return An error condition if flushing caches fails.
+ virtual std::error_condition flush() noexcept = 0;
+
+ /// \brief Write at the current position in the stream
+ ///
+ /// Writes up to the specified number of bytes from the supplied
+ /// buffer to the stream. May write less than the requested number
+ /// of bytes if an error occurs. If the stream supports seeking,
+ /// writing starts at the current position in the stream, and the
+ /// current position is incremented by the number of bytes written.
+ /// \param [in] buffer Buffer containing the data to write. Must
+ /// contain at least the specified number of bytes.
+ /// \param [in] length Number of bytes to write.
+ /// \param [out] actual Number of bytes actually written. Will
+ /// always be less than or equal to the requested length.
+ /// \return An error condition if writing stopped due to an error.
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+};
+
+
+/// \brief Interface to an I/O stream
+///
+/// Represents an object that acts as both a source of and sink for byte
+/// sequences.
+/// \sa read_stream write_stream random_read_write
+class read_write_stream : public virtual read_stream, public virtual write_stream
+{
+public:
+ using ptr = std::unique_ptr<read_write_stream>;
+};
+
+
+/// \brief Interface to a byte sequence that supports seeking
+///
+/// Provides an interface for controlling the position within a byte
+/// sequence that supports seeking.
+/// \sa random_read random_write random_read_write
+class random_access
+{
+public:
+ virtual ~random_access() = default;
+
+ /// \brief Set the position in the stream
+ ///
+ /// Sets the position for the next read or write operation. It may
+ /// be possible to set the position beyond the end of the stream.
+ /// \param [in] offset The offset in bytes, relative to the position
+ /// specified by the whence parameter.
+ /// \param [in] whence One of SEEK_SET, SEEK_CUR or SEEK_END, to
+ /// interpret the offset parameter relative to the beginning of
+ /// the stream, the current position in the stream, or the end of
+ /// the stream, respectively.
+ /// \return An error condition of the operation failed.
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept = 0;
+
+ /// \brief Get the current position in the stream
+ ///
+ /// Gets the position in the stream for the next read or write
+ /// operation. The position may be beyond the end of the stream.
+ /// \param [out] result The position in bytes relative to the
+ /// beginning of the stream. Not valid if the operation fails.
+ /// \return An error condition if the operation failed.
+ virtual std::error_condition tell(std::uint64_t &result) noexcept = 0;
+
+ /// \brief Get the length of the stream
+ ///
+ /// Gets the current length of the stream.
+ /// \param [out] result The length of the stream in bytes. Not
+ /// valid if the operation fails.
+ /// \return An error condtion if the operation failed.
+ virtual std::error_condition length(std::uint64_t &result) noexcept = 0;
+};
+
+
+/// \brief Interface to a random-access byte input sequence
+///
+/// Provides an interface for reading from arbitrary positions within a
+/// byte sequence. No further structure is provided.
+/// \sa read_stream random_write random_read_write
+class random_read : public virtual read_stream, public virtual random_access
+{
+public:
+ using ptr = std::unique_ptr<random_read>;
+
+ /// \brief Read from specified position
+ ///
+ /// Reads up to the specified number of bytes into the supplied
+ /// buffer. If seeking is supported, reading starts at the
+ /// specified position and the current position is unaffected. May
+ /// read less than the requested number of bytes if the end of the
+ /// stream is encountered or an error occurs.
+ /// \param [in] offset The position to start reading from, specified
+ /// as a number of bytes from the beginning of the stream.
+ /// \param [out] buffer Destination buffer. Must be large enough to
+ /// hold the requested number of bytes.
+ /// \param [in] length Maximum number of bytes to read.
+ /// \param [out] actual Number of bytes actually read. Will always
+ /// be less than or equal to the requested length.
+ /// \return An error condition if seeking failed or reading stopped
+ /// due to an error.
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+};
+
+
+/// \brief Interface to a random-access byte output sequence
+///
+/// Provides an interface for writing to arbitrary positions within a
+/// byte sequence. No further structure is provided.
+/// \sa write_stream random_read random_read_write
+class random_write : public virtual write_stream, public virtual random_access
+{
+public:
+ using ptr = std::unique_ptr<random_write>;
+
+ /// \brief Write at specified position
+ ///
+ /// Writes up to the specified number of bytes from the supplied
+ /// buffer. If seeking is supported, writing starts at the
+ /// specified position and the current position is unaffected. May
+ /// write less than the requested number of bytes if an error
+ /// occurs.
+ /// \param [in] offset The position to start writing at, specified
+ /// as a number of bytes from the beginning of the stream.
+ /// \param [in] buffer Buffer containing the data to write. Must
+ /// contain at least the specified number of bytes.
+ /// \param [in] length Number of bytes to write.
+ /// \param [out] actual Number of bytes actually written. Will
+ /// always be less than or equal to the requested length.
+ /// \return An error condition if seeking failed or writing stopped
+ /// due to an error.
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+};
+
+
+/// \brief Interface to a random-access read/write byte sequence
+///
+/// Provides an interface for reading from and writing to arbitrary
+/// positions within a byte sequence. No further structure is provided.
+/// \sa random_read random_write read_write_stream
+class random_read_write : public read_write_stream, public virtual random_read, public virtual random_write
+{
+public:
+ using ptr = std::unique_ptr<random_read_write>;
+};
+
+
+/// \brief Read from the current position in the stream
+///
+/// Reads up to the specified number of bytes from the stream into the
+/// supplied buffer, continuing if interrupted by asynchronous signals.
+/// May read less than the requested number of bytes if the end of the
+/// stream is reached or an error occurs. If the stream supports
+/// seeking, reading starts at the current position in the stream, and
+/// the current position is incremented by the number of bytes read.
+/// The operation may not be atomic if it is interrupted before the
+/// requested number of bytes is read.
+/// \param [in] stream The stream to read from.
+/// \param [out] buffer Destination buffer. Must be large enough to
+/// hold the requested number of bytes.
+/// \param [in] length Maximum number of bytes to read.
+/// \return A pair containing an error condition if reading stopped due
+/// to an error, and the actual number of bytes read.
+std::pair<std::error_condition, std::size_t> read(read_stream &stream, void *buffer, std::size_t length) noexcept;
+
+/// \brief Allocate memory and read from the current position in the
+/// stream
+///
+/// Allocates the specified number of bytes and then reads up to that
+/// number of bytes from the stream into the newly allocated buffer,
+/// continuing if interrupted by asynchronous signals. May read less
+/// than the requested number of bytes if the end of the stream is
+/// reached or an error occurs. If the stream supports seeking,
+/// reading starts at the current position in the stream, and the
+/// current position is incremented by the number of bytes read. The
+/// operation may not be atomic if it is interrupted before the
+/// requested number of bytes is read. No data will be read if
+/// allocation fails.
+/// \param [in] stream The stream to read from.
+/// hold the requested number of bytes.
+/// \param [in] length Maximum number of bytes to read.
+/// \return A tuple containing an error condition if allocation failed
+/// or reading stopped due to an error, the allocated buffer, and the
+/// actual number of bytes read.
+std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read(read_stream &stream, std::size_t length) noexcept;
+
+/// \brief Read from the specified position
+///
+/// Reads up to the specified number of bytes from the stream into the
+/// supplied buffer, continuing if interrupted by asynchronous signals.
+/// May read less than the requested number of bytes if the end of the
+/// stream is reached or an error occurs. If seeking is supported,
+/// reading starts at the specified position and the current position is
+/// unaffected. The operation may not be atomic if it is interrupted
+/// before the requested number of bytes is read.
+/// \param [in] stream The stream to read from.
+/// \param [in] offset The position to start reading from, specified as
+/// a number of bytes from the beginning of the stream.
+/// \param [out] buffer Destination buffer. Must be large enough to
+/// hold the requested number of bytes.
+/// \param [in] length Maximum number of bytes to read.
+/// \return A pair containing an error condition if reading stopped due
+/// to an error, and the actual number of bytes read.
+std::pair<std::error_condition, std::size_t> read_at(random_read &stream, std::uint64_t offset, void *buffer, std::size_t length) noexcept;
+
+/// \brief Allocate memory and read from the specified position
+///
+/// Allocates the specified number of bytes and then reads up to that
+/// number of bytes from the stream into the newly allocated buffer,
+/// continuing if interrupted by asynchronous signals. May read less
+/// than the requested number of bytes if the end of the stream is
+/// reached or an error occurs. If seeking is supported, reading
+/// starts at the specified position and the current position is
+/// unaffected. The operation may not be atomic if it is interrupted
+/// before the requested number of bytes is read. No data will be read
+/// if allocation fails.
+/// \param [in] stream The stream to read from.
+/// \param [in] offset The position to start reading from, specified as
+/// a number of bytes from the beginning of the stream.
+/// \param [out] buffer Destination buffer. Must be large enough to
+/// hold the requested number of bytes.
+/// \param [in] length Maximum number of bytes to read.
+/// \return A tuple containing an error condition if allocation failed
+/// or reading stopped due to an error, the allocated buffer, and the
+/// actual number of bytes read.
+std::tuple<std::error_condition, std::unique_ptr<std::uint8_t []>, std::size_t> read_at(random_read &stream, std::uint64_t offset, std::size_t length) noexcept;
+
+/// \brief Write at the current position in the stream
+///
+/// Writes up to the specified number of bytes from the supplied
+/// buffer to the stream, continuing if interrupted by asynchronous
+/// signals. May write less than the requested number of bytes if an
+/// error occurs. If the stream supports seeking, writing starts at the
+/// current position in the stream, and the current position is
+/// incremented by the number of bytes written. The operation may not
+/// be atomic if it is interrupted before the requested number of bytes
+/// is written.
+/// \param [in] stream The stream to write to.
+/// \param [in] buffer Buffer containing the data to write. Must
+/// contain at least the specified number of bytes.
+/// \param [in] length Number of bytes to write.
+/// \return A pair containing an error condition if writing stopped due
+/// to an error, and the actual number of bytes written.
+std::pair<std::error_condition, std::size_t> write(write_stream &stream, void const *buffer, std::size_t length) noexcept;
+
+/// \brief Write at specified position
+///
+/// Writes up to the specified number of bytes from the supplied buffer,
+/// continuing if interrupted by asynchronous signals. If seeking is
+/// supported, writing starts at the specified position and the current
+/// position is unaffected. May write less than the requested number
+/// of bytes if an error occurs. The operation may not be atomic if it
+/// is interrupted before the requested number of bytes is written.
+/// \param [in] stream The stream to write to.
+/// \param [in] offset The position to start writing at, specified as a
+/// number of bytes from the beginning of the stream.
+/// \param [in] buffer Buffer containing the data to write. Must
+/// contain at least the specified number of bytes.
+/// \param [in] length Number of bytes to write.
+/// \return A pair containing an error condition if writing stopped due
+/// to an error, and the actual number of bytes written.
+std::pair<std::error_condition, std::size_t> write_at(random_write &stream, std::uint64_t offset, void const *buffer, std::size_t length) noexcept;
+
+/// \}
+
+
+random_read::ptr ram_read(void const *data, std::size_t size) noexcept;
+random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept;
+random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept;
+random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept;
+
+random_read::ptr stdio_read(FILE *file) noexcept;
+random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept;
+random_read::ptr stdio_read_noclose(FILE *file) noexcept;
+random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept;
+
+random_read_write::ptr stdio_read_write(FILE *file) noexcept;
+random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept;
+random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept;
+random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept;
+
+random_read::ptr osd_file_read(std::unique_ptr<osd_file> &&file) noexcept;
+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;
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_IOPROCS_H
diff --git a/src/lib/util/ioprocsfill.h b/src/lib/util/ioprocsfill.h
new file mode 100644
index 00000000000..7179d61852c
--- /dev/null
+++ b/src/lib/util/ioprocsfill.h
@@ -0,0 +1,190 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocsfill.h
+
+ Wrappers that automatically fill extra space
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_IOPROCSFILL_H
+#define MAME_LIB_UTIL_IOPROCSFILL_H
+
+#pragma once
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <system_error>
+
+
+namespace util {
+
+template <std::uint8_t DefaultFiller>
+class fill_wrapper_base
+{
+public:
+ std::uint8_t get_filler() const noexcept
+ {
+ return m_filler;
+ }
+
+ void set_filler(std::uint8_t filler) noexcept
+ {
+ m_filler = filler;
+ }
+
+private:
+ std::uint8_t m_filler = DefaultFiller;
+};
+
+
+template <typename Base, std::uint8_t DefaultFiller = 0U>
+class read_stream_fill_wrapper : public Base, public virtual fill_wrapper_base<DefaultFiller>
+{
+public:
+ using Base::Base;
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // not atomic with respect to other read/write calls
+ actual = 0U;
+ std::error_condition err;
+ std::size_t chunk;
+ do
+ {
+ err = Base::read_some(
+ reinterpret_cast<std::uint8_t *>(buffer) + actual,
+ length - actual,
+ chunk);
+ actual += chunk;
+ }
+ while ((length > actual) && ((!err && chunk) || (std::errc::interrupted == err)));
+ assert(length >= actual);
+ std::fill(
+ reinterpret_cast<std::uint8_t *>(buffer) + actual,
+ reinterpret_cast<std::uint8_t *>(buffer) + length,
+ fill_wrapper_base<DefaultFiller>::get_filler());
+ return err;
+ }
+};
+
+
+template <typename Base, std::uint8_t DefaultFiller = 0U>
+class random_read_fill_wrapper : public read_stream_fill_wrapper<Base, DefaultFiller>
+{
+public:
+ using read_stream_fill_wrapper<Base, DefaultFiller>::read_stream_fill_wrapper;
+
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // not atomic with respect to other read/write calls
+ actual = 0U;
+ std::error_condition err;
+ std::size_t chunk;
+ do
+ {
+ err = Base::read_some_at(
+ offset,
+ reinterpret_cast<std::uint8_t *>(buffer) + actual,
+ length - actual,
+ chunk);
+ offset += chunk;
+ actual += chunk;
+ }
+ while ((length > actual) && ((!err && chunk) || (std::errc::interrupted == err)));
+ assert(length >= actual);
+ std::fill(
+ reinterpret_cast<std::uint8_t *>(buffer) + actual,
+ reinterpret_cast<std::uint8_t *>(buffer) + length,
+ fill_wrapper_base<DefaultFiller>::get_filler());
+ return err;
+ }
+};
+
+
+template <typename Base, std::uint8_t DefaultFiller = 0U, std::size_t FillBlock = 1024U>
+class random_write_fill_wrapper : public Base, public virtual fill_wrapper_base<DefaultFiller>
+{
+public:
+ using Base::Base;
+
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // not atomic with respect to other read/write calls
+ std::error_condition err;
+ actual = 0U;
+
+ std::uint64_t offset;
+ std::uint64_t current;
+ err = Base::tell(offset);
+ if (err)
+ return err;
+ err = Base::length(current);
+ if (err)
+ return err;
+
+ if (current < offset)
+ {
+ std::uint64_t unfilled = offset - current;
+ std::uint8_t fill_buffer[FillBlock];
+ std::fill(
+ fill_buffer,
+ fill_buffer + std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled),
+ fill_wrapper_base<DefaultFiller>::get_filler());
+ do
+ {
+ std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled);
+ std::size_t filled;
+ err = Base::write_some_at(current, fill_buffer, chunk, filled);
+ current += filled;
+ unfilled -= filled;
+ if (err && (std::errc::interrupted != err))
+ return err;
+ }
+ while (unfilled);
+ }
+
+ return Base::write_some(buffer, length, actual);
+ }
+
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ // not atomic with respect to other read/write calls
+ std::error_condition err;
+ std::uint64_t current;
+ err = Base::length(current);
+ if (!err && (current < offset))
+ {
+ std::uint64_t unfilled = offset - current;
+ std::uint8_t fill_buffer[FillBlock];
+ std::fill(
+ fill_buffer,
+ fill_buffer + std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled),
+ fill_wrapper_base<DefaultFiller>::get_filler());
+ do
+ {
+ std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled);
+ std::size_t filled;
+ err = Base::write_some_at(current, fill_buffer, chunk, filled);
+ current += filled;
+ unfilled -= filled;
+ }
+ while (unfilled && (!err || (std::errc::interrupted == err)));
+ }
+ if (err)
+ {
+ actual = 0U;
+ return err;
+ }
+ return Base::write_some_at(offset, buffer, length, actual);
+ }
+};
+
+
+template <typename Base, std::uint8_t DefaultFiller = 0U, std::size_t FillBlock = 1024U>
+using random_read_write_fill_wrapper = random_write_fill_wrapper<random_read_fill_wrapper<Base, DefaultFiller>, DefaultFiller, FillBlock>;
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_IOPROCSFILL_H
diff --git a/src/lib/util/ioprocsfilter.cpp b/src/lib/util/ioprocsfilter.cpp
new file mode 100644
index 00000000000..b8d4cc3e786
--- /dev/null
+++ b/src/lib/util/ioprocsfilter.cpp
@@ -0,0 +1,792 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocs.h
+
+ I/O filters
+
+***************************************************************************/
+
+#include "ioprocsfilter.h"
+
+#include "ioprocs.h"
+#include "ioprocsfill.h"
+
+#include <zlib.h>
+
+#include <algorithm>
+#include <cassert>
+#include <cerrno>
+#include <cstdint>
+#include <limits>
+#include <system_error>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+
+namespace util {
+
+namespace {
+
+// helper class for holding zlib data
+
+class zlib_data
+{
+protected:
+ zlib_data(std::size_t buffer_size) noexcept :
+ m_buffer_size(std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), buffer_size))
+ {
+ assert(buffer_size);
+ }
+
+ z_stream &get_z_stream() noexcept { return m_z_stream; }
+ z_stream const &get_z_stream() const noexcept { return m_z_stream; }
+ Bytef *get_buffer() noexcept { return m_buffer.get(); }
+ Bytef const *get_buffer() const noexcept { return m_buffer.get(); }
+ std::size_t get_buffer_size() const noexcept { return m_buffer_size; }
+
+ bool input_empty() const noexcept { return !m_z_stream.avail_in; }
+ bool output_available() const noexcept { return bool(m_z_stream.avail_out); }
+
+ void initialize_z_stream() noexcept
+ {
+ m_z_stream.zalloc = Z_NULL;
+ m_z_stream.zfree = Z_NULL;
+ m_z_stream.opaque = Z_NULL;
+ }
+
+ std::error_condition allocate_buffer() noexcept
+ {
+ if (!m_buffer)
+ m_buffer.reset(new (std::nothrow) Bytef [m_buffer_size]);
+ return m_buffer ? std::error_condition() : std::errc::not_enough_memory;
+ }
+
+ void release_buffer() noexcept
+ {
+ m_buffer.reset();
+ }
+
+ static std::error_condition convert_z_error(int err) noexcept
+ {
+ switch (err)
+ {
+ case Z_OK:
+ case Z_STREAM_END:
+ return std::error_condition();
+ case Z_NEED_DICT:
+ return std::errc::invalid_argument;
+ case Z_ERRNO:
+ return std::error_condition(errno, std::generic_category());
+ case Z_STREAM_ERROR:
+ return std::errc::invalid_argument;
+ case Z_DATA_ERROR:
+ return std::errc::invalid_argument; // TODO: revisit this error code
+ case Z_MEM_ERROR:
+ return std::errc::not_enough_memory;
+ case Z_BUF_ERROR:
+ return std::errc::operation_would_block; // TODO: revisit this error code (should be handled internally)
+ case Z_VERSION_ERROR:
+ return std::errc::invalid_argument; // TODO: revisit this error code (library ABI mismatch)
+ default:
+ return std::errc::io_error; // TODO: better default error code
+ }
+ }
+
+private:
+ z_stream m_z_stream;
+ std::unique_ptr<Bytef []> m_buffer;
+ std::size_t const m_buffer_size;
+};
+
+
+// helper class for decompressing deflated data
+
+class inflate_data : private zlib_data
+{
+protected:
+ inflate_data(std::size_t buffer_size) noexcept : zlib_data(buffer_size)
+ {
+ }
+
+ ~inflate_data()
+ {
+ if (m_inflate_initialized)
+ inflateEnd(&get_z_stream());
+ }
+
+ using zlib_data::input_empty;
+
+ bool stream_initialized() const noexcept
+ {
+ return m_inflate_initialized;
+ }
+
+ std::error_condition initialize_z_stream() noexcept
+ {
+ if (m_inflate_initialized)
+ return std::errc::invalid_argument;
+ std::error_condition err = allocate_buffer();
+ if (err)
+ return err;
+ zlib_data::initialize_z_stream();
+ int const zerr = inflateInit(&get_z_stream());
+ if (Z_OK == zerr)
+ m_inflate_initialized = true;
+ return convert_z_error(zerr);
+ }
+
+ std::error_condition close_z_stream() noexcept
+ {
+ if (!m_inflate_initialized)
+ return std::errc::invalid_argument;
+ int const zerr = inflateEnd(&get_z_stream());
+ m_inflate_initialized = false;
+ return convert_z_error(zerr);
+ }
+
+ std::error_condition decompress_some(bool &stream_end) noexcept
+ {
+ assert(m_inflate_initialized);
+ int const zerr = inflate(&get_z_stream(), Z_SYNC_FLUSH);
+ if (!get_z_stream().avail_in)
+ reset_input();
+ stream_end = Z_STREAM_END == zerr;
+ return convert_z_error(zerr);
+ }
+
+ std::size_t input_available() const noexcept
+ {
+ return std::size_t(get_z_stream().avail_in);
+ }
+
+ void reset_input() noexcept
+ {
+ get_z_stream().next_in = get_buffer();
+ get_z_stream().avail_in = 0U;
+ }
+
+ std::pair<void *, std::size_t> get_unfilled_input() noexcept
+ {
+ assert(get_buffer());
+ Bytef *const base = get_z_stream().next_in + get_z_stream().avail_in;
+ return std::make_pair(base, get_buffer() + get_buffer_size() - base);
+ }
+
+ void add_input(std::size_t length) noexcept
+ {
+ assert(get_buffer());
+ get_z_stream().avail_in += length;
+ assert((get_buffer() + get_buffer_size()) >= (get_z_stream().next_in + get_z_stream().avail_in));
+ }
+
+ std::size_t output_produced() const noexcept
+ {
+ return m_output_max - get_z_stream().avail_out;
+ }
+
+ void set_output(void *buffer, std::size_t length) noexcept
+ {
+ m_output_max = std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), length);
+ get_z_stream().next_out = reinterpret_cast<Bytef *>(buffer);
+ get_z_stream().avail_out = uInt(m_output_max);
+ }
+
+private:
+ std::error_condition allocate_buffer() noexcept
+ {
+ std::error_condition err;
+ if (!get_buffer())
+ {
+ err = zlib_data::allocate_buffer();
+ reset_input();
+ }
+ return err;
+ }
+
+ bool m_inflate_initialized = false;
+ std::size_t m_output_max = 0U;
+};
+
+
+// helper class for deflating data
+
+class deflate_data : private zlib_data
+{
+protected:
+ deflate_data(int level, std::size_t buffer_size) noexcept : zlib_data(buffer_size), m_level(level)
+ {
+ }
+
+ ~deflate_data()
+ {
+ if (m_deflate_initialized)
+ deflateEnd(&get_z_stream());
+ }
+
+ using zlib_data::input_empty;
+ using zlib_data::output_available;
+
+ bool stream_initialized() const noexcept
+ {
+ return m_deflate_initialized;
+ }
+
+ bool compression_finished() const noexcept
+ {
+ return m_compression_finished;
+ }
+
+ std::error_condition initialize_z_stream() noexcept
+ {
+ if (m_deflate_initialized)
+ return std::errc::invalid_argument;
+ std::error_condition err = allocate_buffer();
+ if (err)
+ return err;
+ zlib_data::initialize_z_stream();
+ int const zerr = deflateInit(&get_z_stream(), m_level);
+ reset_output();
+ if (Z_OK == zerr)
+ m_deflate_initialized = true;
+ return convert_z_error(zerr);
+ }
+
+ std::error_condition close_z_stream() noexcept
+ {
+ if (!m_deflate_initialized)
+ return std::errc::invalid_argument;
+ m_deflate_initialized = m_compression_finished = false;
+ return convert_z_error(deflateEnd(&get_z_stream()));
+ }
+
+ std::error_condition reset_z_stream() noexcept
+ {
+ if (!m_deflate_initialized)
+ return std::errc::invalid_argument;
+ assert(get_buffer());
+ reset_output();
+ m_compression_finished = false;
+ return convert_z_error(deflateReset(&get_z_stream()));
+ }
+
+ std::error_condition compress_some() noexcept
+ {
+ assert(m_deflate_initialized);
+ return convert_z_error(deflate(&get_z_stream(), Z_NO_FLUSH));
+ }
+
+ std::error_condition finish_compression() noexcept
+ {
+ assert(m_deflate_initialized);
+ int const zerr = deflate(&get_z_stream(), Z_FINISH);
+ if (Z_STREAM_END == zerr)
+ m_compression_finished = true;
+ return convert_z_error(zerr);
+ }
+
+ std::size_t input_used() const noexcept
+ {
+ return m_input_max - get_z_stream().avail_in;
+ }
+
+ void set_input(void const *data, std::size_t length) noexcept
+ {
+ m_input_max = std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), length);
+ get_z_stream().next_in = const_cast<Bytef z_const *>(reinterpret_cast<Bytef const *>(data));
+ get_z_stream().avail_in = uInt(m_input_max);
+ }
+
+ std::pair<void const *, std::size_t> get_output() const noexcept
+ {
+ assert(get_buffer());
+ return std::make_pair(
+ &get_buffer()[m_output_used],
+ get_buffer_size() - get_z_stream().avail_out - m_output_used);
+ }
+
+ void consume_output(std::size_t length) noexcept
+ {
+ m_output_used += length;
+ assert(m_output_used <= (get_buffer_size() - get_z_stream().avail_out));
+ if (m_output_used == (get_buffer_size() - get_z_stream().avail_out))
+ reset_output();
+ }
+
+private:
+ void reset_output() noexcept
+ {
+ assert(get_buffer());
+ m_output_used = 0U;
+ get_z_stream().next_out = get_buffer();
+ get_z_stream().avail_out = uInt(get_buffer_size());
+ }
+
+ int const m_level;
+ bool m_deflate_initialized = false;
+ bool m_compression_finished = false;
+ std::size_t m_output_used = 0U;
+ std::size_t m_input_max = 0U;
+};
+
+
+// helper for holding an object and deleting it (or not) as necessary
+
+template <typename T>
+class filter_base
+{
+public:
+ filter_base(std::unique_ptr<T> &&object) noexcept : m_object(object.release()), m_owned(true)
+ {
+ assert(m_object);
+ }
+
+ filter_base(T &object) noexcept : m_object(&object), m_owned(false)
+ {
+ }
+
+protected:
+ ~filter_base()
+ {
+ if (m_owned)
+ delete m_object;
+ }
+
+ T &object() noexcept
+ {
+ return *m_object;
+ }
+
+private:
+ T *const m_object;
+ bool const m_owned;
+};
+
+
+// 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_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().read_some(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_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().write_some(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_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().read_some_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_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return this->object().write_some_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>
+class zlib_read_filter : public read_stream, protected filter_base<Stream>, protected inflate_data
+{
+public:
+ zlib_read_filter(std::unique_ptr<Stream> &&stream, std::size_t read_chunk) noexcept :
+ filter_base<Stream>(std::move(stream)),
+ inflate_data(read_chunk)
+ {
+ }
+
+ zlib_read_filter(Stream &stream, std::size_t read_chunk) noexcept :
+ filter_base<Stream>(stream),
+ inflate_data(read_chunk)
+ {
+ }
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ std::error_condition err;
+ actual = 0U;
+
+ if (!stream_initialized())
+ err = initialize_z_stream();
+
+ if (!err && (length > actual))
+ {
+ bool short_input = false;
+ do
+ {
+ if (input_empty())
+ {
+ auto const space = get_unfilled_input();
+ std::size_t filled;
+ std::tie(err, filled) = read(this->object(), space.first, space.second);
+ add_input(filled);
+ short_input = space.second > filled;
+ }
+
+ if (!err && !input_empty())
+ {
+ set_output(reinterpret_cast<std::uint8_t *>(buffer) + actual, length - actual);
+ bool stream_end;
+ err = decompress_some(stream_end);
+ actual += output_produced();
+
+ if (stream_end)
+ {
+ assert(!err);
+ if constexpr (std::is_base_of_v<random_read, Stream>)
+ {
+ if (!input_empty())
+ {
+ std::int64_t const overshoot = std::uint64_t(input_available());
+ reset_input();
+ return this->object().seek(-overshoot, SEEK_CUR);
+ }
+ }
+ else
+ {
+ return std::error_condition();
+ }
+ }
+ }
+ }
+ while (!err && (length > actual) && !short_input);
+ }
+
+ return err;
+ }
+};
+
+
+// filter for deflating data
+
+class zlib_write_filter : public write_stream, protected filter_base<write_stream>, protected deflate_data
+{
+public:
+ zlib_write_filter(write_stream::ptr &&stream, int level, std::size_t buffer_size) noexcept :
+ filter_base<write_stream>(std::move(stream)),
+ deflate_data(level, buffer_size)
+ {
+ }
+
+ zlib_write_filter(write_stream &stream, int level, std::size_t buffer_size) noexcept :
+ filter_base<write_stream>(stream),
+ deflate_data(level, buffer_size)
+ {
+ }
+
+ ~zlib_write_filter()
+ {
+ finalize();
+ }
+
+ virtual std::error_condition finalize() noexcept override
+ {
+ if (!stream_initialized())
+ return std::error_condition();
+
+ do
+ {
+ if (!compression_finished() && output_available())
+ {
+ std::error_condition err = finish_compression();
+ if (err)
+ return err;
+ }
+
+ while ((compression_finished() && get_output().second) || !output_available())
+ {
+ std::error_condition err = write_some();
+ if (err)
+ return err;
+ }
+ }
+ while (!compression_finished());
+
+ return close_z_stream();
+ }
+
+ virtual std::error_condition flush() noexcept override
+ {
+ if (stream_initialized())
+ {
+ auto const output = get_output();
+ if (output.second)
+ {
+ auto const [err, written] = write(object(), output.first, output.second);
+ consume_output(written);
+ if (err)
+ {
+ object().flush();
+ return err;
+ }
+ }
+ }
+ return object().flush();
+ }
+
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ std::error_condition err;
+ actual = 0U;
+
+ if (!stream_initialized())
+ {
+ err = initialize_z_stream();
+ }
+ else if (compression_finished())
+ {
+ while (!err && get_output().second)
+ err = write_some();
+ if (!err)
+ err = reset_z_stream();
+ }
+
+ while (!err && (length > actual))
+ {
+ set_input(reinterpret_cast<std::uint8_t const *>(buffer) + actual, length - actual);
+ do
+ {
+ if (output_available())
+ err = compress_some();
+
+ while (!err && !output_available())
+ err = write_some();
+ }
+ while (!err && !input_empty());
+ actual += input_used();
+ }
+
+ return err;
+ }
+
+private:
+ std::error_condition write_some() noexcept
+ {
+ auto const output = get_output();
+ std::size_t written;
+ std::error_condition const err = object().write_some(output.first, output.second, written);
+ consume_output(written);
+ return err;
+ }
+};
+
+} // 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
+{
+ read_stream::ptr result;
+ if (stream)
+ result.reset(new (std::nothrow) zlib_read_filter<read_stream>(std::move(stream), read_chunk));
+ return result;
+}
+
+read_stream::ptr zlib_read(random_read::ptr &&stream, std::size_t read_chunk) noexcept
+{
+ read_stream::ptr result;
+ if (stream)
+ result.reset(new (std::nothrow) zlib_read_filter<random_read>(std::move(stream), read_chunk));
+ return result;
+}
+
+read_stream::ptr zlib_read(read_stream &stream, std::size_t read_chunk) noexcept
+{
+ return read_stream::ptr(new (std::nothrow) zlib_read_filter<read_stream>(stream, read_chunk));
+}
+
+read_stream::ptr zlib_read(random_read &stream, std::size_t read_chunk) noexcept
+{
+ return read_stream::ptr(new (std::nothrow) zlib_read_filter<random_read>(stream, read_chunk));
+}
+
+
+// creating compressing filters
+
+write_stream::ptr zlib_write(write_stream::ptr &&stream, int level, std::size_t buffer_size) noexcept
+{
+ write_stream::ptr result;
+ if (stream)
+ result.reset(new (std::nothrow) zlib_write_filter(std::move(stream), level, buffer_size));
+ return result;
+}
+
+write_stream::ptr zlib_write(write_stream &stream, int level, std::size_t buffer_size) noexcept
+{
+ return write_stream::ptr(new (std::nothrow) zlib_write_filter(stream, level, buffer_size));
+}
+
+} // namespace util
diff --git a/src/lib/util/ioprocsfilter.h b/src/lib/util/ioprocsfilter.h
new file mode 100644
index 00000000000..fbe24ab46e9
--- /dev/null
+++ b/src/lib/util/ioprocsfilter.h
@@ -0,0 +1,283 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocs.h
+
+ I/O filters
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_IOPROCSFILTER_H
+#define MAME_LIB_UTIL_IOPROCSFILTER_H
+
+#include "utilfwd.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <memory>
+
+
+namespace util {
+
+/// \addtogroup ioprocs
+/// \{
+
+/// \brief Create a read stream filter that fills unused buffer space
+///
+/// Creates a sequential read stream filter that fills unused buffer
+/// space with a specified byte value. If a read operation does not
+/// produce enough data to fill the supplied buffer, the remaining space
+/// in the buffer is filled with the specified filler byte value. Takes
+/// ownership of the underlying input stream.
+/// \param [in] stream Underlying stream to read from.
+/// \param [in] filler Byte value to fill unused buffer space.
+/// \return A pointer to a sequential read stream, or nullptr on error.
+/// \sa read_stream
+std::unique_ptr<read_stream> read_stream_fill(std::unique_ptr<read_stream> &&stream, std::uint8_t filler) noexcept;
+
+/// \brief Create a random access input filter that fills unused buffer
+/// space
+///
+/// Creates a random access input filter that fills unused buffer space
+/// with a specified byte value. If a read operation does not produce
+/// enough data to fill the supplied buffer, the remaining space in the
+/// buffer is filled with the specified filler byte value. Takes
+/// ownership of the underlying input sequence.
+/// \param [in] stream Underlying input sequence to read from.
+/// \param [in] filler Byte value to fill unused buffer space.
+/// \return A pointer to a random access input sequence, or nullptr on
+/// error.
+/// \sa random_read
+std::unique_ptr<random_read> random_read_fill(std::unique_ptr<random_read> &&stream, std::uint8_t filler) noexcept;
+
+/// \brief Create a read stream filter that fills unused buffer space
+///
+/// Creates a sequential read stream filter that fills unused buffer
+/// space with a specified byte value. If a read operation does not
+/// produce enough data to fill the supplied buffer, the remaining space
+/// in the buffer is filled with the specified filler byte value. Does
+/// not take ownership of the underlying input stream.
+/// \param [in] stream Underlying stream to read from.
+/// \param [in] filler Byte value to fill unused buffer space.
+/// \return A pointer to a sequential read stream, or nullptr on error.
+/// \sa read_stream
+std::unique_ptr<read_stream> read_stream_fill(read_stream &stream, std::uint8_t filler) noexcept;
+
+/// \brief Create a random access input filter that fills unused buffer
+/// space
+///
+/// Creates a random access input filter that fills unused buffer space
+/// with a specified byte value. If a read operation does not produce
+/// enough data to fill the supplied buffer, the remaining space in the
+/// buffer is filled with the specified filler byte value. Does not
+/// take ownership of the underlying input sequence.
+/// \param [in] stream Underlying input sequence to read from.
+/// \param [in] filler Byte value to fill unused buffer space.
+/// \return A pointer to a random access input sequence, or nullptr on
+/// error.
+/// \sa random_read
+std::unique_ptr<random_read> random_read_fill(random_read &stream, std::uint8_t filler) noexcept;
+
+
+/// \brief Create a random access output filter that fills unwritten
+/// space
+///
+/// Creates a random access output filter that fills unwritten space
+/// with a specified byte value. If a write operation starts beyond the
+/// end of the output, the space between the end of the output and the
+/// start of the written data is filled with the specified filler byte
+/// value. Takes ownership of the underlying output sequence.
+/// \param [in] stream Underlying output sequence to write to.
+/// \param [in] filler Byte value to fill unwritten space.
+/// \return A pointer to a random access output sequence, or nullptr on
+/// error.
+/// \sa random_write
+std::unique_ptr<random_write> random_write_fill(std::unique_ptr<random_write> &&stream, std::uint8_t filler) noexcept;
+
+/// \brief Create a random access output filter that fills unwritten
+/// space
+///
+/// Creates a random access output filter that fills unwritten space
+/// with a specified byte value. If a write operation starts beyond the
+/// end of the output, the space between the end of the output and the
+/// start of the written data is filled with the specified filler byte
+/// value. Does not take ownership of the underlying output sequence.
+/// \param [in] stream Underlying output sequence to write to.
+/// \param [in] filler Byte value to fill unwritten space.
+/// \return A pointer to a random access output sequence, or nullptr on
+/// error.
+/// \sa random_write
+std::unique_ptr<random_write> random_write_fill(random_write &stream, std::uint8_t filler) noexcept;
+
+
+/// \brief Create a random access I/O filter that fills unused space
+///
+/// Creates a random access I/O sequence that fills unused read buffer
+/// space and unwritten space with a specified byte value. If a read
+/// operation does not produce enough data to fill the supplied buffer,
+/// the remaining space in the buffer is filled with the specified
+/// filler byte value. If a write operation starts beyond the end of
+/// the output, the space between the end of the output and the start of
+/// the written data is filled with the specified filler byte value.
+/// Takes ownership of the underlying I/O sequence.
+/// \param [in] stream Underlying I/O sequence to read from and write
+/// to.
+/// \param [in] filler Byte value to fill unused read buffer space and
+/// unwritten space.
+/// \return A pointer to a random access I/O sequence, or nullptr on
+/// error.
+/// \sa random_read_write
+std::unique_ptr<random_read_write> random_read_write_fill(std::unique_ptr<random_read_write> &&stream, std::uint8_t filler) noexcept;
+
+/// \brief Create a random access I/O filter that fills unused space
+///
+/// Creates a random access I/O sequence that fills unused read buffer
+/// space and unwritten space with a specified byte value. If a read
+/// operation does not produce enough data to fill the supplied buffer,
+/// the remaining space in the buffer is filled with the specified
+/// filler byte value. If a write operation starts beyond the end of
+/// the output, the space between the end of the output and the start of
+/// the written data is filled with the specified filler byte value.
+/// Does not take ownership of the underlying I/O sequence.
+/// \param [in] stream Underlying I/O sequence to read from and write
+/// to.
+/// \param [in] filler Byte value to fill unused read buffer space and
+/// unwritten space.
+/// \return A pointer to a random access I/O sequence, or nullptr on
+/// error.
+/// \sa random_read_write
+std::unique_ptr<random_read_write> random_read_write_fill(random_read_write &stream, std::uint8_t filler) noexcept;
+
+
+/// \brief Create an input stream filter that decompresses
+/// zlib-compressed data
+///
+/// Creates a read stream that decompresses zlib-compressed (deflated)
+/// data read from the underlying input stream. A read operation will
+/// always stop on reaching an end-of-stream marker in the compressed
+/// data. A subsequent read operation will expect to find the beginning
+/// of another block of compressed data. May read past the end of the
+/// compressed data in the underlying input stream. Takes ownership of
+/// the underlying input stream.
+/// \param [in] stream Underlying input stream to read from.
+/// \param [in] read_chunk Size of buffer for reading compressed data in
+/// bytes.
+/// \return A pointer to an input stream, or nullptr on error.
+/// \sa read_stream
+std::unique_ptr<read_stream> zlib_read(std::unique_ptr<read_stream> &&stream, std::size_t read_chunk) noexcept;
+
+/// \brief Create an input stream filter that decompresses
+/// zlib-compressed data
+///
+/// Creates a read stream that decompresses zlib-compressed (deflated)
+/// data read from the underlying input sequence. A read operation will
+/// always stop on reaching an end-of-stream marker in the compressed
+/// data. A subsequent read operation will expect to find the beginning
+/// of another block of compressed data. If a read operation reads past
+/// an end-of-stream marker in the compressed data, it will seek back so
+/// the position for the next read from the underlying input sequence
+/// immediately follows the end-of-stream marker. Takes ownership of
+/// the underlying input sequence.
+/// \param [in] stream Underlying input sequence to read from. Must
+/// support seeking relative to the current position.
+/// \param [in] read_chunk Size of buffer for reading compressed data in
+/// bytes.
+/// \return A pointer to an input stream, or nullptr on error.
+/// \sa read_stream random_read
+std::unique_ptr<read_stream> zlib_read(std::unique_ptr<random_read> &&stream, std::size_t read_chunk) noexcept;
+
+/// \brief Create an input stream filter that decompresses
+/// zlib-compressed data
+///
+/// Creates a read stream that decompresses zlib-compressed (deflated)
+/// data read from the underlying input stream. A read operation will
+/// always stop on reaching an end-of-stream marker in the compressed
+/// data. A subsequent read operation will expect to find the beginning
+/// of another block of compressed data. May read past the end of the
+/// compressed data in the underlying input stream. Does not take
+/// ownership of the underlying input stream.
+/// \param [in] stream Underlying input stream to read from.
+/// \param [in] read_chunk Size of buffer for reading compressed data in
+/// bytes.
+/// \return A pointer to an input stream, or nullptr on error.
+/// \sa read_stream
+std::unique_ptr<read_stream> zlib_read(read_stream &stream, std::size_t read_chunk) noexcept;
+
+/// \brief Create an input stream filter that decompresses
+/// zlib-compressed data
+///
+/// Creates a read stream that decompresses zlib-compressed (deflated)
+/// data read from the underlying input sequence. A read operation will
+/// always stop on reaching an end-of-stream marker in the compressed
+/// data. A subsequent read operation will expect to find the beginning
+/// of another block of compressed data. If a read operation reads past
+/// an end-of-stream marker in the compressed data, it will seek back so
+/// the position for the next read from the underlying input sequence
+/// immediately follows the end-of-stream marker. Does not take
+/// ownership of the underlying input sequence.
+/// \param [in] stream Underlying input sequence to read from. Must
+/// support seeking relative to the current position.
+/// \param [in] read_chunk Size of buffer for reading compressed data in
+/// bytes.
+/// \return A pointer to an input stream, or nullptr on error.
+/// \sa read_stream random_read
+std::unique_ptr<read_stream> zlib_read(random_read &stream, std::size_t read_chunk) noexcept;
+
+
+/// \brief Create an output stream filter that writes zlib-compressed
+/// data
+///
+/// Creates an output stream that compresses data using the zlib deflate
+/// algorithm and writes it to the underlying output stream. Calling
+/// the \c finalize member function compresses any buffered input,
+/// produces an end-of-stream maker, and writes any buffered compressed
+/// data to the underlying output stream. A subsequent write operation
+/// will start a new compressed block. Calling the \c flush member
+/// function writes any buffered compressed data to the underlying
+/// output stream and calls the \c flush member function of the
+/// underlying output stream; it does not ensure all buffered input data
+/// is compressed or force the end of a compressed block. Takes
+/// ownership of the underlying output stream.
+/// \param [in] stream Underlying output stream for writing compressed
+/// data.
+/// \param [in] level Compression level. Use 0 for no compression, 1
+/// for fastest compression, 9 for maximum compression, or -1 for the
+/// default compression level as defined by the zlib library. Larger
+/// values between 1 and 9 provide higher compression at the expense
+/// of speed.
+/// \param [in] buffer_size Size of buffer for compressed data in bytes.
+/// \return A pointer to an output stream, or nullptr on error.
+/// \sa write_stream
+std::unique_ptr<write_stream> zlib_write(std::unique_ptr<write_stream> &&stream, int level, std::size_t buffer_size) noexcept;
+
+/// \brief Create an output stream filter that writes zlib-compressed
+/// data
+///
+/// Creates an output stream that compresses data using the zlib deflate
+/// algorithm and writes it to the underlying output stream. Calling
+/// the \c finalize member function compresses any buffered input,
+/// produces an end-of-stream maker, and writes any buffered compressed
+/// data to the underlying output stream. A subsequent write operation
+/// will start a new compressed block. Calling the \c flush member
+/// function writes any buffered compressed data to the underlying
+/// output stream and calls the \c flush member function of the
+/// underlying output stream; it does not ensure all buffered input data
+/// is compressed or force the end of a compressed block. Does not take
+/// ownership of the underlying output stream.
+/// \param [in] stream Underlying output stream for writing compressed
+/// data.
+/// \param [in] level Compression level. Use 0 for no compression, 1
+/// for fastest compression, 9 for maximum compression, or -1 for the
+/// default compression level as defined by the zlib library. Larger
+/// values between 1 and 9 provide higher compression at the expense
+/// of speed.
+/// \param [in] buffer_size Size of buffer for compressed data in bytes.
+/// \return A pointer to an output stream, or nullptr on error.
+/// \sa write_stream
+std::unique_ptr<write_stream> zlib_write(write_stream &stream, int level, std::size_t buffer_size) noexcept;
+
+/// \}
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_IOPROCSFILTER_H
diff --git a/src/lib/util/ioprocsvec.h b/src/lib/util/ioprocsvec.h
new file mode 100644
index 00000000000..e66000b5729
--- /dev/null
+++ b/src/lib/util/ioprocsvec.h
@@ -0,0 +1,212 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ ioprocsvec.h
+
+ Helper for using a vector as an in-memory file
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_IOPROCSVEC_H
+#define MAME_LIB_UTIL_IOPROCSVEC_H
+
+#include "ioprocs.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <limits>
+#include <system_error>
+#include <type_traits>
+#include <vector>
+
+
+namespace util {
+
+template <typename T = std::uint8_t>
+class vector_read_write_adapter : public random_read_write
+{
+public:
+ vector_read_write_adapter(std::vector<T> &storage) noexcept : m_storage(storage)
+ {
+ check_resized();
+ }
+
+ virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override
+ {
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (0 > offset)
+ return std::errc::invalid_argument;
+ m_pointer = std::uint64_t(offset);
+ return std::error_condition();
+
+ case SEEK_CUR:
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_pointer)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_pointer += offset;
+ return std::error_condition();
+
+ case SEEK_END:
+ check_resized();
+ if (0 > offset)
+ {
+ if (std::uint64_t(-offset) > m_size)
+ return std::errc::invalid_argument;
+ }
+ else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size)
+ {
+ return std::errc::invalid_argument;
+ }
+ m_pointer = std::uint64_t(m_size) + offset;
+ return std::error_condition();
+
+ default:
+ return std::errc::invalid_argument;
+ }
+ }
+
+ virtual std::error_condition tell(std::uint64_t &result) noexcept override
+ {
+ result = m_pointer;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition length(std::uint64_t &result) noexcept override
+ {
+ check_resized();
+ if (std::numeric_limits<uint64_t>::max() < m_size)
+ return std::errc::file_too_large;
+
+ result = m_size;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ do_read(m_pointer, buffer, length, actual);
+ m_pointer += actual;
+ return std::error_condition();
+ }
+
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ do_read(offset, buffer, length, actual);
+ return std::error_condition();
+ }
+
+ virtual std::error_condition finalize() noexcept override
+ {
+ return std::error_condition();
+ }
+
+ virtual std::error_condition flush() noexcept override
+ {
+ return std::error_condition();
+ }
+
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ std::error_condition result = do_write(m_pointer, buffer, length, actual);
+ m_pointer += actual;
+ return result;
+ }
+
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ {
+ return do_write(offset, buffer, length, actual);
+ }
+
+private:
+ using elements_type = typename std::vector<T>::size_type;
+ using common_size_type = std::common_type_t<std::uint64_t, std::size_t>;
+
+ void check_resized() noexcept
+ {
+ if (m_storage.size() != m_elements)
+ {
+ m_elements = m_storage.size();
+ m_size = m_elements * sizeof(T);
+ }
+ }
+
+ void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+ {
+ check_resized();
+ if ((offset < m_size) && length)
+ {
+ actual = std::min(std::size_t(m_size - offset), length);
+ std::memmove(buffer, reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, actual);
+ }
+ else
+ {
+ actual = 0U;
+ }
+ }
+
+ std::error_condition do_write(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
+ {
+ check_resized();
+ std::size_t allocated = m_elements * sizeof(T);
+ std::uint64_t const target = ((std::numeric_limits<std::uint64_t>::max() - offset) >= length)
+ ? (offset + length)
+ : std::numeric_limits<std::uint64_t>::max();
+
+ std::error_condition result;
+ if (target > allocated)
+ {
+ elements_type const required = std::min<std::common_type_t<elements_type, std::uint64_t> >(
+ std::numeric_limits<elements_type>::max(),
+ (target + sizeof(T) - 1) / sizeof(T)); // technically can overflow but unlikely
+ try { m_storage.resize(required); } // unpredictable if constructing/assigning T can throw
+ catch (...) { result = std::errc::not_enough_memory; }
+ m_elements = m_storage.size();
+ allocated = m_elements * sizeof(T);
+ assert(allocated >= m_size);
+
+ if (offset > m_size)
+ {
+ std::size_t const new_size = std::size_t(std::min<common_size_type>(allocated, offset));
+ std::memset(
+ reinterpret_cast<std::uint8_t *>(m_storage.data()) + m_size,
+ 0x00, // POSIX says unwritten areas of files should read as zero
+ new_size - m_size);
+ m_size = new_size;
+ }
+ }
+
+ std::size_t const limit = std::min<common_size_type>(target, allocated);
+ if (limit > offset)
+ {
+ actual = std::size_t(limit - offset);
+ std::memmove(reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, buffer, actual);
+ if (limit > m_size)
+ m_size = limit;
+ }
+ else
+ {
+ actual = 0U;
+ }
+ if (!result && (actual < length))
+ result = std::errc::no_space_on_device;
+
+ return result;
+ }
+
+ std::vector<T> &m_storage;
+ std::uint64_t m_pointer = 0U;
+ elements_type m_elements = 0U;
+ std::size_t m_size = 0U;
+};
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_IOPROCSVEC_H
diff --git a/src/lib/util/jedparse.cpp b/src/lib/util/jedparse.cpp
index c2c9a139a6a..b347a2dd2b5 100644
--- a/src/lib/util/jedparse.cpp
+++ b/src/lib/util/jedparse.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- jedparse.c
+ jedparse.cpp
Parser for .JED files into raw fusemaps.
@@ -16,12 +16,16 @@
***************************************************************************/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
#include "jedparse.h"
+#include "ioprocs.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+#include <tuple>
+
/***************************************************************************
@@ -130,6 +134,7 @@ static void process_field(jed_data *data, const uint8_t *cursrc, const uint8_t *
case 'F':
cursrc++;
pinfo->explicit_numfuses = data->numfuses = suck_number(&cursrc);
+ if (LOG_PARSE) printf("QF\n %lu\n", (unsigned long)data->numfuses);
break;
}
break;
@@ -159,7 +164,7 @@ static void process_field(jed_data *data, const uint8_t *cursrc, const uint8_t *
if (*cursrc == '0' || *cursrc == '1')
{
jed_set_fuse(data, curfuse, *cursrc - '0');
- if (LOG_PARSE) printf(" fuse %u = %d\n", curfuse, 0);
+ if (LOG_PARSE) printf(" fuse %u = %d\n", curfuse, jed_get_fuse(data, curfuse));
if (curfuse >= data->numfuses)
data->numfuses = curfuse + 1;
curfuse++;
@@ -187,52 +192,106 @@ static void process_field(jed_data *data, const uint8_t *cursrc, const uint8_t *
loaded raw into memory
-------------------------------------------------*/
-int jed_parse(const void *data, size_t length, jed_data *result)
+int jed_parse(util::random_read &src, jed_data *result)
{
- const uint8_t *cursrc = (const uint8_t *)data;
- const uint8_t *srcend = cursrc + length;
- const uint8_t *scan;
jed_parse_info pinfo;
- uint16_t checksum;
- int i;
+ std::size_t actual;
+ std::error_condition err;
/* initialize the output and the intermediate info struct */
memset(result, 0, sizeof(*result));
memset(&pinfo, 0, sizeof(pinfo));
/* first scan for the STX character; ignore anything prior */
- while (cursrc < srcend && *cursrc != 0x02)
- cursrc++;
- if (cursrc >= srcend)
- return JEDERR_INVALID_DATA;
+ uint8_t ch;
+ do
+ {
+ std::tie(err, actual) = read(src, &ch, 1);
+ if (err)
+ {
+ if (LOG_PARSE) printf("Read error searching for JED start marker\n");
+ return JEDERR_INVALID_DATA;
+ }
+ else if (actual != 1)
+ {
+ if (LOG_PARSE) printf("End of file encountered while searching for JED start marker\n");
+ return JEDERR_INVALID_DATA;
+ }
+ }
+ while (ch != 0x02);
/* then scan to see if we have an ETX */
- scan = cursrc;
- checksum = 0;
- while (scan < srcend && *scan != 0x03)
- checksum += *scan++ & 0x7f;
- if (scan >= srcend)
+ uint64_t startpos = 0;
+ uint16_t checksum = ch;
+ do
+ {
+ std::tie(err, actual) = read(src, &ch, 1);
+ if (err)
+ {
+ if (LOG_PARSE) printf("Read error searching for JED end marker\n");
+ return JEDERR_INVALID_DATA;
+ }
+ else if (actual != 1)
+ {
+ if (LOG_PARSE) printf("End of file encountered while searching for JED end marker\n");
+ return JEDERR_INVALID_DATA;
+ }
+ checksum += ch & 0x7f;
+
+ /* mark end of comment field */
+ if (ch == '*' && startpos == 0)
+ {
+ if (src.tell(startpos))
+ {
+ if (LOG_PARSE) printf("Error getting file position for end of design specification\n");
+ return JEDERR_INVALID_DATA;
+ }
+ }
+ }
+ while (ch != 0x03);
+
+ /* the ETX becomes the real srcend */
+ uint64_t endpos;
+ if (src.tell(endpos))
+ {
+ if (LOG_PARSE) printf("Error getting file position for end JED data\n");
return JEDERR_INVALID_DATA;
+ }
+ endpos--;
/* see if there is a transmission checksum at the end */
- checksum += *scan;
- if (scan + 4 < srcend && ishex(scan[1]) && ishex(scan[2]) && ishex(scan[3]) && ishex(scan[4]))
+ uint8_t sumbuf[4];
+ std::tie(err, actual) = read(src, &sumbuf[0], 4);
+ if (!err && (actual == 4) && ishex(sumbuf[0]) && ishex(sumbuf[1]) && ishex(sumbuf[2]) && ishex(sumbuf[3]))
{
- uint16_t dessum = (hexval(scan[1]) << 12) | (hexval(scan[2]) << 8) | (hexval(scan[3]) << 4) | hexval(scan[4] << 0);
+ uint16_t dessum = (hexval(sumbuf[0]) << 12) | (hexval(sumbuf[1]) << 8) | (hexval(sumbuf[2]) << 4) | hexval(sumbuf[3] << 0);
if (dessum != 0 && dessum != checksum)
+ {
+ if (LOG_PARSE) printf("Bad transmission checksum %04X (expected %04X)\n", dessum, checksum);
return JEDERR_BAD_XMIT_SUM;
+ }
}
- /* the ETX becomes the real srcend */
- srcend = scan;
-
/* blast through the comment field */
- cursrc++;
- while (cursrc < srcend && *cursrc != '*')
- cursrc++;
+ if (startpos != 0)
+ {
+ if (src.seek(startpos, SEEK_SET))
+ {
+ if (LOG_PARSE) printf("Error seeking start of JED data\n");
+ return JEDERR_INVALID_DATA;
+ }
+ }
+ auto srcdata = std::make_unique<uint8_t[]>(endpos - startpos);
+ std::tie(err, actual) = read(src, &srcdata[0], endpos - startpos);
+ if (err || ((endpos - startpos) != actual))
+ {
+ if (LOG_PARSE) printf("Error reading JED data\n");
+ return JEDERR_INVALID_DATA;
+ }
+ const uint8_t *cursrc = &srcdata[0];
+ const uint8_t *const srcend = &srcdata[endpos - startpos];
/* now loop over fields and decide which ones go in the file output */
- cursrc++;
while (cursrc < srcend)
{
/* skip over delimiters */
@@ -242,7 +301,7 @@ int jed_parse(const void *data, size_t length, jed_data *result)
break;
/* end of field is an asterisk -- find it */
- scan = cursrc;
+ const uint8_t *scan = cursrc;
while (scan < srcend && *scan != '*')
scan++;
if (scan >= srcend)
@@ -255,21 +314,26 @@ int jed_parse(const void *data, size_t length, jed_data *result)
cursrc = scan + 1;
}
+ srcdata.reset();
+
/* if we got an explicit fuse count, override our computed count */
if (pinfo.explicit_numfuses != 0)
result->numfuses = pinfo.explicit_numfuses;
- /* clear out leftover bits */
+ /* clear out leftover bits g*/
if (result->numfuses % 8 != 0)
result->fusemap[result->numfuses / 8] &= (1 << (result->numfuses % 8)) - 1;
memset(&result->fusemap[(result->numfuses + 7) / 8], 0, sizeof(result->fusemap) - (result->numfuses + 7) / 8);
/* validate the checksum */
checksum = 0;
- for (i = 0; i < (result->numfuses + 7) / 8; i++)
+ for (int i = 0; i < ((result->numfuses + 7) / 8); i++)
checksum += result->fusemap[i];
if (pinfo.checksum != 0 && checksum != pinfo.checksum)
+ {
+ if (LOG_PARSE) printf("Bad fuse checksum %04X (expected %04X)\n", pinfo.checksum, checksum);
return JEDERR_BAD_FUSE_SUM;
+ }
return JEDERR_NONE;
}
@@ -283,7 +347,7 @@ int jed_parse(const void *data, size_t length, jed_data *result)
size_t jed_output(const jed_data *data, void *result, size_t length)
{
- uint8_t *curdst = (uint8_t *)result;
+ auto *curdst = (uint8_t *)result;
uint8_t *dstend = curdst + length;
int i, zeros, ones;
char tempbuf[256];
@@ -293,13 +357,13 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
/* always start the DST with a standard header and an STX */
tempbuf[0] = 0x02;
- sprintf(&tempbuf[1], "JEDEC file generated by jedutil*\n");
+ snprintf(&tempbuf[1], 255, "JEDEC file generated by jedutil*\n");
if (curdst + strlen(tempbuf) <= dstend)
memcpy(curdst, tempbuf, strlen(tempbuf));
curdst += strlen(tempbuf);
/* append the package information */
- sprintf(tempbuf, "QF%d*\n", data->numfuses);
+ snprintf(tempbuf, 256, "QF%d*\n", data->numfuses);
if (curdst + strlen(tempbuf) <= dstend)
memcpy(curdst, tempbuf, strlen(tempbuf));
curdst += strlen(tempbuf);
@@ -320,7 +384,7 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
defbyte = (ones > zeros) ? 0xff : 0x00;
/* output the default fuse state */
- sprintf(tempbuf, "F%d*\n", defbyte & 1);
+ snprintf(tempbuf, 256, "F%d*\n", defbyte & 1);
if (curdst + strlen(tempbuf) <= dstend)
memcpy(curdst, tempbuf, strlen(tempbuf));
curdst += strlen(tempbuf);
@@ -336,10 +400,10 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
int j;
/* build up a string of 32 fuses */
- stroffs = sprintf(tempbuf, "L%05d ", i);
+ stroffs = snprintf(tempbuf, 256, "L%05d ", i);
for (j = 0; j < 32 && i+j < data->numfuses; j++)
tempbuf[stroffs++] = '0' + jed_get_fuse(data, i + j);
- stroffs += sprintf(&tempbuf[stroffs], "*\n");
+ stroffs += snprintf(&tempbuf[stroffs], 256-stroffs, "*\n");
/* append to the buffer */
if (curdst + strlen(tempbuf) <= dstend)
@@ -348,7 +412,7 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
}
/* write the checksum */
- sprintf(tempbuf, "C%04X*\n", checksum);
+ snprintf(tempbuf, 256, "C%04X*\n", checksum);
if (curdst + strlen(tempbuf) <= dstend)
memcpy(curdst, tempbuf, strlen(tempbuf));
curdst += strlen(tempbuf);
@@ -361,7 +425,7 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
/* append the ETX and the transmission checksum */
tempbuf[0] = 0x03;
- sprintf(&tempbuf[1], "%04X", checksum);
+ snprintf(&tempbuf[1], 255, "%04X", checksum);
if (curdst + strlen(tempbuf) <= dstend)
memcpy(curdst, tempbuf, strlen(tempbuf));
curdst += strlen(tempbuf);
@@ -377,29 +441,30 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
has been loaded raw into memory
-------------------------------------------------*/
-int jedbin_parse(const void *data, size_t length, jed_data *result)
+int jedbin_parse(util::read_stream &src, jed_data *result)
{
- const uint8_t *cursrc = (const uint8_t *)data;
+ std::error_condition err;
+ std::size_t actual;
/* initialize the output */
memset(result, 0, sizeof(*result));
/* need at least 4 bytes */
- if (length < 4)
+ uint8_t buf[4];
+ std::tie(err, actual) = read(src, &buf[0], 4);
+ if (err || (4 != actual))
return JEDERR_INVALID_DATA;
/* first unpack the number of fuses */
- result->numfuses = (cursrc[0] << 24) | (cursrc[1] << 16) | (cursrc[2] << 8) | cursrc[3];
- cursrc += 4;
+ result->numfuses = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (result->numfuses == 0 || result->numfuses > JED_MAX_FUSES)
return JEDERR_INVALID_DATA;
/* now make sure we have enough data in the source */
- if (length < 4 + (result->numfuses + 7) / 8)
- return JEDERR_INVALID_DATA;
-
/* copy in the data */
- memcpy(result->fusemap, cursrc, (result->numfuses + 7) / 8);
+ std::tie(err, actual) = read(src, result->fusemap, (result->numfuses + 7) / 8);
+ if (err || (((result->numfuses + 7) / 8) != actual))
+ return JEDERR_INVALID_DATA;
return JEDERR_NONE;
}
@@ -424,7 +489,7 @@ int jedbin_parse(const void *data, size_t length, jed_data *result)
size_t jedbin_output(const jed_data *data, void *result, size_t length)
{
- uint8_t *curdst = (uint8_t *)result;
+ auto *curdst = (uint8_t *)result;
/* ensure we have enough room */
if (length >= 4 + (data->numfuses + 7) / 8)
diff --git a/src/lib/util/jedparse.h b/src/lib/util/jedparse.h
index f12101ca154..c2ffe7942bc 100644
--- a/src/lib/util/jedparse.h
+++ b/src/lib/util/jedparse.h
@@ -13,7 +13,10 @@
#pragma once
-#include "osdcore.h"
+#include "utilfwd.h"
+
+#include <cstddef>
+#include <cstdint>
@@ -48,13 +51,13 @@ struct jed_data
***************************************************************************/
/* parse a file (read into memory) into a jed_data structure */
-int jed_parse(const void *data, size_t length, jed_data *result);
+int jed_parse(util::random_read &src, jed_data *result);
/* output a jed_data structure into a well-formatted JED file */
size_t jed_output(const jed_data *data, void *result, size_t length);
/* parse a binary JED file (read into memory) into a jed_data structure */
-int jedbin_parse(const void *data, size_t length, jed_data *result);
+int jedbin_parse(util::read_stream &src, jed_data *result);
/* output a jed_data structure into a binary JED file */
size_t jedbin_output(const jed_data *data, void *result, size_t length);
diff --git a/src/lib/util/language.cpp b/src/lib/util/language.cpp
new file mode 100644
index 00000000000..50b518acd1b
--- /dev/null
+++ b/src/lib/util/language.cpp
@@ -0,0 +1,220 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ language.cpp
+
+ Multi-language support.
+
+***************************************************************************/
+
+#include "language.h"
+
+#include "ioprocs.h"
+
+#include "osdcore.h"
+
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <new>
+#include <unordered_map>
+#include <utility>
+
+
+namespace util {
+
+namespace {
+
+constexpr std::uint32_t MO_MAGIC = 0x950412de;
+constexpr std::uint32_t MO_MAGIC_REVERSED = 0xde120495;
+
+std::unique_ptr<std::uint32_t []> f_translation_data;
+std::unordered_map<std::string_view, std::pair<char const *, std::uint32_t> > f_translation_map;
+
+} // anonymous namespace
+
+
+void unload_translation()
+{
+ f_translation_data.reset();
+ f_translation_map.clear();
+}
+
+
+void load_translation(random_read &file)
+{
+ std::unique_ptr<std::uint32_t []> translation_data;
+ std::unordered_map<std::string_view, std::pair<char const *, std::uint32_t> > translation_map;
+
+ std::uint64_t size = 0;
+ if (file.length(size) || (20 > size))
+ {
+ osd_printf_error("Error reading translation file: %u-byte file is too small to contain translation data\n", size);
+ return;
+ }
+
+ translation_data.reset(new (std::nothrow) std::uint32_t [(size + 3) / 4]);
+ if (!translation_data)
+ {
+ osd_printf_error("Failed to allocate %u bytes to load translation data file\n", size);
+ return;
+ }
+
+ auto const [err, actual] = read(file, translation_data.get(), size);
+ if (err || (actual != size))
+ {
+ osd_printf_error("Error reading translation file: requested %u bytes but got %u bytes\n", size, actual);
+ translation_data.reset();
+ return;
+ }
+
+ if ((translation_data[0] != MO_MAGIC) && (translation_data[0] != MO_MAGIC_REVERSED))
+ {
+ osd_printf_error("Error reading translation file: unrecognized magic number 0x%08X\n", translation_data[0]);
+ translation_data.reset();
+ return;
+ }
+
+ auto fetch_word =
+ [reversed = translation_data[0] == MO_MAGIC_REVERSED, words = translation_data.get()] (size_t offset)
+ {
+ return reversed ? swapendian_int32(words[offset]) : words[offset];
+ };
+
+ // FIXME: check major/minor version number
+
+ if ((fetch_word(3) % 4) || (fetch_word(4) % 4))
+ {
+ osd_printf_error("Error reading translation file: table offsets %u and %u are not word-aligned\n", fetch_word(3), fetch_word(4));
+ translation_data.reset();
+ return;
+ }
+
+ std::uint32_t const number_of_strings = fetch_word(2);
+ std::uint32_t const original_table_offset = fetch_word(3) >> 2;
+ std::uint32_t const translation_table_offset = fetch_word(4) >> 2;
+ if ((4 * (original_table_offset + (std::uint64_t(number_of_strings) * 2))) > size)
+ {
+ osd_printf_error("Error reading translation file: %u-entry original string table at offset %u extends past end of %u-byte file\n", number_of_strings, fetch_word(3), size);
+ translation_data.reset();
+ return;
+ }
+ if ((4 * (translation_table_offset + (std::uint64_t(number_of_strings) * 2))) > size)
+ {
+ osd_printf_error("Error reading translation file: %u-entry translated string table at offset %u extends past end of %u-byte file\n", number_of_strings, fetch_word(4), size);
+ translation_data.reset();
+ return;
+ }
+ osd_printf_verbose("Reading translation file: %u strings, original table at word offset %u, translated table at word offset %u\n", number_of_strings, original_table_offset, translation_table_offset);
+
+ char const *const data = reinterpret_cast<char const *>(translation_data.get());
+ for (std::uint32_t i = 1; number_of_strings > i; ++i)
+ {
+ std::uint32_t const original_length = fetch_word(original_table_offset + (2 * i));
+ std::uint32_t const original_offset = fetch_word(original_table_offset + (2 * i) + 1);
+ if ((original_length + original_offset) >= size)
+ {
+ osd_printf_error("Error reading translation file: %u-byte original string %u at offset %u extends past end of %u-byte file\n", original_length, i, original_offset, size);
+ continue;
+ }
+ if (data[original_length + original_offset])
+ {
+ osd_printf_error("Error reading translation file: %u-byte original string %u at offset %u is not correctly NUL-terminated\n", original_length, i, original_offset);
+ continue;
+ }
+
+ std::uint32_t const translation_length = fetch_word(translation_table_offset + (2 * i));
+ std::uint32_t const translation_offset = fetch_word(translation_table_offset + (2 * i) + 1);
+ if ((translation_length + translation_offset) >= size)
+ {
+ osd_printf_error("Error reading translation file: %u-byte translated string %u at offset %u extends past end of %u-byte file\n", translation_length, i, translation_offset, size);
+ continue;
+ }
+ if (data[translation_length + translation_offset])
+ {
+ osd_printf_error("Error reading translation file: %u-byte translated string %u at offset %u is not correctly NUL-terminated\n", translation_length, i, translation_offset);
+ continue;
+ }
+
+ std::string_view const original(&data[original_offset], original_length);
+ char const *const translation(&data[translation_offset]);
+ auto const ins = translation_map.emplace(original, std::make_pair(translation, translation_length));
+ if (!ins.second)
+ {
+ osd_printf_warning(
+ "Loading translation file: translation %u '%s'='%s' conflicts with previous translation '%s'='%s'\n",
+ i,
+ original,
+ translation,
+ ins.first->first,
+ ins.first->second.first);
+ }
+ }
+
+ osd_printf_verbose("Loaded %u translated string from file\n", translation_map.size());
+ f_translation_data = std::move(translation_data);
+ f_translation_map = std::move(translation_map);
+}
+
+
+char const *lang_translate(char const *message)
+{
+ auto const found = f_translation_map.find(message);
+ if (f_translation_map.end() != found)
+ return found->second.first;
+ return message;
+}
+
+
+std::string_view lang_translate(std::string_view message)
+{
+ auto const found = f_translation_map.find(message);
+ if (f_translation_map.end() != found)
+ return std::string_view(found->second.first, found->second.second);
+ return message;
+}
+
+
+char const *lang_translate(char const *context, char const *message)
+{
+ if (!f_translation_map.empty())
+ {
+ auto const ctxlen(std::strlen(context));
+ auto const msglen(std::strlen(message));
+ std::string key;
+ key.reserve(ctxlen + 1 + msglen);
+ key.append(context, ctxlen);
+ key.append(1, '\004');
+ key.append(message, msglen);
+ auto const found = f_translation_map.find(key);
+ if (f_translation_map.end() != found)
+ return found->second.first;
+ }
+ return message;
+}
+
+
+std::string_view lang_translate(char const *context, std::string_view message)
+{
+ return lang_translate(std::string_view(context), message);
+}
+
+
+std::string_view lang_translate(std::string_view context, std::string_view message)
+{
+ if (!f_translation_map.empty())
+ {
+ std::string key;
+ key.reserve(context.length() + 1 + message.length());
+ key.append(context);
+ key.append(1, '\004');
+ key.append(message);
+ auto const found = f_translation_map.find(key);
+ if (f_translation_map.end() != found)
+ return std::string_view(found->second.first, found->second.second);
+ }
+ return message;
+}
+
+} // namespace util
diff --git a/src/lib/util/language.h b/src/lib/util/language.h
new file mode 100644
index 00000000000..6e13def02e8
--- /dev/null
+++ b/src/lib/util/language.h
@@ -0,0 +1,43 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ language.h
+
+ Multi-language support.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_LANGUAGE_H
+#define MAME_LIB_UTIL_LANGUAGE_H
+
+#pragma once
+
+#include "utilfwd.h"
+
+#include <string_view>
+
+
+//**************************************************************************
+// LOCALIZATION SUPPORT
+//**************************************************************************
+
+#define _(...) lang_translate(__VA_ARGS__)
+
+#define N_(msg) (msg)
+#define N_p(ctx, msg) (msg)
+
+namespace util {
+
+void unload_translation();
+void load_translation(random_read &file);
+
+char const *lang_translate(char const *message);
+std::string_view lang_translate(std::string_view message);
+
+char const *lang_translate(char const *context, char const *message);
+std::string_view lang_translate(char const *context, std::string_view message);
+std::string_view lang_translate(std::string_view context, std::string_view message);
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_LANGUAGE_H
diff --git a/src/lib/util/lrucache.h b/src/lib/util/lrucache.h
new file mode 100644
index 00000000000..c2b4fae7c64
--- /dev/null
+++ b/src/lib/util/lrucache.h
@@ -0,0 +1,408 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ lrucache.h
+
+ Associative LRU cache with map-like behaviour.
+
+***************************************************************************/
+
+#ifndef MAME_UTIL_LRUCACHE_H
+#define MAME_UTIL_LRUCACHE_H
+
+#pragma once
+
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <list>
+#include <memory>
+#include <set>
+#include <stdexcept>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+namespace util {
+
+// LRU cache that behaves like std::map with differences:
+// * drops least-recently used items if necessary on insert to prevent size from exceeding max_size
+// * operator[], at, insert, emplace and find freshen existing entries
+// * iterates from least- to most-recently used rather than in order by key
+// * iterators to dropped items are invalidated
+// * not all map interfaces implemented
+// * copyable and swappable but not movable
+// * swap may invalidate past-the-end iterator, other iterators refer to new container
+template <typename Key, typename T, typename Compare = std::less<Key>, class Allocator = std::allocator<std::pair<Key const, T> > >
+class lru_cache_map
+{
+private:
+ class iterator_compare;
+ typedef std::list<std::pair<Key const, T>, Allocator> value_list;
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<typename value_list::iterator> iterator_allocator_type;
+ typedef std::set<typename value_list::iterator, iterator_compare, iterator_allocator_type> iterator_set;
+
+ class iterator_compare
+ {
+ public:
+ typedef std::true_type is_transparent;
+ iterator_compare(Compare const &comp) : m_comp(comp) { }
+ iterator_compare(iterator_compare const &that) = default;
+ iterator_compare(iterator_compare &&that) = default;
+ Compare key_comp() const { return m_comp; }
+ iterator_compare &operator=(iterator_compare const &that) = default;
+ iterator_compare &operator=(iterator_compare &&that) = default;
+ bool operator()(typename value_list::iterator const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs->first, rhs->first); }
+ template <typename K> bool operator()(typename value_list::iterator const &lhs, K const &rhs) const { return m_comp(lhs->first, rhs); }
+ template <typename K> bool operator()(K const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs, rhs->first); }
+ private:
+ Compare m_comp;
+ };
+
+public:
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key const, T> value_type;
+ typedef typename value_list::size_type size_type;
+ typedef typename value_list::difference_type difference_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef value_type &reference;
+ typedef value_type const &const_reference;
+ typedef typename std::allocator_traits<Allocator>::pointer pointer;
+ typedef typename std::allocator_traits<Allocator>::const_pointer const_pointer;
+ typedef typename value_list::iterator iterator;
+ typedef typename value_list::const_iterator const_iterator;
+ typedef typename value_list::reverse_iterator reverse_iterator;
+ typedef typename value_list::const_reverse_iterator const_reverse_iterator;
+
+ explicit lru_cache_map(size_type max_size)
+ : lru_cache_map(max_size, key_compare())
+ {
+ }
+ lru_cache_map(size_type max_size, key_compare const &comp, allocator_type const &alloc = allocator_type())
+ : m_max_size(max_size)
+ , m_size(0U)
+ , m_elements(alloc)
+ , m_mapping(iterator_compare(comp), iterator_allocator_type(alloc))
+ {
+ assert(0U < m_max_size);
+ }
+ lru_cache_map(lru_cache_map const &that)
+ : m_max_size(that.m_max_size)
+ , m_size(that.m_size)
+ , m_elements(that.m_elements)
+ , m_mapping(that.m_mapping.key_comp(), that.m_mapping.get_allocator())
+ {
+ for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
+ m_mapping.insert(it);
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ }
+
+ allocator_type get_allocator() const { return m_elements.get_allocator(); }
+
+ iterator begin() { return m_elements.begin(); }
+ const_iterator begin() const { return m_elements.cbegin(); }
+ const_iterator cbegin() const { return m_elements.cbegin(); }
+ iterator end() { return m_elements.end(); }
+ const_iterator end() const { return m_elements.cend(); }
+ const_iterator cend() const { return m_elements.cend(); }
+ reverse_iterator rbegin() { return m_elements.rbegin(); }
+ const_reverse_iterator rbegin() const { return m_elements.crbegin(); }
+ const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
+ reverse_iterator rend() { return m_elements.end(); }
+ const_reverse_iterator rend() const { return m_elements.crend(); }
+ const_reverse_iterator crend() const { return m_elements.crend(); }
+
+ bool empty() const { return !m_size; }
+ size_type size() const { return m_size; }
+ size_type max_size() const { return m_max_size; }
+
+ mapped_type &operator[](key_type const &key)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(key));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return inserted->second;
+ }
+ mapped_type &operator[](key_type &&key)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(key));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return inserted->second;
+ }
+ mapped_type &at(key_type const &key)
+ {
+ typename iterator_set::iterator existing(m_mapping.find(key));
+ if (m_mapping.end() != existing)
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ else
+ {
+ throw std::out_of_range("lru_cache_map::at");
+ }
+ }
+ mapped_type const &at(key_type const &key) const
+ {
+ typename iterator_set::iterator existing(m_mapping.find(key));
+ if (m_mapping.end() != existing)
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ else
+ {
+ throw std::out_of_range("lru_cache_map::at");
+ }
+ }
+
+ void clear()
+ {
+ m_size = 0U;
+ m_elements.clear();
+ m_mapping.clear();
+ }
+ std::pair<iterator, bool> insert(value_type const &value)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return std::pair<iterator, bool>(*existing, false);
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), value));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return std::pair<iterator, bool>(inserted, true);
+ }
+ std::pair<iterator, bool> insert(value_type &&value)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return std::pair<iterator, bool>(*existing, false);
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::move(value)));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return std::pair<iterator, bool>(inserted, true);
+ }
+ template <typename P>
+ std::enable_if_t<std::is_constructible<value_type, P>::value, std::pair<iterator, bool> > insert(P &&value)
+ {
+ return emplace(std::forward<P>(value));
+ }
+ template <typename InputIt>
+ void insert(InputIt first, InputIt last)
+ {
+ while (first != last)
+ {
+ insert(*first);
+ ++first;
+ }
+ }
+ void insert(std::initializer_list<value_type> ilist)
+ {
+ for (value_type const &value : ilist)
+ insert(value);
+ }
+ template <typename... Params>
+ std::pair<iterator, bool> emplace(Params &&... args)
+ {
+ // TODO: is there a more efficient way than depending on value_type being efficiently movable?
+ return insert(value_type(std::forward<Params>(args)...));
+ }
+ iterator erase(const_iterator pos)
+ {
+ m_mapping.erase(m_elements.erase(pos, pos));
+ iterator const result(m_elements.erase(pos));
+ --m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return result;
+ }
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ iterator pos(m_elements.erase(first, first));
+ while (pos != last)
+ {
+ m_mapping.erase(pos);
+ pos = m_elements.erase(pos);
+ --m_size;
+ }
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return pos;
+ }
+ size_type erase(key_type const &key)
+ {
+ typename iterator_set::iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return 0U;
+ }
+ else
+ {
+ m_elements.erase(*found);
+ m_mapping.erase(found);
+ --m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return 1U;
+ }
+ }
+ void swap(lru_cache_map &that)
+ {
+ using std::swap;
+ swap(m_max_size, that.m_max_size);
+ swap(m_size, that.m_size);
+ swap(m_elements, that.m_elements);
+ swap(m_mapping, that.m_mapping);
+ }
+
+ size_type count(key_type const &key) const
+ {
+ // TODO: perhaps this should freshen an element
+ return m_mapping.count(key);
+ }
+ template <typename K>
+ size_type count(K const &x) const
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ // TODO: perhaps this should freshen an element
+ return m_mapping.count(x);
+ }
+ iterator find(key_type const &key)
+ {
+ typename iterator_set::const_iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ iterator find(key_type const &key) const
+ {
+ typename iterator_set::const_iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ template <typename K>
+ iterator find(K const &x)
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ typename iterator_set::const_iterator const found(m_mapping.find(x));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ template <typename K>
+ iterator find(K const &x) const
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ typename iterator_set::const_iterator const found(m_mapping.find(x));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+
+ key_compare key_comp() const
+ {
+ return m_mapping.key_comp().key_comp();
+ }
+
+ lru_cache_map &operator=(lru_cache_map const &that)
+ {
+ m_max_size = that.m_max_size;
+ m_size = that.m_size;
+ m_elements = that.m_elements;
+ m_mapping.clear();
+ for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
+ m_mapping.insert(it);
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return *this;
+ }
+
+private:
+ void make_space(typename iterator_set::iterator &existing)
+ {
+ while (m_max_size <= m_size)
+ {
+ if ((m_mapping.end() != existing) && (m_elements.begin() == *existing))
+ existing = m_mapping.erase(existing);
+ else
+ m_mapping.erase(m_elements.begin());
+ m_elements.erase(m_elements.begin());
+ --m_size;
+ }
+ }
+
+ size_type m_max_size;
+ size_type m_size;
+ mutable value_list m_elements;
+ iterator_set m_mapping;
+};
+
+template <typename Key, typename T, typename Compare, class Allocator>
+void swap(lru_cache_map<Key, T, Compare, Allocator> &lhs, lru_cache_map<Key, T, Compare, Allocator> &rhs)
+{
+ lhs.swap(rhs);
+}
+
+} // namespace util
+
+#endif // MAME_UTIL_LRUCACHE_H
diff --git a/src/lib/util/md5.cpp b/src/lib/util/md5.cpp
index 712bd4a23f3..ca36792220e 100644
--- a/src/lib/util/md5.cpp
+++ b/src/lib/util/md5.cpp
@@ -22,10 +22,11 @@
* Still in the public domain.
*/
-#include <string.h> /* for memcpy() */
-
#include "md5.h"
+#include <cstring> /* for memcpy() */
+
+
#ifndef LSB_FIRST
void
byteSwap(UWORD32 *buf, unsigned words)
diff --git a/src/lib/util/mfpresolve.cpp b/src/lib/util/mfpresolve.cpp
new file mode 100644
index 00000000000..c7b9c58fde2
--- /dev/null
+++ b/src/lib/util/mfpresolve.cpp
@@ -0,0 +1,431 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ mfpresolve.h
+
+ Helpers for resolving member function pointers to entry points.
+
+***************************************************************************/
+
+#include "mfpresolve.h"
+
+#include "osdcomm.h"
+
+#include <cstdio>
+
+
+//**************************************************************************
+// MACROS
+//**************************************************************************
+
+#if defined(MAME_DELEGATE_LOG_ADJ)
+ #define LOG(...) printf(__VA_ARGS__)
+#else
+ #define LOG(...) do { if (false) printf(__VA_ARGS__); } while (false)
+#endif
+
+
+
+namespace util::detail {
+
+std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_itanium(
+ std::uintptr_t function,
+ std::ptrdiff_t delta,
+ void const *object) noexcept
+{
+ // 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 ", object, reinterpret_cast<void const *>(function), long(delta));
+ constexpr int deltashift = (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0;
+ object = reinterpret_cast<std::uint8_t const *>(object) + (delta >> deltashift);
+ LOG("Calculated this=%p ", 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_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? !(delta & 1) : !(function & 1))
+ {
+ // conventional function pointer
+ LOG("ptr=%p\n", reinterpret_cast<void const *>(function));
+ return std::make_pair(function, std::uintptr_t(object));
+ }
+ else
+ {
+ // byte index into the vtable to the function
+ auto const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + function - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1);
+ std::uintptr_t result;
+ if (MAME_ABI_CXX_VTABLE_FNDESC)
+ result = std::uintptr_t(vtable_ptr);
+ else
+ result = *reinterpret_cast<std::uintptr_t const *>(vtable_ptr);
+ LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(result));
+ return std::make_pair(result, std::uintptr_t(object));
+ }
+}
+
+
+std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_itanium(
+ std::uintptr_t function,
+ std::ptrdiff_t delta) noexcept
+{
+ constexpr uintptr_t funcmask = ~uintptr_t((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1);
+ constexpr int deltashift = (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0;
+ return std::make_tuple(
+ function & funcmask,
+ delta >> deltashift,
+ (MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? (delta & 1) : (function & 1));
+}
+
+
+std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_msvc(
+ void const *funcptr,
+ std::size_t size,
+ void const *object) noexcept
+{
+ mfp_msvc_unknown_equiv const *unknown;
+ assert(sizeof(*unknown) >= size);
+ unknown = reinterpret_cast<mfp_msvc_unknown_equiv const *>(funcptr);
+
+ LOG("Input this=%p ", object);
+ if (sizeof(mfp_msvc_single_equiv) < size)
+ LOG("thisdelta=%d ", unknown->delta);
+ if (sizeof(mfp_msvc_unknown_equiv) == size)
+ LOG("vptrdelta=%d vindex=%d ", unknown->voffset, unknown->vindex);
+ auto byteptr = reinterpret_cast<std::uint8_t const *>(object);
+
+ // test for pointer to member function cast across virtual inheritance relationship
+ if ((sizeof(mfp_msvc_unknown_equiv) == size) && unknown->vindex)
+ {
+ // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable
+ byteptr += unknown->voffset;
+ auto const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr);
+ byteptr += *reinterpret_cast<int const *>(vptr + unknown->vindex);
+ }
+
+ // add "this" pointer displacement if present in the pointer to member function
+ if (sizeof(mfp_msvc_single_equiv) < size)
+ byteptr += unknown->delta;
+ LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr));
+
+ // walk past recognisable thunks
+ return std::make_pair(bypass_member_function_thunks(unknown->entrypoint, byteptr), std::uintptr_t(byteptr));
+}
+
+
+std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_msvc(
+ void const *funcptr,
+ std::size_t size) noexcept
+{
+ mfp_msvc_unknown_equiv const *unknown;
+ assert(sizeof(*unknown) >= size);
+ unknown = reinterpret_cast<mfp_msvc_unknown_equiv const *>(funcptr);
+
+ // no way to represent pointer to member function cast across virtual inheritance relationship
+ if ((sizeof(mfp_msvc_unknown_equiv) == size) && unknown->vindex)
+ return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false);
+
+ auto const [function, is_virtual] = bypass_member_function_thunks(unknown->entrypoint);
+ return std::make_tuple(
+ function,
+ (sizeof(mfp_msvc_single_equiv) < size) ? unknown->delta : 0,
+ is_virtual);
+}
+
+
+std::uintptr_t bypass_member_function_thunks(
+ std::uintptr_t entrypoint,
+ void const *object) noexcept
+{
+#if defined(__x86_64__) || defined(_M_X64)
+ std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(entrypoint);
+ while (true)
+ {
+ // Assumes Windows calling convention, and doesn't consider that
+ // the "this" pointer could be in RDX if RCX is a pointer to
+ // space for an oversize scalar result. Since the result area
+ // is uninitialised on entry, you won't see something that looks
+ // like a vtable dispatch through RCX in this case - it won't
+ // behave badly, it just won't bypass virtual call thunks in the
+ // rare situations where the return type is an oversize scalar.
+ if (0xe9 == func[0])
+ {
+ // relative jump with 32-bit displacement (typically a resolved PLT entry)
+ LOG("Found relative jump at %p ", func);
+ func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ else if (object && (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+...]
+ LOG("Found virtual member function thunk at %p ", func);
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object);
+ if (0x20 == func[4]) // no displacement
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr);
+ else if (0x60 == func[4]) // 8-bit displacement
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int8_t const *>(func + 5));
+ else // 32-bit displacement
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int32_t const *>(func + 5));
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ 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
+ LOG("Found virtual member function thunk at %p ", func);
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object);
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr);
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9]))
+ {
+ // 8-bit displacement
+ LOG("Found virtual member function thunk at %p ", func);
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object);
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int8_t const *>(func + 6));
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12]))
+ {
+ // 32-bit displacement
+ LOG("Found virtual member function thunk at %p ", func);
+ std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(object);
+ func = *reinterpret_cast<std::uint8_t const *const *>(vptr + *reinterpret_cast<std::int32_t const *>(func + 6));
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ }
+ }
+
+ // clang uses unoptimised thunks if optimisation is disabled
+ // Without optimisation, clang produces thunks like:
+ // 50 push rax
+ // 48 89 0c 24 mov QWORD PTR [rsp],rcx
+ // 48 8b 0c 24 mov rcx,QWORD PTR [rsp]
+ // 48 8b 01 mov rax,QWORD PTR [rcx]
+ // 48 8b 80 xx xx xx xx mov rax,QWORD PTR [rax+...]
+ // 41 5a pop r10
+ // 48 ff e0 jmp rax
+ // Trying to decode these thunks likely isn't worth the effort.
+ // Chasing performance in unoptimised builds isn't very useful,
+ // and the format of these thunks may be fragile.
+
+ // not something we can easily bypass
+ break;
+ }
+ return std::uintptr_t(func);
+#elif defined(__aarch64__) || defined(_M_ARM64)
+ std::uint32_t const *func = reinterpret_cast<std::uint32_t const *>(entrypoint);
+ auto const fetch = [&func] (auto offset) { return little_endianize_int32(func[offset]); };
+ while (true)
+ {
+ if ((0x90000010 == (fetch(0) & 0x9f00001f)) && (0x91000210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2)))
+ {
+ // page-relative jump with +/-4GB reach - adrp xip0,... ; add xip0,xip0,#... ; br xip0
+ LOG("Found page-relative jump at %p ", func);
+ std::int64_t const page =
+ (std::uint64_t(fetch(0) & 0x60000000) >> 17) |
+ (std::uint64_t(fetch(0) & 0x00ffffe0) << 9) |
+ ((fetch(0) & 0x00800000) ? (~std::uint64_t(0) << 33) : 0);
+ std::uint32_t const offset = (fetch(1) & 0x003ffc00) >> 10;
+ func = reinterpret_cast<std::uint32_t const *>(((std::uintptr_t(func) + page) & (~std::uintptr_t(0) << 12)) + offset);
+ LOG("redirecting to %p\n", func);
+ }
+ else if (object && (0xf9400010 == fetch(0)) && (0xf9400210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2)))
+ {
+ // virtual function call thunk - ldr xip0,[x0] ; ldr xip0,[x0,#...] ; br xip0
+ LOG("Found virtual member function thunk at %p ", func);
+ auto const vptr = *reinterpret_cast<std::uint32_t const *const *const *>(object);
+ func = vptr[(fetch(1) & 0x003ffc00) >> 10];
+ LOG("redirecting to %p\n", func);
+ }
+ else
+ {
+ // not something we can easily bypass
+ break;
+ }
+
+ // clang uses horribly sub-optimal thunks for AArch64
+ // Without optimisation, clang produces thunks like:
+ // d10143ff sub sp,sp,#80
+ // f90027e7 str x7,[sp,#72]
+ // f90023e6 str x6,[sp,#64]
+ // f9001fe5 str x5,[sp,#56]
+ // f9001be4 str x4,[sp,#48]
+ // f90017e3 str x3,[sp,#40]
+ // f90013e2 str x2,[sp,#32]
+ // f9000fe1 str x1,[sp,#24]
+ // f90007e0 str x0,[sp,#8]
+ // f94007e0 ldr x0,[sp,#8]
+ // f9400009 ldr x9,[x0]
+ // f9400129 ldr x9,[x9,#...]
+ // 910143ff add sp,sp,#80
+ // d61f0120 br x9
+ // With optimisation, clang produces thunks like:
+ // d10103ff sub sp,sp,#64
+ // a9008be1 stp x1,x2,[sp,#8]
+ // a90193e3 stp x3,x4,[sp,#24]
+ // a9029be5 stp x5,x6,[sp,#40]
+ // f9001fe7 str x7,[sp,#56]
+ // f9400009 ldr x9,[x0]
+ // f9400129 ldr x9,[x9,#...]
+ // 910103ff add sp,sp,#64
+ // d61f0120 br x9
+ // It's more effort than it's worth to try decoding these
+ // thunks.
+
+ }
+ return std::uintptr_t(func);
+#else
+ return entrypoint;
+#endif
+}
+
+
+std::pair<std::uintptr_t, bool> bypass_member_function_thunks(
+ std::uintptr_t entrypoint) noexcept
+{
+#if defined(__x86_64__) || defined(_M_X64)
+ std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(entrypoint);
+ while (true)
+ {
+ // Assumes Windows calling convention, and doesn't consider that
+ // the "this" pointer could be in RDX if RCX is a pointer to
+ // space for an oversize scalar result. Since the result area
+ // is uninitialised on entry, you won't see something that looks
+ // like a vtable dispatch through RCX in this case - it won't
+ // behave badly, it just won't bypass virtual call thunks in the
+ // rare situations where the return type is an oversize scalar.
+ if (0xe9 == func[0])
+ {
+ // relative jump with 32-bit displacement (typically a resolved PLT entry)
+ LOG("Found relative jump at %p ", func);
+ func += std::ptrdiff_t(5) + *reinterpret_cast<std::int32_t const *>(func + 1);
+ LOG("redirecting to %p\n", func);
+ continue;
+ }
+ else 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+...]
+ LOG("Found virtual member function thunk at %p\n", func);
+ if (0x20 == func[4]) // no displacement
+ return std::make_pair(std::uintptr_t(0), true);
+ else if (0x60 == func[4]) // 8-bit displacement
+ return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int8_t const *>(func + 5)), true);
+ else // 32-bit displacement
+ return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int32_t const *>(func + 5)), true);
+ }
+ 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
+ LOG("Found virtual member function thunk at %p\n", func);
+ return std::make_pair(std::uintptr_t(0), true);
+ }
+ else if ((0x40 == func[5]) && (0x48 == func[7]) && (0xff == func[8]) && (0xe0 == func[9]))
+ {
+ // 8-bit displacement
+ LOG("Found virtual member function thunk at %p\n", func);
+ return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int8_t const *>(func + 6)), true);
+ }
+ else if ((0x80 == func[5]) && (0x48 == func[10]) && (0xff == func[11]) && (0xe0 == func[12]))
+ {
+ // 32-bit displacement
+ LOG("Found virtual member function thunk at %p\n", func);
+ return std::make_pair(std::uintptr_t(*reinterpret_cast<std::int32_t const *>(func + 6)), true);
+ }
+ }
+ }
+
+ // clang uses unoptimised thunks if optimisation is disabled
+ // Without optimisation, clang produces thunks like:
+ // 50 push rax
+ // 48 89 0c 24 mov QWORD PTR [rsp],rcx
+ // 48 8b 0c 24 mov rcx,QWORD PTR [rsp]
+ // 48 8b 01 mov rax,QWORD PTR [rcx]
+ // 48 8b 80 xx xx xx xx mov rax,QWORD PTR [rax+...]
+ // 41 5a pop r10
+ // 48 ff e0 jmp rax
+ // Trying to decode these thunks likely isn't worth the effort.
+ // Chasing performance in unoptimised builds isn't very useful,
+ // and the format of these thunks may be fragile.
+
+ // not something we can easily bypass
+ break;
+ }
+ return std::make_pair(std::uintptr_t(func), false);
+#elif defined(__aarch64__) || defined(_M_ARM64)
+ std::uint32_t const *func = reinterpret_cast<std::uint32_t const *>(entrypoint);
+ auto const fetch = [&func] (auto offset) { return little_endianize_int32(func[offset]); };
+ while (true)
+ {
+ if ((0x90000010 == (fetch(0) & 0x9f00001f)) && (0x91000210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2)))
+ {
+ // page-relative jump with +/-4GB reach - adrp xip0,... ; add xip0,xip0,#... ; br xip0
+ LOG("Found page-relative jump at %p ", func);
+ std::int64_t const page =
+ (std::uint64_t(fetch(0) & 0x60000000) >> 17) |
+ (std::uint64_t(fetch(0) & 0x00ffffe0) << 9) |
+ ((fetch(0) & 0x00800000) ? (~std::uint64_t(0) << 33) : 0);
+ std::uint32_t const offset = (fetch(1) & 0x003ffc00) >> 10;
+ func = reinterpret_cast<std::uint32_t const *>(((std::uintptr_t(func) + page) & (~std::uintptr_t(0) << 12)) + offset);
+ LOG("redirecting to %p\n", func);
+ }
+ else if ((0xf9400010 == fetch(0)) && (0xf9400210 == (fetch(1) & 0xffc003ff)) && (0xd61f0200 == fetch(2)))
+ {
+ // virtual function call thunk - ldr xip0,[x0] ; ldr xip0,[x0,#...] ; br xip0
+ LOG("Found virtual member function thunk at %p\n", func);
+ return std::make_pair(std::uintptr_t((fetch(1) & 0x003ffc00) >> (10 - 3)), true);
+ }
+ else
+ {
+ // not something we can easily bypass
+ break;
+ }
+
+ // clang uses horribly sub-optimal thunks for AArch64
+ // Without optimisation, clang produces thunks like:
+ // d10143ff sub sp,sp,#80
+ // f90027e7 str x7,[sp,#72]
+ // f90023e6 str x6,[sp,#64]
+ // f9001fe5 str x5,[sp,#56]
+ // f9001be4 str x4,[sp,#48]
+ // f90017e3 str x3,[sp,#40]
+ // f90013e2 str x2,[sp,#32]
+ // f9000fe1 str x1,[sp,#24]
+ // f90007e0 str x0,[sp,#8]
+ // f94007e0 ldr x0,[sp,#8]
+ // f9400009 ldr x9,[x0]
+ // f9400129 ldr x9,[x9,#...]
+ // 910143ff add sp,sp,#80
+ // d61f0120 br x9
+ // With optimisation, clang produces thunks like:
+ // d10103ff sub sp,sp,#64
+ // a9008be1 stp x1,x2,[sp,#8]
+ // a90193e3 stp x3,x4,[sp,#24]
+ // a9029be5 stp x5,x6,[sp,#40]
+ // f9001fe7 str x7,[sp,#56]
+ // f9400009 ldr x9,[x0]
+ // f9400129 ldr x9,[x9,#...]
+ // 910103ff add sp,sp,#64
+ // d61f0120 br x9
+ // It's more effort than it's worth to try decoding these
+ // thunks.
+
+ }
+ return std::make_pair(std::uintptr_t(func), false);
+#else
+ return std::make_pair(entrypoint, false);
+#endif
+}
+
+} // namespace util::detail
diff --git a/src/lib/util/mfpresolve.h b/src/lib/util/mfpresolve.h
new file mode 100644
index 00000000000..eb3fba2b85d
--- /dev/null
+++ b/src/lib/util/mfpresolve.h
@@ -0,0 +1,151 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ mfpresolve.h
+
+ Helpers for resolving member function pointers to entry points.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_MFPRESOLVE_H
+#define MAME_LIB_UTIL_MFPRESOLVE_H
+
+#pragma once
+
+#include "abi.h"
+
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <tuple>
+#include <utility>
+
+
+namespace util {
+
+namespace detail {
+
+struct mfp_itanium_equiv
+{
+ std::uintptr_t function;
+ std::ptrdiff_t delta;
+
+ constexpr std::ptrdiff_t this_delta() const noexcept { return delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0); }
+ constexpr bool is_virtual() const noexcept { return ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? delta : function) & 1; }
+};
+
+struct mfp_msvc_single_equiv { std::uintptr_t entrypoint; };
+struct mfp_msvc_multi_equiv { std::uintptr_t entrypoint; int delta; };
+struct mfp_msvc_virtual_equiv { std::uintptr_t entrypoint; int delta; int vindex; };
+struct mfp_msvc_unknown_equiv { std::uintptr_t entrypoint; int delta; int voffset; int vindex; };
+
+std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_itanium(std::uintptr_t function, std::ptrdiff_t delta, void const *object) noexcept;
+std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_itanium(std::uintptr_t function, std::ptrdiff_t delta) noexcept;
+std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function_msvc(void const *funcptr, std::size_t size, void const *object) noexcept;
+std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function_msvc(void const *funcptr, std::size_t size) noexcept;
+std::uintptr_t bypass_member_function_thunks(std::uintptr_t entrypoint, void const *object) noexcept;
+std::pair<std::uintptr_t, bool> bypass_member_function_thunks(std::uintptr_t entrypoint) noexcept;
+
+} // namespace detail
+
+
+template <typename T, typename U>
+inline T bypass_member_function_thunks(T entrypoint, U const *object) noexcept
+{
+ return reinterpret_cast<T>(
+ detail::bypass_member_function_thunks(
+ reinterpret_cast<std::uintptr_t>(entrypoint),
+ reinterpret_cast<void const *>(object)));
+}
+
+
+template <typename T, typename Ret, typename... Params>
+inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...), T &object) noexcept
+{
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
+ {
+ detail::mfp_itanium_equiv equiv;
+ assert(sizeof(function) == sizeof(equiv));
+ *reinterpret_cast<decltype(function) *>(&equiv) = function;
+ return detail::resolve_member_function_itanium(equiv.function, equiv.delta, &object);
+ }
+ else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ {
+ return detail::resolve_member_function_msvc(&function, sizeof(function), &object);
+ }
+ else
+ {
+ return std::make_pair(
+ std::uintptr_t(static_cast<void (*)()>(nullptr)),
+ std::uintptr_t(static_cast<void *>(nullptr)));
+ }
+}
+
+
+template <typename T, typename Ret, typename... Params>
+inline std::pair<std::uintptr_t, std::uintptr_t> resolve_member_function(Ret (T::*function)(Params...) const, T const &object) noexcept
+{
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
+ {
+ detail::mfp_itanium_equiv equiv;
+ assert(sizeof(function) == sizeof(equiv));
+ *reinterpret_cast<decltype(function) *>(&equiv) = function;
+ return detail::resolve_member_function_itanium(equiv.function, equiv.delta, &object);
+ }
+ else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ {
+ return detail::resolve_member_function_msvc(&function, sizeof(function), &object);
+ }
+ else
+ {
+ return std::make_pair(
+ std::uintptr_t(static_cast<void (*)()>(nullptr)),
+ std::uintptr_t(static_cast<void *>(nullptr)));
+ }
+}
+
+
+template <typename T, typename Ret, typename... Params>
+inline std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function(Ret (T::*function)(Params...)) noexcept
+{
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
+ {
+ detail::mfp_itanium_equiv equiv;
+ assert(sizeof(function) == sizeof(equiv));
+ *reinterpret_cast<decltype(function) *>(&equiv) = function;
+ return detail::resolve_member_function_itanium(equiv.function, equiv.delta);
+ }
+ else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ {
+ return detail::resolve_member_function_msvc(&function, sizeof(function));
+ }
+ else
+ {
+ return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false);
+ }
+}
+
+
+template <typename T, typename Ret, typename... Params>
+inline std::tuple<std::uintptr_t, std::ptrdiff_t, bool> resolve_member_function(Ret (T::*function)(Params...) const) noexcept
+{
+ if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_ITANIUM)
+ {
+ detail::mfp_itanium_equiv equiv;
+ assert(sizeof(function) == sizeof(equiv));
+ *reinterpret_cast<decltype(function) *>(&equiv) = function;
+ return detail::resolve_member_function_itanium(equiv.function, equiv.delta);
+ }
+ else if (MAME_ABI_CXX_TYPE == MAME_ABI_CXX_MSVC)
+ {
+ return detail::resolve_member_function_msvc(&function, sizeof(function));
+ }
+ else
+ {
+ return std::make_tuple(std::uintptr_t(static_cast<void (*)()>(nullptr)), std::ptrdiff_t(0), false);
+ }
+}
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_MFPRESOLVE_H
diff --git a/src/lib/util/msdib.cpp b/src/lib/util/msdib.cpp
new file mode 100644
index 00000000000..bf33991f116
--- /dev/null
+++ b/src/lib/util/msdib.cpp
@@ -0,0 +1,673 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ msdib.h
+
+ Microsoft Device-Independent Bitmap file loading.
+
+***************************************************************************/
+
+#include "msdib.h"
+
+#include "coretmpl.h"
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include "eminline.h"
+#include "osdcore.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+#include <tuple>
+
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_DIB (1U << 1)
+
+//#define VERBOSE (LOG_GENERAL | LOG_DIB)
+
+#define LOG_OUTPUT_FUNC osd_printf_verbose
+
+#ifndef VERBOSE
+#define VERBOSE 0
+#endif
+
+#define LOGMASKED(mask, ...) do { if (VERBOSE & (mask)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false)
+
+#define LOG(...) LOGMASKED(LOG_GENERAL, __VA_ARGS__)
+
+
+namespace util {
+
+namespace {
+
+// DIB compression schemes
+enum : std::uint32_t
+{
+ DIB_COMP_NONE = 0,
+ DIB_COMP_RLE8 = 1,
+ DIB_COMP_RLE4 = 2,
+ DIB_COMP_BITFIELDS = 3
+};
+
+
+// file header doesn't use natural alignment
+using bitmap_file_header = std::uint8_t [14];
+
+
+// old-style DIB header
+struct bitmap_core_header
+{
+ std::uint32_t size; // size of the header (12, 16 or 64)
+ std::int16_t width; // width of bitmap in pixels
+ std::int16_t height; // height of the image in pixels
+ std::uint16_t planes; // number of colour planes (must be 1)
+ std::uint16_t bpp; // bits per pixel
+};
+
+// new-style DIB header
+struct bitmap_info_header
+{
+ std::uint32_t size; // size of the header
+ std::int32_t width; // width of bitmap in pixels
+ std::int32_t height; // height of bitmap in pixels
+ std::uint16_t planes; // number of colour planes (must be 1)
+ std::uint16_t bpp; // bits per pixel
+ std::uint32_t comp; // compression method
+ std::uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed
+ std::int32_t hres; // horizontal resolution in pixels/metre
+ std::int32_t vres; // horizontal resolution in pixels/metre
+ std::uint32_t colors; // number of colours or 0 for 1 << bpp
+ std::uint32_t important; // number of important colours or 0 if all important
+ std::uint32_t red; // red field mask - must be contiguous
+ std::uint32_t green; // green field mask - must be contiguous
+ std::uint32_t blue; // blue field mask - must be contiguous
+ std::uint32_t alpha; // alpha field mask - must be contiguous
+};
+
+
+union bitmap_headers
+{
+ bitmap_core_header core;
+ bitmap_info_header info;
+};
+
+
+bool dib_parse_mask(std::uint32_t mask, unsigned &shift, unsigned &bits) noexcept
+{
+ shift = count_leading_zeros_32(mask);
+ mask <<= shift;
+ bits = count_leading_ones_32(mask);
+ mask <<= shift;
+ shift = 32 - shift - bits;
+ return !mask;
+}
+
+
+void dib_truncate_channel(unsigned &shift, unsigned &bits) noexcept
+{
+ if (8U < bits)
+ {
+ unsigned const excess(bits - 8);
+ shift += excess;
+ bits -= excess;
+ }
+}
+
+
+std::uint8_t dib_splat_sample(std::uint8_t val, unsigned bits) noexcept
+{
+ assert(8U >= bits);
+ for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1)
+ val |= val >> bits;
+ return val;
+}
+
+
+msdib_error dib_read_file_header(read_stream &fp, std::uint32_t &filelen) noexcept
+{
+ // the bitmap file header doesn't use natural alignment
+ bitmap_file_header file_header;
+ auto const [err, actual] = read(fp, file_header, sizeof(file_header));
+ if (err || (sizeof(file_header) != actual))
+ {
+ LOG("Error reading DIB file header\n");
+ return msdib_error::FILE_TRUNCATED;
+ }
+
+ // only support Windows bitmaps for now
+ if ((0x42 != file_header[0]) || (0x4d != file_header[1]))
+ return msdib_error::BAD_SIGNATURE;
+
+ // do a very basic check on the file length
+ std::uint32_t const file_length(get_u32le(&file_header[2]));
+ if ((sizeof(file_header) + sizeof(bitmap_core_header)) > file_length)
+ return msdib_error::FILE_CORRUPT;
+
+ // check that the offset to the pixel data looks half sane
+ std::uint32_t const pixel_offset(get_u32le(&file_header[10]));
+ if (((sizeof(file_header) + sizeof(bitmap_core_header)) > pixel_offset) || (file_length < pixel_offset))
+ return msdib_error::FILE_CORRUPT;
+
+ // looks OK enough
+ filelen = file_length;
+ return msdib_error::NONE;
+}
+
+
+msdib_error dib_read_bitmap_header(
+ random_read &fp,
+ bitmap_headers &header,
+ unsigned &palette_bytes,
+ bool &indexed,
+ std::size_t &palette_entries,
+ std::size_t &palette_size,
+ std::size_t &row_bytes,
+ std::uint32_t length) noexcept
+{
+ std::error_condition err;
+ 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");
+
+ // ensure the header fits in the space for the image data
+ assert(&header.core.size == &header.info.size);
+ if (sizeof(header.core) > length)
+ return msdib_error::FILE_TRUNCATED;
+ std::memset(&header, 0, sizeof(header));
+ std::tie(err, actual) = read(fp, &header.core.size, sizeof(header.core.size));
+ if (err || (sizeof(header.core.size) != actual))
+ {
+ LOG("Error reading DIB header size (length %u)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ header.core.size = little_endianize_int32(header.core.size);
+ if (length < header.core.size)
+ {
+ LOG("DIB image data (%u bytes) is too small for DIB header (%u bytes)\n", length, header.core.size);
+ return msdib_error::FILE_CORRUPT;
+ }
+
+ // identify and read the header - convert OS/2 headers to Windows 3 format
+ palette_bytes = 4U;
+ switch (header.core.size)
+ {
+ case 16U:
+ case 64U:
+ // extended OS/2 bitmap header with support for compression
+ LOG(
+ "DIB image data (%u bytes) uses unsupported OS/2 DIB header (size %u)\n",
+ length,
+ header.core.size);
+ return msdib_error::UNSUPPORTED_FORMAT;
+
+ case 12U:
+ // introduced in OS/2 and Windows 2.0
+ {
+ palette_bytes = 3U;
+ std::uint32_t const header_read(std::min<std::uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
+ std::tie(err, actual) = read(fp, &header.core.width, header_read);
+ if (err || (header_read != actual))
+ {
+ LOG("Error reading DIB core header from image data (%u bytes)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ 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);
+ header.core.bpp = little_endianize_int16(header.core.bpp);
+ LOGMASKED(
+ LOG_DIB,
+ "Read DIB core header from image data : %d*%d, %u planes, %u bpp\n",
+ header.core.width,
+ header.core.height,
+ header.core.planes,
+ header.core.bpp);
+
+ // this works because the core header only aliases the width/height of the info header
+ header.info.bpp = header.core.bpp;
+ header.info.planes = header.core.planes;
+ header.info.height = header.core.height;
+ header.info.width = header.core.width;
+ header.info.size = 40U;
+ }
+ break;
+
+ default:
+ // the next version will be longer
+ if (124U >= header.core.size)
+ {
+ LOG(
+ "DIB image data (%u bytes) uses unsupported DIB header format (size %u)\n",
+ length,
+ header.core.size);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+ [[fallthrough]];
+ case 40U:
+ case 52U:
+ case 56U:
+ case 108U:
+ case 124U:
+ // the Windows 3 bitmap header with optional extensions
+ {
+ palette_bytes = 4U;
+ std::uint32_t const header_read(std::min<std::uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
+ std::tie(err, actual) = read(fp, &header.info.width, header_read);
+ if (err || (header_read != actual))
+ {
+ LOG("Error reading DIB info header from image data (%u bytes)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ 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);
+ header.info.bpp = little_endianize_int16(header.info.bpp);
+ header.info.comp = little_endianize_int32(header.info.comp);
+ header.info.rawsize = little_endianize_int32(header.info.rawsize);
+ header.info.hres = little_endianize_int32(header.info.hres);
+ header.info.vres = little_endianize_int32(header.info.vres);
+ header.info.colors = little_endianize_int32(header.info.colors);
+ header.info.important = little_endianize_int32(header.info.important);
+ header.info.red = little_endianize_int32(header.info.red);
+ header.info.green = little_endianize_int32(header.info.green);
+ header.info.blue = little_endianize_int32(header.info.blue);
+ header.info.alpha = little_endianize_int32(header.info.alpha);
+ LOGMASKED(
+ LOG_DIB,
+ "Read DIB info header from image data: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n",
+ header.info.width,
+ header.info.height,
+ header.info.hres,
+ header.info.vres,
+ header.info.planes,
+ header.info.bpp,
+ header.info.important,
+ header.info.colors ? "" : "2^",
+ header.info.colors ? header.info.colors : header.info.bpp);
+ }
+ break;
+ }
+
+ // check for unsupported planes/bit depth
+ if ((1U != header.info.planes) || !header.info.bpp || (32U < header.info.bpp) || ((8U < header.info.bpp) ? (header.info.bpp % 8) : (8 % header.info.bpp)))
+ {
+ LOG("DIB image data uses unsupported planes/bits per pixel %u*%u\n", header.info.planes, header.info.bpp);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+
+ // check dimensions
+ if ((0 >= header.info.width) || (0 == header.info.height))
+ {
+ LOG("DIB image data has invalid dimensions %u*%u\n", header.info.width, header.info.height);
+ return msdib_error::FILE_CORRUPT;
+ }
+
+ // ensure compression scheme is supported
+ bool no_palette(false);
+ indexed = true;
+ switch (header.info.comp)
+ {
+ case DIB_COMP_NONE:
+ // uncompressed - direct colour with implied bitfields if more than eight bits/pixel
+ indexed = 8U >= header.info.bpp;
+ if (indexed)
+ {
+ if ((1U << header.info.bpp) < header.info.colors)
+ {
+ osd_printf_verbose(
+ "DIB image data has oversized palette with %u entries for %u bits per pixel\n",
+ header.info.colors,
+ header.info.bpp);
+ }
+ }
+ if (!indexed)
+ {
+ no_palette = true;
+ switch(header.info.bpp)
+ {
+ case 16U:
+ header.info.red = 0x00007c00;
+ header.info.green = 0x000003e0;
+ header.info.blue = 0x0000001f;
+ header.info.alpha = 0x00000000;
+ break;
+ case 24U:
+ case 32U:
+ header.info.red = 0x00ff0000;
+ header.info.green = 0x0000ff00;
+ header.info.blue = 0x000000ff;
+ header.info.alpha = 0x00000000;
+ break;
+ }
+ }
+ break;
+
+ case DIB_COMP_BITFIELDS:
+ // uncompressed direct colour with explicitly-specified bitfields
+ indexed = false;
+ if (offsetof(bitmap_info_header, alpha) > header.info.size)
+ {
+ osd_printf_verbose(
+ "DIB image data specifies bit masks but is too small (size %u)\n",
+ header.info.size);
+ return msdib_error::FILE_CORRUPT;
+ }
+ break;
+
+ default:
+ LOG("DIB image data uses unsupported compression scheme %u\n", header.info.comp);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+
+ // we can now calculate the size of the palette and row data
+ palette_entries =
+ indexed
+ ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp))
+ : (no_palette ? 0U : header.info.colors);
+ palette_size = palette_bytes * palette_entries;
+ row_bytes = ((31 + (header.info.width * header.info.bpp)) >> 5) << 2;
+
+ // header looks OK
+ return msdib_error::NONE;
+}
+
+} // anonymous namespace
+
+
+
+msdib_error msdib_verify_header(random_read &fp) noexcept
+{
+ msdib_error err;
+
+ // file header
+ std::uint32_t file_length;
+ err = dib_read_file_header(fp, file_length);
+ if (msdib_error::NONE != err)
+ return err;
+
+ // bitmap header
+ bitmap_headers header;
+ unsigned palette_bytes;
+ bool indexed;
+ std::size_t palette_entries, palette_size, row_bytes;
+ err = dib_read_bitmap_header(
+ fp,
+ header,
+ palette_bytes,
+ indexed,
+ palette_entries,
+ palette_size,
+ row_bytes,
+ file_length - sizeof(bitmap_file_header));
+ if (msdib_error::NONE != err)
+ return err;
+
+ // check length
+ std::size_t const required_size(
+ sizeof(bitmap_file_header) +
+ header.info.size +
+ palette_size +
+ (row_bytes * std::abs(header.info.height)));
+ if (required_size > file_length)
+ return msdib_error::FILE_TRUNCATED;
+
+ // good chance this file is supported
+ return msdib_error::NONE;
+}
+
+
+msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap) noexcept
+{
+ std::uint32_t file_length;
+ msdib_error const headerr(dib_read_file_header(fp, file_length));
+ if (msdib_error::NONE != headerr)
+ return headerr;
+
+ return msdib_read_bitmap_data(fp, bitmap, file_length - sizeof(bitmap_file_header), 0U);
+}
+
+
+msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight) noexcept
+{
+ // read the bitmap header
+ bitmap_headers header;
+ unsigned palette_bytes;
+ bool indexed;
+ std::size_t palette_entries, palette_size, row_bytes;
+ msdib_error const head_error(dib_read_bitmap_header(fp, header, palette_bytes, indexed, palette_entries, palette_size, row_bytes, length));
+ if (msdib_error::NONE != head_error)
+ return head_error;
+
+ // check dimensions
+ bool const top_down(0 > header.info.height);
+ if (top_down)
+ header.info.height = -header.info.height;
+ bool have_and_mask((2 * dirheight) == header.info.height);
+ if (!have_and_mask && (0 < dirheight) && (dirheight != header.info.height))
+ {
+ osd_printf_verbose(
+ "DIB image data height %d doesn't match directory height %u with or without AND mask\n",
+ header.info.height,
+ dirheight);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+ if (have_and_mask)
+ header.info.height >>= 1;
+
+ // we can now calculate the size of the image data
+ std::size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2);
+ std::size_t const required_size(
+ header.info.size +
+ palette_size +
+ ((row_bytes + (have_and_mask ? mask_row_bytes : 0U)) * header.info.height));
+ if (required_size > length)
+ {
+ LOG("DIB image data (%u bytes) smaller than calculated DIB data size (%u bytes)\n", length, required_size);
+ return msdib_error::FILE_TRUNCATED;
+ }
+
+ // load the palette for indexed colour formats or the shifts for direct colour formats
+ unsigned red_shift(0), green_shift(0), blue_shift(0), alpha_shift(0);
+ unsigned red_bits(0), green_bits(0), blue_bits(0), alpha_bits(0);
+ std::unique_ptr<rgb_t []> palette;
+ if (indexed)
+ {
+ // read palette and convert
+ 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;
+ auto const [err, actual] = read(fp, palette_data.get(), palette_size);
+ if (err || (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));
+ 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]);
+ }
+ else
+ {
+ // skip over the palette if necessary
+ if (palette_entries)
+ {
+ 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(
+ dib_parse_mask(header.info.red, red_shift, red_bits) &&
+ dib_parse_mask(header.info.green, green_shift, green_bits) &&
+ dib_parse_mask(header.info.blue, blue_shift, blue_bits) &&
+ dib_parse_mask(header.info.alpha, alpha_shift, alpha_bits));
+ if (!masks_contiguous)
+ {
+ osd_printf_verbose(
+ "DIB image data specifies non-contiguous channel masks 0x%x | 0x%x | 0x%x | 0x%x\n",
+ header.info.red,
+ header.info.green,
+ header.info.blue,
+ header.info.alpha);
+ }
+ if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp))
+ {
+ LOG(
+ "DIB image data specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n",
+ header.info.red,
+ header.info.green,
+ header.info.blue,
+ header.info.alpha,
+ header.info.bpp);
+ return msdib_error::FILE_CORRUPT;
+ }
+ LOGMASKED(
+ LOG_DIB,
+ "DIB image data using channels: R((x >> %2$u) & 0x%3$0*1$x) G((x >> %4$u) & 0x%5$0*1$x) B((x >> %6$u) & 0x%7$0*1$x) A((x >> %8$u) & 0x%9$0*1$x)\n",
+ (header.info.bpp + 3) >> 2,
+ red_shift,
+ (std::uint32_t(1) << red_bits) - 1,
+ green_shift,
+ (std::uint32_t(1) << green_bits) - 1,
+ blue_shift,
+ (std::uint32_t(1) << blue_bits) - 1,
+ alpha_shift,
+ (std::uint32_t(1) << alpha_bits) - 1);
+
+ // the MAME bitmap only supports 8 bits/sample maximum
+ dib_truncate_channel(red_shift, red_bits);
+ dib_truncate_channel(green_shift, green_bits);
+ dib_truncate_channel(blue_shift, blue_bits);
+ dib_truncate_channel(alpha_shift, alpha_bits);
+ }
+
+ // allocate a row buffer as well as the destination bitmap
+ 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);
+ if (!bitmap.valid())
+ return msdib_error::OUT_OF_MEMORY;
+
+ // process row data
+ 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)
+ {
+ auto const [err, actual] = read(fp, row_data.get(), row_bytes);
+ if (err || (row_bytes != actual))
+ {
+ LOG("Error reading DIB row %d data from image data\n", i);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ std::uint8_t *src(row_data.get());
+ std::uint32_t *dest(&bitmap.pix(y));
+ unsigned shift(0U);
+ for (std::int32_t x = 0; header.info.width > x; ++x, ++dest)
+ {
+ // extract or compose a pixel
+ std::uint32_t pix(0U);
+ if (8U >= header.info.bpp)
+ {
+ assert(8U > shift);
+ pix = *src >> (8U - header.info.bpp);
+ *src <<= header.info.bpp;
+ shift += header.info.bpp;
+ if (8U <= shift)
+ {
+ shift = 0U;
+ ++src;
+ }
+ }
+ else for (shift = 0; header.info.bpp > shift; shift += 8U, ++src)
+ {
+ pix |= std::uint32_t(*src) << shift;
+ }
+
+ // convert to RGB
+ if (indexed)
+ {
+ if (palette_entries > pix)
+ {
+ *dest = palette[pix];
+ }
+ else
+ {
+ *dest = rgb_t::transparent();
+ osd_printf_verbose(
+ "DIB image data has out-of-range color %u at (%d, %d) with %u palette entries\n",
+ pix,
+ x,
+ y,
+ palette_entries);
+ }
+ }
+ else
+ {
+ std::uint8_t r(dib_splat_sample(BIT(pix, red_shift, red_bits), red_bits));
+ std::uint8_t g(dib_splat_sample(BIT(pix, green_shift, green_bits), green_bits));
+ std::uint8_t b(dib_splat_sample(BIT(pix, blue_shift, blue_bits), blue_bits));
+ std::uint8_t a(dib_splat_sample(BIT(pix, alpha_shift, alpha_bits), alpha_bits));
+ *dest = rgb_t(alpha_bits ? a : 255, r, g, b);
+ }
+ }
+ }
+
+ // process the AND mask if present
+ if (have_and_mask)
+ {
+ for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
+ {
+ auto const [err, actual] = read(fp, row_data.get(), mask_row_bytes);
+ if (err || (mask_row_bytes != actual))
+ {
+ LOG("Error reading DIB mask row %d data from image data\n", i);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ std::uint8_t *src(row_data.get());
+ std::uint32_t *dest(&bitmap.pix(y));
+ unsigned shift(0U);
+ for (std::int32_t x = 0; header.info.width > x; ++x, ++dest)
+ {
+ assert(8U > shift);
+ rgb_t pix(*dest);
+ *dest = pix.set_a(BIT(*src, 7U - shift) ? 0U : pix.a());
+ if (8U <= ++shift)
+ {
+ shift = 0U;
+ ++src;
+ }
+ }
+ }
+ }
+
+ // we're done!
+ return msdib_error::NONE;
+}
+
+} // namespace util
diff --git a/src/lib/util/msdib.h b/src/lib/util/msdib.h
new file mode 100644
index 00000000000..fb6697c2eb6
--- /dev/null
+++ b/src/lib/util/msdib.h
@@ -0,0 +1,45 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ msdib.h
+
+ Microsoft Device-Independent Bitmap file loading.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_MSDIB_H
+#define MAME_LIB_UTIL_MSDIB_H
+
+#pragma once
+
+#include "bitmap.h"
+#include "utilfwd.h"
+
+#include <cstdint>
+
+
+namespace util {
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+// Error types
+enum class msdib_error
+{
+ NONE,
+ OUT_OF_MEMORY,
+ FILE_ERROR,
+ BAD_SIGNATURE,
+ FILE_TRUNCATED,
+ FILE_CORRUPT,
+ UNSUPPORTED_FORMAT
+};
+
+msdib_error msdib_verify_header(random_read &fp) noexcept;
+msdib_error msdib_read_bitmap(random_read &fp, bitmap_argb32 &bitmap) noexcept;
+msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U) noexcept;
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_MSDIB_H
diff --git a/src/lib/util/multibyte.h b/src/lib/util/multibyte.h
new file mode 100644
index 00000000000..6af34de83ed
--- /dev/null
+++ b/src/lib/util/multibyte.h
@@ -0,0 +1,301 @@
+// license:BSD-3-Clause
+// copyright-holders:Mietek Bak
+#ifndef MAME_UTIL_MULTIBYTE_H
+#define MAME_UTIL_MULTIBYTE_H
+
+#pragma once
+
+#include "coretmpl.h"
+#include "osdcomm.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+// unsigned big-endian
+
+constexpr osd::u16 get_u16be(osd::u8 const *buf) noexcept
+{
+ return (osd::u16(buf[0]) << 8)
+ | (osd::u16(buf[1]) << 0);
+}
+
+constexpr osd::u32 get_u24be(osd::u8 const *buf) noexcept
+{
+ return (osd::u32(buf[0]) << 16)
+ | (osd::u32(buf[1]) << 8)
+ | (osd::u32(buf[2]) << 0);
+}
+
+constexpr osd::u32 get_u32be(osd::u8 const *buf) noexcept
+{
+ return (osd::u32(buf[0]) << 24)
+ | (osd::u32(buf[1]) << 16)
+ | (osd::u32(buf[2]) << 8)
+ | (osd::u32(buf[3]) << 0);
+}
+
+constexpr osd::u64 get_u48be(osd::u8 const *buf) noexcept
+{
+ return (osd::u64(buf[0]) << 40)
+ | (osd::u64(buf[1]) << 32)
+ | (osd::u64(buf[2]) << 24)
+ | (osd::u64(buf[3]) << 16)
+ | (osd::u64(buf[4]) << 8)
+ | (osd::u64(buf[5]) << 0);
+}
+
+constexpr osd::u64 get_u64be(osd::u8 const *buf) noexcept
+{
+ return (osd::u64(buf[0]) << 56)
+ | (osd::u64(buf[1]) << 48)
+ | (osd::u64(buf[2]) << 40)
+ | (osd::u64(buf[3]) << 32)
+ | (osd::u64(buf[4]) << 24)
+ | (osd::u64(buf[5]) << 16)
+ | (osd::u64(buf[6]) << 8)
+ | (osd::u64(buf[7]) << 0);
+}
+
+inline void put_u16be(osd::u8 *buf, osd::u16 data) noexcept
+{
+ buf[0] = osd::u8(data >> 8);
+ buf[1] = osd::u8(data >> 0);
+}
+
+inline void put_u24be(osd::u8 *buf, osd::u32 data) noexcept
+{
+ buf[0] = osd::u8(data >> 16);
+ buf[1] = osd::u8(data >> 8);
+ buf[2] = osd::u8(data >> 0);
+}
+
+inline void put_u32be(osd::u8 *buf, osd::u32 data) noexcept
+{
+ buf[0] = osd::u8(data >> 24);
+ buf[1] = osd::u8(data >> 16);
+ buf[2] = osd::u8(data >> 8);
+ buf[3] = osd::u8(data >> 0);
+}
+
+inline void put_u48be(osd::u8 *buf, osd::u64 data) noexcept
+{
+ buf[0] = osd::u8(data >> 40);
+ buf[1] = osd::u8(data >> 32);
+ buf[2] = osd::u8(data >> 24);
+ buf[3] = osd::u8(data >> 16);
+ buf[4] = osd::u8(data >> 8);
+ buf[5] = osd::u8(data >> 0);
+}
+
+inline void put_u64be(osd::u8 *buf, osd::u64 data) noexcept
+{
+ buf[0] = osd::u8(data >> 56);
+ buf[1] = osd::u8(data >> 48);
+ buf[2] = osd::u8(data >> 40);
+ buf[3] = osd::u8(data >> 32);
+ buf[4] = osd::u8(data >> 24);
+ buf[5] = osd::u8(data >> 16);
+ buf[6] = osd::u8(data >> 8);
+ buf[7] = osd::u8(data >> 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// signed big-endian
+
+constexpr osd::s16 get_s16be(osd::u8 const *buf) noexcept
+{
+ return get_u16be(buf);
+}
+
+constexpr osd::s32 get_s24be(osd::u8 const *buf) noexcept
+{
+ return util::sext(get_u24be(buf), 24);
+}
+
+constexpr osd::s32 get_s32be(osd::u8 const *buf) noexcept
+{
+ return get_u32be(buf);
+}
+
+constexpr osd::s64 get_s48be(osd::u8 const *buf) noexcept
+{
+ return util::sext(get_u48be(buf), 48);
+}
+
+constexpr osd::s64 get_s64be(osd::u8 const *buf) noexcept
+{
+ return get_u64be(buf);
+}
+
+inline void put_s16be(osd::u8 *buf, osd::s16 data) noexcept
+{
+ put_u16be(buf, data);
+}
+
+inline void put_s24be(osd::u8 *buf, osd::s32 data) noexcept
+{
+ put_u24be(buf, data);
+}
+
+inline void put_s32be(osd::u8 *buf, osd::s32 data) noexcept
+{
+ put_u32be(buf, data);
+}
+
+inline void put_s48be(osd::u8 *buf, osd::s64 data) noexcept
+{
+ put_u48be(buf, data);
+}
+
+inline void put_s64be(osd::u8 *buf, osd::s64 data) noexcept
+{
+ put_u64be(buf, data);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// unsigned little-endian
+
+constexpr osd::u16 get_u16le(osd::u8 const *buf) noexcept
+{
+ return (osd::u16(buf[0]) << 0)
+ | (osd::u16(buf[1]) << 8);
+}
+
+constexpr osd::u32 get_u24le(osd::u8 const *buf) noexcept
+{
+ return (osd::u32(buf[0]) << 0)
+ | (osd::u32(buf[1]) << 8)
+ | (osd::u32(buf[2]) << 16);
+}
+
+constexpr osd::u32 get_u32le(osd::u8 const *buf) noexcept
+{
+ return (osd::u32(buf[0]) << 0)
+ | (osd::u32(buf[1]) << 8)
+ | (osd::u32(buf[2]) << 16)
+ | (osd::u32(buf[3]) << 24);
+}
+
+constexpr osd::u64 get_u48le(osd::u8 const *buf) noexcept
+{
+ return (osd::u64(buf[0]) << 0)
+ | (osd::u64(buf[1]) << 8)
+ | (osd::u64(buf[2]) << 16)
+ | (osd::u64(buf[3]) << 24)
+ | (osd::u64(buf[4]) << 32)
+ | (osd::u64(buf[5]) << 40);
+}
+
+constexpr osd::u64 get_u64le(osd::u8 const *buf) noexcept
+{
+ return (osd::u64(buf[0]) << 0)
+ | (osd::u64(buf[1]) << 8)
+ | (osd::u64(buf[2]) << 16)
+ | (osd::u64(buf[3]) << 24)
+ | (osd::u64(buf[4]) << 32)
+ | (osd::u64(buf[5]) << 40)
+ | (osd::u64(buf[6]) << 48)
+ | (osd::u64(buf[7]) << 56);
+}
+
+inline void put_u16le(osd::u8 *buf, osd::u16 data) noexcept
+{
+ buf[0] = osd::u8(data >> 0);
+ buf[1] = osd::u8(data >> 8);
+}
+
+inline void put_u24le(osd::u8 *buf, osd::u32 data) noexcept
+{
+ buf[0] = osd::u8(data >> 0);
+ buf[1] = osd::u8(data >> 8);
+ buf[2] = osd::u8(data >> 16);
+}
+
+inline void put_u32le(osd::u8 *buf, osd::u32 data) noexcept
+{
+ buf[0] = osd::u8(data >> 0);
+ buf[1] = osd::u8(data >> 8);
+ buf[2] = osd::u8(data >> 16);
+ buf[3] = osd::u8(data >> 24);
+}
+
+inline void put_u48le(osd::u8 *buf, osd::u64 data) noexcept
+{
+ buf[0] = osd::u8(data >> 0);
+ buf[1] = osd::u8(data >> 8);
+ buf[2] = osd::u8(data >> 16);
+ buf[3] = osd::u8(data >> 24);
+ buf[4] = osd::u8(data >> 32);
+ buf[5] = osd::u8(data >> 40);
+}
+
+inline void put_u64le(osd::u8 *buf, osd::u64 data) noexcept
+{
+ buf[0] = osd::u8(data >> 0);
+ buf[1] = osd::u8(data >> 8);
+ buf[2] = osd::u8(data >> 16);
+ buf[3] = osd::u8(data >> 24);
+ buf[4] = osd::u8(data >> 32);
+ buf[5] = osd::u8(data >> 40);
+ buf[6] = osd::u8(data >> 48);
+ buf[7] = osd::u8(data >> 56);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// signed little-endian
+
+constexpr osd::s16 get_s16le(osd::u8 const *buf) noexcept
+{
+ return get_u16le(buf);
+}
+
+constexpr osd::s32 get_s24le(osd::u8 const *buf) noexcept
+{
+ return util::sext(get_u24le(buf), 24);
+}
+
+constexpr osd::s32 get_s32le(osd::u8 const *buf) noexcept
+{
+ return get_u32le(buf);
+}
+
+constexpr osd::s64 get_s48le(osd::u8 const *buf) noexcept
+{
+ return util::sext(get_u48le(buf), 48);
+}
+
+constexpr osd::s64 get_s64le(osd::u8 const *buf) noexcept
+{
+ return get_u64le(buf);
+}
+
+inline void put_s16le(osd::u8 *buf, osd::s16 data) noexcept
+{
+ put_u16le(buf, data);
+}
+
+inline void put_s24le(osd::u8 *buf, osd::s32 data) noexcept
+{
+ put_u24le(buf, data);
+}
+
+inline void put_s32le(osd::u8 *buf, osd::s32 data) noexcept
+{
+ put_u32le(buf, data);
+}
+
+inline void put_s48le(osd::u8 *buf, osd::s64 data) noexcept
+{
+ put_u48le(buf, data);
+}
+
+inline void put_s64le(osd::u8 *buf, osd::s64 data) noexcept
+{
+ put_u64le(buf, data);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // MAME_UTIL_MULTIBYTE_H
diff --git a/src/lib/util/nanosvg.cpp b/src/lib/util/nanosvg.cpp
index 09973865675..0cb05308d19 100644
--- a/src/lib/util/nanosvg.cpp
+++ b/src/lib/util/nanosvg.cpp
@@ -1,6 +1,10 @@
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+#define NANOSVG_ALL_COLOR_KEYWORDS
#define NANOSVG_IMPLEMENTATION
#define NANOSVGRAST_IMPLEMENTATION
-#define NANOSVG_ALL_COLOR_KEYWORDS
#include <nanosvg/src/nanosvg.h>
#include <nanosvg/src/nanosvgrast.h>
diff --git a/src/lib/util/nanosvg.h b/src/lib/util/nanosvg.h
new file mode 100644
index 00000000000..49940f0b9be
--- /dev/null
+++ b/src/lib/util/nanosvg.h
@@ -0,0 +1,33 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ nanosvg.h
+
+ NanoSVG helpers.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_NANOSVG_H
+#define MAME_LIB_UTIL_NANOSVG_H
+
+#include <nanosvg/src/nanosvg.h>
+#include <nanosvg/src/nanosvgrast.h>
+
+#include <memory>
+
+
+namespace util {
+
+struct nsvg_deleter
+{
+ void operator()(NSVGimage *ptr) const { nsvgDelete(ptr); }
+ void operator()(NSVGrasterizer *ptr) const { nsvgDeleteRasterizer(ptr); }
+};
+
+
+using nsvg_image_ptr = std::unique_ptr<NSVGimage, nsvg_deleter>;
+using nsvg_rasterizer_ptr = std::unique_ptr<NSVGrasterizer, nsvg_deleter>;
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_NANOSVG_H
diff --git a/src/lib/util/notifier.h b/src/lib/util/notifier.h
new file mode 100644
index 00000000000..237417e49ab
--- /dev/null
+++ b/src/lib/util/notifier.h
@@ -0,0 +1,240 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/// \file
+/// \brief Simple broadcast notifier
+///
+/// Classes for managing subscriptions and calling multiple listener
+/// functions for notifications.
+#ifndef MAME_LIB_UTIL_NOTIFIER_H
+#define MAME_LIB_UTIL_NOTIFIER_H
+
+#pragma once
+
+#include "delegate.h"
+
+#include <cstddef>
+#include <memory>
+#include <utility>
+#include <vector>
+
+
+namespace util {
+
+/// \brief A subscription to a notification source
+///
+/// Class for representing a subscription to a notifier. Automatically
+/// unsubscribes on destruction, if explicitly reset, if assigned to, or
+/// when taking ownership of a different subscription by move
+/// assignment.
+/// \sa notifier
+class notifier_subscription
+{
+public:
+ /// \brief Create an empty subscription
+ ///
+ /// Initialises an instance not referring to a subscription.
+ notifier_subscription() noexcept : m_token(), m_live(nullptr), m_index(0U) { }
+
+ /// \brief Transfer ownership of a subscription
+ ///
+ /// Transfers ownership of a subscription to a new instance.
+ /// \param [in,out] that The subscription to transfer ownership
+ /// from. Will no longer refer to a subscription after ownership
+ /// is transferred away.
+ notifier_subscription(notifier_subscription &&that) noexcept :
+ m_token(std::move(that.m_token)),
+ m_live(that.m_live),
+ m_index(that.m_index)
+ {
+ that.m_token.reset();
+ }
+
+ /// \brief Unsubscribe and destroy a subscription
+ ///
+ /// Unsubscribes if the subscription is active and cleans up the
+ /// subscription instance.
+ ~notifier_subscription() noexcept
+ {
+ auto token(m_token.lock());
+ if (token)
+ (*m_live)[m_index] = false;
+ }
+
+ /// \brief Swap two subscriptions
+ ///
+ /// Exchanges ownership of subscriptions between two instances.
+ /// \param [in,out] that The subscription to exchange ownership
+ /// with.
+ void swap(notifier_subscription &that) noexcept
+ {
+ using std::swap;
+ swap(m_token, that.m_token);
+ swap(m_live, that.m_live);
+ swap(m_index, that.m_index);
+ }
+
+ /// \brief Unsubscribe from notifications
+ ///
+ /// If the instance refers to an active subscription, cancel it so
+ /// no future notifications will be received.
+ void reset() noexcept
+ {
+ auto token(m_token.lock());
+ if (token)
+ (*m_live)[m_index] = false;
+ m_token.reset();
+ }
+
+ /// \brief Test whether a subscription is active
+ ///
+ /// Tests whether a subscription is active. A subscription will be
+ /// inactive if it is default constructed, reset, transferred away,
+ /// or if the underlying notifier is destructed.
+ /// \return True if the subscription is active, false otherwise.
+ explicit operator bool() const noexcept { return !m_token.expired(); }
+
+ /// \brief Transfer ownership of a subscription
+ ///
+ /// Transfers ownership of a subscription to an existing instance.
+ /// If the subscription is active, it will be cancelled before it
+ /// takes ownership of the other subscription.
+ /// \param [in,out] that The subscription to transfer ownership
+ /// from. Will no longer refer to a subscription after ownership
+ /// is transferred away.
+ /// \return A reference to the instance that ownership was
+ /// transferred to.
+ notifier_subscription &operator=(notifier_subscription &&that) noexcept
+ {
+ {
+ auto token(m_token.lock());
+ if (token)
+ (*m_live)[m_index] = false;
+ }
+ m_token = std::move(that.m_token);
+ m_live = that.m_live;
+ m_index = that.m_index;
+ that.m_token.reset();
+ return *this;
+ }
+
+protected:
+ notifier_subscription(
+ std::shared_ptr<int> const &token,
+ std::vector<bool> &live,
+ std::vector<bool>::size_type index) :
+ m_token(token),
+ m_live(&live),
+ m_index(index)
+ {
+ }
+
+private:
+ notifier_subscription(notifier_subscription const &) = delete;
+ notifier_subscription &operator=(notifier_subscription const &) = delete;
+
+ std::weak_ptr<int> m_token;
+ std::vector<bool> *m_live;
+ std::vector<bool>::size_type m_index;
+};
+
+
+/// \brief Broadcast notifier
+///
+/// Calls multiple listener functions. Allows listeners to subscribe
+/// and unsubscribe. Subscriptions are managed using
+/// \c notifier_subscription instances.
+/// \tparam Params Argument types for the listener functions.
+/// \sa notifier_subscription
+template <typename... Params>
+class notifier
+{
+public:
+ /// \brief Listener delegate type
+ ///
+ /// The delegate type used to represent listener functions.
+ using delegate_type = delegate<void (Params...)>;
+
+ /// \brief Create a new notifier
+ ///
+ /// Creates a new notifier with no initial subscribers.
+ notifier() : m_token(std::make_shared<int>(0)) { }
+
+ /// \brief Destroy a notifier
+ ///
+ /// Destroys a notifier, causing any subscriptions to become
+ /// inactive.
+ ~notifier() noexcept { m_token.reset(); }
+
+ /// \brief Add a listener
+ ///
+ /// Adds a listener function subscription, returning an object for
+ /// managing the subscription.
+ /// \param [in] listener The function to be called on notifications.
+ /// \return A subscription object. Destroy the object or call its
+ /// \c reset member function to unsubscribe.
+ notifier_subscription subscribe(delegate_type &&listener)
+ {
+ struct subscription_impl : notifier_subscription
+ {
+ subscription_impl(notifier &host, std::size_t index) noexcept :
+ notifier_subscription(host.m_token, host.m_live, index)
+ {
+ }
+ };
+ for (std::size_t i = 0U; m_listeners.size() > i; ++i)
+ {
+ if (!m_live[i])
+ {
+ m_live[i] = true;
+ m_listeners[i] = std::move(listener);
+ return subscription_impl(*this, i);
+ }
+ }
+ m_live.emplace_back(true);
+ try
+ {
+ m_listeners.emplace_back(std::move(listener));
+ }
+ catch (...)
+ {
+ m_live.pop_back();
+ throw;
+ }
+ return subscription_impl(*this, m_listeners.size() - 1);
+ }
+
+ /// \brief Call listeners
+ ///
+ /// Calls all active listener functions.
+ /// \param [in] args Arguments to pass to the listener functions.
+ void operator()(Params... args) const
+ {
+ for (std::size_t i = 0U; m_listeners.size() > i; ++i)
+ {
+ if (m_live[i])
+ m_listeners[i](args...);
+ }
+ }
+
+private:
+ notifier(notifier const &) = delete;
+ notifier &operator=(notifier const &) = delete;
+
+ std::shared_ptr<int> m_token;
+ std::vector<bool> m_live;
+ std::vector<delegate_type> m_listeners;
+};
+
+
+/// \brief Swap two notifier subscriptions
+///
+/// Exchanges ownership of two notifier subscriptions. Allows the
+/// swappable idiom to be used with notifier subscriptions.
+/// \param [in,out] x Takes ownership of the subscription from \p y.
+/// \param [in,out] y Takes ownership of the subscription from \p x.
+/// \sa notifier_subscription
+inline void swap(notifier_subscription &x, notifier_subscription &y) noexcept { x.swap(y); }
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_NOTIFIER_H
diff --git a/src/lib/util/opresolv.cpp b/src/lib/util/opresolv.cpp
index 05b8a5e8898..2b269b59e04 100644
--- a/src/lib/util/opresolv.cpp
+++ b/src/lib/util/opresolv.cpp
@@ -8,15 +8,16 @@
****************************************************************************/
-#include <ctype.h>
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-#include "pool.h"
-#include "corestr.h"
#include "opresolv.h"
+#include "strformat.h"
+
+#include <algorithm>
+#include <cassert>
+#include <cctype>
+#include <cstdlib>
+#include <cstring>
+
namespace util {
diff --git a/src/lib/util/opresolv.h b/src/lib/util/opresolv.h
index 52030dc4336..c53898a17f4 100644
--- a/src/lib/util/opresolv.h
+++ b/src/lib/util/opresolv.h
@@ -43,7 +43,7 @@
#pragma once
-#include <stdlib.h>
+#include <cstdlib>
#include <vector>
#include <string>
@@ -109,7 +109,7 @@ public:
}
// accessors
- const option_type type() const { return m_type; }
+ option_type type() const { return m_type; }
int parameter() const { return m_parameter; }
const char *identifier() const { return m_identifier; }
const char *display_name() const { return m_display_name; }
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp
index cffb7493fe3..a111c211f0e 100644
--- a/src/lib/util/options.cpp
+++ b/src/lib/util/options.cpp
@@ -8,14 +8,21 @@
***************************************************************************/
-#include <stdarg.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <assert.h>
#include "options.h"
+
+#include "corefile.h"
#include "corestr.h"
+#include "osdcore.h"
+
+#include <locale>
#include <string>
+#include <cassert>
+#include <cctype>
+#include <cstdarg>
+#include <cstdlib>
+#include <sstream>
+
const int core_options::MAX_UNADORNED_OPTIONS;
@@ -50,17 +57,19 @@ const char *const core_options::s_option_unadorned[MAX_UNADORNED_OPTIONS] =
namespace
{
- void trim_spaces_and_quotes(std::string &data)
+ std::string_view trim_spaces_and_quotes(std::string_view data)
{
// trim any whitespace
- strtrimspace(data);
+ data = strtrimspace(data);
// trim quotes
- if (data.find_first_of('"') == 0 && data.find_last_of('"') == data.length() - 1)
+ if (data.length() >= 2 && data.front() == '"' && data.back() == '"')
{
- data.erase(0, 1);
- data.erase(data.length() - 1, 1);
+ data.remove_prefix(1);
+ data.remove_suffix(1);
}
+
+ return data;
}
};
@@ -106,7 +115,7 @@ options_error_exception::options_error_exception(std::string &&message)
// entry - constructor
//-------------------------------------------------
-core_options::entry::entry(std::vector<std::string> &&names, core_options::option_type type, const char *description)
+core_options::entry::entry(std::vector<std::string> &&names, option_type type, const char *description)
: m_names(std::move(names))
, m_priority(OPTION_PRIORITY_DEFAULT)
, m_type(type)
@@ -115,7 +124,7 @@ core_options::entry::entry(std::vector<std::string> &&names, core_options::optio
assert(m_names.empty() == (m_type == option_type::HEADER));
}
-core_options::entry::entry(std::string &&name, core_options::option_type type, const char *description)
+core_options::entry::entry(std::string &&name, option_type type, const char *description)
: entry(std::vector<std::string>({ std::move(name) }), type, description)
{
}
@@ -134,7 +143,7 @@ core_options::entry::~entry()
// entry::value
//-------------------------------------------------
-const char *core_options::entry::value() const
+const char *core_options::entry::value() const noexcept
{
// returning 'nullptr' from here signifies a value entry that is essentially "write only"
// and cannot be meaningfully persisted (e.g. - a command or the software name)
@@ -143,18 +152,54 @@ const char *core_options::entry::value() const
//-------------------------------------------------
+// entry::value_unsubstituted
+//-------------------------------------------------
+
+const char *core_options::entry::value_unsubstituted() const noexcept
+{
+ return value();
+}
+
+
+//-------------------------------------------------
+// entry::copy_from
+//-------------------------------------------------
+
+void core_options::entry::copy_from(const entry &that, bool always_override)
+{
+ // it is invalid to set the value on a header
+ assert(type() != option_type::HEADER);
+
+ // only set the value if we have priority
+ if (always_override || that.priority() >= priority())
+ {
+ if (internal_copy_value(that))
+ {
+ m_priority = that.priority();
+
+ // invoke the value changed handler, if appropriate
+ if (m_value_changed_handler)
+ m_value_changed_handler(value());
+ }
+ }
+}
+
+
+//-------------------------------------------------
// entry::set_value
//-------------------------------------------------
-void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override)
+void core_options::entry::set_value(std::string &&newvalue, int priority_value, bool always_override, bool perform_substitutions)
{
// it is invalid to set the value on a header
assert(type() != option_type::HEADER);
+ validate(newvalue);
+
// only set the value if we have priority
if (always_override || priority_value >= priority())
{
- internal_set_value(std::move(newvalue));
+ internal_set_value(std::move(newvalue), perform_substitutions);
m_priority = priority_value;
// invoke the value changed handler, if appropriate
@@ -176,57 +221,125 @@ void core_options::entry::set_default_value(std::string &&newvalue)
//-------------------------------------------------
+// entry::internal_copy_value
+//-------------------------------------------------
+
+bool core_options::entry::internal_copy_value(const entry &that)
+{
+ char const *const newvalue = that.value();
+ if (newvalue)
+ {
+ std::string stringval = newvalue;
+ validate(stringval);
+
+ internal_set_value(std::move(stringval), false);
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+//-------------------------------------------------
// entry::validate
//-------------------------------------------------
void core_options::entry::validate(const std::string &data)
{
- float fval;
- int ival;
+ std::istringstream str(data);
+ str.imbue(std::locale::classic());
switch (type())
{
case option_type::BOOLEAN:
- // booleans must be 0 or 1
- if (sscanf(data.c_str(), "%d", &ival) != 1 || ival < 0 || ival > 1)
- throw options_warning_exception("Illegal boolean value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+ {
+ // booleans must be 0 or 1
+ int ival;
+ if (!(str >> ival) || (0 > ival) || (1 < ival))
+ throw options_warning_exception("Illegal boolean value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+ }
break;
case option_type::INTEGER:
- // integers must be integral
- if (sscanf(data.c_str(), "%d", &ival) != 1)
- throw options_warning_exception("Illegal integer value for %s: \"%s\"; reverting to %s\n", name(), data, value());
-
- // range checking
- if (has_range())
{
- int minimum_integer = atoi(minimum());
- int maximum_integer = atoi(maximum());
- if (ival < minimum_integer || ival > maximum_integer)
- throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be between %d and %d); reverting to %s\n", name(), data, minimum_integer, maximum_integer, value());
+ // integers must be integral
+ int ival;
+ if (!(str >> ival))
+ throw options_warning_exception("Illegal integer value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+
+ // range checking
+ char const *const strmin(minimum());
+ char const *const strmax(maximum());
+ int imin(0), imax(0);
+ if (strmin && *strmin)
+ {
+ str.str(strmin);
+ str.seekg(0);
+ str >> imin;
+ }
+ if (strmax && *strmax)
+ {
+ str.str(strmax);
+ str.seekg(0);
+ str >> imax;
+ }
+ if ((strmin && *strmin && (ival < imin)) || (strmax && *strmax && (ival > imax)))
+ {
+ if (!strmax || !*strmax)
+ throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be no less than %d); reverting to %s\n", name(), data, imin, value());
+ else if (!strmin || !*strmin)
+ throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be no greater than %d); reverting to %s\n", name(), data, imax, value());
+ else
+ throw options_warning_exception("Out-of-range integer value for %s: \"%s\" (must be between %d and %d, inclusive); reverting to %s\n", name(), data, imin, imax, value());
+ }
}
break;
case option_type::FLOAT:
- if (sscanf(data.c_str(), "%f", &fval) != 1)
- throw options_warning_exception("Illegal float value for %s: \"%s\"; reverting to %s\n", name(), data, value());
-
- // range checking
- if (has_range())
{
- float minimum_float = atof(minimum());
- float maximum_float = atof(maximum());
- if (fval < minimum_float || fval > maximum_float)
- throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be between %f and %f); reverting to %s\n", name(), data, minimum_float, maximum_float, value());
+ float fval;
+ if (!(str >> fval))
+ throw options_warning_exception("Illegal float value for %s: \"%s\"; reverting to %s\n", name(), data, value());
+
+ // range checking
+ char const *const strmin(minimum());
+ char const *const strmax(maximum());
+ float fmin(0), fmax(0);
+ if (strmin && *strmin)
+ {
+ str.str(strmin);
+ str.seekg(0);
+ str >> fmin;
+ }
+ if (strmax && *strmax)
+ {
+ str.str(strmax);
+ str.seekg(0);
+ str >> fmax;
+ }
+ if ((strmin && *strmin && (fval < fmin)) || (strmax && *strmax && (fval > fmax)))
+ {
+ if (!strmax || !*strmax)
+ throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be no less than %f); reverting to %s\n", name(), data, fmin, value());
+ else if (!strmin || !*strmin)
+ throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be no greater than %f); reverting to %s\n", name(), data, fmax, value());
+ else
+ throw options_warning_exception("Out-of-range float value for %s: \"%s\" (must be between %f and %f, inclusive); reverting to %s\n", name(), data, fmin, fmax, value());
+ }
}
break;
- case OPTION_STRING:
+ case option_type::STRING:
+ case option_type::PATH:
+ case option_type::MULTIPATH:
// strings can be anything
break;
- case OPTION_INVALID:
- case OPTION_HEADER:
+ case option_type::INVALID:
+ case option_type::HEADER:
default:
// anything else is invalid
throw options_error_exception("Attempted to set invalid option %s\n", name());
@@ -238,7 +351,7 @@ void core_options::entry::validate(const std::string &data)
// entry::minimum
//-------------------------------------------------
-const char *core_options::entry::minimum() const
+const char *core_options::entry::minimum() const noexcept
{
return nullptr;
}
@@ -248,7 +361,7 @@ const char *core_options::entry::minimum() const
// entry::maximum
//-------------------------------------------------
-const char *core_options::entry::maximum() const
+const char *core_options::entry::maximum() const noexcept
{
return nullptr;
}
@@ -258,7 +371,7 @@ const char *core_options::entry::maximum() const
// entry::has_range
//-------------------------------------------------
-bool core_options::entry::has_range() const
+bool core_options::entry::has_range() const noexcept
{
return minimum() && maximum();
}
@@ -268,11 +381,10 @@ bool core_options::entry::has_range() const
// entry::default_value
//-------------------------------------------------
-const std::string &core_options::entry::default_value() const
+const std::string &core_options::entry::default_value() const noexcept
{
- // I don't really want this generally available, but MewUI seems to need it. Please
- // do not use
- throw false;
+ // I don't really want this generally available, but MewUI seems to need it. Please do not use.
+ abort();
}
@@ -286,10 +398,12 @@ const std::string &core_options::entry::default_value() const
core_options::simple_entry::simple_entry(std::vector<std::string> &&names, const char *description, core_options::option_type type, std::string &&defdata, std::string &&minimum, std::string &&maximum)
: entry(std::move(names), type, description)
- , m_defdata(std::move(defdata))
+ , m_defdata_unsubst(std::move(defdata))
, m_minimum(std::move(minimum))
, m_maximum(std::move(maximum))
{
+ m_defdata = type_specific_substitutions(m_defdata_unsubst);
+ m_data_unsubst = m_defdata_unsubst;
m_data = m_defdata;
}
@@ -307,25 +421,47 @@ core_options::simple_entry::~simple_entry()
// simple_entry::value
//-------------------------------------------------
-const char *core_options::simple_entry::value() const
+const char *core_options::simple_entry::value() const noexcept
+{
+ switch (type())
+ {
+ case option_type::BOOLEAN:
+ case option_type::INTEGER:
+ case option_type::FLOAT:
+ case option_type::STRING:
+ case option_type::PATH:
+ case option_type::MULTIPATH:
+ return m_data.c_str();
+
+ default:
+ // this is an option type for which returning a value is
+ // a meaningless operation (e.g. - core_options::option_type::COMMAND)
+ return nullptr;
+ }
+}
+
+
+//-------------------------------------------------
+// simple_entry::value_unsubstituted
+//-------------------------------------------------
+
+const char *core_options::simple_entry::value_unsubstituted() const noexcept
{
- const char *result;
switch (type())
{
case core_options::option_type::BOOLEAN:
case core_options::option_type::INTEGER:
case core_options::option_type::FLOAT:
case core_options::option_type::STRING:
- result = m_data.c_str();
- break;
+ case core_options::option_type::PATH:
+ case core_options::option_type::MULTIPATH:
+ return m_data_unsubst.c_str();
default:
// this is an option type for which returning a value is
// a meaningless operation (e.g. - core_options::option_type::COMMAND)
- result = nullptr;
- break;
+ return nullptr;
}
- return result;
}
@@ -333,7 +469,7 @@ const char *core_options::simple_entry::value() const
// simple_entry::default_value
//-------------------------------------------------
-const std::string &core_options::simple_entry::default_value() const
+const std::string &core_options::simple_entry::default_value() const noexcept
{
// only MewUI seems to need this; please don't use
return m_defdata;
@@ -341,12 +477,50 @@ const std::string &core_options::simple_entry::default_value() const
//-------------------------------------------------
-// internal_set_value
+// simple_entry::internal_set_value
+//-------------------------------------------------
+
+void core_options::simple_entry::internal_set_value(std::string &&newvalue, bool perform_substitutions)
+{
+ m_data = perform_substitutions
+ ? type_specific_substitutions(newvalue)
+ : newvalue;
+ m_data_unsubst = std::move(newvalue);
+}
+
+
+//-------------------------------------------------
+// simple_entry::internal_copy_value
//-------------------------------------------------
-void core_options::simple_entry::internal_set_value(std::string &&newvalue)
+bool core_options::simple_entry::internal_copy_value(const entry &that)
{
- m_data = std::move(newvalue);
+ simple_entry const *const simple = dynamic_cast<simple_entry const *>(&that);
+ if (!simple)
+ {
+ return entry::internal_copy_value(that);
+ }
+ else
+ {
+ switch (simple->type())
+ {
+ case option_type::BOOLEAN:
+ case option_type::INTEGER:
+ case option_type::FLOAT:
+ case option_type::STRING:
+ case option_type::PATH:
+ case option_type::MULTIPATH:
+ validate(simple->m_data);
+
+ m_data = simple->m_data;
+ m_data_unsubst = simple->m_data_unsubst;
+
+ return true;
+
+ default:
+ return false;
+ }
+ }
}
@@ -356,7 +530,47 @@ void core_options::simple_entry::internal_set_value(std::string &&newvalue)
void core_options::simple_entry::set_default_value(std::string &&newvalue)
{
- m_data = m_defdata = std::move(newvalue);
+ m_data = m_defdata = type_specific_substitutions(newvalue);
+ m_data_unsubst = m_defdata_unsubst = std::move(newvalue);
+}
+
+
+//-------------------------------------------------
+// type_specific_substitutions
+//-------------------------------------------------
+
+std::string core_options::simple_entry::type_specific_substitutions(std::string_view s) const noexcept
+{
+ switch (type())
+ {
+ case option_type::PATH:
+ return osd_subst_env(s);
+
+ case option_type::MULTIPATH:
+ {
+ std::ostringstream result;
+ while (!s.empty())
+ {
+ std::string_view::size_type split = s.find(';');
+ if (std::string_view::npos == split)
+ split = s.length();
+ result << osd_subst_env(s.substr(0, split));
+ if (s.length() > split)
+ {
+ result << s[split];
+ s.remove_prefix(split + 1);
+ }
+ else
+ {
+ s.remove_prefix(split);
+ }
+ }
+ return std::move(result).str();
+ }
+
+ default:
+ return std::string(s);
+ }
}
@@ -364,7 +578,7 @@ void core_options::simple_entry::set_default_value(std::string &&newvalue)
// minimum
//-------------------------------------------------
-const char *core_options::simple_entry::minimum() const
+const char *core_options::simple_entry::minimum() const noexcept
{
return m_minimum.c_str();
}
@@ -374,7 +588,7 @@ const char *core_options::simple_entry::minimum() const
// maximum
//-------------------------------------------------
-const char *core_options::simple_entry::maximum() const
+const char *core_options::simple_entry::maximum() const noexcept
{
return m_maximum.c_str();
}
@@ -412,11 +626,7 @@ void core_options::add_entry(entry::shared_ptr &&entry, const char *after_header
for (const std::string &name : entry->names())
{
// append the entry
- add_to_entry_map(std::string(name), entry);
-
- // for booleans, add the "-noXYZ" option as well
- if (entry->type() == option_type::BOOLEAN)
- add_to_entry_map(std::string("no") + name, entry);
+ add_to_entry_map(name, entry);
}
// and add the entry to the vector
@@ -429,13 +639,13 @@ void core_options::add_entry(entry::shared_ptr &&entry, const char *after_header
// map
//-------------------------------------------------
-void core_options::add_to_entry_map(std::string &&name, entry::shared_ptr &entry)
+void core_options::add_to_entry_map(const std::string &name, entry::shared_ptr &entry)
{
// it is illegal to call this method for something that already exists
assert(m_entrymap.find(name) == m_entrymap.end());
// append the entry
- m_entrymap.emplace(std::make_pair(name, entry::weak_ptr(entry)));
+ m_entrymap.emplace(std::make_pair(std::string_view(name), entry::weak_ptr(entry)));
}
@@ -454,26 +664,39 @@ void core_options::add_entry(const options_entry &opt, bool override_existing)
{
// first extract any range
std::string namestr(opt.name);
- int lparen = namestr.find_first_of('(', 0);
- int dash = namestr.find_first_of('-', lparen + 1);
- int rparen = namestr.find_first_of(')', dash + 1);
- if (lparen != -1 && dash != -1 && rparen != -1)
+ std::string::size_type lparen = namestr.find_first_of('(', 0);
+ if (lparen != std::string::npos)
{
- strtrimspace(minimum.assign(namestr.substr(lparen + 1, dash - (lparen + 1))));
- strtrimspace(maximum.assign(namestr.substr(dash + 1, rparen - (dash + 1))));
- namestr.erase(lparen, rparen + 1 - lparen);
+ std::string::size_type dash = namestr.find_first_of('-', lparen + 1);
+ if (dash != std::string::npos)
+ {
+ std::string::size_type rparen = namestr.find_first_of(')', dash + 1);
+ if (rparen != std::string::npos)
+ {
+ minimum.assign(strtrimspace(std::string_view(&namestr[lparen + 1], dash - (lparen + 1))));
+ maximum.assign(strtrimspace(std::string_view(&namestr[dash + 1], rparen - (dash + 1))));
+ namestr.erase(lparen, rparen + 1 - lparen);
+ }
+ }
}
// then chop up any semicolon-separated names
- size_t semi;
+ std::string::size_type semi;
while ((semi = namestr.find_first_of(';')) != std::string::npos)
{
names.push_back(namestr.substr(0, semi));
+
+ // for booleans, add the "-noXYZ" option as well
+ if (opt.type == option_type::BOOLEAN)
+ names.push_back(std::string("no") + names.back());
+
namestr.erase(0, semi + 1);
}
// finally add the last item
names.push_back(std::move(namestr));
+ if (opt.type == option_type::BOOLEAN)
+ names.push_back(std::string("no") + names.back());
}
// we might be called with an existing entry
@@ -482,7 +705,7 @@ void core_options::add_entry(const options_entry &opt, bool override_existing)
{
for (const std::string &name : names)
{
- existing_entry = get_entry(name.c_str());
+ existing_entry = get_entry(name);
if (existing_entry)
break;
}
@@ -558,10 +781,12 @@ void core_options::add_entries(const options_entry *entrylist, bool override_exi
// of an option
//-------------------------------------------------
-void core_options::set_default_value(const char *name, const char *defvalue)
+void core_options::set_default_value(std::string_view name, std::string &&defvalue)
{
// update the data and default data
- get_entry(name)->set_default_value(defvalue);
+ auto entry = get_entry(name);
+ assert(entry != nullptr);
+ entry->set_default_value(std::move(defvalue));
}
@@ -570,10 +795,12 @@ void core_options::set_default_value(const char *name, const char *defvalue)
// of an option
//-------------------------------------------------
-void core_options::set_description(const char *name, const char *description)
+void core_options::set_description(std::string_view name, const char *description)
{
// update the data and default data
- get_entry(name)->set_description(description);
+ auto entry = get_entry(name);
+ assert(entry != nullptr);
+ entry->set_description(description);
}
@@ -596,7 +823,7 @@ void core_options::parse_command_line(const std::vector<std::string> &args, int
if (!args[arg].empty() && args[arg][0] == '-')
{
auto curentry = get_entry(&args[arg][1]);
- if (curentry && curentry->type() == OPTION_COMMAND)
+ if (curentry && curentry->type() == core_options::option_type::COMMAND)
{
// can only have one command
if (!m_command.empty())
@@ -619,7 +846,7 @@ void core_options::parse_command_line(const std::vector<std::string> &args, int
// special case - collect unadorned arguments after commands into a special place
if (is_unadorned && !m_command.empty())
{
- m_command_arguments.push_back(std::move(args[arg]));
+ m_command_arguments.push_back(args[arg]);
command_argument_processed();
continue;
}
@@ -634,11 +861,11 @@ void core_options::parse_command_line(const std::vector<std::string> &args, int
}
// at this point, we've already processed commands
- if (curentry->type() == OPTION_COMMAND)
+ if (curentry->type() == core_options::option_type::COMMAND)
continue;
// get the data for this argument, special casing booleans
- std::string newdata;
+ std::string_view newdata;
if (curentry->type() == option_type::BOOLEAN)
{
newdata = (strncmp(&curarg[1], "no", 2) == 0) ? "0" : "1";
@@ -657,7 +884,7 @@ void core_options::parse_command_line(const std::vector<std::string> &args, int
}
// set the new data
- do_set_value(*curentry, std::move(newdata), priority, error_stream, condition);
+ do_set_value(*curentry, newdata, priority, error_stream, condition, false);
}
// did we have any errors that may need to be aggregated?
@@ -677,7 +904,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool i
// loop over lines in the file
char buffer[4096];
- while (inifile.gets(buffer, ARRAY_LENGTH(buffer)) != nullptr)
+ while (inifile.gets(buffer, std::size(buffer)) != nullptr)
{
// find the extent of the name
char *optionname;
@@ -731,9 +958,7 @@ void core_options::parse_ini_file(util::core_file &inifile, int priority, bool i
}
// set the new data
- std::string data = optiondata;
- trim_spaces_and_quotes(data);
- do_set_value(*curentry, std::move(data), priority, error_stream, condition);
+ do_set_value(*curentry, trim_spaces_and_quotes(optiondata), priority, error_stream, condition, true);
}
// did we have any errors that may need to be aggregated?
@@ -777,13 +1002,9 @@ void core_options::copy_from(const core_options &that)
if (dest_entry->names().size() > 0)
{
// identify the source entry
- const entry::shared_ptr source_entry = that.get_entry(dest_entry->name());
+ const entry::shared_const_ptr source_entry = that.get_entry(dest_entry->name());
if (source_entry)
- {
- const char *value = source_entry->value();
- if (value)
- dest_entry->set_value(value, source_entry->priority(), true);
- }
+ dest_entry->copy_from(*source_entry, false);
}
}
}
@@ -799,6 +1020,7 @@ std::string core_options::output_ini(const core_options *diff) const
{
// INI files are complete, so always start with a blank buffer
std::ostringstream buffer;
+ buffer.imbue(std::locale::classic());
int num_valid_headers = 0;
int unadorned_index = 0;
@@ -816,7 +1038,7 @@ std::string core_options::output_ini(const core_options *diff) const
else
{
const std::string &name(curentry->name());
- const char *value(curentry->value());
+ const char *value(curentry->value_unsubstituted());
// check if it's unadorned
bool is_unadorned = false;
@@ -830,7 +1052,7 @@ std::string core_options::output_ini(const core_options *diff) const
if (value)
{
// look up counterpart in diff, if diff is specified
- if (!diff || strcmp(value, diff->value(name.c_str())))
+ if (!diff || strcmp(value, diff->get_entry(name.c_str())->value_unsubstituted()))
{
// output header, if we have one
if (last_header)
@@ -869,13 +1091,16 @@ std::string core_options::output_help() const
// loop over all items
for (auto &curentry : m_entries)
{
- // header: just print
if (curentry->type() == option_type::HEADER)
+ {
+ // header: just print
util::stream_format(buffer, "\n#\n# %s\n#\n", curentry->description());
-
- // otherwise, output entries for all non-deprecated items
+ }
else if (curentry->description() != nullptr)
- util::stream_format(buffer, "-%-20s%s\n", curentry->name(), curentry->description());
+ {
+ // otherwise, output entries for all non-deprecated items
+ util::stream_format(buffer, "-%-19s %s\n", curentry->name(), curentry->description());
+ }
}
return buffer.str();
}
@@ -885,9 +1110,10 @@ std::string core_options::output_help() const
// value - return the raw option value
//-------------------------------------------------
-const char *core_options::value(const char *option) const
+const char *core_options::value(std::string_view option) const noexcept
{
- return get_entry(option)->value();
+ auto const entry = get_entry(option);
+ return entry ? entry->value() : nullptr;
}
@@ -895,9 +1121,48 @@ const char *core_options::value(const char *option) const
// description - return description of option
//-------------------------------------------------
-const char *core_options::description(const char *option) const
+const char *core_options::description(std::string_view option) const noexcept
+{
+ auto const entry = get_entry(option);
+ return entry ? entry->description() : nullptr;
+}
+
+
+//-------------------------------------------------
+// value - return the option value as an integer
+//-------------------------------------------------
+
+int core_options::int_value(std::string_view option) const
{
- return get_entry(option)->description();
+ char const *const data = value(option);
+ if (!data)
+ return 0;
+ std::istringstream str(data);
+ str.imbue(std::locale::classic());
+ int ival;
+ if (str >> ival)
+ return ival;
+ else
+ return 0;
+}
+
+
+//-------------------------------------------------
+// value - return the option value as a float
+//-------------------------------------------------
+
+float core_options::float_value(std::string_view option) const
+{
+ char const *const data = value(option);
+ if (!data)
+ return 0.0f;
+ std::istringstream str(data);
+ str.imbue(std::locale::classic());
+ float fval;
+ if (str >> fval)
+ return fval;
+ else
+ return 0.0f;
}
@@ -909,24 +1174,31 @@ const char *core_options::description(const char *option) const
// set_value - set the raw option value
//-------------------------------------------------
-void core_options::set_value(const std::string &name, const std::string &value, int priority)
+void core_options::set_value(std::string_view name, std::string_view value, int priority)
+{
+ set_value(name, std::string(value), priority);
+}
+
+void core_options::set_value(std::string_view name, const char *value, int priority)
{
set_value(name, std::string(value), priority);
}
-void core_options::set_value(const std::string &name, std::string &&value, int priority)
+void core_options::set_value(std::string_view name, std::string &&value, int priority)
{
- get_entry(name)->set_value(std::move(value), priority);
+ auto entry = get_entry(name);
+ assert(entry != nullptr);
+ entry->set_value(std::move(value), priority, false, false);
}
-void core_options::set_value(const std::string &name, int value, int priority)
+void core_options::set_value(std::string_view name, int value, int priority)
{
- set_value(name, string_format("%d", value), priority);
+ set_value(name, util::string_format("%d", value), priority);
}
-void core_options::set_value(const std::string &name, float value, int priority)
+void core_options::set_value(std::string_view name, float value, int priority)
{
- set_value(name, string_format("%f", value), priority);
+ set_value(name, util::string_format("%f", value), priority);
}
@@ -957,13 +1229,13 @@ void core_options::remove_entry(core_options::entry &delentry)
// do_set_value
//-------------------------------------------------
-void core_options::do_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition)
+void core_options::do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions)
{
// this is called when parsing a command line or an INI - we want to catch the option_exception and write
// any exception messages to the error stream
try
{
- curentry.set_value(std::move(data), priority);
+ curentry.set_value(std::string(data), priority, false, perform_substitutions);
}
catch (options_warning_exception &ex)
{
@@ -984,13 +1256,13 @@ void core_options::do_set_value(entry &curentry, std::string &&data, int priorit
// get_entry
//-------------------------------------------------
-const core_options::entry::shared_ptr core_options::get_entry(const std::string &name) const
+core_options::entry::shared_const_ptr core_options::get_entry(std::string_view name) const noexcept
{
auto curentry = m_entrymap.find(name);
return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
}
-core_options::entry::shared_ptr core_options::get_entry(const std::string &name)
+core_options::entry::shared_ptr core_options::get_entry(std::string_view name) noexcept
{
auto curentry = m_entrymap.find(name);
return (curentry != m_entrymap.end()) ? curentry->second.lock() : nullptr;
@@ -1001,9 +1273,11 @@ core_options::entry::shared_ptr core_options::get_entry(const std::string &name)
// set_value_changed_handler
//-------------------------------------------------
-void core_options::set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler)
+void core_options::set_value_changed_handler(std::string_view name, std::function<void(const char *)> &&handler)
{
- get_entry(name)->set_value_changed_handler(std::move(handler));
+ auto entry = get_entry(name);
+ assert(entry != nullptr);
+ entry->set_value_changed_handler(std::move(handler));
}
@@ -1011,17 +1285,17 @@ void core_options::set_value_changed_handler(const std::string &name, std::funct
// header_exists
//-------------------------------------------------
-bool core_options::header_exists(const char *description) const
+bool core_options::header_exists(const char *description) const noexcept
{
auto iter = std::find_if(
- m_entries.begin(),
- m_entries.end(),
- [description](const auto &entry)
- {
- return entry->type() == option_type::HEADER
- && entry->description()
- && !strcmp(entry->description(), description);
- });
+ m_entries.begin(),
+ m_entries.end(),
+ [description](const auto &entry)
+ {
+ return entry->type() == option_type::HEADER
+ && entry->description()
+ && !strcmp(entry->description(), description);
+ });
return iter != m_entries.end();
}
@@ -1034,7 +1308,7 @@ bool core_options::header_exists(const char *description) const
void core_options::revert(int priority_hi, int priority_lo)
{
for (entry::shared_ptr &curentry : m_entries)
- if (curentry->type() != option_type::HEADER)
+ if (curentry->type() != option_type::HEADER && curentry->type() != option_type::COMMAND)
curentry->revert(priority_hi, priority_lo);
}
@@ -1048,7 +1322,7 @@ void core_options::simple_entry::revert(int priority_hi, int priority_lo)
// if our priority is within the range, revert to the default
if (priority() <= priority_hi && priority() >= priority_lo)
{
- set_value(std::string(default_value()), priority(), true);
+ set_value(std::string(m_defdata_unsubst), priority(), true, true);
set_priority(OPTION_PRIORITY_DEFAULT);
}
}
diff --git a/src/lib/util/options.h b/src/lib/util/options.h
index 3fcd9d5e144..8b85b9f58e6 100644
--- a/src/lib/util/options.h
+++ b/src/lib/util/options.h
@@ -11,9 +11,20 @@
#ifndef MAME_LIB_UTIL_OPTIONS_H
#define MAME_LIB_UTIL_OPTIONS_H
-#include "corefile.h"
+#include "strformat.h"
+#include "utilfwd.h"
+
+#include <algorithm>
+#include <exception>
+#include <functional>
+#include <iosfwd>
+#include <memory>
#include <unordered_map>
#include <sstream>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <vector>
//**************************************************************************
@@ -93,7 +104,9 @@ public:
BOOLEAN, // boolean option
INTEGER, // integer option
FLOAT, // floating-point option
- STRING // string option
+ STRING, // string option
+ PATH, // single path option
+ MULTIPATH // semicolon-delimited paths option
};
// information about a single entry in the options
@@ -101,6 +114,7 @@ public:
{
public:
typedef std::shared_ptr<entry> shared_ptr;
+ typedef std::shared_ptr<const entry> shared_const_ptr;
typedef std::weak_ptr<entry> weak_ptr;
// constructor/destructor
@@ -113,32 +127,35 @@ public:
virtual ~entry();
// accessors
- const std::vector<std::string> &names() const { return m_names; }
- const std::string &name() const { return m_names[0]; }
- virtual const char *value() const;
- int priority() const { return m_priority; }
- void set_priority(int priority) { m_priority = priority; }
- option_type type() const { return m_type; }
- const char *description() const { return m_description; }
- virtual const std::string &default_value() const;
- virtual const char *minimum() const;
- virtual const char *maximum() const;
- bool has_range() const;
+ const std::vector<std::string> &names() const noexcept { return m_names; }
+ const std::string &name() const noexcept { return m_names[0]; }
+ virtual const char *value() const noexcept;
+ virtual const char *value_unsubstituted() const noexcept;
+ int priority() const noexcept { return m_priority; }
+ void set_priority(int priority) noexcept { m_priority = priority; }
+ option_type type() const noexcept { return m_type; }
+ const char *description() const noexcept { return m_description; }
+ virtual const std::string &default_value() const noexcept;
+ virtual const char *minimum() const noexcept;
+ virtual const char *maximum() const noexcept;
+ bool has_range() const noexcept;
// mutators
- void set_value(std::string &&newvalue, int priority, bool always_override = false);
+ void copy_from(const entry &that, bool always_override);
+ void set_value(std::string &&newvalue, int priority, bool always_override = false, bool perform_substitutions = false);
virtual void set_default_value(std::string &&newvalue);
void set_description(const char *description) { m_description = description; }
void set_value_changed_handler(std::function<void(const char *)> &&handler) { m_value_changed_handler = std::move(handler); }
virtual void revert(int priority_hi, int priority_lo) { }
protected:
- virtual void internal_set_value(std::string &&newvalue) = 0;
+ virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) = 0;
+ virtual bool internal_copy_value(const entry &that);
- private:
void validate(const std::string &value);
- std::vector<std::string> m_names;
+ private:
+ const std::vector<std::string> m_names;
int m_priority;
core_options::option_type m_type;
const char * m_description;
@@ -154,13 +171,13 @@ public:
virtual ~core_options();
// getters
- const std::string &command() const { return m_command; }
- const std::vector<std::string> &command_arguments() const { assert(!m_command.empty()); return m_command_arguments; }
- const entry::shared_ptr get_entry(const std::string &name) const;
- entry::shared_ptr get_entry(const std::string &name);
- const std::vector<entry::shared_ptr> &entries() const { return m_entries; }
- bool exists(const std::string &name) const { return get_entry(name) != nullptr; }
- bool header_exists(const char *description) const;
+ const std::string &command() const noexcept { return m_command; }
+ const std::vector<std::string> &command_arguments() const noexcept { assert(!m_command.empty()); return m_command_arguments; }
+ entry::shared_const_ptr get_entry(std::string_view name) const noexcept;
+ entry::shared_ptr get_entry(std::string_view name) noexcept;
+ const std::vector<entry::shared_ptr> &entries() const noexcept { return m_entries; }
+ bool exists(std::string_view name) const noexcept { return get_entry(name) != nullptr; }
+ bool header_exists(const char *description) const noexcept;
// configuration
void add_entry(entry::shared_ptr &&entry, const char *after_header = nullptr);
@@ -168,10 +185,12 @@ public:
void add_entry(std::vector<std::string> &&names, const char *description, option_type type, std::string &&default_value = "", std::string &&minimum = "", std::string &&maximum = "");
void add_header(const char *description);
void add_entries(const options_entry *entrylist, bool override_existing = false);
- void set_default_value(const char *name, const char *defvalue);
- void set_description(const char *name, const char *description);
+ void set_default_value(std::string_view name, std::string &&defvalue);
+ void set_default_value(std::string_view name, std::string_view defvalue) { set_default_value(name, std::string(defvalue)); }
+ void set_default_value(std::string_view name, const char *defvalue) { set_default_value(name, std::string(defvalue)); }
+ void set_description(std::string_view name, const char *description);
void remove_entry(entry &delentry);
- void set_value_changed_handler(const std::string &name, std::function<void(const char *)> &&handler);
+ void set_value_changed_handler(std::string_view name, std::function<void(const char *)> &&handler);
void revert(int priority_hi = OPTION_PRIORITY_MAXIMUM, int priority_lo = OPTION_PRIORITY_DEFAULT);
// parsing/input
@@ -184,20 +203,21 @@ public:
std::string output_help() const;
// reading
- const char *value(const char *option) const;
- const char *description(const char *option) const;
- bool bool_value(const char *option) const { return int_value(option) != 0; }
- int int_value(const char *option) const { return atoi(value(option)); }
- float float_value(const char *option) const { return atof(value(option)); }
+ const char *value(std::string_view option) const noexcept;
+ const char *description(std::string_view option) const noexcept;
+ bool bool_value(std::string_view option) const { return int_value(option) != 0; }
+ int int_value(std::string_view option) const;
+ float float_value(std::string_view option) const;
// setting
- void set_value(const std::string &name, const std::string &value, int priority);
- void set_value(const std::string &name, std::string &&value, int priority);
- void set_value(const std::string &name, int value, int priority);
- void set_value(const std::string &name, float value, int priority);
+ void set_value(std::string_view name, std::string_view value, int priority);
+ void set_value(std::string_view name, const char *value, int priority);
+ void set_value(std::string_view name, std::string &&value, int priority);
+ void set_value(std::string_view name, int value, int priority);
+ void set_value(std::string_view name, float value, int priority);
// misc
- static const char *unadorned(int x = 0) { return s_option_unadorned[std::min(x, MAX_UNADORNED_OPTIONS - 1)]; }
+ static const char *unadorned(int x = 0) noexcept { return s_option_unadorned[std::min(x, MAX_UNADORNED_OPTIONS - 1)]; }
protected:
virtual void command_argument_processed() { }
@@ -215,23 +235,29 @@ private:
~simple_entry();
// getters
- virtual const char *value() const override;
- virtual const char *minimum() const override;
- virtual const char *maximum() const override;
- virtual const std::string &default_value() const override;
+ virtual const char *value() const noexcept override;
+ virtual const char *value_unsubstituted() const noexcept override;
+ virtual const char *minimum() const noexcept override;
+ virtual const char *maximum() const noexcept override;
+ virtual const std::string &default_value() const noexcept override;
virtual void revert(int priority_hi, int priority_lo) override;
virtual void set_default_value(std::string &&newvalue) override;
protected:
- virtual void internal_set_value(std::string &&newvalue) override;
+ virtual void internal_set_value(std::string &&newvalue, bool perform_substitutions) override;
+ virtual bool internal_copy_value(const entry &that) override;
private:
// internal state
std::string m_data; // data for this item
+ std::string m_data_unsubst; // data for this item, prior to any substitution
std::string m_defdata; // default data for this item
+ std::string m_defdata_unsubst; // default data for this item, prior to any substitution
std::string m_minimum; // minimum value
std::string m_maximum; // maximum value
+
+ std::string type_specific_substitutions(std::string_view s) const noexcept;
};
// used internally in core_options
@@ -243,13 +269,13 @@ private:
};
// internal helpers
- void add_to_entry_map(std::string &&name, entry::shared_ptr &entry);
- void do_set_value(entry &curentry, std::string &&data, int priority, std::ostream &error_stream, condition_type &condition);
+ void add_to_entry_map(const std::string &name, entry::shared_ptr &entry);
+ void do_set_value(entry &curentry, std::string_view data, int priority, std::ostream &error_stream, condition_type &condition, bool perform_substitutions);
void throw_options_exception_if_appropriate(condition_type condition, std::ostringstream &error_stream);
// internal state
std::vector<entry::shared_ptr> m_entries; // canonical list of entries
- std::unordered_map<std::string, entry::weak_ptr> m_entrymap; // map for fast lookup
+ std::unordered_map<std::string_view, entry::weak_ptr> m_entrymap; // map for fast lookup
std::string m_command; // command found
std::vector<std::string> m_command_arguments; // command arguments
static const char *const s_option_unadorned[]; // array of unadorned option "names"
@@ -265,14 +291,4 @@ struct options_entry
const char * description; // description for -showusage
};
-// legacy option types
-const core_options::option_type OPTION_INVALID = core_options::option_type::INVALID;
-const core_options::option_type OPTION_HEADER = core_options::option_type::HEADER;
-const core_options::option_type OPTION_COMMAND = core_options::option_type::COMMAND;
-const core_options::option_type OPTION_BOOLEAN = core_options::option_type::BOOLEAN;
-const core_options::option_type OPTION_INTEGER = core_options::option_type::INTEGER;
-const core_options::option_type OPTION_FLOAT = core_options::option_type::FLOAT;
-const core_options::option_type OPTION_STRING = core_options::option_type::STRING;
-
-
#endif // MAME_LIB_UTIL_OPTIONS_H
diff --git a/src/lib/util/palette.cpp b/src/lib/util/palette.cpp
index 488f7f8a2d2..612dc9042be 100644
--- a/src/lib/util/palette.cpp
+++ b/src/lib/util/palette.cpp
@@ -8,12 +8,12 @@
******************************************************************************/
-#include <assert.h>
-
#include "palette.h"
-#include <stdlib.h>
-#include <math.h>
+
#include <algorithm>
+#include <cassert>
+#include <cmath>
+#include <cstdlib>
//**************************************************************************
@@ -44,9 +44,7 @@ inline rgb_t palette_t::adjust_palette_entry(rgb_t entry, float brightness, floa
// dirty_state - constructor
//-------------------------------------------------
-palette_client::dirty_state::dirty_state()
- : m_mindirty(0),
- m_maxdirty(0)
+palette_client::dirty_state::dirty_state() : m_mindirty(0), m_maxdirty(0)
{
}
@@ -56,7 +54,7 @@ palette_client::dirty_state::dirty_state()
// min/max values
//-------------------------------------------------
-const uint32_t *palette_client::dirty_state::dirty_list(uint32_t &mindirty, uint32_t &maxdirty)
+const uint32_t *palette_client::dirty_state::dirty_list(uint32_t &mindirty, uint32_t &maxdirty) noexcept
{
// fill in the mindirty/maxdirty
mindirty = m_mindirty;
@@ -77,7 +75,7 @@ void palette_client::dirty_state::resize(uint32_t colors)
// resize to the correct number of dwords and mark all entries dirty
uint32_t dirty_dwords = (colors + 31) / 32;
m_dirty.resize(dirty_dwords);
- memset(&m_dirty[0], 0xff, dirty_dwords*4);
+ std::fill(m_dirty.begin(), m_dirty.end(), ~uint32_t(0));
// mark all entries dirty
m_dirty[dirty_dwords - 1] &= (1 << (colors % 32)) - 1;
@@ -92,7 +90,7 @@ void palette_client::dirty_state::resize(uint32_t colors)
// mark_dirty - mark a single entry dirty
//-------------------------------------------------
-void palette_client::dirty_state::mark_dirty(uint32_t index)
+void palette_client::dirty_state::mark_dirty(uint32_t index) noexcept
{
m_dirty[index / 32] |= 1 << (index % 32);
m_mindirty = std::min(m_mindirty, index);
@@ -105,10 +103,11 @@ void palette_client::dirty_state::mark_dirty(uint32_t index)
// entries as clean
//-------------------------------------------------
-void palette_client::dirty_state::reset()
+void palette_client::dirty_state::reset() noexcept
{
// erase relevant entries in the new live one
- memset(&m_dirty[m_mindirty / 32], 0, ((m_maxdirty / 32) + 1 - (m_mindirty / 32)) * sizeof(uint32_t));
+ if (m_mindirty <= m_maxdirty)
+ std::fill(&m_dirty[m_mindirty / 32], &m_dirty[m_maxdirty / 32] + 1, 0);
m_mindirty = m_dirty.size() * 32 - 1;
m_maxdirty = 0;
}
@@ -167,20 +166,18 @@ palette_client::~palette_client()
// list for a client
//-------------------------------------------------
-const uint32_t *palette_client::dirty_list(uint32_t &mindirty, uint32_t &maxdirty)
+const uint32_t *palette_client::dirty_list(uint32_t &mindirty, uint32_t &maxdirty) noexcept
{
// if nothing to report, report nothing and don't swap
- const uint32_t *result = m_live->dirty_list(mindirty, maxdirty);
- if (result == nullptr)
- return nullptr;
-
- // swap the live and previous lists
- dirty_state *temp = m_live;
- m_live = m_previous;
- m_previous = temp;
+ uint32_t const *const result = m_live->dirty_list(mindirty, maxdirty);
+ if (result)
+ {
+ // swap the live and previous lists
+ std::swap(m_live, m_previous);
- // reset new live one and return the pointer to the previous
- m_live->reset();
+ // reset new live one and return the pointer to the previous
+ m_live->reset();
+ }
return result;
}
@@ -265,7 +262,7 @@ palette_t::~palette_t()
// palette_t - destructor
//-------------------------------------------------
-void palette_t::deref()
+void palette_t::deref() noexcept
{
if (--m_refcount == 0)
delete this;
diff --git a/src/lib/util/palette.h b/src/lib/util/palette.h
index ff2c7bb80c9..269389b67d6 100644
--- a/src/lib/util/palette.h
+++ b/src/lib/util/palette.h
@@ -13,8 +13,8 @@
#pragma once
-#include "osdcore.h"
-#include "coretmpl.h"
+#include <cstdint>
+#include <vector>
//**************************************************************************
@@ -100,12 +100,12 @@ public:
~palette_client();
// getters
- palette_client *next() const { return m_next; }
- palette_t &palette() const { return m_palette; }
- const uint32_t *dirty_list(uint32_t &mindirty, uint32_t &maxdirty);
+ palette_client *next() const noexcept { return m_next; }
+ palette_t &palette() const noexcept { return m_palette; }
+ const uint32_t *dirty_list(uint32_t &mindirty, uint32_t &maxdirty) noexcept;
// dirty marking
- void mark_dirty(uint32_t index) { m_live->mark_dirty(index); }
+ void mark_dirty(uint32_t index) noexcept { m_live->mark_dirty(index); }
private:
// internal object to track dirty states
@@ -116,10 +116,10 @@ private:
dirty_state();
// operations
- const uint32_t *dirty_list(uint32_t &mindirty, uint32_t &maxdirty);
+ const uint32_t *dirty_list(uint32_t &mindirty, uint32_t &maxdirty) noexcept;
void resize(uint32_t colors);
- void mark_dirty(uint32_t index);
- void reset();
+ void mark_dirty(uint32_t index) noexcept;
+ void reset() noexcept;
private:
// internal state
@@ -149,15 +149,15 @@ public:
static palette_t *alloc(uint32_t numcolors, uint32_t numgroups = 1);
// reference counting
- void ref() { m_refcount++; }
- void deref();
+ void ref() noexcept { m_refcount++; }
+ void deref() noexcept;
// getters
- int num_colors() const { return m_numcolors; }
- int num_groups() const { return m_numgroups; }
- int max_index() const { return m_numcolors * m_numgroups + 2; }
- uint32_t black_entry() const { return m_numcolors * m_numgroups + 0; }
- uint32_t white_entry() const { return m_numcolors * m_numgroups + 1; }
+ int num_colors() const noexcept { return m_numcolors; }
+ int num_groups() const noexcept { return m_numgroups; }
+ int max_index() const noexcept { return m_numcolors * m_numgroups + 2; }
+ uint32_t black_entry() const noexcept { return m_numcolors * m_numgroups + 0; }
+ uint32_t white_entry() const noexcept { return m_numcolors * m_numgroups + 1; }
// overall adjustments
void set_brightness(float brightness);
@@ -165,9 +165,9 @@ public:
void set_gamma(float gamma);
// entry getters
- rgb_t entry_color(uint32_t index) const { return (index < m_numcolors) ? m_entry_color[index] : rgb_t::black(); }
- rgb_t entry_adjusted_color(uint32_t index) const { return (index < m_numcolors * m_numgroups) ? m_adjusted_color[index] : rgb_t::black(); }
- float entry_contrast(uint32_t index) const { return (index < m_numcolors) ? m_entry_contrast[index] : 1.0f; }
+ rgb_t entry_color(uint32_t index) const noexcept { return (index < m_numcolors) ? m_entry_color[index] : rgb_t::black(); }
+ rgb_t entry_adjusted_color(uint32_t index) const noexcept { return (index < m_numcolors * m_numgroups) ? m_adjusted_color[index] : rgb_t::black(); }
+ float entry_contrast(uint32_t index) const noexcept { return (index < m_numcolors) ? m_entry_contrast[index] : 1.0f; }
// entry setters
void entry_set_color(uint32_t index, rgb_t rgb);
@@ -177,9 +177,9 @@ public:
void entry_set_contrast(uint32_t index, float contrast);
// entry list getters
- const rgb_t *entry_list_raw() const { return &m_entry_color[0]; }
- const rgb_t *entry_list_adjusted() const { return &m_adjusted_color[0]; }
- const rgb_t *entry_list_adjusted_rgb15() const { return &m_adjusted_rgb15[0]; }
+ const rgb_t *entry_list_raw() const noexcept { return &m_entry_color[0]; }
+ const rgb_t *entry_list_adjusted() const noexcept { return &m_adjusted_color[0]; }
+ const rgb_t *entry_list_adjusted_rgb15() const noexcept { return &m_adjusted_rgb15[0]; }
// group adjustments
void group_set_brightness(uint32_t group, float brightness);
@@ -198,14 +198,14 @@ private:
void update_adjusted_color(uint32_t group, uint32_t index);
// internal state
- uint32_t m_refcount; // reference count on the palette
- uint32_t m_numcolors; // number of colors in the palette
- uint32_t m_numgroups; // number of groups in the palette
+ uint32_t m_refcount; // reference count on the palette
+ uint32_t m_numcolors; // number of colors in the palette
+ uint32_t m_numgroups; // number of groups in the palette
- float m_brightness; // overall brightness value
- float m_contrast; // overall contrast value
- float m_gamma; // overall gamma value
- uint8_t m_gamma_map[256]; // gamma map
+ float m_brightness; // overall brightness value
+ float m_contrast; // overall contrast value
+ float m_gamma; // overall gamma value
+ uint8_t m_gamma_map[256]; // gamma map
std::vector<rgb_t> m_entry_color; // array of raw colors
std::vector<float> m_entry_contrast; // contrast value for each entry
@@ -229,7 +229,7 @@ private:
//-------------------------------------------------
template<int _NumBits>
-inline uint8_t palexpand(uint8_t bits)
+constexpr uint8_t palexpand(uint8_t bits)
{
if (_NumBits == 1) { return (bits & 1) ? 0xff : 0x00; }
if (_NumBits == 2) { bits &= 3; return (bits << 6) | (bits << 4) | (bits << 2) | bits; }
@@ -246,13 +246,13 @@ inline uint8_t palexpand(uint8_t bits)
// palxbit - convert an x-bit value to 8 bits
//-------------------------------------------------
-inline uint8_t pal1bit(uint8_t bits) { return palexpand<1>(bits); }
-inline uint8_t pal2bit(uint8_t bits) { return palexpand<2>(bits); }
-inline uint8_t pal3bit(uint8_t bits) { return palexpand<3>(bits); }
-inline uint8_t pal4bit(uint8_t bits) { return palexpand<4>(bits); }
-inline uint8_t pal5bit(uint8_t bits) { return palexpand<5>(bits); }
-inline uint8_t pal6bit(uint8_t bits) { return palexpand<6>(bits); }
-inline uint8_t pal7bit(uint8_t bits) { return palexpand<7>(bits); }
+constexpr uint8_t pal1bit(uint8_t bits) { return palexpand<1>(bits); }
+constexpr uint8_t pal2bit(uint8_t bits) { return palexpand<2>(bits); }
+constexpr uint8_t pal3bit(uint8_t bits) { return palexpand<3>(bits); }
+constexpr uint8_t pal4bit(uint8_t bits) { return palexpand<4>(bits); }
+constexpr uint8_t pal5bit(uint8_t bits) { return palexpand<5>(bits); }
+constexpr uint8_t pal6bit(uint8_t bits) { return palexpand<6>(bits); }
+constexpr uint8_t pal7bit(uint8_t bits) { return palexpand<7>(bits); }
//-------------------------------------------------
@@ -261,21 +261,27 @@ inline uint8_t pal7bit(uint8_t bits) { return palexpand<7>(bits); }
//-------------------------------------------------
template<int _RBits, int _GBits, int _BBits>
-inline rgb_t rgbexpand(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift)
+constexpr rgb_t rgbexpand(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift)
{
return rgb_t(palexpand<_RBits>(data >> rshift), palexpand<_GBits>(data >> gshift), palexpand<_BBits>(data >> bshift));
}
+template<int _ABits, int _RBits, int _GBits, int _BBits>
+constexpr rgb_t argbexpand(uint32_t data, uint8_t ashift, uint8_t rshift, uint8_t gshift, uint8_t bshift)
+{
+ return rgb_t(palexpand<_ABits>(data >> ashift), palexpand<_RBits>(data >> rshift), palexpand<_GBits>(data >> gshift), palexpand<_BBits>(data >> bshift));
+}
+
//-------------------------------------------------
// palxxx - create an x-x-x color by extracting
// bits from a uint32_t
//-------------------------------------------------
-inline rgb_t pal332(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<3,3,2>(data, rshift, gshift, bshift); }
-inline rgb_t pal444(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<4,4,4>(data, rshift, gshift, bshift); }
-inline rgb_t pal555(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<5,5,5>(data, rshift, gshift, bshift); }
-inline rgb_t pal565(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<5,6,5>(data, rshift, gshift, bshift); }
-inline rgb_t pal888(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<8,8,8>(data, rshift, gshift, bshift); }
+constexpr rgb_t pal332(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<3,3,2>(data, rshift, gshift, bshift); }
+constexpr rgb_t pal444(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<4,4,4>(data, rshift, gshift, bshift); }
+constexpr rgb_t pal555(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<5,5,5>(data, rshift, gshift, bshift); }
+constexpr rgb_t pal565(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<5,6,5>(data, rshift, gshift, bshift); }
+constexpr rgb_t pal888(uint32_t data, uint8_t rshift, uint8_t gshift, uint8_t bshift) { return rgbexpand<8,8,8>(data, rshift, gshift, bshift); }
#endif // MAME_UTIL_PALETTE_H
diff --git a/src/lib/util/path.cpp b/src/lib/util/path.cpp
new file mode 100644
index 00000000000..bdfb7f4443f
--- /dev/null
+++ b/src/lib/util/path.cpp
@@ -0,0 +1,82 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Nathan Woods
+/***************************************************************************
+
+ path.cpp
+
+ Path and filename utilities.
+
+***************************************************************************/
+
+#include "path.h"
+
+#include <algorithm>
+#include <cctype>
+#include <iterator>
+
+
+/***************************************************************************
+ FILENAME UTILITIES
+***************************************************************************/
+
+// -------------------------------------------------
+// core_filename_extract_base - extract the base
+// name from a filename; note that this makes
+// assumptions about path separators
+// -------------------------------------------------
+
+std::string_view core_filename_extract_base(std::string_view name, bool strip_extension) noexcept
+{
+ // find the start of the basename
+ auto const start = std::find_if(name.rbegin(), name.rend(), &util::is_directory_separator);
+ if (start == name.rbegin())
+ return std::string_view();
+
+ // find the end of the basename
+ auto const chop_position = strip_extension
+ ? std::find(name.rbegin(), start, '.')
+ : start;
+ auto const end = ((chop_position != start) && (std::next(chop_position) != start))
+ ? std::next(chop_position)
+ : name.rbegin();
+
+ return std::string_view(&*start.base(), end.base() - start.base());
+}
+
+
+// -------------------------------------------------
+// core_filename_extract_extension
+// -------------------------------------------------
+
+std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period) noexcept
+{
+ auto loc = filename.find_last_of('.');
+ if (loc != std::string_view::npos)
+ return filename.substr(loc + (strip_period ? 1 : 0));
+ else
+ return std::string_view();
+}
+
+
+// -------------------------------------------------
+// core_filename_ends_with - does the given
+// filename end with the specified extension?
+// -------------------------------------------------
+
+bool core_filename_ends_with(std::string_view filename, std::string_view extension) noexcept
+{
+ auto namelen = filename.length();
+ auto extlen = extension.length();
+
+ // first if the extension is bigger than the name, we definitely don't match
+ bool matches = namelen >= extlen;
+
+ // work backwards checking for a match
+ while (matches && extlen > 0 && namelen > 0)
+ {
+ if (std::tolower(uint8_t(filename[--namelen])) != std::tolower(uint8_t(extension[--extlen])))
+ matches = false;
+ }
+
+ return matches;
+}
diff --git a/src/lib/util/path.h b/src/lib/util/path.h
new file mode 100644
index 00000000000..83785d0999f
--- /dev/null
+++ b/src/lib/util/path.h
@@ -0,0 +1,100 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb, Aaron Giles
+/***************************************************************************
+
+ path.h
+
+ Filesystem path utilties
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_PATH_H
+#define MAME_LIB_UTIL_PATH_H
+
+#include "osdfile.h" // for PATH_SEPARATOR
+
+#include <string>
+#include <string_view>
+#include <utility>
+
+
+namespace util {
+
+/// \defgroup pathutils Filesystem path utilities
+/// \{
+
+/// \brief Is a character a directory separator?
+///
+/// Determine whether a character is used to separate components within
+/// a filesystem path.
+/// \param [in] c A character to test.
+/// \return True if the character is used to separate components in
+/// filesystem paths.
+constexpr bool is_directory_separator(char c)
+{
+#if defined(_WIN32)
+ return ('\\' == c) || ('/' == c) || (':' == c);
+#else
+ return '/' == c;
+#endif
+}
+
+
+/// \brief Append components to a filesystem path
+///
+/// Appends directory components to a filesystem path.
+/// \param [in,out] path The path to append to.
+/// \param [in] next The first directory component to append to the
+/// path.
+/// \param [in] more Additional directory components to append to the
+/// path.
+/// \return A reference to the modified path.
+template <typename T, typename... U>
+inline std::string &path_append(std::string &path, T &&next, U &&... more)
+{
+ if (!path.empty() && !is_directory_separator(path.back()))
+ path.append(PATH_SEPARATOR);
+ path.append(std::forward<T>(next));
+ if constexpr (sizeof...(U) > 0U)
+ return path_append(path, std::forward<U>(more)...);
+ else
+ return path;
+}
+
+
+/// \brief Concatenate filsystem paths
+///
+/// Concatenates multiple filesystem paths.
+/// \param [in] first Initial filesystem path.
+/// \param [in] more Additional directory components to append to the
+/// intial path.
+/// \return The concatenated filesystem path.
+template <typename T, typename... U>
+inline std::string path_concat(T &&first, U &&... more)
+{
+ std::string result(std::forward<T>(first));
+ if constexpr (sizeof...(U) > 0U)
+ path_append(result, std::forward<U>(more)...);
+ return result;
+}
+
+/// \}
+
+} // namespace util
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+/* ----- filename utilities ----- */
+
+// extract the base part of a filename (remove extensions and paths)
+std::string_view core_filename_extract_base(std::string_view name, bool strip_extension = false) noexcept;
+
+// extracts the file extension from a filename
+std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period = false) noexcept;
+
+// true if the given filename ends with a particular extension
+bool core_filename_ends_with(std::string_view filename, std::string_view extension) noexcept;
+
+#endif // MAME_LIB_UTIL_PATH_H
diff --git a/src/lib/util/plaparse.cpp b/src/lib/util/plaparse.cpp
index d1e7c679f97..9f4d1c0bc14 100644
--- a/src/lib/util/plaparse.cpp
+++ b/src/lib/util/plaparse.cpp
@@ -1,22 +1,24 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles, hap
+// copyright-holders:Aaron Giles, Curt Coder, hap
/***************************************************************************
- plaparse.h
+ plaparse.cpp
- Simple parser for Berkeley standard PLA files into raw fusemaps.
- It supports no more than one output matrix, and is limited to
- keywords: i, o, p, phase, e
+ Parser for Berkeley standard (aka Espresso) PLA files into raw fusemaps.
+ It supports no more than one output matrix, and is limited to keywords:
+ i, o, p, phase, e
***************************************************************************/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include "jedparse.h"
#include "plaparse.h"
+#include "ioprocs.h"
+
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <cctype>
+
/***************************************************************************
@@ -33,10 +35,10 @@
struct parse_info
{
- uint32_t inputs; /* number of input columns */
- uint32_t outputs; /* number of output columns */
- uint32_t terms; /* number of terms */
- uint32_t xorval[JED_MAX_FUSES/64]; /* output polarity */
+ uint32_t inputs; // number of input columns
+ uint32_t outputs; // number of output columns
+ uint32_t terms; // number of terms
+ uint32_t xorval[JED_MAX_FUSES/64]; // output polarity
uint32_t xorptr;
};
@@ -61,19 +63,30 @@ static bool iscrlf(char c)
character stream
-------------------------------------------------*/
-static uint32_t suck_number(const uint8_t **src, const uint8_t *srcend)
+static uint32_t suck_number(util::random_read &src)
{
uint32_t value = 0;
// find first digit
- while (*src < srcend && !iscrlf(**src) && !isdigit(**src))
- (*src)++;
-
- // loop over and accumulate digits
- while (*src < srcend && isdigit(**src))
+ bool found = false;
+ while (true)
{
- value = value * 10 + (**src) - '0';
- (*src)++;
+ uint8_t ch;
+ auto const [err, actual] = read(src, &ch, 1);
+ if (err || (1 != actual))
+ break;
+
+ // loop over and accumulate digits
+ if (isdigit(ch))
+ {
+ found = true;
+ value = value * 10 + ch - '0';
+ }
+ else if (found || iscrlf(ch))
+ {
+ src.seek(-1, SEEK_CUR);
+ break;
+ }
}
return value;
@@ -89,89 +102,85 @@ static uint32_t suck_number(const uint8_t **src, const uint8_t *srcend)
process_terms - process input/output matrix
-------------------------------------------------*/
-static bool process_terms(jed_data *data, const uint8_t **src, const uint8_t *srcend, parse_info *pinfo)
+static bool process_terms(jed_data *data, util::random_read &src, uint8_t ch, parse_info *pinfo)
{
uint32_t curinput = 0;
uint32_t curoutput = 0;
bool outputs = false;
- // symbols for 0, 1, dont_care, no_meaning
- // PLA format documentation also describes them as simply 0, 1, 2, 3
- static const char symbols[] = { "01-~" };
-
- while (*src < srcend && **src != '.' && **src != '#')
+ while (ch != '.' && ch != '#')
{
if (!outputs)
{
// and-matrix
- if (strrchr(symbols, **src))
+ switch (ch)
+ {
+ case '0':
curinput++;
+ jed_set_fuse(data, data->numfuses++, 0);
+ jed_set_fuse(data, data->numfuses++, 1);
+ if (LOG_PARSE) printf("01");
+ break;
- switch (**src)
- {
- case '0':
- jed_set_fuse(data, data->numfuses++, 0);
- jed_set_fuse(data, data->numfuses++, 1);
-
- if (LOG_PARSE) printf("01");
- break;
-
- case '1':
- jed_set_fuse(data, data->numfuses++, 1);
- jed_set_fuse(data, data->numfuses++, 0);
-
- if (LOG_PARSE) printf("10");
- break;
-
- // anything goes
- case '-':
- jed_set_fuse(data, data->numfuses++, 1);
- jed_set_fuse(data, data->numfuses++, 1);
-
- if (LOG_PARSE) printf("11");
- break;
-
- // this product term is inhibited
- case '~':
- jed_set_fuse(data, data->numfuses++, 0);
- jed_set_fuse(data, data->numfuses++, 0);
-
- if (LOG_PARSE) printf("00");
- break;
-
- case ' ': case '\t':
- if (curinput > 0)
- {
- outputs = true;
- if (LOG_PARSE) printf(" ");
- }
- break;
-
- default:
- break;
+ case '1':
+ curinput++;
+ jed_set_fuse(data, data->numfuses++, 1);
+ jed_set_fuse(data, data->numfuses++, 0);
+ if (LOG_PARSE) printf("10");
+ break;
+
+ // anything goes
+ case '-':
+ curinput++;
+ jed_set_fuse(data, data->numfuses++, 1);
+ jed_set_fuse(data, data->numfuses++, 1);
+ if (LOG_PARSE) printf("11");
+ break;
+
+ // this product term is inhibited
+ case '~':
+ curinput++;
+ jed_set_fuse(data, data->numfuses++, 0);
+ jed_set_fuse(data, data->numfuses++, 0);
+ if (LOG_PARSE) printf("00");
+ break;
+
+ case ' ': case '\t':
+ if (curinput > 0)
+ {
+ outputs = true;
+ if (LOG_PARSE) printf(" ");
+ }
+ break;
+
+ default:
+ break;
}
}
else
{
// or-matrix
- if (strrchr(symbols, **src))
+ switch (ch)
{
+ case '1':
curoutput++;
- if (**src == '1')
- {
- jed_set_fuse(data, data->numfuses++, 0);
- if (LOG_PARSE) printf("0");
- }
- else
- {
- // write 1 for anything else
- jed_set_fuse(data, data->numfuses++, 1);
- if (LOG_PARSE) printf("1");
- }
+ jed_set_fuse(data, data->numfuses++, 0);
+ if (LOG_PARSE) printf("0");
+ break;
+
+ // write 1 for anything else
+ case '0': case '-': case '~':
+ curoutput++;
+ jed_set_fuse(data, data->numfuses++, 1);
+ if (LOG_PARSE) printf("1");
+ break;
+
+ default:
+ break;
}
}
- if (iscrlf(**src) && outputs)
+ if (iscrlf(ch) && outputs)
{
outputs = false;
if (LOG_PARSE) printf("\n");
@@ -183,9 +192,14 @@ static bool process_terms(jed_data *data, const uint8_t **src, const uint8_t *sr
curoutput = 0;
}
- (*src)++;
+ auto const [err, actual] = read(src, &ch, 1);
+ if (err)
+ return false;
+ if (actual != 1)
+ return true;
}
+ src.seek(-1, SEEK_CUR);
return true;
}
@@ -195,7 +209,7 @@ static bool process_terms(jed_data *data, const uint8_t **src, const uint8_t *sr
process_field - process a single field
-------------------------------------------------*/
-static bool process_field(jed_data *data, const uint8_t **src, const uint8_t *srcend, parse_info *pinfo)
+static bool process_field(jed_data *data, util::random_read &src, parse_info *pinfo)
{
// valid keywords
static const char *const keywords[] = { "i", "o", "p", "phase", "e", "\0" };
@@ -212,15 +226,18 @@ static bool process_field(jed_data *data, const uint8_t **src, const uint8_t *sr
// find keyword
char dest[0x10];
- memset(dest, 0, ARRAY_LENGTH(dest));
- const uint8_t *seek = *src;
+ memset(dest, 0, sizeof(dest));
int destptr = 0;
- while (seek < srcend && isalpha(*seek) && destptr < ARRAY_LENGTH(dest) - 1)
+ uint8_t seek;
+ while (true)
{
- dest[destptr] = tolower(*seek);
- seek++;
- destptr++;
+ auto const [err, actual] = read(src, &seek, 1);
+ if (err || (actual != 1) || !isalpha(seek))
+ break;
+ dest[destptr++] = tolower(seek);
+ if (destptr == sizeof(dest))
+ break;
}
uint8_t find = 0;
@@ -230,58 +247,58 @@ static bool process_field(jed_data *data, const uint8_t **src, const uint8_t *sr
if (find == KW_INVALID)
return false;
- (*src) += strlen(keywords[find]);
-
// handle it
switch (find)
{
- // number of inputs
- case KW_INPUTS:
- pinfo->inputs = suck_number(src, srcend);
- if (pinfo->inputs == 0 || pinfo->inputs >= (JED_MAX_FUSES/2))
- return false;
-
- if (LOG_PARSE) printf("Inputs: %u\n", pinfo->inputs);
- break;
-
- // number of outputs
- case KW_OUTPUTS:
- pinfo->outputs = suck_number(src, srcend);
- if (pinfo->outputs == 0 || pinfo->outputs >= (JED_MAX_FUSES/2))
- return false;
-
- if (LOG_PARSE) printf("Outputs: %u\n", pinfo->outputs);
- break;
-
- // number of product terms (optional)
- case KW_TERMS:
- pinfo->terms = suck_number(src, srcend);
- if (pinfo->terms == 0 || pinfo->terms >= (JED_MAX_FUSES/2))
- return false;
-
- if (LOG_PARSE) printf("Terms: %u\n", pinfo->terms);
- break;
-
- // output polarity (optional)
- case KW_PHASE:
- if (LOG_PARSE) printf("Phase...\n");
- while (*src < srcend && !iscrlf(**src) && pinfo->xorptr < (JED_MAX_FUSES/2))
+ // number of inputs
+ case KW_INPUTS:
+ pinfo->inputs = suck_number(src);
+ if (pinfo->inputs == 0 || pinfo->inputs >= (JED_MAX_FUSES/2))
+ return false;
+
+ if (LOG_PARSE) printf("Inputs: %u\n", pinfo->inputs);
+ break;
+
+ // number of outputs
+ case KW_OUTPUTS:
+ pinfo->outputs = suck_number(src);
+ if (pinfo->outputs == 0 || pinfo->outputs >= (JED_MAX_FUSES/2))
+ return false;
+
+ if (LOG_PARSE) printf("Outputs: %u\n", pinfo->outputs);
+ break;
+
+ // number of product terms (optional)
+ case KW_TERMS:
+ pinfo->terms = suck_number(src);
+ if (pinfo->terms == 0 || pinfo->terms >= (JED_MAX_FUSES/2))
+ return false;
+
+ if (LOG_PARSE) printf("Terms: %u\n", pinfo->terms);
+ break;
+
+ // output polarity (optional)
+ case KW_PHASE:
+ if (LOG_PARSE) printf("Phase...\n");
+ while (true)
+ {
+ auto const [err, actual] = read(src, &seek, 1);
+ if (err || (actual != 1) || iscrlf(seek) || (pinfo->xorptr >= (JED_MAX_FUSES / 2)))
+ break;
+ if (seek == '0' || seek == '1')
{
- if (**src == '0' || **src == '1')
- {
- // 0 is negative
- if (**src == '0')
- pinfo->xorval[pinfo->xorptr/32] |= 1 << (pinfo->xorptr & 31);
- pinfo->xorptr++;
- }
- (*src)++;
+ // 0 is negative
+ if (seek == '0')
+ pinfo->xorval[pinfo->xorptr/32] |= 1 << (pinfo->xorptr & 31);
+ pinfo->xorptr++;
}
- break;
+ }
+ break;
- // end of file (optional)
- case KW_END:
- if (LOG_PARSE) printf("End of file\n");
- break;
+ // end of file (optional)
+ case KW_END:
+ if (LOG_PARSE) printf("End of file\n");
+ break;
}
return true;
@@ -295,54 +312,57 @@ static bool process_field(jed_data *data, const uint8_t **src, const uint8_t *sr
-------------------------------------------------*/
/**
- * @fn int pla_parse(const void *data, size_t length, jed_data *result)
+ * @fn int pla_parse(util::random_read &src, jed_data *result)
*
* @brief Pla parse.
*
- * @param data The data.
- * @param length The length.
+ * @param src The source file.
* @param [out] result If non-null, the result.
*
* @return An int.
*/
-int pla_parse(const void *data, size_t length, jed_data *result)
+int pla_parse(util::random_read &src, jed_data *result)
{
- const uint8_t *src = (const uint8_t *)data;
- const uint8_t *srcend = src + length;
-
parse_info pinfo;
memset(&pinfo, 0, sizeof(pinfo));
result->numfuses = 0;
memset(result->fusemap, 0, sizeof(result->fusemap));
- while (src < srcend)
+ while (true)
{
- switch (*src)
+ std::error_condition err;
+ size_t actual;
+ uint8_t ch;
+ std::tie(err, actual) = read(src, &ch, 1);
+ if (err || (actual != 1))
+ break;
+ switch (ch)
{
- // comment line
- case '#':
- while (src < srcend && !iscrlf(*src))
- src++;
- break;
+ // comment line
+ case '#':
+ do
+ {
+ std::tie(err, actual) = read(src, &ch, 1);
+ }
+ while (!err && (actual == 1) && !iscrlf(ch));
+ break;
- // keyword
- case '.':
- src++;
- if (!process_field(result, &src, srcend, &pinfo))
- return JEDERR_INVALID_DATA;
- break;
+ // keyword
+ case '.':
+ if (!process_field(result, src, &pinfo))
+ return JEDERR_INVALID_DATA;
+ break;
- // terms
- case '0': case '1': case '-': case '~':
- if (!process_terms(result, &src, srcend, &pinfo))
- return JEDERR_INVALID_DATA;
- break;
+ // terms
+ case '0': case '1': case '-': case '~':
+ if (!process_terms(result, src, ch, &pinfo))
+ return JEDERR_INVALID_DATA;
+ break;
- default:
- src++;
- break;
+ default:
+ break;
}
}
diff --git a/src/lib/util/plaparse.h b/src/lib/util/plaparse.h
index b0d3cbbe4e3..a5453145e31 100644
--- a/src/lib/util/plaparse.h
+++ b/src/lib/util/plaparse.h
@@ -1,10 +1,10 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles, hap
+// copyright-holders:Aaron Giles, Curt Coder, hap
/***************************************************************************
plaparse.h
- Parser for Berkeley standard PLA files into raw fusemaps.
+ Parser for Berkeley standard (aka Espresso) PLA files into raw fusemaps.
***************************************************************************/
@@ -13,7 +13,6 @@
#pragma once
-#include "osdcore.h"
#include "jedparse.h"
@@ -22,6 +21,6 @@
***************************************************************************/
/* parse a file (read into memory) into a jed_data structure */
-int pla_parse(const void *data, size_t length, jed_data *result);
+int pla_parse(util::random_read &src, jed_data *result);
#endif // MAME_UTIL_PLAPARSE_H
diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp
index 6b13f4f9593..ce28a866604 100644
--- a/src/lib/util/png.cpp
+++ b/src/lib/util/png.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles, Vas Crabb
/*********************************************************************
- png.c
+ png.cpp
PNG reading functions.
@@ -10,40 +10,24 @@
#include "png.h"
+#include "ioprocs.h"
#include "unicode.h"
+#include "osdcomm.h"
+
#include <zlib.h>
#include <algorithm>
#include <cassert>
-#include <cstdint>
-#include <cstring>
+#include <cerrno>
#include <cmath>
+#include <cstdlib>
+#include <cstring>
#include <new>
-
-#include <math.h>
-#include <stdlib.h>
-
-
-
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
-
-static const int samples[] = { 1, 0, 3, 1, 2, 0, 4 };
-
-
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-static inline int compute_rowbytes(const png_info &pnginfo)
-{
- return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8;
-}
+#include <tuple>
+namespace util {
/***************************************************************************
GENERAL FUNCTIONS
@@ -54,7 +38,7 @@ static inline int compute_rowbytes(const png_info &pnginfo)
pnginfo structure
-------------------------------------------------*/
-void png_info::free_data()
+void png_info::free_data() noexcept
{
textlist.clear();
palette.reset();
@@ -66,6 +50,12 @@ void png_info::free_data()
namespace {
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+constexpr int samples[] = { 1, 0, 3, 1, 2, 0, 4 };
+
constexpr std::uint8_t PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
#define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A"
@@ -101,13 +91,51 @@ constexpr std::uint8_t PNG_PF_Average = 3;
constexpr std::uint8_t PNG_PF_Paeth = 4;
-inline uint8_t fetch_8bit(uint8_t const *v) { return *v; }
-inline uint16_t fetch_16bit(uint8_t const *v) { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); }
-inline uint32_t fetch_32bit(uint8_t const *v) { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); }
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+inline int compute_rowbytes(const png_info &pnginfo) noexcept { return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; }
+
+inline uint8_t fetch_8bit(uint8_t const *v) noexcept { return *v; }
+inline uint16_t fetch_16bit(uint8_t const *v) noexcept { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); }
+inline uint32_t fetch_32bit(uint8_t const *v) noexcept { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); }
+
+inline void put_8bit(uint8_t *v, uint8_t data) noexcept { *v = data; }
+inline void put_16bit(uint8_t *v, uint16_t data) noexcept { *reinterpret_cast<uint16_t *>(v) = big_endianize_int16(data); }
+inline void put_32bit(uint8_t *v, uint32_t data) noexcept { *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;
+ }
+};
-inline void put_8bit(uint8_t *v, uint8_t data) { *v = data; }
-inline void put_16bit(uint8_t *v, uint16_t data) { *reinterpret_cast<uint16_t *>(v) = big_endianize_int16(data); }
-inline void put_32bit(uint8_t *v, uint32_t data) { *reinterpret_cast<uint32_t *>(v) = big_endianize_int32(data); }
+png_category_impl const f_png_category_instance;
class png_private
@@ -122,21 +150,21 @@ private:
struct image_data_chunk
{
- image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) : length(l), data(std::move(d)) { }
+ image_data_chunk(std::uint32_t l, std::unique_ptr<std::uint8_t []> &&d) noexcept : length(l), data(std::move(d)) { }
std::uint32_t length;
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) noexcept
{
// do some basic checks for unsupported images
- if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
- return PNGERR_UNSUPPORTED_FORMAT; // unknown colour format
+ if (!pnginfo.bit_depth || (std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
+ return png_error::UNSUPPORTED_FORMAT; // unknown colour format
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
if ((3 == pnginfo.color_type) && (!pnginfo.num_palette || !pnginfo.palette))
- return PNGERR_FILE_CORRUPT; // indexed colour with no palette
+ return png_error::FILE_CORRUPT; // indexed colour with no palette
// calculate the offset for each pass of the interlace
unsigned const pass_count(get_pass_count());
@@ -145,14 +173,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 PNGERR_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 = PNGERR_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) && (PNGERR_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));
@@ -161,7 +190,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) && (PNGERR_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++);
@@ -172,17 +201,17 @@ private:
}
// if we errored, free the image data
- if (error != PNGERR_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) noexcept
{
// only deflate is permitted
if (0 != pnginfo.compression_method)
- return PNGERR_DECOMPRESS_ERROR;
+ return png_error::DECOMPRESS_ERROR;
// allocate zlib stream
z_stream stream;
@@ -194,14 +223,18 @@ private:
stream.avail_in = 0;
stream.next_in = Z_NULL;
zerr = inflateInit(&stream);
- if (Z_OK != zerr)
- return PNGERR_DECOMPRESS_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 if (Z_OK != zerr)
+ return png_error::DECOMPRESS_ERROR;
// decompress IDAT blocks
stream.next_out = pnginfo.image.get();
stream.avail_out = expected;
stream.avail_in = 0;
- std::list<image_data_chunk>::const_iterator it = idata.begin();
+ auto it = idata.begin();
while ((idata.end() != it) && ((Z_OK == zerr) || (Z_BUF_ERROR == zerr)) && !stream.avail_in)
{
stream.avail_in = it->length;
@@ -217,28 +250,28 @@ 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 PNGERR_NONE;
+ return std::error_condition();
else
- return PNGERR_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) noexcept
{
if (0 != pnginfo.filter_method)
- return PNGERR_UNKNOWN_FILTER;
+ return png_error::UNKNOWN_FILTER;
switch (type)
{
case PNG_PF_None: // no filter, just copy
std::copy_n(src, rowbytes, dst);
- return PNGERR_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 PNGERR_NONE;
+ return std::error_condition();
case PNG_PF_Up: // UP = pixel above
if (dstprev)
@@ -250,7 +283,7 @@ private:
{
std::copy_n(src, rowbytes, dst);
}
- return PNGERR_NONE;
+ return std::error_condition();
case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel
if (dstprev)
@@ -267,7 +300,7 @@ private:
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
*dst = *src + (dst[-bpp] >> 1);
}
- return PNGERR_NONE;
+ return std::error_condition();
case PNG_PF_Paeth: // PAETH = special filter
for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst)
@@ -281,20 +314,20 @@ private:
int32_t const dc(std::abs(prediction - pc));
*dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc);
}
- return PNGERR_NONE;
+ return std::error_condition();
default: // unknown filter type
- return PNGERR_UNKNOWN_FILTER;
+ 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) noexcept
{
switch (type)
{
case PNG_CN_IHDR: // image header
if (13 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.width = fetch_32bit(&data[0]);
pnginfo.height = fetch_32bit(&data[4]);
pnginfo.bit_depth = fetch_8bit(&data[8]);
@@ -307,31 +340,31 @@ private:
case PNG_CN_PLTE: // palette
pnginfo.num_palette = length / 3;
if ((length % 3) || ((3 == pnginfo.color_type) && ((1 << pnginfo.bit_depth) < pnginfo.num_palette)))
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.palette = std::move(data);
break;
case PNG_CN_tRNS: // transparency information
if (((0 == pnginfo.color_type) && (2 > length)) || ((2 == pnginfo.color_type) && (6 > length)))
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.num_trans = length;
pnginfo.trans = std::move(data);
break;
case PNG_CN_IDAT: // image data
try { idata.emplace_back(length, std::move(data)); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
break;
case PNG_CN_gAMA: // gamma
if (4 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0;
break;
case PNG_CN_pHYs: // physical information
if (9 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.xres = fetch_32bit(&data[0]);
pnginfo.yres = fetch_32bit(&data[4]);
pnginfo.resolution_unit = fetch_8bit(&data[8]);
@@ -361,25 +394,25 @@ private:
}
catch (std::bad_alloc const &)
{
- return PNGERR_OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
break;
/* anything else */
default:
if ((type & 0x20000000) == 0)
- return PNGERR_UNKNOWN_CHUNK;
+ return png_error::UNKNOWN_CHUNK;
break;
}
- return PNGERR_NONE;
+ return std::error_condition();
}
- unsigned get_pass_count() const
+ unsigned get_pass_count() const noexcept
{
return (1 == pnginfo.interlace_method) ? 7 : 1;
}
- std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const
+ std::pair<uint32_t, uint32_t> get_pass_dimensions(unsigned pass) const noexcept
{
if (0 == pnginfo.interlace_method)
return std::make_pair(pnginfo.width, pnginfo.height);
@@ -387,110 +420,130 @@ private:
return std::make_pair((pnginfo.width + ADAM7_X_BIAS[pass]) >> ADAM7_X_SHIFT[pass], (pnginfo.height + ADAM7_Y_BIAS[pass]) >> ADAM7_Y_SHIFT[pass]);
}
- std::uint32_t get_pass_bytes(unsigned pass) const
+ std::uint32_t get_pass_bytes(unsigned pass) const noexcept
{
return get_pass_bytes(pass, pnginfo.bit_depth);
}
- std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const
+ std::uint32_t get_pass_bytes(unsigned pass, uint8_t bit_depth) const noexcept
{
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
return (get_row_bytes(dimensions.first, bit_depth) + 1) * dimensions.second;
}
- std::uint32_t get_row_bytes(std::uint32_t width) const
+ std::uint32_t get_row_bytes(std::uint32_t width) const noexcept
{
return get_row_bytes(width, pnginfo.bit_depth);
}
- std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const
+ std::uint32_t get_row_bytes(std::uint32_t width, uint8_t bit_depth) const noexcept
{
return ((width * samples[pnginfo.color_type] * bit_depth) + 7) >> 3;
}
- std::uint32_t get_bytes_per_pixel() const
+ std::uint32_t get_bytes_per_pixel() const noexcept
{
return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3;
}
- static png_error read_chunk(util::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) noexcept
{
+ 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)
- return PNGERR_FILE_TRUNCATED;
+ // fetch the length of this chunk
+ std::tie(err, actual) = read(fp, tempbuff, 4);
+ 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)
- return PNGERR_FILE_TRUNCATED;
+ // fetch the type of this chunk
+ std::tie(err, actual) = read(fp, tempbuff, 4);
+ if (err)
+ return err;
+ else if (4 != actual)
+ return png_error::FILE_TRUNCATED;
type = fetch_32bit(tempbuff);
- /* stop when we hit an IEND chunk */
+ // stop when we hit an IEND chunk
if (type == PNG_CN_IEND)
- return PNGERR_NONE;
+ return std::error_condition();
- /* start the CRC with the chunk type (but not the length) */
+ // start the CRC with the chunk type (but not the length)
std::uint32_t crc = crc32(0, tempbuff, 4);
- /* read the chunk itself into an allocated memory buffer */
+ // read the chunk itself into an allocated memory buffer
if (length)
{
- /* allocate memory for this chunk */
- try { data.reset(new std::uint8_t [length]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
-
- /* read the data from the file */
- if (fp.read(data.get(), length) != length)
+ // allocate memory and read the data from the file
+ std::tie(err, data, actual) = read(fp, length);
+ if (err)
{
data.reset();
- return PNGERR_FILE_TRUNCATED;
+ return err;
+ }
+ else if (length != actual)
+ {
+ data.reset();
+ return png_error::FILE_TRUNCATED;
}
- /* update the CRC */
+ // update the CRC
crc = crc32(crc, data.get(), length);
}
- /* read the CRC */
- if (fp.read(tempbuff, 4) != 4)
+ // read the CRC
+ std::tie(err, actual) = read(fp, tempbuff, 4);
+ if (err)
{
data.reset();
- return PNGERR_FILE_TRUNCATED;
+ return err;
+ }
+ else if (4 != actual)
+ {
+ data.reset();
+ return png_error::FILE_TRUNCATED;
}
std::uint32_t const chunk_crc = fetch_32bit(tempbuff);
- /* validate the CRC */
+ // validate the CRC
if (crc != chunk_crc)
{
data.reset();
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
}
- return PNGERR_NONE;
+ return std::error_condition();
}
png_info & pnginfo;
public:
- png_private(png_info &info) : pnginfo(info)
+ png_private(png_info &info) noexcept : pnginfo(info)
{
}
- png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const
+ std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) const noexcept
{
// do some basic checks for unsupported images
if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8))
- return PNGERR_UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first
- if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
- return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format
+ return png_error::UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first
+ if ((std::size(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
+ return png_error::UNSUPPORTED_FORMAT; // unknown colour sample format
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth))
- return PNGERR_UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp
+ return png_error::UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp
- // everything looks sane, allocate the bitmap and deinterlace into it
+ // try to allocate the bitmap
bitmap.allocate(pnginfo.width, pnginfo.height);
+ if (!bitmap.valid())
+ return std::errc::not_enough_memory;
+
+ // everything looks sane, deinterlace into the bitmap
std::uint8_t accumalpha(0xff);
uint32_t const bps(pnginfo.bit_depth >> 3);
uint32_t const bpp(bps * samples[pnginfo.color_type]);
@@ -519,7 +572,7 @@ public:
accumalpha &= alpha;
std::uint16_t const paloffs(std::uint16_t(*src) * 3);
rgb_t const pix(alpha, pnginfo.palette[paloffs], pnginfo.palette[paloffs + 1], pnginfo.palette[paloffs + 2]);
- bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
+ bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
}
}
}
@@ -537,7 +590,7 @@ public:
std::uint8_t const a_val((pnginfo.trans && (transpen == i_val)) ? 0x00 : 0xff);
i_val >>= samp_shift;
accumalpha &= a_val;
- bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, i_val, i_val, i_val);
+ bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, i_val, i_val, i_val);
}
}
}
@@ -552,7 +605,7 @@ public:
{
accumalpha &= src[a];
rgb_t const pix(src[a], src[i], src[i], src[i]);
- bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
+ bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
}
}
}
@@ -578,7 +631,7 @@ public:
g_val >>= samp_shift;
b_val >>= samp_shift;
accumalpha &= a_val;
- bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, r_val, g_val, b_val);
+ bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = rgb_t(a_val, r_val, g_val, b_val);
}
}
}
@@ -595,7 +648,7 @@ public:
{
accumalpha &= src[a];
rgb_t const pix(src[a], src[r], src[g], src[b]);
- bitmap.pix32((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
+ bitmap.pix((y << y_shift) + y_offs, (x << x_shift) + x_offs) = pix;
}
}
}
@@ -603,22 +656,22 @@ public:
// set hasalpha flag and return
hasalpha = 0xffU != accumalpha;
- return PNGERR_NONE;
+ return std::error_condition();
}
- png_error expand_buffer_8bit()
+ std::error_condition expand_buffer_8bit() noexcept
{
// nothing to do if we're at 8 or greater already
if (pnginfo.bit_depth >= 8)
- return PNGERR_NONE;
+ return std::error_condition();
// do some basic checks for unsupported images
if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth))
- return PNGERR_UNSUPPORTED_FORMAT; // bit depth must be a factor of eight
+ return png_error::UNSUPPORTED_FORMAT; // bit depth must be a factor of eight
if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type))
- return PNGERR_UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour
+ return png_error::UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
// calculate the offset for each pass of the interlace on the input and output
unsigned const pass_count(get_pass_count());
@@ -631,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 PNGERR_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);
@@ -687,13 +740,13 @@ public:
pnginfo.image = std::move(outbuf);
pnginfo.bit_depth = 8;
- return PNGERR_NONE;
+ return std::error_condition();
}
- png_error read_file(util::core_file &fp)
+ std::error_condition read_file(read_stream &fp) noexcept
{
// initialize the data structures
- png_error error = PNGERR_NONE;
+ std::error_condition error;
pnginfo.reset();
std::list<image_data_chunk> idata;
@@ -701,13 +754,13 @@ public:
error = verify_header(fp);
// loop until we hit an IEND chunk
- while (PNGERR_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 (PNGERR_NONE == error)
+ if (!error)
{
if (chunk_type == PNG_CN_IEND)
break; // stop when we hit an IEND chunk
@@ -717,29 +770,32 @@ public:
}
// finish processing the image
- if (PNGERR_NONE == error)
+ if (!error)
error = process(idata);
// if we have an error, free all the output data
- if (error != PNGERR_NONE)
+ if (error)
pnginfo.reset();
return error;
}
- static png_error verify_header(util::core_file &fp)
+ static std::error_condition verify_header(read_stream &fp) noexcept
{
- EQUIVALENT_ARRAY(PNG_SIGNATURE, std::uint8_t) signature;
+ std::uint8_t signature[sizeof(PNG_SIGNATURE)];
// read 8 bytes
- if (fp.read(signature, sizeof(signature)) != sizeof(signature))
- return PNGERR_FILE_TRUNCATED;
+ auto const [err, actual] = read(fp, signature, sizeof(signature));
+ 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 PNGERR_BAD_SIGNATURE;
+ return png_error::BAD_SIGNATURE;
- return PNGERR_NONE;
+ return std::error_condition();
}
};
@@ -760,7 +816,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7];
core stream
-------------------------------------------------*/
-png_error png_info::verify_header(util::core_file &fp)
+std::error_condition png_info::verify_header(read_stream &fp) noexcept
{
return png_private::verify_header(fp);
}
@@ -770,7 +826,7 @@ png_error png_info::verify_header(util::core_file &fp)
read_file - read a PNG from a core stream
-------------------------------------------------*/
-png_error png_info::read_file(util::core_file &fp)
+std::error_condition png_info::read_file(read_stream &fp) noexcept
{
return png_private(*this).read_file(fp);
}
@@ -781,20 +837,20 @@ png_error png_info::read_file(util::core_file &fp)
bitmap
-------------------------------------------------*/
-png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap)
+std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap) noexcept
{
- png_error result;
+ std::error_condition result;
png_info pnginfo;
png_private png(pnginfo);
// read the PNG data
result = png.read_file(fp);
- if (PNGERR_NONE != result)
+ if (result)
return result;
// resample to 8bpp if necessary
result = png.expand_buffer_8bit();
- if (PNGERR_NONE != result)
+ if (result)
{
pnginfo.free_data();
return result;
@@ -811,7 +867,7 @@ png_error png_read_bitmap(util::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) noexcept
{
return png_private(*this).copy_to_bitmap(bitmap, hasalpha);
}
@@ -822,7 +878,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() noexcept
{
return png_private(*this).expand_buffer_8bit();
}
@@ -837,40 +893,38 @@ png_error png_info::expand_buffer_8bit()
add_text - add a text entry to the png_info
-------------------------------------------------*/
-png_error png_info::add_text(const char *keyword, const char *text)
+std::error_condition png_info::add_text(std::string_view keyword, std::string_view text) noexcept
{
// apply rules to keyword
char32_t prev(0);
std::size_t cnt(0);
- char const *const kwend(keyword + std::strlen(keyword));
- for (char const *ptr = keyword; kwend > ptr; )
+ for (std::string_view kw = keyword; !kw.empty(); )
{
char32_t ch;
- int const len(uchar_from_utf8(&ch, ptr, kwend - ptr));
- if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (keyword == ptr)) && (32 == ch)))
- return PNGERR_UNSUPPORTED_FORMAT;
+ int const len(uchar_from_utf8(&ch, kw));
+ if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (0 == cnt)) && (32 == ch)))
+ return png_error::UNSUPPORTED_FORMAT;
prev = ch;
++cnt;
- ptr += len;
+ kw.remove_prefix(len);
}
if ((32 == prev) || (1 > cnt) || (79 < cnt))
- return PNGERR_UNSUPPORTED_FORMAT;
+ return png_error::UNSUPPORTED_FORMAT;
// apply rules to text
- char const *const textend(text + std::strlen(text));
- for (char const *ptr = text; textend > ptr; )
+ for (std::string_view tx = text; !tx.empty(); )
{
char32_t ch;
- int const len(uchar_from_utf8(&ch, ptr, textend - ptr));
+ int const len(uchar_from_utf8(&ch, tx));
if ((0 >= len) || (1 > ch) || (255 < ch))
- return PNGERR_UNSUPPORTED_FORMAT;
- ptr += len;
+ return png_error::UNSUPPORTED_FORMAT;
+ tx.remove_prefix(len);
}
// allocate a new text element
- try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword, kwend), std::forward_as_tuple(text, textend)); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
- return PNGERR_NONE;
+ try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword), std::forward_as_tuple(text)); }
+ catch (std::bad_alloc const &) { return std::errc::not_enough_memory; }
+ return std::error_condition();
}
@@ -879,34 +933,38 @@ png_error png_info::add_text(const char *keyword, const char *text)
the given file
-------------------------------------------------*/
-static png_error write_chunk(util::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) noexcept
{
- uint8_t tempbuff[8];
- uint32_t crc;
+ std::error_condition err;
+ std::uint8_t tempbuff[8];
+ std::uint32_t crc;
- /* stuff the length/type into the buffer */
+ // stuff the length/type into the buffer
put_32bit(tempbuff + 0, length);
put_32bit(tempbuff + 4, type);
crc = crc32(0, tempbuff + 4, 4);
- /* write that data */
- if (fp.write(tempbuff, 8) != 8)
- return PNGERR_FILE_ERROR;
+ // write that data
+ std::tie(err, std::ignore) = write(fp, tempbuff, 8);
+ if (err)
+ return err;
- /* append the actual data */
+ // append the actual data
if (length > 0)
{
- if (fp.write(data, length) != length)
- return PNGERR_FILE_ERROR;
+ std::tie(err, std::ignore) = write(fp, data, length);
+ if (err)
+ return err;
crc = crc32(crc, data, length);
}
- /* write the CRC */
+ // write the CRC
put_32bit(tempbuff, crc);
- if (fp.write(tempbuff, 4) != 4)
- return PNGERR_FILE_ERROR;
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
+ if (err)
+ return err;
- return PNGERR_NONE;
+ return std::error_condition();
}
@@ -915,33 +973,43 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t
chunk to the given file by deflating it
-------------------------------------------------*/
-static png_error write_deflated_chunk(util::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) noexcept
{
- 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::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 */
+ // stuff the length/type into the buffer
put_32bit(tempbuff + 0, length);
put_32bit(tempbuff + 4, type);
crc = crc32(0, tempbuff + 4, 4);
- /* write that data */
- if (fp.write(tempbuff, 8) != 8)
- return PNGERR_FILE_ERROR;
+ // write that data
+ std::tie(err, std::ignore) = write(fp, tempbuff, 8);
+ if (err)
+ return err;
- /* initialize the stream */
+ // 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)
- return PNGERR_COMPRESS_ERROR;
-
- /* now loop until we run out of data */
+ 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
for ( ; ; )
{
/* compress this chunk */
@@ -949,50 +1017,63 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
stream.avail_out = sizeof(tempbuff);
zerr = deflate(&stream, Z_FINISH);
- /* if there's data to write, do it */
+ // if there's data to write, do it
if (stream.avail_out < sizeof(tempbuff))
{
int bytes = sizeof(tempbuff) - stream.avail_out;
- if (fp.write(tempbuff, bytes) != bytes)
+ std::tie(err, std::ignore) = write(fp, tempbuff, bytes);
+ if (err)
{
deflateEnd(&stream);
- return PNGERR_FILE_ERROR;
+ return err;
}
crc = crc32(crc, tempbuff, bytes);
zlength += bytes;
}
- /* stop at the end of the stream */
+ // stop at the end of the stream
if (zerr == Z_STREAM_END)
break;
- /* other errors are fatal */
+ // other errors are fatal
if (zerr != Z_OK)
{
deflateEnd(&stream);
- return PNGERR_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) */
+ // clean up deflater
zerr = deflateEnd(&stream);
- if (zerr != Z_OK)
- return PNGERR_COMPRESS_ERROR;
-
- /* write the CRC */
+ 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 PNGERR_FILE_ERROR;
-
- /* seek back and update the length */
- fp.seek(lengthpos, SEEK_SET);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
+ if (err)
+ return err;
+
+ // seek back and update the length
+ err = fp.seek(lengthpos, SEEK_SET);
+ if (err)
+ return err;
put_32bit(tempbuff + 0, zlength);
- if (fp.write(tempbuff, 4) != 4)
- return PNGERR_FILE_ERROR;
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
+ if (err)
+ return err;
- /* return to the end */
- fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
- return PNGERR_NONE;
+ // return to the end
+ return fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
}
@@ -1001,26 +1082,24 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
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) noexcept
{
- int rowbytes;
- int x, y;
-
- /* set the common info */
+ // 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 PNGERR_OUT_OF_MEMORY; }
+ // allocate memory for the palette
+ pnginfo.palette.reset(new (std::nothrow) std::uint8_t [3 * 256]);
+ if (!pnginfo.palette)
+ return std::errc::not_enough_memory;
- /* build the palette */
+ // 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();
@@ -1028,30 +1107,27 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
pnginfo.palette[3 * x + 2] = color.b();
}
- /* allocate memory for the image */
- try
- {
- pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]);
- }
- catch (std::bad_alloc const &)
+ // allocate memory for the image
+ pnginfo.image.reset(new (std::nothrow) std::uint8_t [pnginfo.height * (rowbytes + 1)]);
+ if (!pnginfo.image)
{
pnginfo.palette.reset();
- return PNGERR_OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
- /* copy in the pixels, specifying a nullptr filter */
- for (y = 0; y < pnginfo.height; y++)
+ // copy in the pixels, specifying a nullptr filter
+ for (int y = 0; y < pnginfo.height; y++)
{
- uint16_t *src = reinterpret_cast<uint16_t *>(bitmap.raw_pixptr(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 */
+ // 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 PNGERR_NONE;
+ return std::error_condition();
}
@@ -1060,77 +1136,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) noexcept
{
- int alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
- int rowbytes;
- int x, y;
+ bool const alpha = (bitmap.format() == BITMAP_FORMAT_ARGB32);
- /* set the common info */
+ // 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 PNGERR_OUT_OF_MEMORY; }
+ // allocate memory for the image
+ 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++)
+ // copy in the pixels, specifying a nullptr filter
+ for (int y = 0; y < pnginfo.height; y++)
{
uint8_t *dst = &pnginfo.image[y * (rowbytes + 1)];
- /* store the filter byte, then copy the data */
+ // store the filter byte, then copy the data
*dst++ = 0;
- /* 16bpp palettized format */
if (bitmap.format() == BITMAP_FORMAT_IND16)
{
- uint16_t *src16 = reinterpret_cast<uint16_t *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ // 16bpp palettized format
+ uint16_t const *src16 = reinterpret_cast<uint16_t const *>(bitmap.raw_pixptr(y));
+ 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)
{
- uint32_t *src32 = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ // 32-bit RGB direct
+ uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
+ 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)
{
- uint32_t *src32 = reinterpret_cast<uint32_t *>(bitmap.raw_pixptr(y));
- for (x = 0; x < pnginfo.width; x++)
+ // 32-bit ARGB direct
+ uint32_t const *src32 = reinterpret_cast<uint32_t const *>(bitmap.raw_pixptr(y));
+ 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
- return PNGERR_UNSUPPORTED_FORMAT;
+ {
+ // unsupported format
+ return png_error::UNSUPPORTED_FORMAT;
+ }
}
- return PNGERR_NONE;
+ return std::error_condition();
}
@@ -1139,17 +1213,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(util::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) noexcept
{
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 != PNGERR_NONE)
+ if (error)
return error;
// if we wanted to get clever and do filtering, we would do it here
@@ -1163,18 +1237,18 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
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 != PNGERR_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 != PNGERR_NONE)
+ if (error)
return error;
- // write a single IDAT chunk */
+ // write a single IDAT chunk
error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1));
- if (error != PNGERR_NONE)
+ if (error)
return error;
// write TEXT chunks
@@ -1182,38 +1256,36 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
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 PNGERR_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
- char const *const kwend(text.first.c_str() + text.first.length());
- for (char const *src = text.first.c_str(); kwend > src; ++dst)
+ for (std::string_view src = text.first; !src.empty(); ++dst)
{
char32_t ch;
- int const len(uchar_from_utf8(&ch, src, kwend - src));
+ int const len(uchar_from_utf8(&ch, src));
if (0 >= len)
break;
*dst = std::uint8_t(ch);
- src += len;
+ src.remove_prefix(len);
}
- // NUL separator between keword and text
+ // NUL separator between keyword and text
*dst++ = 0;
// convert text to ISO-8859-1
- char const *const textend(text.second.c_str() + text.second.length());
- for (char const *src = text.second.c_str(); textend > src; ++dst)
+ for (std::string_view src = text.second; !src.empty(); ++dst)
{
char32_t ch;
- int const len(uchar_from_utf8(&ch, src, textend - src));
+ int const len(uchar_from_utf8(&ch, src));
if (0 >= len)
break;
*dst = std::uint8_t(ch);
- src += len;
+ src.remove_prefix(len);
}
error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]);
- if (error != PNGERR_NONE)
+ if (error)
return error;
}
@@ -1222,18 +1294,19 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
}
-png_error png_write_bitmap(util::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) noexcept
{
// 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 PNGERR_FILE_ERROR;
+ auto const [err, written] = write(fp, PNG_SIGNATURE, sizeof(PNG_SIGNATURE));
+ if (err)
+ return err;
- /* write the rest of the PNG data */
+ // write the rest of the PNG data
return write_png_stream(fp, *info, bitmap, palette_length, palette);
}
@@ -1245,25 +1318,13 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &
********************************************************************************/
-/**
- * @fn png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate)
- *
- * @brief Mng capture start.
- *
- * @param [in,out] fp If non-null, the fp.
- * @param [in,out] bitmap The bitmap.
- * @param rate The rate.
- *
- * @return A png_error.
- */
-
-png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate)
+std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate) noexcept
{
- uint8_t mhdr[28];
-
- if (fp.write(MNG_Signature, 8) != 8)
- return PNGERR_FILE_ERROR;
+ auto const [err, written] = write(fp, MNG_Signature, 8);
+ if (err)
+ return err;
+ uint8_t mhdr[28];
memset(mhdr, 0, 28);
put_32bit(mhdr + 0, bitmap.width());
put_32bit(mhdr + 4, bitmap.height());
@@ -1272,36 +1333,27 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate)
return write_chunk(fp, mhdr, MNG_CN_MHDR, 28);
}
-/**
- * @fn png_error mng_capture_frame(util::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(util::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) noexcept
{
return write_png_stream(fp, info, bitmap, palette_length, palette);
}
-/**
- * @fn png_error mng_capture_stop(util::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(util::core_file &fp)
+
+std::error_condition mng_capture_stop(random_write &fp) noexcept
{
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 ff14ff5f674..2f9b7ac29cb 100644
--- a/src/lib/util/png.h
+++ b/src/lib/util/png.h
@@ -14,36 +14,39 @@
#pragma once
#include "bitmap.h"
-#include "corefile.h"
-#include "osdcore.h"
+#include "utilfwd.h"
+#include <cstdint>
#include <list>
#include <memory>
#include <string>
+#include <string_view>
+#include <system_error>
#include <utility>
+namespace util {
/***************************************************************************
CONSTANTS
***************************************************************************/
/* Error types */
-enum png_error
+enum class png_error : int
{
- PNGERR_NONE,
- PNGERR_OUT_OF_MEMORY,
- PNGERR_UNKNOWN_FILTER,
- PNGERR_FILE_ERROR,
- PNGERR_BAD_SIGNATURE,
- PNGERR_DECOMPRESS_ERROR,
- PNGERR_FILE_TRUNCATED,
- PNGERR_FILE_CORRUPT,
- PNGERR_UNKNOWN_CHUNK,
- PNGERR_COMPRESS_ERROR,
- PNGERR_UNSUPPORTED_FORMAT
+ UNKNOWN_FILTER = 1,
+ BAD_SIGNATURE,
+ DECOMPRESS_ERROR,
+ FILE_TRUNCATED,
+ FILE_CORRUPT,
+ UNKNOWN_CHUNK,
+ COMPRESS_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()); }
+
/***************************************************************************
@@ -57,16 +60,16 @@ public:
~png_info() { free_data(); }
- png_error read_file(util::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) noexcept;
+ std::error_condition copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha) noexcept;
+ std::error_condition expand_buffer_8bit() noexcept;
- png_error add_text(char const *keyword, char const *text);
+ std::error_condition add_text(std::string_view keyword, std::string_view text) noexcept;
- void free_data();
- void reset() { free_data(); operator=(png_info()); }
+ void free_data() noexcept;
+ void reset() noexcept { free_data(); operator=(png_info()); }
- static png_error verify_header(util::core_file &fp);
+ static std::error_condition verify_header(read_stream &fp) noexcept;
std::unique_ptr<std::uint8_t []> image;
std::uint32_t width, height;
@@ -99,12 +102,21 @@ private:
FUNCTION PROTOTYPES
***************************************************************************/
-png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap);
+std::error_condition png_read_bitmap(read_stream &fp, bitmap_argb32 &bitmap) noexcept;
+
+std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) noexcept;
+
+std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate) noexcept;
+std::error_condition mng_capture_frame(random_write &fp, png_info &info, bitmap_t const &bitmap, int palette_length, rgb_t const *palette) noexcept;
+std::error_condition mng_capture_stop(random_write &fp) noexcept;
+
+} // namespace util
+
+
+namespace std {
-png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
+template <> struct is_error_condition_enum<util::png_error> : public std::true_type { };
-png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, double rate);
-png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
-png_error mng_capture_stop(util::core_file &fp);
+} // namespace std
#endif // MAME_LIB_UTIL_PNG_H
diff --git a/src/lib/util/pool.cpp b/src/lib/util/pool.cpp
deleted file mode 100644
index 606a7aea4b6..00000000000
--- a/src/lib/util/pool.cpp
+++ /dev/null
@@ -1,621 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
-/***************************************************************************
-
- pool.c
-
- Resource pool code
-
-***************************************************************************/
-
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdlib.h>
-#include "pool.h"
-#include "coreutil.h"
-
-
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-#define OBJECT_ENTRY_BLOCK 256
-#define POOL_HASH_SIZE 3797
-
-
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-/* an object type entry */
-struct objtype_entry
-{
- objtype_entry * next;
- uint32_t type;
- const char * friendly;
- void (*destructor)(void *, size_t);
-};
-
-
-/* an entry in a pool */
-struct object_entry
-{
- object_entry * next;
- object_entry * globalnext;
- object_entry * globalprev;
- objtype_entry * type;
- void * object;
- size_t size;
- const char * file;
- int line;
-};
-
-
-/* a block of entry items */
-struct object_entry_block
-{
- object_entry_block *next;
- object_entry entry[OBJECT_ENTRY_BLOCK];
-};
-
-
-/* the object pool itself */
-struct object_pool
-{
- object_entry * hashtable[POOL_HASH_SIZE];
- object_entry * globallist;
- object_entry * freelist;
- object_entry_block *blocklist;
- objtype_entry * typelist;
- void (*fail)(const char *message);
-};
-
-
-/* an iterator over objects in a pool */
-struct object_pool_iterator
-{
- object_pool * pool;
- object_type type;
- object_entry * last;
-};
-
-
-
-/***************************************************************************
- FUNCTION PROTOTYPES
-***************************************************************************/
-
-static void memory_destruct(void *object, size_t size);
-static void report_failure(object_pool *pool, const char *format, ...) ATTR_PRINTF(2,3);
-
-
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-/*-------------------------------------------------
- hash_object - compute the hash for a given
- object
--------------------------------------------------*/
-
-static inline int hash_object(void *object)
-{
- return ((size_t)object >> 4) % POOL_HASH_SIZE;
-}
-
-
-/*-------------------------------------------------
- get_object_type - return the type entry for
- a given object type
--------------------------------------------------*/
-
-static inline objtype_entry *get_object_type(object_pool *pool, object_type type)
-{
- objtype_entry *entry;
-
- for (entry = pool->typelist; entry != nullptr; entry = entry->next)
- if (entry->type == type)
- return entry;
-
- return nullptr;
-}
-
-
-
-/***************************************************************************
- RESOURCE POOL MANAGEMENT
-***************************************************************************/
-
-/*-------------------------------------------------
- pool_alloc - allocates a new memory pool
--------------------------------------------------*/
-
-object_pool *pool_alloc_lib(void (*fail)(const char *message))
-{
- object_pool *pool;
-
- /* allocate memory for the pool itself */
- pool = (object_pool *)malloc(sizeof(*pool));
- if (pool == nullptr)
- return nullptr;
- memset(pool, 0, sizeof(*pool));
-
- /* set the failure handler */
- pool->fail = fail;
-
- /* register the built-in types */
- pool_type_register(pool, OBJTYPE_MEMORY, "Memory", memory_destruct);
-
- return pool;
-}
-
-
-/*-------------------------------------------------
- pool_type_register - register a new pool
- type
--------------------------------------------------*/
-
-void pool_type_register(object_pool *pool, object_type type, const char *friendly, void (*destructor)(void *, size_t))
-{
- objtype_entry *newtype = get_object_type(pool, type);
-
- /* if the type doesn't already exist... */
- if (newtype == nullptr)
- {
- /* allocate a new entry */
- newtype = (objtype_entry *)malloc(sizeof(*newtype));
- if (newtype == nullptr)
- {
- report_failure(pool, "Error adding new type %s\n", friendly);
- return;
- }
- memset(newtype, 0, sizeof(*newtype));
-
- /* link us in */
- newtype->next = pool->typelist;
- pool->typelist = newtype;
- }
-
- /* fill in the type information */
- newtype->type = type;
- newtype->friendly = friendly;
- newtype->destructor = destructor;
-}
-
-
-/*-------------------------------------------------
- pool_clear - frees all contained memory blocks
- in a pool
--------------------------------------------------*/
-
-void pool_clear(object_pool *pool)
-{
- object_entry *entry, *next;
-
- /* iterate over all entries in the global list and free them */
- for (entry = pool->globallist; entry != nullptr; entry = next)
- {
- /* remember the next entry */
- next = entry->globalnext;
-
- /* call the destructor */
- (*entry->type->destructor)(entry->object, entry->size);
-
- /* add ourself to the free list */
- entry->next = pool->freelist;
- entry->globalnext = entry->globalprev = nullptr;
- pool->freelist = entry;
- }
-
- /* zap the hashtable */
- memset(pool->hashtable, 0, sizeof(pool->hashtable));
-}
-
-
-/*-------------------------------------------------
- pool_free_lib - frees a memory pool and all
- contained memory blocks
--------------------------------------------------*/
-
-void pool_free_lib(object_pool *pool)
-{
- object_entry_block *block, *nextblock;
- objtype_entry *type, *nexttype;
-
- /* clear the pool */
- pool_clear(pool);
-
- /* free all entry blocks */
- for (block = pool->blocklist; block != nullptr; block = nextblock)
- {
- nextblock = block->next;
- free(block);
- }
-
- /* free all types */
- for (type = pool->typelist; type != nullptr; type = nexttype)
- {
- nexttype = type->next;
- free(type);
- }
-
- /* free the pool itself */
- free(pool);
-}
-
-
-
-/***************************************************************************
- OBJECT MANAGEMENT
-***************************************************************************/
-
-/*-------------------------------------------------
- pool_object_add_file_line - add an object to
- the resource pool
--------------------------------------------------*/
-
-void *pool_object_add_file_line(object_pool *pool, object_type _type, void *object, size_t size, const char *file, int line)
-{
- objtype_entry *type = get_object_type(pool, _type);
- int hashnum = hash_object(object);
- object_entry *entry;
-
- /* if we have an invalid type, fail */
- if (type == nullptr)
- {
- report_failure(pool, "pool_object_add (via %s:%d): Attempted to add object of unknown type with size %d", file, line, (int)size);
- return object;
- }
-
- /* if we get a nullptr object, fail */
- if (object == nullptr)
- {
- report_failure(pool, "pool_object_add (via %s:%d): Attempted to add a nullptr object of size %d", file, line, (int)size);
- return object;
- }
-
- /* allocate a new entry */
- if (pool->freelist == nullptr)
- {
- object_entry_block *block;
- int entrynum;
-
- /* if we need a new block, allocate that now */
- block = (object_entry_block *)malloc(sizeof(*block));
- if (block == nullptr)
- return nullptr;
- memset(block, 0, sizeof(*block));
-
- /* hook us into the blocklist */
- block->next = pool->blocklist;
- pool->blocklist = block;
-
- /* add all entries to the free list */
- for (entrynum = 0; entrynum < ARRAY_LENGTH(block->entry); entrynum++)
- {
- block->entry[entrynum].next = pool->freelist;
- pool->freelist = &block->entry[entrynum];
- }
- }
-
- /* remove the head of the freelist */
- entry = pool->freelist;
- pool->freelist = entry->next;
-
- /* fill in the entry */
- entry->type = type;
- entry->object = object;
- entry->size = size;
- entry->file = file;
- entry->line = line;
-
- /* hook us into the global list */
- if (pool->globallist != nullptr)
- pool->globallist->globalprev = entry;
- entry->globalprev = nullptr;
- entry->globalnext = pool->globallist;
- pool->globallist = entry;
-
- /* hook up to the appropriate hash table */
- entry->next = pool->hashtable[hashnum];
- pool->hashtable[hashnum] = entry;
- return object;
-}
-
-
-/*-------------------------------------------------
- pool_object_remove - remove an entry from a
- pool, optionally calling its destructor
--------------------------------------------------*/
-
-void *pool_object_remove(object_pool *pool, void *object, int destruct)
-{
- int hashnum = hash_object(object);
- object_entry **entryptr;
-
- /* find the object in question and remove it */
- for (entryptr = &pool->hashtable[hashnum]; *entryptr != nullptr; entryptr = &(*entryptr)->next)
- if ((*entryptr)->object == object)
- {
- object_entry *entry = *entryptr;
-
- /* call the destructor */
- if (destruct)
- (*entry->type->destructor)(entry->object, entry->size);
-
- /* remove us from the global list */
- if (entry->globalprev != nullptr)
- entry->globalprev->globalnext = entry->globalnext;
- if (entry->globalnext != nullptr)
- entry->globalnext->globalprev = entry->globalprev;
- if (pool->globallist == entry)
- pool->globallist = entry->globalnext;
-
- /* remove us from the list */
- *entryptr = entry->next;
-
- /* add us to the free list */
- entry->next = pool->freelist;
- pool->freelist = entry;
- break;
- }
-
- return nullptr;
-}
-
-
-/*-------------------------------------------------
- pool_object_exists - return true if an
- object exists in the pool
--------------------------------------------------*/
-
-bool pool_object_exists(object_pool *pool, object_type type, void *object)
-{
- int hashnum = hash_object(object);
- object_entry *entry;
-
- /* find the object in question */
- for (entry = pool->hashtable[hashnum]; entry != nullptr; entry = entry->next)
- if (entry->object == object && (type == OBJTYPE_WILDCARD || entry->type->type == type))
- return true;
-
- return false;
-}
-
-
-
-/***************************************************************************
- OBJECT ITERATORS
-***************************************************************************/
-
-/*-------------------------------------------------
- pool_iterate_begin - begin iterating over
- objects in an object pool
--------------------------------------------------*/
-
-object_pool_iterator *pool_iterate_begin(object_pool *pool, object_type type)
-{
- object_pool_iterator *iter;
-
- /* allocate the iterator */
- iter = (object_pool_iterator *)malloc(sizeof(*iter));
- if (iter == nullptr)
- return nullptr;
- memset(iter, 0, sizeof(*iter));
-
- /* fill it in */
- iter->pool = pool;
- iter->type = type;
- iter->last = nullptr;
- return iter;
-}
-
-
-/*-------------------------------------------------
- pool_iterate_next - get the next object in the
- object pool
--------------------------------------------------*/
-
-bool pool_iterate_next(object_pool_iterator *iter, void **objectptr, size_t *sizeptr, object_type *typeptr)
-{
- /* if no previous entry, find the first */
- if (iter->last == nullptr)
- iter->last = iter->pool->globallist;
- else
- iter->last = iter->last->globalnext;
-
- /* stop when we get one */
- if (iter->last != nullptr)
- {
- if (objectptr != nullptr)
- *objectptr = iter->last;
- if (sizeptr != nullptr)
- *sizeptr = iter->last->size;
- if (typeptr != nullptr)
- *typeptr = iter->last->type->type;
- return true;
- }
-
- /* nothing left */
- return false;
-}
-
-
-/*-------------------------------------------------
- pool_iterate_end - finish iterating over
- objects in an object pool
--------------------------------------------------*/
-
-void pool_iterate_end(object_pool_iterator *iter)
-{
- free(iter);
-}
-
-
-
-/***************************************************************************
- MEMORY HELPERS
-***************************************************************************/
-
-/*-------------------------------------------------
- pool_malloc_file_line - allocates memory in a
- memory pool
--------------------------------------------------*/
-
-void *pool_malloc_file_line(object_pool *pool, size_t size, const char *file, int line)
-{
- void *ptr = malloc(size);
- return pool_object_add_file_line(pool, OBJTYPE_MEMORY, ptr, size, file, line);
-}
-
-
-/*-------------------------------------------------
- pool_realloc_file_line - reallocs memory in a
- memory pool
--------------------------------------------------*/
-
-void *pool_realloc_file_line(object_pool *pool, void *ptr, size_t size, const char *file, int line)
-{
- if (ptr != nullptr)
- pool_object_remove(pool, ptr, false);
- ptr = realloc(ptr, size);
- if (size != 0)
- pool_object_add_file_line(pool, OBJTYPE_MEMORY, ptr, size, file, line);
- return ptr;
-}
-
-
-/*-------------------------------------------------
- pool_strdup_file_line - strdup in a memory
- pool
--------------------------------------------------*/
-
-char *pool_strdup_file_line(object_pool *pool, const char *str, const char *file, int line)
-{
- char *ptr = (char *)pool_malloc_file_line(pool, strlen(str) + 1, file, line);
- if (ptr != nullptr)
- strcpy(ptr, str);
- return ptr;
-}
-
-
-
-/***************************************************************************
- GENERIC DESTRUCTORS
-***************************************************************************/
-
-/*-------------------------------------------------
- memory_destruct - generic destructor
- that just does a free() on the object
--------------------------------------------------*/
-
-static void memory_destruct(void *object, size_t size)
-{
- free(object);
-}
-
-
-
-/***************************************************************************
- INTERNAL FUNCTIONS
-***************************************************************************/
-
-/*-------------------------------------------------
- report_failure - report a failure to the
- failure callback
--------------------------------------------------*/
-
-static void report_failure(object_pool *pool, const char *format, ...)
-{
- /* only do the work if we have a callback */
- if (pool->fail != nullptr)
- {
- char message[1024];
- va_list argptr;
-
- /* generate the final message text */
- va_start(argptr, format);
- vsprintf(message, format, argptr);
- va_end(argptr);
-
- /* call the callback with the message */
- (*pool->fail)(message);
- }
-}
-
-
-
-/***************************************************************************
- TESTING FUNCTIONS
-***************************************************************************/
-
-static bool has_memory_error;
-
-
-/*-------------------------------------------------
- memory_error - report a memory error
--------------------------------------------------*/
-
-static void memory_error(const char *message)
-{
- printf("memory test failure: %s\n", message);
- has_memory_error = true;
-}
-
-
-/*-------------------------------------------------
- test_memory_pools - unit tests for memory
- pool behavior
--------------------------------------------------*/
-
-/**
- * @fn int test_memory_pools(void)
- *
- * @brief Tests memory pools.
- *
- * @return An int.
- */
-
-bool test_memory_pools(void)
-{
- object_pool *pool;
- void *ptrs[16];
- int i;
-
- has_memory_error = false;
- pool = pool_alloc_lib(memory_error);
- memset(ptrs, 0, sizeof(ptrs));
-
- ptrs[0] = pool_malloc_lib(pool, 50);
- ptrs[1] = pool_malloc_lib(pool, 100);
-
- ptrs[0] = pool_realloc_lib(pool, ptrs[0], 150);
- ptrs[1] = pool_realloc_lib(pool, ptrs[1], 200);
-
- ptrs[2] = pool_malloc_lib(pool, 250);
- ptrs[3] = pool_malloc_lib(pool, 300);
-
- ptrs[0] = pool_realloc_lib(pool, ptrs[0], 350);
- ptrs[1] = pool_realloc_lib(pool, ptrs[1], 400);
-
- ptrs[2] = pool_realloc_lib(pool, ptrs[2], 450);
- ptrs[3] = pool_realloc_lib(pool, ptrs[3], 500);
-
- ptrs[0] = pool_realloc_lib(pool, ptrs[0], 0);
- ptrs[1] = pool_realloc_lib(pool, ptrs[1], 0);
-
- ptrs[2] = pool_realloc_lib(pool, ptrs[2], 550);
- ptrs[3] = pool_realloc_lib(pool, ptrs[3], 600);
-
- /* some heavier stress tests */
- for (i = 0; i < 512; i++)
- {
- ptrs[i % ARRAY_LENGTH(ptrs)] = pool_realloc_lib(pool,
- ptrs[i % ARRAY_LENGTH(ptrs)], rand() % 1000);
- }
-
- pool_free_lib(pool);
- return has_memory_error;
-}
diff --git a/src/lib/util/pool.h b/src/lib/util/pool.h
deleted file mode 100644
index 2d1c5af31a7..00000000000
--- a/src/lib/util/pool.h
+++ /dev/null
@@ -1,118 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
-/***************************************************************************
-
- pool.h
-
- Abstract object pool management
-
-***************************************************************************/
-
-#ifndef MAME_UTIL_POOL_H
-#define MAME_UTIL_POOL_H
-
-#pragma once
-
-#include "osdcore.h"
-
-
-/***************************************************************************
- MACROS
-***************************************************************************/
-
-/* helper macros for memory pools that pass file/line number */
-#define pool_malloc_lib(pool, size) pool_malloc_file_line((pool), (size), __FILE__, __LINE__)
-#define pool_realloc_lib(pool, ptr, size) pool_realloc_file_line((pool), (ptr), (size), __FILE__, __LINE__)
-#define pool_strdup_lib(pool, size) pool_strdup_file_line((pool), (size), __FILE__, __LINE__)
-
-/* macro to define a 4-character type for a pool */
-#define OBJECT_TYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
-
-/* built-in pool types */
-#define OBJTYPE_WILDCARD (0)
-#define OBJTYPE_MEMORY OBJECT_TYPE('m','e','m','o')
-
-
-
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
-
-/* pool types are UINT32s */
-typedef uint32_t object_type;
-
-/* opaque type representing a pool of objects */
-struct object_pool;
-
-/* opaque type representing an iterator over pool objects */
-struct object_pool_iterator;
-
-
-
-/***************************************************************************
- PROTOTYPES
-***************************************************************************/
-
-
-/* ----- object pool management ----- */
-
-/* allocate a new object pool */
-object_pool *pool_alloc_lib(void (*fail)(const char *message));
-
-/* register a new object type; returns true if the type already existed and was overridden */
-void pool_type_register(object_pool *pool, object_type type, const char *friendly, void (*destructor)(void *, size_t));
-
-/* free all allocated objects in a pool */
-void pool_clear(object_pool *pool);
-
-/* free an object pool, including all allocated objects */
-void pool_free_lib(object_pool *pool);
-
-
-
-/* ----- object management ----- */
-
-/* add an object to the pool, along with its filename/line number */
-void *pool_object_add_file_line(object_pool *pool, object_type type, void *object, size_t size, const char *file, int line);
-
-/* remove an object from the pool (optionally calling destructor) */
-void *pool_object_remove(object_pool *pool, void *object, int destruct);
-
-/* does an object exist in the pool? */
-bool pool_object_exists(object_pool *pool, object_type type, void *object);
-
-
-
-/* ----- object iterators ----- */
-
-/* begin iterating over objects in an object pool */
-object_pool_iterator *pool_iterate_begin(object_pool *pool, object_type type);
-
-/* get the next object in the object pool */
-bool pool_iterate_next(object_pool_iterator *iter, void **objectptr, size_t *sizeptr, object_type *typeptr);
-
-/* finish iterating over objects in an object pool */
-void pool_iterate_end(object_pool_iterator *iter);
-
-
-
-/* ----- memory helpers ----- */
-
-/* malloc memory and register it with the given pool */
-void *pool_malloc_file_line(object_pool *pool, size_t size, const char *file, int line);
-
-/* realloc memory and register it with the given pool */
-void *pool_realloc_file_line(object_pool *pool, void *ptr, size_t size, const char *file, int line);
-
-/* strdup memory and register it with the given pool */
-char *pool_strdup_file_line(object_pool *pool, const char *str, const char *file, int line);
-
-
-
-/* ----- miscellaneous ----- */
-
-/* internal unit tests */
-bool test_memory_pools(void);
-
-
-#endif // MAME_UTIL_POOL_H
diff --git a/src/lib/util/server_http_impl.hpp b/src/lib/util/server_http_impl.hpp
index e949e6b2483..7de440bce2c 100644
--- a/src/lib/util/server_http_impl.hpp
+++ b/src/lib/util/server_http_impl.hpp
@@ -63,10 +63,10 @@ namespace webpp {
}
}
public:
- virtual Response& status(int number) { m_ostream << statusToString(number); return *this; }
- virtual void type(std::string str) { m_header << "Content-Type: "<< str << "\r\n"; }
- virtual void send(std::string str) { m_ostream << m_header.str() << "Content-Length: " << str.length() << "\r\n\r\n" << str; }
- virtual size_t size() const { return m_streambuf.size(); }
+ virtual Response& status(int number) override { m_ostream << statusToString(number); return *this; }
+ virtual void type(std::string str) override { m_header << "Content-Type: "<< str << "\r\n"; }
+ virtual void send(std::string str) override { m_ostream << m_header.str() << "Content-Length: " << str.length() << "\r\n\r\n" << str; }
+ virtual size_t size() const override { return m_streambuf.size(); }
std::shared_ptr<socket_type> socket() { return m_socket; }
/// If true, force server to close the connection after the response have been sent.
diff --git a/src/lib/util/server_ws_impl.hpp b/src/lib/util/server_ws_impl.hpp
index 9fc5876cab2..c1ba9f1a392 100644
--- a/src/lib/util/server_ws_impl.hpp
+++ b/src/lib/util/server_ws_impl.hpp
@@ -3,6 +3,7 @@
#ifndef MAME_LIB_UTIL_SERVER_WS_IMPL_HPP
#define MAME_LIB_UTIL_SERVER_WS_IMPL_HPP
#include "path_to_regex.hpp"
+#include "base64.hpp"
#include "crypto.hpp"
#include "asio.h"
@@ -73,10 +74,10 @@ namespace webpp {
public:
virtual ~Connection() {}
- explicit Connection(const std::shared_ptr<socket_type> &socket) : super(0), socket(socket), strand(socket->get_io_service()), closed(false) { }
+ Connection(asio::io_context &context, const std::shared_ptr<socket_type> &socket) : super(0), socket(socket), strand(context), closed(false) { }
private:
- explicit Connection(socket_type *socket): super(0), socket(socket), strand(socket->get_io_service()), closed(false) { }
+ Connection(asio::io_context &context, socket_type *socket): super(0), socket(socket), strand(context), closed(false) { }
class SendData {
public:
@@ -360,7 +361,7 @@ namespace webpp {
std::shared_ptr<asio::system_timer> get_timeout_timer(const std::shared_ptr<Connection> &connection, size_t seconds) {
if (seconds == 0)
return nullptr;
- auto timer = std::make_shared<asio::system_timer>(connection->socket->get_io_service());
+ auto timer = std::make_shared<asio::system_timer>(connection->socket->get_executor());
timer->expires_at(std::chrono::system_clock::now() + std::chrono::seconds(static_cast<long>(seconds)));
timer->async_wait([connection](const std::error_code& ec){
if(!ec) {
@@ -651,7 +652,7 @@ namespace webpp {
void timer_idle_init(const std::shared_ptr<Connection> &connection) {
if(config.timeout_idle>0) {
- connection->timer_idle= std::make_unique<asio::system_timer>(connection->socket->get_io_service());
+ connection->timer_idle= std::make_unique<asio::system_timer>(connection->socket->get_executor());
connection->timer_idle->expires_from_now(std::chrono::seconds(static_cast<unsigned long>(config.timeout_idle)));
timer_idle_expired_function(connection);
}
@@ -692,7 +693,7 @@ namespace webpp {
void accept() override {
//Create new socket for this connection (stored in Connection::socket)
//Shared_ptr is used to pass temporary objects to the asynchronous functions
- std::shared_ptr<Connection> connection(new Connection(new WS(*io_context)));
+ std::shared_ptr<Connection> connection(new Connection(*io_context, new WS(*io_context)));
acceptor->async_accept(*connection->socket, [this, connection](const std::error_code& ec) {
//Immediately start accepting a new connection (if io_context hasn't been stopped)
diff --git a/src/lib/util/sha1.cpp b/src/lib/util/sha1.cpp
deleted file mode 100644
index f1f00a60310..00000000000
--- a/src/lib/util/sha1.cpp
+++ /dev/null
@@ -1,443 +0,0 @@
-// license:LGPL-2.1+
-// copyright-holders:Peter Gutmann, Andrew Kuchling, Niels Moeller
-/* sha1.h
- *
- * The sha1 hash function.
- */
-
-/* nettle, low-level cryptographics library
- *
- * Copyright 2001 Peter Gutmann, Andrew Kuchling, Niels Moeller
- *
- * The nettle library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or (at your
- * option) any later version.
- *
- * The nettle library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with the nettle library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#include "sha1.h"
-
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
-static unsigned int READ_UINT32(const uint8_t* data)
-{
- return ((uint32_t)data[0] << 24) |
- ((uint32_t)data[1] << 16) |
- ((uint32_t)data[2] << 8) |
- ((uint32_t)data[3]);
-}
-
-static void WRITE_UINT32(unsigned char* data, uint32_t val)
-{
- data[0] = (val >> 24) & 0xFF;
- data[1] = (val >> 16) & 0xFF;
- data[2] = (val >> 8) & 0xFF;
- data[3] = (val >> 0) & 0xFF;
-}
-
-
-/* A block, treated as a sequence of 32-bit words. */
-#define SHA1_DATA_LENGTH 16
-
-/* The SHA f()-functions. The f1 and f3 functions can be optimized to
- save one boolean operation each - thanks to Rich Schroeppel,
- rcs@cs.arizona.edu for discovering this */
-
-/* #define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) Rounds 0-19 */
-#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */
-#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */
-/* #define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) Rounds 40-59 */
-#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */
-#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */
-
-/* The SHA Mysterious Constants */
-
-#define K1 0x5A827999L /* Rounds 0-19 */
-#define K2 0x6ED9EBA1L /* Rounds 20-39 */
-#define K3 0x8F1BBCDCL /* Rounds 40-59 */
-#define K4 0xCA62C1D6L /* Rounds 60-79 */
-
-/* SHA initial values */
-
-#define h0init 0x67452301L
-#define h1init 0xEFCDAB89L
-#define h2init 0x98BADCFEL
-#define h3init 0x10325476L
-#define h4init 0xC3D2E1F0L
-
-/* 32-bit rotate left - kludged with shifts */
-#ifdef _MSC_VER
-#define ROTL(n,X) _lrotl(X, n)
-#else
-#define ROTL(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) )
-#endif
-
-/* The initial expanding function. The hash function is defined over an
- 80-word expanded input array W, where the first 16 are copies of the input
- data, and the remaining 64 are defined by
-
- W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ]
-
- This implementation generates these values on the fly in a circular
- buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this
- optimization.
-
- The updated SHA changes the expanding function by adding a rotate of 1
- bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor
- for this information */
-
-#define expand(W,i) ( W[ i & 15 ] = \
- ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \
- W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) )
-
-
-/* The prototype SHA sub-round. The fundamental sub-round is:
-
- a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data;
- b' = a;
- c' = ROTL( 30, b );
- d' = c;
- e' = d;
-
- but this is implemented by unrolling the loop 5 times and renaming the
- variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration.
- This code is then replicated 20 times for each of the 4 functions, using
- the next 20 values from the W[] array each time */
-
-#define subRound(a, b, c, d, e, f, k, data) \
- ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
-
-/* Initialize the SHA values */
-
-/**
- * @fn void sha1_init(struct sha1_ctx *ctx)
- *
- * @brief Sha 1 initialise.
- *
- * @param [in,out] ctx If non-null, the context.
- */
-
-void
-sha1_init(struct sha1_ctx *ctx)
-{
- /* Set the h-vars to their initial values */
- ctx->digest[ 0 ] = h0init;
- ctx->digest[ 1 ] = h1init;
- ctx->digest[ 2 ] = h2init;
- ctx->digest[ 3 ] = h3init;
- ctx->digest[ 4 ] = h4init;
-
- /* Initialize bit count */
- ctx->count_low = ctx->count_high = 0;
-
- /* Initialize buffer */
- ctx->index = 0;
-}
-
-/* Perform the SHA transformation. Note that this code, like MD5, seems to
- break some optimizing compilers due to the complexity of the expressions
- and the size of the basic block. It may be necessary to split it into
- sections, e.g. based on the four subrounds
-
- Note that this function destroys the data area */
-
-/**
- * @fn static void sha1_transform(uint32_t *state, uint32_t *data)
- *
- * @brief Sha 1 transform.
- *
- * @param [in,out] state If non-null, the state.
- * @param [in,out] data If non-null, the data.
- */
-
-static void
-sha1_transform(uint32_t *state, uint32_t *data)
-{
- uint32_t A, B, C, D, E; /* Local vars */
-
- /* Set up first buffer and local data buffer */
- A = state[0];
- B = state[1];
- C = state[2];
- D = state[3];
- E = state[4];
-
- /* Heavy mangling, in 4 sub-rounds of 20 interations each. */
- subRound( A, B, C, D, E, f1, K1, data[ 0] );
- subRound( E, A, B, C, D, f1, K1, data[ 1] );
- subRound( D, E, A, B, C, f1, K1, data[ 2] );
- subRound( C, D, E, A, B, f1, K1, data[ 3] );
- subRound( B, C, D, E, A, f1, K1, data[ 4] );
- subRound( A, B, C, D, E, f1, K1, data[ 5] );
- subRound( E, A, B, C, D, f1, K1, data[ 6] );
- subRound( D, E, A, B, C, f1, K1, data[ 7] );
- subRound( C, D, E, A, B, f1, K1, data[ 8] );
- subRound( B, C, D, E, A, f1, K1, data[ 9] );
- subRound( A, B, C, D, E, f1, K1, data[10] );
- subRound( E, A, B, C, D, f1, K1, data[11] );
- subRound( D, E, A, B, C, f1, K1, data[12] );
- subRound( C, D, E, A, B, f1, K1, data[13] );
- subRound( B, C, D, E, A, f1, K1, data[14] );
- subRound( A, B, C, D, E, f1, K1, data[15] );
- subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) );
- subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) );
- subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) );
- subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) );
-
- subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) );
- subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) );
- subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) );
- subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) );
- subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) );
- subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) );
- subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) );
- subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) );
- subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) );
- subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) );
- subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) );
- subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) );
- subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) );
- subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) );
- subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) );
- subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) );
- subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) );
- subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) );
- subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) );
- subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) );
-
- subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) );
- subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) );
- subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) );
- subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) );
- subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) );
- subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) );
- subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) );
- subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) );
- subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) );
- subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) );
- subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) );
- subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) );
- subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) );
- subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) );
- subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) );
- subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) );
- subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) );
- subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) );
- subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) );
- subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) );
-
- subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) );
- subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) );
- subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) );
- subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) );
- subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) );
- subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) );
- subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) );
- subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) );
- subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) );
- subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) );
- subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) );
- subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) );
- subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) );
- subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) );
- subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) );
- subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) );
- subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) );
- subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) );
- subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) );
- subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) );
-
- /* Build message digest */
- state[0] += A;
- state[1] += B;
- state[2] += C;
- state[3] += D;
- state[4] += E;
-}
-
-/**
- * @fn static void sha1_block(struct sha1_ctx *ctx, const uint8_t *block)
- *
- * @brief Sha 1 block.
- *
- * @param [in,out] ctx If non-null, the context.
- * @param block The block.
- */
-
-static void
-sha1_block(struct sha1_ctx *ctx, const uint8_t *block)
-{
- uint32_t data[SHA1_DATA_LENGTH];
- int i;
-
- /* Update block count */
- if (!++ctx->count_low)
- ++ctx->count_high;
-
- /* Endian independent conversion */
- for (i = 0; i<SHA1_DATA_LENGTH; i++, block += 4)
- data[i] = READ_UINT32(block);
-
- sha1_transform(ctx->digest, data);
-}
-
-/**
- * @fn void sha1_update(struct sha1_ctx *ctx, unsigned length, const uint8_t *buffer)
- *
- * @brief Sha 1 update.
- *
- * @param [in,out] ctx If non-null, the context.
- * @param length The length.
- * @param buffer The buffer.
- */
-
-void
-sha1_update(struct sha1_ctx *ctx,
- unsigned length, const uint8_t *buffer)
-{
- if (ctx->index)
- { /* Try to fill partial block */
- unsigned left = SHA1_DATA_SIZE - ctx->index;
- if (length < left)
- {
- memcpy(ctx->block + ctx->index, buffer, length);
- ctx->index += length;
- return; /* Finished */
- }
- else
- {
- memcpy(ctx->block + ctx->index, buffer, left);
- sha1_block(ctx, ctx->block);
- buffer += left;
- length -= left;
- }
- }
- while (length >= SHA1_DATA_SIZE)
- {
- sha1_block(ctx, buffer);
- buffer += SHA1_DATA_SIZE;
- length -= SHA1_DATA_SIZE;
- }
- ctx->index = length;
- if (length)
- /* Buffer leftovers */
- memcpy(ctx->block, buffer, length);
-}
-
-/* Final wrapup - pad to SHA1_DATA_SIZE-byte boundary with the bit pattern
- 1 0* (64-bit count of bits processed, MSB-first) */
-
-/**
- * @fn void sha1_final(struct sha1_ctx *ctx)
- *
- * @brief Sha 1 final.
- *
- * @param [in,out] ctx If non-null, the context.
- */
-
-void
-sha1_final(struct sha1_ctx *ctx)
-{
- uint32_t data[SHA1_DATA_LENGTH];
- int i;
- int words;
-
- i = ctx->index;
-
- /* Set the first char of padding to 0x80. This is safe since there is
- always at least one byte free */
-
- assert(i < SHA1_DATA_SIZE);
- ctx->block[i++] = 0x80;
-
- /* Fill rest of word */
- for( ; i & 3; i++)
- ctx->block[i] = 0;
-
- /* i is now a multiple of the word size 4 */
- words = i >> 2;
- for (i = 0; i < words; i++)
- data[i] = READ_UINT32(ctx->block + 4*i);
-
- if (words > (SHA1_DATA_LENGTH-2))
- { /* No room for length in this block. Process it and
- * pad with another one */
- for (i = words ; i < SHA1_DATA_LENGTH; i++)
- data[i] = 0;
- sha1_transform(ctx->digest, data);
- for (i = 0; i < (SHA1_DATA_LENGTH-2); i++)
- data[i] = 0;
- }
- else
- for (i = words ; i < SHA1_DATA_LENGTH - 2; i++)
- data[i] = 0;
-
- /* There are 512 = 2^9 bits in one block */
- data[SHA1_DATA_LENGTH-2] = (ctx->count_high << 9) | (ctx->count_low >> 23);
- data[SHA1_DATA_LENGTH-1] = (ctx->count_low << 9) | (ctx->index << 3);
- sha1_transform(ctx->digest, data);
-}
-
-/**
- * @fn void sha1_digest(const struct sha1_ctx *ctx, unsigned length, uint8_t *digest)
- *
- * @brief Sha 1 digest.
- *
- * @param ctx The context.
- * @param length The length.
- * @param [in,out] digest If non-null, the digest.
- */
-
-void
-sha1_digest(const struct sha1_ctx *ctx,
- unsigned length,
- uint8_t *digest)
-{
- unsigned i;
- unsigned words;
- unsigned leftover;
-
- assert(length <= SHA1_DIGEST_SIZE);
-
- words = length / 4;
- leftover = length % 4;
-
- for (i = 0; i < words; i++, digest += 4)
- WRITE_UINT32(digest, ctx->digest[i]);
-
- if (leftover)
- {
- uint32_t word;
- unsigned j = leftover;
-
- assert(i < _SHA1_DIGEST_LENGTH);
-
- word = ctx->digest[i];
-
- switch (leftover)
- {
- default:
- /* this is just here to keep the compiler happy; it can never happen */
- case 3:
- digest[--j] = (word >> 8) & 0xff;
- /* Fall through */
- case 2:
- digest[--j] = (word >> 16) & 0xff;
- /* Fall through */
- case 1:
- digest[--j] = (word >> 24) & 0xff;
- }
- }
-}
diff --git a/src/lib/util/sha1.h b/src/lib/util/sha1.h
deleted file mode 100644
index 177caf9f6dc..00000000000
--- a/src/lib/util/sha1.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// license:LGPL-2.1+
-// copyright-holders:Peter Gutmann, Andrew Kuchling, Niels Moeller
-/* sha1.h
- *
- * The sha1 hash function.
- */
-
-/* nettle, low-level cryptographics library
- *
- * Copyright 2001 Niels Moeller
- *
- * The nettle library is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or (at your
- * option) any later version.
- *
- * The nettle library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with the nettle library; see the file COPYING.LIB. If not, write to
- * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef NETTLE_SHA1_H_INCLUDED
-#define NETTLE_SHA1_H_INCLUDED
-
-#include "osdcore.h"
-
-#define SHA1_DIGEST_SIZE 20
-#define SHA1_DATA_SIZE 64
-
-/* Digest is kept internally as 4 32-bit words. */
-#define _SHA1_DIGEST_LENGTH 5
-
-struct sha1_ctx
-{
- uint32_t digest[_SHA1_DIGEST_LENGTH]; /* Message digest */
- uint32_t count_low, count_high; /* 64-bit block count */
- uint8_t block[SHA1_DATA_SIZE]; /* SHA1 data buffer */
- unsigned int index; /* index into buffer */
-};
-
-void
-sha1_init(struct sha1_ctx *ctx);
-
-void
-sha1_update(struct sha1_ctx *ctx,
- unsigned length,
- const uint8_t *data);
-
-void
-sha1_final(struct sha1_ctx *ctx);
-
-void
-sha1_digest(const struct sha1_ctx *ctx,
- unsigned length,
- uint8_t *digest);
-
-#endif /* NETTLE_SHA1_H_INCLUDED */
diff --git a/src/lib/util/sha1.hpp b/src/lib/util/sha1.hpp
deleted file mode 100644
index a293bebb7cc..00000000000
--- a/src/lib/util/sha1.hpp
+++ /dev/null
@@ -1,181 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Micael Hildenborg
-/*
- Copyright (c) 2011, Micael Hildenborg
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions are met:
- * Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
- * Neither the name of Micael Hildenborg nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
-
- THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef SHA1_DEFINED
-#define SHA1_DEFINED
-
-namespace sha1 {
-
-namespace { // local
-
-// Rotate an integer value to left.
-inline unsigned int rol(unsigned int value, unsigned int steps) {
- return ((value << steps) | (value >> (32 - steps)));
-}
-
-// Sets the first 16 integers in the buffert to zero.
-// Used for clearing the W buffert.
-inline void clearWBuffert(unsigned int * buffert)
-{
- for (int pos = 16; --pos >= 0;)
- {
- buffert[pos] = 0;
- }
-}
-
-inline void innerHash(unsigned int * result, unsigned int * w)
-{
- unsigned int a = result[0];
- unsigned int b = result[1];
- unsigned int c = result[2];
- unsigned int d = result[3];
- unsigned int e = result[4];
-
- int round = 0;
-
- #define sha1macro(func,val) \
- { \
- const unsigned int t = rol(a, 5) + (func) + e + val + w[round]; \
- e = d; \
- d = c; \
- c = rol(b, 30); \
- b = a; \
- a = t; \
- }
-
- while (round < 16)
- {
- sha1macro((b & c) | (~b & d), 0x5a827999)
- ++round;
- }
- while (round < 20)
- {
- w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
- sha1macro((b & c) | (~b & d), 0x5a827999)
- ++round;
- }
- while (round < 40)
- {
- w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
- sha1macro(b ^ c ^ d, 0x6ed9eba1)
- ++round;
- }
- while (round < 60)
- {
- w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
- sha1macro((b & c) | (b & d) | (c & d), 0x8f1bbcdc)
- ++round;
- }
- while (round < 80)
- {
- w[round] = rol((w[round - 3] ^ w[round - 8] ^ w[round - 14] ^ w[round - 16]), 1);
- sha1macro(b ^ c ^ d, 0xca62c1d6)
- ++round;
- }
-
- #undef sha1macro
-
- result[0] += a;
- result[1] += b;
- result[2] += c;
- result[3] += d;
- result[4] += e;
-}
-
-} // namespace
-
-/// Calculate a SHA1 hash
-/**
- * @param src points to any kind of data to be hashed.
- * @param bytelength the number of bytes to hash from the src pointer.
- * @param hash should point to a buffer of at least 20 bytes of size for storing
- * the sha1 result in.
- */
-inline void calc(void const * src, size_t bytelength, unsigned char * hash) {
- // Init the result array.
- unsigned int result[5] = { 0x67452301, 0xefcdab89, 0x98badcfe,
- 0x10325476, 0xc3d2e1f0 };
-
- // Cast the void src pointer to be the byte array we can work with.
- unsigned char const * sarray = static_cast<unsigned char const *>(src);
-
- // The reusable round buffer
- unsigned int w[80];
-
- // Loop through all complete 64byte blocks.
-
- size_t endCurrentBlock;
- size_t currentBlock = 0;
-
- if (bytelength >= 64) {
- size_t const endOfFullBlocks = bytelength - 64;
-
- while (currentBlock <= endOfFullBlocks) {
- endCurrentBlock = currentBlock + 64;
-
- // Init the round buffer with the 64 byte block data.
- for (int roundPos = 0; currentBlock < endCurrentBlock; currentBlock += 4)
- {
- // This line will swap endian on big endian and keep endian on
- // little endian.
- w[roundPos++] = static_cast<unsigned int>(sarray[currentBlock + 3])
- | (static_cast<unsigned int>(sarray[currentBlock + 2]) << 8)
- | (static_cast<unsigned int>(sarray[currentBlock + 1]) << 16)
- | (static_cast<unsigned int>(sarray[currentBlock]) << 24);
- }
- innerHash(result, w);
- }
- }
-
- // Handle the last and not full 64 byte block if existing.
- endCurrentBlock = bytelength - currentBlock;
- clearWBuffert(w);
- size_t lastBlockBytes = 0;
- for (;lastBlockBytes < endCurrentBlock; ++lastBlockBytes) {
- w[lastBlockBytes >> 2] |= static_cast<unsigned int>(sarray[lastBlockBytes + currentBlock]) << ((3 - (lastBlockBytes & 3)) << 3);
- }
-
- w[lastBlockBytes >> 2] |= 0x80 << ((3 - (lastBlockBytes & 3)) << 3);
- if (endCurrentBlock >= 56) {
- innerHash(result, w);
- clearWBuffert(w);
- }
- w[15] = bytelength << 3;
- innerHash(result, w);
-
- // Store hash in result pointer, and make sure we get in in the correct
- // order on both endian models.
- for (int hashByte = 20; --hashByte >= 0;) {
- hash[hashByte] = (result[hashByte >> 2] >> (((3 - hashByte) & 0x3) << 3)) & 0xff;
- }
-}
-
-} // namespace sha1
-
-#endif // SHA1_DEFINED
diff --git a/src/lib/util/simh_tape_file.cpp b/src/lib/util/simh_tape_file.cpp
new file mode 100644
index 00000000000..ffd6a48aeb3
--- /dev/null
+++ b/src/lib/util/simh_tape_file.cpp
@@ -0,0 +1,622 @@
+// license:BSD-3-Clause
+// copyright-holders:Mietek Bak
+
+// best read together with SIMH magtape spec (rev 17 Jan 2022)
+// http://simh.trailing-edge.com/docs/simh_magtape.pdf
+
+#include "simh_tape_file.h"
+
+#include "ioprocs.h"
+#include "multibyte.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <stdexcept>
+
+#include <fcntl.h>
+
+//////////////////////////////////////////////////////////////////////////////
+
+// constants and helpers
+
+enum class simh_marker : osd::u32 {
+ TAPE_MARK = 0x00000000, // filemark; TODO: SIMH doesn't define setmarks
+ ERASE_GAP = 0xfffffffe,
+ EOM = 0xffffffff
+};
+
+inline bool is_simh_marker_half_gap_forward(const simh_marker marker)
+{
+ // this function is used when we're reading normally (from BOM to EOM); returns true for erase gap markers that have been half overwritten
+ return osd::u32(marker) == 0xfffeffff;
+}
+
+inline bool is_simh_marker_half_gap_reverse(const simh_marker marker)
+{
+ // this function is used when we're reading in reverse (from EOM to BOM); returns true for erase gap markers that have been half overwritten
+ return osd::u32(marker) >= 0xffff0000 && osd::u32(marker) <= 0xfffffffd;
+}
+
+inline bool is_simh_marker_eod_forward(const simh_marker marker)
+{
+ // this function is used when we're reading normally (from BOM to EOM); returns true for markers that we consider EOD
+ return marker == simh_marker::ERASE_GAP
+ || is_simh_marker_half_gap_forward(marker)
+ || marker == simh_marker::EOM; // logical EOM
+}
+
+inline bool is_simh_marker_eod_reverse(const simh_marker marker)
+{
+ // this function is used when we're reading in reverse (from EOM to BOM); returns true for markers that we consider EOD
+ return marker == simh_marker::ERASE_GAP
+ || is_simh_marker_half_gap_reverse(marker)
+ || marker == simh_marker::EOM; // logical EOM
+}
+
+enum class simh_marker_class : osd::u8 {
+ GOOD_DATA_RECORD = 0x0,
+ PRIVATE_DATA_RECORD_1 = 0x1,
+ PRIVATE_DATA_RECORD_2 = 0x2,
+ PRIVATE_DATA_RECORD_3 = 0x3,
+ PRIVATE_DATA_RECORD_4 = 0x4,
+ PRIVATE_DATA_RECORD_5 = 0x5,
+ PRIVATE_DATA_RECORD_6 = 0x6,
+ PRIVATE_MARKER = 0x7,
+ BAD_DATA_RECORD = 0x8,
+ RESERVED_DATA_RECORD_9 = 0x9,
+ RESERVED_DATA_RECORD_A = 0xa,
+ RESERVED_DATA_RECORD_B = 0xb,
+ RESERVED_DATA_RECORD_C = 0xc,
+ RESERVED_DATA_RECORD_D = 0xd,
+ TAPE_DESCRIPTION_DATA_RECORD = 0xe,
+ RESERVED_MARKER = 0xf
+};
+
+inline simh_marker_class get_simh_marker_class(const simh_marker marker)
+{
+ return simh_marker_class(osd::u32(marker) >> 28);
+}
+
+inline osd::u32 get_simh_marker_value(const simh_marker marker)
+{
+ return osd::u32(marker) & 0x0fffffff;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// construction
+
+simh_tape_file::simh_tape_file(util::random_read_write &file, osd::u64 file_size, bool read_only, bool create)
+ : m_file(file)
+ , m_file_size(file_size)
+ , m_read_only(read_only)
+ , m_pos(0)
+{
+ if (create) {
+ if (read_only) // error: we cannot create read-only file
+ throw std::runtime_error("read-only file");
+
+ write_byte_repeat(0, 0xff, m_file_size); // we assume simh_marker::EOM == 0xffffffff
+ }
+}
+
+simh_tape_file::~simh_tape_file()
+{
+ m_file.finalize();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// internal operations
+
+void simh_tape_file::raw_seek(const osd::u64 pos) const
+{
+ std::error_condition err = m_file.seek(pos, SEEK_SET);
+ if (err) // error: we failed to seek to expected byte offset
+ throw std::runtime_error(std::string("failed seek: ") + err.message());
+}
+
+void simh_tape_file::raw_read(osd::u8 *const buf, const osd::u32 len) const
+{
+ auto const [err, actual_len] = read(m_file, buf, len);
+ if (err || actual_len != len) // error: we failed to read expected number of bytes
+ throw std::runtime_error(std::string("failed read: ") + (err ? err.message() : std::string("unexpected length")));
+}
+
+void simh_tape_file::raw_write(const osd::u8 *const buf, const osd::u32 len) const
+{
+ auto const [err, actual_len] = write(m_file, buf, len);
+ if (err || actual_len != len) // error: we failed to write expected number of bytes
+ throw std::runtime_error(std::string("failed write: ") + (err ? err.message() : std::string("unexpected length")));
+}
+
+void simh_tape_file::read_bytes(const osd::u64 pos, osd::u8 *const buf, const osd::u32 len) const
+{
+ raw_seek(pos);
+ raw_read(buf, len);
+}
+
+osd::u32 simh_tape_file::read_word(const osd::u64 pos) const
+{
+ const osd::u32 tmp_len = 4;
+ osd::u8 tmp_buf[tmp_len];
+ raw_seek(pos);
+ raw_read(tmp_buf, tmp_len);
+ return get_u32le(tmp_buf);
+}
+
+void simh_tape_file::write_bytes(const osd::u64 pos, const osd::u8 *const buf, const osd::u32 len) const
+{
+ raw_seek(pos);
+ raw_write(buf, len);
+}
+
+void simh_tape_file::write_byte_repeat(const osd::u64 pos, const osd::u8 data, const osd::u32 len) const
+{
+ const osd::u32 tmp_len = 4096;
+ osd::u8 tmp_buf[tmp_len];
+ memset(tmp_buf, data, std::min(len, tmp_len));
+ raw_seek(pos);
+ for (osd::u32 i = 0; i < len / tmp_len; i++)
+ raw_write(tmp_buf, tmp_len);
+ raw_write(tmp_buf, len % tmp_len);
+}
+
+void simh_tape_file::write_word(const osd::u64 pos, const osd::u32 data) const
+{
+ const osd::u32 tmp_len = 4;
+ osd::u8 tmp_buf[tmp_len];
+ put_u32le(tmp_buf, data);
+ raw_seek(pos);
+ raw_write(tmp_buf, tmp_len);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// position-preserving operations
+
+std::pair<tape_status, osd::u32> simh_tape_file::read_position() const
+{
+ // this module only keeps track of current tape position, therefore this function scans from BOM to find and return current block address, taking linear time; TODO: this module could be rewritten to also keep track of current block address, therefore enabling this function to only take constant time
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ if (m_pos == 0) // we're at BOM and next block is 0
+ return std::pair(tape_status::BOM, 0);
+
+ if (m_pos == m_file_size) // we're at physical EOM and there is no next block
+ return std::pair(tape_status::EOM, 0);
+
+ // we need to count how many blocks are between BOM and us
+ osd::u32 blocks_num = 0;
+ osd::u64 tmp_pos = 0;
+ while (tmp_pos < m_pos) {
+ if (tmp_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(tmp_pos));
+ if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
+ tmp_pos += 4;
+ continue;
+ }
+ if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::UNKNOWN_EW : tape_status::UNKNOWN, 0);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ tmp_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
+ if (tmp_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ // we counted another block
+ tmp_pos += read_len;
+ blocks_num++;
+ break;
+ default: // we try to skip other blocks
+ if (tmp_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ tmp_pos += read_len;
+ }
+ }
+ if (m_pos < tmp_pos) // we're inside some block
+ return std::pair(is_ew() ? tape_status::UNKNOWN_EW : tape_status::UNKNOWN, 0);
+
+ // we're right after some block (at EOD, or at filemark, or at another block)
+ assert(tmp_pos == m_pos);
+ return std::pair(is_ew() ? tape_status::EW : tape_status::OK, blocks_num);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// non-destructive operations
+
+void simh_tape_file::rewind(bool eom)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ m_pos = eom ? m_file_size : 0;
+}
+
+tape_status simh_tape_file::locate_block(osd::u32 req_block_addr)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ osd::u32 blocks_num = 0;
+ m_pos = 0;
+ while (m_pos < m_file_size) {
+ if (m_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos));
+ if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
+ m_pos += 4;
+ continue;
+ }
+ if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
+ return is_ew() ? tape_status::EOD_EW : tape_status::EOD;
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ if (blocks_num == req_block_addr) // success: we located requested block
+ return tape_status::OK;
+
+ m_pos += read_len;
+ blocks_num++;
+ break;
+ default: // we try to skip other blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ }
+ }
+ // error: we reached physical EOM
+ assert(m_pos == m_file_size);
+ return tape_status::EOM;
+}
+
+tape_status simh_tape_file::space_eod()
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ while (m_pos < m_file_size) {
+ if (m_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos));
+ if (marker == simh_marker::TAPE_MARK) { // we skip filemarks
+ m_pos += 4;
+ continue;
+ }
+ if (is_simh_marker_eod_forward(marker)) // success: we reached EOD
+ return tape_status::OK;
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
+ default:
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ }
+ }
+ // error: we reached physical EOM
+ assert(m_pos == m_file_size);
+ return tape_status::EOM;
+}
+
+std::pair<tape_status, osd::u32> simh_tape_file::space_blocks(osd::u32 req_blocks_num)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ assert(req_blocks_num > 0);
+ osd::u32 blocks_num = 0;
+ while (m_pos < m_file_size) {
+ if (m_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos));
+ if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
+ m_pos += 4;
+ return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, blocks_num);
+ }
+ if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, blocks_num);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ blocks_num++;
+ if (blocks_num == req_blocks_num) // success: we're done
+ return std::pair(tape_status::OK, blocks_num);
+
+ break;
+ default: // we try to skip other blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ }
+ }
+ // error: we reached physical EOM
+ assert(m_pos == m_file_size);
+ return std::pair(tape_status::EOM, blocks_num);
+}
+
+std::pair<tape_status, osd::u32> simh_tape_file::space_filemarks(osd::u32 req_filemarks_num, bool setmarks, bool sequential)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ assert(req_filemarks_num > 0);
+ assert(!setmarks); // TODO: SIMH doesn't define setmarks
+ assert(!sequential); // TODO: support spacing over sequential filemarks, once we have good way to test it
+ osd::u32 filemarks_num = 0;
+ while (m_pos < m_file_size) {
+ if (m_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos));
+ if (marker == simh_marker::TAPE_MARK) { // we count filemarks
+ m_pos += 4;
+ filemarks_num++;
+ if (filemarks_num == req_filemarks_num) // success: we're done
+ return std::pair(tape_status::OK, filemarks_num);
+
+ continue;
+ }
+ if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, filemarks_num);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
+ default:
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ }
+ }
+ // error: we reached physical EOM
+ assert(m_pos == m_file_size);
+ return std::pair(tape_status::EOM, filemarks_num);
+}
+
+std::pair<tape_status, osd::u32> simh_tape_file::space_blocks_reverse(osd::u32 req_blocks_num)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ assert(req_blocks_num > 0);
+ osd::u32 blocks_num = 0;
+ while (m_pos > 0) {
+ if (m_pos - 4 < 0) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos - 4));
+ if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
+ m_pos -= 4;
+ return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, blocks_num);
+ }
+ if (is_simh_marker_eod_reverse(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, blocks_num);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos -= 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to count data blocks
+ if (m_pos - read_len < 0) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos -= read_len;
+ blocks_num++;
+ if (blocks_num == req_blocks_num) // success: we're done
+ return std::pair(tape_status::OK, blocks_num);
+
+ break;
+ default: // we try to skip other blocks
+ if (m_pos - read_len < 0) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos -= read_len;
+ }
+ }
+ // error: we reached BOM
+ assert(m_pos == 0);
+ return std::pair(tape_status::BOM, blocks_num);
+}
+
+std::pair<tape_status, osd::u32> simh_tape_file::space_filemarks_reverse(osd::u32 req_filemarks_num, bool setmarks, bool sequential)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ assert(req_filemarks_num > 0);
+ assert(!setmarks); // TODO: SIMH doesn't define setmarks
+ assert(!sequential); // TODO: support spacing over sequential filemarks, once we have good way to test it
+ osd::u32 filemarks_num = 0;
+ while (m_pos > 0) {
+ if (m_pos - 4 < 0) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos - 4));
+ if (marker == simh_marker::TAPE_MARK) { // we count filemarks
+ m_pos -= 4;
+ filemarks_num++;
+ if (filemarks_num == req_filemarks_num) // success: we're done
+ return std::pair(tape_status::OK, filemarks_num);
+
+ continue;
+ }
+ if (is_simh_marker_eod_reverse(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, filemarks_num);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos -= 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to skip all blocks
+ default:
+ if (m_pos - read_len < 0) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos -= read_len;
+ }
+ }
+ // error: we reached BOM
+ assert(m_pos == 0);
+ return std::pair(tape_status::BOM, filemarks_num);
+}
+
+std::pair<tape_status, osd::u32> simh_tape_file::read_block(osd::u8 *buf, osd::u32 buf_size)
+{
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ while (m_pos < m_file_size) {
+ if (m_pos + 4 > m_file_size) // error: truncated marker
+ throw std::runtime_error("truncated marker");
+
+ const simh_marker marker = simh_marker(read_word(m_pos));
+ if (marker == simh_marker::TAPE_MARK) { // error: we reached filemark
+ m_pos += 4;
+ return std::pair(is_ew() ? tape_status::FILEMARK_EW : tape_status::FILEMARK, 0);
+ }
+ if (is_simh_marker_eod_forward(marker)) // error: we reached EOD
+ return std::pair(is_ew() ? tape_status::EOD_EW : tape_status::EOD, 0);
+
+ const simh_marker_class marker_class = get_simh_marker_class(marker);
+ const osd::u32 block_len = get_simh_marker_value(marker);
+ const osd::u32 pad_len = block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 read_len = 4 + block_len + pad_len + 4;
+ switch (marker_class) {
+ case simh_marker_class::PRIVATE_MARKER: // we skip other markers
+ case simh_marker_class::RESERVED_MARKER:
+ m_pos += 4;
+ break;
+ case simh_marker_class::GOOD_DATA_RECORD: // we try to read data blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ if (block_len > buf_size) // error: block length too big
+ throw std::runtime_error("block length too big");
+
+ // success: we read another block
+ read_bytes(m_pos + 4, buf, block_len);
+ m_pos += read_len;
+ return std::pair(tape_status::OK, block_len);
+
+ default: // we try to skip other blocks
+ if (m_pos + read_len > m_file_size) // error: truncated block
+ throw std::runtime_error("truncated block");
+
+ m_pos += read_len;
+ }
+ }
+ // error: we reached physical EOM
+ assert(m_pos == m_file_size);
+ return std::pair(tape_status::EOM, 0);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+// destructive operations
+
+void simh_tape_file::erase(bool eom)
+{
+ assert(!m_read_only);
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ const osd::u32 write_len = m_file_size - m_pos; // we always erase entire remainder of tape
+ write_byte_repeat(m_pos, 0xff, write_len); // we assume simh_marker::EOM == 0xffffffff
+ m_pos += write_len;
+}
+
+tape_status simh_tape_file::write_block(const osd::u8 *buf, osd::u32 req_block_len)
+{
+ assert(!m_read_only);
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ const osd::u32 pad_len = req_block_len % 2; // pad odd-length blocks with 1 byte
+ const osd::u32 write_len = 4 + req_block_len + pad_len + 4;
+ if (m_pos + write_len >= m_file_size) // error: we reached physical EOM
+ return tape_status::EOM;
+
+ write_word(m_pos, req_block_len);
+ write_bytes(m_pos + 4, buf, req_block_len);
+ write_byte_repeat(m_pos + 4 + req_block_len, 0, pad_len);
+ write_word(m_pos + 4 + req_block_len + pad_len, req_block_len);
+ m_pos += write_len;
+ return is_ew() ? tape_status::EW : tape_status::OK; // success: we wrote another block
+}
+
+tape_status simh_tape_file::write_filemarks(osd::u32 req_filemarks_num, bool setmarks)
+{
+ assert(!m_read_only);
+ assert(m_pos <= m_file_size);
+ assert(m_pos % 2 == 0);
+ assert(!setmarks); // TODO: SIMH doesn't define setmarks
+ const osd::u32 write_len = req_filemarks_num * 4;
+ if (m_pos + write_len >= m_file_size) // error: we reached physical EOM
+ return tape_status::EOM;
+
+ for (osd::u32 i = 0; i < write_len; i += 4)
+ write_word(m_pos + i, osd::u32(simh_marker::TAPE_MARK));
+ m_pos += write_len;
+ return is_ew() ? tape_status::EW : tape_status::OK; // success: we wrote all filemarks
+}
+
+//////////////////////////////////////////////////////////////////////////////
diff --git a/src/lib/util/simh_tape_file.h b/src/lib/util/simh_tape_file.h
new file mode 100644
index 00000000000..62c704a376b
--- /dev/null
+++ b/src/lib/util/simh_tape_file.h
@@ -0,0 +1,65 @@
+// license:BSD-3-Clause
+// copyright-holders:Mietek Bak
+#ifndef MAME_UTIL_SIMH_TAPE_FILE_H
+#define MAME_UTIL_SIMH_TAPE_FILE_H
+
+#pragma once
+
+#include "tape_file_interface.h"
+#include "utilfwd.h"
+
+#include "osdcomm.h"
+
+#include <string>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class simh_tape_file : public tape_file_interface
+{
+public:
+ // construction and destruction
+ simh_tape_file(util::random_read_write &file, osd::u64 file_size, bool read_only, bool create = false);
+ virtual ~simh_tape_file();
+
+ // position-preserving operations
+ virtual bool is_read_only() const override { return m_read_only; }
+ virtual bool is_ew() const override { return m_pos + 32768 >= m_file_size; } // 32KB from EOM; TODO: ANSI says EW should be 10ft from EOM regardless of density
+ virtual osd::u8 get_density_code() const override { return 0; } // TODO: SIMH doesn't define density
+ virtual std::pair<tape_status, osd::u32> read_position() const override;
+
+ // non-destructive operations
+ virtual void rewind(bool eom) override;
+ virtual tape_status locate_block(osd::u32 req_block_addr) override;
+ virtual tape_status space_eod() override;
+ virtual std::pair<tape_status, osd::u32> space_blocks(osd::u32 req_blocks_num) override;
+ virtual std::pair<tape_status, osd::u32> space_blocks_reverse(osd::u32 req_blocks_num) override;
+ virtual std::pair<tape_status, osd::u32> space_filemarks(osd::u32 req_marks_num, bool setmarks = false, bool sequential = false) override;
+ virtual std::pair<tape_status, osd::u32> space_filemarks_reverse(osd::u32 req_marks_num, bool setmarks = false, bool sequential = false) override;
+ virtual std::pair<tape_status, osd::u32> read_block(osd::u8 *buf, osd::u32 buf_size) override;
+
+ // destructive operations
+ virtual void erase(bool eom) override;
+ virtual tape_status write_block(const osd::u8 *buf, osd::u32 len) override;
+ virtual tape_status write_filemarks(osd::u32 req_marks_num, bool setmarks = false) override;
+
+protected:
+ // internal operations
+ void raw_seek(const osd::u64 pos) const;
+ void raw_read(osd::u8 *const buf, const osd::u32 len) const;
+ void raw_write(const osd::u8 *const buf, const osd::u32 len) const;
+ void read_bytes(const osd::u64 pos, osd::u8 *const buf, const osd::u32 len) const;
+ osd::u32 read_word(const osd::u64 pos) const;
+ void write_bytes(const osd::u64 pos, const osd::u8 *const buf, const osd::u32 len) const;
+ void write_byte_repeat(const osd::u64 pos, const osd::u8 data, const osd::u32 len) const;
+ void write_word(const osd::u64 pos, const osd::u32 data) const;
+
+ // state
+ util::random_read_write &m_file; // tape image file
+ osd::u64 m_file_size; // size of tape image file
+ bool m_read_only; // should we disallow destructive operations on tape image
+ osd::u64 m_pos; // tape position
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // MAME_UTIL_SIMH_TAPE_FILE_H
diff --git a/src/lib/util/strformat.cpp b/src/lib/util/strformat.cpp
index c47bb1b8996..f62ba55dc2d 100644
--- a/src/lib/util/strformat.cpp
+++ b/src/lib/util/strformat.cpp
@@ -9,7 +9,6 @@
***************************************************************************/
#include "strformat.h"
-#include "vecstream.h"
#include <iostream>
#include <sstream>
@@ -24,69 +23,115 @@ template class format_chars<wchar_t>;
template void format_flags::apply(std::ostream &) const;
template void format_flags::apply(std::wostream &) const;
-template void format_flags::apply(std::iostream &) const;
-template void format_flags::apply(std::wiostream &) const;
-template void format_flags::apply(std::ostringstream &) const;
-template void format_flags::apply(std::wostringstream &) const;
-template void format_flags::apply(std::stringstream &) const;
-template void format_flags::apply(std::wstringstream &) const;
-template void format_flags::apply(ovectorstream &) const;
-template void format_flags::apply(wovectorstream &) const;
-template void format_flags::apply(vectorstream &) const;
-template void format_flags::apply(wvectorstream &) const;
-
-template class format_argument<std::ostream>;
-template class format_argument<std::wostream>;
-template class format_argument<std::iostream>;
-template class format_argument<std::wiostream>;
-template class format_argument<std::ostringstream>;
-template class format_argument<std::wostringstream>;
-template class format_argument<std::stringstream>;
-template class format_argument<std::wstringstream>;
-template class format_argument<ovectorstream>;
-template class format_argument<wovectorstream>;
-template class format_argument<vectorstream>;
-template class format_argument<wvectorstream>;
-
-template class format_argument_pack<std::ostream>;
-template class format_argument_pack<std::wostream>;
-template class format_argument_pack<std::iostream>;
-template class format_argument_pack<std::wiostream>;
-template class format_argument_pack<std::ostringstream>;
-template class format_argument_pack<std::wostringstream>;
-template class format_argument_pack<std::stringstream>;
-template class format_argument_pack<std::wstringstream>;
-template class format_argument_pack<ovectorstream>;
-template class format_argument_pack<wovectorstream>;
-template class format_argument_pack<vectorstream>;
-template class format_argument_pack<wvectorstream>;
-
-template std::ostream::off_type stream_format(std::ostream &, format_argument_pack<std::ostream> const &);
-template std::wostream::off_type stream_format(std::wostream &, format_argument_pack<std::wostream> const &);
-template std::iostream::off_type stream_format(std::iostream &, format_argument_pack<std::ostream> const &);
-template std::iostream::off_type stream_format(std::iostream &, format_argument_pack<std::iostream> const &);
-template std::wiostream::off_type stream_format(std::wiostream &, format_argument_pack<std::wostream> const &);
-template std::wiostream::off_type stream_format(std::wiostream &, format_argument_pack<std::wiostream> const &);
-template std::ostringstream::off_type stream_format(std::ostringstream &, format_argument_pack<std::ostream> const &);
-template std::ostringstream::off_type stream_format(std::ostringstream &, format_argument_pack<std::ostringstream> const &);
-template std::wostringstream::off_type stream_format(std::wostringstream &, format_argument_pack<std::wostream> const &);
-template std::wostringstream::off_type stream_format(std::wostringstream &, format_argument_pack<std::wostringstream> const &);
-template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::ostream> const &);
-template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::iostream> const &);
-template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::stringstream> const &);
-template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wostream> const &);
-template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wiostream> const &);
-template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wstringstream> const &);
-template ovectorstream::off_type stream_format(ovectorstream &, format_argument_pack<std::ostream> const &);
-template ovectorstream::off_type stream_format(ovectorstream &, format_argument_pack<ovectorstream> const &);
-template wovectorstream::off_type stream_format(wovectorstream &, format_argument_pack<std::wostream> const &);
-template wovectorstream::off_type stream_format(wovectorstream &, format_argument_pack<wovectorstream> const &);
-template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<std::ostream> const &);
-template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<std::iostream> const &);
-template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<vectorstream> const &);
-template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<std::wostream> const &);
-template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<std::wiostream> const &);
-template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<wvectorstream> const &);
+
+template class format_argument<char>;
+template void format_argument<char>::static_output<bool>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<char>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<signed char>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<unsigned char>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<short>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<unsigned short>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<int>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<unsigned int>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<long>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<unsigned long>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<long long>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<unsigned long long>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<char *>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<char const *>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<std::string>(std::ostream &, format_flags const &, void const *);
+template void format_argument<char>::static_output<std::string_view>(std::ostream &, format_flags const &, void const *);
+template bool format_argument<char>::static_make_integer<bool>(void const *, int &);
+template bool format_argument<char>::static_make_integer<char>(void const *, int &);
+template bool format_argument<char>::static_make_integer<signed char>(void const *, int &);
+template bool format_argument<char>::static_make_integer<unsigned char>(void const *, int &);
+template bool format_argument<char>::static_make_integer<short>(void const *, int &);
+template bool format_argument<char>::static_make_integer<unsigned short>(void const *, int &);
+template bool format_argument<char>::static_make_integer<int>(void const *, int &);
+template bool format_argument<char>::static_make_integer<unsigned int>(void const *, int &);
+template bool format_argument<char>::static_make_integer<long>(void const *, int &);
+template bool format_argument<char>::static_make_integer<unsigned long>(void const *, int &);
+template bool format_argument<char>::static_make_integer<long long>(void const *, int &);
+template bool format_argument<char>::static_make_integer<unsigned long long>(void const *, int &);
+template bool format_argument<char>::static_make_integer<char *>(void const *, int &);
+template bool format_argument<char>::static_make_integer<char const *>(void const *, int &);
+template bool format_argument<char>::static_make_integer<std::string>(void const *, int &);
+template bool format_argument<char>::static_make_integer<std::string_view>(void const *, int &);
+template void format_argument<char>::static_store_integer<bool>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<char>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<signed char>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<unsigned char>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<short>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<unsigned short>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<int>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<unsigned int>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<long>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<unsigned long>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<long long>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<unsigned long long>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<char *>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<char const *>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<std::string>(void const *, std::streamoff);
+template void format_argument<char>::static_store_integer<std::string_view>(void const *, std::streamoff);
+
+template class format_argument<wchar_t>;
+template void format_argument<wchar_t>::static_output<bool>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<char>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<signed char>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<unsigned char>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<wchar_t>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<short>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<unsigned short>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<int>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<unsigned int>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<long>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<unsigned long>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<long long>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<unsigned long long>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<wchar_t *>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<wchar_t const *>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<std::wstring>(std::wostream &, format_flags const &, void const *);
+template void format_argument<wchar_t>::static_output<std::wstring_view>(std::wostream &, format_flags const &, void const *);
+template bool format_argument<wchar_t>::static_make_integer<bool>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<char>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<signed char>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<unsigned char>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<wchar_t>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<short>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<unsigned short>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<int>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<unsigned int>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<long>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<unsigned long>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<long long>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<unsigned long long>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<wchar_t *>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<wchar_t const *>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<std::wstring>(void const *, int &);
+template bool format_argument<wchar_t>::static_make_integer<std::wstring_view>(void const *, int &);
+template void format_argument<wchar_t>::static_store_integer<bool>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<char>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<signed char>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<unsigned char>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<wchar_t>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<short>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<unsigned short>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<int>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<unsigned int>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<long>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<unsigned long>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<long long>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<unsigned long long>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<wchar_t *>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<wchar_t const *>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<std::wstring>(void const *, std::streamoff);
+template void format_argument<wchar_t>::static_store_integer<std::wstring_view>(void const *, std::streamoff);
+
+template class format_argument_pack<char>;
+template class format_argument_pack<wchar_t>;
+
+template std::ostream::off_type stream_format(std::ostream &, format_argument_pack<char> const &);
+template std::wostream::off_type stream_format(std::wostream &, format_argument_pack<wchar_t> const &);
} // namespace detail
diff --git a/src/lib/util/strformat.h b/src/lib/util/strformat.h
index db3666e448b..f09965bc002 100644
--- a/src/lib/util/strformat.h
+++ b/src/lib/util/strformat.h
@@ -106,11 +106,12 @@
The format string type can be a pointer to a NUL-terminated string,
an array containing a NUL-terminated or non-terminated string, or a
STL contiguous container holding a string (e.g. std::string,
- std::vector or std::array). Note that NUL characters characters are
- only treated as terminators for pointers and arrays, they are
- treated as normal characters for other containers. A non-contiguous
- container (e.g. std::list or std::deque) will result in undesirable
- behaviour likely culminating in a crash.
+ std::string_view, std::vector or std::array). Note that NUL
+ characters characters are only treated as terminators for pointers
+ and arrays, they are treated as normal characters for other
+ containers. Using a non-contiguous container (e.g. std::list or
+ std::deque) will result in undesirable behaviour likely culminating
+ in a crash.
The value type of the format string and the character type of the
output stream/string need to match. You can't use a wchar_t format
@@ -131,6 +132,7 @@
- Inappropriate type for parameterised width/precision
- Positional width/precision specifier not terminated with $
- Inappropriate type for n conversion
+ - Default conversion for type that lacks stream out operator
Some limitations have been described in passing. Major limitations
and bugs include:
@@ -171,8 +173,6 @@
#pragma once
-#include "vecstream.h"
-
#include <algorithm>
#include <array>
#include <cassert>
@@ -184,26 +184,14 @@
#include <locale>
#include <sstream>
#include <string>
+#include <string_view>
#include <type_traits>
#include <utility>
-#if defined(__GLIBCXX__)
-namespace std {
-namespace mame_cxx14_compat {
-template <typename T>
-inline constexpr auto cbegin(const T& cont) noexcept(noexcept(std::begin(cont))) -> decltype(std::begin(cont))
-{ return std::begin(cont); }
-
-template <typename T>
-inline constexpr auto cend(const T& cont) noexcept(noexcept(std::end(cont))) -> decltype(std::end(cont))
-{ return std::end(cont); }
-}
-using namespace mame_cxx14_compat;
-}
-#endif
-
namespace util {
+
namespace detail {
+
//**************************************************************************
// FORMAT CHARACTER DEFINITIONS
//**************************************************************************
@@ -370,52 +358,54 @@ public:
{
}
- template <typename Stream> void apply(Stream &stream) const
+ template <typename Character, typename Traits = std::char_traits<Character> >
+ void apply(std::basic_ostream<Character, Traits> &stream) const
{
- typedef format_chars<typename Stream::char_type> chars;
+ using stream_type = std::basic_ostream<Character, Traits>;
+ using chars = format_chars<Character>;
stream.unsetf(
- Stream::basefield |
- Stream::adjustfield |
- Stream::floatfield |
- Stream::boolalpha |
- Stream::showbase |
- Stream::showpoint |
- Stream::showpos |
- Stream::uppercase);
-
- if (get_alternate_format()) stream.setf(Stream::showbase | Stream::showpoint);
+ stream_type::basefield |
+ stream_type::adjustfield |
+ stream_type::floatfield |
+ stream_type::boolalpha |
+ stream_type::showbase |
+ stream_type::showpoint |
+ stream_type::showpos |
+ stream_type::uppercase);
+
+ if (get_alternate_format()) stream.setf(stream_type::showbase | stream_type::showpoint);
stream.fill(get_zero_pad() ? chars::zero : chars::space);
- stream.setf(get_left_align() ? Stream::left : get_zero_pad() ? Stream::internal : Stream::right);
- if (positive_sign::plus == get_positive_sign()) stream.setf(Stream::showpos);
+ stream.setf(get_left_align() ? stream_type::left : get_zero_pad() ? stream_type::internal : stream_type::right);
+ if (positive_sign::plus == get_positive_sign()) stream.setf(stream_type::showpos);
stream.precision((get_precision() < 0) ? 6 : get_precision());
stream.width(get_field_width());
- if (get_uppercase()) stream.setf(Stream::uppercase);
+ if (get_uppercase()) stream.setf(stream_type::uppercase);
switch (get_conversion())
{
case conversion::unspecified:
break;
case conversion::signed_decimal:
case conversion::unsigned_decimal:
- stream.setf(Stream::dec);
+ stream.setf(stream_type::dec);
break;
case conversion::octal:
- stream.setf(Stream::oct);
+ stream.setf(stream_type::oct);
break;
case conversion::hexadecimal:
- stream.setf(Stream::hex | Stream::scientific | Stream::fixed);
+ stream.setf(stream_type::hex | stream_type::scientific | stream_type::fixed);
break;
case conversion::scientific_decimal:
- stream.setf(Stream::dec | Stream::scientific);
+ stream.setf(stream_type::dec | stream_type::scientific);
break;
case conversion::fixed_decimal:
- stream.setf(Stream::dec | Stream::fixed);
+ stream.setf(stream_type::dec | stream_type::fixed);
break;
case conversion::floating_decimal:
- stream.setf(Stream::dec);
+ stream.setf(stream_type::dec);
break;
case conversion::scientific_hexadecimal:
- stream.setf(Stream::hex | Stream::scientific | Stream::fixed);
+ stream.setf(stream_type::hex | stream_type::scientific | stream_type::fixed);
break;
case conversion::character:
case conversion::string:
@@ -556,6 +546,7 @@ public:
case conversion::octal:
case conversion::hexadecimal:
m_positive_sign = positive_sign::none;
+ [[fallthrough]];
case conversion::signed_decimal:
if (0 <= m_precision)
m_zero_pad = false;
@@ -588,207 +579,268 @@ template <typename Stream, typename T>
class format_output
{
private:
- template <typename U> struct signed_integer_semantics
- { static constexpr bool value = std::is_integral<U>::value && std::is_signed<U>::value; };
- template <typename U> struct unsigned_integer_semantics
- { static constexpr bool value = std::is_integral<U>::value && !std::is_signed<U>::value; };
- template <typename U> struct default_semantics
- { static constexpr bool value = !signed_integer_semantics<U>::value && !unsigned_integer_semantics<U>::value; };
+ template <typename U>
+ struct string_semantics : public std::false_type { };
+ template <typename CharT, typename Traits, typename Allocator>
+ struct string_semantics<std::basic_string<CharT, Traits, Allocator> > : public std::true_type { };
+ template <typename CharT, typename Traits>
+ struct string_semantics<std::basic_string_view<CharT, Traits> > : public std::true_type { };
+ template <typename U>
+ using signed_integer_semantics = std::bool_constant<std::is_integral_v<U> && std::is_signed_v<U> >;
+ template <typename U>
+ using unsigned_integer_semantics = std::bool_constant<std::is_integral_v<U> && !std::is_signed_v<U> >;
+
+ template <typename U>
+ static std::enable_if_t<std::is_same_v<std::make_signed_t<U>, std::make_signed_t<char> > || std::is_integral_v<U> > apply_signed(Stream &str, U const &value)
+ {
+ if constexpr (std::is_same_v<std::make_signed_t<U>, std::make_signed_t<char> >)
+ str << int(std::make_signed_t<U>(value));
+ else if constexpr (!std::is_signed_v<U> || std::is_same_v<typename Stream::char_type, U>)
+ str << std::make_signed_t<U>(value);
+#if __cplusplus > 201703L
+ else if constexpr (!std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
+ str << std::make_signed_t<U>(value);
+#endif
+ else
+ str << value;
+ }
+
+ template <typename U>
+ static std::enable_if_t<std::is_same_v<std::make_unsigned_t<U>, std::make_unsigned_t<char> > || std::is_integral_v<U> > apply_unsigned(Stream &str, U const &value)
+ {
+ if constexpr (std::is_same_v<std::make_unsigned_t<U>, std::make_unsigned_t<char> >)
+ str << unsigned(std::make_unsigned_t<U>(value));
+ else if constexpr (!std::is_unsigned_v<U> || std::is_same_v<typename Stream::char_type, U>)
+ str << std::make_unsigned_t<U>(value);
+#if __cplusplus > 201703L
+ else if constexpr (!std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
+ str << std::make_unsigned_t<U>(value);
+#endif
+ else
+ str << value;
+ }
public:
template <typename U>
- static void apply(std::enable_if_t<signed_integer_semantics<U>::value, Stream> &str, format_flags const &flags, U const &value)
+ static void apply(Stream &str, format_flags const &flags, U const &value)
{
- switch (flags.get_conversion())
+ if constexpr (string_semantics<U>::value)
{
- case format_flags::conversion::signed_decimal:
- switch (flags.get_length())
+ int const precision(flags.get_precision());
+ if ((0 <= precision) && (value.size() > unsigned(precision)))
{
- case format_flags::length::character:
- str << int(static_cast<signed char>(value));
- break;
- case format_flags::length::short_integer:
- str << short(value);
- break;
- case format_flags::length::long_integer:
- str << long(value);
- break;
- case format_flags::length::long_long_integer:
- str << static_cast<long long>(value);
- break;
- case format_flags::length::integer_maximum:
- str << std::intmax_t(value);
- break;
- case format_flags::length::size_type:
- str << std::make_signed_t<std::size_t>(value);
- break;
- case format_flags::length::pointer_difference:
- str << std::make_signed_t<std::ptrdiff_t>(value);
- break;
- case format_flags::length::integer_32:
- str << std::uint32_t(std::int32_t(value));
- break;
- case format_flags::length::integer_64:
- str << std::int64_t(value);
- break;
- default:
- if (std::is_same<std::make_signed_t<U>, std::make_signed_t<char> >::value)
- str << int(value);
+ if constexpr (std::is_same_v<typename U::value_type, typename Stream::char_type>)
+ {
+ unsigned width(flags.get_field_width());
+ bool const pad(unsigned(precision) < width);
+ typename Stream::fmtflags const adjust(str.flags() & Stream::adjustfield);
+ if (!pad || (Stream::left == adjust)) str.write(&*value.begin(), unsigned(precision));
+ if (pad)
+ {
+ for (width -= precision; 0U < width; --width) str.put(str.fill());
+ if (Stream::left != adjust) str.write(&*value.begin(), unsigned(precision));
+ }
+ str.width(0);
+ }
else
- str << value;
+ {
+ str << value.substr(0, unsigned(precision));
+ }
}
- break;
- case format_flags::conversion::unsigned_decimal:
- case format_flags::conversion::octal:
- case format_flags::conversion::hexadecimal:
- switch (flags.get_length())
+ else
{
- case format_flags::length::character:
- str << unsigned(static_cast<unsigned char>(static_cast<signed char>(value)));
- break;
- case format_flags::length::short_integer:
- str << static_cast<unsigned short>(short(value));
- break;
- case format_flags::length::long_integer:
- str << static_cast<unsigned long>(long(value));
- break;
- case format_flags::length::long_long_integer:
- str << static_cast<unsigned long long>(static_cast<long long>(value));
- break;
- case format_flags::length::integer_maximum:
- str << std::uintmax_t(std::intmax_t(value));
- break;
- case format_flags::length::size_type:
- str << std::make_unsigned_t<std::size_t>(std::make_signed_t<std::size_t>(value));
- break;
- case format_flags::length::pointer_difference:
- str << std::make_unsigned_t<std::ptrdiff_t>(std::make_signed_t<std::ptrdiff_t>(value));
- break;
- case format_flags::length::integer_32:
- str << std::uint32_t(std::int32_t(value));
- break;
- case format_flags::length::integer_64:
- str << std::uint64_t(std::int64_t(value));
- break;
- default:
- if (std::is_same<std::make_unsigned_t<U>, std::make_unsigned_t<char> >::value)
- str << unsigned(std::make_unsigned_t<U>(value));
- else
- str << std::make_unsigned_t<U>(value);
+ str << value;
}
- break;
- case format_flags::conversion::character:
- if (std::is_signed<typename Stream::char_type>::value)
- str << typename Stream::char_type(value);
- else
- str << typename Stream::char_type(std::make_signed_t<typename Stream::char_type>(value));
- break;
- case format_flags::conversion::pointer:
- str << reinterpret_cast<void const *>(std::uintptr_t(std::intptr_t(value)));
- break;
- default:
- str << value;
}
- }
- template <typename U>
- static void apply(std::enable_if_t<unsigned_integer_semantics<U>::value, Stream> &str, format_flags const &flags, U const &value)
- {
- switch (flags.get_conversion())
+ else if constexpr (signed_integer_semantics<U>::value)
{
- case format_flags::conversion::signed_decimal:
- switch (flags.get_length())
+ switch (flags.get_conversion())
{
- case format_flags::length::character:
- str << int(static_cast<signed char>(static_cast<unsigned char>(value)));
- break;
- case format_flags::length::short_integer:
- str << short(static_cast<unsigned short>(value));
- break;
- case format_flags::length::long_integer:
- str << long(static_cast<unsigned long>(value));
- break;
- case format_flags::length::long_long_integer:
- str << static_cast<long long>(static_cast<unsigned long long>(value));
- break;
- case format_flags::length::integer_maximum:
- str << std::intmax_t(std::uintmax_t(value));
- break;
- case format_flags::length::size_type:
- str << std::make_signed_t<std::size_t>(std::make_unsigned_t<std::size_t>(value));
+ case format_flags::conversion::signed_decimal:
+ switch (flags.get_length())
+ {
+ case format_flags::length::character:
+ str << int(static_cast<signed char>(value));
+ break;
+ case format_flags::length::short_integer:
+ str << short(value);
+ break;
+ case format_flags::length::long_integer:
+ str << long(value);
+ break;
+ case format_flags::length::long_long_integer:
+ str << static_cast<long long>(value);
+ break;
+ case format_flags::length::integer_maximum:
+ str << std::intmax_t(value);
+ break;
+ case format_flags::length::size_type:
+ str << std::make_signed_t<std::size_t>(value);
+ break;
+ case format_flags::length::pointer_difference:
+ str << std::make_signed_t<std::ptrdiff_t>(value);
+ break;
+ case format_flags::length::integer_32:
+ str << std::uint32_t(std::int32_t(value));
+ break;
+ case format_flags::length::integer_64:
+ str << std::int64_t(value);
+ break;
+ default:
+ apply_signed(str, value);
+ }
break;
- case format_flags::length::pointer_difference:
- str << std::make_signed_t<std::ptrdiff_t>(std::make_unsigned_t<std::ptrdiff_t>(value));
+ case format_flags::conversion::unsigned_decimal:
+ case format_flags::conversion::octal:
+ case format_flags::conversion::hexadecimal:
+ switch (flags.get_length())
+ {
+ case format_flags::length::character:
+ str << unsigned(static_cast<unsigned char>(static_cast<signed char>(value)));
+ break;
+ case format_flags::length::short_integer:
+ str << static_cast<unsigned short>(short(value));
+ break;
+ case format_flags::length::long_integer:
+ str << static_cast<unsigned long>(long(value));
+ break;
+ case format_flags::length::long_long_integer:
+ str << static_cast<unsigned long long>(static_cast<long long>(value));
+ break;
+ case format_flags::length::integer_maximum:
+ str << std::uintmax_t(std::intmax_t(value));
+ break;
+ case format_flags::length::size_type:
+ str << std::make_unsigned_t<std::size_t>(std::make_signed_t<std::size_t>(value));
+ break;
+ case format_flags::length::pointer_difference:
+ str << std::make_unsigned_t<std::ptrdiff_t>(std::make_signed_t<std::ptrdiff_t>(value));
+ break;
+ case format_flags::length::integer_32:
+ str << std::uint32_t(std::int32_t(value));
+ break;
+ case format_flags::length::integer_64:
+ str << std::uint64_t(std::int64_t(value));
+ break;
+ default:
+ apply_unsigned(str, value);
+ }
break;
- case format_flags::length::integer_32:
- str << std::int32_t(std::uint32_t(value));
+ case format_flags::conversion::character:
+ if (std::is_signed<typename Stream::char_type>::value)
+ str << typename Stream::char_type(value);
+ else
+ str << typename Stream::char_type(std::make_signed_t<typename Stream::char_type>(value));
break;
- case format_flags::length::integer_64:
- str << std::int64_t(std::uint64_t(value));
+ case format_flags::conversion::pointer:
+ str << reinterpret_cast<void const *>(std::uintptr_t(std::intptr_t(value)));
break;
default:
- if (std::is_same<std::make_signed_t<U>, std::make_signed_t<char> >::value)
- str << int(std::make_signed_t<U>(value));
- else
- str << std::make_signed_t<U>(value);
+ str << value;
}
- break;
- case format_flags::conversion::unsigned_decimal:
- case format_flags::conversion::octal:
- case format_flags::conversion::hexadecimal:
- switch (flags.get_length())
+ }
+ else if constexpr (unsigned_integer_semantics<U>::value)
+ {
+ switch (flags.get_conversion())
{
- case format_flags::length::character:
- str << unsigned(static_cast<unsigned char>(value));
- break;
- case format_flags::length::short_integer:
- str << static_cast<unsigned short>(value);
- break;
- case format_flags::length::long_integer:
- str << static_cast<unsigned long>(value);
- break;
- case format_flags::length::long_long_integer:
- str << static_cast<unsigned long long>(value);
- break;
- case format_flags::length::integer_maximum:
- str << std::uintmax_t(value);
- break;
- case format_flags::length::size_type:
- str << std::make_unsigned_t<std::size_t>(value);
+ case format_flags::conversion::signed_decimal:
+ switch (flags.get_length())
+ {
+ case format_flags::length::character:
+ str << int(static_cast<signed char>(static_cast<unsigned char>(value)));
+ break;
+ case format_flags::length::short_integer:
+ str << short(static_cast<unsigned short>(value));
+ break;
+ case format_flags::length::long_integer:
+ str << long(static_cast<unsigned long>(value));
+ break;
+ case format_flags::length::long_long_integer:
+ str << static_cast<long long>(static_cast<unsigned long long>(value));
+ break;
+ case format_flags::length::integer_maximum:
+ str << std::intmax_t(std::uintmax_t(value));
+ break;
+ case format_flags::length::size_type:
+ str << std::make_signed_t<std::size_t>(std::make_unsigned_t<std::size_t>(value));
+ break;
+ case format_flags::length::pointer_difference:
+ str << std::make_signed_t<std::ptrdiff_t>(std::make_unsigned_t<std::ptrdiff_t>(value));
+ break;
+ case format_flags::length::integer_32:
+ str << std::int32_t(std::uint32_t(value));
+ break;
+ case format_flags::length::integer_64:
+ str << std::int64_t(std::uint64_t(value));
+ break;
+ default:
+ apply_signed(str, value);
+ }
break;
- case format_flags::length::pointer_difference:
- str << std::make_unsigned_t<std::ptrdiff_t>(value);
+ case format_flags::conversion::unsigned_decimal:
+ case format_flags::conversion::octal:
+ case format_flags::conversion::hexadecimal:
+ switch (flags.get_length())
+ {
+ case format_flags::length::character:
+ str << unsigned(static_cast<unsigned char>(value));
+ break;
+ case format_flags::length::short_integer:
+ str << static_cast<unsigned short>(value);
+ break;
+ case format_flags::length::long_integer:
+ str << static_cast<unsigned long>(value);
+ break;
+ case format_flags::length::long_long_integer:
+ str << static_cast<unsigned long long>(value);
+ break;
+ case format_flags::length::integer_maximum:
+ str << std::uintmax_t(value);
+ break;
+ case format_flags::length::size_type:
+ str << std::make_unsigned_t<std::size_t>(value);
+ break;
+ case format_flags::length::pointer_difference:
+ str << std::make_unsigned_t<std::ptrdiff_t>(value);
+ break;
+ case format_flags::length::integer_32:
+ str << std::uint32_t(std::int32_t(value));
+ break;
+ case format_flags::length::integer_64:
+ str << std::int64_t(value);
+ break;
+ default:
+ apply_unsigned(str, value);
+ }
break;
- case format_flags::length::integer_32:
- str << std::uint32_t(std::int32_t(value));
+ case format_flags::conversion::character:
+ if (std::is_signed<typename Stream::char_type>::value)
+ str << typename Stream::char_type(value);
+ else
+ str << typename Stream::char_type(std::make_signed_t<typename Stream::char_type>(value));
break;
- case format_flags::length::integer_64:
- str << std::int64_t(value);
+ case format_flags::conversion::pointer:
+ str << reinterpret_cast<void const *>(std::uintptr_t(value));
break;
default:
- if (std::is_same<std::make_unsigned_t<U>, std::make_unsigned_t<char> >::value)
- str << unsigned(value);
+#if __cplusplus > 201703L
+ if constexpr (!std::is_invocable_v<decltype([] (auto &x, auto &y) -> decltype(x << y) { return x << y; }), Stream &, U const &>)
+ {
+ assert(false); // stream out operator not declared or declared deleted
+ str << '?';
+ }
else
+#endif
+ {
str << value;
+ }
}
- break;
- case format_flags::conversion::character:
- if (std::is_signed<typename Stream::char_type>::value)
- str << typename Stream::char_type(value);
- else
- str << typename Stream::char_type(std::make_signed_t<typename Stream::char_type>(value));
- break;
- case format_flags::conversion::pointer:
- str << reinterpret_cast<void const *>(std::uintptr_t(value));
- break;
- default:
+ }
+ else
+ {
str << value;
}
}
- template <typename U>
- static void apply(std::enable_if_t<default_semantics<U>::value, Stream> &str, format_flags const &flags, U const &value)
- {
- str << value;
- }
static void apply(Stream &str, format_flags const &flags, bool value)
{
switch (flags.get_conversion())
@@ -810,88 +862,59 @@ public:
str << value;
}
}
- template <typename CharT, typename Traits, typename Allocator>
- static void apply(std::enable_if_t<std::is_same<CharT, typename Stream::char_type>::value, Stream> &str, format_flags const &flags, std::basic_string<CharT, Traits, Allocator> const &value)
- {
- int const precision(flags.get_precision());
- if ((0 <= precision) && (value.size() > unsigned(precision)))
- {
- unsigned width(flags.get_field_width());
- bool const pad(unsigned(precision) < width);
- typename Stream::fmtflags const adjust(str.flags() & Stream::adjustfield);
- if (!pad || (Stream::left == adjust)) str.write(&*value.begin(), unsigned(precision));
- if (pad)
- {
- for (width -= precision; 0U < width; --width) str.put(str.fill());
- if (Stream::left != adjust) str.write(&*value.begin(), unsigned(precision));
- }
- str.width(0);
- }
- else
- {
- str << value;
- }
- }
- template <typename CharT, typename Traits, typename Allocator>
- static void apply(std::enable_if_t<!std::is_same<CharT, typename Stream::char_type>::value, Stream> &str, format_flags const &flags, std::basic_string<CharT, Traits, Allocator> const &value)
- {
- int const precision(flags.get_precision());
- if ((0 <= precision) && (value.size() > unsigned(precision)))
- str << value.substr(0, unsigned(precision));
- else
- str << value;
- }
};
template <typename Stream, typename T>
class format_output<Stream, T *>
{
protected:
- template <typename U> struct string_semantics
- { static constexpr bool value = std::is_same<std::remove_const_t<U>, typename Stream::char_type>::value; };
+ template <typename U>
+ using string_semantics = std::bool_constant<std::is_same_v<std::remove_const_t<U>, typename Stream::char_type> >;
public:
template <typename U>
- static void apply(std::enable_if_t<string_semantics<U>::value, Stream> &str, format_flags const &flags, U const *value)
+ static void apply(Stream &str, format_flags const &flags, U const *value)
{
- switch (flags.get_conversion())
+ if constexpr (string_semantics<U>::value)
{
- case format_flags::conversion::string:
+ switch (flags.get_conversion())
{
- int precision(flags.get_precision());
- if (0 <= flags.get_precision())
+ case format_flags::conversion::string:
{
- std::streamsize cnt(0);
- for ( ; (0 < precision) && (U(format_chars<U>::nul) != value[cnt]); --precision, ++cnt) { }
- unsigned width(flags.get_field_width());
- bool const pad(std::make_unsigned_t<std::streamsize>(cnt) < width);
- typename Stream::fmtflags const adjust(str.flags() & Stream::adjustfield);
- if (!pad || (Stream::left == adjust)) str.write(value, cnt);
- if (pad)
+ int precision(flags.get_precision());
+ if (0 <= flags.get_precision())
{
- for (width -= cnt; 0U < width; --width) str.put(str.fill());
- if (Stream::left != adjust) str.write(value, cnt);
+ std::streamsize cnt(0);
+ for ( ; (0 < precision) && (U(format_chars<U>::nul) != value[cnt]); --precision, ++cnt) { }
+ unsigned width(flags.get_field_width());
+ bool const pad(std::make_unsigned_t<std::streamsize>(cnt) < width);
+ typename Stream::fmtflags const adjust(str.flags() & Stream::adjustfield);
+ if (!pad || (Stream::left == adjust)) str.write(value, cnt);
+ if (pad)
+ {
+ for (width -= cnt; 0U < width; --width) str.put(str.fill());
+ if (Stream::left != adjust) str.write(value, cnt);
+ }
+ str.width(0);
+ }
+ else
+ {
+ str << value;
}
- str.width(0);
- }
- else
- {
- str << value;
}
+ break;
+ case format_flags::conversion::pointer:
+ str << reinterpret_cast<void const *>(const_cast<std::remove_volatile_t<U> *>(value));
+ break;
+ default:
+ str << value;
}
- break;
- case format_flags::conversion::pointer:
+ }
+ else
+ {
str << reinterpret_cast<void const *>(const_cast<std::remove_volatile_t<U> *>(value));
- break;
- default:
- str << value;
}
}
- template <typename U>
- static void apply(std::enable_if_t<!string_semantics<U>::value, Stream> &str, format_flags const &flags, U const *value)
- {
- str << reinterpret_cast<void const *>(const_cast<std::remove_volatile_t<U> *>(value));
- }
};
template <typename Stream, typename T, std::size_t N>
@@ -920,27 +943,28 @@ template <typename T>
class format_make_integer
{
private:
- template <typename U> struct use_unsigned_cast
- { static constexpr bool value = std::is_convertible<U const, unsigned>::value && std::is_unsigned<U>::value; };
- template <typename U> struct use_signed_cast
- { static constexpr bool value = !use_unsigned_cast<U>::value && std::is_convertible<U const, int>::value; };
- template <typename U> struct disable
- { static constexpr bool value = !use_unsigned_cast<U>::value && !use_signed_cast<U>::value; };
+ template <typename U>
+ using use_unsigned_cast = std::bool_constant<std::is_convertible_v<U const, unsigned> && std::is_unsigned_v<U> >;
+ template <typename U>
+ using use_signed_cast = std::bool_constant<!use_unsigned_cast<U>::value && std::is_convertible_v<U const, int> >;
public:
- template <typename U> static std::enable_if_t<use_unsigned_cast<U>::value, bool> apply(U const &value, int &result)
- {
- result = int(unsigned(value));
- return true;
- }
- template <typename U> static std::enable_if_t<use_signed_cast<U>::value, bool> apply(U const &value, int &result)
+ template <typename U> static bool apply(U const &value, int &result)
{
- result = int(value);
- return true;
- }
- template <typename U> static std::enable_if_t<disable<U>::value, bool> apply(U const &value, int &result)
- {
- return false;
+ if constexpr (use_unsigned_cast<U>::value)
+ {
+ result = int(unsigned(value));
+ return true;
+ }
+ else if constexpr (use_signed_cast<U>::value)
+ {
+ result = int(value);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
}
};
@@ -953,32 +977,33 @@ template <typename T>
class format_store_integer
{
private:
- template <typename U> struct is_non_const_ptr
- { static constexpr bool value = std::is_pointer<U>::value && !std::is_const<std::remove_pointer_t<U> >::value; };
- template <typename U> struct is_unsigned_ptr
- { static constexpr bool value = std::is_pointer<U>::value && std::is_unsigned<std::remove_pointer_t<U> >::value; };
- template <typename U> struct use_unsigned_cast
- { static constexpr bool value = is_non_const_ptr<U>::value && is_unsigned_ptr<U>::value && std::is_convertible<std::make_unsigned_t<std::streamoff>, std::remove_pointer_t<U> >::value; };
- template <typename U> struct use_signed_cast
- { static constexpr bool value = is_non_const_ptr<U>::value && !use_unsigned_cast<U>::value && std::is_convertible<std::streamoff, std::remove_pointer_t<U> >::value; };
- template <typename U> struct disable
- { static constexpr bool value = !use_unsigned_cast<U>::value && !use_signed_cast<U>::value; };
+ template <typename U>
+ using is_non_const_ptr = std::bool_constant<std::is_pointer_v<U> && !std::is_const_v<std::remove_pointer_t<U> > >;
+ template <typename U>
+ using is_unsigned_ptr = std::bool_constant<std::is_pointer_v<U> && std::is_unsigned_v<std::remove_pointer_t<U> > >;
+ template <typename U>
+ using use_unsigned_cast = std::bool_constant<is_non_const_ptr<U>::value && is_unsigned_ptr<U>::value && std::is_convertible_v<std::make_unsigned_t<std::streamoff>, std::remove_pointer_t<U> > >;
+ template <typename U>
+ using use_signed_cast = std::bool_constant<is_non_const_ptr<U>::value && !use_unsigned_cast<U>::value && std::is_convertible_v<std::streamoff, std::remove_pointer_t<U> > >;
public:
- template <typename U> static std::enable_if_t<use_unsigned_cast<U>::value, bool> apply(U const &value, std::streamoff data)
- {
- *value = std::remove_pointer_t<U>(std::make_unsigned_t<std::streamoff>(data));
- return true;
- }
- template <typename U> static std::enable_if_t<use_signed_cast<U>::value, bool> apply(U const &value, std::streamoff data)
- {
- *value = std::remove_pointer_t<U>(std::make_signed_t<std::streamoff>(data));
- return true;
- }
- template <typename U> static std::enable_if_t<disable<U>::value, bool> apply(U const &value, std::streamoff data)
+ template <typename U> static bool apply(U const &value, std::streamoff data)
{
- assert(false); // inappropriate type for storing characters written so far
- return false;
+ if constexpr (use_unsigned_cast<U>::value)
+ {
+ *value = std::remove_pointer_t<U>(std::make_unsigned_t<std::streamoff>(data));
+ return true;
+ }
+ else if constexpr (use_signed_cast<U>::value)
+ {
+ *value = std::remove_pointer_t<U>(std::make_signed_t<std::streamoff>(data));
+ return true;
+ }
+ else
+ {
+ assert(false); // inappropriate type for storing characters written so far
+ return false;
+ }
}
};
@@ -987,10 +1012,12 @@ public:
// NON-POLYMORPHIC ARGUMENT WRAPPER
//**************************************************************************
-template <typename Stream>
+template <typename Character, typename Traits = std::char_traits<Character> >
class format_argument
{
public:
+ using stream_type = std::basic_ostream<Character, Traits>;
+
format_argument()
: m_value(nullptr)
, m_output_function(nullptr)
@@ -1008,18 +1035,18 @@ public:
{
}
- void output(Stream &str, format_flags const &flags) const { m_output_function(str, flags, m_value); }
+ void output(stream_type &str, format_flags const &flags) const { m_output_function(str, flags, m_value); }
bool make_integer(int &result) const { return m_make_integer_function(m_value, result); }
void store_integer(std::streamoff data) const { m_store_integer_function(m_value, data); }
private:
- typedef void (*output_function)(Stream &str, format_flags const &flags, void const *value);
+ typedef void (*output_function)(stream_type &str, format_flags const &flags, void const *value);
typedef bool (*make_integer_function)(void const *value, int &result);
typedef void (*store_integer_function)(void const *value, std::streamoff data);
- template <typename T> static void static_output(Stream &str, format_flags const &flags, void const *value)
+ template <typename T> static void static_output(stream_type &str, format_flags const &flags, void const *value)
{
- format_output<Stream, T>::apply(str, flags, *reinterpret_cast<T const *>(value));
+ format_output<stream_type, T>::apply(str, flags, *reinterpret_cast<T const *>(value));
}
template <typename T> static bool static_make_integer(void const *value, int &result)
@@ -1043,11 +1070,12 @@ private:
// NON-POLYMORPHIC ARGUMENT PACK WRAPPER BASE
//**************************************************************************
-template <typename Stream = std::ostream>
+template <typename Character = char, typename Traits = std::char_traits<Character> >
class format_argument_pack
{
public:
- typedef typename Stream::char_type char_type;
+ using stream_type = std::basic_ostream<Character, Traits>;
+ using char_type = Character;
typedef char_type const *iterator;
iterator format_begin() const
{
@@ -1061,7 +1089,7 @@ public:
{
return m_argument_count;
}
- format_argument<Stream> const &operator[](std::size_t index) const
+ format_argument<char_type, Traits> const &operator[](std::size_t index) const
{
assert(m_argument_count > index);
return m_arguments[index];
@@ -1069,16 +1097,16 @@ public:
protected:
template <typename T>
- struct handle_char_ptr { static constexpr bool value = std::is_pointer<T>::value && std::is_same<std::remove_cv_t<std::remove_pointer_t<T> >, char_type>::value; };
+ using handle_char_ptr = std::bool_constant<std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<std::remove_pointer_t<T> >, char_type> >;
template <typename T>
- struct handle_char_array { static constexpr bool value = std::is_array<T>::value && std::is_same<std::remove_cv_t<std::remove_extent_t<T> >, char_type>::value; };
+ using handle_char_array = std::bool_constant<std::is_array_v<T> && std::is_same_v<std::remove_cv_t<std::remove_extent_t<T> >, char_type> >;
template <typename T>
- struct handle_container { static constexpr bool value = !handle_char_ptr<T>::value && !handle_char_array<T>::value; };
+ using handle_container = std::bool_constant<!handle_char_ptr<T>::value && !handle_char_array<T>::value>;
template <typename Format>
format_argument_pack(
Format &&fmt,
- format_argument<Stream> const *arguments,
+ format_argument<char_type, Traits> const *arguments,
std::enable_if_t<handle_char_ptr<std::remove_reference_t<Format> >::value, std::size_t> argument_count)
: m_begin(fmt)
, m_end(nullptr)
@@ -1094,7 +1122,7 @@ protected:
template <typename Format>
format_argument_pack(
Format &&fmt,
- format_argument<Stream> const *arguments,
+ format_argument<char_type, Traits> const *arguments,
std::enable_if_t<handle_char_array<std::remove_reference_t<Format> >::value, std::size_t> argument_count)
: m_begin(std::cbegin(fmt))
, m_end(std::cend(fmt))
@@ -1110,7 +1138,7 @@ protected:
template <typename Format>
format_argument_pack(
Format &&fmt,
- format_argument<Stream> const *arguments,
+ format_argument<char_type, Traits> const *arguments,
std::enable_if_t<handle_container<std::remove_reference_t<Format> >::value, std::size_t> argument_count)
: m_begin(fmt.empty() ? nullptr : &*std::cbegin(fmt))
, m_end(fmt.empty() ? nullptr : (m_begin + std::distance(std::cbegin(fmt), std::cend(fmt))))
@@ -1124,17 +1152,17 @@ protected:
assert(m_arguments || !m_argument_count);
}
- format_argument_pack(format_argument_pack<Stream> const &) = default;
- format_argument_pack(format_argument_pack<Stream> &&) = default;
- format_argument_pack &operator=(format_argument_pack<Stream> const &) = default;
- format_argument_pack &operator=(format_argument_pack<Stream> &&) = default;
+ format_argument_pack(format_argument_pack<char_type, Traits> const &) = default;
+ format_argument_pack(format_argument_pack<char_type, Traits> &&) = default;
+ format_argument_pack &operator=(format_argument_pack<char_type, Traits> const &) = default;
+ format_argument_pack &operator=(format_argument_pack<char_type, Traits> &&) = default;
private:
- iterator m_begin;
- iterator m_end;
- bool m_check_nul;
- format_argument<Stream> const *m_arguments;
- std::size_t m_argument_count;
+ iterator m_begin;
+ iterator m_end;
+ bool m_check_nul;
+ format_argument<char_type, Traits> const *m_arguments;
+ std::size_t m_argument_count;
};
@@ -1142,27 +1170,27 @@ private:
// ARGUMENT PACK WRAPPER IMPLEMENTATION
//**************************************************************************
-template <typename Stream, std::size_t Count>
+template <typename Character, typename Traits, std::size_t Count>
class format_argument_pack_impl
- : private std::array<format_argument<Stream>, Count>
- , public format_argument_pack<Stream>
+ : private std::array<format_argument<Character, Traits>, Count>
+ , public format_argument_pack<Character, Traits>
{
public:
- using typename format_argument_pack<Stream>::iterator;
- using format_argument_pack<Stream>::operator[];
+ using typename format_argument_pack<Character, Traits>::iterator;
+ using format_argument_pack<Character, Traits>::operator[];
template <typename Format, typename... Params>
format_argument_pack_impl(Format &&fmt, Params &&... args)
- : std::array<format_argument<Stream>, Count>({ { format_argument<Stream>(std::forward<Params>(args))... } })
- , format_argument_pack<Stream>(std::forward<Format>(fmt), Count ? &*this->cbegin() : nullptr, Count)
+ : std::array<format_argument<Character, Traits>, Count>({ { format_argument<Character, Traits>(std::forward<Params>(args))... } })
+ , format_argument_pack<Character, Traits>(std::forward<Format>(fmt), Count ? &*this->cbegin() : nullptr, Count)
{
static_assert(sizeof...(Params) == Count, "Wrong number of constructor arguments");
}
- format_argument_pack_impl(format_argument_pack_impl<Stream, Count> const &) = default;
- format_argument_pack_impl(format_argument_pack_impl<Stream, Count> &&) = default;
- format_argument_pack_impl &operator=(format_argument_pack_impl<Stream, Count> const &) = default;
- format_argument_pack_impl &operator=(format_argument_pack_impl<Stream, Count> &&) = default;
+ format_argument_pack_impl(format_argument_pack_impl<Character, Traits, Count> const &) = default;
+ format_argument_pack_impl(format_argument_pack_impl<Character, Traits, Count> &&) = default;
+ format_argument_pack_impl &operator=(format_argument_pack_impl<Character, Traits, Count> const &) = default;
+ format_argument_pack_impl &operator=(format_argument_pack_impl<Character, Traits, Count> &&) = default;
};
@@ -1171,9 +1199,9 @@ public:
//**************************************************************************
template <typename Stream = std::ostream, typename Format, typename... Params>
-inline format_argument_pack_impl<Stream, sizeof...(Params)> make_format_argument_pack(Format &&fmt, Params &&... args)
+inline auto make_format_argument_pack(Format &&fmt, Params &&... args)
{
- return format_argument_pack_impl<Stream, sizeof...(Params)>(std::forward<Format>(fmt), std::forward<Params>(args)...);
+ return format_argument_pack_impl<typename Stream::char_type, typename Stream::traits_type, sizeof...(Params)>(std::forward<Format>(fmt), std::forward<Params>(args)...);
}
@@ -1406,38 +1434,45 @@ public:
break;
case format_helper::X:
flags.set_uppercase();
+ [[fallthrough]];
case format_helper::x:
flags.set_conversion(format_flags::conversion::hexadecimal);
break;
case format_helper::E:
flags.set_uppercase();
+ [[fallthrough]];
case format_helper::e:
flags.set_conversion(format_flags::conversion::scientific_decimal);
break;
case format_helper::F:
flags.set_uppercase();
+ [[fallthrough]];
case format_helper::f:
flags.set_conversion(format_flags::conversion::fixed_decimal);
break;
case format_helper::G:
flags.set_uppercase();
+ [[fallthrough]];
case format_helper::g:
flags.set_conversion(format_flags::conversion::floating_decimal);
break;
case format_helper::A:
flags.set_uppercase();
+ [[fallthrough]];
case format_helper::a:
flags.set_conversion(format_flags::conversion::scientific_hexadecimal);
break;
case format_helper::C:
if (format_flags::length::unspecified == flags.get_length())
flags.set_length(format_flags::length::long_integer);
+ [[fallthrough]];
case format_helper::c:
flags.set_conversion(format_flags::conversion::character);
break;
case format_helper::S:
if (format_flags::length::unspecified == flags.get_length())
flags.set_length(format_flags::length::long_integer);
+ [[fallthrough]];
case format_helper::s:
flags.set_conversion(format_flags::conversion::string);
break;
@@ -1514,15 +1549,16 @@ private:
// CORE FORMATTING FUNCTION
//**************************************************************************
-template <typename Stream, typename Base>
-typename Stream::off_type stream_format(Stream &str, format_argument_pack<Base> const &args)
+template <typename Character, typename Traits = std::char_traits<Character> >
+typename std::basic_ostream<Character, Traits>::off_type stream_format(std::basic_ostream<Character, Traits> &str, format_argument_pack<Character, Traits> const &args)
{
- typedef format_helper<format_argument_pack<Base> > format_helper;
- typedef typename format_argument_pack<Base>::iterator iterator;
+ using stream_type = std::basic_ostream<Character, Traits>;
+ using format_helper = format_helper<format_argument_pack<Character, Traits> >;
+ using iterator = typename format_argument_pack<Character, Traits>::iterator;
class stream_preserver
{
public:
- stream_preserver(Stream &stream)
+ stream_preserver(stream_type &stream)
: m_stream(stream)
, m_fill(stream.fill())
, m_flags(stream.flags())
@@ -1538,14 +1574,14 @@ typename Stream::off_type stream_format(Stream &str, format_argument_pack<Base>
m_stream.fill(m_fill);
}
private:
- Stream &m_stream;
- typename Stream::char_type m_fill;
- typename Stream::fmtflags m_flags;
- std::streamsize m_precision;
- std::streamsize m_width;
+ stream_type &m_stream;
+ typename stream_type::char_type m_fill;
+ typename stream_type::fmtflags m_flags;
+ std::streamsize m_precision;
+ std::streamsize m_width;
};
- typename Stream::pos_type const begin(str.tellp());
+ typename stream_type::pos_type const begin(str.tellp());
stream_preserver const preserver(str);
int next_pos(1);
iterator start = args.format_begin();
@@ -1618,7 +1654,7 @@ typename Stream::off_type stream_format(Stream &str, format_argument_pack<Base>
}
else if (format_flags::conversion::percent == flags.get_conversion())
{
- str << typename Stream::char_type(format_chars<typename Stream::char_type>::percent);
+ str << Character(format_chars<Character>::percent);
start = it;
}
else
@@ -1629,10 +1665,10 @@ typename Stream::off_type stream_format(Stream &str, format_argument_pack<Base>
continue;
if (format_flags::conversion::tell == flags.get_conversion())
{
- typename Stream::pos_type const current(str.tellp());
+ typename stream_type::pos_type const current(str.tellp());
args[arg_pos - 1].store_integer(
- ((typename Stream::pos_type(-1) == begin) || (typename Stream::pos_type(-1) == current))
- ? typename Stream::off_type(-1)
+ ((typename stream_type::pos_type(-1) == begin) || (typename stream_type::pos_type(-1) == current))
+ ? typename stream_type::off_type(-1)
: (current - begin));
}
else
@@ -1643,9 +1679,9 @@ typename Stream::off_type stream_format(Stream &str, format_argument_pack<Base>
}
}
}
- typename Stream::pos_type const end(str.tellp());
- return ((typename Stream::pos_type(-1) == begin) || (typename Stream::pos_type(-1) == end))
- ? typename Stream::off_type(-1)
+ typename stream_type::pos_type const end(str.tellp());
+ return ((typename stream_type::pos_type(-1) == begin) || (typename stream_type::pos_type(-1) == end))
+ ? typename stream_type::off_type(-1)
: (end - begin);
}
@@ -1662,14 +1698,14 @@ inline typename Stream::off_type stream_format(Stream &str, Format const &fmt, P
return detail::stream_format(str, detail::make_format_argument_pack<Stream>(fmt, std::forward<Params>(args)...));
}
-template <typename Stream, typename Base>
-inline typename Stream::off_type stream_format(Stream &str, detail::format_argument_pack<Base> const &args)
+template <typename Stream, typename Character, typename Traits>
+inline typename Stream::off_type stream_format(Stream &str, detail::format_argument_pack<Character, Traits> const &args)
{
return detail::stream_format(str, args);
}
-template <typename Stream, typename Base>
-inline typename Stream::off_type stream_format(Stream &str, detail::format_argument_pack<Base> &&args)
+template <typename Stream, typename Character, typename Traits>
+inline typename Stream::off_type stream_format(Stream &str, detail::format_argument_pack<Character, Traits> &&args)
{
return detail::stream_format(str, args);
}
@@ -1682,7 +1718,7 @@ inline typename Stream::off_type stream_format(Stream &str, detail::format_argum
template <typename String = std::string, typename Format, typename... Params>
inline String string_format(Format &&fmt, Params &&... args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
stream_format(str, fmt, std::forward<Params>(args)...);
return str.str();
@@ -1691,45 +1727,45 @@ inline String string_format(Format &&fmt, Params &&... args)
template <typename String = std::string, typename Format, typename... Params>
inline String string_format(std::locale const &locale, Format &&fmt, Params &&... args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
str.imbue(locale);
stream_format(str, fmt, std::forward<Params>(args)...);
return str.str();
};
-template <typename String = std::string, typename Stream>
-inline String string_format(detail::format_argument_pack<Stream> const &args)
+template <typename String = std::string>
+inline String string_format(detail::format_argument_pack<typename String::value_type, typename String::traits_type> const &args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
detail::stream_format(str, args);
return str.str();
};
-template <typename String = std::string, typename Stream>
-inline String string_format(detail::format_argument_pack<Stream> &&args)
+template <typename String = std::string>
+inline String string_format(detail::format_argument_pack<typename String::value_type, typename String::traits_type> &&args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
detail::stream_format(str, std::move(args));
return str.str();
};
-template <typename String = std::string, typename Stream>
-inline String string_format(std::locale const &locale, detail::format_argument_pack<Stream> const &args)
+template <typename String = std::string>
+inline String string_format(std::locale const &locale, detail::format_argument_pack<typename String::value_type, typename String::traits_type> const &args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
str.imbue(locale);
detail::stream_format(str, args);
return str.str();
};
-template <typename String = std::string, typename Stream>
-inline String string_format(std::locale const &locale, detail::format_argument_pack<Stream> &&args)
+template <typename String = std::string>
+inline String string_format(std::locale const &locale, detail::format_argument_pack<typename String::value_type, typename String::traits_type> &&args)
{
- typedef std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type> ostream;
+ using ostream = std::basic_ostringstream<typename String::value_type, typename String::traits_type, typename String::allocator_type>;
ostream str;
str.imbue(locale);
detail::stream_format(str, std::move(args));
@@ -1746,8 +1782,6 @@ using detail::make_format_argument_pack;
} // namespace util
-using util::string_format;
-
//**************************************************************************
// EXTERNAL TEMPLATE INSTANTIATIONS
@@ -1762,69 +1796,115 @@ extern template class format_chars<wchar_t>;
extern template void format_flags::apply(std::ostream &) const;
extern template void format_flags::apply(std::wostream &) const;
-extern template void format_flags::apply(std::iostream &) const;
-extern template void format_flags::apply(std::wiostream &) const;
-extern template void format_flags::apply(std::ostringstream &) const;
-extern template void format_flags::apply(std::wostringstream &) const;
-extern template void format_flags::apply(std::stringstream &) const;
-extern template void format_flags::apply(std::wstringstream &) const;
-extern template void format_flags::apply(ovectorstream &) const;
-extern template void format_flags::apply(wovectorstream &) const;
-extern template void format_flags::apply(vectorstream &) const;
-extern template void format_flags::apply(wvectorstream &) const;
-
-extern template class format_argument<std::ostream>;
-extern template class format_argument<std::wostream>;
-extern template class format_argument<std::iostream>;
-extern template class format_argument<std::wiostream>;
-extern template class format_argument<std::ostringstream>;
-extern template class format_argument<std::wostringstream>;
-extern template class format_argument<std::stringstream>;
-extern template class format_argument<std::wstringstream>;
-extern template class format_argument<ovectorstream>;
-extern template class format_argument<wovectorstream>;
-extern template class format_argument<vectorstream>;
-extern template class format_argument<wvectorstream>;
-
-extern template class format_argument_pack<std::ostream>;
-extern template class format_argument_pack<std::wostream>;
-extern template class format_argument_pack<std::iostream>;
-extern template class format_argument_pack<std::wiostream>;
-extern template class format_argument_pack<std::ostringstream>;
-extern template class format_argument_pack<std::wostringstream>;
-extern template class format_argument_pack<std::stringstream>;
-extern template class format_argument_pack<std::wstringstream>;
-extern template class format_argument_pack<ovectorstream>;
-extern template class format_argument_pack<wovectorstream>;
-extern template class format_argument_pack<vectorstream>;
-extern template class format_argument_pack<wvectorstream>;
-
-extern template std::ostream::off_type stream_format(std::ostream &, format_argument_pack<std::ostream> const &);
-extern template std::wostream::off_type stream_format(std::wostream &, format_argument_pack<std::wostream> const &);
-extern template std::iostream::off_type stream_format(std::iostream &, format_argument_pack<std::ostream> const &);
-extern template std::iostream::off_type stream_format(std::iostream &, format_argument_pack<std::iostream> const &);
-extern template std::wiostream::off_type stream_format(std::wiostream &, format_argument_pack<std::wostream> const &);
-extern template std::wiostream::off_type stream_format(std::wiostream &, format_argument_pack<std::wiostream> const &);
-extern template std::ostringstream::off_type stream_format(std::ostringstream &, format_argument_pack<std::ostream> const &);
-extern template std::ostringstream::off_type stream_format(std::ostringstream &, format_argument_pack<std::ostringstream> const &);
-extern template std::wostringstream::off_type stream_format(std::wostringstream &, format_argument_pack<std::wostream> const &);
-extern template std::wostringstream::off_type stream_format(std::wostringstream &, format_argument_pack<std::wostringstream> const &);
-extern template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::ostream> const &);
-extern template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::iostream> const &);
-extern template std::stringstream::off_type stream_format(std::stringstream &, format_argument_pack<std::stringstream> const &);
-extern template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wostream> const &);
-extern template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wiostream> const &);
-extern template std::wstringstream::off_type stream_format(std::wstringstream &, format_argument_pack<std::wstringstream> const &);
-extern template ovectorstream::off_type stream_format(ovectorstream &, format_argument_pack<std::ostream> const &);
-extern template ovectorstream::off_type stream_format(ovectorstream &, format_argument_pack<ovectorstream> const &);
-extern template wovectorstream::off_type stream_format(wovectorstream &, format_argument_pack<std::wostream> const &);
-extern template wovectorstream::off_type stream_format(wovectorstream &, format_argument_pack<wovectorstream> const &);
-extern template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<std::ostream> const &);
-extern template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<std::iostream> const &);
-extern template vectorstream::off_type stream_format(vectorstream &, format_argument_pack<vectorstream> const &);
-extern template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<std::wostream> const &);
-extern template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<std::wiostream> const &);
-extern template wvectorstream::off_type stream_format(wvectorstream &, format_argument_pack<wvectorstream> const &);
+
+extern template class format_argument<char>;
+extern template void format_argument<char>::static_output<bool>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<char>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<signed char>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<unsigned char>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<short>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<unsigned short>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<int>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<unsigned int>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<long>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<unsigned long>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<long long>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<unsigned long long>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<char *>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<char const *>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<std::string>(std::ostream &, format_flags const &, void const *);
+extern template void format_argument<char>::static_output<std::string_view>(std::ostream &, format_flags const &, void const *);
+extern template bool format_argument<char>::static_make_integer<bool>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<char>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<signed char>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<unsigned char>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<short>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<unsigned short>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<int>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<unsigned int>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<long>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<unsigned long>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<long long>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<unsigned long long>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<char *>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<char const *>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<std::string>(void const *, int &);
+extern template bool format_argument<char>::static_make_integer<std::string_view>(void const *, int &);
+extern template void format_argument<char>::static_store_integer<bool>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<char>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<signed char>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<unsigned char>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<short>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<unsigned short>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<int>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<unsigned int>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<long>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<unsigned long>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<long long>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<unsigned long long>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<char *>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<char const *>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<std::string>(void const *, std::streamoff);
+extern template void format_argument<char>::static_store_integer<std::string_view>(void const *, std::streamoff);
+
+extern template class format_argument<wchar_t>;
+extern template void format_argument<wchar_t>::static_output<bool>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<char>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<signed char>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<unsigned char>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<wchar_t>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<short>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<unsigned short>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<int>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<unsigned int>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<long>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<unsigned long>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<long long>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<unsigned long long>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<wchar_t *>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<wchar_t const *>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<std::wstring>(std::wostream &, format_flags const &, void const *);
+extern template void format_argument<wchar_t>::static_output<std::wstring_view>(std::wostream &, format_flags const &, void const *);
+extern template bool format_argument<wchar_t>::static_make_integer<bool>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<char>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<signed char>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<unsigned char>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<wchar_t>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<short>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<unsigned short>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<int>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<unsigned int>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<long>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<unsigned long>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<long long>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<unsigned long long>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<wchar_t *>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<wchar_t const *>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<std::wstring>(void const *, int &);
+extern template bool format_argument<wchar_t>::static_make_integer<std::wstring_view>(void const *, int &);
+extern template void format_argument<wchar_t>::static_store_integer<bool>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<char>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<signed char>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<unsigned char>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<wchar_t>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<short>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<unsigned short>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<int>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<unsigned int>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<long>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<unsigned long>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<long long>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<unsigned long long>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<wchar_t *>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<wchar_t const *>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<std::wstring>(void const *, std::streamoff);
+extern template void format_argument<wchar_t>::static_store_integer<std::wstring_view>(void const *, std::streamoff);
+
+extern template class format_argument_pack<char>;
+extern template class format_argument_pack<wchar_t>;
+
+extern template std::ostream::off_type stream_format(std::ostream &, format_argument_pack<char> const &);
+extern template std::wostream::off_type stream_format(std::wostream &, format_argument_pack<wchar_t> const &);
} // namespace detail
diff --git a/src/lib/util/tape_file_interface.h b/src/lib/util/tape_file_interface.h
new file mode 100644
index 00000000000..8003c05777f
--- /dev/null
+++ b/src/lib/util/tape_file_interface.h
@@ -0,0 +1,58 @@
+// license:BSD-3-Clause
+// copyright-holders:Mietek Bak
+#ifndef MAME_UTIL_TAPE_FILE_INTERFACE_H
+#define MAME_UTIL_TAPE_FILE_INTERFACE_H
+
+#pragma once
+
+#include <cstdint>
+#include <utility>
+
+//////////////////////////////////////////////////////////////////////////////
+
+enum class tape_status : std::uint8_t {
+ OK, // oll korrekt
+ BOM, // beginning of medium
+ EW, // early warning
+ FILEMARK, // filemark
+ FILEMARK_EW, // filemark and early warning
+ SETMARK, // setmark
+ SETMARK_EW, // setmark and early warning
+ EOD, // end of data
+ EOD_EW, // end of data and early warning
+ UNKNOWN, // unknown
+ UNKNOWN_EW, // unknown and early warning
+ EOM // end of medium
+};
+
+class tape_file_interface
+{
+public:
+ // construction
+ virtual ~tape_file_interface() {}
+
+ // position-preserving operations
+ virtual bool is_read_only() const = 0;
+ virtual bool is_ew() const = 0;
+ virtual std::uint8_t get_density_code() const = 0;
+ virtual std::pair<tape_status, std::uint32_t> read_position() const = 0;
+
+ // non-destructive operations
+ virtual void rewind(bool eom) = 0;
+ virtual tape_status locate_block(std::uint32_t req_block_addr) = 0;
+ virtual tape_status space_eod() = 0;
+ virtual std::pair<tape_status, std::uint32_t> space_blocks(std::uint32_t req_blocks_num) = 0;
+ virtual std::pair<tape_status, std::uint32_t> space_filemarks(std::uint32_t req_marks_num, bool setmarks = false, bool sequential = false) = 0;
+ virtual std::pair<tape_status, std::uint32_t> space_blocks_reverse(std::uint32_t req_blocks_num) = 0;
+ virtual std::pair<tape_status, std::uint32_t> space_filemarks_reverse(std::uint32_t req_marks_num, bool setmarks = false, bool sequential = false) = 0;
+ virtual std::pair<tape_status, std::uint32_t> read_block(std::uint8_t *buf, std::uint32_t buf_size) = 0;
+
+ // destructive operations
+ virtual void erase(bool eom) = 0;
+ virtual tape_status write_block(const std::uint8_t *buf, std::uint32_t len) = 0;
+ virtual tape_status write_filemarks(std::uint32_t req_marks_num, bool setmarks = false) = 0;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // MAME_UTIL_TAPE_FILE_INTERFACE_H
diff --git a/src/lib/util/timeconv.cpp b/src/lib/util/timeconv.cpp
index 20f4e29a03e..469b6b592a2 100644
--- a/src/lib/util/timeconv.cpp
+++ b/src/lib/util/timeconv.cpp
@@ -14,25 +14,10 @@
namespace util {
-/***************************************************************************
- PROTOTYPES
-***************************************************************************/
-
-static std::chrono::system_clock::duration calculate_system_clock_adjustment();
-
-
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
-std::chrono::system_clock::duration system_clock_adjustment(calculate_system_clock_adjustment());
-
-
-/***************************************************************************
- IMPLEMENTATION
-***************************************************************************/
+namespace {
-static std::chrono::system_clock::duration calculate_system_clock_adjustment()
+std::chrono::system_clock::duration calculate_system_clock_adjustment()
{
constexpr auto days_in_year(365);
constexpr auto days_in_four_years((days_in_year * 4) + 1);
@@ -67,6 +52,39 @@ static std::chrono::system_clock::duration calculate_system_clock_adjustment()
return result - std::chrono::system_clock::from_time_t(0).time_since_epoch();
}
+} // anonymous namespace
+
+
+
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+std::chrono::system_clock::duration system_clock_adjustment(calculate_system_clock_adjustment());
+
+
+
+/***************************************************************************
+ IMPLEMENTATION
+***************************************************************************/
+
+arbitrary_datetime arbitrary_datetime::now()
+{
+ time_t sec;
+ time(&sec);
+ auto t = *localtime(&sec);
+
+ arbitrary_datetime dt;
+ dt.year = t.tm_year + 1900;
+ dt.month = t.tm_mon + 1;
+ dt.day_of_month = t.tm_mday;
+ dt.hour = t.tm_hour;
+ dt.minute = t.tm_min;
+ dt.second = t.tm_sec;
+
+ return dt;
+}
+
// -------------------------------------------------
diff --git a/src/lib/util/timeconv.h b/src/lib/util/timeconv.h
index 26decf0fd65..91327dbac16 100644
--- a/src/lib/util/timeconv.h
+++ b/src/lib/util/timeconv.h
@@ -13,15 +13,16 @@
#pragma once
-#include "osdcore.h"
#include "coreutil.h"
-#include <chrono>
#include <algorithm>
+#include <chrono>
+#include <cstring>
#include <stdexcept>
namespace util {
+
/***************************************************************************
GLOBAL VARIABLES
***************************************************************************/
@@ -48,6 +49,8 @@ struct arbitrary_datetime
int hour; // hour (0-23)
int minute; // minute (0-59)
int second; // second (0-59)
+
+ static struct arbitrary_datetime now();
};
@@ -56,7 +59,7 @@ struct arbitrary_datetime
// date of the epoch's begining
//---------------------------------------------------------
-template<typename Rep, int Y, int M, int D, int H, int N, int S, typename Ratio>
+template <typename Rep, int Y, int M, int D, int H, int N, int S, typename Ratio>
class arbitrary_clock
{
public:
@@ -87,16 +90,16 @@ public:
// with a different scale to this arbitrary_clock's scale
//---------------------------------------------------------
- template<typename Rep2, int Y2, int M2, int D2, int H2, int N2, int S2, typename Ratio2>
- static time_point from_arbitrary_time_point(const std::chrono::time_point<arbitrary_clock<Rep2, Y2, M2, D2, H2, N2, S2, Ratio2> > &tp)
+ template <typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2>
+ static time_point from_arbitrary_time_point(const std::chrono::time_point<arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2> > &tp)
{
arbitrary_datetime dt;
- dt.year = Y2;
- dt.month = M2;
- dt.day_of_month = D2;
- dt.hour = H2;
- dt.minute = N2;
- dt.second = S2;
+ dt.year = YY;
+ dt.month = MM;
+ dt.day_of_month = DD;
+ dt.hour = HH;
+ dt.minute = NN;
+ dt.second = SS;
const duration adjustment = duration_from_arbitrary_datetime(dt, false);
const duration result_duration = std::chrono::duration_cast<duration>(tp.time_since_epoch() + adjustment);
@@ -109,10 +112,10 @@ public:
// of this scale to one of different scale
//---------------------------------------------------------
- template<typename Rep2, int Y2, int M2, int D2, int H2, int N2, int S2, typename Ratio2>
- static std::chrono::time_point<arbitrary_clock<Rep2, Y2, M2, D2, H2, N2, S2, Ratio2> > to_arbitrary_time_point(const time_point &tp)
+ template <typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2>
+ static std::chrono::time_point<arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2> > to_arbitrary_time_point(const time_point &tp)
{
- return arbitrary_clock<Rep2, Y2, M2, D2, H2, N2, S2, Ratio2>::from_arbitrary_time_point(tp);
+ return arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2>::from_arbitrary_time_point(tp);
}
@@ -123,14 +126,14 @@ public:
static struct tm to_tm(const time_point &tp)
{
std::chrono::time_point<tm_conversion_clock> normalized_tp = to_arbitrary_time_point<
- std::int64_t,
- tm_conversion_clock::base_year,
- tm_conversion_clock::base_month,
- tm_conversion_clock::base_day,
- tm_conversion_clock::base_hour,
- tm_conversion_clock::base_minute,
- tm_conversion_clock::base_second,
- tm_conversion_clock::period>(tp);
+ std::int64_t,
+ tm_conversion_clock::base_year,
+ tm_conversion_clock::base_month,
+ tm_conversion_clock::base_day,
+ tm_conversion_clock::base_hour,
+ tm_conversion_clock::base_minute,
+ tm_conversion_clock::base_second,
+ tm_conversion_clock::period>(tp);
return internal_to_tm(normalized_tp.time_since_epoch());
}
@@ -142,14 +145,14 @@ public:
static std::chrono::time_point<std::chrono::system_clock> to_system_clock(const time_point &tp)
{
auto normalized_tp = to_arbitrary_time_point<
- std::int64_t,
- system_conversion_clock::base_year,
- system_conversion_clock::base_month,
- system_conversion_clock::base_day,
- system_conversion_clock::base_hour,
- system_conversion_clock::base_minute,
- system_conversion_clock::base_second,
- system_conversion_clock::period>(tp);
+ std::int64_t,
+ system_conversion_clock::base_year,
+ system_conversion_clock::base_month,
+ system_conversion_clock::base_day,
+ system_conversion_clock::base_hour,
+ system_conversion_clock::base_minute,
+ system_conversion_clock::base_second,
+ system_conversion_clock::period>(tp);
return std::chrono::time_point<std::chrono::system_clock>(normalized_tp.time_since_epoch() + system_clock_adjustment);
}
@@ -179,13 +182,10 @@ private:
static int clamp_or_throw(int value, int minimum, int maximum, bool clamp, const char *out_of_range_message)
{
- if (value < minimum || value > maximum)
- {
- if (clamp)
- value = std::min(std::max(value, minimum), maximum);
- else
- throw std::out_of_range(out_of_range_message);
- }
+ if (clamp)
+ value = std::clamp(value, minimum, maximum);
+ else if (value < minimum || value > maximum)
+ throw std::out_of_range(out_of_range_message);
return value;
}
diff --git a/src/lib/util/un7z.cpp b/src/lib/util/un7z.cpp
index 700d26169d9..5ee97d62777 100644
--- a/src/lib/util/un7z.cpp
+++ b/src/lib/util/un7z.cpp
@@ -13,9 +13,13 @@
#include "unzip.h"
#include "corestr.h"
+#include "ioprocs.h"
#include "unicode.h"
#include "timeconv.h"
+#include "osdcore.h"
+#include "osdfile.h"
+
#include "lzma/C/7z.h"
#include "lzma/C/7zAlloc.h"
#include "lzma/C/7zCrc.h"
@@ -36,76 +40,109 @@
namespace util {
+
namespace {
+
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
-struct CSzFile
+struct CFileInStream : public ISeekInStream
{
- CSzFile() : currfpos(0), length(0), osdfile() {}
+ CFileInStream() noexcept
+ {
+ Read = &CFileInStream::read_static;
+ Seek = &CFileInStream::seek_static;
+ }
- std::uint64_t currfpos;
- std::uint64_t length;
- osd_file::ptr osdfile;
+ random_read::ptr file;
+ std::uint64_t currfpos = 0;
+ std::uint64_t length = 0;
- SRes read(void *data, std::size_t &size)
+private:
+ SRes read(void *data, std::size_t &size) noexcept
{
- if (!osdfile)
+ if (!file)
{
- osd_printf_error("un7z: called CSzFile::read without file\n");
+ osd_printf_error("un7z: called CFileInStream::read without file\n");
return SZ_ERROR_READ;
}
if (!size)
return SZ_OK;
- // TODO: this casts a size_t to a uint32_t, so it will fail if >=4GB is requested at once (need a loop)
- std::uint32_t read_length(0);
- auto const err = osdfile->read(data, currfpos, size, read_length);
+ auto const [err, read_length] = read_at(*file, currfpos, data, size);
size = read_length;
currfpos += read_length;
- return (osd_file::error::NONE == err) ? SZ_OK : SZ_ERROR_READ;
+ return !err ? SZ_OK : SZ_ERROR_READ;
}
- SRes seek(Int64 &pos, ESzSeek origin)
+ SRes seek(Int64 &pos, ESzSeek origin) noexcept
{
+ // need to synthesise this because the OSD file wrapper doesn't implement SEEK_END
switch (origin)
{
- case SZ_SEEK_CUR: currfpos += pos; break;
- case SZ_SEEK_SET: currfpos = pos; break;
- case SZ_SEEK_END: currfpos = length + pos; break;
- default: return SZ_ERROR_READ;
+ case SZ_SEEK_CUR:
+ if ((0 > pos) && (-pos > currfpos))
+ {
+ osd_printf_error("un7z: attemped to seek back %d bytes when current offset is %u\n", -pos, currfpos);
+ return SZ_ERROR_READ;
+ }
+ currfpos += pos;
+ break;
+ case SZ_SEEK_SET:
+ currfpos = pos;
+ break;
+ case SZ_SEEK_END:
+ if ((0 > pos) && (-pos > length))
+ {
+ osd_printf_error("un7z: attemped to seek %d bytes before end of file %u\n", -pos, currfpos);
+ return SZ_ERROR_READ;
+ }
+ currfpos = length + pos;
+ break;
+ default:
+ return SZ_ERROR_READ;
}
pos = currfpos;
return SZ_OK;
}
-};
+ static SRes read_static(ISeekInStreamPtr pp, void *buf, size_t *size) noexcept
+ {
+ return static_cast<CFileInStream *>(const_cast<ISeekInStream *>(pp))->read(buf, *size);
+ }
-struct CFileInStream : public ISeekInStream, public CSzFile
-{
- CFileInStream();
+ static SRes seek_static(ISeekInStreamPtr pp, Int64 *pos, ESzSeek origin) noexcept
+ {
+ return static_cast<CFileInStream *>(const_cast<ISeekInStream *>(pp))->seek(*pos, origin);
+ }
};
-class m7z_file_impl
+class m7z_file_impl
{
public:
typedef std::unique_ptr<m7z_file_impl> ptr;
- m7z_file_impl(const std::string &filename);
+ m7z_file_impl(std::string &&filename) noexcept;
+
+ m7z_file_impl(random_read::ptr &&file) noexcept
+ : m7z_file_impl(std::string())
+ {
+ m_archive_stream.file = std::move(file);
+ }
virtual ~m7z_file_impl()
{
if (m_out_buffer)
- IAlloc_Free(&m_alloc_imp, m_out_buffer);
+ ISzAlloc_Free(&m_alloc_imp, m_out_buffer);
if (m_inited)
SzArEx_Free(&m_db, &m_alloc_imp);
}
- static ptr find_cached(const std::string &filename)
+ static ptr find_cached(std::string_view filename) noexcept
{
std::lock_guard<std::mutex> guard(s_cache_mutex);
for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++)
@@ -113,47 +150,60 @@ public:
// if we have a valid entry and it matches our filename, use it and remove from the cache
if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename))
{
+ using std::swap;
ptr result;
- std::swap(s_cache[cachenum], result);
- osd_printf_verbose("un7z: found %s in cache\n", filename.c_str());
+ swap(s_cache[cachenum], result);
+ osd_printf_verbose("un7z: found %s in cache\n", filename);
return result;
}
}
return ptr();
}
- static void close(ptr &&archive);
- static void cache_clear()
+
+ static void close(ptr &&archive) noexcept;
+
+ static void cache_clear() noexcept
{
// clear call cache entries
std::lock_guard<std::mutex> guard(s_cache_mutex);
- for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { }
+ for (auto &cached : s_cache)
+ cached.reset();
}
- archive_file::error initialize();
+ std::error_condition initialize() noexcept;
- int first_file() { return search(0, 0, std::string(), false, false, false); }
- int next_file() { return (m_curr_file_idx < 0) ? -1 : search(m_curr_file_idx + 1, 0, std::string(), false, false, false); }
+ int first_file() noexcept
+ {
+ return search(0, 0, std::string_view(), false, false, false);
+ }
- int search(std::uint32_t crc)
+ int next_file() noexcept
{
- return search(0, crc, std::string(), true, false, false);
+ return (m_curr_file_idx < 0) ? -1 : search(m_curr_file_idx + 1, 0, std::string_view(), false, false, false);
}
- int search(const std::string &filename, bool partialpath)
+
+ int search(std::uint32_t crc) noexcept
+ {
+ return search(0, crc, std::string_view(), true, false, false);
+ }
+
+ int search(std::string_view filename, bool partialpath) noexcept
{
return search(0, 0, filename, false, true, partialpath);
}
- int search(std::uint32_t crc, const std::string &filename, bool partialpath)
+
+ int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept
{
return search(0, crc, filename, true, true, partialpath);
}
- bool current_is_directory() const { return m_curr_is_dir; }
- const std::string &current_name() const { return m_curr_name; }
- std::uint64_t current_uncompressed_length() const { return m_curr_length; }
- virtual std::chrono::system_clock::time_point current_last_modified() const { return m_curr_modified; }
- std::uint32_t current_crc() const { return m_curr_crc; }
+ bool current_is_directory() const noexcept { return m_curr_is_dir; }
+ const std::string &current_name() const noexcept { return m_curr_name; }
+ std::uint64_t current_uncompressed_length() const noexcept { return m_curr_length; }
+ virtual std::chrono::system_clock::time_point current_last_modified() const noexcept { return m_curr_modified; }
+ std::uint32_t current_crc() const noexcept { return m_curr_crc; }
- archive_file::error decompress(void *buffer, std::uint32_t length);
+ std::error_condition decompress(void *buffer, std::size_t length) noexcept;
private:
m7z_file_impl(const m7z_file_impl &) = delete;
@@ -164,12 +214,12 @@ private:
int search(
int i,
std::uint32_t search_crc,
- const std::string &search_filename,
+ std::string_view search_filename,
bool matchcrc,
bool matchname,
- bool partialpath);
+ bool partialpath) noexcept;
void make_utf8_name(int index);
- void set_curr_modified();
+ void set_curr_modified() noexcept;
static constexpr std::size_t CACHE_SIZE = 8;
static std::array<ptr, CACHE_SIZE> s_cache;
@@ -185,11 +235,11 @@ private:
std::uint32_t m_curr_crc; // current file crc
std::vector<UInt16> m_utf16_buf;
- std::vector<char32_t> m_uchar_buf;
+ std::vector<char32_t> m_uchar_buf;
std::vector<char> m_utf8_buf;
CFileInStream m_archive_stream;
- CLookToRead m_look_stream;
+ CLookToRead2 m_look_stream;
CSzArEx m_db;
ISzAlloc m_alloc_imp;
ISzAlloc m_alloc_temp_imp;
@@ -199,39 +249,39 @@ private:
UInt32 m_block_index;
Byte * m_out_buffer;
std::size_t m_out_buffer_size;
-
+ Byte m_look_stream_buf[65'536];
};
class m7z_file_wrapper : public archive_file
{
public:
- m7z_file_wrapper(m7z_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); }
+ m7z_file_wrapper(m7z_file_impl::ptr &&impl) noexcept : m_impl(std::move(impl)) { assert(m_impl); }
virtual ~m7z_file_wrapper() override { m7z_file_impl::close(std::move(m_impl)); }
- virtual int first_file() override { return m_impl->first_file(); }
- virtual int next_file() override { return m_impl->next_file(); }
+ virtual int first_file() noexcept override { return m_impl->first_file(); }
+ virtual int next_file() noexcept override { return m_impl->next_file(); }
- virtual int search(std::uint32_t crc) override
+ virtual int search(std::uint32_t crc) noexcept override
{
return m_impl->search(crc);
}
- virtual int search(const std::string &filename, bool partialpath) override
+ virtual int search(std::string_view filename, bool partialpath) noexcept override
{
return m_impl->search(filename, partialpath);
}
- virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) override
+ virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept override
{
return m_impl->search(crc, filename, partialpath);
}
- virtual bool current_is_directory() const override { return m_impl->current_is_directory(); }
- virtual const std::string &current_name() const override { return m_impl->current_name(); }
- virtual std::uint64_t current_uncompressed_length() const override { return m_impl->current_uncompressed_length(); }
- virtual std::chrono::system_clock::time_point current_last_modified() const override { return m_impl->current_last_modified(); }
- virtual std::uint32_t current_crc() const override { return m_impl->current_crc(); }
+ virtual bool current_is_directory() const noexcept override { return m_impl->current_is_directory(); }
+ virtual const std::string &current_name() const noexcept override { return m_impl->current_name(); }
+ virtual std::uint64_t current_uncompressed_length() const noexcept override { return m_impl->current_uncompressed_length(); }
+ virtual std::chrono::system_clock::time_point current_last_modified() const noexcept override { return m_impl->current_last_modified(); }
+ virtual std::uint32_t current_crc() const noexcept override { return m_impl->current_crc(); }
- virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); }
+ virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept override { return m_impl->decompress(buffer, length); }
private:
m7z_file_impl::ptr m_impl;
@@ -249,48 +299,20 @@ std::mutex m7z_file_impl::s_cache_mutex;
/***************************************************************************
- 7Zip Memory / File handling (adapted from 7zfile.c/.h and 7zalloc.c/.h)
-***************************************************************************/
-
-/* ---------- FileInStream ---------- */
-
-extern "C" {
-static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
-{
- return (reinterpret_cast<CFileInStream *>(pp)->read(buf, *size) == 0) ? SZ_OK : SZ_ERROR_READ;
-}
-
-static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin)
-{
- return reinterpret_cast<CFileInStream *>(pp)->seek(*pos, origin);
-}
-
-} // extern "C"
-
-
-CFileInStream::CFileInStream()
-{
- Read = &FileInStream_Read;
- Seek = &FileInStream_Seek;
-}
-
-
-
-/***************************************************************************
CACHE MANAGEMENT
***************************************************************************/
-m7z_file_impl::m7z_file_impl(const std::string &filename)
- : m_filename(filename)
+m7z_file_impl::m7z_file_impl(std::string &&filename) noexcept
+ : m_filename(std::move(filename))
, m_curr_file_idx(-1)
, m_curr_is_dir(false)
, m_curr_name()
, m_curr_length(0)
, m_curr_modified()
, m_curr_crc(0)
- , m_utf16_buf(128)
- , m_uchar_buf(128)
- , m_utf8_buf(512)
+ , m_utf16_buf()
+ , m_uchar_buf()
+ , m_utf8_buf()
, m_inited(false)
, m_block_index(0)
, m_out_buffer(nullptr)
@@ -302,38 +324,70 @@ m7z_file_impl::m7z_file_impl(const std::string &filename)
m_alloc_temp_imp.Alloc = &SzAllocTemp;
m_alloc_temp_imp.Free = &SzFreeTemp;
- LookToRead_CreateVTable(&m_look_stream, False);
+ LookToRead2_CreateVTable(&m_look_stream, False);
m_look_stream.realStream = &m_archive_stream;
- LookToRead_Init(&m_look_stream);
+ m_look_stream.buf = m_look_stream_buf;
+ m_look_stream.bufSize = std::size(m_look_stream_buf);
+ LookToRead2_INIT(&m_look_stream);
}
-archive_file::error m7z_file_impl::initialize()
+std::error_condition m7z_file_impl::initialize() noexcept
{
- osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length);
- if (err != osd_file::error::NONE)
- return archive_file::error::FILE_ERROR;
+ try
+ {
+ if (m_utf16_buf.size() < 128)
+ m_utf16_buf.resize(128);
+ if (m_uchar_buf.size() < 128)
+ m_uchar_buf.resize(128);
+ m_utf8_buf.reserve(512);
+ }
+ catch (...)
+ {
+ return std::errc::not_enough_memory;
+ }
- osd_printf_verbose("un7z: opened archive file %s\n", m_filename.c_str());
+ if (!m_archive_stream.file)
+ {
+ osd_file::ptr file;
+ std::error_condition const err = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_archive_stream.length);
+ if (err)
+ return err;
+ m_archive_stream.file = osd_file_read(std::move(file));
+ osd_printf_verbose("un7z: opened archive file %s\n", m_filename);
+ }
+ else if (!m_archive_stream.length)
+ {
+ std::error_condition const err = m_archive_stream.file->length(m_archive_stream.length);
+ if (err)
+ {
+ osd_printf_verbose(
+ "un7z: error getting length of archive file %s (%s:%d %s)\n",
+ m_filename, err.category().name(), err.value(), err.message());
+ return err;
+ }
+ }
- CrcGenerateTable(); // FIXME: doesn't belong here - it should be called once statically
+ // TODO: coordinate this with other LZMA users in the codebase?
+ struct crc_table_generator { crc_table_generator() { CrcGenerateTable(); } };
+ static crc_table_generator crc_table;
SzArEx_Init(&m_db);
m_inited = true;
- SRes const res = SzArEx_Open(&m_db, &m_look_stream.s, &m_alloc_imp, &m_alloc_temp_imp);
+ SRes const res = SzArEx_Open(&m_db, &m_look_stream.vt, &m_alloc_imp, &m_alloc_temp_imp);
if (res != SZ_OK)
{
- osd_printf_error("un7z: error opening %s as 7z archive (%d)\n", m_filename.c_str(), int(res));
+ osd_printf_error("un7z: error opening %s as 7z archive (%d)\n", m_filename, int(res));
switch (res)
{
case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED;
- case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY;
+ case SZ_ERROR_MEM: return std::errc::not_enough_memory;
case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED;
- default: return archive_file::error::FILE_ERROR;
+ default: return std::errc::io_error; // TODO: better default error?
}
}
- return archive_file::error::NONE;
+ return std::error_condition();
}
@@ -342,33 +396,38 @@ archive_file::error m7z_file_impl::initialize()
to the cache
-------------------------------------------------*/
-void m7z_file_impl::close(ptr &&archive)
+void m7z_file_impl::close(ptr &&archive) noexcept
{
- if (!archive) return;
+ // if the filename isn't empty, the implementation can be cached
+ if (archive && !archive->m_filename.empty())
+ {
+ // close the open files
+ osd_printf_verbose("un7z: closing archive file %s and sending to cache\n", archive->m_filename);
+ archive->m_archive_stream.file.reset();
- // close the open files
- osd_printf_verbose("un7z: closing archive file %s and sending to cache\n", archive->m_filename.c_str());
- archive->m_archive_stream.osdfile.reset();
+ // find the first nullptr entry in the cache
+ std::lock_guard<std::mutex> guard(s_cache_mutex);
+ std::size_t cachenum;
+ for (cachenum = 0; cachenum < s_cache.size(); cachenum++)
+ if (!s_cache[cachenum])
+ break;
- // find the first nullptr entry in the cache
- std::lock_guard<std::mutex> guard(s_cache_mutex);
- std::size_t cachenum;
- for (cachenum = 0; cachenum < s_cache.size(); cachenum++)
- if (!s_cache[cachenum])
- break;
+ // if no room left in the cache, free the bottommost entry
+ if (cachenum == s_cache.size())
+ {
+ cachenum--;
+ osd_printf_verbose("un7z: removing %s from cache to make space\n", s_cache[cachenum]->m_filename);
+ s_cache[cachenum].reset();
+ }
- // if no room left in the cache, free the bottommost entry
- if (cachenum == s_cache.size())
- {
- cachenum--;
- osd_printf_verbose("un7z: removing %s from cache to make space\n", s_cache[cachenum]->m_filename.c_str());
- s_cache[cachenum].reset();
+ // move everyone else down and place us at the top
+ for ( ; cachenum > 0; cachenum--)
+ s_cache[cachenum] = std::move(s_cache[cachenum - 1]);
+ s_cache[0] = std::move(archive);
}
- // move everyone else down and place us at the top
- for ( ; cachenum > 0; cachenum--)
- s_cache[cachenum] = std::move(s_cache[cachenum - 1]);
- s_cache[0] = std::move(archive);
+ // make sure it's cleaned up
+ archive.reset();
}
@@ -382,42 +441,46 @@ void m7z_file_impl::close(ptr &&archive)
from a _7Z into the target buffer
-------------------------------------------------*/
-archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length)
+std::error_condition m7z_file_impl::decompress(void *buffer, std::size_t length) noexcept
{
// if we don't have enough buffer, error
if (length < m_curr_length)
{
- osd_printf_error("un7z: buffer too small to decompress %s from %s\n", m_curr_name.c_str(), m_filename.c_str());
+ osd_printf_error("un7z: buffer too small to decompress %s from %s\n", m_curr_name, m_filename);
return archive_file::error::BUFFER_TOO_SMALL;
}
// make sure the file is open..
- if (!m_archive_stream.osdfile)
+ if (!m_archive_stream.file)
{
- m_archive_stream.currfpos = 0;
- osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length);
- if (err != osd_file::error::NONE)
+ m_archive_stream.currfpos = 0; // FIXME: should it really be changing the file pointer out from under LZMA?
+ osd_file::ptr file;
+ std::error_condition const err = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_archive_stream.length);
+ if (err)
{
- osd_printf_error("un7z: error reopening archive file %s (%d)\n", m_filename.c_str(), int(err));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error(
+ "un7z: error reopening archive file %s (%s:%d %s)\n",
+ m_filename, err.category().name(), err.value(), err.message());
+ return err;
}
- osd_printf_verbose("un7z: reopened archive file %s\n", m_filename.c_str());
+ m_archive_stream.file = osd_file_read(std::move(file));
+ osd_printf_verbose("un7z: reopened archive file %s\n", m_filename);
}
std::size_t offset(0);
std::size_t out_size_processed(0);
SRes const res = SzArEx_Extract(
- &m_db, &m_look_stream.s, m_curr_file_idx, // requested file
+ &m_db, &m_look_stream.vt, m_curr_file_idx, // requested file
&m_block_index, &m_out_buffer, &m_out_buffer_size, // solid block caching
&offset, &out_size_processed, // data size/offset
&m_alloc_imp, &m_alloc_temp_imp); // allocator helpers
if (res != SZ_OK)
{
- osd_printf_error("un7z: error decompressing %s from %s (%d)\n", m_curr_name.c_str(), m_filename.c_str(), int(res));
+ osd_printf_error("un7z: error decompressing %s from %s (%d)\n", m_curr_name, m_filename, int(res));
switch (res)
{
case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED;
- case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY;
+ case SZ_ERROR_MEM: return std::errc::not_enough_memory;
case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED;
default: return archive_file::error::DECOMPRESS_ERROR;
}
@@ -425,45 +488,64 @@ archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length
// copy to destination buffer
std::memcpy(buffer, m_out_buffer + offset, (std::min<std::size_t>)(length, out_size_processed));
- return archive_file::error::NONE;
+ return std::error_condition();
}
int m7z_file_impl::search(
int i,
std::uint32_t search_crc,
- const std::string &search_filename,
+ std::string_view search_filename,
bool matchcrc,
bool matchname,
- bool partialpath)
+ bool partialpath) noexcept
{
- for ( ; i < m_db.NumFiles; i++)
- {
- make_utf8_name(i);
- bool const is_dir(SzArEx_IsDir(&m_db, i));
- const std::uint64_t size(SzArEx_GetFileSize(&m_db, i));
- const std::uint32_t crc(m_db.CRCs.Vals[i]);
- const bool crcmatch(SzBitArray_Check(m_db.CRCs.Defs, i) && (crc == search_crc));
- auto const partialoffset(m_utf8_buf.size() - 1 - search_filename.length());
- bool const partialpossible((m_utf8_buf.size() > (search_filename.length() + 1)) && (m_utf8_buf[partialoffset - 1] == '/'));
- const bool namematch(
- !core_stricmp(search_filename.c_str(), &m_utf8_buf[0]) ||
- (partialpath && partialpossible && !core_stricmp(search_filename.c_str(), &m_utf8_buf[partialoffset])));
-
- const bool found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch);
- if (found)
+ try
+ {
+ for ( ; i < m_db.NumFiles; i++)
{
- m_curr_file_idx = i;
- m_curr_is_dir = is_dir;
- m_curr_name = &m_utf8_buf[0];
- m_curr_length = size;
- set_curr_modified();
- m_curr_crc = crc;
-
- return i;
+ make_utf8_name(i);
+ bool const is_dir(SzArEx_IsDir(&m_db, i));
+ const std::uint64_t size(SzArEx_GetFileSize(&m_db, i));
+ const std::uint32_t crc(m_db.CRCs.Vals[i]);
+
+ const bool crcmatch(SzBitArray_Check(m_db.CRCs.Defs, i) && (crc == search_crc));
+ bool found;
+ if (!matchname)
+ {
+ found = !matchcrc || (crcmatch && !is_dir);
+ }
+ else
+ {
+ auto const partialoffset = m_utf8_buf.size() - search_filename.length();
+ const bool namematch =
+ (search_filename.length() == m_utf8_buf.size()) &&
+ (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_utf8_buf[0], search_filename.length()));
+ bool const partialmatch =
+ partialpath &&
+ ((m_utf8_buf.size() > search_filename.length()) && (m_utf8_buf[partialoffset - 1] == '/')) &&
+ (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_utf8_buf[partialoffset], search_filename.length()));
+ found = (!matchcrc || crcmatch) && (namematch || partialmatch);
+ }
+
+ if (found)
+ {
+ // set the name first - resizing it can throw an exception, and we want the state to be consistent
+ m_curr_name.assign(m_utf8_buf.begin(), m_utf8_buf.end());
+ m_curr_file_idx = i;
+ m_curr_is_dir = is_dir;
+ m_curr_length = size;
+ set_curr_modified();
+ m_curr_crc = crc;
+
+ return i;
+ }
}
}
-
+ catch (...)
+ {
+ // allocation error handling name
+ }
return -1;
}
@@ -473,14 +555,14 @@ void m7z_file_impl::make_utf8_name(int index)
std::size_t len, out_pos;
len = SzArEx_GetFileNameUtf16(&m_db, index, nullptr);
- m_utf16_buf.resize((std::max<std::size_t>)(m_utf16_buf.size(), len));
+ m_utf16_buf.resize(std::max<std::size_t>(m_utf16_buf.size(), len));
SzArEx_GetFileNameUtf16(&m_db, index, &m_utf16_buf[0]);
- m_uchar_buf.resize((std::max<std::size_t>)(m_uchar_buf.size(), len));
+ m_uchar_buf.resize(std::max<std::size_t>(m_uchar_buf.size(), len));
out_pos = 0;
for (std::size_t in_pos = 0; in_pos < (len - 1); )
{
- const int used = uchar_from_utf16(&m_uchar_buf[out_pos], (char16_t *)&m_utf16_buf[in_pos], len - in_pos);
+ const int used = uchar_from_utf16(&m_uchar_buf[out_pos], reinterpret_cast<char16_t const *>(&m_utf16_buf[in_pos]), len - in_pos);
if (used < 0)
{
in_pos++;
@@ -495,7 +577,7 @@ void m7z_file_impl::make_utf8_name(int index)
}
len = out_pos;
- m_utf8_buf.resize((std::max<std::size_t>)(m_utf8_buf.size(), 4 * len + 1));
+ m_utf8_buf.resize((std::max<std::size_t>)(m_utf8_buf.size(), 4 * len));
out_pos = 0;
for (std::size_t in_pos = 0; in_pos < len; in_pos++)
{
@@ -506,30 +588,35 @@ void m7z_file_impl::make_utf8_name(int index)
out_pos += produced;
assert(out_pos < m_utf8_buf.size());
}
- m_utf8_buf[out_pos++] = '\0';
m_utf8_buf.resize(out_pos);
}
-void m7z_file_impl::set_curr_modified()
+void m7z_file_impl::set_curr_modified() noexcept
{
if (SzBitWithVals_Check(&m_db.MTime, m_curr_file_idx))
{
CNtfsFileTime const &file_time(m_db.MTime.Vals[m_curr_file_idx]);
- auto ticks = ntfs_duration_from_filetime(file_time.High, file_time.Low);
- m_curr_modified = system_clock_time_point_from_ntfs_duration(ticks);
- }
- else
- {
- // FIXME: what do we do about a lack of time?
+ try
+ {
+ auto ticks = ntfs_duration_from_filetime(file_time.High, file_time.Low);
+ m_curr_modified = system_clock_time_point_from_ntfs_duration(ticks);
+ return;
+ }
+ catch (...)
+ {
+ }
}
+
+ // no modification time available, or out-of-range exception
+ m_curr_modified = std::chrono::system_clock::from_time_t(std::time_t(0));
}
} // anonymous namespace
-archive_file::error archive_file::open_7z(const std::string &filename, ptr &result)
+std::error_condition archive_file::open_7z(std::string_view filename, ptr &result) noexcept
{
// ensure we start with a nullptr result
result.reset();
@@ -540,21 +627,49 @@ archive_file::error archive_file::open_7z(const std::string &filename, ptr &resu
if (!newimpl)
{
// allocate memory for the 7z file structure
- try { newimpl = std::make_unique<m7z_file_impl>(filename); }
- catch (...) { return error::OUT_OF_MEMORY; }
- error const err = newimpl->initialize();
- if (err != error::NONE) return err;
+ try { newimpl = std::make_unique<m7z_file_impl>(std::string(filename)); }
+ catch (...) { return std::errc::not_enough_memory; }
+ auto const err = newimpl->initialize();
+ if (err)
+ return err;
}
- try
+ // allocate the archive API wrapper
+ result.reset(new (std::nothrow) m7z_file_wrapper(std::move(newimpl)));
+ if (result)
{
- result = std::make_unique<m7z_file_wrapper>(std::move(newimpl));
- return error::NONE;
+ return std::error_condition();
}
- catch (...)
+ else
+ {
+ m7z_file_impl::close(std::move(newimpl));
+ return std::errc::not_enough_memory;
+ }
+}
+
+std::error_condition archive_file::open_7z(random_read::ptr &&file, ptr &result) noexcept
+{
+ // ensure we start with a nullptr result
+ result.reset();
+
+ // allocate memory for the zip_file structure
+ m7z_file_impl::ptr newimpl(new (std::nothrow) m7z_file_impl(std::move(file)));
+ if (!newimpl)
+ return std::errc::not_enough_memory;
+ auto const err = newimpl->initialize();
+ if (err)
+ return err;
+
+ // allocate the archive API wrapper
+ result.reset(new (std::nothrow) m7z_file_wrapper(std::move(newimpl)));
+ if (result)
+ {
+ return std::error_condition();
+ }
+ else
{
m7z_file_impl::close(std::move(newimpl));
- return error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
}
@@ -564,7 +679,7 @@ archive_file::error archive_file::open_7z(const std::string &filename, ptr &resu
cache and free all memory
-------------------------------------------------*/
-void m7z_file_cache_clear()
+void m7z_file_cache_clear() noexcept
{
// This is a trampoline called from unzip.cpp to avoid the need to have the zip and 7zip code in one file
m7z_file_impl::cache_clear();
diff --git a/src/lib/util/unicode.cpp b/src/lib/util/unicode.cpp
index 8e6478beee3..a07da6c7759 100644
--- a/src/lib/util/unicode.cpp
+++ b/src/lib/util/unicode.cpp
@@ -10,9 +10,10 @@
#include "unicode.h"
+#include "osdcomm.h"
+
#ifdef _WIN32
#include "strconv.h"
-#define UTF8PROC_DLLEXPORT
#endif
#include <utf8proc.h>
@@ -125,6 +126,17 @@ bool uchar_is_digit(char32_t uchar)
// into a unicode character
//-----------------------------------------------
+int uchar_from_utf8(char32_t *uchar, std::string_view utf8str)
+{
+ return uchar_from_utf8(uchar, utf8str.data(), utf8str.length());
+}
+
+
+//-------------------------------------------------
+// uchar_from_utf8 - convert a UTF-8 sequence
+// into a unicode character
+//-----------------------------------------------
+
int uchar_from_utf8(char32_t *uchar, const char *utf8char, size_t count)
{
// validate parameters
@@ -195,7 +207,7 @@ int uchar_from_utf8(char32_t *uchar, const char *utf8char, size_t count)
{
char32_t const auxchar = (unsigned char)utf8char[i];
- // all auxillary chars must be between 0x80-0xbf
+ // all auxiliary chars must be between 0x80-0xbf
if ((auxchar & 0xc0) != 0x80)
return -1;
@@ -268,18 +280,21 @@ int uchar_from_utf16f(char32_t *uchar, const char16_t *utf16char, size_t count)
// into a Unicode string
//-------------------------------------------------
-std::u32string ustr_from_utf8(const std::string &utf8str)
+std::u32string ustr_from_utf8(std::string_view utf8str)
{
std::u32string result;
- char const *utf8char(utf8str.c_str());
- size_t remaining(utf8str.length());
- while (remaining)
+ if (!utf8str.empty())
{
- char32_t ch;
- int const consumed(uchar_from_utf8(&ch, utf8char, remaining));
- result.append(1, (consumed > 0) ? ch : char32_t(0x00fffdU));
- utf8char += (consumed > 0) ? consumed : 1;
- remaining -= (consumed > 0) ? consumed : 1;
+ char const *utf8char(&utf8str[0]);
+ auto remaining(utf8str.length());
+ while (remaining)
+ {
+ char32_t ch;
+ int const consumed(uchar_from_utf8(&ch, utf8char, remaining));
+ result.append(1, (consumed > 0) ? ch : char32_t(0x00fffdU));
+ utf8char += (consumed > 0) ? consumed : 1;
+ remaining -= (consumed > 0) ? consumed : 1;
+ }
}
return result;
}
@@ -371,8 +386,8 @@ int utf8_from_uchar(char *utf8string, size_t count, char32_t uchar)
std::string utf8_from_uchar(char32_t uchar)
{
char buffer[UTF8_CHAR_MAX];
- auto len = utf8_from_uchar(buffer, ARRAY_LENGTH(buffer), uchar);
- return std::string(buffer, len);
+ auto const len = utf8_from_uchar(buffer, std::size(buffer), uchar);
+ return std::string(buffer, std::max<decltype(len)>(len, 0));
}
@@ -439,14 +454,14 @@ int utf16f_from_uchar(char16_t *utf16string, size_t count, char32_t uchar)
// wstring_from_utf8
//-------------------------------------------------
-std::wstring wstring_from_utf8(const std::string &utf8string)
+std::wstring wstring_from_utf8(std::string_view utf8string)
{
-#ifdef WIN32
+#ifdef _WIN32
// for some reason, using codecvt yields bad results on MinGW (but not MSVC)
return osd::text::to_wstring(utf8string);
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
- return converter.from_bytes(utf8string);
+ return converter.from_bytes(utf8string.data(), utf8string.data() + utf8string.length());
#endif
}
@@ -455,14 +470,14 @@ std::wstring wstring_from_utf8(const std::string &utf8string)
// utf8_from_wstring
//-------------------------------------------------
-std::string utf8_from_wstring(const std::wstring &string)
+std::string utf8_from_wstring(std::wstring_view string)
{
-#ifdef WIN32
+#ifdef _WIN32
// for some reason, using codecvt yields bad results on MinGW (but not MSVC)
return osd::text::from_wstring(string);
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
- return converter.to_bytes(string);
+ return converter.to_bytes(string.data(), string.data() + string.length());
#endif
}
@@ -472,17 +487,6 @@ std::string utf8_from_wstring(const std::wstring &string)
// unicode
//-------------------------------------------------
-std::string normalize_unicode(const std::string &s, unicode_normalization_form normalization_form, bool fold_case)
-{
- return internal_normalize_unicode(s.c_str(), s.length(), normalization_form, fold_case, false);
-}
-
-
-//-------------------------------------------------
-// normalize_unicode - uses utf8proc to normalize
-// unicode
-//-------------------------------------------------
-
std::string normalize_unicode(const char *s, unicode_normalization_form normalization_form, bool fold_case)
{
return internal_normalize_unicode(s, 0, normalization_form, fold_case, true);
@@ -494,9 +498,9 @@ std::string normalize_unicode(const char *s, unicode_normalization_form normaliz
// unicode
//-------------------------------------------------
-std::string normalize_unicode(const char *s, size_t length, unicode_normalization_form normalization_form, bool fold_case)
+std::string normalize_unicode(std::string_view s, unicode_normalization_form normalization_form, bool fold_case)
{
- return internal_normalize_unicode(s, length, normalization_form, fold_case, false);
+ return internal_normalize_unicode(s.data(), s.length(), normalization_form, fold_case, false);
}
diff --git a/src/lib/util/unicode.h b/src/lib/util/unicode.h
index 46e1aec3496..761c809595c 100644
--- a/src/lib/util/unicode.h
+++ b/src/lib/util/unicode.h
@@ -19,11 +19,12 @@
#pragma once
-#include "osdcore.h"
-
#include <string>
+#include <string_view>
-#include <stdlib.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
@@ -36,46 +37,6 @@
#define UTF8_CHAR_MAX 6
#define UTF16_CHAR_MAX 2
-// these are UTF-8 encoded strings for common characters
-#define UTF8_NBSP "\xc2\xa0" /* non-breaking space */
-
-#define UTF8_MULTIPLY "\xc3\x97" /* multiplication sign */
-#define UTF8_DIVIDE "\xc3\xb7" /* division sign */
-#define UTF8_SQUAREROOT "\xe2\x88\x9a" /* square root symbol */
-#define UTF8_PLUSMINUS "\xc2\xb1" /* plusminus symbol */
-
-#define UTF8_POW_2 "\xc2\xb2" /* superscript 2 */
-#define UTF8_POW_X "\xcb\xa3" /* superscript x */
-#define UTF8_POW_Y "\xca\xb8" /* superscript y */
-#define UTF8_PRIME "\xca\xb9" /* prime symbol */
-#define UTF8_DEGREES "\xc2\xb0" /* degrees symbol */
-
-#define UTF8_SMALL_PI "\xcf\x80" /* Greek small letter pi */
-#define UTF8_CAPITAL_SIGMA "\xce\xa3" /* Greek capital letter sigma */
-#define UTF8_CAPITAL_DELTA "\xce\x94" /* Greek capital letter delta */
-
-#define UTF8_MACRON "\xc2\xaf" /* macron symbol */
-#define UTF8_NONSPACE_MACRON "\xcc\x84" /* nonspace macron, use after another char */
-
-#define a_RING "\xc3\xa5" /* small a with a ring */
-#define a_UMLAUT "\xc3\xa4" /* small a with an umlaut */
-#define o_UMLAUT "\xc3\xb6" /* small o with an umlaut */
-#define u_UMLAUT "\xc3\xbc" /* small u with an umlaut */
-#define e_ACUTE "\xc3\xa9" /* small e with an acute */
-#define n_TILDE "\xc3\xb1" /* small n with a tilde */
-
-#define A_RING "\xc3\x85" /* capital A with a ring */
-#define A_UMLAUT "\xc3\x84" /* capital A with an umlaut */
-#define O_UMLAUT "\xc3\x96" /* capital O with an umlaut */
-#define U_UMLAUT "\xc3\x9c" /* capital U with an umlaut */
-#define E_ACUTE "\xc3\x89" /* capital E with an acute */
-#define N_TILDE "\xc3\x91" /* capital N with a tilde */
-
-#define UTF8_LEFT "\xe2\x86\x90" /* cursor left */
-#define UTF8_RIGHT "\xe2\x86\x92" /* cursor right */
-#define UTF8_UP "\xe2\x86\x91" /* cursor up */
-#define UTF8_DOWN "\xe2\x86\x93" /* cursor down */
-
enum class unicode_normalization_form { C, D, KC, KD };
@@ -95,9 +56,10 @@ bool uchar_is_digit(char32_t uchar);
// converting strings to 32-bit Unicode chars
int uchar_from_utf8(char32_t *uchar, const char *utf8char, size_t count);
+int uchar_from_utf8(char32_t *uchar, std::string_view utf8str);
int uchar_from_utf16(char32_t *uchar, const char16_t *utf16char, size_t count);
int uchar_from_utf16f(char32_t *uchar, const char16_t *utf16char, size_t count);
-std::u32string ustr_from_utf8(const std::string &utf8str);
+std::u32string ustr_from_utf8(std::string_view utf8str);
// converting 32-bit Unicode chars to strings
int utf8_from_uchar(char *utf8string, size_t count, char32_t uchar);
@@ -106,13 +68,12 @@ int utf16_from_uchar(char16_t *utf16string, size_t count, char32_t uchar);
int utf16f_from_uchar(char16_t *utf16string, size_t count, char32_t uchar);
// converting UTF-8 strings to/from "wide" strings
-std::wstring wstring_from_utf8(const std::string &utf8string);
-std::string utf8_from_wstring(const std::wstring &string);
+std::wstring wstring_from_utf8(std::string_view utf8string);
+std::string utf8_from_wstring(std::wstring_view string);
// unicode normalization
-std::string normalize_unicode(const std::string &s, unicode_normalization_form normalization_form, bool fold_case = false);
std::string normalize_unicode(const char *s, unicode_normalization_form normalization_form, bool fold_case = false);
-std::string normalize_unicode(const char *s, size_t length, unicode_normalization_form normalization_form, bool fold_case = false);
+std::string normalize_unicode(std::string_view s, unicode_normalization_form normalization_form, bool fold_case = false);
// upper and lower case
char32_t uchar_toupper(char32_t ch);
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp
index 035fc92ecc7..2ca8e722d25 100644
--- a/src/lib/util/unzip.cpp
+++ b/src/lib/util/unzip.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
- unzip.c
+ unzip.cpp
Functions to manipulate data within ZIP files.
@@ -12,52 +12,83 @@
#include "corestr.h"
#include "hashing.h"
-#include "osdcore.h"
+#include "ioprocs.h"
+#include "multibyte.h"
#include "timeconv.h"
+#include "osdcore.h"
+#include "osdfile.h"
+
#include "lzma/C/LzmaDec.h"
#include <zlib.h>
+#include <zstd.h>
#include <algorithm>
#include <array>
#include <cassert>
+#include <cerrno>
#include <chrono>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <mutex>
+#include <optional>
#include <ratio>
#include <utility>
#include <vector>
namespace util {
+
namespace {
+
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
+class archive_category_impl : public std::error_category
+{
+public:
+ virtual char const *name() const noexcept override { return "archive"; }
+
+ virtual std::string message(int condition) const override
+ {
+ using namespace std::literals;
+ static std::string_view const s_messages[] = {
+ "No error"sv,
+ "Bad archive signature"sv,
+ "Decompression error"sv,
+ "Archive file truncated"sv,
+ "Archive file corrupt"sv,
+ "Archive file uses unsupported features"sv,
+ "Buffer too small for data"sv };
+ if ((0 <= condition) && (std::size(s_messages) > condition))
+ return std::string(s_messages[condition]);
+ else
+ return "Unknown error"s;
+ }
+};
+
+
class zip_file_impl
{
public:
- typedef std::unique_ptr<zip_file_impl> ptr;
-
- zip_file_impl(const std::string &filename)
- : m_filename(filename)
- , m_file()
- , m_length(0)
- , m_ecd()
- , m_cd()
- , m_cd_pos(0)
- , m_header()
- , m_curr_is_dir(false)
- , m_buffer()
+ using ptr = std::unique_ptr<zip_file_impl>;
+
+ zip_file_impl(std::string &&filename) noexcept
+ : m_filename(std::move(filename))
{
std::fill(m_buffer.begin(), m_buffer.end(), 0);
}
- static ptr find_cached(const std::string &filename)
+ zip_file_impl(random_read::ptr &&file) noexcept
+ : zip_file_impl(std::string())
+ {
+ m_file = std::move(file);
+ }
+
+ static ptr find_cached(std::string_view filename) noexcept
{
std::lock_guard<std::mutex> guard(s_cache_mutex);
for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++)
@@ -65,43 +96,47 @@ public:
// if we have a valid entry and it matches our filename, use it and remove from the cache
if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename))
{
+ using std::swap;
ptr result;
- std::swap(s_cache[cachenum], result);
- osd_printf_verbose("unzip: found %s in cache\n", filename.c_str());
+ swap(s_cache[cachenum], result);
+ osd_printf_verbose("unzip: found %s in cache\n", filename);
return result;
}
}
return ptr();
}
- static void close(ptr &&zip);
- static void cache_clear()
+
+ static void close(ptr &&zip) noexcept;
+
+ static void cache_clear() noexcept
{
// clear call cache entries
std::lock_guard<std::mutex> guard(s_cache_mutex);
- for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { }
+ for (auto &cached : s_cache)
+ cached.reset();
}
- archive_file::error initialize()
+ std::error_condition initialize() noexcept
{
// read ecd data
auto const ziperr = read_ecd();
- if (ziperr != archive_file::error::NONE)
+ if (ziperr)
return ziperr;
// verify that we can work with this zipfile (no disk spanning allowed)
if (m_ecd.disk_number != m_ecd.cd_start_disk_number)
{
- osd_printf_error("unzip: %s central directory starts in another segment\n", m_filename.c_str());
+ osd_printf_error("unzip: %s central directory starts in another segment\n", m_filename);
return archive_file::error::UNSUPPORTED;
}
if (m_ecd.cd_disk_entries != m_ecd.cd_total_entries)
{
- osd_printf_error("unzip: %s not all central directory entries reside in this segment\n", m_filename.c_str());
+ osd_printf_error("unzip: %s not all central directory entries reside in this segment\n", m_filename);
return archive_file::error::UNSUPPORTED;
}
if (std::size_t(m_ecd.cd_size) != m_ecd.cd_size)
{
- osd_printf_error("unzip: %s central directory too large to load\n", m_filename.c_str());
+ osd_printf_error("unzip: %s central directory too large to load\n", m_filename);
return archive_file::error::UNSUPPORTED;
}
@@ -109,8 +144,8 @@ public:
try { m_cd.resize(std::size_t(m_ecd.cd_size)); }
catch (...)
{
- osd_printf_error("unzip: %s failed to allocate memory for central directory\n", m_filename.c_str());
- return archive_file::error::OUT_OF_MEMORY;
+ osd_printf_error("unzip: %s failed to allocate memory for central directory\n", m_filename);
+ return std::errc::not_enough_memory;
}
// read the central directory
@@ -118,68 +153,73 @@ public:
std::size_t cd_offs(0);
while (cd_remaining)
{
- std::uint32_t const chunk(std::uint32_t((std::min<std::uint64_t>)(std::numeric_limits<std::uint32_t>::max(), cd_remaining)));
- std::uint32_t read_length(0);
- auto const filerr = m_file->read(&m_cd[cd_offs], m_ecd.cd_start_disk_offset + cd_offs, chunk, read_length);
- if (filerr != osd_file::error::NONE)
+ std::size_t const chunk(std::size_t(std::min<std::uint64_t>(std::numeric_limits<std::size_t>::max(), cd_remaining)));
+ auto const [filerr, read_length] = read_at(*m_file, m_ecd.cd_start_disk_offset + cd_offs, &m_cd[cd_offs], chunk);
+ if (filerr)
{
- osd_printf_error("unzip: %s error reading central directory (%d)\n", m_filename.c_str(), int(filerr));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error(
+ "unzip: %s error reading central directory (%s:%d %s)\n",
+ m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
if (!read_length)
{
- osd_printf_error("unzip: %s unexpectedly reached end-of-file while reading central directory\n", m_filename.c_str());
+ osd_printf_error("unzip: %s unexpectedly reached end-of-file while reading central directory\n", m_filename);
return archive_file::error::FILE_TRUNCATED;
}
cd_remaining -= read_length;
cd_offs += read_length;
}
- osd_printf_verbose("unzip: read %s central directory\n", m_filename.c_str());
+ osd_printf_verbose("unzip: read %s central directory\n", m_filename);
- return archive_file::error::NONE;
+ return std::error_condition();
}
- int first_file()
+ int first_file() noexcept
{
m_cd_pos = 0;
- return search(0, std::string(), false, false, false);
+ return search(0, std::string_view(), false, false, false);
}
- int next_file()
+
+ int next_file() noexcept
{
- return search(0, std::string(), false, false, false);
+ return search(0, std::string_view(), false, false, false);
}
- int search(std::uint32_t crc)
+ int search(std::uint32_t crc) noexcept
{
m_cd_pos = 0;
- return search(crc, std::string(), true, false, false);
+ return search(crc, std::string_view(), true, false, false);
}
- int search(const std::string &filename, bool partialpath)
+
+ int search(std::string_view filename, bool partialpath) noexcept
{
m_cd_pos = 0;
return search(0, filename, false, true, partialpath);
}
- int search(std::uint32_t crc, const std::string &filename, bool partialpath)
+
+ int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept
{
m_cd_pos = 0;
return search(crc, filename, true, true, partialpath);
}
- bool current_is_directory() const { return m_curr_is_dir; }
- const std::string &current_name() const { return m_header.file_name; }
- std::uint64_t current_uncompressed_length() const { return m_header.uncompressed_length; }
- std::chrono::system_clock::time_point current_last_modified() const
+ bool current_is_directory() const noexcept { return m_curr_is_dir; }
+
+ const std::string &current_name() const noexcept { return m_header.file_name; }
+
+ std::uint64_t current_uncompressed_length() const noexcept { return m_header.uncompressed_length; }
+
+ std::chrono::system_clock::time_point current_last_modified() const noexcept
{
- if (!m_header.modified_cached)
- {
+ if (!m_header.modified)
m_header.modified = decode_dos_time(m_header.modified_date, m_header.modified_time);
- m_header.modified_cached = true;
- }
- return m_header.modified;
+ return *m_header.modified;
}
- std::uint32_t current_crc() const { return m_header.crc; }
- archive_file::error decompress(void *buffer, std::uint32_t length);
+ std::uint32_t current_crc() const noexcept { return m_header.crc; }
+
+ std::error_condition decompress(void *buffer, std::size_t length) noexcept;
private:
zip_file_impl(const zip_file_impl &) = delete;
@@ -187,28 +227,43 @@ private:
zip_file_impl &operator=(const zip_file_impl &) = delete;
zip_file_impl &operator=(zip_file_impl &&) = delete;
- int search(std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname, bool partialpath);
+ int search(std::uint32_t search_crc, std::string_view search_filename, bool matchcrc, bool matchname, bool partialpath) noexcept;
- archive_file::error reopen()
+ std::error_condition reopen() noexcept
{
if (!m_file)
{
- auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length);
- if (filerr != osd_file::error::NONE)
+ osd_file::ptr file;
+ auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_length);
+ if (filerr)
{
// this would spam every time it looks for a non-existent archive, which is a lot
- //osd_printf_error("unzip: error reopening archive file %s (%d)\n", m_filename.c_str(), int(filerr));
- return archive_file::error::FILE_ERROR;
+ //osd_printf_error("unzip: error reopening archive file %s (%s:%d %s)\n", m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
- else
+ m_file = osd_file_read(std::move(file));
+ if (!m_file)
{
- osd_printf_verbose("unzip: opened archive file %s\n", m_filename.c_str());
+ osd_printf_error("unzip: not enough memory to open archive file %s\n", m_filename);
+ return std::errc::not_enough_memory;
}
+ osd_printf_verbose("unzip: opened archive file %s\n", m_filename);
}
- return archive_file::error::NONE;
+ else if (!m_length)
+ {
+ auto const filerr = m_file->length(m_length);
+ if (filerr)
+ {
+ osd_printf_verbose(
+ "unzip: error getting length of archive file %s (%s:%d %s)\n",
+ m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
+ }
+ }
+ return std::error_condition();
}
- static std::chrono::system_clock::time_point decode_dos_time(std::uint16_t date, std::uint16_t time)
+ static std::chrono::system_clock::time_point decode_dos_time(std::uint16_t date, std::uint16_t time) noexcept
{
// FIXME: work out why this doesn't always work
// negative tm_isdst should automatically determine whether DST is in effect for the date,
@@ -227,30 +282,40 @@ private:
}
// ZIP file parsing
- archive_file::error read_ecd();
- archive_file::error get_compressed_data_offset(std::uint64_t &offset);
+ std::error_condition read_ecd() noexcept;
+ std::error_condition get_compressed_data_offset(std::uint64_t &offset) noexcept;
// decompression interfaces
- archive_file::error decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length);
- archive_file::error decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length);
- archive_file::error decompress_data_type_14(std::uint64_t offset, void *buffer, std::uint32_t length);
+ std::error_condition decompress_data_type_0(std::uint64_t offset, void *buffer, std::size_t length) noexcept;
+ std::error_condition decompress_data_type_8(std::uint64_t offset, void *buffer, std::size_t length) noexcept;
+ std::error_condition decompress_data_type_14(std::uint64_t offset, void *buffer, std::size_t length) noexcept;
+ std::error_condition decompress_data_type_93(std::uint64_t offset, void *buffer, std::size_t length) noexcept;
struct file_header
{
- std::uint16_t version_created; // version made by
- std::uint16_t version_needed; // version needed to extract
- std::uint16_t bit_flag; // general purpose bit flag
- std::uint16_t compression; // compression method
- mutable std::chrono::system_clock::time_point modified; // last mod file date/time
- std::uint32_t crc; // crc-32
- std::uint64_t compressed_length; // compressed size
- std::uint64_t uncompressed_length; // uncompressed size
- std::uint32_t start_disk_number; // disk number start
- std::uint64_t local_header_offset; // relative offset of local header
- std::string file_name; // file name
-
- std::uint16_t modified_date, modified_time;
- mutable bool modified_cached;
+ private:
+ using optional_time_point = std::optional<std::chrono::system_clock::time_point>;
+
+ public:
+ file_header() noexcept { }
+ file_header(file_header const &) = default;
+ file_header(file_header &&) noexcept = default;
+ file_header &operator=(file_header const &) = default;
+ file_header &operator=(file_header &&) noexcept = default;
+
+ std::uint16_t version_created; // version made by
+ std::uint16_t version_needed; // version needed to extract
+ std::uint16_t bit_flag; // general purpose bit flag
+ std::uint16_t compression; // compression method
+ mutable optional_time_point modified; // last mod file date/time
+ std::uint32_t crc; // crc-32
+ std::uint64_t compressed_length; // compressed size
+ std::uint64_t uncompressed_length; // uncompressed size
+ std::uint32_t start_disk_number; // disk number start
+ std::uint64_t local_header_offset; // relative offset of local header
+ std::string file_name; // file name
+
+ std::uint16_t modified_date, modified_time;
};
// contains extracted end of central directory information
@@ -270,15 +335,15 @@ private:
static std::mutex s_cache_mutex;
const std::string m_filename; // copy of ZIP filename (for caching)
- osd_file::ptr m_file; // OSD file handle
- std::uint64_t m_length; // length of zip file
+ random_read::ptr m_file; // file handle
+ std::uint64_t m_length = 0; // length of zip file
ecd m_ecd; // end of central directory
std::vector<std::uint8_t> m_cd; // central directory raw data
- std::uint32_t m_cd_pos; // position in central directory
+ std::uint32_t m_cd_pos = 0; // position in central directory
file_header m_header; // current file header
- bool m_curr_is_dir; // current file is directory
+ bool m_curr_is_dir = false; // current file is directory
std::array<std::uint8_t, DECOMPRESS_BUFSIZE> m_buffer; // buffer for decompression
};
@@ -287,32 +352,32 @@ private:
class zip_file_wrapper : public archive_file
{
public:
- zip_file_wrapper(zip_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); }
+ zip_file_wrapper(zip_file_impl::ptr &&impl) noexcept : m_impl(std::move(impl)) { assert(m_impl); }
virtual ~zip_file_wrapper() { zip_file_impl::close(std::move(m_impl)); }
- virtual int first_file() override { return m_impl->first_file(); }
- virtual int next_file() override { return m_impl->next_file(); }
+ virtual int first_file() noexcept override { return m_impl->first_file(); }
+ virtual int next_file() noexcept override { return m_impl->next_file(); }
- virtual int search(std::uint32_t crc) override
+ virtual int search(std::uint32_t crc) noexcept override
{
return m_impl->search(crc);
}
- virtual int search(const std::string &filename, bool partialpath) override
+ virtual int search(std::string_view filename, bool partialpath) noexcept override
{
return m_impl->search(filename, partialpath);
}
- virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) override
+ virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept override
{
return m_impl->search(crc, filename, partialpath);
}
- virtual bool current_is_directory() const override { return m_impl->current_is_directory(); }
- virtual const std::string &current_name() const override { return m_impl->current_name(); }
- virtual std::uint64_t current_uncompressed_length() const override { return m_impl->current_uncompressed_length(); }
- virtual std::chrono::system_clock::time_point current_last_modified() const override { return m_impl->current_last_modified(); }
- virtual std::uint32_t current_crc() const override { return m_impl->current_crc(); }
+ virtual bool current_is_directory() const noexcept override { return m_impl->current_is_directory(); }
+ virtual const std::string &current_name() const noexcept override { return m_impl->current_name(); }
+ virtual std::uint64_t current_uncompressed_length() const noexcept override { return m_impl->current_uncompressed_length(); }
+ virtual std::chrono::system_clock::time_point current_last_modified() const noexcept override { return m_impl->current_last_modified(); }
+ virtual std::uint32_t current_crc() const noexcept override { return m_impl->current_crc(); }
- virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); }
+ virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept override { return m_impl->decompress(buffer, length); }
private:
zip_file_impl::ptr m_impl;
@@ -322,37 +387,23 @@ private:
class reader_base
{
protected:
- reader_base(void const *buf) : m_buffer(reinterpret_cast<std::uint8_t const *>(buf)) { }
+ reader_base(void const *buf) noexcept : m_buffer(reinterpret_cast<std::uint8_t const *>(buf)) { }
- std::uint8_t read_byte(std::size_t offs) const
+ std::uint8_t read_byte(std::size_t offs) const noexcept
{
return m_buffer[offs];
}
- std::uint16_t read_word(std::size_t offs) const
+ std::uint16_t read_word(std::size_t offs) const noexcept
{
- return
- (std::uint16_t(m_buffer[offs + 1]) << 8) |
- (std::uint16_t(m_buffer[offs + 0]) << 0);
+ return get_u16le(&m_buffer[offs]);
}
- std::uint32_t read_dword(std::size_t offs) const
+ std::uint32_t read_dword(std::size_t offs) const noexcept
{
- return
- (std::uint32_t(m_buffer[offs + 3]) << 24) |
- (std::uint32_t(m_buffer[offs + 2]) << 16) |
- (std::uint32_t(m_buffer[offs + 1]) << 8) |
- (std::uint32_t(m_buffer[offs + 0]) << 0);
+ return get_u32le(&m_buffer[offs]);
}
- std::uint64_t read_qword(std::size_t offs) const
+ std::uint64_t read_qword(std::size_t offs) const noexcept
{
- return
- (std::uint64_t(m_buffer[offs + 7]) << 56) |
- (std::uint64_t(m_buffer[offs + 6]) << 48) |
- (std::uint64_t(m_buffer[offs + 5]) << 40) |
- (std::uint64_t(m_buffer[offs + 4]) << 32) |
- (std::uint64_t(m_buffer[offs + 3]) << 24) |
- (std::uint64_t(m_buffer[offs + 2]) << 16) |
- (std::uint64_t(m_buffer[offs + 1]) << 8) |
- (std::uint64_t(m_buffer[offs + 0]) << 0);
+ return get_u64le(&m_buffer[offs]);
}
std::string read_string(std::size_t offs, std::string::size_type len) const
{
@@ -370,17 +421,17 @@ protected:
class extra_field_reader : private reader_base
{
public:
- extra_field_reader(void const *buf, std::size_t len) : reader_base(buf), m_length(len) { }
+ extra_field_reader(void const *buf, std::size_t len) noexcept : reader_base(buf), m_length(len) { }
- std::uint16_t header_id() const { return read_word(0x00); }
- std::uint16_t data_size() const { return read_word(0x02); }
- void const * data() const { return m_buffer + 0x04; }
- extra_field_reader next() const { return extra_field_reader(m_buffer + total_length(), m_length - total_length()); }
+ std::uint16_t header_id() const noexcept { return read_word(0x00); }
+ std::uint16_t data_size() const noexcept { return read_word(0x02); }
+ void const * data() const noexcept { return m_buffer + 0x04; }
+ extra_field_reader next() const noexcept { return extra_field_reader(m_buffer + total_length(), m_length - total_length()); }
- bool length_sufficient() const { return (m_length >= minimum_length()) && (m_length >= total_length()); }
+ bool length_sufficient() const noexcept { return (m_length >= minimum_length()) && (m_length >= total_length()); }
- std::size_t total_length() const { return minimum_length() + data_size(); }
- static std::size_t minimum_length() { return 0x04; }
+ std::size_t total_length() const noexcept { return minimum_length() + data_size(); }
+ static constexpr std::size_t minimum_length() { return 0x04; }
private:
std::size_t m_length;
@@ -390,131 +441,131 @@ private:
class local_file_header_reader : private reader_base
{
public:
- local_file_header_reader(void const *buf) : reader_base(buf) { }
-
- std::uint32_t signature() const { return read_dword(0x00); }
- std::uint8_t version_needed() const { return m_buffer[0x04]; }
- std::uint8_t os_needed() const { return m_buffer[0x05]; }
- std::uint16_t general_flag() const { return read_word(0x06); }
- std::uint16_t compression_method() const { return read_word(0x08); }
- std::uint16_t modified_time() const { return read_word(0x0a); }
- std::uint16_t modified_date() const { return read_word(0x0c); }
- std::uint32_t crc32() const { return read_dword(0x0e); }
- std::uint32_t compressed_size() const { return read_dword(0x12); }
- std::uint32_t uncompressed_size() const { return read_dword(0x16); }
- std::uint16_t file_name_length() const { return read_word(0x1a); }
- std::uint16_t extra_field_length() const { return read_word(0x1c); }
+ local_file_header_reader(void const *buf) noexcept : reader_base(buf) { }
+
+ std::uint32_t signature() const noexcept { return read_dword(0x00); }
+ std::uint8_t version_needed() const noexcept { return m_buffer[0x04]; }
+ std::uint8_t os_needed() const noexcept { return m_buffer[0x05]; }
+ std::uint16_t general_flag() const noexcept { return read_word(0x06); }
+ std::uint16_t compression_method() const noexcept { return read_word(0x08); }
+ std::uint16_t modified_time() const noexcept { return read_word(0x0a); }
+ std::uint16_t modified_date() const noexcept { return read_word(0x0c); }
+ std::uint32_t crc32() const noexcept { return read_dword(0x0e); }
+ std::uint32_t compressed_size() const noexcept { return read_dword(0x12); }
+ std::uint32_t uncompressed_size() const noexcept { return read_dword(0x16); }
+ std::uint16_t file_name_length() const noexcept { return read_word(0x1a); }
+ std::uint16_t extra_field_length() const noexcept { return read_word(0x1c); }
std::string file_name() const { return read_string(0x1e, file_name_length()); }
void file_name(std::string &result) const { read_string(result, 0x1e, file_name_length()); }
- extra_field_reader extra_field() const { return extra_field_reader(m_buffer + 0x1e + file_name_length(), extra_field_length()); }
+ extra_field_reader extra_field() const noexcept { return extra_field_reader(m_buffer + 0x1e + file_name_length(), extra_field_length()); }
- bool signature_correct() const { return signature() == 0x04034b50; }
+ bool signature_correct() const noexcept { return signature() == 0x04034b50; }
- std::size_t total_length() const { return minimum_length() + file_name_length() + extra_field_length(); }
- static std::size_t minimum_length() { return 0x1e; }
+ std::size_t total_length() const noexcept { return minimum_length() + file_name_length() + extra_field_length(); }
+ static constexpr std::size_t minimum_length() { return 0x1e; }
};
class central_dir_entry_reader : private reader_base
{
public:
- central_dir_entry_reader(void const *buf) : reader_base(buf) { }
-
- std::uint32_t signature() const { return read_dword(0x00); }
- std::uint8_t version_created() const { return m_buffer[0x04]; }
- std::uint8_t os_created() const { return m_buffer[0x05]; }
- std::uint8_t version_needed() const { return m_buffer[0x06]; }
- std::uint8_t os_needed() const { return m_buffer[0x07]; }
- std::uint16_t general_flag() const { return read_word(0x08); }
- std::uint16_t compression_method() const { return read_word(0x0a); }
- std::uint16_t modified_time() const { return read_word(0x0c); }
- std::uint16_t modified_date() const { return read_word(0x0e); }
- std::uint32_t crc32() const { return read_dword(0x10); }
- std::uint32_t compressed_size() const { return read_dword(0x14); }
- std::uint32_t uncompressed_size() const { return read_dword(0x18); }
- std::uint16_t file_name_length() const { return read_word(0x1c); }
- std::uint16_t extra_field_length() const { return read_word(0x1e); }
- std::uint16_t file_comment_length() const { return read_word(0x20); }
- std::uint16_t start_disk() const { return read_word(0x22); }
- std::uint16_t int_file_attr() const { return read_word(0x24); }
- std::uint32_t ext_file_attr() const { return read_dword(0x26); }
- std::uint32_t header_offset() const { return read_dword(0x2a); }
+ central_dir_entry_reader(void const *buf) noexcept : reader_base(buf) { }
+
+ std::uint32_t signature() const noexcept { return read_dword(0x00); }
+ std::uint8_t version_created() const noexcept { return m_buffer[0x04]; }
+ std::uint8_t os_created() const noexcept { return m_buffer[0x05]; }
+ std::uint8_t version_needed() const noexcept { return m_buffer[0x06]; }
+ std::uint8_t os_needed() const noexcept { return m_buffer[0x07]; }
+ std::uint16_t general_flag() const noexcept { return read_word(0x08); }
+ std::uint16_t compression_method() const noexcept { return read_word(0x0a); }
+ std::uint16_t modified_time() const noexcept { return read_word(0x0c); }
+ std::uint16_t modified_date() const noexcept { return read_word(0x0e); }
+ std::uint32_t crc32() const noexcept { return read_dword(0x10); }
+ std::uint32_t compressed_size() const noexcept { return read_dword(0x14); }
+ std::uint32_t uncompressed_size() const noexcept { return read_dword(0x18); }
+ std::uint16_t file_name_length() const noexcept { return read_word(0x1c); }
+ std::uint16_t extra_field_length() const noexcept { return read_word(0x1e); }
+ std::uint16_t file_comment_length() const noexcept { return read_word(0x20); }
+ std::uint16_t start_disk() const noexcept { return read_word(0x22); }
+ std::uint16_t int_file_attr() const noexcept { return read_word(0x24); }
+ std::uint32_t ext_file_attr() const noexcept { return read_dword(0x26); }
+ std::uint32_t header_offset() const noexcept { return read_dword(0x2a); }
std::string file_name() const { return read_string(0x2e, file_name_length()); }
void file_name(std::string &result) const { read_string(result, 0x2e, file_name_length()); }
- extra_field_reader extra_field() const { return extra_field_reader(m_buffer + 0x2e + file_name_length(), extra_field_length()); }
+ extra_field_reader extra_field() const noexcept { return extra_field_reader(m_buffer + 0x2e + file_name_length(), extra_field_length()); }
std::string file_comment() const { return read_string(0x2e + file_name_length() + extra_field_length(), file_comment_length()); }
void file_comment(std::string &result) const { read_string(result, 0x2e + file_name_length() + extra_field_length(), file_comment_length()); }
- bool signature_correct() const { return signature() == 0x02014b50; }
+ bool signature_correct() const noexcept { return signature() == 0x02014b50; }
- std::size_t total_length() const { return minimum_length() + file_name_length() + extra_field_length() + file_comment_length(); }
- static std::size_t minimum_length() { return 0x2e; }
+ std::size_t total_length() const noexcept { return minimum_length() + file_name_length() + extra_field_length() + file_comment_length(); }
+ static constexpr std::size_t minimum_length() { return 0x2e; }
};
class ecd64_reader : private reader_base
{
public:
- ecd64_reader(void const *buf) : reader_base(buf) { }
-
- std::uint32_t signature() const { return read_dword(0x00); }
- std::uint64_t ecd64_size() const { return read_qword(0x04); }
- std::uint8_t version_created() const { return m_buffer[0x0c]; }
- std::uint8_t os_created() const { return m_buffer[0x0d]; }
- std::uint8_t version_needed() const { return m_buffer[0x0e]; }
- std::uint8_t os_needed() const { return m_buffer[0x0f]; }
- std::uint32_t this_disk_no() const { return read_dword(0x10); }
- std::uint32_t dir_start_disk() const { return read_dword(0x14); }
- std::uint64_t dir_disk_entries() const { return read_qword(0x18); }
- std::uint64_t dir_total_entries() const { return read_qword(0x20); }
- std::uint64_t dir_size() const { return read_qword(0x28); }
- std::uint64_t dir_offset() const { return read_qword(0x30); }
- void const * extensible_data() const { return m_buffer + 0x38; }
-
- bool signature_correct() const { return signature() == 0x06064b50; }
-
- std::size_t total_length() const { return 0x0c + ecd64_size(); }
- static std::size_t minimum_length() { return 0x38; }
+ ecd64_reader(void const *buf) noexcept : reader_base(buf) { }
+
+ std::uint32_t signature() const noexcept { return read_dword(0x00); }
+ std::uint64_t ecd64_size() const noexcept { return read_qword(0x04); }
+ std::uint8_t version_created() const noexcept { return m_buffer[0x0c]; }
+ std::uint8_t os_created() const noexcept { return m_buffer[0x0d]; }
+ std::uint8_t version_needed() const noexcept { return m_buffer[0x0e]; }
+ std::uint8_t os_needed() const noexcept { return m_buffer[0x0f]; }
+ std::uint32_t this_disk_no() const noexcept { return read_dword(0x10); }
+ std::uint32_t dir_start_disk() const noexcept { return read_dword(0x14); }
+ std::uint64_t dir_disk_entries() const noexcept { return read_qword(0x18); }
+ std::uint64_t dir_total_entries() const noexcept { return read_qword(0x20); }
+ std::uint64_t dir_size() const noexcept { return read_qword(0x28); }
+ std::uint64_t dir_offset() const noexcept { return read_qword(0x30); }
+ void const * extensible_data() const noexcept { return m_buffer + 0x38; }
+
+ bool signature_correct() const noexcept { return signature() == 0x06064b50; }
+
+ std::size_t total_length() const noexcept { return 0x0c + ecd64_size(); }
+ static constexpr std::size_t minimum_length() { return 0x38; }
};
class ecd64_locator_reader : private reader_base
{
public:
- ecd64_locator_reader(void const *buf) : reader_base(buf) { }
+ ecd64_locator_reader(void const *buf) noexcept : reader_base(buf) { }
- std::uint32_t signature() const { return read_dword(0x00); }
- std::uint32_t ecd64_disk() const { return read_dword(0x04); }
- std::uint64_t ecd64_offset() const { return read_qword(0x08); }
- std::uint32_t total_disks() const { return read_dword(0x10); }
+ std::uint32_t signature() const noexcept { return read_dword(0x00); }
+ std::uint32_t ecd64_disk() const noexcept { return read_dword(0x04); }
+ std::uint64_t ecd64_offset() const noexcept { return read_qword(0x08); }
+ std::uint32_t total_disks() const noexcept { return read_dword(0x10); }
- bool signature_correct() const { return signature() == 0x07064b50; }
+ bool signature_correct() const noexcept { return signature() == 0x07064b50; }
- std::size_t total_length() const { return minimum_length(); }
- static std::size_t minimum_length() { return 0x14; }
+ std::size_t total_length() const noexcept { return minimum_length(); }
+ static constexpr std::size_t minimum_length() { return 0x14; }
};
class ecd_reader : private reader_base
{
public:
- ecd_reader(void const *buf) : reader_base(buf) { }
-
- std::uint32_t signature() const { return read_dword(0x00); }
- std::uint16_t this_disk_no() const { return read_word(0x04); }
- std::uint16_t dir_start_disk() const { return read_word(0x06); }
- std::uint16_t dir_disk_entries() const { return read_word(0x08); }
- std::uint16_t dir_total_entries() const { return read_word(0x0a); }
- std::uint32_t dir_size() const { return read_dword(0x0c); }
- std::uint32_t dir_offset() const { return read_dword(0x10); }
- std::uint16_t comment_length() const { return read_word(0x14); }
+ ecd_reader(void const *buf) noexcept : reader_base(buf) { }
+
+ std::uint32_t signature() const noexcept { return read_dword(0x00); }
+ std::uint16_t this_disk_no() const noexcept { return read_word(0x04); }
+ std::uint16_t dir_start_disk() const noexcept { return read_word(0x06); }
+ std::uint16_t dir_disk_entries() const noexcept { return read_word(0x08); }
+ std::uint16_t dir_total_entries() const noexcept { return read_word(0x0a); }
+ std::uint32_t dir_size() const noexcept { return read_dword(0x0c); }
+ std::uint32_t dir_offset() const noexcept { return read_dword(0x10); }
+ std::uint16_t comment_length() const noexcept { return read_word(0x14); }
std::string comment() const { return read_string(0x16, comment_length()); }
void comment(std::string &result) const { read_string(result, 0x16, comment_length()); }
- bool signature_correct() const { return signature() == 0x06054b50; }
+ bool signature_correct() const noexcept { return signature() == 0x06054b50; }
- std::size_t total_length() const { return minimum_length() + comment_length(); }
- static std::size_t minimum_length() { return 0x16; }
+ std::size_t total_length() const noexcept { return minimum_length() + comment_length(); }
+ static constexpr std::size_t minimum_length() { return 0x16; }
};
@@ -524,26 +575,26 @@ public:
template <typename T>
zip64_ext_info_reader(
T const &header,
- extra_field_reader const &field)
+ extra_field_reader const &field) noexcept
: reader_base(field.data())
, m_uncompressed_size(header.uncompressed_size())
, m_compressed_size(header.compressed_size())
, m_header_offset(header.header_offset())
, m_start_disk(header.start_disk())
- , m_offs_compressed_size(~m_uncompressed_size ? 0 : 8)
- , m_offs_header_offset(m_offs_compressed_size + (~m_compressed_size ? 0 : 8))
- , m_offs_start_disk(m_offs_header_offset + (~m_header_offset ? 0 : 8))
- , m_offs_end(m_offs_start_disk + (~m_start_disk ? 0 : 4))
+ , m_offs_compressed_size((0xffff'ffffU != m_uncompressed_size) ? 0 : 8)
+ , m_offs_header_offset(m_offs_compressed_size + ((0xffff'ffffU != m_compressed_size) ? 0 : 8))
+ , m_offs_start_disk(m_offs_header_offset + ((0xffff'ffffU != m_header_offset) ? 0 : 8))
+ , m_offs_end(m_offs_start_disk + ((0xffffU != m_start_disk) ? 0 : 4))
{
}
- std::uint64_t uncompressed_size() const { return ~m_uncompressed_size ? m_uncompressed_size : read_qword(0x00); }
- std::uint64_t compressed_size() const { return ~m_compressed_size ? m_compressed_size : read_qword(m_offs_compressed_size); }
- std::uint64_t header_offset() const { return ~m_header_offset ? m_header_offset : read_qword(m_offs_header_offset); }
- std::uint32_t start_disk() const { return ~m_start_disk ? m_start_disk : read_dword(m_offs_start_disk); }
+ std::uint64_t uncompressed_size() const noexcept { return (0xffff'ffffU != m_uncompressed_size) ? m_uncompressed_size : read_qword(0x00); }
+ std::uint64_t compressed_size() const noexcept { return (0xffff'ffffU != m_compressed_size) ? m_compressed_size : read_qword(m_offs_compressed_size); }
+ std::uint64_t header_offset() const noexcept { return (0xffff'ffffU != m_header_offset) ? m_header_offset : read_qword(m_offs_header_offset); }
+ std::uint32_t start_disk() const noexcept { return (0xffffU != m_start_disk) ? m_start_disk : read_dword(m_offs_start_disk); }
- std::size_t total_length() const { return minimum_length() + m_offs_end; }
- static std::size_t minimum_length() { return 0x00; }
+ std::size_t total_length() const noexcept { return minimum_length() + m_offs_end; }
+ static constexpr std::size_t minimum_length() { return 0x00; }
private:
std::uint32_t m_uncompressed_size;
@@ -561,15 +612,15 @@ private:
class utf8_path_reader : private reader_base
{
public:
- utf8_path_reader(extra_field_reader const &field) : reader_base(field.data()), m_length(field.data_size()) { }
+ utf8_path_reader(extra_field_reader const &field) noexcept : reader_base(field.data()), m_length(field.data_size()) { }
- std::uint8_t version() const { return m_buffer[0]; }
- std::uint32_t name_crc32() const { return read_dword(0x01); }
+ std::uint8_t version() const noexcept { return m_buffer[0]; }
+ std::uint32_t name_crc32() const noexcept { return read_dword(0x01); }
std::string unicode_name() const { return read_string(0x05, m_length - 0x05); }
void unicode_name(std::string &result) const { return read_string(result, 0x05, m_length - 0x05); }
- std::size_t total_length() const { return m_length; }
- static std::size_t minimum_length() { return 0x05; }
+ std::size_t total_length() const noexcept { return m_length; }
+ static constexpr std::size_t minimum_length() { return 0x05; }
private:
std::size_t m_length;
@@ -579,17 +630,17 @@ private:
class ntfs_tag_reader : private reader_base
{
public:
- ntfs_tag_reader(void const *buf, std::size_t len) : reader_base(buf), m_length(len) { }
+ ntfs_tag_reader(void const *buf, std::size_t len) noexcept : reader_base(buf), m_length(len) { }
- std::uint16_t tag() const { return read_word(0x00); }
- std::uint16_t size() const { return read_word(0x02); }
- void const * data() const { return m_buffer + 0x04; }
- ntfs_tag_reader next() const { return ntfs_tag_reader(m_buffer + total_length(), m_length - total_length()); }
+ std::uint16_t tag() const noexcept { return read_word(0x00); }
+ std::uint16_t size() const noexcept { return read_word(0x02); }
+ void const * data() const noexcept { return m_buffer + 0x04; }
+ ntfs_tag_reader next() const noexcept { return ntfs_tag_reader(m_buffer + total_length(), m_length - total_length()); }
- bool length_sufficient() const { return (m_length >= minimum_length()) && (m_length >= total_length()); }
+ bool length_sufficient() const noexcept { return (m_length >= minimum_length()) && (m_length >= total_length()); }
- std::size_t total_length() const { return minimum_length() + size(); }
- static std::size_t minimum_length() { return 0x04; }
+ std::size_t total_length() const noexcept { return minimum_length() + size(); }
+ static constexpr std::size_t minimum_length() { return 0x04; }
private:
std::size_t m_length;
@@ -599,13 +650,13 @@ private:
class ntfs_reader : private reader_base
{
public:
- ntfs_reader(extra_field_reader const &field) : reader_base(field.data()), m_length(field.data_size()) { }
+ ntfs_reader(extra_field_reader const &field) noexcept : reader_base(field.data()), m_length(field.data_size()) { }
- std::uint32_t reserved() const { return read_dword(0x00); }
- ntfs_tag_reader tag1() const { return ntfs_tag_reader(m_buffer + 0x04, m_length - 4); }
+ std::uint32_t reserved() const noexcept { return read_dword(0x00); }
+ ntfs_tag_reader tag1() const noexcept { return ntfs_tag_reader(m_buffer + 0x04, m_length - 4); }
- std::size_t total_length() const { return m_length; }
- static std::size_t minimum_length() { return 0x08; }
+ std::size_t total_length() const noexcept { return m_length; }
+ static constexpr std::size_t minimum_length() { return 0x08; }
private:
std::size_t m_length;
@@ -615,14 +666,14 @@ private:
class ntfs_times_reader : private reader_base
{
public:
- ntfs_times_reader(ntfs_tag_reader const &tag) : reader_base(tag.data()) { }
+ ntfs_times_reader(ntfs_tag_reader const &tag) noexcept : reader_base(tag.data()) { }
- std::uint64_t mtime() const { return read_qword(0x00); }
- std::uint64_t atime() const { return read_qword(0x08); }
- std::uint64_t ctime() const { return read_qword(0x10); }
+ std::uint64_t mtime() const noexcept { return read_qword(0x00); }
+ std::uint64_t atime() const noexcept { return read_qword(0x08); }
+ std::uint64_t ctime() const noexcept { return read_qword(0x10); }
- std::size_t total_length() const { return minimum_length(); }
- static std::size_t minimum_length() { return 0x18; }
+ std::size_t total_length() const noexcept { return minimum_length(); }
+ static constexpr std::size_t minimum_length() { return 0x18; }
};
@@ -631,16 +682,16 @@ class general_flag_reader
public:
general_flag_reader(std::uint16_t val) : m_value(val) { }
- bool encrypted() const { return bool(m_value & 0x0001); }
- bool implode_8k_dict() const { return bool(m_value & 0x0002); }
- bool implode_3_trees() const { return bool(m_value & 0x0004); }
- unsigned deflate_option() const { return unsigned((m_value >> 1) & 0x0003); }
- bool lzma_eos_mark() const { return bool(m_value & 0x0002); }
- bool use_descriptor() const { return bool(m_value & 0x0008); }
- bool patch_data() const { return bool(m_value & 0x0020); }
- bool strong_encryption() const { return bool(m_value & 0x0040); }
- bool utf8_encoding() const { return bool(m_value & 0x0800); }
- bool directory_encryption() const { return bool(m_value & 0x2000); }
+ bool encrypted() const noexcept { return bool(m_value & 0x0001); }
+ bool implode_8k_dict() const noexcept { return bool(m_value & 0x0002); }
+ bool implode_3_trees() const noexcept { return bool(m_value & 0x0004); }
+ unsigned deflate_option() const noexcept { return unsigned((m_value >> 1) & 0x0003); }
+ bool lzma_eos_mark() const noexcept { return bool(m_value & 0x0002); }
+ bool use_descriptor() const noexcept { return bool(m_value & 0x0008); }
+ bool patch_data() const noexcept { return bool(m_value & 0x0020); }
+ bool strong_encryption() const noexcept { return bool(m_value & 0x0040); }
+ bool utf8_encoding() const noexcept { return bool(m_value & 0x0800); }
+ bool directory_encryption() const noexcept { return bool(m_value & 0x2000); }
private:
std::uint16_t m_value;
@@ -652,6 +703,8 @@ private:
GLOBAL VARIABLES
***************************************************************************/
+archive_category_impl const f_archive_category_instance;
+
std::array<zip_file_impl::ptr, zip_file_impl::CACHE_SIZE> zip_file_impl::s_cache;
std::mutex zip_file_impl::s_cache_mutex;
@@ -662,33 +715,38 @@ std::mutex zip_file_impl::s_cache_mutex;
to the cache
-------------------------------------------------*/
-void zip_file_impl::close(ptr &&zip)
+void zip_file_impl::close(ptr &&zip) noexcept
{
- if (!zip) return;
+ // if the filename isn't empty, the cached directory can be reused
+ if (zip && !zip->m_filename.empty())
+ {
+ // close the open files
+ osd_printf_verbose("unzip: closing archive file %s and sending to cache\n", zip->m_filename);
+ zip->m_file.reset();
- // close the open files
- osd_printf_verbose("unzip: closing archive file %s and sending to cache\n", zip->m_filename.c_str());
- zip->m_file.reset();
+ // find the first nullptr entry in the cache
+ std::lock_guard<std::mutex> guard(s_cache_mutex);
+ std::size_t cachenum;
+ for (cachenum = 0; cachenum < s_cache.size(); cachenum++)
+ if (!s_cache[cachenum])
+ break;
- // find the first nullptr entry in the cache
- std::lock_guard<std::mutex> guard(s_cache_mutex);
- std::size_t cachenum;
- for (cachenum = 0; cachenum < s_cache.size(); cachenum++)
- if (!s_cache[cachenum])
- break;
+ // if no room left in the cache, free the bottommost entry
+ if (cachenum == s_cache.size())
+ {
+ cachenum--;
+ osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename);
+ s_cache[cachenum].reset();
+ }
- // if no room left in the cache, free the bottommost entry
- if (cachenum == s_cache.size())
- {
- cachenum--;
- osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename.c_str());
- s_cache[cachenum].reset();
+ // move everyone else down and place us at the top
+ for ( ; cachenum > 0; cachenum--)
+ s_cache[cachenum] = std::move(s_cache[cachenum - 1]);
+ s_cache[0] = std::move(zip);
}
- // move everyone else down and place us at the top
- for ( ; cachenum > 0; cachenum--)
- s_cache[cachenum] = std::move(s_cache[cachenum - 1]);
- s_cache[0] = std::move(zip);
+ // make sure it's cleaned up
+ zip.reset();
}
@@ -701,7 +759,7 @@ void zip_file_impl::close(ptr &&zip)
entry in the ZIP
-------------------------------------------------*/
-int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname, bool partialpath)
+int zip_file_impl::search(std::uint32_t search_crc, std::string_view search_filename, bool matchcrc, bool matchname, bool partialpath) noexcept
{
// if we're at or past the end, we're done
while ((m_cd_pos + central_dir_entry_reader::minimum_length()) <= m_ecd.cd_size)
@@ -711,98 +769,122 @@ int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_fi
if (!reader.signature_correct() || ((m_cd_pos + reader.total_length()) > m_ecd.cd_size))
break;
- // extract file header info
- m_header.version_created = reader.version_created();
- m_header.version_needed = reader.version_needed();
- m_header.bit_flag = reader.general_flag();
- m_header.compression = reader.compression_method();
- m_header.crc = reader.crc32();
- m_header.compressed_length = reader.compressed_size();
- m_header.uncompressed_length = reader.uncompressed_size();
- m_header.start_disk_number = reader.start_disk();
- m_header.local_header_offset = reader.header_offset();
-
- // don't immediately decode DOS timestamp - it's expensive
- m_header.modified_date = reader.modified_date();
- m_header.modified_time = reader.modified_time();
- m_header.modified_cached = false;
-
- // advance the position
- m_cd_pos += reader.total_length();
-
- // copy the filename
- bool is_utf8(general_flag_reader(m_header.bit_flag).utf8_encoding());
- reader.file_name(m_header.file_name);
-
- // walk the extra data
- for (auto extra = reader.extra_field(); extra.length_sufficient(); extra = extra.next())
+ // setting std::string can raise allocation exceptions
+ try
{
- // look for ZIP64 extended info
- if ((extra.header_id() == 0x0001) && (extra.data_size() >= zip64_ext_info_reader::minimum_length()))
+ // extract file header info
+ file_header header;
+ header.version_created = reader.version_created();
+ header.version_needed = reader.version_needed();
+ header.bit_flag = reader.general_flag();
+ header.compression = reader.compression_method();
+ header.crc = reader.crc32();
+ header.compressed_length = reader.compressed_size();
+ header.uncompressed_length = reader.uncompressed_size();
+ header.start_disk_number = reader.start_disk();
+ header.local_header_offset = reader.header_offset();
+
+ // don't immediately decode DOS timestamp - it's expensive
+ header.modified_date = reader.modified_date();
+ header.modified_time = reader.modified_time();
+ header.modified = std::nullopt;
+
+ // copy the filename
+ bool is_utf8(general_flag_reader(header.bit_flag).utf8_encoding());
+ reader.file_name(header.file_name);
+
+ // walk the extra data
+ for (auto extra = reader.extra_field(); extra.length_sufficient(); extra = extra.next())
{
- zip64_ext_info_reader const ext64(reader, extra);
- if (extra.data_size() >= ext64.total_length())
+ // look for ZIP64 extended info
+ if ((extra.header_id() == 0x0001) && (extra.data_size() >= zip64_ext_info_reader::minimum_length()))
{
- m_header.compressed_length = ext64.compressed_size();
- m_header.uncompressed_length = ext64.uncompressed_size();
- m_header.start_disk_number = ext64.start_disk();
- m_header.local_header_offset = ext64.header_offset();
+ zip64_ext_info_reader const ext64(reader, extra);
+ if (extra.data_size() >= ext64.total_length())
+ {
+ header.compressed_length = ext64.compressed_size();
+ header.uncompressed_length = ext64.uncompressed_size();
+ header.start_disk_number = ext64.start_disk();
+ header.local_header_offset = ext64.header_offset();
+ }
}
- }
- // look for Info-ZIP UTF-8 path
- if (!is_utf8 && (extra.header_id() == 0x7075) && (extra.data_size() >= utf8_path_reader::minimum_length()))
- {
- utf8_path_reader const utf8path(extra);
- if (utf8path.version() == 1)
+ // look for Info-ZIP UTF-8 path
+ if (!is_utf8 && (extra.header_id() == 0x7075) && (extra.data_size() >= utf8_path_reader::minimum_length()))
{
- auto const addr(m_header.file_name.empty() ? nullptr : &m_header.file_name[0]);
- auto const length(m_header.file_name.empty() ? 0 : m_header.file_name.length() * sizeof(m_header.file_name[0]));
- auto const crc(crc32_creator::simple(addr, length));
- if (utf8path.name_crc32() == crc.m_raw)
+ utf8_path_reader const utf8path(extra);
+ if (utf8path.version() == 1)
{
- utf8path.unicode_name(m_header.file_name);
- is_utf8 = true;
+ auto const addr(header.file_name.empty() ? nullptr : &header.file_name[0]);
+ auto const length(header.file_name.empty() ? 0 : header.file_name.length() * sizeof(header.file_name[0]));
+ auto const crc(crc32_creator::simple(addr, length));
+ if (utf8path.name_crc32() == crc.m_raw)
+ {
+ utf8path.unicode_name(header.file_name);
+ is_utf8 = true;
+ }
}
}
- }
- // look for NTFS extra field
- if ((extra.header_id() == 0x000a) && (extra.data_size() >= ntfs_reader::minimum_length()))
- {
- ntfs_reader const ntfs(extra);
- for (auto tag = ntfs.tag1(); tag.length_sufficient(); tag = tag.next())
+ // look for NTFS extra field
+ if ((extra.header_id() == 0x000a) && (extra.data_size() >= ntfs_reader::minimum_length()))
{
- if ((tag.tag() == 0x0001) && (tag.size() >= ntfs_times_reader::minimum_length()))
+ ntfs_reader const ntfs(extra);
+ for (auto tag = ntfs.tag1(); tag.length_sufficient(); tag = tag.next())
{
- ntfs_times_reader const times(tag);
- ntfs_duration const ticks(times.mtime());
- m_header.modified = system_clock_time_point_from_ntfs_duration(ticks);
- m_header.modified_cached = true;
+ if ((tag.tag() == 0x0001) && (tag.size() >= ntfs_times_reader::minimum_length()))
+ {
+ ntfs_times_reader const times(tag);
+ ntfs_duration const ticks(times.mtime());
+ try
+ {
+ header.modified = system_clock_time_point_from_ntfs_duration(ticks);
+ }
+ catch (...)
+ {
+ // out-of-range exception - let it fall back to DOS-style timestamp
+ }
+ }
}
}
}
- }
- // FIXME: if (!is_utf8) convert filename to UTF8 (assume CP437 or something)
+ // FIXME: if (!is_utf8) convert filename to UTF8 (assume CP437 or something)
- // chop off trailing slash for directory entries
- bool const is_dir(!m_header.file_name.empty() && (m_header.file_name.back() == '/'));
- if (is_dir) m_header.file_name.resize(m_header.file_name.length() - 1);
+ // chop off trailing slash for directory entries
+ bool const is_dir(!header.file_name.empty() && (header.file_name.back() == '/'));
+ if (is_dir)
+ header.file_name.resize(header.file_name.length() - 1);
- // check to see if it matches query
- bool const crcmatch(search_crc == m_header.crc);
- auto const partialoffset(m_header.file_name.length() - search_filename.length());
- bool const partialpossible((m_header.file_name.length() > search_filename.length()) && (m_header.file_name[partialoffset - 1] == '/'));
- const bool namematch(
- !core_stricmp(search_filename.c_str(), m_header.file_name.c_str()) ||
- (partialpath && partialpossible && !core_stricmp(search_filename.c_str(), m_header.file_name.c_str() + partialoffset)));
+ // advance the position
+ m_header = std::move(header);
+ m_curr_is_dir = is_dir;
+ m_cd_pos += reader.total_length();
+ }
+ catch (...)
+ {
+ break;
+ }
- bool const found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch);
- if (found)
+ // quick return if not required to match on name
+ if (!matchname)
{
- m_curr_is_dir = is_dir;
- return 0;
+ if (!matchcrc || ((search_crc == m_header.crc) && !m_curr_is_dir))
+ return 0;
+ }
+ else if (!m_curr_is_dir)
+ {
+ // check to see if it matches query
+ auto const partialoffset = m_header.file_name.length() - search_filename.length();
+ const bool namematch =
+ (search_filename.length() == m_header.file_name.length()) &&
+ (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_header.file_name[0], search_filename.length()));
+ bool const partialmatch =
+ partialpath &&
+ ((m_header.file_name.length() > search_filename.length()) && (m_header.file_name[partialoffset - 1] == '/')) &&
+ (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_header.file_name[partialoffset], search_filename.length()));
+ if ((!matchcrc || (search_crc == m_header.crc)) && (namematch || partialmatch))
+ return 0;
}
}
return -1;
@@ -814,53 +896,49 @@ int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_fi
from a ZIP into the target buffer
-------------------------------------------------*/
-archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length)
+std::error_condition zip_file_impl::decompress(void *buffer, std::size_t length) noexcept
{
- archive_file::error ziperr;
- std::uint64_t offset;
-
// if we don't have enough buffer, error
if (length < m_header.uncompressed_length)
{
- osd_printf_error("unzip: buffer too small to decompress %s from %s\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error("unzip: buffer too small to decompress %s from %s\n", m_header.file_name, m_filename);
return archive_file::error::BUFFER_TOO_SMALL;
}
// make sure the info in the header aligns with what we know
if (m_header.start_disk_number != m_ecd.disk_number)
{
- osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name, m_filename);
return archive_file::error::UNSUPPORTED;
}
// get the compressed data offset
- ziperr = get_compressed_data_offset(offset);
- if (ziperr != archive_file::error::NONE)
+ std::uint64_t offset = 0;
+ auto const ziperr = get_compressed_data_offset(offset);
+ if (ziperr)
return ziperr;
// handle compression types
switch (m_header.compression)
{
case 0:
- ziperr = decompress_data_type_0(offset, buffer, length);
- break;
+ return decompress_data_type_0(offset, buffer, length);
case 8:
- ziperr = decompress_data_type_8(offset, buffer, length);
- break;
+ return decompress_data_type_8(offset, buffer, length);
case 14:
- ziperr = decompress_data_type_14(offset, buffer, length);
- break;
+ return decompress_data_type_14(offset, buffer, length);
+
+ case 93:
+ return decompress_data_type_93(offset, buffer, length);
default:
osd_printf_error(
"unzip: %s in %s uses unsupported compression method %u\n",
- m_header.file_name.c_str(), m_filename.c_str(), m_header.compression);
- ziperr = archive_file::error::UNSUPPORTED;
- break;
+ m_header.file_name, m_filename, m_header.compression);
+ return archive_file::error::UNSUPPORTED;
}
- return ziperr;
}
@@ -873,16 +951,16 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
read_ecd - read the ECD data
-------------------------------------------------*/
-archive_file::error zip_file_impl::read_ecd()
+std::error_condition zip_file_impl::read_ecd() noexcept
{
// make sure the file handle is open
auto const ziperr = reopen();
- if (ziperr != archive_file::error::NONE)
+ if (ziperr)
return ziperr;
// we may need multiple tries
- osd_file::error error;
- std::uint32_t read_length;
+ std::error_condition filerr;
+ std::size_t read_length;
std::uint32_t buflen = 1024;
while (buflen < 65536)
{
@@ -892,19 +970,26 @@ archive_file::error zip_file_impl::read_ecd()
// allocate buffer
std::unique_ptr<std::uint8_t []> buffer;
- try { buffer.reset(new std::uint8_t[buflen + 1]); }
- catch (...)
+ buffer.reset(new (std::nothrow) std::uint8_t[buflen + 1]);
+ if (!buffer)
{
- osd_printf_error("unzip: %s failed to allocate memory for ECD search\n", m_filename.c_str());
- return archive_file::error::OUT_OF_MEMORY;
+ osd_printf_error("unzip: %s failed to allocate memory for ECD search\n", m_filename);
+ return std::errc::not_enough_memory;
}
// read in one buffers' worth of data
- error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length);
- if ((error != osd_file::error::NONE) || (read_length != buflen))
+ std::tie(filerr, read_length) = read_at(*m_file, m_length - buflen, &buffer[0], buflen);
+ if (filerr)
+ {
+ osd_printf_error(
+ "unzip: error reading %s to search for ECD (%s:%d %s)\n",
+ m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
+ }
+ if (read_length != buflen)
{
- osd_printf_error("unzip: %s error reading file to search for ECD (%d)\n", m_filename.c_str(), int(error));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s to search for ECD\n", m_filename);
+ return archive_file::error::FILE_TRUNCATED;
}
// find the ECD signature
@@ -919,7 +1004,7 @@ archive_file::error zip_file_impl::read_ecd()
// if we found it, fill out the data
if (offset >= 0)
{
- osd_printf_verbose("unzip: found %s ECD\n", m_filename.c_str());
+ osd_printf_verbose("unzip: found %s ECD at %d\n", m_filename, offset);
// extract ECD info
ecd_reader const ecd_rd(buffer.get() + offset);
@@ -933,47 +1018,56 @@ archive_file::error zip_file_impl::read_ecd()
// is the file too small to contain a ZIP64 ECD locator?
if ((m_length - buflen + offset) < ecd64_locator_reader::minimum_length())
{
- osd_printf_verbose("unzip: %s too small to contain ZIP64 ECD locator\n", m_filename.c_str());
- return archive_file::error::NONE;
+ osd_printf_verbose("unzip: %s too small to contain ZIP64 ECD locator\n", m_filename);
+ return std::error_condition();
}
// try to read the ZIP64 ECD locator
- error = m_file->read(
- &buffer[0],
+ std::tie(filerr, read_length) = read_at(
+ *m_file,
m_length - buflen + offset - ecd64_locator_reader::minimum_length(),
- ecd64_locator_reader::minimum_length(),
- read_length);
- if ((error != osd_file::error::NONE) || (read_length != ecd64_locator_reader::minimum_length()))
+ &buffer[0],
+ ecd64_locator_reader::minimum_length());
+ if (filerr)
+ {
+ osd_printf_error(
+ "unzip: error reading %s to search for ZIP64 ECD locator (%s:%d %s)\n",
+ m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
+ }
+ if (read_length != ecd64_locator_reader::minimum_length())
{
- osd_printf_error("unzip: error reading %s to search for ZIP64 ECD locator (%d)\n", m_filename.c_str(), int(error));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s to search for ZIP64 ECD locator\n", m_filename);
+ return archive_file::error::FILE_TRUNCATED;
}
// if the signature isn't correct, it's not a ZIP64 archive
ecd64_locator_reader const ecd64_loc_rd(buffer.get());
if (!ecd64_loc_rd.signature_correct())
{
- osd_printf_verbose("unzip: %s has no ZIP64 ECD locator\n", m_filename.c_str());
- return archive_file::error::NONE;
+ osd_printf_verbose("unzip: %s has no ZIP64 ECD locator\n", m_filename);
+ return std::error_condition();
}
// check that the ZIP64 ECD is in this segment (assuming this segment is the last segment)
if ((ecd64_loc_rd.ecd64_disk() + 1) != ecd64_loc_rd.total_disks())
{
- osd_printf_error("unzip: %s ZIP64 ECD resides in a different segment\n", m_filename.c_str());
+ osd_printf_error("unzip: %s ZIP64 ECD resides in a different segment\n", m_filename);
return archive_file::error::UNSUPPORTED;
}
// try to read the ZIP64 ECD
- error = m_file->read(&buffer[0], ecd64_loc_rd.ecd64_offset(), ecd64_reader::minimum_length(), read_length);
- if (error != osd_file::error::NONE)
+ std::tie(filerr, read_length) = read_at(*m_file, ecd64_loc_rd.ecd64_offset(), &buffer[0], ecd64_reader::minimum_length());
+ if (filerr)
{
- osd_printf_error("unzip: error reading %s ZIP64 ECD (%d)\n", m_filename.c_str(), int(error));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error(
+ "unzip: error reading %s ZIP64 ECD (%s:%d %s)\n",
+ m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
if (read_length != ecd64_reader::minimum_length())
{
- osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s ZIP64 ECD\n", m_filename.c_str());
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s ZIP64 ECD\n", m_filename);
return archive_file::error::FILE_TRUNCATED;
}
@@ -982,23 +1076,23 @@ archive_file::error zip_file_impl::read_ecd()
if (!ecd64_rd.signature_correct())
{
osd_printf_error(
- "unzip: %s ZIP64 ECD has incorrect signature 0x%08lx\n",
- m_filename.c_str(), long(ecd64_rd.signature()));
+ "unzip: %s ZIP64 ECD has incorrect signature 0x%08x\n",
+ m_filename, ecd64_rd.signature());
return archive_file::error::BAD_SIGNATURE;
}
if (ecd64_rd.total_length() < ecd64_reader::minimum_length())
{
- osd_printf_error("unzip: %s ZIP64 ECD appears to be too small\n", m_filename.c_str());
+ osd_printf_error("unzip: %s ZIP64 ECD appears to be too small\n", m_filename);
return archive_file::error::UNSUPPORTED;
}
if (ecd64_rd.version_needed() > 63)
{
osd_printf_error(
"unzip: %s ZIP64 ECD requires unsupported version %u.%u\n",
- m_filename.c_str(), unsigned(ecd64_rd.version_needed() / 10), unsigned(ecd64_rd.version_needed() % 10));
+ m_filename, ecd64_rd.version_needed() / 10, ecd64_rd.version_needed() % 10);
return archive_file::error::UNSUPPORTED;
}
- osd_printf_verbose("unzip: found %s ZIP64 ECD\n", m_filename.c_str());
+ osd_printf_verbose("unzip: found %s ZIP64 ECD\n", m_filename);
// extract ZIP64 ECD info
m_ecd.disk_number = ecd64_rd.this_disk_no();
@@ -1008,7 +1102,7 @@ archive_file::error zip_file_impl::read_ecd()
m_ecd.cd_size = ecd64_rd.dir_size();
m_ecd.cd_start_disk_offset = ecd64_rd.dir_offset();
- return archive_file::error::NONE;
+ return std::error_condition();
}
// didn't find it; free this buffer and expand our search
@@ -1018,12 +1112,12 @@ archive_file::error zip_file_impl::read_ecd()
}
else
{
- osd_printf_error("unzip: %s couldn't find ECD\n", m_filename.c_str());
+ osd_printf_error("unzip: %s couldn't find ECD\n", m_filename);
return archive_file::error::BAD_SIGNATURE;
}
}
- osd_printf_error("unzip: %s couldn't find ECD in last 64KiB\n", m_filename.c_str());
- return archive_file::error::OUT_OF_MEMORY;
+ osd_printf_error("unzip: %s couldn't find ECD in last 64KiB\n", m_filename);
+ return archive_file::error::UNSUPPORTED; // TODO: revisit this error code
}
@@ -1032,53 +1126,52 @@ archive_file::error zip_file_impl::read_ecd()
offset of the compressed data
-------------------------------------------------*/
-archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &offset)
+std::error_condition zip_file_impl::get_compressed_data_offset(std::uint64_t &offset) noexcept
{
// don't support a number of features
general_flag_reader const flags(m_header.bit_flag);
if (m_header.start_disk_number != m_ecd.disk_number)
{
- osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name, m_filename);
return archive_file::error::UNSUPPORTED;
}
if (m_header.version_needed > 63)
{
osd_printf_error(
"unzip: %s in %s requires unsupported version %u.%u\n",
- m_header.file_name.c_str(), m_filename.c_str(), unsigned(m_header.version_needed / 10), unsigned(m_header.version_needed % 10));
+ m_header.file_name, m_filename, m_header.version_needed / 10, m_header.version_needed % 10);
return archive_file::error::UNSUPPORTED;
}
if (flags.encrypted() || flags.strong_encryption())
{
- osd_printf_error("unzip: %s in %s is encrypted\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error("unzip: %s in %s is encrypted\n", m_header.file_name, m_filename);
return archive_file::error::UNSUPPORTED;
}
if (flags.patch_data())
{
- osd_printf_error("unzip: %s in %s is compressed patch data\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error("unzip: %s in %s is compressed patch data\n", m_header.file_name, m_filename);
return archive_file::error::UNSUPPORTED;
}
// make sure the file handle is open
auto const ziperr = reopen();
- if (ziperr != archive_file::error::NONE)
+ if (ziperr)
return ziperr;
// now go read the fixed-sized part of the local file header
- std::uint32_t read_length;
- auto const error = m_file->read(&m_buffer[0], m_header.local_header_offset, local_file_header_reader::minimum_length(), read_length);
- if (error != osd_file::error::NONE)
+ auto const [filerr, read_length] = read_at(*m_file, m_header.local_header_offset, &m_buffer[0], local_file_header_reader::minimum_length());
+ if (filerr)
{
osd_printf_error(
- "unzip: error reading local file header for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(error));
- return archive_file::error::FILE_ERROR;
+ "unzip: error reading local file header for %s in %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
if (read_length != local_file_header_reader::minimum_length())
{
osd_printf_error(
"unzip: unexpectedly reached end-of-file while reading local file header for %s in %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::FILE_TRUNCATED;
}
@@ -1087,13 +1180,13 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
if (!reader.signature_correct())
{
osd_printf_error(
- "unzip: local file header for %s in %s has incorrect signature %04lx\n",
- m_header.file_name.c_str(), m_filename.c_str(), long(reader.signature()));
+ "unzip: local file header for %s in %s has incorrect signature %04x\n",
+ m_header.file_name, m_filename, reader.signature());
return archive_file::error::BAD_SIGNATURE;
}
offset = m_header.local_header_offset + reader.total_length();
- return archive_file::error::NONE;
+ return std::error_condition();
}
@@ -1107,24 +1200,27 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
type 0 data (which is uncompressed)
-------------------------------------------------*/
-archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length)
+std::error_condition zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::size_t length) noexcept
{
// the data is uncompressed; just read it
- std::uint32_t read_length(0);
- auto const filerr = m_file->read(buffer, offset, m_header.compressed_length, read_length);
- if (filerr != osd_file::error::NONE)
+ auto const [filerr, read_length] = read_at(*m_file, offset, buffer, m_header.compressed_length);
+ if (filerr)
{
- osd_printf_error("unzip: error reading %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
- return archive_file::error::FILE_ERROR;
+ osd_printf_error(
+ "unzip: error reading %s from %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
else if (read_length != m_header.compressed_length)
{
- osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s from %s\n", m_header.file_name.c_str(), m_filename.c_str());
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading %s from %s\n",
+ m_header.file_name, m_filename);
return archive_file::error::FILE_TRUNCATED;
}
else
{
- return archive_file::error::NONE;
+ return std::error_condition();
}
}
@@ -1134,9 +1230,24 @@ archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset,
type 8 data (which is deflated)
-------------------------------------------------*/
-archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length)
+std::error_condition zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::size_t length) noexcept
{
- std::uint64_t input_remaining = m_header.compressed_length;
+ auto const convert_zerr =
+ [] (int e) -> std::error_condition
+ {
+ switch (e)
+ {
+ case Z_OK:
+ return std::error_condition();
+ case Z_ERRNO:
+ return std::error_condition(errno, std::generic_category());
+ case Z_MEM_ERROR:
+ return std::errc::not_enough_memory;
+ default:
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ };
+ std::uint64_t input_remaining(m_header.compressed_length);
int zerr;
// reset the stream
@@ -1152,38 +1263,38 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
zerr = inflateInit2(&stream, -MAX_WBITS);
if (zerr != Z_OK)
{
+ auto result = convert_zerr(zerr);
osd_printf_error(
"unzip: error allocating zlib stream to inflate %s from %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), zerr);
- return archive_file::error::DECOMPRESS_ERROR;
+ m_header.file_name, m_filename, zerr);
+ return result;
}
// loop until we're done
- while (1)
+ while (true)
{
// read in the next chunk of data
- std::uint32_t read_length(0);
- auto const filerr = m_file->read(
- &m_buffer[0],
+ auto const [filerr, read_length] = read_at(
+ *m_file,
offset,
- std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())),
- read_length);
- if (filerr != osd_file::error::NONE)
+ &m_buffer[0],
+ std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())));
+ if (filerr)
{
osd_printf_error(
- "unzip: error reading compressed data for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
+ "unzip: error reading compressed data for %s in %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
inflateEnd(&stream);
- return archive_file::error::FILE_ERROR;
+ return filerr;
}
offset += read_length;
// if we read nothing, but still have data left, the file is truncated
- if ((read_length == 0) && (input_remaining > 0))
+ if (!read_length && input_remaining)
{
osd_printf_error(
"unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
inflateEnd(&stream);
return archive_file::error::FILE_TRUNCATED;
}
@@ -1205,9 +1316,12 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
}
else if (zerr != Z_OK)
{
- osd_printf_error("unzip: error inflating %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
+ auto result = convert_zerr(zerr);
+ osd_printf_error(
+ "unzip: error inflating %s from %s (%d)\n",
+ m_header.file_name, m_filename, zerr);
inflateEnd(&stream);
- return archive_file::error::DECOMPRESS_ERROR;
+ return result;
}
}
@@ -1215,20 +1329,23 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
zerr = inflateEnd(&stream);
if (zerr != Z_OK)
{
- osd_printf_error("unzip: error finishing inflation of %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
- return archive_file::error::DECOMPRESS_ERROR;
+ auto result = convert_zerr(zerr);
+ osd_printf_error(
+ "unzip: error finishing inflation of %s from %s (%d)\n",
+ m_header.file_name, m_filename, zerr);
+ return result;
}
// if anything looks funny, report an error
- if ((stream.avail_out > 0) || (input_remaining > 0))
+ if (stream.avail_out || input_remaining)
{
osd_printf_error(
"unzip: inflation of %s from %s doesn't appear to have completed correctly\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::DECOMPRESS_ERROR;
}
- return archive_file::error::NONE;
+ return std::error_condition();
}
@@ -1237,7 +1354,7 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
type 14 data (LZMA)
-------------------------------------------------*/
-archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, void *buffer, std::uint32_t length)
+std::error_condition zip_file_impl::decompress_data_type_14(std::uint64_t offset, void *buffer, std::size_t length) noexcept
{
// two-byte version
// two-byte properties size (little-endian)
@@ -1252,15 +1369,15 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
SizeT output_remaining(length);
std::uint64_t input_remaining(m_header.compressed_length);
- std::uint32_t read_length;
- osd_file::error filerr;
+ std::size_t read_length;
+ std::error_condition filerr;
SRes lzerr;
ELzmaStatus lzstatus(LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK);
// reset the stream
ISzAlloc alloc_imp;
- alloc_imp.Alloc = [](void *p, std::size_t size) -> void * { return (size == 0) ? nullptr : std::malloc(size); };
- alloc_imp.Free = [](void *p, void *address) -> void { std::free(address); };
+ alloc_imp.Alloc = [] (ISzAllocPtr p, std::size_t size) -> void * { return size ? std::malloc(size) : nullptr; };
+ alloc_imp.Free = [] (ISzAllocPtr p, void *address) -> void { std::free(address); };
CLzmaDec stream;
LzmaDec_Construct(&stream);
@@ -1269,16 +1386,16 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
{
osd_printf_error(
"unzip:compressed data for %s in %s is too small to hold LZMA properties header\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::DECOMPRESS_ERROR;
}
- filerr = m_file->read(&m_buffer[0], offset, 4, read_length);
- if (filerr != osd_file::error::NONE)
+ std::tie(filerr, read_length) = read_at(*m_file, offset, &m_buffer[0], 4);
+ if (filerr)
{
osd_printf_error(
- "unzip: error reading LZMA properties header for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
- return archive_file::error::FILE_ERROR;
+ "unzip: error reading LZMA properties header for %s in %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
offset += read_length;
input_remaining -= read_length;
@@ -1286,31 +1403,31 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
{
osd_printf_error(
"unzip: unexpectedly reached end-of-file while reading LZMA properties header for %s in %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::FILE_TRUNCATED;
}
- std::uint16_t const props_size((std::uint16_t(m_buffer[3]) << 8) | std::uint16_t(m_buffer[2]));
+ std::uint16_t const props_size(get_u16le(&m_buffer[2]));
if (props_size > m_buffer.size())
{
osd_printf_error(
"unzip: %s in %s has excessively large LZMA properties\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::UNSUPPORTED;
}
else if (props_size > input_remaining)
{
osd_printf_error(
"unzip:compressed data for %s in %s is too small to hold LZMA properties\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::DECOMPRESS_ERROR;
}
- filerr = m_file->read(&m_buffer[0], offset, props_size, read_length);
- if (filerr != osd_file::error::NONE)
+ std::tie(filerr, read_length) = read_at(*m_file, offset, &m_buffer[0], props_size);
+ if (filerr)
{
osd_printf_error(
- "unzip: error reading LZMA properties for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
- return archive_file::error::FILE_ERROR;
+ "unzip: error reading LZMA properties for %s in %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ return filerr;
}
offset += read_length;
input_remaining -= read_length;
@@ -1318,7 +1435,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
{
osd_printf_error(
"unzip: unexpectedly reached end-of-file while reading LZMA properties for %s in %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
return archive_file::error::FILE_TRUNCATED;
}
@@ -1328,14 +1445,21 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
{
osd_printf_error(
"unzip: memory error allocating LZMA decoder to decompress %s from %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
- return archive_file::error::OUT_OF_MEMORY;
+ m_header.file_name, m_filename);
+ return std::errc::not_enough_memory;
+ }
+ else if (SZ_ERROR_UNSUPPORTED)
+ {
+ osd_printf_error(
+ "unzip: LZMA decoder does not support properties for %s in %s\n",
+ m_header.file_name, m_filename);
+ return archive_file::error::UNSUPPORTED;
}
else if (SZ_OK != lzerr)
{
osd_printf_error(
"unzip: error allocating LZMA decoder to decompress %s from %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(lzerr));
+ m_header.file_name, m_filename, int(lzerr));
return archive_file::error::DECOMPRESS_ERROR;
}
LzmaDec_Init(&stream);
@@ -1344,28 +1468,28 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
while (0 < input_remaining)
{
// read in the next chunk of data
- filerr = m_file->read(
- &m_buffer[0],
+ std::tie(filerr, read_length) = read_at(
+ *m_file,
offset,
- std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())),
- read_length);
- if (filerr != osd_file::error::NONE)
+ &m_buffer[0],
+ std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())));
+ if (filerr)
{
osd_printf_error(
- "unzip: error reading compressed data for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
+ "unzip: error reading compressed data for %s in %s (%s)\n",
+ m_header.file_name, m_filename);
LzmaDec_Free(&stream, &alloc_imp);
- return archive_file::error::FILE_ERROR;
+ return filerr;
}
offset += read_length;
input_remaining -= read_length;
// if we read nothing, but still have data left, the file is truncated
- if ((0 == read_length) && (0 < input_remaining))
+ if (!read_length && input_remaining)
{
osd_printf_error(
"unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n",
- m_header.file_name.c_str(), m_filename.c_str());
+ m_header.file_name, m_filename);
LzmaDec_Free(&stream, &alloc_imp);
return archive_file::error::FILE_TRUNCATED;
}
@@ -1380,11 +1504,11 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
&output_remaining,
reinterpret_cast<Byte const *>(&m_buffer[0]),
&len,
- ((0 == input_remaining) && eos_mark) ? LZMA_FINISH_END : LZMA_FINISH_ANY,
+ (!input_remaining && eos_mark) ? LZMA_FINISH_END : LZMA_FINISH_ANY,
&lzstatus);
if (SZ_OK != lzerr)
{
- osd_printf_error("unzip: error decoding LZMA data for %s in %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(lzerr));
+ osd_printf_error("unzip: error decoding LZMA data for %s in %s (%d)\n", m_header.file_name, m_filename, int(lzerr));
LzmaDec_Free(&stream, &alloc_imp);
return archive_file::error::DECOMPRESS_ERROR;
}
@@ -1396,26 +1520,111 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
// if anything looks funny, report an error
if (LZMA_STATUS_FINISHED_WITH_MARK == lzstatus)
{
- return archive_file::error::NONE;
+ return std::error_condition();
}
else if (eos_mark)
{
osd_printf_error(
"unzip: LZMA end mark not found for %s in %s (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus));
+ m_header.file_name, m_filename, int(lzstatus));
return archive_file::error::DECOMPRESS_ERROR;
}
else if (LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK != lzstatus)
{
osd_printf_error(
"unzip: LZMA decompression of %s from %s doesn't appear to have completed correctly (%d)\n",
- m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus));
+ m_header.file_name, m_filename, int(lzstatus));
return archive_file::error::DECOMPRESS_ERROR;
}
else
{
- return archive_file::error::NONE;
+ return std::error_condition();
+ }
+}
+
+
+/*-------------------------------------------------
+ decompress_data_type_93 - decompress
+ type 14 data (Zstandard)
+-------------------------------------------------*/
+
+std::error_condition zip_file_impl::decompress_data_type_93(std::uint64_t offset, void *buffer, std::size_t length) noexcept
+{
+ // create decompression stream
+ ZSTD_DStream *const stream(ZSTD_createDStream());
+ if (!stream)
+ {
+ osd_printf_error(
+ "unzip: error allocating Zstandard stream to decompress %s from %s\n",
+ m_header.file_name, m_filename);
+ return std::errc::not_enough_memory;
+ }
+
+ // loop until we're done
+ std::uint64_t input_remaining(m_header.compressed_length);
+ while (input_remaining && length)
+ {
+ // read in the next chunk of data
+ auto const [filerr, read_length] = read_at(
+ *m_file,
+ offset,
+ &m_buffer[0],
+ std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())));
+ if (filerr)
+ {
+ osd_printf_error(
+ "unzip: error reading compressed data for %s in %s (%s:%d %s)\n",
+ m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message());
+ ZSTD_freeDStream(stream);
+ return filerr;
+ }
+ offset += read_length;
+
+ // if we read nothing, but still have data left, the file is truncated
+ if (!read_length && input_remaining)
+ {
+ osd_printf_error(
+ "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n",
+ m_header.file_name, m_filename);
+ ZSTD_freeDStream(stream);
+ return archive_file::error::FILE_TRUNCATED;
+ }
+
+ // fill out the input data
+ ZSTD_inBuffer input{ &m_buffer[0], read_length, 0 };
+ input_remaining -= read_length;
+
+ // now decompress
+ while ((input.pos < input.size) && length)
+ {
+ ZSTD_outBuffer output{ buffer, length, 0 };
+ auto const result(ZSTD_decompressStream(stream, &output, &input));
+ if (ZSTD_isError(result))
+ {
+ osd_printf_error(
+ "unzip: error decompressing %s from %s (%u: %s)\n",
+ m_header.file_name, m_filename, result, ZSTD_getErrorName(result));
+ ZSTD_freeDStream(stream);
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ buffer = reinterpret_cast<std::uint8_t *>(buffer) + output.pos;
+ length -= output.pos;
+ }
+ }
+
+ // free stream
+ ZSTD_freeDStream(stream);
+
+ // if anything looks funny, report an error
+ if (length || input_remaining)
+ {
+ osd_printf_error(
+ "unzip: decompression of %s from %s doesn't appear to have completed correctly\n",
+ m_header.file_name, m_filename);
+ return archive_file::error::DECOMPRESS_ERROR;
}
+
+ return std::error_condition();
}
} // anonymous namespace
@@ -1426,7 +1635,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset,
un7z.cpp TRAMPOLINES
***************************************************************************/
-void m7z_file_cache_clear();
+void m7z_file_cache_clear() noexcept;
@@ -1438,7 +1647,7 @@ void m7z_file_cache_clear();
zip_file_open - opens a ZIP file for reading
-------------------------------------------------*/
-archive_file::error archive_file::open_zip(const std::string &filename, ptr &result)
+std::error_condition archive_file::open_zip(std::string_view filename, ptr &result) noexcept
{
// ensure we start with a nullptr result
result.reset();
@@ -1449,21 +1658,49 @@ archive_file::error archive_file::open_zip(const std::string &filename, ptr &res
if (!newimpl)
{
// allocate memory for the zip_file structure
- try { newimpl = std::make_unique<zip_file_impl>(filename); }
- catch (...) { return error::OUT_OF_MEMORY; }
- auto const ziperr = newimpl->initialize();
- if (ziperr != error::NONE) return ziperr;
+ try { newimpl = std::make_unique<zip_file_impl>(std::string(filename)); }
+ catch (...) { return std::errc::not_enough_memory; }
+ auto const err = newimpl->initialize();
+ if (err)
+ return err;
+ }
+
+ // allocate the archive API wrapper
+ result.reset(new (std::nothrow) zip_file_wrapper(std::move(newimpl)));
+ if (result)
+ {
+ return std::error_condition();
+ }
+ else
+ {
+ zip_file_impl::close(std::move(newimpl));
+ return std::errc::not_enough_memory;
}
+}
+
+std::error_condition archive_file::open_zip(random_read::ptr &&file, ptr &result) noexcept
+{
+ // ensure we start with a nullptr result
+ result.reset();
- try
+ // allocate memory for the zip_file structure
+ zip_file_impl::ptr newimpl(new (std::nothrow) zip_file_impl(std::move(file)));
+ if (!newimpl)
+ return std::errc::not_enough_memory;
+ auto const err = newimpl->initialize();
+ if (err)
+ return err;
+
+ // allocate the archive API wrapper
+ result.reset(new (std::nothrow) zip_file_wrapper(std::move(newimpl)));
+ if (result)
{
- result = std::make_unique<zip_file_wrapper>(std::move(newimpl));
- return error::NONE;
+ return std::error_condition();
}
- catch (...)
+ else
{
zip_file_impl::close(std::move(newimpl));
- return error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
}
@@ -1473,7 +1710,7 @@ archive_file::error archive_file::open_zip(const std::string &filename, ptr &res
cache and free all memory
-------------------------------------------------*/
-void archive_file::cache_clear()
+void archive_file::cache_clear() noexcept
{
zip_file_impl::cache_clear();
m7z_file_cache_clear();
@@ -1484,4 +1721,15 @@ archive_file::~archive_file()
{
}
+
+/*-------------------------------------------------
+ archive_category - gets the archive error
+ category instance
+-------------------------------------------------*/
+
+std::error_category const &archive_category() noexcept
+{
+ return f_archive_category_instance;
+}
+
} // namespace util
diff --git a/src/lib/util/unzip.h b/src/lib/util/unzip.h
index a1388cd0b4c..2108eadee28 100644
--- a/src/lib/util/unzip.h
+++ b/src/lib/util/unzip.h
@@ -7,21 +7,24 @@
archive file management.
***************************************************************************/
-
-#pragma once
-
#ifndef MAME_LIB_UTIL_UNZIP_H
#define MAME_LIB_UTIL_UNZIP_H
-#include "osdcore.h"
+#pragma once
+
+#include "utilfwd.h"
#include <chrono>
#include <cstdint>
#include <memory>
#include <string>
+#include <string_view>
+#include <system_error>
+#include <type_traits>
namespace util {
+
/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
@@ -32,12 +35,9 @@ class archive_file
public:
// Error types
- enum class error
+ enum class error : int
{
- NONE = 0,
- OUT_OF_MEMORY,
- FILE_ERROR,
- BAD_SIGNATURE,
+ BAD_SIGNATURE = 1,
DECOMPRESS_ERROR,
FILE_TRUNCATED,
FILE_CORRUPT,
@@ -51,40 +51,54 @@ public:
/* ----- archive file access ----- */
// open a ZIP file and parse its central directory
- static error open_zip(const std::string &filename, ptr &zip);
+ static std::error_condition open_zip(std::string_view filename, ptr &result) noexcept;
+ static std::error_condition open_zip(std::unique_ptr<random_read> &&file, ptr &result) noexcept;
// open a 7Z file and parse its central directory
- static error open_7z(const std::string &filename, ptr &result);
+ static std::error_condition open_7z(std::string_view filename, ptr &result) noexcept;
+ static std::error_condition open_7z(std::unique_ptr<random_read> &&file, ptr &result) noexcept;
// close an archive file (may actually be left open due to caching)
virtual ~archive_file();
// clear out all open files from the cache
- static void cache_clear();
+ static void cache_clear() noexcept;
/* ----- contained file access ----- */
// iterating over files - returns negative on reaching end
- virtual int first_file() = 0;
- virtual int next_file() = 0;
+ virtual int first_file() noexcept = 0;
+ virtual int next_file() noexcept = 0;
// find a file index by crc, filename or both - returns non-negative on match
- virtual int search(std::uint32_t crc) = 0;
- virtual int search(const std::string &filename, bool partialpath) = 0;
- virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) = 0;
+ virtual int search(std::uint32_t crc) noexcept = 0;
+ virtual int search(std::string_view filename, bool partialpath) noexcept = 0;
+ virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept = 0;
// information on most recently found file
- virtual bool current_is_directory() const = 0;
- virtual const std::string &current_name() const = 0;
- virtual std::uint64_t current_uncompressed_length() const = 0;
- virtual std::chrono::system_clock::time_point current_last_modified() const = 0;
- virtual std::uint32_t current_crc() const = 0;
+ virtual bool current_is_directory() const noexcept = 0;
+ virtual const std::string &current_name() const noexcept = 0;
+ virtual std::uint64_t current_uncompressed_length() const noexcept = 0;
+ virtual std::chrono::system_clock::time_point current_last_modified() const noexcept = 0;
+ virtual std::uint32_t current_crc() const noexcept = 0;
// decompress the most recently found file in the ZIP
- virtual error decompress(void *buffer, std::uint32_t length) = 0;
+ virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept = 0;
};
+
+// error category for archive errors
+std::error_category const &archive_category() noexcept;
+inline std::error_condition make_error_condition(archive_file::error err) noexcept { return std::error_condition(int(err), archive_category()); }
+
} // namespace util
+
+namespace std {
+
+template <> struct is_error_condition_enum<util::archive_file::error> : public std::true_type { };
+
+} // namespace std
+
#endif // MAME_LIB_UTIL_UNZIP_H
diff --git a/src/lib/util/utf8.h b/src/lib/util/utf8.h
new file mode 100644
index 00000000000..5ef459d94a0
--- /dev/null
+++ b/src/lib/util/utf8.h
@@ -0,0 +1,20 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles, Curt Coder, hap
+/***************************************************************************
+
+ utf8.h
+
+ UTF-8 string literals.
+
+***************************************************************************/
+
+#ifndef MAME_LIB_UTIL_UTF8_H
+#define MAME_LIB_UTIL_UTF8_H
+
+// these are UTF-8 encoded strings for common characters
+#define UTF8_LEFT "\xe2\x86\x90" /* cursor left */
+#define UTF8_RIGHT "\xe2\x86\x92" /* cursor right */
+#define UTF8_UP "\xe2\x86\x91" /* cursor up */
+#define UTF8_DOWN "\xe2\x86\x93" /* cursor down */
+
+#endif // MAME_LIB_UTIL_UTF8_H
diff --git a/src/lib/util/utilfwd.h b/src/lib/util/utilfwd.h
new file mode 100644
index 00000000000..b99eeb6bc9a
--- /dev/null
+++ b/src/lib/util/utilfwd.h
@@ -0,0 +1,52 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/**********************************************************************
+
+ utilfwd.h
+
+ Forward declarations of types
+
+**********************************************************************/
+#ifndef MAME_LIB_UTIL_UTILFWD_H
+#define MAME_LIB_UTIL_UTILFWD_H
+
+// aviio.h
+class avi_file;
+
+// chd.h
+class chd_file;
+
+
+namespace util {
+
+// corefile.h
+class core_file;
+
+// ioprocs.h
+class read_stream;
+class write_stream;
+class read_write_stream;
+class random_access;
+class random_read;
+class random_write;
+class random_read_write;
+
+// opresolv.h
+class option_guide;
+class option_resolution;
+
+// unzip.h
+class archive_file;
+
+} // namespace util
+
+
+namespace util::xml {
+
+// xmlfile.h
+class data_node;
+class file;
+
+} // namespace util::xml
+
+#endif // MAME_LIB_UTIL_UTILFWD_H
diff --git a/src/lib/util/vbiparse.cpp b/src/lib/util/vbiparse.cpp
index bfd145dd99f..c0aaed5abcf 100644
--- a/src/lib/util/vbiparse.cpp
+++ b/src/lib/util/vbiparse.cpp
@@ -8,9 +8,13 @@
***************************************************************************/
-#include "osdcore.h"
#include "vbiparse.h"
-#include <math.h>
+
+#include "multibyte.h"
+
+#include <cmath>
+#include <cstdio>
+#include <cstring>
#include <algorithm>
@@ -362,22 +366,12 @@ void vbi_parse_all(const uint16_t *source, int sourcerowpixels, int sourcewidth,
void vbi_metadata_pack(uint8_t *dest, uint32_t framenum, const vbi_metadata *vbi)
{
- dest[0] = framenum >> 16;
- dest[1] = framenum >> 8;
- dest[2] = framenum >> 0;
+ put_u24be(&dest[0], framenum);
dest[3] = vbi->white;
- dest[4] = vbi->line16 >> 16;
- dest[5] = vbi->line16 >> 8;
- dest[6] = vbi->line16 >> 0;
- dest[7] = vbi->line17 >> 16;
- dest[8] = vbi->line17 >> 8;
- dest[9] = vbi->line17 >> 0;
- dest[10] = vbi->line18 >> 16;
- dest[11] = vbi->line18 >> 8;
- dest[12] = vbi->line18 >> 0;
- dest[13] = vbi->line1718 >> 16;
- dest[14] = vbi->line1718 >> 8;
- dest[15] = vbi->line1718 >> 0;
+ put_u24be(&dest[4], vbi->line16);
+ put_u24be(&dest[7], vbi->line17);
+ put_u24be(&dest[10], vbi->line18);
+ put_u24be(&dest[13], vbi->line1718);
}
@@ -399,10 +393,10 @@ void vbi_metadata_pack(uint8_t *dest, uint32_t framenum, const vbi_metadata *vbi
void vbi_metadata_unpack(vbi_metadata *vbi, uint32_t *framenum, const uint8_t *source)
{
if (framenum != nullptr)
- *framenum = (source[0] << 16) | (source[1] << 8) | source[2];
+ *framenum = get_u24be(&source[0]);
vbi->white = source[3];
- vbi->line16 = (source[4] << 16) | (source[5] << 8) | source[6];
- vbi->line17 = (source[7] << 16) | (source[8] << 8) | source[9];
- vbi->line18 = (source[10] << 16) | (source[11] << 8) | source[12];
- vbi->line1718 = (source[13] << 16) | (source[14] << 8) | source[15];
+ vbi->line16 = get_u24be(&source[4]);
+ vbi->line17 = get_u24be(&source[7]);
+ vbi->line18 = get_u24be(&source[10]);
+ vbi->line1718 = get_u24be(&source[13]);
}
diff --git a/src/lib/util/vbiparse.h b/src/lib/util/vbiparse.h
index 8113cb5cb32..08e2359e117 100644
--- a/src/lib/util/vbiparse.h
+++ b/src/lib/util/vbiparse.h
@@ -13,7 +13,7 @@
#pragma once
-#include "osdcomm.h"
+#include <cstdint>
/***************************************************************************
CONSTANTS
diff --git a/src/lib/util/wavwrite.cpp b/src/lib/util/wavwrite.cpp
index 0667e9dcaff..ecb5514b48f 100644
--- a/src/lib/util/wavwrite.cpp
+++ b/src/lib/util/wavwrite.cpp
@@ -1,88 +1,92 @@
// license:BSD-3-Clause
// copyright-holders:Aaron Giles
+#include "wavwrite.h"
+
#include "osdcomm.h"
-#include "corealloc.h"
+
+#include <cstdio>
+#include <new>
+#include <string>
#include <vector>
-#include "wavwrite.h"
+
+
+namespace util {
struct wav_file
{
- FILE *file;
- uint32_t total_offs;
- uint32_t data_offs;
+ FILE *file = nullptr;
+ std::vector<std::int16_t> temp;
+ std::uint32_t total_offs = 0U;
+ std::uint32_t data_offs = 0U;
};
-wav_file *wav_open(const char *filename, int sample_rate, int channels)
+wav_file_ptr wav_open(std::string_view filename, int sample_rate, int channels)
{
- wav_file *wav;
- uint32_t bps, temp32;
- uint16_t align, temp16;
+ std::uint32_t temp32;
+ std::uint16_t temp16;
- /* allocate memory for the wav struct */
- wav = global_alloc_nothrow(wav_file);
+ // allocate memory for the wav struct
+ wav_file_ptr wav(new (std::nothrow) wav_file);
if (!wav)
return nullptr;
- /* create the file */
- wav->file = fopen(filename, "wb");
+ // create the file
+ wav->file = std::fopen(std::string(filename).c_str(), "wb"); // ugly - need to force NUL termination on filename
if (!wav->file)
- {
- global_free(wav);
return nullptr;
- }
- /* write the 'RIFF' header */
- fwrite("RIFF", 1, 4, wav->file);
+ // write the 'RIFF' header
+ std::fwrite("RIFF", 1, 4, wav->file);
- /* write the total size */
+ // write the total size
temp32 = 0;
- wav->total_offs = ftell(wav->file);
- fwrite(&temp32, 1, 4, wav->file);
+ wav->total_offs = std::ftell(wav->file);
+ std::fwrite(&temp32, 1, 4, wav->file);
- /* write the 'WAVE' type */
- fwrite("WAVE", 1, 4, wav->file);
+ // write the 'WAVE' type
+ std::fwrite("WAVE", 1, 4, wav->file);
- /* write the 'fmt ' tag */
- fwrite("fmt ", 1, 4, wav->file);
+ // write the 'fmt ' tag
+ std::fwrite("fmt ", 1, 4, wav->file);
- /* write the format length */
+ // write the format length
temp32 = little_endianize_int32(16);
- fwrite(&temp32, 1, 4, wav->file);
+ std::fwrite(&temp32, 1, 4, wav->file);
- /* write the format (PCM) */
+ // write the format (PCM)
temp16 = little_endianize_int16(1);
- fwrite(&temp16, 1, 2, wav->file);
+ std::fwrite(&temp16, 1, 2, wav->file);
- /* write the channels */
+ // write the channels
temp16 = little_endianize_int16(channels);
- fwrite(&temp16, 1, 2, wav->file);
+ std::fwrite(&temp16, 1, 2, wav->file);
- /* write the sample rate */
+ // write the sample rate
temp32 = little_endianize_int32(sample_rate);
- fwrite(&temp32, 1, 4, wav->file);
+ std::fwrite(&temp32, 1, 4, wav->file);
- /* write the bytes/second */
- bps = sample_rate * 2 * channels;
+ // write the bytes/second
+ std::uint32_t const bps = sample_rate * 2 * channels;
temp32 = little_endianize_int32(bps);
- fwrite(&temp32, 1, 4, wav->file);
+ std::fwrite(&temp32, 1, 4, wav->file);
- /* write the block align */
- align = 2 * channels;
+ // write the block align
+ std::uint16_t const align = 2 * channels;
temp16 = little_endianize_int16(align);
- fwrite(&temp16, 1, 2, wav->file);
+ std::fwrite(&temp16, 1, 2, wav->file);
- /* write the bits/sample */
+ // write the bits/sample
temp16 = little_endianize_int16(16);
- fwrite(&temp16, 1, 2, wav->file);
+ std::fwrite(&temp16, 1, 2, wav->file);
- /* write the 'data' tag */
- fwrite("data", 1, 4, wav->file);
+ // write the 'data' tag
+ std::fwrite("data", 1, 4, wav->file);
- /* write the data length */
+ // write the data length
temp32 = 0;
- wav->data_offs = ftell(wav->file);
- fwrite(&temp32, 1, 4, wav->file);
+ wav->data_offs = std::ftell(wav->file);
+ std::fwrite(&temp32, 1, 4, wav->file);
return wav;
}
@@ -90,102 +94,102 @@ wav_file *wav_open(const char *filename, int sample_rate, int channels)
void wav_close(wav_file *wav)
{
- uint32_t total;
- uint32_t temp32;
-
- if (!wav) return;
-
- total = ftell(wav->file);
-
- /* update the total file size */
- fseek(wav->file, wav->total_offs, SEEK_SET);
- temp32 = total - (wav->total_offs + 4);
- temp32 = little_endianize_int32(temp32);
- fwrite(&temp32, 1, 4, wav->file);
+ if (!wav)
+ return;
- /* update the data size */
- fseek(wav->file, wav->data_offs, SEEK_SET);
- temp32 = total - (wav->data_offs + 4);
- temp32 = little_endianize_int32(temp32);
- fwrite(&temp32, 1, 4, wav->file);
+ if (wav->file)
+ {
+ std::uint32_t temp32;
+ std::uint32_t const total = std::ftell(wav->file);
+
+ // update the total file size
+ std::fseek(wav->file, wav->total_offs, SEEK_SET);
+ temp32 = total - (wav->total_offs + 4);
+ temp32 = little_endianize_int32(temp32);
+ std::fwrite(&temp32, 1, 4, wav->file);
+
+ // update the data size
+ std::fseek(wav->file, wav->data_offs, SEEK_SET);
+ temp32 = total - (wav->data_offs + 4);
+ temp32 = little_endianize_int32(temp32);
+ std::fwrite(&temp32, 1, 4, wav->file);
+
+ std::fclose(wav->file);
+ }
- fclose(wav->file);
- global_free(wav);
+ delete wav;
}
-void wav_add_data_16(wav_file *wav, int16_t *data, int samples)
+void wav_add_data_16(wav_file &wav, int16_t *data, int samples)
{
- if (!wav) return;
-
- /* just write and flush the data */
- fwrite(data, 2, samples, wav->file);
- fflush(wav->file);
+ // just write and flush the data
+ std::fwrite(data, 2, samples, wav.file);
+ std::fflush(wav.file);
}
-void wav_add_data_32(wav_file *wav, int32_t *data, int samples, int shift)
+void wav_add_data_32(wav_file &wav, int32_t *data, int samples, int shift)
{
- std::vector<int16_t> temp;
- int i;
+ if (!samples)
+ return;
- if (!wav || !samples) return;
+ // resize dynamic array - don't want it to copy if it needs to expand
+ wav.temp.clear();
+ wav.temp.resize(samples);
- /* resize dynamic array */
- temp.resize(samples);
-
- /* clamp */
- for (i = 0; i < samples; i++)
+ // clamp
+ for (int i = 0; i < samples; i++)
{
int val = data[i] >> shift;
- temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ wav.temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
}
- /* write and flush */
- fwrite(&temp[0], 2, samples, wav->file);
- fflush(wav->file);
+ // write and flush
+ std::fwrite(&wav.temp[0], 2, samples, wav.file);
+ std::fflush(wav.file);
}
-void wav_add_data_16lr(wav_file *wav, int16_t *left, int16_t *right, int samples)
+void wav_add_data_16lr(wav_file &wav, int16_t *left, int16_t *right, int samples)
{
- std::vector<int16_t> temp;
- int i;
-
- if (!wav || !samples) return;
+ if (!samples)
+ return;
- /* resize dynamic array */
- temp.resize(samples * 2);
+ // resize dynamic array - don't want it to copy if it needs to expand
+ wav.temp.clear();
+ wav.temp.resize(samples * 2);
- /* interleave */
- for (i = 0; i < samples * 2; i++)
- temp[i] = (i & 1) ? right[i / 2] : left[i / 2];
+ // interleave
+ for (int i = 0; i < samples * 2; i++)
+ wav.temp[i] = (i & 1) ? right[i / 2] : left[i / 2];
- /* write and flush */
- fwrite(&temp[0], 4, samples, wav->file);
- fflush(wav->file);
+ // write and flush
+ std::fwrite(&wav.temp[0], 4, samples, wav.file);
+ std::fflush(wav.file);
}
-void wav_add_data_32lr(wav_file *wav, int32_t *left, int32_t *right, int samples, int shift)
+void wav_add_data_32lr(wav_file &wav, int32_t *left, int32_t *right, int samples, int shift)
{
- std::vector<int16_t> temp;
- int i;
+ if (!samples)
+ return;
- if (!wav || !samples) return;
+ // resize dynamic array - don't want it to copy if it needs to expand
+ wav.temp.clear();
+ wav.temp.resize(samples * 2);
- /* resize dynamic array */
- temp.resize(samples);
-
- /* interleave */
- for (i = 0; i < samples * 2; i++)
+ // interleave
+ for (int i = 0; i < samples * 2; i++)
{
int val = (i & 1) ? right[i / 2] : left[i / 2];
val >>= shift;
- temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
+ wav.temp[i] = (val < -32768) ? -32768 : (val > 32767) ? 32767 : val;
}
- /* write and flush */
- fwrite(&temp[0], 4, samples, wav->file);
- fflush(wav->file);
+ // write and flush
+ std::fwrite(&wav.temp[0], 4, samples, wav.file);
+ std::fflush(wav.file);
}
+
+} // namespace util
diff --git a/src/lib/util/wavwrite.h b/src/lib/util/wavwrite.h
index 8ceb6ad2a0d..3f6ac6fd5c1 100644
--- a/src/lib/util/wavwrite.h
+++ b/src/lib/util/wavwrite.h
@@ -5,14 +5,35 @@
#pragma once
+#include <cstdint>
+#include <memory>
+#include <string_view>
+
+
+namespace util {
+
struct wav_file;
-wav_file *wav_open(const char *filename, int sample_rate, int channels);
-void wav_close(wav_file*wavptr);
+void wav_close(wav_file *wavptr);
+
+struct wav_deleter
+{
+ void operator()(wav_file *wavptr) const
+ {
+ if (wavptr)
+ wav_close(wavptr);
+ }
+};
+
+using wav_file_ptr = std::unique_ptr<wav_file, wav_deleter>;
+
+wav_file_ptr wav_open(std::string_view filename, int sample_rate, int channels);
+
+void wav_add_data_16(wav_file &wavptr, std::int16_t *data, int samples);
+void wav_add_data_32(wav_file &wavptr, std::int32_t *data, int samples, int shift);
+void wav_add_data_16lr(wav_file &wavptr, std::int16_t *left, std::int16_t *right, int samples);
+void wav_add_data_32lr(wav_file &wavptr, std::int32_t *left, std::int32_t *right, int samples, int shift);
-void wav_add_data_16(wav_file *wavptr, int16_t *data, int samples);
-void wav_add_data_32(wav_file *wavptr, int32_t *data, int samples, int shift);
-void wav_add_data_16lr(wav_file *wavptr, int16_t *left, int16_t *right, int samples);
-void wav_add_data_32lr(wav_file *wavptr, int32_t *left, int32_t *right, int samples, int shift);
+} // namespace util
#endif // MAME_UTIL_WAVWRITE_H
diff --git a/src/lib/util/xmlfile.cpp b/src/lib/util/xmlfile.cpp
index 1014a7b245c..db8d87930e2 100644
--- a/src/lib/util/xmlfile.cpp
+++ b/src/lib/util/xmlfile.cpp
@@ -8,13 +8,14 @@
***************************************************************************/
-#include <assert.h>
-
#include "xmlfile.h"
+#include "osdcore.h"
+
#include <expat.h>
#include <algorithm>
+#include <cassert>
#include <cctype>
#include <cstdlib>
#include <cstring>
@@ -23,40 +24,73 @@
#include <sstream>
-namespace util { namespace xml {
+namespace util::xml {
namespace {
-/***************************************************************************
- CONSTANTS
-***************************************************************************/
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
constexpr unsigned TEMP_BUFFER_SIZE(4096U);
-std::locale const f_portable_locale("C");
+
+
+//**************************************************************************
+// UTILITY FUNCTIONS
+//**************************************************************************
+
+void write_escaped(core_file &file, std::string const &str)
+{
+ // FIXME: check for errors
+ std::string::size_type pos = 0;
+ while ((str.size() > pos) && (std::string::npos != pos))
+ {
+ std::string::size_type const found = str.find_first_of("\"&<>", pos);
+ if (found != std::string::npos)
+ {
+ write(file, &str[pos], found - pos);
+ switch (str[found])
+ {
+ case '"': file.puts("&quot;"); pos = found + 1; break;
+ case '&': file.puts("&amp;"); pos = found + 1; break;
+ case '<': file.puts("&lt;"); pos = found + 1; break;
+ case '>': file.puts("&gt;"); pos = found + 1; break;
+ default: pos = found;
+ }
+ }
+ else
+ {
+ write(file, &str[pos], str.size() - pos);
+ pos = found;
+ }
+ }
+}
} // anonymous namespace
-/***************************************************************************
- TYPE DEFINITIONS
-***************************************************************************/
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
struct parse_info
{
+ parse_info() { memset(&parser, 0, sizeof(parser)); }
+
XML_Parser parser;
file::ptr rootnode;
- data_node * curnode;
- uint32_t flags;
+ data_node * curnode = nullptr;
+ uint32_t flags = 0;
};
-/***************************************************************************
- PROTOTYPES
-***************************************************************************/
+//**************************************************************************
+// PROTOTYPES
+//**************************************************************************
-/* expat interfaces */
+// expat interfaces
static bool expat_setup_parser(parse_info &info, parse_options const *opts);
static void expat_element_start(void *data, const XML_Char *name, const XML_Char **attributes);
static void expat_data(void *data, const XML_Char *s, int len);
@@ -64,17 +98,17 @@ static void expat_element_end(void *data, const XML_Char *name);
-/***************************************************************************
- XML FILE OBJECTS
-***************************************************************************/
+//**************************************************************************
+// XML FILE OBJECTS
+//**************************************************************************
file::file() { }
file::~file() { }
-/*-------------------------------------------------
- create - create a new, empty XML file
--------------------------------------------------*/
+//-------------------------------------------------
+// create - create a new, empty XML file
+//-------------------------------------------------
file::ptr file::create()
{
@@ -83,32 +117,31 @@ file::ptr file::create()
}
-/*-------------------------------------------------
- read - parse an XML file into its nodes
--------------------------------------------------*/
+//-------------------------------------------------
+// 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)
{
+ // set up the parser
parse_info info;
- int done;
-
- /* set up the parser */
if (!expat_setup_parser(info, opts))
return ptr();
- /* loop through the file and parse it */
+ // 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();
+ // read as much as we can
+ auto const [err, bytes] = util::read(file, tempbuf, sizeof(tempbuf)); // FIXME: better error handling
+ done = !bytes;
- /* parse the data */
+ // 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);
@@ -122,29 +155,29 @@ file::ptr file::read(util::core_file &file, parse_options const *opts)
}
while (!done);
- /* free the parser */
+ // free the parser
XML_ParserFree(info.parser);
- /* return the root node */
+ // return the root node
return std::move(info.rootnode);
}
-/*-------------------------------------------------
- string_read - parse an XML string into its
- nodes
--------------------------------------------------*/
+//-------------------------------------------------
+// string_read - parse an XML string into its
+// nodes
+//-------------------------------------------------
file::ptr file::string_read(const char *string, parse_options const *opts)
{
parse_info info;
int length = (int)strlen(string);
- /* set up the parser */
+ // set up the parser
if (!expat_setup_parser(info, opts))
return ptr();
- /* parse the data */
+ // parse the data
if (XML_Parse(info.parser, string, length, 1) == XML_STATUS_ERROR)
{
if (opts != nullptr && opts->error != nullptr)
@@ -159,36 +192,36 @@ file::ptr file::string_read(const char *string, parse_options const *opts)
return ptr();
}
- /* free the parser */
+ // free the parser
XML_ParserFree(info.parser);
- /* return the root node */
+ // return the root node
return std::move(info.rootnode);
}
-/*-------------------------------------------------
- file_write - write an XML tree to a file
--------------------------------------------------*/
+//-------------------------------------------------
+// file_write - write an XML tree to a file
+//-------------------------------------------------
void file::write(util::core_file &file) const
{
- /* ensure this is a root node */
+ // ensure this is a root node
assert(!get_name());
- /* output a simple header */
+ // output a simple header
file.printf("<?xml version=\"1.0\"?>\n");
file.printf("<!-- This file is autogenerated; comments and unknown tags will be stripped -->\n");
- /* loop over children of the root and output */
+ // loop over children of the root and output
write_recursive(0, file);
}
-/***************************************************************************
- XML NODE MANAGEMENT
-***************************************************************************/
+//**************************************************************************
+// XML NODE MANAGEMENT
+//**************************************************************************
data_node::data_node()
: line(0)
@@ -214,10 +247,10 @@ data_node::data_node(data_node *parent, const char *name, const char *value)
}
-/*-------------------------------------------------
- free_node_recursive - recursively free
- the data allocated to an XML node
--------------------------------------------------*/
+//-------------------------------------------------
+// free_node_recursive - recursively free
+// the data allocated to an XML node
+//-------------------------------------------------
data_node::~data_node()
{
@@ -229,7 +262,7 @@ void data_node::free_children()
{
for (data_node *nchild = nullptr; m_first_child; m_first_child = nchild)
{
- /* note the next node and free this node */
+ // note the next node and free this node
nchild = m_first_child->get_next_sibling();
delete m_first_child;
}
@@ -250,13 +283,13 @@ void data_node::append_value(char const *value, int length)
void data_node::trim_whitespace()
{
- /* first strip leading spaces */
+ // first strip leading spaces
std::string::iterator start = m_value.begin();
while ((m_value.end() != start) && std::isspace(uint8_t(*start)))
++start;
m_value.replace(m_value.begin(), start, 0U, '\0');
- /* then strip trailing spaces */
+ // then strip trailing spaces
std::string::iterator end = m_value.end();
while ((m_value.begin() != end) && std::isspace(uint8_t(*std::prev(end))))
--end;
@@ -264,27 +297,37 @@ void data_node::trim_whitespace()
}
-/*-------------------------------------------------
- data_node::count_children - count the
- number of child nodes
--------------------------------------------------*/
+//-------------------------------------------------
+// data_node::count_children - count the
+// number of child nodes
+//-------------------------------------------------
-int data_node::count_children() const
+std::size_t data_node::count_children() const
{
- int count = 0;
-
- /* loop over children and count */
+ // loop over children and count
+ std::size_t count = 0;
for (data_node const *node = get_first_child(); node; node = node->get_next_sibling())
count++;
return count;
}
-/*-------------------------------------------------
- data_node::get_child - find the first
- child of the specified node with the specified
- tag
--------------------------------------------------*/
+//-------------------------------------------------
+// data_node::count_children - count the
+// number of child nodes
+//-------------------------------------------------
+
+std::size_t data_node::count_attributes() const
+{
+ return m_attributes.size();
+}
+
+
+//-------------------------------------------------
+// data_node::get_child - find the first
+// child of the specified node with the specified
+// tag
+//-------------------------------------------------
data_node *data_node::get_child(const char *name)
{
@@ -297,11 +340,11 @@ data_node const *data_node::get_child(const char *name) const
}
-/*-------------------------------------------------
- find_first_matching_child - find the first
- child of the specified node with the
- specified tag or attribute/value pair
--------------------------------------------------*/
+//-------------------------------------------------
+// find_first_matching_child - find the first
+// child of the specified node with the
+// specified tag or attribute/value pair
+//-------------------------------------------------
data_node *data_node::find_first_matching_child(const char *name, const char *attribute, const char *matchval)
{
@@ -314,11 +357,11 @@ data_node const *data_node::find_first_matching_child(const char *name, const ch
}
-/*-------------------------------------------------
- data_node::get_next_sibling - find the
- next sibling of the specified node with the
- specified tag
--------------------------------------------------*/
+//-------------------------------------------------
+// data_node::get_next_sibling - find the
+// next sibling of the specified node with the
+// specified tag
+//-------------------------------------------------
data_node *data_node::get_next_sibling(const char *name)
{
@@ -331,11 +374,11 @@ data_node const *data_node::get_next_sibling(const char *name) const
}
-/*-------------------------------------------------
- find_next_matching_sibling - find the next
- sibling of the specified node with the
- specified tag or attribute/value pair
--------------------------------------------------*/
+//-------------------------------------------------
+// find_next_matching_sibling - find the next
+// sibling of the specified node with the
+// specified tag or attribute/value pair
+//-------------------------------------------------
data_node *data_node::find_next_matching_sibling(const char *name, const char *attribute, const char *matchval)
{
@@ -348,17 +391,17 @@ data_node const *data_node::find_next_matching_sibling(const char *name, const c
}
-/*-------------------------------------------------
- add_child - add a new child node to the
- given node
--------------------------------------------------*/
+//-------------------------------------------------
+// add_child - add a new child node to the
+// given node
+//-------------------------------------------------
data_node *data_node::add_child(const char *name, const char *value)
{
if (!name || !*name)
return nullptr;
- /* new element: create a new node */
+ // new element: create a new node
data_node *node;
try { node = new data_node(this, name, value); }
catch (...) { return nullptr; }
@@ -369,7 +412,7 @@ data_node *data_node::add_child(const char *name, const char *value)
return nullptr;
}
- /* add us to the end of the list of siblings */
+ // add us to the end of the list of siblings
data_node **pnode;
for (pnode = &m_first_child; *pnode; pnode = &(*pnode)->m_next) { }
*pnode = node;
@@ -378,19 +421,19 @@ data_node *data_node::add_child(const char *name, const char *value)
}
-/*-------------------------------------------------
- get_or_add_child - find a child node of
- the specified type; if not found, add one
--------------------------------------------------*/
+//-------------------------------------------------
+// get_or_add_child - find a child node of
+// the specified type; if not found, add one
+//-------------------------------------------------
data_node *data_node::get_or_add_child(const char *name, const char *value)
{
- /* find the child first */
+ // find the child first
data_node *const child = m_first_child->get_sibling(name);
if (child)
return child;
- /* if not found, do a standard add child */
+ // if not found, do a standard add child
return add_child(name, value);
}
@@ -422,8 +465,14 @@ data_node *data_node::copy_into(data_node &parent) const
}
else
{
- dst = dst->get_parent();
- src = src->get_parent()->get_next_sibling();
+ do
+ {
+ dst = dst->get_parent();
+ src = src->get_parent();
+ next = src->get_next_sibling();
+ }
+ while (!next && (&parent != dst));
+ src = next;
}
}
}
@@ -432,17 +481,17 @@ data_node *data_node::copy_into(data_node &parent) const
}
-/*-------------------------------------------------
- delete_node - delete a node and its
- children
--------------------------------------------------*/
+//-------------------------------------------------
+// delete_node - delete a node and its
+// children
+//-------------------------------------------------
void data_node::delete_node()
{
- /* don't allow deletion of document root */
+ // don't allow deletion of document root
if (m_parent)
{
- /* first unhook us from the list of children of our parent */
+ // first unhook us from the list of children of our parent
for (data_node **pnode = &m_parent->m_first_child; *pnode; pnode = &(*pnode)->m_next)
{
if (*pnode == this)
@@ -452,25 +501,25 @@ void data_node::delete_node()
}
}
- /* now free ourselves and our children */
+ // now free ourselves and our children
delete this;
}
else
{
- /* remove all children of document root */
+ // remove all children of document root
free_children();
}
}
-/*-------------------------------------------------
- get_next_sibling - find the next sibling of
- the specified node with the specified tag
--------------------------------------------------*/
+//-------------------------------------------------
+// get_next_sibling - find the next sibling of
+// the specified node with the specified tag
+//-------------------------------------------------
data_node *data_node::get_sibling(const char *name)
{
- /* loop over siblings and find a matching name */
+ // loop over siblings and find a matching name
for (data_node *node = this; node; node = node->get_next_sibling())
if (strcmp(node->get_name(), name) == 0)
return node;
@@ -479,7 +528,7 @@ data_node *data_node::get_sibling(const char *name)
data_node const *data_node::get_sibling(const char *name) const
{
- /* loop over siblings and find a matching name */
+ // loop over siblings and find a matching name
for (data_node const *node = this; node; node = node->get_next_sibling())
if (strcmp(node->get_name(), name) == 0)
return node;
@@ -487,21 +536,21 @@ data_node const *data_node::get_sibling(const char *name) const
}
-/*-------------------------------------------------
- find_matching_sibling - find the next
- sibling of the specified node with the
- specified tag or attribute/value pair
--------------------------------------------------*/
+//-------------------------------------------------
+// find_matching_sibling - find the next
+// sibling of the specified node with the
+// specified tag or attribute/value pair
+//-------------------------------------------------
data_node *data_node::find_matching_sibling(const char *name, const char *attribute, const char *matchval)
{
- /* loop over siblings and find a matching attribute */
+ // loop over siblings and find a matching attribute
for (data_node *node = this; node; node = node->get_next_sibling())
{
- /* can pass nullptr as a wildcard for the node name */
+ // can pass nullptr as a wildcard for the node name
if (!name || !strcmp(name, node->get_name()))
{
- /* find a matching attribute */
+ // find a matching attribute
attribute_node const *const attr = node->get_attribute(attribute);
if (attr && !strcmp(attr->value.c_str(), matchval))
return node;
@@ -512,13 +561,13 @@ data_node *data_node::find_matching_sibling(const char *name, const char *attrib
data_node const *data_node::find_matching_sibling(const char *name, const char *attribute, const char *matchval) const
{
- /* loop over siblings and find a matching attribute */
+ // loop over siblings and find a matching attribute
for (data_node const *node = this; node; node = node->get_next_sibling())
{
- /* can pass nullptr as a wildcard for the node name */
+ // can pass nullptr as a wildcard for the node name
if (!name || !strcmp(name, node->get_name()))
{
- /* find a matching attribute */
+ // find a matching attribute
attribute_node const *const attr = node->get_attribute(attribute);
if (attr && !strcmp(attr->value.c_str(), matchval))
return node;
@@ -529,9 +578,9 @@ data_node const *data_node::find_matching_sibling(const char *name, const char *
-/***************************************************************************
- XML ATTRIBUTE MANAGEMENT
-***************************************************************************/
+//**************************************************************************
+// XML ATTRIBUTE MANAGEMENT
+//**************************************************************************
bool data_node::has_attribute(const char *attribute) const
{
@@ -539,14 +588,14 @@ bool data_node::has_attribute(const char *attribute) const
}
-/*-------------------------------------------------
- get_attribute - get the value of the
- specified attribute, or nullptr if not found
--------------------------------------------------*/
+//-------------------------------------------------
+// get_attribute - get the value of the
+// specified attribute, or nullptr if not found
+//-------------------------------------------------
data_node::attribute_node *data_node::get_attribute(const char *attribute)
{
- /* loop over attributes and find a match */
+ // loop over attributes and find a match
for (attribute_node &anode : m_attributes)
if (strcmp(anode.name.c_str(), attribute) == 0)
return &anode;
@@ -555,7 +604,7 @@ data_node::attribute_node *data_node::get_attribute(const char *attribute)
data_node::attribute_node const *data_node::get_attribute(const char *attribute) const
{
- /* loop over attributes and find a match */
+ // loop over attributes and find a match
for (attribute_node const &anode : m_attributes)
if (strcmp(anode.name.c_str(), attribute) == 0)
return &anode;
@@ -563,11 +612,24 @@ data_node::attribute_node const *data_node::get_attribute(const char *attribute)
}
-/*-------------------------------------------------
- get_attribute_string - get the string
- value of the specified attribute; if not
- found, return = the provided default
--------------------------------------------------*/
+//-------------------------------------------------
+// get_attribute_string_ptr - get a pointer to
+// the string value of the specified attribute;
+// if not found, return = nullptr
+//-------------------------------------------------
+
+std::string const *data_node::get_attribute_string_ptr(const char *attribute) const
+{
+ attribute_node const *attr = get_attribute(attribute);
+ return attr ? &attr->value : nullptr;
+}
+
+
+//-------------------------------------------------
+// get_attribute_string - get the string
+// value of the specified attribute; if not
+// found, return = the provided default
+//-------------------------------------------------
const char *data_node::get_attribute_string(const char *attribute, const char *defvalue) const
{
@@ -576,34 +638,35 @@ const char *data_node::get_attribute_string(const char *attribute, const char *d
}
-/*-------------------------------------------------
- get_attribute_int - get the integer
- value of the specified attribute; if not
- found, return = the provided default
--------------------------------------------------*/
+//-------------------------------------------------
+// get_attribute_int - get the integer
+// value of the specified attribute; if not
+// found, return = the provided default
+//-------------------------------------------------
-int data_node::get_attribute_int(const char *attribute, int defvalue) const
+long long data_node::get_attribute_int(const char *attribute, long long defvalue) const
{
- char const *const string = get_attribute_string(attribute, nullptr);
- if (!string)
+ attribute_node const *attr = get_attribute(attribute);
+ if (!attr)
return defvalue;
+ std::string const &string = attr->value;
std::istringstream stream;
- stream.imbue(f_portable_locale);
- int result;
+ stream.imbue(std::locale::classic());
+ long long result;
if (string[0] == '$')
{
stream.str(&string[1]);
- unsigned uvalue;
+ unsigned long long uvalue;
stream >> std::hex >> uvalue;
- result = int(uvalue);
+ result = static_cast<long long>(uvalue);
}
else if ((string[0] == '0') && ((string[1] == 'x') || (string[1] == 'X')))
{
stream.str(&string[2]);
- unsigned uvalue;
+ unsigned long long uvalue;
stream >> std::hex >> uvalue;
- result = int(uvalue);
+ result = static_cast<long long>(uvalue);
}
else if (string[0] == '#')
{
@@ -620,179 +683,139 @@ int data_node::get_attribute_int(const char *attribute, int defvalue) const
}
-/*-------------------------------------------------
- get_attribute_int_format - return the
- format of the given integer attribute
--------------------------------------------------*/
+//-------------------------------------------------
+// get_attribute_int_format - return the
+// format of the given integer attribute
+//-------------------------------------------------
data_node::int_format data_node::get_attribute_int_format(const char *attribute) const
{
- char const *const string = get_attribute_string(attribute, nullptr);
- if (!string)
+ attribute_node const *attr = get_attribute(attribute);
+ if (!attr)
return int_format::DECIMAL;
- else if (string[0] == '$')
+ std::string const &string = attr->value;
+
+ if (string[0] == '$')
return int_format::HEX_DOLLAR;
else if (string[0] == '0' && string[1] == 'x')
return int_format::HEX_C;
- if (string[0] == '#')
+ else if (string[0] == '#')
return int_format::DECIMAL_HASH;
else
return int_format::DECIMAL;
}
-/*-------------------------------------------------
- get_attribute_float - get the float
- value of the specified attribute; if not
- found, return = the provided default
--------------------------------------------------*/
+//-------------------------------------------------
+// get_attribute_float - get the float
+// value of the specified attribute; if not
+// found, return = the provided default
+//-------------------------------------------------
float data_node::get_attribute_float(const char *attribute, float defvalue) const
{
- char const *const string = get_attribute_string(attribute, nullptr);
- if (!string)
+ attribute_node const *attr = get_attribute(attribute);
+ if (!attr)
return defvalue;
- std::istringstream stream(string);
- stream.imbue(f_portable_locale);
+ std::istringstream stream(attr->value);
+ stream.imbue(std::locale::classic());
float result;
return (stream >> result) ? result : defvalue;
}
-/*-------------------------------------------------
- set_attribute - set a new attribute and
- string value on the node
--------------------------------------------------*/
+//-------------------------------------------------
+// set_attribute - set a new attribute and
+// string value on the node
+//-------------------------------------------------
void data_node::set_attribute(const char *name, const char *value)
{
attribute_node *anode;
- /* first find an existing one to replace */
+ // first find an existing one to replace
anode = get_attribute(name);
if (anode != nullptr)
{
- /* if we found it, free the old value and replace it */
+ // if we found it, free the old value and replace it
anode->value = value;
}
else
{
- /* otherwise, create a new node */
+ // otherwise, create a new node
add_attribute(name, value);
}
}
-/*-------------------------------------------------
- set_attribute_int - set a new attribute and
- integer value on the node
--------------------------------------------------*/
+//-------------------------------------------------
+// set_attribute_int - set a new attribute and
+// integer value on the node
+//-------------------------------------------------
-void data_node::set_attribute_int(const char *name, int value)
+void data_node::set_attribute_int(const char *name, long long value)
{
- set_attribute(name, string_format(f_portable_locale, "%d", value).c_str());
+ set_attribute(name, string_format(std::locale::classic(), "%d", value).c_str());
}
-/*-------------------------------------------------
- set_attribute_int - set a new attribute and
- float value on the node
--------------------------------------------------*/
+//-------------------------------------------------
+// set_attribute_int - set a new attribute and
+// float value on the node
+//-------------------------------------------------
void data_node::set_attribute_float(const char *name, float value)
{
- set_attribute(name, string_format(f_portable_locale, "%f", value).c_str());
+ set_attribute(name, string_format(std::locale::classic(), "%f", value).c_str());
}
-/***************************************************************************
- MISCELLANEOUS INTERFACES
-***************************************************************************/
+//**************************************************************************
+// MISCELLANEOUS INTERFACES
+//**************************************************************************
-/*-------------------------------------------------
- normalize_string - normalize a string
- to ensure it doesn't contain embedded tags
--------------------------------------------------*/
+//-------------------------------------------------
+// normalize_string - normalize a string
+// to ensure it doesn't contain embedded tags
+//-------------------------------------------------
-const char *normalize_string(const char *string)
+std::string normalize_string(std::string_view string)
{
- static char buffer[1024];
- char *d = &buffer[0];
+ std::string result;
+ result.reserve(string.length());
- if (string != nullptr)
+ for (char ch : string)
{
- while (*string)
+ switch (ch)
{
- switch (*string)
- {
- case '\"' : d += sprintf(d, "&quot;"); break;
- case '&' : d += sprintf(d, "&amp;"); break;
- case '<' : d += sprintf(d, "&lt;"); break;
- case '>' : d += sprintf(d, "&gt;"); break;
- default:
- *d++ = *string;
- }
- ++string;
+ case '\"': result.append("&quot;"); break;
+ case '&': result.append("&amp;"); break;
+ case '<': result.append("&lt;"); break;
+ case '>': result.append("&gt;"); break;
+ default: result.append(1, ch); break;
}
}
- *d++ = 0;
- return buffer;
-}
-
-
-
-/***************************************************************************
- EXPAT INTERFACES
-***************************************************************************/
-
-/*-------------------------------------------------
- expat_malloc/expat_realloc/expat_free -
- wrappers for memory allocation functions so
- that they pass through out memory tracking
- systems
--------------------------------------------------*/
-static void *expat_malloc(size_t size)
-{
- uint32_t *result = (uint32_t *)malloc(size + 4 * sizeof(uint32_t));
- *result = size;
- return &result[4];
+ return result;
}
-static void expat_free(void *ptr)
-{
- if (ptr != nullptr)
- free(&((uint32_t *)ptr)[-4]);
-}
-static void *expat_realloc(void *ptr, size_t size)
-{
- void *newptr = expat_malloc(size);
- if (newptr == nullptr)
- return nullptr;
- if (ptr != nullptr)
- {
- uint32_t oldsize = ((uint32_t *)ptr)[-4];
- memcpy(newptr, ptr, oldsize);
- expat_free(ptr);
- }
- return newptr;
-}
+//**************************************************************************
+// EXPAT INTERFACES
+//**************************************************************************
-/*-------------------------------------------------
- expat_setup_parser - set up expat for parsing
--------------------------------------------------*/
+//-------------------------------------------------
+// expat_setup_parser - set up expat for parsing
+//-------------------------------------------------
static bool expat_setup_parser(parse_info &info, parse_options const *opts)
{
- XML_Memory_Handling_Suite memcallbacks;
-
- /* setup info structure */
- memset(&info, 0, sizeof(info));
+ // setup info structure
+ info = parse_info();
if (opts != nullptr)
{
info.flags = opts->flags;
@@ -804,105 +827,102 @@ static bool expat_setup_parser(parse_info &info, parse_options const *opts)
}
}
- /* create a root node */
+ // create a root node
info.rootnode = file::create();
if (!info.rootnode)
return false;
info.curnode = info.rootnode.get();
- /* create the XML parser */
- memcallbacks.malloc_fcn = expat_malloc;
- memcallbacks.realloc_fcn = expat_realloc;
- memcallbacks.free_fcn = expat_free;
- info.parser = XML_ParserCreate_MM(nullptr, &memcallbacks, nullptr);
- if (info.parser == nullptr)
+ // create the XML parser
+ info.parser = XML_ParserCreate(nullptr);
+ if (!info.parser)
{
info.rootnode.reset();
return false;
}
- /* configure the parser */
+ // configure the parser
XML_SetElementHandler(info.parser, expat_element_start, expat_element_end);
XML_SetCharacterDataHandler(info.parser, expat_data);
XML_SetUserData(info.parser, &info);
- /* optional parser initialization step */
+ // optional parser initialization step
if (opts != nullptr && opts->init_parser != nullptr)
(*opts->init_parser)(info.parser);
return true;
}
-/*-------------------------------------------------
- expat_element_start - expat callback for a new
- element
--------------------------------------------------*/
+//------------------------------------------------
+// expat_element_start - expat callback for a new
+// element
+//------------------------------------------------
static void expat_element_start(void *data, const XML_Char *name, const XML_Char **attributes)
{
- parse_info *info = (parse_info *) data;
+ auto *info = (parse_info *) data;
data_node **curnode = &info->curnode;
data_node *newnode;
int attr;
- /* add a new child node to the current node */
+ // add a new child node to the current node
newnode = (*curnode)->add_child(name, nullptr);
if (newnode == nullptr)
return;
- /* remember the line number */
+ // remember the line number
newnode->line = XML_GetCurrentLineNumber(info->parser);
- /* add all the attributes as well */
+ // add all the attributes as well
for (attr = 0; attributes[attr]; attr += 2)
newnode->add_attribute(attributes[attr+0], attributes[attr+1]);
- /* set us up as the current node */
+ // set us up as the current node
*curnode = newnode;
}
-/*-------------------------------------------------
- expat_data - expat callback for an additional
- element data
--------------------------------------------------*/
+//-------------------------------------------------
+// expat_data - expat callback for an additional
+// element data
+//-------------------------------------------------
static void expat_data(void *data, const XML_Char *s, int len)
{
- parse_info *info = (parse_info *) data;
+ auto *info = (parse_info *) data;
data_node **curnode = &info->curnode;
(*curnode)->append_value(s, len);
}
-/*-------------------------------------------------
- expat_element_end - expat callback for the end
- of an element
--------------------------------------------------*/
+//-------------------------------------------------
+// expat_element_end - expat callback for the end
+// of an element
+//-------------------------------------------------
static void expat_element_end(void *data, const XML_Char *name)
{
- parse_info *info = (parse_info *) data;
+ auto *info = (parse_info *) data;
data_node **curnode = &info->curnode;
- /* strip leading/trailing spaces from the value data */
+ // strip leading/trailing spaces from the value data
if (!(info->flags & PARSE_FLAG_WHITESPACE_SIGNIFICANT))
(*curnode)->trim_whitespace();
- /* back us up a node */
+ // back us up a node
*curnode = (*curnode)->get_parent();
}
-/***************************************************************************
- NODE/ATTRIBUTE ADDITIONS
-***************************************************************************/
+//**************************************************************************
+// NODE/ATTRIBUTE ADDITIONS
+//**************************************************************************
-/*-------------------------------------------------
- add_attribute - add a new attribute to the
- given node
--------------------------------------------------*/
+//-------------------------------------------------
+// add_attribute - add a new attribute to the
+// given node
+//-------------------------------------------------
void data_node::add_attribute(const char *name, const char *value)
{
@@ -919,58 +939,65 @@ void data_node::add_attribute(const char *name, const char *value)
-/***************************************************************************
- RECURSIVE TREE OPERATIONS
-***************************************************************************/
+//**************************************************************************
+// RECURSIVE TREE OPERATIONS
+//**************************************************************************
-/*-------------------------------------------------
- write_node_recursive - recursively write
- an XML node and its children to a file
--------------------------------------------------*/
+//-------------------------------------------------
+// write_node_recursive - recursively write
+// an XML node and its children to a file
+//-------------------------------------------------
void data_node::write_recursive(int indent, util::core_file &file) const
{
if (!get_name())
{
- /* root node doesn't generate tag */
+ // root node doesn't generate tag
for (data_node const *child = this->get_first_child(); child; child = child->get_next_sibling())
child->write_recursive(indent, file);
}
else
{
- /* output this tag */
+ // output this tag
file.printf("%*s<%s", indent, "", get_name());
- /* output any attributes */
+ // output any attributes, escaping as necessary
for (attribute_node const &anode : m_attributes)
- file.printf(" %s=\"%s\"", anode.name, anode.value);
+ {
+ file.printf(" %s=\"", anode.name);
+ write_escaped(file, anode.value);
+ file.puts("\"");
+ }
if (!get_first_child() && !get_value())
{
- /* if there are no children and no value, end the tag here */
+ // if there are no children and no value, end the tag here
file.printf(" />\n");
}
else
{
- /* otherwise, close this tag and output more stuff */
+ // otherwise, close this tag and output more stuff
file.printf(">\n");
- /* if there is a value, output that here */
- if (get_value())
- file.printf("%*s%s\n", indent + 4, "", get_value());
+ // if there is a value, output that here
+ if (!m_value.empty())
+ {
+ file.printf("%*s", indent + 4, "");
+ write_escaped(file, m_value);
+ file.puts("\n");
+ }
- /* loop over children and output them as well */
+ // loop over children and output them as well
if (get_first_child())
{
for (data_node const *child = this->get_first_child(); child; child = child->get_next_sibling())
child->write_recursive(indent + 4, file);
}
- /* write a closing tag */
+ // write a closing tag
file.printf("%*s</%s>\n", indent, "", get_name());
}
}
}
-
-} } // namespace util::xml
+} // namespace util::xml
diff --git a/src/lib/util/xmlfile.h b/src/lib/util/xmlfile.h
index 8d09b2b7e1e..8949ede2d6d 100644
--- a/src/lib/util/xmlfile.h
+++ b/src/lib/util/xmlfile.h
@@ -7,17 +7,16 @@
XML file parsing code.
***************************************************************************/
-
-#pragma once
-
#ifndef MAME_LIB_UTIL_XMLFILE_H
#define MAME_LIB_UTIL_XMLFILE_H
-#include "osdcore.h"
+#pragma once
+
#include "corefile.h"
#include <list>
#include <string>
+#include <string_view>
#include <utility>
@@ -25,7 +24,7 @@
struct XML_ParserStruct;
-namespace util { namespace xml {
+namespace util::xml {
/***************************************************************************
CONSTANTS
@@ -89,8 +88,9 @@ public:
data_node *get_parent() { return m_parent; }
data_node const *get_parent() const { return m_parent; }
- // count the number of child nodes
- int count_children() const;
+ // count the number of children
+ std::size_t count_children() const;
+ std::size_t count_attributes() const;
// get the first child
data_node *get_first_child() { return m_first_child; }
@@ -134,11 +134,14 @@ public:
// return whether a node has the specified attribute
bool has_attribute(const char *attribute) const;
+ // return a pointer to the string value of an attribute, or nullptr if not present
+ std::string const *get_attribute_string_ptr(const char *attribute) const;
+
// return the string value of an attribute, or the specified default if not present
const char *get_attribute_string(const char *attribute, const char *defvalue) const;
// return the integer value of an attribute, or the specified default if not present
- int get_attribute_int(const char *attribute, int defvalue) const;
+ long long get_attribute_int(const char *attribute, long long defvalue) const;
// return the format of the given integer attribute
int_format get_attribute_int_format(const char *attribute) const;
@@ -150,7 +153,7 @@ public:
void set_attribute(const char *name, const char *value);
// set the integer value of an attribute
- void set_attribute_int(const char *name, int value);
+ void set_attribute_int(const char *name, long long value);
// set the float value of an attribute
void set_attribute_float(const char *name, float value);
@@ -221,7 +224,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);
@@ -243,8 +246,8 @@ private:
/* ----- miscellaneous interfaces ----- */
/* normalize a string into something that can be written to an XML file */
-const char *normalize_string(const char *string);
+std::string normalize_string(std::string_view string);
-} } // namespace util::xml
+} // namespace util::xml
-#endif /* MAME_LIB_UTIL_XMLFILE_H */
+#endif // MAME_LIB_UTIL_XMLFILE_H
diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp
index d8e90de11ff..a5bb2cae8af 100644
--- a/src/lib/util/zippath.cpp
+++ b/src/lib/util/zippath.cpp
@@ -9,14 +9,15 @@
***************************************************************************/
#include "zippath.h"
-#include "unzip.h"
-#include "corestr.h"
-#include "osdcore.h"
-#include <stdlib.h>
+#include "corestr.h"
+#include "path.h"
+#include "unzip.h"
+#include <algorithm>
#include <cassert>
#include <cctype>
+#include <cstdlib>
#include <forward_list>
#include <new>
@@ -47,21 +48,20 @@ int is_path_separator(char c)
// is_root - tests to see if this path is the root
// -------------------------------------------------
-bool is_root(std::string const &path)
+bool is_root(std::string_view path)
{
- // FIXME: get rid of the assumption that paths are DOS-like
-
- std::string::size_type i = 0;
+#if defined(_WIN32)
+ // FIXME: don't assume paths are DOS-like - UNC paths, \\?\ long path prefix, etc. complicate this
// skip drive letter
- if (isalpha(path[i]) && (path[i + 1] == ':'))
- i += 2;
+ if (path.length() >= 2 && isalpha(path[0]) && (path[1] == ':'))
+ path.remove_prefix(2);
// skip path separators
- while (is_path_separator(path[i]))
- i++;
-
- return path[i] == '\0';
+ return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
+#else
+ return path.find_first_not_of(PATH_SEPARATOR) == std::string_view::npos;
+#endif
}
@@ -71,10 +71,9 @@ bool is_root(std::string const &path)
// 7-zip file
// -------------------------------------------------
-bool is_7z_file(std::string const &path)
+bool is_7z_file(std::string_view path)
{
- auto const s = path.rfind('.');
- return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".7z");
+ return core_filename_ends_with(path, ".7z");
}
@@ -83,10 +82,9 @@ bool is_7z_file(std::string const &path)
// ZIP file
// -------------------------------------------------
-bool is_zip_file(std::string const &path)
+bool is_zip_file(std::string_view path)
{
- auto const s = path.rfind('.');
- return (std::string::npos != s) && !core_stricmp(path.c_str() + s, ".zip");
+ return core_filename_ends_with(path, ".zip") || core_filename_ends_with(path, ".imz");
}
@@ -120,7 +118,7 @@ bool is_zip_path_separator(char c)
// character, normalizing separators as '/'
// -------------------------------------------------
-char next_path_char(std::string const &s, std::string::size_type &pos)
+char next_path_char(std::string_view s, std::string_view::size_type &pos)
{
// skip over any initial separators
if (pos == 0)
@@ -159,11 +157,11 @@ char next_path_char(std::string const &s, std::string::size_type &pos)
// type of a sub path in a zip file
// -------------------------------------------------
-int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd::directory::entry::entry_type &type)
+int zippath_find_sub_path(archive_file &zipfile, std::string_view subpath, osd::directory::entry::entry_type &type)
{
for (int header = zipfile.first_file(); header >= 0; header = zipfile.next_file())
{
- std::string::size_type i = 0, j = 0;
+ std::string_view::size_type i = 0, j = 0;
char c1, c2;
do
{
@@ -194,57 +192,30 @@ int zippath_find_sub_path(archive_file &zipfile, std::string const &subpath, osd
// -------------------------------------------------
-// parse_parent_path - parses out the parent path
-// -------------------------------------------------
-
-void parse_parent_path(const std::string &path, std::string::size_type *beginpos, std::string::size_type *endpos)
-{
- std::string::size_type length = path.length();
- std::string::size_type pos;
-
- // skip over trailing path separators
- pos = length ? (length - 1) : std::string::npos;
- while ((pos > 0) && (pos != std::string::npos) && is_path_separator(path[pos]))
- pos--;
-
- // return endpos
- if (endpos != nullptr)
- *endpos = pos;
-
- // now skip until we find a path separator
- while ((pos != std::string::npos) && !is_path_separator(path[pos]))
- pos = (pos > 0) ? pos - 1 : std::string::npos;
-
- // return beginpos
- if (beginpos != nullptr)
- *beginpos = pos;
-}
-
-
-// -------------------------------------------------
// zippath_resolve - separates a ZIP path out into
// true path and ZIP entry components
// -------------------------------------------------
-osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
+std::error_condition zippath_resolve(std::string_view path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath)
{
newpath.clear();
// be conservative
- entry_type = osd::directory::entry::entry_type::NONE;
zipfile.reset();
std::string apath(path);
std::string apath_trimmed;
- osd::directory::entry::entry_type current_entry_type;
+ osd::directory::entry::entry_type current_entry_type = osd::directory::entry::entry_type::NONE;
bool went_up = false;
do
{
// trim the path of trailing path separators
- auto i = apath.length();
- while ((i > 1) && is_path_separator(apath[i - 1]))
- i--;
- apath.resize(i);
+ auto const i = apath.find_last_not_of(PATH_SEPARATOR);
+ if (i != std::string::npos)
+ apath.erase(std::max<decltype(i)>(i + 1, 2)); // don't erase drive letter
+ else if (!is_root(apath))
+ break;
+
apath_trimmed = apath;
// stat the path
@@ -259,77 +230,46 @@ osd_file::error zippath_resolve(const char *path, osd::directory::entry::entry_t
else
{
// if we have not found the file or directory, go up
- current_entry_type = osd::directory::entry::entry_type::NONE;
went_up = true;
apath = zippath_parent(apath);
}
}
- while ((current_entry_type == osd::directory::entry::entry_type::NONE) && !is_root(apath.c_str()));
+ while ((current_entry_type == osd::directory::entry::entry_type::NONE) && !is_root(apath));
// if we did not find anything, then error out
if (current_entry_type == osd::directory::entry::entry_type::NONE)
- return osd_file::error::NOT_FOUND;
+ {
+ entry_type = osd::directory::entry::entry_type::NONE;
+ return std::errc::no_such_file_or_directory;
+ }
// is this file a ZIP file?
if ((current_entry_type == osd::directory::entry::entry_type::FILE) &&
- ((is_zip_file(apath_trimmed) && (archive_file::open_zip(apath_trimmed, zipfile) == archive_file::error::NONE)) ||
- (is_7z_file(apath_trimmed) && (archive_file::open_7z(apath_trimmed, zipfile) == archive_file::error::NONE))))
+ ((is_zip_file(apath_trimmed) && !archive_file::open_zip(apath_trimmed, zipfile)) ||
+ (is_7z_file(apath_trimmed) && !archive_file::open_7z(apath_trimmed, zipfile))))
{
- auto i = strlen(path + apath.length());
+ auto i = path.length() - apath.length();
while ((i > 0) && is_zip_path_separator(path[apath.length() + i - 1]))
i--;
- newpath.assign(path + apath.length(), i);
+ newpath.assign(path, apath.length(), i);
// this was a true ZIP path - attempt to identify the type of path
zippath_find_sub_path(*zipfile, newpath, current_entry_type);
if (current_entry_type == osd::directory::entry::entry_type::NONE)
- return osd_file::error::NOT_FOUND;
+ return std::errc::no_such_file_or_directory;
}
else
{
// this was a normal path
if (went_up)
- return osd_file::error::NOT_FOUND;
+ return std::errc::no_such_file_or_directory;
newpath = path;
}
// success!
entry_type = current_entry_type;
- return osd_file::error::NONE;
-}
-
-
-// -------------------------------------------------
-// file_error_from_zip_error - translates a
-// osd_file::error to a zip_error
-// -------------------------------------------------
-
-osd_file::error file_error_from_zip_error(archive_file::error ziperr)
-{
- osd_file::error filerr;
- switch(ziperr)
- {
- case archive_file::error::NONE:
- filerr = osd_file::error::NONE;
- break;
- case archive_file::error::OUT_OF_MEMORY:
- filerr = osd_file::error::OUT_OF_MEMORY;
- break;
- case archive_file::error::BAD_SIGNATURE:
- case archive_file::error::DECOMPRESS_ERROR:
- case archive_file::error::FILE_TRUNCATED:
- case archive_file::error::FILE_CORRUPT:
- case archive_file::error::UNSUPPORTED:
- case archive_file::error::FILE_ERROR:
- filerr = osd_file::error::INVALID_DATA;
- break;
- case archive_file::error::BUFFER_TOO_SMALL:
- default:
- filerr = osd_file::error::FAILURE;
- break;
- }
- return filerr;
+ return std::error_condition();
}
@@ -338,27 +278,24 @@ osd_file::error file_error_from_zip_error(archive_file::error ziperr)
// from a zip file entry
// -------------------------------------------------
-osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
+std::error_condition create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file)
{
- osd_file::error filerr;
- archive_file::error ziperr;
+ // TODO: would be more efficient if we could open a memory-based core_file with uninitialised contents and decompress into it
+ std::error_condition filerr;
void *ptr = malloc(zip.current_uncompressed_length());
if (!ptr)
{
- filerr = osd_file::error::OUT_OF_MEMORY;
+ filerr = std::errc::not_enough_memory;
goto done;
}
- ziperr = zip.decompress(ptr, zip.current_uncompressed_length());
- if (ziperr != archive_file::error::NONE)
- {
- filerr = file_error_from_zip_error(ziperr);
+ filerr = zip.decompress(ptr, zip.current_uncompressed_length());
+ if (filerr)
goto done;
- }
filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file);
- if (filerr != osd_file::error::NONE)
+ if (filerr)
goto done;
done:
@@ -415,6 +352,9 @@ public:
m_zipfile(std::move(zipfile)),
m_zipprefix(std::move(zipprefix))
{
+ for (char &ch : m_zipprefix)
+ if (is_path_separator(ch))
+ ch = '/';
}
virtual bool is_archive() const override { return true; }
@@ -520,15 +460,15 @@ private:
bool m_called_zip_first = false;
archive_file::ptr const m_zipfile;
- std::string const m_zipprefix;
+ std::string m_zipprefix;
std::forward_list<std::string> m_returned_dirlist;
};
-class filesystem_diectory_impl : public zippath_directory_base
+class filesystem_directory_impl : public zippath_directory_base
{
public:
- filesystem_diectory_impl(std::string const &path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(path))
+ filesystem_directory_impl(std::string_view path) : zippath_directory_base(!is_root(path)), m_directory(osd::directory::open(std::string(path)))
{
}
@@ -574,7 +514,7 @@ private:
// zippath_directory::open - opens a directory
// -------------------------------------------------
-osd_file::error zippath_directory::open(std::string const &path, ptr &directory)
+std::error_condition zippath_directory::open(std::string_view path, ptr &directory)
{
try
{
@@ -582,34 +522,34 @@ osd_file::error zippath_directory::open(std::string const &path, ptr &directory)
osd::directory::entry::entry_type entry_type;
archive_file::ptr zipfile;
std::string zipprefix;
- osd_file::error const err = zippath_resolve(path.c_str(), entry_type, zipfile, zipprefix);
- if (osd_file::error::NONE != err)
+ std::error_condition const err = zippath_resolve(path, entry_type, zipfile, zipprefix);
+ if (err)
return err;
// we have to be a directory
if (osd::directory::entry::entry_type::DIR != entry_type)
- return osd_file::error::NOT_FOUND;
+ return std::errc::not_a_directory;
// was the result a ZIP?
if (zipfile)
{
directory = std::make_unique<archive_directory_impl>(std::move(zipfile), std::move(zipprefix));
- return osd_file::error::NONE;
+ return std::error_condition();
}
else
{
// a conventional directory
- std::unique_ptr<filesystem_diectory_impl> result(new filesystem_diectory_impl(path));
+ std::unique_ptr<filesystem_directory_impl> result(new filesystem_directory_impl(path));
if (!*result)
- return osd_file::error::FAILURE;
+ return std::errc::io_error; // TODO: any way to give a better error here?
directory = std::move(result);
- return osd_file::error::NONE;
+ return std::error_condition();
}
}
catch (std::bad_alloc const &)
{
- return osd_file::error::OUT_OF_MEMORY;
+ return std::errc::not_enough_memory;
}
}
@@ -628,69 +568,18 @@ zippath_directory::~zippath_directory()
// zippath_parent - retrieves the parent directory
// -------------------------------------------------
-std::string &zippath_parent(std::string &dst, const std::string &path)
+std::string zippath_parent(std::string_view path)
{
- std::string::size_type pos;
- parse_parent_path(path, &pos, nullptr);
-
- if (pos != std::string::npos)
- dst = path.substr(0, pos + 1);
- else
- dst.clear();
- return dst;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent - retrieves the parent directory
-// -------------------------------------------------
-
-std::string zippath_parent(const std::string &path)
-{
- std::string result;
- zippath_parent(result, path);
- return result;
-}
-
-
-
-// -------------------------------------------------
-// zippath_parent_basename - retrieves the parent
-// directory basename
-// -------------------------------------------------
-
-/**
- * @fn std::string &zippath_parent_basename(std::string &dst, const std::string &path)
- *
- * @brief Zippath parent basename.
- *
- * @param [in,out] dst Destination for the.
- * @param path Full pathname of the file.
- *
- * @return A std::string&amp;
- */
-
-std::string &zippath_parent_basename(std::string &dst, const std::string &path)
-{
- std::string::size_type beginpos, endpos;
- parse_parent_path(path, &beginpos, &endpos);
- dst.copy((char*)(path.c_str() + beginpos + 1), endpos - beginpos);
- return dst;
-}
-
-
+ // skip over trailing path separators
+ auto pos = std::find_if_not(path.rbegin(), path.rend(), &is_path_separator);
-// -------------------------------------------------
-// zippath_parent_basename - retrieves the parent
-// directory basename
-// -------------------------------------------------
+ // now skip until we find a path separator
+ pos = std::find_if(pos, path.rend(), &is_path_separator);
-std::string zippath_parent_basename(const std::string &path)
-{
- std::string result;
- zippath_parent_basename(result, path);
- return result;
+ if (path.rend() != pos)
+ return std::string(path.begin(), pos.base());
+ else
+ return std::string();
}
@@ -725,7 +614,7 @@ std::string &zippath_combine(std::string &dst, const std::string &path1, const s
{
dst.assign(path2);
}
- else if (!path1.empty() && !is_path_separator(*path1.rbegin()))
+ else if (!path1.empty() && !is_path_separator(path1.back()))
{
dst.assign(path1).append(PATH_SEPARATOR).append(path2);
}
@@ -760,7 +649,7 @@ std::string zippath_combine(const std::string &path1, const std::string &path2)
// -------------------------------------------------
/**
- * @fn osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
+ * @fn std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
*
* @brief Zippath fopen.
*
@@ -772,76 +661,84 @@ std::string zippath_combine(const std::string &path1, const std::string &path2)
* @return A osd_file::error.
*/
-osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
+std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path)
{
- osd_file::error filerr = osd_file::error::NOT_FOUND;
- archive_file::error ziperr;
+ std::error_condition filerr = std::errc::no_such_file_or_directory;
archive_file::ptr zip;
- int header;
- osd::directory::entry::entry_type entry_type;
- int len;
- /* first, set up the two types of paths */
+ // first, set up the two types of paths
std::string mainpath(filename);
std::string subpath;
file = nullptr;
- /* loop through */
- while((file == nullptr) && (mainpath.length() > 0)
- && ((openflags == OPEN_FLAG_READ) || (subpath.length() == 0)))
+ // loop through
+ while (!file && !mainpath.empty())
{
- /* is the mainpath a ZIP path? */
+ // is the mainpath a ZIP path?
if (is_zip_file(mainpath) || is_7z_file(mainpath))
{
- /* this file might be a zip file - lets take a look */
- ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
- if (ziperr == archive_file::error::NONE)
+ // this file might be a zip file - lets take a look
+ std::error_condition const ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip);
+ if (!ziperr)
{
- /* it is a zip file - error if we're not opening for reading */
- if (openflags != OPEN_FLAG_READ)
+ osd::directory::entry::entry_type entry_type;
+ int header;
+ if (!subpath.empty())
{
- filerr = osd_file::error::ACCESS_DENIED;
- goto done;
- }
-
- if (subpath.length() > 0)
header = zippath_find_sub_path(*zip, subpath, entry_type);
+ }
else
+ {
header = zip->first_file();
+ entry_type = osd::directory::entry::entry_type::FILE;
+ }
if (header < 0)
{
- filerr = osd_file::error::NOT_FOUND;
+ if (openflags & OPEN_FLAG_CREATE)
+ filerr = std::errc::permission_denied;
+ else
+ filerr = std::errc::no_such_file_or_directory;
+ goto done;
+ }
+ else if (osd::directory::entry::entry_type::DIR == entry_type)
+ {
+ filerr = std::errc::is_a_directory;
+ goto done;
+ }
+ else if (openflags & OPEN_FLAG_WRITE)
+ {
+ filerr = std::errc::permission_denied;
goto done;
}
- /* attempt to read the file */
+ // attempt to read the file
filerr = create_core_file_from_zip(*zip, file);
- if (filerr != osd_file::error::NONE)
+ if (filerr)
goto done;
- /* update subpath, if appropriate */
- if (subpath.length() == 0)
+ // update subpath, if appropriate
+ if (subpath.empty())
subpath.assign(zip->current_name());
- /* we're done */
+ // we're done
goto done;
}
}
- if (subpath.length() == 0)
+ if (subpath.empty())
filerr = util::core_file::open(filename, openflags, file);
else
- filerr = osd_file::error::NOT_FOUND;
+ filerr = std::errc::no_such_file_or_directory;
- /* if we errored, then go up a directory */
- if (filerr != osd_file::error::NONE)
+ // if we errored, then go up a directory
+ if (filerr)
{
- /* go up a directory */
+ // go up a directory
auto temp = zippath_parent(mainpath);
- /* append to the sub path */
- if (subpath.length() > 0)
+ // append to the sub path
+ if (!subpath.empty())
{
std::string temp2;
mainpath = mainpath.substr(temp.length());
@@ -853,8 +750,8 @@ osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, u
mainpath = mainpath.substr(temp.length());
subpath.assign(mainpath);
}
- /* get the new main path, truncating path separators */
- len = temp.length();
+ // get the new main path, truncating path separators
+ auto len = temp.length();
while (len > 0 && is_zip_file_separator(temp[len - 1]))
len--;
temp = temp.substr(0, len);
@@ -863,17 +760,17 @@ osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, u
}
done:
- /* store the revised path */
+ // store the revised path
revised_path.clear();
- if (filerr == osd_file::error::NONE)
+ if (!filerr)
{
- /* canonicalize mainpath */
+ // canonicalize mainpath
std::string alloc_fullpath;
filerr = osd_get_full_path(alloc_fullpath, mainpath);
- if (filerr == osd_file::error::NONE)
+ if (!filerr)
{
revised_path = alloc_fullpath;
- if (subpath.length() > 0)
+ if (!subpath.empty())
revised_path.append(PATH_SEPARATOR).append(subpath);
}
}
diff --git a/src/lib/util/zippath.h b/src/lib/util/zippath.h
index cf6c2249f9c..955c75cfc8f 100644
--- a/src/lib/util/zippath.h
+++ b/src/lib/util/zippath.h
@@ -17,6 +17,8 @@
#include "unzip.h"
#include <string>
+#include <string_view>
+#include <system_error>
namespace util {
@@ -31,7 +33,7 @@ public:
typedef std::unique_ptr<zippath_directory> ptr;
// opens a directory
- static osd_file::error open(std::string const &path, ptr &directory);
+ static std::error_condition open(std::string_view path, ptr &directory);
// closes a directory
virtual ~zippath_directory();
@@ -52,12 +54,7 @@ public:
// ----- path operations -----
// retrieves the parent directory
-std::string &zippath_parent(std::string &dst, const std::string &path);
-std::string zippath_parent(const std::string &path);
-
-// retrieves the parent directory basename
-std::string &zippath_parent_basename(std::string &dst, const std::string &path);
-std::string zippath_parent_basename(const std::string &path);
+std::string zippath_parent(std::string_view path);
// combines two paths
std::string &zippath_combine(std::string &dst, const std::string &path1, const std::string &path2);
@@ -67,7 +64,7 @@ std::string zippath_combine(const std::string &path1, const std::string &path2);
// ----- file operations -----
// opens a zip path file
-osd_file::error zippath_fopen(const std::string &filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path);
+std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path);
} // namespace util