summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/aviio.cpp2
-rw-r--r--src/lib/util/bitstream.h68
-rw-r--r--src/lib/util/cdrom.cpp1766
-rw-r--r--src/lib/util/cdrom.h81
-rw-r--r--src/lib/util/chd.cpp1351
-rw-r--r--src/lib/util/chd.h53
-rw-r--r--src/lib/util/chdcodec.cpp87
-rw-r--r--src/lib/util/chdcodec.h7
-rw-r--r--src/lib/util/corefile.cpp124
-rw-r--r--src/lib/util/corefile.h2
-rw-r--r--src/lib/util/corestr.cpp1
-rw-r--r--src/lib/util/coretmpl.h2
-rw-r--r--src/lib/util/coreutil.cpp12
-rw-r--r--src/lib/util/coreutil.h7
-rw-r--r--src/lib/util/delegate.cpp208
-rw-r--r--src/lib/util/dvdrom.cpp4
-rw-r--r--src/lib/util/flac.cpp47
-rw-r--r--src/lib/util/flac.h10
-rw-r--r--src/lib/util/harddisk.cpp7
-rw-r--r--src/lib/util/hash.cpp5
-rw-r--r--src/lib/util/ioprocs.cpp201
-rw-r--r--src/lib/util/ioprocs.h127
-rw-r--r--src/lib/util/ioprocsfill.h65
-rw-r--r--src/lib/util/ioprocsfilter.cpp28
-rw-r--r--src/lib/util/ioprocsvec.h8
-rw-r--r--src/lib/util/jedparse.cpp24
-rw-r--r--src/lib/util/language.cpp7
-rw-r--r--src/lib/util/mfpresolve.cpp431
-rw-r--r--src/lib/util/mfpresolve.h151
-rw-r--r--src/lib/util/msdib.cpp26
-rw-r--r--src/lib/util/options.h2
-rw-r--r--src/lib/util/plaparse.cpp40
-rw-r--r--src/lib/util/png.cpp62
-rw-r--r--src/lib/util/server_http_impl.hpp8
-rw-r--r--src/lib/util/simh_tape_file.cpp6
-rw-r--r--src/lib/util/un7z.cpp3
-rw-r--r--src/lib/util/unzip.cpp45
-rw-r--r--src/lib/util/xmlfile.cpp18
38 files changed, 2950 insertions, 2146 deletions
diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp
index 40ce6ebe689..7b5e8d00d87 100644
--- a/src/lib/util/aviio.cpp
+++ b/src/lib/util/aviio.cpp
@@ -48,7 +48,7 @@ static constexpr std::uint64_t FOUR_GB = std::uint64_t(1) << 32;
* @brief A constant that defines maximum sound channels.
*/
-static constexpr unsigned MAX_SOUND_CHANNELS = 2;
+static constexpr unsigned MAX_SOUND_CHANNELS = 16;
/**
* @def SOUND_BUFFER_MSEC
diff --git a/src/lib/util/bitstream.h b/src/lib/util/bitstream.h
index 4f3817afe6f..eb8b2005329 100644
--- a/src/lib/util/bitstream.h
+++ b/src/lib/util/bitstream.h
@@ -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 36a51104d3c..ab227dce1eb 100644
--- a/src/lib/util/cdrom.cpp
+++ b/src/lib/util/cdrom.cpp
@@ -21,10 +21,12 @@
#include "corestr.h"
#include "multibyte.h"
#include "osdfile.h"
+#include "path.h"
#include "strformat.h"
#include <cassert>
#include <cstdlib>
+#include <tuple>
/***************************************************************************
@@ -34,7 +36,6 @@
/** @brief The verbose. */
#define VERBOSE (0)
#define EXTRA_VERBOSE (0)
-#if VERBOSE
/**
* @def LOG(x) do
@@ -44,31 +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
+#define LOG(x) do { if (VERBOSE) { osd_printf_info x; } } while (0)
@@ -159,7 +136,7 @@ cdrom_file::cdrom_file(std::string_view inputfile)
std::error_condition err = parse_toc(inputfile, cdtoc, cdtrack_info);
if (err)
{
- fprintf(stderr, "Error reading input file: %s\n", err.message().c_str());
+ osd_printf_error("Error reading input file: %s\n", err.message());
throw nullptr;
}
@@ -175,13 +152,13 @@ cdrom_file::cdrom_file(std::string_view inputfile)
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", cdtrack_info.track[i].fname.c_str());
+ 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])
{
- fprintf(stderr, "Unable to open file: %s\n", cdtrack_info.track[i].fname.c_str());
+ osd_printf_error("Unable to open file: %s\n", cdtrack_info.track[i].fname);
throw nullptr;
}
}
@@ -204,6 +181,9 @@ cdrom_file::cdrom_file(std::string_view inputfile)
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;
@@ -215,22 +195,30 @@ cdrom_file::cdrom_file(std::string_view inputfile)
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)
- 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 logframes %d\n", 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);
+ {
+ 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
@@ -313,21 +301,26 @@ cdrom_file::cdrom_file(chd_file *_chd)
logofs += track.frames;
if (EXTRA_VERBOSE)
- 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 logframes %d\n", 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);
+ {
+ 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
@@ -387,7 +380,7 @@ std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasec
if ((cdtoc.tracks[tracknum].pgdatasize == 0) && (lbasector < cdtoc.tracks[tracknum].logframeofs))
{
if (EXTRA_VERBOSE)
- printf("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs);
+ osd_printf_verbose("PG missing sector: LBA %d, trklog %d\n", lbasector, cdtoc.tracks[tracknum].logframeofs);
memset(dest, 0, length);
return result;
}
@@ -423,13 +416,13 @@ std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasec
sourcefileoffset += chdsector * bytespersector + startoffs;
if (EXTRA_VERBOSE)
- printf("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset);
+ osd_printf_verbose("Reading %u bytes from sector %d from track %d at offset %lu\n", (unsigned)length, chdsector, tracknum + 1, (unsigned long)sourcefileoffset);
- size_t actual;
result = srcfile.seek(sourcefileoffset, SEEK_SET);
+ size_t actual;
if (!result)
- result = srcfile.read(dest, length, actual);
- // FIXME: if (actual < length) report error
+ std::tie(result, actual) = read(srcfile, dest, length);
+ // FIXME: if (!result && (actual < length)) report error
needswap = cdtrack_info.track[tracknum].swap;
}
@@ -437,9 +430,10 @@ std::error_condition cdrom_file::read_partial_sector(void *dest, uint32_t lbasec
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)
{
- std::swap(buffer[ swapindex ], buffer[ swapindex + 1 ]);
+ using std::swap;
+ swap(buffer[ swapindex ], buffer[ swapindex + 1 ]);
}
}
return result;
@@ -604,6 +598,27 @@ uint32_t cdrom_file::get_track(uint32_t frame) const
return track;
}
+uint32_t cdrom_file::get_track_index(uint32_t frame) const
+{
+ 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;
+
+ for (int i = 0; i < std::size(cdtrack_info.track[track].idx); i++)
+ {
+ if (index_offset >= cdtrack_info.track[track].idx[i])
+ index = i;
+ else
+ break;
+ }
+
+ if (cdtrack_info.track[track].idx[index] == -1)
+ index = 1; // valid index not found, default to index 1
+
+ return index;
+}
+
/***************************************************************************
EXTRA UTILITIES
@@ -682,11 +697,6 @@ void cdrom_file::get_info_from_type_string(const char *typestring, uint32_t *trk
*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;
@@ -891,91 +901,78 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
std::string metadata;
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 < 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];
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)
+ // 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 chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
+ }
+ 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;
- track = &toc.tracks[tracknum - 1];
}
else
{
- err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc.numtrks, metadata);
+ // fall through to GD-ROM detection
+ err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
if (!err)
- {
- /* 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 chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
- return chd_file::error::INVALID_DATA;
- track = &toc.tracks[tracknum - 1];
- }
+ toc.flags |= CD_FLAG_GDROMLE; // legacy GDROM track was detected
else
- {
- err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc.numtrks, metadata);
- if (!err)
- /* 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)
- {
- /* 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 chd_file::error::INVALID_DATA;
- if (tracknum == 0 || tracknum > MAX_TRACKS)
- return chd_file::error::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;
convert_type_string_to_track_info(type, track);
if (track->datasize == 0)
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;
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 + 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;
@@ -999,7 +996,7 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
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;
@@ -1011,8 +1008,11 @@ std::error_condition cdrom_file::parse_metadata(chd_file *chd, toc &toc)
auto *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]);
toc.numtrks = *mrp++;
+ toc.numsessions = 1;
+
for (int i = 0; i < MAX_TRACKS; i++)
{
+ toc.tracks[i].session = 0;
toc.tracks[i].trktype = *mrp++;
toc.tracks[i].subtype = *mrp++;
toc.tracks[i].datasize = *mrp++;
@@ -1070,7 +1070,16 @@ std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
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];
@@ -1090,15 +1099,6 @@ std::error_condition cdrom_file::write_metadata(chd_file *chd, const toc &toc)
toc.tracks[i].postgap);
err = chd->write_metadata(CDROM_TRACK_METADATA2_TAG, i, metadata);
}
- else
- {
- 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);
- }
if (err)
return err;
}
@@ -1440,14 +1440,14 @@ void cdrom_file::ecc_clear(uint8_t *sector)
*
* @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)
+ * @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, sizeof(linebuffer), token, sizeof(token) );
+#define TOKENIZE i = tokenize( linebuffer, i, std::size(linebuffer), token, std::size(token) );
/***************************************************************************
@@ -1467,9 +1467,12 @@ void cdrom_file::ecc_clear(uint8_t *sector)
std::string cdrom_file::get_file_path(std::string &path)
{
int pos = path.find_last_of('\\');
- if (pos!=-1) {
+ if (pos!=-1)
+ {
path = path.substr(0,pos+1);
- } else {
+ }
+ else
+ {
pos = path.find_last_of('/');
path = path.substr(0,pos+1);
}
@@ -1518,20 +1521,20 @@ uint64_t cdrom_file::get_file_size(std::string_view filename)
* @return An int.
*/
-int cdrom_file::tokenize( const char *linebuffer, int i, int linebuffersize, char *token, int tokensize )
+int cdrom_file::tokenize(const char *linebuffer, int i, int linebuffersize, char *token, int tokensize)
{
int j = 0;
- int singlequote = 0;
- int doublequote = 0;
+ bool singlequote = false;
+ bool doublequote = false;
while ((i < linebuffersize) && isspace((uint8_t)linebuffer[i]))
{
i++;
}
- while ((i < linebuffersize) && (j < tokensize))
+ while ((i < linebuffersize) && (j < tokensize) && (linebuffer[i] != '\0'))
{
- if (!singlequote && linebuffer[i] == '"' )
+ if (!singlequote && linebuffer[i] == '"')
{
doublequote = !doublequote;
}
@@ -1572,19 +1575,19 @@ int cdrom_file::tokenize( const char *linebuffer, int i, int linebuffersize, cha
* @return An int.
*/
-int cdrom_file::msf_to_frames( char *token )
+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 )
+ if (sscanf(token, "%d:%d:%d", &m, &s, &f) == 1)
{
f = m;
}
else
{
- /* convert to just frames */
+ // convert to just frames
s += (m * 60);
f += (s * 75);
}
@@ -1624,7 +1627,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
std::error_condition const filerr = osd_file::open(fname, OPEN_FLAG_READ, file, fsize);
if (filerr)
{
- printf("ERROR: could not open (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: could not open (%s)\n", fname);
return 0;
}
@@ -1633,12 +1636,12 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
offset += actual;
if (offset < 4)
{
- printf("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname.c_str());
+ osd_printf_error("ERROR: unexpected RIFF offset %lu (%s)\n", offset, fname);
return 0;
}
if (memcmp(&buf[0], "RIFF", 4) != 0)
{
- printf("ERROR: could not find RIFF header (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: could not find RIFF header (%s)\n", fname);
return 0;
}
@@ -1647,7 +1650,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
offset += actual;
if (offset < 8)
{
- printf("ERROR: unexpected size offset %lu (%s)\n", offset, fname.c_str());
+ osd_printf_error("ERROR: unexpected size offset %lu (%s)\n", offset, fname);
return 0;
}
filesize = little_endianize_int32(filesize);
@@ -1657,12 +1660,12 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
offset += actual;
if (offset < 12)
{
- printf("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname.c_str());
+ osd_printf_error("ERROR: unexpected WAVE offset %lu (%s)\n", offset, fname);
return 0;
}
if (memcmp(&buf[0], "WAVE", 4) != 0)
{
- printf("ERROR: could not find WAVE header (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: could not find WAVE header (%s)\n", fname);
return 0;
}
@@ -1681,7 +1684,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
offset += length;
if (offset >= filesize)
{
- printf("ERROR: could not find fmt tag (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: could not find fmt tag (%s)\n", fname);
return 0;
}
}
@@ -1692,7 +1695,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
temp16 = little_endianize_int16(temp16);
if (temp16 != 1)
{
- printf("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname.c_str());
+ osd_printf_error("ERROR: unsupported format %u - only PCM is supported (%s)\n", temp16, fname);
return 0;
}
@@ -1702,7 +1705,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
temp16 = little_endianize_int16(temp16);
if (temp16 != 2)
{
- printf("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname.c_str());
+ osd_printf_error("ERROR: unsupported number of channels %u - only stereo is supported (%s)\n", temp16, fname);
return 0;
}
@@ -1712,7 +1715,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
rate = little_endianize_int32(rate);
if (rate != 44100)
{
- printf("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname.c_str());
+ osd_printf_error("ERROR: unsupported samplerate %u - only 44100 is supported (%s)\n", rate, fname);
return 0;
}
@@ -1726,7 +1729,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
bits = little_endianize_int16(bits);
if (bits != 16)
{
- printf("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname.c_str());
+ osd_printf_error("ERROR: unsupported bits/sample %u - only 16 is supported (%s)\n", bits, fname);
return 0;
}
@@ -1748,7 +1751,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
offset += length;
if (offset >= filesize)
{
- printf("ERROR: could not find data tag (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: could not find data tag (%s)\n", fname);
return 0;
}
}
@@ -1756,7 +1759,7 @@ uint32_t cdrom_file::parse_wav_sample(std::string_view filename, uint32_t *datao
/* if there was a 0 length data block, we're done */
if (length == 0)
{
- printf("ERROR: empty data block (%s)\n", fname.c_str());
+ osd_printf_error("ERROR: empty data block (%s)\n", fname);
return 0;
}
@@ -1858,13 +1861,15 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
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))
{
- printf("ERROR: Not a Nero 5.5 or later image!\n");
+ osd_printf_error("ERROR: Not a Nero 5.5 or later image!\n");
fclose(infile);
return chd_file::error::UNSUPPORTED_FORMAT;
}
@@ -1873,7 +1878,7 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
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.");
+ osd_printf_error("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it.");
fclose(infile);
return chd_file::error::UNSUPPORTED_FORMAT;
}
@@ -1920,8 +1925,7 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
// 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;
+ outinfo.track[track-1].idx[0] = outinfo.track[track-1].idx[1] = 0;
switch (mode)
{
@@ -1931,12 +1935,12 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
break;
case 0x0300: // Mode 2 Form 1
- printf("ERROR: Mode 2 Form 1 tracks not supported\n");
+ osd_printf_error("ERROR: Mode 2 Form 1 tracks not supported\n");
fclose(infile);
return chd_file::error::UNSUPPORTED_FORMAT;
case 0x0500: // raw data
- printf("ERROR: Raw data tracks not supported\n");
+ osd_printf_error("ERROR: Raw data tracks not supported\n");
fclose(infile);
return chd_file::error::UNSUPPORTED_FORMAT;
@@ -1951,22 +1955,22 @@ std::error_condition cdrom_file::parse_nero(std::string_view tocfname, toc &outt
break;
case 0x0f00: // raw data with sub-channel
- printf("ERROR: Raw data tracks with sub-channel not supported\n");
+ 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
- printf("ERROR: Audio tracks with sub-channel not supported\n");
+ 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
- printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n");
+ 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:
- printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
+ osd_printf_error("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode);
fclose(infile);
return chd_file::error::UNSUPPORTED_FORMAT;
}
@@ -2041,31 +2045,38 @@ std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outto
outtoc.numtrks = 1;
+ outtoc.numsessions = 1;
outinfo.track[0].fname = tocfname;
outinfo.track[0].offset = 0;
- outinfo.track[0].idx0offs = 0;
- outinfo.track[0].idx1offs = 0;
+ outinfo.track[0].idx[0] = outinfo.track[0].idx[1] = 0;
- if ((size % 2048)==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 ) {
+ }
+ 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 ) {
+ }
+ 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");
+ }
+ else
+ {
+ osd_printf_error("ERROR: Unrecognized track type\n");
return chd_file::error::UNSUPPORTED_FORMAT;
}
@@ -2103,7 +2114,9 @@ std::error_condition cdrom_file::parse_iso(std::string_view tocfname, toc &outto
std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
- int i, numtracks;
+ char token[512];
+ int i = 0;
+ int trackcnt = 0;
std::string path = std::string(tocfname);
@@ -2122,89 +2135,172 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
outtoc.flags = CD_FLAG_GDROM;
char linebuffer[512];
- fgets(linebuffer,511,infile);
- numtracks=atoi(linebuffer);
+ 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;
+ }
- for(i=0;i<numtracks;++i)
+ if (numtracks == 0)
{
- char *tok;
- int trknum;
- int trksize,trktype;
- int sz;
+ osd_printf_error("GDI header specifies no tracks\n");
+ return chd_file::error::INVALID_DATA;
+ }
- fgets(linebuffer,511,infile);
+ while (!feof(infile))
+ {
+ int paramcnt = 0;
- tok=strtok(linebuffer," ");
+ if (!fgets(linebuffer,511,infile))
+ break;
- trknum=atoi(tok)-1;
+ i = 0;
+ TOKENIZE
- outinfo.track[trknum].swap=false;
- outinfo.track[trknum].offset=0;
+ // 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;
- tok=strtok(nullptr," ");
- outtoc.tracks[trknum].physframeofs=atoi(tok);
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ outtoc.tracks[trknum].physframeofs = atoi(token);
- tok=strtok(nullptr," ");
- trktype=atoi(tok);
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trktype = atoi(token);
- tok=strtok(nullptr," ");
- trksize=atoi(tok);
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+ const int trksize = atoi(token);
- if(trktype==4 && trksize==2352)
+ if (trktype == 4 && trksize == 2352)
{
- outtoc.tracks[trknum].trktype=CD_TRACK_MODE1_RAW;
- outtoc.tracks[trknum].datasize=2352;
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1_RAW;
+ outtoc.tracks[trknum].datasize = 2352;
}
- if(trktype==4 && trksize==2048)
+ else if (trktype == 4 && trksize == 2048)
{
- outtoc.tracks[trknum].trktype=CD_TRACK_MODE1;
- outtoc.tracks[trknum].datasize=2048;
+ outtoc.tracks[trknum].trktype = CD_TRACK_MODE1;
+ outtoc.tracks[trknum].datasize = 2048;
}
- if(trktype==0)
+ else if (trktype == 0)
{
- outtoc.tracks[trknum].trktype=CD_TRACK_AUDIO;
- outtoc.tracks[trknum].datasize=2352;
+ 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;
+ }
- std::string name;
+ // skip to start of next token
+ int pi = i;
+ while (pi < std::size(linebuffer) && isspace((uint8_t)linebuffer[pi]))
+ pi++;
- 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,'"');
+ 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;
}
- outinfo.track[trknum].fname.assign(path).append(name);
- sz = get_file_size(outinfo.track[trknum].fname);
+ TOKENIZE
+ if (token[0])
+ paramcnt++;
+
+ outinfo.track[trknum].fname.assign(path).append(token);
- outtoc.tracks[trknum].frames = sz/trksize;
+ 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)
{
- int dif=outtoc.tracks[trknum].physframeofs-(outtoc.tracks[trknum-1].frames+outtoc.tracks[trknum-1].physframeofs);
+ 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(i=0; i < numtracks; i++)
+ for (int 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);
+ 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 */
@@ -2212,12 +2308,13 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
/* store the number of tracks found */
outtoc.numtrks = numtracks;
+ outtoc.numsessions = 1;
return std::error_condition();
}
/*-------------------------------------------------
- parse_cue - parse a CDRWin format CUE file
+ parse_cue - parse a .CUE file
-------------------------------------------------*/
/**
@@ -2230,15 +2327,23 @@ std::error_condition cdrom_file::parse_gdi(std::string_view tocfname, toc &outto
* @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;
- static char token[512];
+ 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)
@@ -2254,554 +2359,289 @@ std::error_condition cdrom_file::parse_cue(std::string_view tocfname, toc &outto
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 */
- fgets(linebuffer, 511, infile);
+ if (!fgets(linebuffer, 511, infile))
+ break;
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
- {
- i = 0;
+ i = 0;
- TOKENIZE
+ TOKENIZE
+
+ if (!strcmp(token, "REM"))
+ {
+ /* skip to actual data of REM command */
+ while (i < std::size(linebuffer) && isspace((uint8_t)linebuffer[i]))
+ i++;
- if (!strcmp(token, "FILE"))
+ if (!strncmp(linebuffer+i, "SESSION", 7))
{
- /* found the data file for a track */
+ /* IsoBuster extension */
TOKENIZE
- /* keep the filename */
- lastfname.assign(path).append(token);
-
- /* get the file type */
+ /* get the session number */
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);
- printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str());
- return chd_file::error::INVALID_DATA;
- }
- }
- else
- {
- fclose(infile);
- printf("ERROR: Unhandled track type %s\n", token);
- return chd_file::error::UNSUPPORTED_FORMAT;
- }
+ 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 (!strcmp(token, "TRACK"))
+ else if ((outtoc.flags & CD_FLAG_MULTISESSION) && !strncmp(linebuffer+i, "PREGAP", 6))
{
- /* get the track number */
- TOKENIZE
- trknum = strtoul(token, nullptr, 10) - 1;
+ /*
+ Redump extension? PREGAP associated with the session instead of the track
- /* next token on the line is the track type */
+ 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
- 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);
-
- 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 chd_file::error::UNSUPPORTED_FORMAT;
- }
-
- /* next (optional) token on the line is the subcode type */
+ /* get pregap time */
TOKENIZE
-
- convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ session_pregap = msf_to_frames(token);
}
- else if (!strcmp(token, "INDEX")) /* only in bin/cue files */
+ else if (!strncmp(linebuffer+i, "LEAD-OUT", 8))
{
- int idx, frames;
+ /*
+ 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
- /* get index number */
+ Redump and DiscImageCreator (multiple bins) - Lead-out time is the duration of just the lead-out
+ */
TOKENIZE
- idx = strtoul(token, nullptr, 10);
- /* get index */
+ /* get lead-out time */
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;
- }
- }
+ int leadout_offset = msf_to_frames(token);
+ outinfo.track[trknum].leadout = leadout_offset;
}
- else if (!strcmp(token, "PREGAP"))
+ else if (!strncmp(linebuffer+i, "LEAD-IN", 7))
{
- int frames;
-
- /* get index */
+ /*
+ 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
- frames = msf_to_frames( token );
- outtoc.tracks[trknum].pregap = frames;
+ /* get lead-in time */
+ TOKENIZE
+ leadin = msf_to_frames(token);
}
- else if (!strcmp(token, "POSTGAP"))
+ else if (is_gdrom && !strncmp(linebuffer+i, "SINGLE-DENSITY AREA", 19))
{
- int frames;
-
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
-
- outtoc.tracks[trknum].postgap = frames;
+ /* 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;
}
}
- }
-
- /* 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)
+ else if (!strcmp(token, "FILE"))
{
- outinfo.track[trknum].swap = true;
- }
+ /* found the data file for a track */
+ TOKENIZE
- // 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))
+ /* keep the filename */
+ if (!is_multibin)
{
- /* 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);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- 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 /* data files are different */
- {
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return std::errc::no_such_file_or_directory;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
- }
+ std::string prevfname(std::move(lastfname));
+ lastfname.assign(path).append(token);
+ is_multibin = !prevfname.empty() && lastfname != prevfname;
}
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;
+ lastfname.assign(path).append(token);
+ }
- 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);
- }
+ /* get the file type */
+ TOKENIZE
- if (!outtoc.tracks[trknum].frames)
- {
- printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
- return chd_file::error::INVALID_DATA;
- }
- }
- else /* data files are different */
+ 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)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str());
- return std::errc::no_such_file_or_directory;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
+ 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;
+ }
}
- //printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset);
- }
-
- 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)
-{
- 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];
- while (!feof(infile))
- {
- fgets(linebuffer, 511, infile);
-
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
+ else if (!strcmp(token, "TRACK"))
{
- has_rem_singledensity = has_rem_singledensity || !strncmp(linebuffer, "REM SINGLE-DENSITY AREA", 23);
- has_rem_highdensity = has_rem_highdensity || !strncmp(linebuffer, "REM HIGH-DENSITY AREA", 21);
- }
- }
-
- fclose(infile);
-
- return has_rem_singledensity && has_rem_highdensity;
-}
-
-/*-----------------------------------------------------------------
- parse_gdicue - parse a Redump multi-CUE for Dreamcast GDI
-------------------------------------------------------------------*/
-
-/**
- * @fn std::error_condition parse_gdicue(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.
- *
- * 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.
- *
- * There are three Dreamcast disc patterns.
- *
- * Pattern I - (SD) DATA + AUDIO, (HD) DATA
- * Pattern II - (SD) DATA + AUDIO, (HD) DATA + ... + AUDIO
- * Pattern III - (SD) DATA + AUDIO, (HD) DATA + ... + DATA
- *
- * TOSEC layout is preferred and this code adjusts the TOC and INFO generated by a Redump .cue to match the
- * layout from a TOSEC .gdi.
- */
-
-std::error_condition cdrom_file::parse_gdicue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
-{
- int i, trknum;
- static char token[512];
- std::string lastfname;
- uint32_t wavlen, wavoffs;
- std::string path = std::string(tocfname);
- enum gdi_area current_area = SINGLE_DENSITY;
- enum gdi_pattern disc_pattern = TYPE_UNKNOWN;
-
- 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;
-
- outtoc.flags = CD_FLAG_GDROM;
+ /* get the track number */
+ TOKENIZE
+ trknum = strtoul(token, nullptr, 10) - 1;
- char linebuffer[512];
- while (!feof(infile))
- {
- /* get the next line */
- fgets(linebuffer, 511, infile);
+ /* next token on the line is the track type */
+ TOKENIZE
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
- {
- /* single-density area starts LBA = 0 */
- if (!strncmp(linebuffer, "REM SINGLE-DENSITY AREA", 23))
+ 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)
{
- current_area = SINGLE_DENSITY;
- continue;
+ /*
+ 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;
}
- /* high-density area starts LBA = 45000 */
- if (!strncmp(linebuffer, "REM HIGH-DENSITY AREA", 21))
+ if (wavlen != 0)
{
- current_area = HIGH_DENSITY;
- continue;
+ outtoc.tracks[trknum].frames = wavlen/2352;
+ outinfo.track[trknum].offset = wavoffs;
+ wavoffs = wavlen = 0;
}
- i = 0;
-
- TOKENIZE
+ outinfo.track[trknum].fname.assign(lastfname); /* default filename to the last one */
- if (!strcmp(token, "FILE"))
+ if (EXTRA_VERBOSE)
{
- /* 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"))
+ if (is_gdrom)
{
- outinfo.track[trknum+1].swap = true;
- }
- else if (!strcmp(token, "WAVE"))
- {
- wavlen = parse_wav_sample(lastfname, &wavoffs);
- if (!wavlen)
- {
- fclose(infile);
- printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str());
- return chd_file::error::INVALID_DATA;
- }
+ 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
{
- fclose(infile);
- printf("ERROR: Unhandled track type %s\n", token);
- return chd_file::error::UNSUPPORTED_FORMAT;
+ osd_printf_verbose("trk %d: fname %s offset %d\n", trknum, outinfo.track[trknum].fname, outinfo.track[trknum].offset);
}
}
- else if (!strcmp(token, "TRACK"))
+
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
{
- /* get the track number */
- TOKENIZE
- trknum = strtoul(token, nullptr, 10) - 1;
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
- /* next token on the line is the track type */
- TOKENIZE
+ /* next (optional) token on the line is the subcode 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;
- outtoc.tracks[trknum].multicuearea = current_area;
- outinfo.track[trknum].idx0offs = -1;
- outinfo.track[trknum].idx1offs = 0;
-
- outinfo.track[trknum].fname.assign(lastfname); // default filename to the last one
-
- if (EXTRA_VERBOSE)
- printf("trk %d: fname %s offset %d area %d\n", trknum, outinfo.track[trknum].fname.c_str(), outinfo.track[trknum].offset, outtoc.tracks[trknum].multicuearea);
-
- 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 chd_file::error::UNSUPPORTED_FORMAT;
- }
+ convert_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
+ }
+ else if (!strcmp(token, "INDEX"))
+ {
+ int idx, frames;
- /* next (optional) token on the line is the subcode type */
- TOKENIZE
+ /* get index number */
+ TOKENIZE
+ idx = strtoul(token, nullptr, 10);
- 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 */
+ TOKENIZE
+ frames = msf_to_frames(token);
- /* get index number */
- TOKENIZE
- idx = strtoul(token, nullptr, 10);
+ if (idx < 0 || idx > MAX_INDEX)
+ {
+ osd_printf_error("ERROR: encountered invalid index %d\n", idx);
+ return chd_file::error::INVALID_DATA;
+ }
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
+ outinfo.track[trknum].idx[idx] = frames;
- if (idx == 0)
+ if (idx == 1)
+ {
+ if (outtoc.tracks[trknum].pregap == 0 && outinfo.track[trknum].idx[0] != -1)
{
- outinfo.track[trknum].idx0offs = frames;
+ 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 (idx == 1)
+ 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].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;
- }
+ outinfo.track[trknum].idx[0] = frames;
}
}
- else if (!strcmp(token, "PREGAP"))
- {
- int frames;
+ }
+ else if (!strcmp(token, "PREGAP"))
+ {
+ int frames;
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
+ /* get index */
+ TOKENIZE
+ frames = msf_to_frames(token);
- outtoc.tracks[trknum].pregap = frames;
- }
- else if (!strcmp(token, "POSTGAP"))
+ 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 frames;
+ int last_idx = i;
- /* get index */
TOKENIZE
- frames = msf_to_frames( token );
- outtoc.tracks[trknum].postgap = frames;
+ 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;
}
}
}
@@ -2811,164 +2651,255 @@ std::error_condition cdrom_file::parse_gdicue(std::string_view tocfname, toc &ou
/* 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;
- // this is true for cue/bin and cue/iso, and we need it for cue/wav since .WAV is little-endian
+ 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)
+ /* 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)
{
- // is this the last track?
- if (trknum == (outtoc.numtrks-1))
+ /* 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)
{
- /* 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))
+ 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)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- 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);
+ /* 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) */
}
- else /* data files are different */
+
+ if (outinfo.track[trknum].leadin == -1 && trknum > 0 && outtoc.tracks[trknum].session != outtoc.tracks[trknum-1].session)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
- {
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str());
- return std::errc::no_such_file_or_directory;
- }
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
+ /* add a standard lead-in to the first track of a new session */
+ outinfo.track[trknum].leadin = 4500; /* lead-in (1m0s0f) */
}
}
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)
+ if (outinfo.track[trknum].leadout != -1)
{
- outtoc.tracks[trknum].frames = outinfo.track[trknum+1].idx0offs - outinfo.track[trknum].idx0offs;
-
- if (trknum == 0) // track 0 offset is 0
+ /*
+ 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)
{
- 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);
- }
+ outtoc.tracks[trknum].frames = endframes; /* trim track length */
- if (!outtoc.tracks[trknum].frames)
- {
- printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1);
- return chd_file::error::INVALID_DATA;
+ 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;
+ }
}
}
- else /* data files are different */
+
+ if (trknum > 0 && outinfo.track[trknum-1].leadout != -1)
{
- tlen = get_file_size(outinfo.track[trknum].fname);
- if (tlen == 0)
+ /*
+ 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"))
{
- printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str());
- return std::errc::no_such_file_or_directory;
+ 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);
}
- tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outtoc.tracks[trknum].frames = tlen;
- outinfo.track[trknum].offset = 0;
}
}
}
}
- /*
- * Dreamcast patterns are identified by track types and number of tracks
- */
- if (outtoc.numtrks > 4 && outtoc.tracks[outtoc.numtrks-1].pgtype == CD_TRACK_MODE1_RAW)
+ if (is_gdrom)
{
- if (outtoc.tracks[outtoc.numtrks-2].pgtype == CD_TRACK_AUDIO)
- disc_pattern = TYPE_III_SPLIT;
- else
- disc_pattern = TYPE_III;
+ /*
+ * 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;
+ }
+ }
}
- else if (outtoc.numtrks > 3)
+
+ if (EXTRA_VERBOSE)
{
- if (outtoc.tracks[outtoc.numtrks-1].pgtype == CD_TRACK_AUDIO)
- disc_pattern = TYPE_II;
- else
- disc_pattern = TYPE_III;
+ 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);
+ }
}
- else if (outtoc.numtrks == 3)
+
+ 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)
{
- disc_pattern = TYPE_I;
+ return false;
}
- /*
- * Special handling for TYPE_III_SPLIT, pregap in last track contains 75 frames audio and 150 frames data
- */
- if (disc_pattern == TYPE_III_SPLIT)
+ path = get_file_path(path);
+
+ char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
+ while (!feof(infile))
{
- assert(outtoc.tracks[outtoc.numtrks-1].pregap == 225);
+ if (!fgets(linebuffer, 511, infile))
+ break;
- // grow the AUDIO track into DATA track by 75 frames as per Pattern III
- outtoc.tracks[outtoc.numtrks-2].frames += 225;
- outtoc.tracks[outtoc.numtrks-2].padframes += 150;
- outinfo.track[outtoc.numtrks-2].offset = 150 * (outtoc.tracks[outtoc.numtrks-2].datasize+outtoc.tracks[outtoc.numtrks-2].subsize);
- outtoc.tracks[outtoc.numtrks-2].splitframes = 75;
+ int i = 0;
- // skip the pregap when reading the DATA track
- outtoc.tracks[outtoc.numtrks-1].frames -= 225;
- outinfo.track[outtoc.numtrks-1].offset += 225 * (outtoc.tracks[outtoc.numtrks-1].datasize+outtoc.tracks[outtoc.numtrks-1].subsize);
- }
+ TOKENIZE
- /*
- * 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)
+ if (!strcmp(token, "REM"))
{
- 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;
+ /* 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;
}
}
- if (EXTRA_VERBOSE)
- for (trknum = 0; trknum < outtoc.numtrks; trknum++)
- {
- printf("trk %d: %d frames @ offset %d, pad=%d, split=%d, area=%d, phys=%d, pregap=%d, pgtype=%d, idx0=%d, idx1=%d, (true %d)\n",
- 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,
- outinfo.track[trknum].idx0offs,
- outinfo.track[trknum].idx1offs,
- outtoc.tracks[trknum].frames - outtoc.tracks[trknum].padframes);
- }
+ fclose(infile);
- return std::error_condition();
+ return has_rem_singledensity && has_rem_highdensity;
}
/*-------------------------------------------------
@@ -2989,7 +2920,7 @@ std::error_condition cdrom_file::parse_gdicue(std::string_view tocfname, toc &ou
std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outtoc, track_input_info &outinfo)
{
- static char token[512];
+ char token[512];
auto pos = tocfname.rfind('.');
std::string tocfext = pos == std::string_view::npos ? std::string() : strmakelower(tocfname.substr(pos + 1));
@@ -3001,10 +2932,7 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
if (tocfext == "cue")
{
- if (is_gdicue(tocfname))
- return parse_gdicue(tocfname, outtoc, outinfo);
- else
- return parse_cue(tocfname, outtoc, outinfo);
+ return parse_cue(tocfname, outtoc, outinfo);
}
if (tocfext == "nrg")
@@ -3034,132 +2962,167 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
int trknum = -1;
char linebuffer[512];
+ memset(linebuffer, 0, sizeof(linebuffer));
+
while (!feof(infile))
{
/* get the next line */
- fgets(linebuffer, 511, infile);
+ if (!fgets(linebuffer, 511, infile))
+ break;
- /* if EOF didn't hit, keep going */
- if (!feof(infile))
- {
- int i = 0;
+ 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;
- if ((!strcmp(token, "DATAFILE")) || (!strcmp(token, "AUDIOFILE")) || (!strcmp(token, "FILE")))
- {
- int f;
+ /* found the data file for a track */
+ TOKENIZE
- /* found the data file for a track */
- TOKENIZE
+ /* keep the filename */
+ outinfo.track[trknum].fname.assign(path).append(token);
- /* keep the filename */
- outinfo.track[trknum].fname.assign(path).append(token);
+ /* get either the offset or the length */
+ TOKENIZE
- /* get either the offset or the length */
+ if (!strcmp(token, "SWAP"))
+ {
TOKENIZE
- if (!strcmp(token, "SWAP"))
- {
- TOKENIZE
+ outinfo.track[trknum].swap = true;
+ }
+ else
+ {
+ outinfo.track[trknum].swap = false;
+ }
- 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);
- 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;
+ }
- f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- }
- else
- {
- f = 0;
- }
+ outinfo.track[trknum].offset = f;
- 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]))
{
- // 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);
+ // it was an offset.
+ f *= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize);
- outinfo.track[trknum].offset += f;
+ 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;
+ // this is the length.
+ f = msf_to_frames(token);
}
- else
- {
- /* guesstimate the track length? */
- f = 0;
- }
-
- outtoc.tracks[trknum].frames = f;
}
- else if (!strcmp(token, "TRACK"))
+ else if (trknum == 0 && outinfo.track[trknum].offset != 0)
{
- trknum++;
-
- /* next token on the line is the track type */
- TOKENIZE
+ /* 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].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;
+ outtoc.tracks[trknum].frames = f;
+ }
+ else if (!strcmp(token, "TRACK"))
+ {
+ trknum++;
- 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 chd_file::error::UNSUPPORTED_FORMAT;
- }
+ /* next token on the line is the track type */
+ TOKENIZE
- /* next (optional) token on the line is the subcode 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_subtype_string_to_track_info(token, &outtoc.tracks[trknum]);
- }
- else if (!strcmp(token, "START"))
+ convert_type_string_to_track_info(token, &outtoc.tracks[trknum]);
+ if (outtoc.tracks[trknum].datasize == 0)
{
- int frames;
+ fclose(infile);
+ osd_printf_error("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token);
+ return chd_file::error::UNSUPPORTED_FORMAT;
+ }
- /* get index */
- TOKENIZE
- frames = msf_to_frames( token );
+ /* next (optional) token on the line is the subcode type */
+ TOKENIZE
- outtoc.tracks[trknum].pregap = frames;
- }
+ 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;
}
}
@@ -3168,6 +3131,7 @@ std::error_condition cdrom_file::parse_toc(std::string_view tocfname, toc &outto
/* 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 214a7ed39d6..b11ecb5b3ba 100644
--- a/src/lib/util/cdrom.h
+++ b/src/lib/util/cdrom.h
@@ -16,6 +16,12 @@
#include "ioprocs.h"
#include "osdcore.h"
+#include <algorithm>
+#include <string>
+#include <string_view>
+#include <system_error>
+
+
class cdrom_file {
public:
// tracks are padded to a multiple of this many frames
@@ -24,6 +30,7 @@ public:
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;
@@ -53,29 +60,47 @@ public:
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_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 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 to read from the next file; needed for Redump split-bin 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 */
@@ -91,20 +116,21 @@ public:
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];
+ track_info tracks[MAX_TRACKS + 1];
};
struct track_input_entry
{
track_input_entry() { reset(); }
- void reset() { fname.clear(); offset = idx0offs = idx1offs = 0; swap = false; }
+ 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
- uint32_t idx0offs;
- uint32_t idx1offs;
+ int32_t idx[MAX_INDEX + 1];
+ int32_t leadin, leadout; // TODO: these should probably be their own tracks entirely
};
struct track_input_info
@@ -128,6 +154,7 @@ public:
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);
@@ -135,10 +162,18 @@ public:
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_gdicue(std::string_view tocfname, toc &outtoc, track_input_info &outinfo);
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 { return track == 0xaa || cdtoc.tracks[track].trktype == CD_TRACK_AUDIO ? 0x10 : 0x14; }
+ 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; }
@@ -254,8 +289,8 @@ private:
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( char *token );
+ 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);
diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp
index 194884bfd9a..c05977a84eb 100644
--- a/src/lib/util/chd.cpp
+++ b/src/lib/util/chd.cpp
@@ -2,8 +2,6 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chd.c
-
MAME Compressed Hunks of Data file format
***************************************************************************/
@@ -22,11 +20,13 @@
#include <zlib.h>
+#include <algorithm>
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <ctime>
#include <new>
+#include <tuple>
//**************************************************************************
@@ -134,7 +134,7 @@ struct chd_file::metadata_hash
// stream in bigendian order
//-------------------------------------------------
-inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base)const
+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));
@@ -147,7 +147,7 @@ inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base)const
// 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));
}
@@ -155,45 +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) const
+inline std::error_condition chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const noexcept
{
// no file = failure
- if (!m_file)
- throw std::error_condition(error::NOT_OPEN);
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
// seek and read
- m_file->seek(offset, SEEK_SET);
+ std::error_condition err;
+ err = m_file->seek(offset, SEEK_SET);
+ if (UNEXPECTED(err))
+ return err;
size_t count;
- std::error_condition err = m_file->read(dest, length, count);
- if (err)
- throw err;
- else if (count != length)
- throw std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is cut off)
+ 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)
- throw std::error_condition(error::NOT_OPEN);
+ if (UNEXPECTED(!m_file))
+ return std::error_condition(error::NOT_OPEN);
// seek and write
- m_file->seek(offset, SEEK_SET);
- size_t count;
- std::error_condition err = m_file->write(source, length, count);
- if (err)
- throw err;
- else if (count != length)
- throw std::error_condition(std::errc::interrupted); // can theoretically happen if write is inuterrupted by a signal
+ std::error_condition err;
+ err = m_file->seek(offset, SEEK_SET);
+ if (UNEXPECTED(err))
+ return err;
+ return write(*m_file, source, length).first;
}
@@ -205,21 +205,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)
{
- std::error_condition err;
-
// no file = failure
- if (!m_file)
+ if (UNEXPECTED(!m_file))
throw std::error_condition(error::NOT_OPEN);
// seek to the end and align if necessary
+ std::error_condition err;
err = m_file->seek(0, SEEK_END);
- if (err)
+ if (UNEXPECTED(err))
throw err;
if (alignment != 0)
{
uint64_t offset;
err = m_file->tell(offset);
- if (err)
+ if (UNEXPECTED(err))
throw err;
uint32_t delta = offset % alignment;
if (delta != 0)
@@ -232,8 +231,8 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3
{
uint32_t bytes_to_write = std::min<std::size_t>(sizeof(buffer), delta);
size_t count;
- err = m_file->write(buffer, bytes_to_write, count);
- if (err)
+ std::tie(err, count) = write(*m_file, buffer, bytes_to_write);
+ if (UNEXPECTED(err))
throw err;
delta -= count;
}
@@ -243,14 +242,11 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3
// write the real data
uint64_t offset;
err = m_file->tell(offset);
- if (err)
+ if (UNEXPECTED(err))
throw err;
- size_t count;
- err = m_file->write(source, length, count);
- if (err)
+ std::tie(err, std::ignore) = write(*m_file, source, length);
+ if (UNEXPECTED(err))
throw err;
- else if (count != length)
- throw std::error_condition(std::errc::interrupted); // can theoretically happen if write is interrupted by a signal
return offset;
}
@@ -260,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;
}
@@ -338,20 +337,14 @@ bool chd_file::parent_missing() const noexcept
* @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 (std::error_condition const &)
- {
- // 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);
}
/**
@@ -367,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)
+ 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 (std::error_condition const &)
{
- // on failure, return nullptr
+ // on failure, return null
return util::sha1_t::null;
}
}
@@ -400,17 +395,19 @@ 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)
+ 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 (std::error_condition const &)
@@ -441,49 +438,51 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr
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 = get_u16be(&rawmap[12]) + (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())
{
+ // uncompressed case
if (get_u32be(&rawmap[0]) == 0)
{
compressor = CHD_CODEC_PARENT;
@@ -494,12 +493,12 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr
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:
@@ -525,9 +524,12 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr
default:
return error::UNKNOWN_COMPRESSION;
+ }
}
- break;
+ }
+ break;
}
+
return std::error_condition();
}
@@ -541,20 +543,36 @@ std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compr
* @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();
}
/**
@@ -569,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)
- throw std::error_condition(error::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 std::error_condition chd_file::create(util::random_read_write::ptr &&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
@@ -605,12 +624,12 @@ std::error_condition chd_file::create(
uint64_t logicalbytes,
uint32_t hunkbytes,
uint32_t unitbytes,
- chd_codec_type compression[4])
+ const chd_codec_type (&compression)[4])
{
// make sure we don't already have a file open
- if (m_file)
+ if (UNEXPECTED(m_file))
return error::ALREADY_OPEN;
- else if (!file)
+ else if (UNEXPECTED(!file))
return std::errc::invalid_argument;
// set the header parameters
@@ -626,7 +645,7 @@ std::error_condition chd_file::create(
}
/**
- * @fn std::error_condition chd_file::create(util::random_read_write::ptr &&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
@@ -645,13 +664,13 @@ std::error_condition chd_file::create(
util::random_read_write::ptr &&file,
uint64_t logicalbytes,
uint32_t hunkbytes,
- chd_codec_type compression[4],
+ const chd_codec_type (&compression)[4],
chd_file &parent)
{
// make sure we don't already have a file open
- if (m_file)
+ if (UNEXPECTED(m_file))
return error::ALREADY_OPEN;
- else if (!file)
+ else if (UNEXPECTED(!file))
return std::errc::invalid_argument;
// set the header parameters
@@ -667,7 +686,7 @@ std::error_condition chd_file::create(
}
/**
- * @fn std::error_condition chd_file::create(std::string_view 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
@@ -687,23 +706,23 @@ std::error_condition chd_file::create(
uint64_t logicalbytes,
uint32_t hunkbytes,
uint32_t unitbytes,
- chd_codec_type compression[4])
+ const chd_codec_type (&compression)[4])
{
// make sure we don't already have a file open
- if (m_file)
+ if (UNEXPECTED(m_file))
return error::ALREADY_OPEN;
// create the new file
util::core_file::ptr file;
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
- if (filerr)
+ if (UNEXPECTED(filerr))
return filerr;
// create the file normally, then claim the file
std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression);
// if an error happened, close and delete the file
- if (chderr)
+ if (UNEXPECTED(chderr))
{
file.reset();
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
@@ -712,7 +731,7 @@ std::error_condition chd_file::create(
}
/**
- * @fn std::error_condition chd_file::create(std::string_view 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
@@ -731,24 +750,24 @@ std::error_condition chd_file::create(
std::string_view filename,
uint64_t logicalbytes,
uint32_t hunkbytes,
- chd_codec_type compression[4],
+ const chd_codec_type (&compression)[4],
chd_file &parent)
{
// make sure we don't already have a file open
- if (m_file)
+ if (UNEXPECTED(m_file))
return error::ALREADY_OPEN;
// create the new file
util::core_file::ptr file;
std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file);
- if (filerr)
+ if (UNEXPECTED(filerr))
return filerr;
// create the file normally, then claim the file
std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent);
// if an error happened, close and delete the file
- if (chderr)
+ if (UNEXPECTED(chderr))
{
file.reset();
osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view
@@ -777,14 +796,14 @@ std::error_condition chd_file::open(
const open_parent_func &open_parent)
{
// make sure we don't already have a file open
- if (m_file)
+ 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;
std::error_condition filerr = util::core_file::open(filename, openflags, file);
- if (filerr)
+ if (UNEXPECTED(filerr))
return filerr;
// now open the CHD
@@ -812,9 +831,9 @@ std::error_condition chd_file::open(
const open_parent_func &open_parent)
{
// make sure we don't already have a file open
- if (m_file)
+ if (UNEXPECTED(m_file))
return error::ALREADY_OPEN;
- else if (!file)
+ else if (UNEXPECTED(!file))
return std::errc::invalid_argument;
// open the file
@@ -848,7 +867,7 @@ void chd_file::close()
m_hunkcount = 0;
m_unitbytes = 0;
m_unitcount = 0;
- memset(m_compression, 0, sizeof(m_compression));
+ std::fill(std::begin(m_compression), std::end(m_compression), 0);
m_parent.reset();
m_parent_missing = false;
@@ -873,6 +892,105 @@ 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 std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer)
*
@@ -893,126 +1011,148 @@ void chd_file::close()
* @param hunknum The hunknum.
* @param [in,out] buffer If non-null, the buffer.
*
- * @return The hunk.
+ * @return An error condition.
*/
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)
- throw std::error_condition(error::NOT_OPEN);
-
- // return an error if out of range
- if (hunknum >= m_hunkcount)
- throw std::error_condition(error::HUNK_OUT_OF_RANGE);
-
// get a pointer to the map entry
- uint64_t blockoffs;
- uint32_t blocklen;
- util::crc32_t blockcrc;
- uint8_t *rawmap;
- auto *dest = reinterpret_cast<uint8_t *>(buffer);
switch (m_version)
{
- // v3/v4 map entries
- case 3:
- case 4:
- rawmap = &m_rawmap[16 * hunknum];
- blockoffs = get_u64be(&rawmap[0]);
- blockcrc = get_u32be(&rawmap[8]);
+ // 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 = get_u16be(&rawmap[12]) + (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 std::error_condition(error::DECOMPRESSION_ERROR);
+ 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_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 std::error_condition(error::DECOMPRESSION_ERROR);
+ 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 (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc)
- throw 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_SELF_HUNK:
+ return read_hunk(blockoffs, dest);
- case V34_MAP_ENTRY_TYPE_PARENT_HUNK:
- if (m_parent_missing)
- throw std::error_condition(error::REQUIRES_PARENT);
- return m_parent->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 = mulu_32x32(get_u32be(rawmap), 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 std::error_condition(error::REQUIRES_PARENT);
+ return file_read(blockoffs, dest, m_hunkbytes);
+ else if (UNEXPECTED(m_parent_missing))
+ return std::error_condition(error::REQUIRES_PARENT);
else if (m_parent)
- m_parent->read_hunk(hunknum, dest);
+ return m_parent->read_hunk(hunknum, dest);
else
memset(dest, 0, m_hunkbytes);
return std::error_condition();
}
-
- // compressed case
- blocklen = get_u24be(&rawmap[1]);
- blockoffs = get_u48be(&rawmap[4]);
- blockcrc = get_u16be(&rawmap[10]);
- 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 std::error_condition(error::DECOMPRESSION_ERROR);
- if (m_decompressor[rawmap[0]]->lossy() && util::crc16_creator::simple(&m_compressed[0], blocklen) != blockcrc)
- throw std::error_condition(error::DECOMPRESSION_ERROR);
- return std::error_condition();
+ {
+ 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 std::error_condition(error::DECOMPRESSION_ERROR);
- return std::error_condition();
+ {
+ 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 std::error_condition(error::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 std::error_condition(std::errc::io_error);
+ // if we get here, the map contained an unsupported block type
+ return std::error_condition(error::INVALID_DATA);
}
catch (std::error_condition const &err)
{
@@ -1042,68 +1182,73 @@ std::error_condition chd_file::read_hunk(uint32_t hunknum, 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)
- throw std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::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 = get_u32be(rawmap);
+ // 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 auto *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 std::error_condition();
+ // 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;
-
- // write the map entry back
- put_u32be(rawmap, rawentry);
- file_write(m_mapoffset + hunknum * 4, rawmap, 4);
-
- // update the cached hunk if we just wrote it
- if (hunknum == m_cachehunk && buffer != &m_cache[0])
- memcpy(&m_cache[0], buffer, m_hunkbytes);
}
- else
+ catch (std::error_condition const &err)
{
- // otherwise, just overwrite
- file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes);
+ // just return errors
+ return err;
}
+
+ // 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);
+
return std::error_condition();
}
- catch (std::error_condition const &err)
+ else
{
- // just return errors
- return err;
+ // otherwise, just overwrite
+ return file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes);
}
}
@@ -1163,36 +1308,36 @@ std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer,
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;
+ 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);
-
- // if it's a full block, just read directly from disk unless it's the cached hunk
- std::error_condition err;
- if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
- err = read_hunk(curhunk, dest);
+ 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);
- // 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)
+ 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)
- return err;
+ // advance
dest += endoffs + 1 - startoffs;
}
return std::error_condition();
@@ -1216,27 +1361,28 @@ std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_
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 auto *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
std::error_condition err;
- if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk)
+ 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)
+ if (UNEXPECTED(err))
return err;
m_cachehunk = curhunk;
}
@@ -1245,7 +1391,7 @@ std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer,
}
// handle errors and advance
- if (err)
+ if (UNEXPECTED(err))
return err;
source += endoffs + 1 - startoffs;
}
@@ -1266,29 +1412,20 @@ std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer,
* @param searchindex The searchindex.
* @param [in,out] output The output.
*
- * @return The metadata.
+ * @return An error condition.
*/
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))
- return std::error_condition(error::METADATA_NOT_FOUND);
-
- // read the metadata
- output.assign(metaentry.length, '\0');
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
- return std::error_condition();
- }
- catch (std::error_condition const &err)
- {
- // just return errors
+ // 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);
}
/**
@@ -1303,29 +1440,20 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_
* @param searchindex The searchindex.
* @param [in,out] output The output.
*
- * @return The metadata.
+ * @return An error condition.
*/
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 std::error_condition(error::METADATA_NOT_FOUND);
-
- // read the metadata
- output.resize(metaentry.length);
- file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length);
- return std::error_condition();
- }
- catch (std::error_condition const &err)
- {
- // just return errors
+ // 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);
}
/**
@@ -1342,29 +1470,19 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_
* @param outputlen The outputlen.
* @param [in,out] resultlen The resultlen.
*
- * @return The metadata.
+ * @return An error condition.
*/
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 std::error_condition(error::METADATA_NOT_FOUND);
-
- // read the metadata
- resultlen = metaentry.length;
- file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen));
- return std::error_condition();
- }
- catch (std::error_condition const &err)
- {
- // just return errors
+ // 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));
}
/**
@@ -1381,31 +1499,28 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_
* @param [in,out] resulttag The resulttag.
* @param [in,out] resultflags The resultflags.
*
- * @return The metadata.
+ * @return An error condition.
*/
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 std::error_condition(error::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 std::error_condition();
- }
- catch (std::error_condition const &err)
- {
- // just return errors
+ std::error_condition 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();
}
/**
@@ -1426,40 +1541,52 @@ std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_
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 std::error_condition(std::errc::invalid_argument);
-
- // 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];
- put_u24be(length, inputlen);
- 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)
{
@@ -1475,7 +1602,9 @@ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t
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
@@ -1487,6 +1616,10 @@ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t
// return any errors
return err;
}
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
}
/**
@@ -1507,23 +1640,13 @@ std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t
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 std::error_condition(error::METADATA_NOT_FOUND);
-
- // point the previous to the next, unlinking us
- metadata_set_previous_next(metaentry.prev, metaentry.next);
- return std::error_condition();
- }
- catch (std::error_condition const &err)
- {
- // return any errors
+ // 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);
}
/**
@@ -1542,34 +1665,32 @@ std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_
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;
+ std::error_condition err;
+ for (err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true))
{
- // 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
- std::error_condition err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags);
- if (err)
- throw err;
- }
- return std::error_condition();
+ // 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;
+
+ // write it to the destination
+ err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags);
+ if (UNEXPECTED(err))
+ return err;
}
- catch (std::error_condition const &err)
- {
- // return any errors
+ if (err == error::METADATA_NOT_FOUND)
+ return std::error_condition();
+ else
return err;
- }
}
/**
@@ -1595,15 +1716,18 @@ 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;
@@ -1611,6 +1735,8 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1)
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())
@@ -1772,7 +1898,7 @@ uint32_t chd_file::guess_unitbytes()
void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1)
{
// verify header length
- if (get_u32be(&rawheader[8]) != V3_HEADER_SIZE)
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V3_HEADER_SIZE))
throw std::error_condition(error::INVALID_FILE);
// extract core info
@@ -1835,7 +1961,7 @@ 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 (get_u32be(&rawheader[8]) != V4_HEADER_SIZE)
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V4_HEADER_SIZE))
throw std::error_condition(error::INVALID_FILE);
// extract core info
@@ -1895,7 +2021,7 @@ 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 (get_u32be(&rawheader[8]) != V5_HEADER_SIZE)
+ if (UNEXPECTED(get_u32be(&rawheader[8]) != V5_HEADER_SIZE))
throw std::error_condition(error::INVALID_FILE);
// extract core info
@@ -1969,9 +2095,9 @@ std::error_condition chd_file::compress_v5_map()
{
uint8_t curcomp = m_rawmap[hunknum * 12 + 0];
- // promote self block references to more compact forms
if (curcomp == COMPRESSION_SELF)
{
+ // 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;
@@ -1981,10 +2107,9 @@ std::error_condition 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)
{
+ // 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;
@@ -2032,26 +2157,37 @@ std::error_condition 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)
+ if (UNEXPECTED(err != HUFFERR_NONE))
throw std::error_condition(error::COMPRESSION_ERROR);
err = encoder.export_tree_rle(bitbuf);
- if (err != HUFFERR_NONE)
+ 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;
@@ -2134,13 +2270,16 @@ std::error_condition chd_file::compress_v5_map()
// then write the map offset
uint8_t rawbuf[sizeof(uint64_t)];
put_u64be(rawbuf, m_mapoffset);
- file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf));
- return std::error_condition();
+ return file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf));
}
catch (std::error_condition const &err)
{
return err;
}
+ catch (std::bad_alloc const &)
+ {
+ return std::errc::not_enough_memory;
+ }
}
/**
@@ -2165,7 +2304,10 @@ void chd_file::decompress_v5_map()
// read the reader
uint8_t rawbuf[16];
- file_read(m_mapoffset, rawbuf, sizeof(rawbuf));
+ 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]);
@@ -2175,13 +2317,15 @@ void chd_file::decompress_v5_map()
// 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)
+ 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;
@@ -2265,7 +2409,7 @@ void chd_file::decompress_v5_map()
}
// verify the final CRC
- if (util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc)
+ if (UNEXPECTED(util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc))
throw std::error_condition(error::DECOMPRESSION_ERROR);
}
@@ -2295,13 +2439,13 @@ std::error_condition chd_file::create_common()
m_metaoffset = 0;
// if we have a parent, it must be V3 or later
- if (m_parent && m_parent->version() < 3)
+ 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)
+ if (UNEXPECTED(m_hunkbytes % m_unitbytes != 0))
throw std::error_condition(std::errc::invalid_argument);
- if (m_parent && m_unitbytes != m_parent->unit_bytes())
+ if (UNEXPECTED(m_parent && m_unitbytes != m_parent->unit_bytes()))
throw std::error_condition(std::errc::invalid_argument);
// verify the compression types
@@ -2336,7 +2480,9 @@ std::error_condition chd_file::create_common()
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;
@@ -2354,8 +2500,10 @@ std::error_condition 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;
}
@@ -2411,10 +2559,12 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun
// 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)
+ if (UNEXPECTED(memcmp(rawheader, "MComprHD", 8) != 0))
throw std::error_condition(error::INVALID_FILE);
m_version = get_u32be(&rawheader[12]);
@@ -2433,7 +2583,7 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun
if (m_version < HEADER_VERSION)
m_allow_writes = false;
- if (writeable && !m_allow_writes)
+ 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)
@@ -2447,7 +2597,7 @@ std::error_condition chd_file::open_common(bool writeable, const open_parent_fun
else if (m_parent->sha1() != parentsha1)
throw std::error_condition(error::INVALID_PARENT);
}
- else if (m_parent)
+ else if (UNEXPECTED(m_parent))
{
throw std::error_condition(std::errc::invalid_argument);
}
@@ -2481,16 +2631,22 @@ void chd_file::create_open_common()
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)
+ 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);
@@ -2505,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)
- throw std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::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 std::error_condition(error::COMPRESSION_ERROR);
+ return std::error_condition();
}
/**
@@ -2563,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);
@@ -2593,11 +2744,13 @@ 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 std::error_condition(std::errc::invalid_argument);
+ if (UNEXPECTED(otherhunk >= hunknum))
+ throw std::error_condition(error::HUNK_OUT_OF_RANGE);
// update the map entry
uint8_t *rawmap = &m_rawmap[hunknum * 12];
@@ -2621,10 +2774,12 @@ 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;
put_u24be(&rawmap[1], 0);
put_u48be(&rawmap[4], parentunit);
@@ -2632,7 +2787,7 @@ void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit)
}
/**
- * @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
@@ -2643,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) const
+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)
@@ -2665,7 +2820,9 @@ 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 = get_u32be(&raw_meta_header[0]);
@@ -2676,7 +2833,7 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada
// 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;
@@ -2684,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;
}
/**
@@ -2698,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)];
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));
}
/**
@@ -2732,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
@@ -2743,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;
}
/**
@@ -2778,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);
@@ -2839,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));
@@ -2880,20 +3039,19 @@ void chd_file_compressor::compress_begin()
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 std::errc::io_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)
@@ -2917,64 +3075,60 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do
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())
{
+ // if we're uncompressed, use regular writes
std::error_condition err = write_hunk(item.m_hunknum, item.m_data);
- if (err)
+ 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)
+ 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 (false);
+ }
// reset the item and advance
item.m_status = WS_READY;
@@ -2983,23 +3137,24 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do
// 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 std::error_condition();
- set_raw_sha1(m_compsha1.finish());
+ std::error_condition err = set_raw_sha1(m_compsha1.finish());
+ if (UNEXPECTED(err))
+ return err;
return compress_v5_map();
}
}
@@ -3014,9 +3169,9 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do
// 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 ? error::WALKING_PARENT : error::COMPRESSING;
@@ -3037,7 +3192,7 @@ std::error_condition chd_file_compressor::compress_continue(double &progress, do
void *chd_file_compressor::async_walk_parent_static(void *param, int threadid)
{
- auto *item = reinterpret_cast<work_item *>(param);
+ auto *const item = reinterpret_cast<work_item *>(param);
item->m_compressor->async_walk_parent(*item);
return nullptr;
}
@@ -3079,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)
{
- auto *item = reinterpret_cast<work_item *>(param);
+ auto *const item = reinterpret_cast<work_item *>(param);
item->m_compressor->async_compress_hunk(*item, threadid);
return nullptr;
}
@@ -3106,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
@@ -3142,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())
@@ -3199,12 +3362,12 @@ void chd_file_compressor::async_read()
catch (std::error_condition const &err)
{
fprintf(stderr, "CHD error occurred: %s\n", err.message().c_str());
- m_read_error = true;
+ m_read_error = err;
}
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
}
}
@@ -3222,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));
@@ -3279,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;
@@ -3312,36 +3475,40 @@ void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, ut
m_map[crc16] = entry;
}
-bool chd_file::is_hd() const
+std::error_condition chd_file::check_is_hd() const noexcept
{
metadata_entry metaentry;
return metadata_find(HARD_DISK_METADATA_TAG, 0, metaentry);
}
-bool chd_file::is_cd() const
+std::error_condition chd_file::check_is_cd() const noexcept
{
metadata_entry metaentry;
- return metadata_find(CDROM_OLD_METADATA_TAG, 0, metaentry)
- || metadata_find(CDROM_TRACK_METADATA_TAG, 0, metaentry)
- || metadata_find(CDROM_TRACK_METADATA2_TAG, 0, 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;
}
-bool chd_file::is_gd() const
+std::error_condition chd_file::check_is_gd() const noexcept
{
metadata_entry metaentry;
- return metadata_find(GDROM_OLD_METADATA_TAG, 0, metaentry)
- || metadata_find(GDROM_TRACK_METADATA_TAG, 0, 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;
}
-bool chd_file::is_dvd() const
+std::error_condition chd_file::check_is_dvd() const noexcept
{
metadata_entry metaentry;
return metadata_find(DVD_METADATA_TAG, 0, metaentry);
}
-bool chd_file::is_av() const
+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 3e52cab4f9b..671f4bdbd01 100644
--- a/src/lib/util/chd.h
+++ b/src/lib/util/chd.h
@@ -2,8 +2,6 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chd.h
-
MAME Compressed Hunks of Data file format
***************************************************************************/
@@ -309,24 +307,24 @@ public:
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 { return (m_compression[0] != CHD_CODEC_NONE); }
+ 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();
- util::sha1_t raw_sha1();
- util::sha1_t parent_sha1();
+ 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
- std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]);
- std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]);
- std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent);
- std::error_condition create(util::random_read_write::ptr &&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
std::error_condition open(std::string_view filename, bool writeable = false, chd_file *parent = nullptr, const open_parent_func &open_parent = nullptr);
@@ -336,6 +334,7 @@ public:
void close();
// read/write
+ 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);
@@ -361,23 +360,23 @@ public:
std::error_condition codec_configure(chd_codec_type codec, int param, void *config);
// typing
- bool is_hd() const;
- bool is_cd() const;
- bool is_gd() const;
- bool is_dvd() const;
- bool is_av() const;
+ 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
- util::sha1_t be_read_sha1(const uint8_t *base) const;
- void be_write_sha1(uint8_t *base, util::sha1_t value);
- void file_read(uint64_t offset, void *dest, uint32_t length) const;
- 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();
@@ -389,12 +388,12 @@ private:
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) const;
- 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);
@@ -466,7 +465,7 @@ 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
@@ -563,7 +562,7 @@ private:
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?
+ std::error_condition m_read_error; // error during reading, if any
// work item thread
static constexpr int WORK_BUFFER_HUNKS = 256;
diff --git a/src/lib/util/chdcodec.cpp b/src/lib/util/chdcodec.cpp
index c59d880374c..c8a1f573bd0 100644
--- a/src/lib/util/chdcodec.cpp
+++ b/src/lib/util/chdcodec.cpp
@@ -2,8 +2,6 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chdcodec.c
-
Codecs used by the CHD format
***************************************************************************/
@@ -333,16 +331,16 @@ 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 / 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)
+ : 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 % cdrom_file::FRAME_SIZE != 0)
@@ -402,16 +400,16 @@ private:
// ======================> 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 / 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)
+ : 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 % cdrom_file::FRAME_SIZE != 0)
@@ -490,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;
@@ -553,7 +552,7 @@ const codec_entry f_codec_list[] =
// instance of the given type
//-------------------------------------------------
-const codec_entry *find_in_list(chd_codec_type 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)
@@ -575,9 +574,9 @@ const codec_entry *find_in_list(chd_codec_type type)
//-------------------------------------------------
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)
{
}
@@ -607,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)
{
@@ -622,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);
+}
+
//**************************************************************************
@@ -668,7 +664,7 @@ chd_decompressor::ptr chd_codec_list::new_decompressor(chd_codec_type type, chd_
// corresponds to a supported codec
//-------------------------------------------------
-bool chd_codec_list::codec_exists(chd_codec_type type)
+bool chd_codec_list::codec_exists(chd_codec_type type) noexcept
{
// find in the list and construct the class
return bool(find_in_list(type));
@@ -680,7 +676,7 @@ bool chd_codec_list::codec_exists(chd_codec_type type)
// codec
//-------------------------------------------------
-const char *chd_codec_list::codec_name(chd_codec_type type)
+const char *chd_codec_list::codec_name(chd_codec_type type) noexcept
{
// find in the list and construct the class
const codec_entry *entry = find_in_list(type);
@@ -1588,8 +1584,8 @@ 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 % cdrom_file::FRAME_SIZE != 0)
@@ -1717,8 +1713,8 @@ 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 % cdrom_file::FRAME_SIZE != 0)
@@ -1943,21 +1939,13 @@ 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)
{
@@ -1967,12 +1955,9 @@ void chd_avhuff_decompressor::decompress(const uint8_t *src, uint32_t complen, u
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);
}
/**
diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h
index c3234ea600d..c477333544f 100644
--- a/src/lib/util/chdcodec.h
+++ b/src/lib/util/chdcodec.h
@@ -2,8 +2,6 @@
// copyright-holders:Aaron Giles
/***************************************************************************
- chdcodec.h
-
Codecs used by the CHD format
***************************************************************************/
@@ -89,6 +87,7 @@ 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;
};
@@ -104,8 +103,8 @@ public:
static chd_decompressor::ptr new_decompressor(chd_codec_type type, chd_file &file);
// utilities
- static bool codec_exists(chd_codec_type type);
- static const char *codec_name(chd_codec_type type);
+ static bool codec_exists(chd_codec_type type) noexcept;
+ static const char *codec_name(chd_codec_type type) noexcept;
};
diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp
index 3d5c705ab86..74a742b0306 100644
--- a/src/lib/util/corefile.cpp
+++ b/src/lib/util/corefile.cpp
@@ -48,13 +48,13 @@ public:
virtual std::error_condition tell(std::uint64_t &result) noexcept override { return m_file.tell(result); }
virtual std::error_condition length(std::uint64_t &result) noexcept override { return m_file.length(result); }
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read(buffer, length, actual); }
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.read_at(offset, buffer, length, actual); }
+ virtual std::error_condition 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); }
virtual std::error_condition finalize() noexcept override { return m_file.finalize(); }
virtual std::error_condition flush() noexcept override { return m_file.flush(); }
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write(buffer, length, actual); }
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { return m_file.write_at(offset, buffer, length, actual); }
+ virtual 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); }
virtual bool eof() const override { return m_file.eof(); }
@@ -167,13 +167,13 @@ public:
~core_in_memory_file() override { purge(); }
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition 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::error_condition finalize() noexcept override { return std::error_condition(); }
virtual std::error_condition flush() noexcept override { clear_putback(); return std::error_condition(); }
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override { actual = 0; return std::errc::bad_file_descriptor; }
+ virtual 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; }
void const *buffer() const { return m_data; }
@@ -217,13 +217,13 @@ public:
}
~core_osd_file() override;
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual std::error_condition 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::error_condition finalize() noexcept override;
virtual std::error_condition flush() noexcept override;
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override;
+ virtual 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::error_condition truncate(std::uint64_t offset) override;
@@ -260,9 +260,8 @@ int core_text_file::getc()
{
if (!pos)
{
- std::size_t readlen;
std::uint8_t bom[4];
- read(bom, 4, readlen);
+ auto const [err, readlen] = read(*this, bom, 4); // FIXME: check for errors
if (readlen == 4)
{
if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf)
@@ -303,15 +302,14 @@ int core_text_file::getc()
// fetch the next character
// FIXME: all of this plays fast and loose with error checking and seeks backwards far too frequently
char16_t utf16_buffer[UTF16_CHAR_MAX];
- auto uchar = char32_t(~0);
+ char32_t uchar = ~char32_t(0);
switch (m_text_type)
{
default:
case text_file_type::OSD:
{
char default_buffer[16];
- std::size_t readlen;
- read(default_buffer, sizeof(default_buffer), readlen);
+ 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]));
@@ -323,8 +321,7 @@ int core_text_file::getc()
case text_file_type::UTF8:
{
char utf8_buffer[UTF8_CHAR_MAX];
- std::size_t readlen;
- read(utf8_buffer, sizeof(utf8_buffer), readlen);
+ 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]));
@@ -335,8 +332,7 @@ int core_text_file::getc()
case text_file_type::UTF16BE:
{
- std::size_t readlen;
- read(utf16_buffer, sizeof(utf16_buffer), readlen);
+ 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]));
@@ -347,8 +343,7 @@ int core_text_file::getc()
case text_file_type::UTF16LE:
{
- std::size_t readlen;
- read(utf16_buffer, sizeof(utf16_buffer), readlen);
+ 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]));
@@ -360,8 +355,7 @@ int core_text_file::getc()
case text_file_type::UTF32BE:
{
// FIXME: deal with read returning short
- std::size_t readlen;
- read(&uchar, sizeof(uchar), readlen);
+ auto const [err, readlen] = read(*this, &uchar, sizeof(uchar));
if (sizeof(uchar) == readlen)
uchar = big_endianize_int32(uchar);
}
@@ -370,8 +364,7 @@ int core_text_file::getc()
case text_file_type::UTF32LE:
{
// FIXME: deal with read returning short
- std::size_t readlen;
- read(&uchar, sizeof(uchar), readlen);
+ auto const [err, readlen] = read(*this, &uchar, sizeof(uchar));
if (sizeof(uchar) == readlen)
uchar = little_endianize_int32(uchar);
}
@@ -467,7 +460,7 @@ char *core_text_file::gets(char *s, int n)
int core_text_file::puts(std::string_view s)
{
- // TODO: what to do about write errors or short writes (interrupted)?
+ // 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.
@@ -511,8 +504,7 @@ int core_text_file::puts(std::string_view s)
// if we overflow, break into chunks
if (pconvbuf >= convbuf + std::size(convbuf) - 10)
{
- std::size_t written;
- write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here
count += written;
pconvbuf = convbuf;
}
@@ -521,8 +513,7 @@ int core_text_file::puts(std::string_view s)
// final flush
if (pconvbuf != convbuf)
{
- std::size_t written;
- write(convbuf, pconvbuf - convbuf, written); // FIXME: error ignored here
+ auto const [err, written] = write(*this, convbuf, pconvbuf - convbuf); // FIXME: error ignored here
count += written;
}
@@ -646,7 +637,7 @@ std::size_t core_basic_file::safe_buffer_copy(
// read - read from a file
//-------------------------------------------------
-std::error_condition core_in_memory_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
+std::error_condition core_in_memory_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
clear_putback();
@@ -659,7 +650,7 @@ std::error_condition core_in_memory_file::read(void *buffer, std::size_t length,
return std::error_condition();
}
-std::error_condition core_in_memory_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+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
{
clear_putback();
@@ -705,17 +696,17 @@ core_osd_file::~core_osd_file()
// read - read from a file
//-------------------------------------------------
-std::error_condition core_osd_file::read(void *buffer, std::size_t length, std::size_t &actual) noexcept
+std::error_condition core_osd_file::read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept
{
// since osd_file works like pread/pwrite, implement in terms of read_at
// core_osd_file is declared final, so a derived class can't interfere
- std::error_condition err = read_at(index(), buffer, length, actual);
+ std::error_condition err = read_some_at(index(), buffer, length, actual);
add_offset(actual);
return err;
}
-std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept
+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)
{
@@ -750,18 +741,12 @@ std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer,
}
else
{
- // read the remainder directly from the file
- do
- {
- // may need to split into chunks if size_t is larger than 32 bits
- std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length - actual);
- std::uint32_t bytes_read;
- err = m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + actual, offset + actual, chunk, bytes_read);
- if (err || !bytes_read)
- break;
+ // 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;
- }
- while (actual < length);
}
}
@@ -774,17 +759,17 @@ std::error_condition core_osd_file::read_at(std::uint64_t offset, void *buffer,
// write - write to a file
//-------------------------------------------------
-std::error_condition core_osd_file::write(void const *buffer, std::size_t length, std::size_t &actual) noexcept
+std::error_condition core_osd_file::write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept
{
// since osd_file works like pread/pwrite, implement in terms of write_at
// core_osd_file is declared final, so a derived class can't interfere
- std::error_condition err = write_at(index(), buffer, length, actual);
+ std::error_condition err = write_some_at(index(), buffer, length, actual);
add_offset(actual);
return err;
}
-std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept
+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
{
// flush any buffered char
clear_putback();
@@ -792,24 +777,23 @@ std::error_condition core_osd_file::write_at(std::uint64_t offset, void const *b
// invalidate any buffered data
m_bufferbytes = 0U;
- // do the write - may need to split into chunks if size_t is larger than 32 bits
- actual = 0U;
- while (length)
+ // 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
- std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length);
- std::uint32_t bytes_written;
- std::error_condition err = m_file->write(buffer, offset, chunk, bytes_written);
- if (err)
- return err;
+ actual = 0U;
+ }
+ else
+ {
assert(chunk >= bytes_written);
offset += bytes_written;
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + bytes_written;
- length -= bytes_written;
- actual += bytes_written;
+ actual = bytes_written;
set_size((std::max)(size(), offset));
}
- return std::error_condition();
+ return err;
}
@@ -968,7 +952,7 @@ core_file::~core_file()
// pointer
//-------------------------------------------------
-std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) noexcept
+std::error_condition core_file::load(std::string_view filename, void **data, std::size_t &length) noexcept
{
std::error_condition err;
@@ -983,20 +967,20 @@ std::error_condition core_file::load(std::string_view filename, void **data, std
err = file->length(size);
if (err)
return err;
- else if (std::uint32_t(size) != size) // TODO: change interface to use size_t rather than uint32_t for output size
+ else if (std::size_t(size) != size)
return std::errc::file_too_large;
// allocate memory
*data = std::malloc(std::size_t(size));
if (!*data)
return std::errc::not_enough_memory;
- length = std::uint32_t(size);
+ length = std::size_t(size);
// read the data
if (size)
{
std::size_t actual;
- err = file->read(*data, std::size_t(size), actual);
+ std::tie(err, actual) = read(*file, *data, std::size_t(size));
if (err || (size != actual))
{
std::free(*data);
@@ -1004,7 +988,7 @@ std::error_condition core_file::load(std::string_view filename, void **data, std
if (err)
return err;
else
- return std::errc::io_error; // TODO: revisit this error code - either interrupted by an async signal or file truncated out from under us
+ return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us
}
}
@@ -1038,14 +1022,14 @@ std::error_condition core_file::load(std::string_view filename, std::vector<uint
if (size)
{
std::size_t actual;
- err = file->read(&data[0], std::size_t(size), 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 - either interrupted by an async signal or file truncated out from under us
+ return std::errc::io_error; // TODO: revisit this error code - file truncated out from under us
}
}
diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h
index 315645d4e2c..fda5015d8ba 100644
--- a/src/lib/util/corefile.h
+++ b/src/lib/util/corefile.h
@@ -77,7 +77,7 @@ public:
virtual char *gets(char *s, int n) = 0;
// open a file with the specified filename, read it into memory, and return a pointer
- static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length) noexcept;
+ 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;
diff --git a/src/lib/util/corestr.cpp b/src/lib/util/corestr.cpp
index 586f56c1aa3..9fa0a3990a2 100644
--- a/src/lib/util/corestr.cpp
+++ b/src/lib/util/corestr.cpp
@@ -15,6 +15,7 @@
#include <memory>
#include <cctype>
+#include <cstdint>
#include <cstdlib>
diff --git a/src/lib/util/coretmpl.h b/src/lib/util/coretmpl.h
index 55313a4a0f5..87f8cfbf09e 100644
--- a/src/lib/util/coretmpl.h
+++ b/src/lib/util/coretmpl.h
@@ -654,7 +654,7 @@ template <typename T, typename U, typename... V> constexpr T bitswap(T val, U b,
/// 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> T bitswap(T val, U... b) noexcept
+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");
diff --git a/src/lib/util/coreutil.cpp b/src/lib/util/coreutil.cpp
index 94ec6dec273..cebbceb037e 100644
--- a/src/lib/util/coreutil.cpp
+++ b/src/lib/util/coreutil.cpp
@@ -10,7 +10,6 @@
#include "coreutil.h"
#include <cassert>
-#include <zlib.h>
/***************************************************************************
@@ -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 1f487232619..813deb27a83 100644
--- a/src/lib/util/coreutil.h
+++ b/src/lib/util/coreutil.h
@@ -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/delegate.cpp b/src/lib/util/delegate.cpp
index 8949f90f146..fb2fd4e2091 100644
--- a/src/lib/util/delegate.cpp
+++ b/src/lib/util/delegate.cpp
@@ -10,6 +10,8 @@
#include "delegate.h"
+#include "mfpresolve.h"
+
#include <cstdio>
#include <sstream>
@@ -70,31 +72,9 @@ const delegate_mfp_compatible::raw_mfp_data delegate_mfp_compatible::s_null_mfp
delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_generic_class *&object) const
{
- // apply the "this" delta to the object first - the value is shifted to the left one bit position for the ARM-like variant
- LOG("Input this=%p ptr=%p adj=%ld ", reinterpret_cast<void const *>(object), reinterpret_cast<void const *>(m_function), long(m_this_delta));
- object = reinterpret_cast<delegate_generic_class *>(
- reinterpret_cast<std::uint8_t *>(object) + (m_this_delta >> ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 1 : 0)));
- LOG("Calculated this=%p ", reinterpret_cast<void const *>(object));
-
- // test the virtual member function flag - it's the low bit of either the ptr or adj field, depending on the variant
- if ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? !(m_this_delta & 1) : !(m_function & 1))
- {
- // conventional function pointer
- LOG("ptr=%p\n", reinterpret_cast<void const *>(m_function));
- return reinterpret_cast<delegate_generic_function>(m_function);
- }
- else
- {
- // byte index into the vtable to the function
- std::uint8_t const *const vtable_ptr = *reinterpret_cast<std::uint8_t const *const *>(object) + m_function - ((MAME_ABI_CXX_ITANIUM_MFP_TYPE == MAME_ABI_CXX_ITANIUM_MFP_ARM) ? 0 : 1);
- delegate_generic_function result;
- if (MAME_ABI_CXX_VTABLE_FNDESC)
- result = reinterpret_cast<delegate_generic_function>(uintptr_t(vtable_ptr));
- else
- result = *reinterpret_cast<delegate_generic_function const *>(vtable_ptr);
- LOG("ptr=%p (vtable)\n", reinterpret_cast<void const *>(result));
- return result;
- }
+ 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);
}
@@ -107,181 +87,9 @@ delegate_generic_function delegate_mfp_itanium::convert_to_generic(delegate_gene
delegate_generic_function delegate_mfp_msvc::adjust_this_pointer(delegate_generic_class *&object) const
{
- LOG("Input this=%p ", reinterpret_cast<void const *>(object));
- if (sizeof(single_base_equiv) < m_size)
- LOG("thisdelta=%d ", m_this_delta);
- if (sizeof(unknown_base_equiv) == m_size)
- LOG("vptrdelta=%d vindex=%d ", m_vptr_offs, m_vt_index);
- std::uint8_t *byteptr = reinterpret_cast<std::uint8_t *>(object);
-
- // test for pointer to member function cast across virtual inheritance relationship
- if ((sizeof(unknown_base_equiv) == m_size) && m_vt_index)
- {
- // add offset from "this" pointer to location of vptr, and add offset to virtual base from vtable
- byteptr += m_vptr_offs;
- std::uint8_t const *const vptr = *reinterpret_cast<std::uint8_t const *const *>(byteptr);
- byteptr += *reinterpret_cast<int const *>(vptr + m_vt_index);
- }
-
- // add "this" pointer displacement if present in the pointer to member function
- if (sizeof(single_base_equiv) < m_size)
- byteptr += m_this_delta;
- LOG("Calculated this=%p\n", reinterpret_cast<void const *>(byteptr));
- object = reinterpret_cast<delegate_generic_class *>(byteptr);
-
- // walk past recognisable thunks
-#if defined(__x86_64__) || defined(_M_X64)
- std::uint8_t const *func = reinterpret_cast<std::uint8_t const *>(m_function);
- 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 ", 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 reinterpret_cast<delegate_generic_function>(std::uintptr_t(func));
-#elif defined(__aarch64__) || defined(_M_ARM64)
- std::uint32_t const *func = reinterpret_cast<std::uint32_t const *>(m_function);
- while (true)
- {
- // Assumes little Endian mode. Instructions are always stored
- // in little Endian format on AArch64, so if big Endian mode is
- // to be supported, the values need to be swapped.
- if ((0x90000010 == (func[0] & 0x9f00001f)) && (0x91000210 == (func[1] & 0xffc003ff)) && (0xd61f0200 == func[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(func[0] & 0x60000000) >> 17) |
- (std::uint64_t(func[0] & 0x00ffffe0) << 9) |
- ((func[0] & 0x00800000) ? (~std::uint64_t(0) << 33) : 0);
- std::uint32_t const offset = (func[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 == func[0]) && (0xf9400210 == (func[1] & 0xffc003ff)) && (0xd61f0200 == func[2]))
- {
- // virtual function call thunk - ldr xip0,[x0] ; ldr xip0,[x0,#...] ; br xip0
- LOG("Found virtual member function thunk at %p ", func);
- std::uint32_t const *const *const vptr = *reinterpret_cast<std::uint32_t const *const *const *>(object);
- func = vptr[(func[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 reinterpret_cast<delegate_generic_function>(std::uintptr_t(func));
-#else
- return reinterpret_cast<delegate_generic_function>(m_function);
-#endif
+ 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);
}
} // namespace util::detail
diff --git a/src/lib/util/dvdrom.cpp b/src/lib/util/dvdrom.cpp
index 36dc29e7215..0b2379ef7e6 100644
--- a/src/lib/util/dvdrom.cpp
+++ b/src/lib/util/dvdrom.cpp
@@ -58,8 +58,8 @@ dvdrom_file::dvdrom_file(chd_file *_chd)
throw nullptr;
/* check it's actually a DVD-ROM */
- if (!chd->is_dvd())
- throw nullptr;
+ if (std::error_condition err = chd->check_is_dvd())
+ throw err;
sector_count = chd->unit_count();
}
diff --git a/src/lib/util/flac.cpp b/src/lib/util/flac.cpp
index 4d6a3bca81b..d3110ca0d38 100644
--- a/src/lib/util/flac.cpp
+++ b/src/lib/util/flac.cpp
@@ -18,6 +18,7 @@
#include <cstring>
#include <iterator>
#include <new>
+#include <tuple>
//**************************************************************************
@@ -84,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);
}
@@ -266,8 +269,7 @@ FLAC__StreamEncoderWriteStatus flac_encoder::write_callback(const FLAC__byte buf
int count = bytes - offset;
if (m_file)
{
- size_t actual;
- m_file->write(buffer, count, actual); // TODO: check for errors
+ /*auto const [err, actual] =*/ write(*m_file, buffer, count); // FIXME: check for errors
}
else
{
@@ -281,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;
+}
+
//**************************************************************************
@@ -537,7 +571,8 @@ FLAC__StreamDecoderReadStatus flac_decoder::read_callback(FLAC__byte buffer[], s
if (m_file) // if a file, just read
{
- m_file->read(buffer, expected, *bytes); // TODO: check for errors
+ std::error_condition err;
+ std::tie(err, *bytes) = read(*m_file, buffer, expected); // FIXME: check for errors
}
else // otherwise, copy from memory
{
@@ -601,12 +636,12 @@ 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());
@@ -633,7 +668,7 @@ FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame
}
}
-template <flac_decoder::DECODE_MODE Mode, bool SwapEndian> FLAC__StreamDecoderWriteStatus flac_decoder::write_callback(const ::FLAC__Frame *frame, const FLAC__int32 * const 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;
diff --git a/src/lib/util/flac.h b/src/lib/util/flac.h
index c3fee4b574b..be3a0a8ac96 100644
--- a/src/lib/util/flac.h
+++ b/src/lib/util/flac.h
@@ -62,6 +62,10 @@ 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
@@ -123,9 +127,9 @@ private:
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[]);
- template <DECODE_MODE Mode, bool SwapEndian> 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
diff --git a/src/lib/util/harddisk.cpp b/src/lib/util/harddisk.cpp
index 6eb12b38091..266e5d25a2b 100644
--- a/src/lib/util/harddisk.cpp
+++ b/src/lib/util/harddisk.cpp
@@ -16,6 +16,7 @@
#include "osdcore.h"
#include <cstdlib>
+#include <tuple>
/*-------------------------------------------------
@@ -118,7 +119,7 @@ bool hard_disk_file::read(uint32_t lbasector, void *buffer)
size_t actual = 0;
std::error_condition err = fhandle->seek(fileoffset + (lbasector * hdinfo.sectorbytes), SEEK_SET);
if (!err)
- err = fhandle->read(buffer, hdinfo.sectorbytes, actual);
+ std::tie(err, actual) = util::read(*fhandle, buffer, hdinfo.sectorbytes);
return !err && (actual == hdinfo.sectorbytes);
}
}
@@ -151,8 +152,8 @@ bool hard_disk_file::write(uint32_t lbasector, const void *buffer)
size_t actual = 0;
std::error_condition err = fhandle->seek(fileoffset + (lbasector * hdinfo.sectorbytes), SEEK_SET);
if (!err)
- err = fhandle->write(buffer, hdinfo.sectorbytes, actual);
- return !err && (actual == hdinfo.sectorbytes);
+ std::tie(err, actual) = util::write(*fhandle, buffer, hdinfo.sectorbytes);
+ return !err;
}
}
diff --git a/src/lib/util/hash.cpp b/src/lib/util/hash.cpp
index 9100ff6abde..f2c528e9b22 100644
--- a/src/lib/util/hash.cpp
+++ b/src/lib/util/hash.cpp
@@ -410,14 +410,13 @@ std::error_condition hash_collection::compute(random_read &stream, uint64_t offs
unsigned const chunk_length = std::min(length, sizeof(buffer));
// read one chunk
- std::size_t bytes_read;
- std::error_condition err = stream.read_at(offset, buffer, chunk_length, bytes_read);
+ 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 -= chunk_length;
+ length -= bytes_read;
// append the chunk
creator->append(buffer, bytes_read);
diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp
index 0477f1efae1..687968f85ee 100644
--- a/src/lib/util/ioprocs.cpp
+++ b/src/lib/util/ioprocs.cpp
@@ -20,6 +20,7 @@
#include <cstring>
#include <iterator>
#include <limits>
+#include <new>
#include <type_traits>
@@ -120,14 +121,14 @@ public:
{
}
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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_at(std::uint64_t offset, 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
{
do_read(offset, buffer, length, actual);
return std::error_condition();
@@ -239,7 +240,7 @@ public:
{
}
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
if (m_dangling_write)
{
@@ -270,7 +271,7 @@ public:
return std::error_condition();
}
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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;
@@ -338,7 +339,7 @@ public:
return std::error_condition();
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
{
if (m_dangling_read)
{
@@ -361,7 +362,7 @@ public:
return std::error_condition();
}
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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;
@@ -409,7 +410,7 @@ public:
set_filler(fill);
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
{
actual = 0U;
@@ -473,7 +474,7 @@ public:
return std::error_condition();
}
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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;
@@ -634,18 +635,12 @@ public:
{
}
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- // TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
- if (std::numeric_limits<std::uint32_t>::max() < length)
- {
- actual = 0U;
- return std::errc::invalid_argument;
- }
-
// 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, std::uint32_t(length), count);
+ std::error_condition err = file().read(buffer, m_pointer, chunk, count);
if (!err)
{
m_pointer += count;
@@ -658,18 +653,12 @@ public:
return err;
}
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- // TODO: should the client have to deal with reading less than expected even if EOF isn't hit?
- if (std::numeric_limits<std::uint32_t>::max() < length)
- {
- actual = 0U;
- return std::errc::invalid_argument;
- }
-
// 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, std::uint32_t(length), count);
+ std::error_condition err = file().read(buffer, offset, chunk, count);
if (!err)
actual = std::size_t(count);
else
@@ -696,48 +685,148 @@ public:
return file().flush();
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- actual = 0U;
- while (length)
+ // 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 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 written;
- std::error_condition err = file().write(buffer, m_pointer, chunk, written);
- if (err)
- return err;
- m_pointer += written;
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
- length -= written;
- actual += written;
+ actual = std::size_t(count);
+ m_pointer += count;
}
- return std::error_condition();
+ else
+ {
+ actual = 0U;
+ }
+ return err;
}
- virtual std::error_condition write_at(std::uint64_t offset, 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
{
- actual = 0U;
- while (length)
- {
- // 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 written;
- std::error_condition err = file().write(buffer, offset, chunk, written);
- if (err)
- return err;
- offset += written;
- buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written;
- length -= written;
- actual += written;
- }
- return std::error_condition();
+ // 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
diff --git a/src/lib/util/ioprocs.h b/src/lib/util/ioprocs.h
index efe0a231a11..9e2c29d2a14 100644
--- a/src/lib/util/ioprocs.h
+++ b/src/lib/util/ioprocs.h
@@ -19,6 +19,8 @@
#include <cstdlib>
#include <memory>
#include <system_error>
+#include <tuple>
+#include <utility>
// FIXME: make a proper place for OSD forward declarations
@@ -56,7 +58,7 @@ public:
/// \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(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -100,7 +102,7 @@ public:
/// \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(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -184,7 +186,7 @@ public:
/// 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_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -214,7 +216,7 @@ public:
/// 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_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
+ virtual std::error_condition write_some_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0;
};
@@ -229,6 +231,123 @@ 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;
+
/// \}
diff --git a/src/lib/util/ioprocsfill.h b/src/lib/util/ioprocsfill.h
index e49b10687f0..7179d61852c 100644
--- a/src/lib/util/ioprocsfill.h
+++ b/src/lib/util/ioprocsfill.h
@@ -45,9 +45,21 @@ class read_stream_fill_wrapper : public Base, public virtual fill_wrapper_base<D
public:
using Base::Base;
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- std::error_condition err = Base::read(buffer, length, actual);
+ // 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,
@@ -64,9 +76,23 @@ class random_read_fill_wrapper : public read_stream_fill_wrapper<Base, DefaultFi
public:
using read_stream_fill_wrapper<Base, DefaultFiller>::read_stream_fill_wrapper;
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- std::error_condition err = Base::read_at(offset, buffer, length, actual);
+ // 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,
@@ -83,8 +109,9 @@ class random_write_fill_wrapper : public Base, public virtual fill_wrapper_base<
public:
using Base::Base;
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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;
@@ -108,23 +135,22 @@ public:
do
{
std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled);
- err = Base::write_at(current, fill_buffer, chunk, actual);
- if (err)
- {
- actual = 0U;
+ 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;
- }
- current += chunk;
- unfilled -= chunk;
}
while (unfilled);
}
- return Base::write(buffer, length, actual);
+ return Base::write_some(buffer, length, actual);
}
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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);
@@ -139,18 +165,19 @@ public:
do
{
std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled);
- err = Base::write_at(current, fill_buffer, chunk, actual);
- current += chunk;
- unfilled -= chunk;
+ std::size_t filled;
+ err = Base::write_some_at(current, fill_buffer, chunk, filled);
+ current += filled;
+ unfilled -= filled;
}
- while (unfilled && !err);
+ while (unfilled && (!err || (std::errc::interrupted == err)));
}
if (err)
{
actual = 0U;
return err;
}
- return Base::write_at(offset, buffer, length, actual);
+ return Base::write_some_at(offset, buffer, length, actual);
}
};
diff --git a/src/lib/util/ioprocsfilter.cpp b/src/lib/util/ioprocsfilter.cpp
index 68b9c90d018..b8d4cc3e786 100644
--- a/src/lib/util/ioprocsfilter.cpp
+++ b/src/lib/util/ioprocsfilter.cpp
@@ -21,6 +21,7 @@
#include <cstdint>
#include <limits>
#include <system_error>
+#include <tuple>
#include <type_traits>
#include <utility>
@@ -372,9 +373,9 @@ class read_stream_proxy : public virtual read_stream, public T
public:
using T::T;
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- return this->object().read(buffer, length, actual);
+ return this->object().read_some(buffer, length, actual);
}
};
@@ -397,9 +398,9 @@ public:
return this->object().flush();
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition write_some(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
{
- return this->object().write(buffer, length, actual);
+ return this->object().write_some(buffer, length, actual);
}
};
@@ -437,9 +438,9 @@ class random_read_proxy : public virtual random_read, public read_stream_proxy<T
public:
using read_stream_proxy<T>::read_stream_proxy;
- virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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_at(offset, buffer, length, actual);
+ return this->object().read_some_at(offset, buffer, length, actual);
}
};
@@ -452,9 +453,9 @@ class random_write_proxy : public virtual random_write, public write_stream_prox
public:
using write_stream_proxy<T>::write_stream_proxy;
- virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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_at(offset, buffer, length, actual);
+ return this->object().write_some_at(offset, buffer, length, actual);
}
};
@@ -487,7 +488,7 @@ public:
{
}
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_some(void *buffer, std::size_t length, std::size_t &actual) noexcept override
{
std::error_condition err;
actual = 0U;
@@ -504,7 +505,7 @@ public:
{
auto const space = get_unfilled_input();
std::size_t filled;
- err = this->object().read(space.first, space.second, filled);
+ std::tie(err, filled) = read(this->object(), space.first, space.second);
add_input(filled);
short_input = space.second > filled;
}
@@ -598,8 +599,7 @@ public:
auto const output = get_output();
if (output.second)
{
- std::size_t written;
- std::error_condition err = object().write(output.first, output.second, written);
+ auto const [err, written] = write(object(), output.first, output.second);
consume_output(written);
if (err)
{
@@ -611,7 +611,7 @@ public:
return object().flush();
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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;
@@ -651,7 +651,7 @@ private:
{
auto const output = get_output();
std::size_t written;
- std::error_condition err = object().write(output.first, output.second, written);
+ std::error_condition const err = object().write_some(output.first, output.second, written);
consume_output(written);
return err;
}
diff --git a/src/lib/util/ioprocsvec.h b/src/lib/util/ioprocsvec.h
index d3c1c7fe01b..e66000b5729 100644
--- a/src/lib/util/ioprocsvec.h
+++ b/src/lib/util/ioprocsvec.h
@@ -90,14 +90,14 @@ public:
return std::error_condition();
}
- virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override
+ virtual std::error_condition read_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_at(std::uint64_t offset, 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
{
do_read(offset, buffer, length, actual);
return std::error_condition();
@@ -113,14 +113,14 @@ public:
return std::error_condition();
}
- virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override
+ 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_at(std::uint64_t offset, 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
{
return do_write(offset, buffer, length, actual);
}
diff --git a/src/lib/util/jedparse.cpp b/src/lib/util/jedparse.cpp
index 5915886b1a6..b347a2dd2b5 100644
--- a/src/lib/util/jedparse.cpp
+++ b/src/lib/util/jedparse.cpp
@@ -24,6 +24,7 @@
#include <cstdlib>
#include <cstring>
#include <cctype>
+#include <tuple>
@@ -194,7 +195,6 @@ static void process_field(jed_data *data, const uint8_t *cursrc, const uint8_t *
int jed_parse(util::random_read &src, jed_data *result)
{
jed_parse_info pinfo;
- int i;
std::size_t actual;
std::error_condition err;
@@ -206,7 +206,7 @@ int jed_parse(util::random_read &src, jed_data *result)
uint8_t ch;
do
{
- err = src.read(&ch, 1, actual);
+ std::tie(err, actual) = read(src, &ch, 1);
if (err)
{
if (LOG_PARSE) printf("Read error searching for JED start marker\n");
@@ -225,7 +225,7 @@ int jed_parse(util::random_read &src, jed_data *result)
uint16_t checksum = ch;
do
{
- err = src.read(&ch, 1, actual);
+ std::tie(err, actual) = read(src, &ch, 1);
if (err)
{
if (LOG_PARSE) printf("Read error searching for JED end marker\n");
@@ -261,7 +261,8 @@ int jed_parse(util::random_read &src, jed_data *result)
/* see if there is a transmission checksum at the end */
uint8_t sumbuf[4];
- if (!src.read(&sumbuf[0], 4, actual) && actual == 4 && ishex(sumbuf[0]) && ishex(sumbuf[1]) && ishex(sumbuf[2]) && ishex(sumbuf[3]))
+ 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(sumbuf[0]) << 12) | (hexval(sumbuf[1]) << 8) | (hexval(sumbuf[2]) << 4) | hexval(sumbuf[3] << 0);
if (dessum != 0 && dessum != checksum)
@@ -281,7 +282,8 @@ int jed_parse(util::random_read &src, jed_data *result)
}
}
auto srcdata = std::make_unique<uint8_t[]>(endpos - startpos);
- if (src.read(&srcdata[0], endpos - startpos, actual) || actual != 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;
@@ -325,7 +327,7 @@ int jed_parse(util::random_read &src, jed_data *result)
/* 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)
{
@@ -441,13 +443,16 @@ size_t jed_output(const jed_data *data, void *result, size_t length)
int jedbin_parse(util::read_stream &src, jed_data *result)
{
+ std::error_condition err;
+ std::size_t actual;
+
/* initialize the output */
memset(result, 0, sizeof(*result));
/* need at least 4 bytes */
uint8_t buf[4];
- std::size_t actual;
- if (src.read(&buf[0], 4, actual) || actual != 4)
+ std::tie(err, actual) = read(src, &buf[0], 4);
+ if (err || (4 != actual))
return JEDERR_INVALID_DATA;
/* first unpack the number of fuses */
@@ -457,7 +462,8 @@ int jedbin_parse(util::read_stream &src, jed_data *result)
/* now make sure we have enough data in the source */
/* copy in the data */
- if (src.read(result->fusemap, (result->numfuses + 7) / 8, actual) || actual != (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;
}
diff --git a/src/lib/util/language.cpp b/src/lib/util/language.cpp
index 3a00fe80ad7..50b518acd1b 100644
--- a/src/lib/util/language.cpp
+++ b/src/lib/util/language.cpp
@@ -61,11 +61,10 @@ void load_translation(random_read &file)
return;
}
- std::size_t read;
- file.read(translation_data.get(), size, read);
- if (read != size)
+ 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, read);
+ osd_printf_error("Error reading translation file: requested %u bytes but got %u bytes\n", size, actual);
translation_data.reset();
return;
}
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
index e70ba1b592a..bf33991f116 100644
--- a/src/lib/util/msdib.cpp
+++ b/src/lib/util/msdib.cpp
@@ -20,6 +20,7 @@
#include <cassert>
#include <cstdlib>
#include <cstring>
+#include <tuple>
#define LOG_GENERAL (1U << 0)
@@ -127,11 +128,10 @@ std::uint8_t dib_splat_sample(std::uint8_t val, unsigned bits) noexcept
msdib_error dib_read_file_header(read_stream &fp, std::uint32_t &filelen) noexcept
{
- std::size_t actual;
-
// the bitmap file header doesn't use natural alignment
bitmap_file_header file_header;
- if (fp.read(file_header, sizeof(file_header), actual) || (sizeof(file_header) != actual))
+ 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;
@@ -167,6 +167,7 @@ msdib_error dib_read_bitmap_header(
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
@@ -178,7 +179,8 @@ msdib_error dib_read_bitmap_header(
if (sizeof(header.core) > length)
return msdib_error::FILE_TRUNCATED;
std::memset(&header, 0, sizeof(header));
- if (fp.read(&header.core.size, sizeof(header.core.size), actual) || (sizeof(header.core.size) != actual))
+ 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;
@@ -208,7 +210,8 @@ msdib_error dib_read_bitmap_header(
{
palette_bytes = 3U;
std::uint32_t const header_read(std::min<std::uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
- if (fp.read(&header.core.width, header_read, actual) || (header_read != actual))
+ 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;
@@ -261,7 +264,8 @@ msdib_error dib_read_bitmap_header(
{
palette_bytes = 4U;
std::uint32_t const header_read(std::min<std::uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
- if (fp.read(&header.info.width, header_read, actual) || (header_read != actual))
+ 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;
@@ -445,7 +449,6 @@ 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) noexcept
{
// read the bitmap header
- std::size_t actual;
bitmap_headers header;
unsigned palette_bytes;
bool indexed;
@@ -492,7 +495,8 @@ msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::
std::unique_ptr<std::uint8_t []> palette_data(new (std::nothrow) std::uint8_t [palette_size]);
if (!palette_data)
return msdib_error::OUT_OF_MEMORY;
- if (fp.read(palette_data.get(), palette_size, actual) || (palette_size != actual))
+ 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;
@@ -575,7 +579,8 @@ msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::
int const y_inc(top_down ? 1 : -1);
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
{
- if (fp.read(row_data.get(), row_bytes, actual) || (row_bytes != actual))
+ 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;
@@ -638,7 +643,8 @@ msdib_error msdib_read_bitmap_data(random_read &fp, bitmap_argb32 &bitmap, std::
{
for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
{
- if (fp.read(row_data.get(), mask_row_bytes, actual) || (mask_row_bytes != actual))
+ 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;
diff --git a/src/lib/util/options.h b/src/lib/util/options.h
index 99db519a5e5..8b85b9f58e6 100644
--- a/src/lib/util/options.h
+++ b/src/lib/util/options.h
@@ -12,6 +12,7 @@
#define MAME_LIB_UTIL_OPTIONS_H
#include "strformat.h"
+#include "utilfwd.h"
#include <algorithm>
#include <exception>
@@ -43,7 +44,6 @@ const int OPTION_PRIORITY_MAXIMUM = 255; // maximum priority
//**************************************************************************
struct options_entry;
-namespace util { class core_file; }
// exception thrown by core_options when an illegal request is made
class options_exception : public std::exception
diff --git a/src/lib/util/plaparse.cpp b/src/lib/util/plaparse.cpp
index 423939b41a8..9f4d1c0bc14 100644
--- a/src/lib/util/plaparse.cpp
+++ b/src/lib/util/plaparse.cpp
@@ -68,11 +68,14 @@ static uint32_t suck_number(util::random_read &src)
uint32_t value = 0;
// find first digit
- uint8_t ch;
- std::size_t actual;
bool found = false;
- while (!src.read(&ch, 1, actual) && actual == 1)
+ while (true)
{
+ uint8_t ch;
+ auto const [err, actual] = read(src, &ch, 1);
+ if (err || (1 != actual))
+ break;
+
// loop over and accumulate digits
if (isdigit(ch))
{
@@ -189,8 +192,8 @@ static bool process_terms(jed_data *data, util::random_read &src, uint8_t ch, pa
curoutput = 0;
}
- std::size_t actual;
- if (src.read(&ch, 1, actual))
+ auto const [err, actual] = read(src, &ch, 1);
+ if (err)
return false;
if (actual != 1)
return true;
@@ -227,9 +230,11 @@ static bool process_field(jed_data *data, util::random_read &src, parse_info *pi
int destptr = 0;
uint8_t seek;
- std::size_t actual;
- while (!src.read(&seek, 1, actual) && actual == 1 && isalpha(seek))
+ while (true)
{
+ auto const [err, actual] = read(src, &seek, 1);
+ if (err || (actual != 1) || !isalpha(seek))
+ break;
dest[destptr++] = tolower(seek);
if (destptr == sizeof(dest))
break;
@@ -275,8 +280,11 @@ static bool process_field(jed_data *data, util::random_read &src, parse_info *pi
// output polarity (optional)
case KW_PHASE:
if (LOG_PARSE) printf("Phase...\n");
- while (!src.read(&seek, 1, actual) && actual == 1 && !iscrlf(seek) && pinfo->xorptr < (JED_MAX_FUSES/2))
+ 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')
{
// 0 is negative
@@ -322,19 +330,23 @@ int pla_parse(util::random_read &src, jed_data *result)
result->numfuses = 0;
memset(result->fusemap, 0, sizeof(result->fusemap));
- uint8_t ch;
- std::size_t actual;
- while (!src.read(&ch, 1, actual) && actual == 1)
+ while (true)
{
+ 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.read(&ch, 1, actual) && actual == 1)
+ do
{
- if (iscrlf(ch))
- break;
+ std::tie(err, actual) = read(src, &ch, 1);
}
+ while (!err && (actual == 1) && !iscrlf(ch));
break;
// keyword
diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp
index 8e077f92114..ce28a866604 100644
--- a/src/lib/util/png.cpp
+++ b/src/lib/util/png.cpp
@@ -24,6 +24,7 @@
#include <cstdlib>
#include <cstring>
#include <new>
+#include <tuple>
namespace util {
@@ -452,7 +453,7 @@ private:
std::uint8_t tempbuff[4];
// fetch the length of this chunk
- err = fp.read(tempbuff, 4, actual);
+ std::tie(err, actual) = read(fp, tempbuff, 4);
if (err)
return err;
else if (4 != actual)
@@ -460,7 +461,7 @@ private:
length = fetch_32bit(tempbuff);
// fetch the type of this chunk
- err = fp.read(tempbuff, 4, actual);
+ std::tie(err, actual) = read(fp, tempbuff, 4);
if (err)
return err;
else if (4 != actual)
@@ -477,13 +478,8 @@ private:
// read the chunk itself into an allocated memory buffer
if (length)
{
- // allocate memory for this chunk
- data.reset(new (std::nothrow) std::uint8_t [length]);
- if (!data)
- return std::errc::not_enough_memory;
-
- // read the data from the file
- err = fp.read(data.get(), length, actual);
+ // allocate memory and read the data from the file
+ std::tie(err, data, actual) = read(fp, length);
if (err)
{
data.reset();
@@ -500,7 +496,7 @@ private:
}
// read the CRC
- err = fp.read(tempbuff, 4, actual);
+ std::tie(err, actual) = read(fp, tempbuff, 4);
if (err)
{
data.reset();
@@ -789,8 +785,7 @@ public:
std::uint8_t signature[sizeof(PNG_SIGNATURE)];
// read 8 bytes
- std::size_t actual;
- std::error_condition err = fp.read(signature, sizeof(signature), actual);
+ auto const [err, actual] = read(fp, signature, sizeof(signature));
if (err)
return err;
else if (sizeof(signature) != actual)
@@ -941,7 +936,6 @@ std::error_condition png_info::add_text(std::string_view keyword, std::string_vi
static std::error_condition write_chunk(write_stream &fp, const uint8_t *data, uint32_t type, uint32_t length) noexcept
{
std::error_condition err;
- std::size_t written;
std::uint8_t tempbuff[8];
std::uint32_t crc;
@@ -951,30 +945,24 @@ static std::error_condition write_chunk(write_stream &fp, const uint8_t *data, u
crc = crc32(0, tempbuff + 4, 4);
// write that data
- err = fp.write(tempbuff, 8, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 8);
if (err)
return err;
- else if (8 != written)
- return std::errc::io_error;
// append the actual data
if (length > 0)
{
- err = fp.write(data, length, written);
+ std::tie(err, std::ignore) = write(fp, data, length);
if (err)
return err;
- else if (length != written)
- return std::errc::io_error;
crc = crc32(crc, data, length);
}
// write the CRC
put_32bit(tempbuff, crc);
- err = fp.write(tempbuff, 4, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
if (err)
return err;
- else if (4 != written)
- return std::errc::io_error;
return std::error_condition();
}
@@ -993,7 +981,6 @@ static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data
if (err)
return err;
- std::size_t written;
std::uint8_t tempbuff[8192];
std::uint32_t zlength = 0;
z_stream stream;
@@ -1006,11 +993,9 @@ static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data
crc = crc32(0, tempbuff + 4, 4);
// write that data
- err = fp.write(tempbuff, 8, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 8);
if (err)
return err;
- else if (8 != written)
- return std::errc::io_error;
// initialize the stream
memset(&stream, 0, sizeof(stream));
@@ -1036,17 +1021,12 @@ static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data
if (stream.avail_out < sizeof(tempbuff))
{
int bytes = sizeof(tempbuff) - stream.avail_out;
- err = fp.write(tempbuff, bytes, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, bytes);
if (err)
{
deflateEnd(&stream);
return err;
}
- else if (bytes != written)
- {
- deflateEnd(&stream);
- return std::errc::io_error;
- }
crc = crc32(crc, tempbuff, bytes);
zlength += bytes;
}
@@ -1079,22 +1059,18 @@ static std::error_condition write_deflated_chunk(random_write &fp, uint8_t *data
// write the CRC
put_32bit(tempbuff, crc);
- err = fp.write(tempbuff, 4, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
if (err)
return err;
- else if (4 != written)
- return std::errc::io_error;
// seek back and update the length
err = fp.seek(lengthpos, SEEK_SET);
if (err)
return err;
put_32bit(tempbuff + 0, zlength);
- err = fp.write(tempbuff, 4, written);
+ std::tie(err, std::ignore) = write(fp, tempbuff, 4);
if (err)
return err;
- else if (4 != written)
- return std::errc::io_error;
// return to the end
return fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
@@ -1326,12 +1302,9 @@ std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t
info = &pnginfo;
// write the PNG signature
- std::size_t written;
- std::error_condition err = fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE), written);
+ auto const [err, written] = write(fp, PNG_SIGNATURE, sizeof(PNG_SIGNATURE));
if (err)
return err;
- else if (sizeof(PNG_SIGNATURE) != written)
- return std::errc::io_error;
// write the rest of the PNG data
return write_png_stream(fp, *info, bitmap, palette_length, palette);
@@ -1347,12 +1320,9 @@ std::error_condition png_write_bitmap(random_write &fp, png_info *info, bitmap_t
std::error_condition mng_capture_start(random_write &fp, bitmap_t const &bitmap, unsigned rate) noexcept
{
- std::size_t written;
- std::error_condition err = fp.write(MNG_Signature, 8, written);
+ auto const [err, written] = write(fp, MNG_Signature, 8);
if (err)
return err;
- else if (8 != written)
- return std::errc::io_error;
uint8_t mhdr[28];
memset(mhdr, 0, 28);
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/simh_tape_file.cpp b/src/lib/util/simh_tape_file.cpp
index 4fe1af5ae88..ffd6a48aeb3 100644
--- a/src/lib/util/simh_tape_file.cpp
+++ b/src/lib/util/simh_tape_file.cpp
@@ -119,16 +119,14 @@ void simh_tape_file::raw_seek(const osd::u64 pos) const
void simh_tape_file::raw_read(osd::u8 *const buf, const osd::u32 len) const
{
- size_t actual_len;
- std::error_condition err = m_file.read(buf, len, actual_len);
+ 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
{
- size_t actual_len;
- std::error_condition err = m_file.write(buf, len, actual_len);
+ 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")));
}
diff --git a/src/lib/util/un7z.cpp b/src/lib/util/un7z.cpp
index 362c95ee235..5ee97d62777 100644
--- a/src/lib/util/un7z.cpp
+++ b/src/lib/util/un7z.cpp
@@ -71,8 +71,7 @@ private:
if (!size)
return SZ_OK;
- std::size_t read_length(0);
- std::error_condition const err = file->read_at(currfpos, data, size, read_length);
+ auto const [err, read_length] = read_at(*file, currfpos, data, size);
size = read_length;
currfpos += read_length;
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp
index c1df469fed4..2ca8e722d25 100644
--- a/src/lib/util/unzip.cpp
+++ b/src/lib/util/unzip.cpp
@@ -154,8 +154,7 @@ public:
while (cd_remaining)
{
std::size_t const chunk(std::size_t(std::min<std::uint64_t>(std::numeric_limits<std::size_t>::max(), cd_remaining)));
- std::size_t read_length(0);
- std::error_condition const filerr = m_file->read_at(m_ecd.cd_start_disk_offset + cd_offs, &m_cd[cd_offs], chunk, read_length);
+ 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(
@@ -914,7 +913,7 @@ std::error_condition zip_file_impl::decompress(void *buffer, std::size_t length)
}
// get the compressed data offset
- std::uint64_t offset;
+ std::uint64_t offset = 0;
auto const ziperr = get_compressed_data_offset(offset);
if (ziperr)
return ziperr;
@@ -979,7 +978,7 @@ std::error_condition zip_file_impl::read_ecd() noexcept
}
// read in one buffers' worth of data
- filerr = m_file->read_at(m_length - buflen, &buffer[0], buflen, read_length);
+ std::tie(filerr, read_length) = read_at(*m_file, m_length - buflen, &buffer[0], buflen);
if (filerr)
{
osd_printf_error(
@@ -1024,11 +1023,11 @@ std::error_condition zip_file_impl::read_ecd() noexcept
}
// try to read the ZIP64 ECD locator
- filerr = m_file->read_at(
+ std::tie(filerr, read_length) = read_at(
+ *m_file,
m_length - buflen + offset - ecd64_locator_reader::minimum_length(),
&buffer[0],
- ecd64_locator_reader::minimum_length(),
- read_length);
+ ecd64_locator_reader::minimum_length());
if (filerr)
{
osd_printf_error(
@@ -1058,7 +1057,7 @@ std::error_condition zip_file_impl::read_ecd() noexcept
}
// try to read the ZIP64 ECD
- filerr = m_file->read_at(ecd64_loc_rd.ecd64_offset(), &buffer[0], ecd64_reader::minimum_length(), read_length);
+ 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(
@@ -1160,8 +1159,7 @@ std::error_condition zip_file_impl::get_compressed_data_offset(std::uint64_t &of
return ziperr;
// now go read the fixed-sized part of the local file header
- std::size_t read_length;
- std::error_condition const filerr = m_file->read_at(m_header.local_header_offset, &m_buffer[0], local_file_header_reader::minimum_length(), read_length);
+ 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(
@@ -1205,8 +1203,7 @@ std::error_condition zip_file_impl::get_compressed_data_offset(std::uint64_t &of
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::size_t read_length(0);
- std::error_condition const filerr = m_file->read_at(offset, buffer, m_header.compressed_length, read_length);
+ auto const [filerr, read_length] = read_at(*m_file, offset, buffer, m_header.compressed_length);
if (filerr)
{
osd_printf_error(
@@ -1277,12 +1274,11 @@ std::error_condition zip_file_impl::decompress_data_type_8(std::uint64_t offset,
while (true)
{
// read in the next chunk of data
- std::size_t read_length(0);
- auto const filerr = m_file->read_at(
+ 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())),
- read_length);
+ std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())));
if (filerr)
{
osd_printf_error(
@@ -1393,7 +1389,7 @@ std::error_condition zip_file_impl::decompress_data_type_14(std::uint64_t offset
m_header.file_name, m_filename);
return archive_file::error::DECOMPRESS_ERROR;
}
- filerr = m_file->read_at(offset, &m_buffer[0], 4, read_length);
+ std::tie(filerr, read_length) = read_at(*m_file, offset, &m_buffer[0], 4);
if (filerr)
{
osd_printf_error(
@@ -1425,7 +1421,7 @@ std::error_condition zip_file_impl::decompress_data_type_14(std::uint64_t offset
m_header.file_name, m_filename);
return archive_file::error::DECOMPRESS_ERROR;
}
- filerr = m_file->read_at(offset, &m_buffer[0], props_size, read_length);
+ std::tie(filerr, read_length) = read_at(*m_file, offset, &m_buffer[0], props_size);
if (filerr)
{
osd_printf_error(
@@ -1472,11 +1468,11 @@ std::error_condition 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_at(
+ std::tie(filerr, read_length) = read_at(
+ *m_file,
offset,
&m_buffer[0],
- std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())),
- read_length);
+ std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())));
if (filerr)
{
osd_printf_error(
@@ -1569,12 +1565,11 @@ std::error_condition zip_file_impl::decompress_data_type_93(std::uint64_t offset
while (input_remaining && length)
{
// read in the next chunk of data
- std::size_t read_length(0);
- auto const filerr = m_file->read_at(
+ 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())),
- read_length);
+ std::size_t(std::min<std::uint64_t>(input_remaining, m_buffer.size())));
if (filerr)
{
osd_printf_error(
diff --git a/src/lib/util/xmlfile.cpp b/src/lib/util/xmlfile.cpp
index 39604a29a87..db8d87930e2 100644
--- a/src/lib/util/xmlfile.cpp
+++ b/src/lib/util/xmlfile.cpp
@@ -41,14 +41,14 @@ constexpr unsigned TEMP_BUFFER_SIZE(4096U);
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)
{
- std::size_t written;
- file.write(&str[pos], found - pos, written);
+ write(file, &str[pos], found - pos);
switch (str[found])
{
case '"': file.puts("&quot;"); pos = found + 1; break;
@@ -60,8 +60,7 @@ void write_escaped(core_file &file, std::string const &str)
}
else
{
- std::size_t written;
- file.write(&str[pos], str.size() - pos, written);
+ write(file, &str[pos], str.size() - pos);
pos = found;
}
}
@@ -77,10 +76,12 @@ void write_escaped(core_file &file, std::string const &str)
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;
};
@@ -134,8 +135,7 @@ file::ptr file::read(read_stream &file, parse_options const *opts)
char tempbuf[TEMP_BUFFER_SIZE];
// read as much as we can
- size_t bytes;
- file.read(tempbuf, sizeof(tempbuf), bytes); // TODO: better error handling
+ auto const [err, bytes] = util::read(file, tempbuf, sizeof(tempbuf)); // FIXME: better error handling
done = !bytes;
// parse the data
@@ -815,7 +815,7 @@ std::string normalize_string(std::string_view string)
static bool expat_setup_parser(parse_info &info, parse_options const *opts)
{
// setup info structure
- memset(&info, 0, sizeof(info));
+ info = parse_info();
if (opts != nullptr)
{
info.flags = opts->flags;