diff options
Diffstat (limited to 'src/lib/util/chd.cpp')
-rw-r--r-- | src/lib/util/chd.cpp | 2086 |
1 files changed, 1157 insertions, 929 deletions
diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 3b61d044cd3..c05977a84eb 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -2,26 +2,31 @@ // copyright-holders:Aaron Giles /*************************************************************************** - chd.c - MAME Compressed Hunks of Data file format ***************************************************************************/ -#include <assert.h> - #include "chd.h" + #include "avhuff.h" -#include "hashing.h" -#include "flac.h" #include "cdrom.h" +#include "corefile.h" #include "coretmpl.h" +#include "flac.h" +#include "hashing.h" +#include "multibyte.h" + +#include "eminline.h" + #include <zlib.h> -#include <time.h> -#include <stddef.h> -#include <stdlib.h> + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <cstdlib> +#include <ctime> #include <new> -#include "eminline.h" +#include <tuple> //************************************************************************** @@ -101,12 +106,12 @@ enum // description of where a metadata entry lives within the file struct chd_file::metadata_entry { - uint64_t offset; // offset within the file of the header - uint64_t next; // offset within the file of the next header - uint64_t prev; // offset within the file of the previous header - uint32_t length; // length of the metadata - uint32_t metatag; // metadata tag - uint8_t flags; // flag bits + uint64_t offset; // offset within the file of the header + uint64_t next; // offset within the file of the next header + uint64_t prev; // offset within the file of the previous header + uint32_t length; // length of the metadata + uint32_t metatag; // metadata tag + uint8_t flags; // flag bits }; @@ -114,7 +119,7 @@ struct chd_file::metadata_entry struct chd_file::metadata_hash { - uint8_t tag[4]; // tag of the metadata in big-endian + uint8_t tag[4]; // tag of the metadata in big-endian util::sha1_t sha1; // hash data }; @@ -125,41 +130,11 @@ struct chd_file::metadata_hash //************************************************************************** //------------------------------------------------- -// be_read - extract a big-endian number from -// a byte buffer -//------------------------------------------------- - -inline uint64_t chd_file::be_read(const uint8_t *base, int numbytes) -{ - uint64_t result = 0; - while (numbytes--) - result = (result << 8) | *base++; - return result; -} - - -//------------------------------------------------- -// be_write - write a big-endian number to a byte -// buffer -//------------------------------------------------- - -inline void chd_file::be_write(uint8_t *base, uint64_t value, int numbytes) -{ - base += numbytes; - while (numbytes--) - { - *--base = value; - value >>= 8; - } -} - - -//------------------------------------------------- // be_read_sha1 - fetch a sha1_t from a data // stream in bigendian order //------------------------------------------------- -inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base) +inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base) const noexcept { util::sha1_t result; memcpy(&result.m_raw[0], base, sizeof(result.m_raw)); @@ -172,7 +147,7 @@ inline util::sha1_t chd_file::be_read_sha1(const uint8_t *base) // stream in bigendian order //------------------------------------------------- -inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) +inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) noexcept { memcpy(base, &value.m_raw[0], sizeof(value.m_raw)); } @@ -180,39 +155,45 @@ inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) //------------------------------------------------- // file_read - read from the file at the given -// offset; on failure throw an error +// offset. //------------------------------------------------- -inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length) +inline std::error_condition chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const noexcept { // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // seek and read - m_file->seek(offset, SEEK_SET); - uint32_t count = m_file->read(dest, length); - if (count != length) - throw CHDERR_READ_ERROR; + std::error_condition err; + err = m_file->seek(offset, SEEK_SET); + if (UNEXPECTED(err)) + return err; + size_t count; + std::tie(err, count) = read(*m_file, dest, length); + if (UNEXPECTED(!err && (count != length))) + return std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is truncated) + return err; } //------------------------------------------------- // file_write - write to the file at the given -// offset; on failure throw an error +// offset. //------------------------------------------------- -inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t length) +inline std::error_condition chd_file::file_write(uint64_t offset, const void *source, uint32_t length) noexcept { // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // seek and write - m_file->seek(offset, SEEK_SET); - uint32_t count = m_file->write(source, length); - if (count != length) - throw CHDERR_WRITE_ERROR; + std::error_condition err; + err = m_file->seek(offset, SEEK_SET); + if (UNEXPECTED(err)) + return err; + return write(*m_file, source, length).first; } @@ -225,14 +206,20 @@ inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t l inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint32_t alignment) { // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (UNEXPECTED(!m_file)) + throw std::error_condition(error::NOT_OPEN); // seek to the end and align if necessary - m_file->seek(0, SEEK_END); + std::error_condition err; + err = m_file->seek(0, SEEK_END); + if (UNEXPECTED(err)) + throw err; if (alignment != 0) { - uint64_t offset = m_file->tell(); + uint64_t offset; + err = m_file->tell(offset); + if (UNEXPECTED(err)) + throw err; uint32_t delta = offset % alignment; if (delta != 0) { @@ -242,20 +229,24 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3 delta = alignment - delta; while (delta != 0) { - uint32_t bytes_to_write = (std::min<std::size_t>)(sizeof(buffer), delta); - uint32_t count = m_file->write(buffer, bytes_to_write); - if (count != bytes_to_write) - throw CHDERR_WRITE_ERROR; - delta -= bytes_to_write; + uint32_t bytes_to_write = std::min<std::size_t>(sizeof(buffer), delta); + size_t count; + std::tie(err, count) = write(*m_file, buffer, bytes_to_write); + if (UNEXPECTED(err)) + throw err; + delta -= count; } } } // write the real data - uint64_t offset = m_file->tell(); - uint32_t count = m_file->write(source, length); - if (count != length) - throw CHDERR_READ_ERROR; + uint64_t offset; + err = m_file->tell(offset); + if (UNEXPECTED(err)) + throw err; + std::tie(err, std::ignore) = write(*m_file, source, length); + if (UNEXPECTED(err)) + throw err; return offset; } @@ -265,11 +256,14 @@ inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint3 // necessary to represent all numbers 0..value //------------------------------------------------- -inline uint8_t chd_file::bits_for_value(uint64_t value) +inline uint8_t chd_file::bits_for_value(uint64_t value) noexcept { uint8_t result = 0; while (value != 0) - value >>= 1, result++; + { + value >>= 1; + result++; + } return result; } @@ -288,11 +282,8 @@ inline uint8_t chd_file::bits_for_value(uint64_t value) */ chd_file::chd_file() - : m_file(nullptr), - m_owns_file(false) { // reset state - memset(m_decompressor, 0, sizeof(m_decompressor)); close(); } @@ -311,6 +302,32 @@ chd_file::~chd_file() } /** + * @fn util::random_read chd_file::file() + * + * @brief ------------------------------------------------- + * file - return our underlying file + * -------------------------------------------------. + * + * @return A random_read. + */ + +util::random_read &chd_file::file() +{ + assert(m_file); + return *m_file; +} + +bool chd_file::parent_missing() const noexcept +{ + if (m_parent_missing) + return true; + else if (!m_parent) + return false; + else + return m_parent->parent_missing(); +} + +/** * @fn util::sha1_t chd_file::sha1() * * @brief ------------------------------------------------- @@ -320,20 +337,14 @@ chd_file::~chd_file() * @return A sha1_t. */ -util::sha1_t chd_file::sha1() +util::sha1_t chd_file::sha1() const noexcept { - try - { - // read the big-endian version - uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); - return be_read_sha1(rawbuf); - } - catch (chd_error &) - { - // on failure, return nullptr - return util::sha1_t::null; - } + // read the big-endian version + uint8_t rawbuf[sizeof(util::sha1_t)]; + std::error_condition err = file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + return util::sha1_t::null; // on failure, return null + return be_read_sha1(rawbuf); } /** @@ -349,22 +360,24 @@ util::sha1_t chd_file::sha1() * @return A sha1_t. */ -util::sha1_t chd_file::raw_sha1() +util::sha1_t chd_file::raw_sha1() const noexcept { try { // determine offset within the file for data-only - if (m_rawsha1_offset == 0) - throw CHDERR_UNSUPPORTED_VERSION; + if (UNEXPECTED(!m_rawsha1_offset)) + throw std::error_condition(error::UNSUPPORTED_VERSION); // read the big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_read(m_rawsha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; return be_read_sha1(rawbuf); } - catch (chd_error &) + catch (std::error_condition const &) { - // on failure, return nullptr + // on failure, return null return util::sha1_t::null; } } @@ -382,20 +395,22 @@ util::sha1_t chd_file::raw_sha1() * @return A sha1_t. */ -util::sha1_t chd_file::parent_sha1() +util::sha1_t chd_file::parent_sha1() const noexcept { try { // determine offset within the file - if (m_parentsha1_offset == 0) - throw CHDERR_UNSUPPORTED_VERSION; + if (UNEXPECTED(!m_parentsha1_offset)) + throw std::error_condition(error::UNSUPPORTED_VERSION); // read the big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; - file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_read(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; return be_read_sha1(rawbuf); } - catch (chd_error &) + catch (std::error_condition const &) { // on failure, return nullptr return util::sha1_t::null; @@ -403,7 +418,7 @@ util::sha1_t chd_file::parent_sha1() } /** - * @fn chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes) + * @fn std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes) * * @brief ------------------------------------------------- * hunk_info - return information about this hunk @@ -413,60 +428,62 @@ util::sha1_t chd_file::parent_sha1() * @param [in,out] compressor The compressor. * @param [in,out] compbytes The compbytes. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes) +std::error_condition chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes) { // error if invalid if (hunknum >= m_hunkcount) - return CHDERR_HUNK_OUT_OF_RANGE; + return std::error_condition(error::HUNK_OUT_OF_RANGE); // get the map pointer - uint8_t *rawmap; switch (m_version) { - // v3/v4 map entries - case 3: - case 4: - rawmap = &m_rawmap[16 * hunknum]; + // v3/v4 map entries + case 3: + case 4: + { + uint8_t const *const rawmap = &m_rawmap[16 * hunknum]; switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) { - case V34_MAP_ENTRY_TYPE_COMPRESSED: - compressor = CHD_CODEC_ZLIB; - compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16); - break; + case V34_MAP_ENTRY_TYPE_COMPRESSED: + compressor = CHD_CODEC_ZLIB; + compbytes = get_u16be(&rawmap[12]) + (rawmap[14] << 16); + break; - case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: - compressor = CHD_CODEC_NONE; - compbytes = m_hunkbytes; - break; + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + compressor = CHD_CODEC_NONE; + compbytes = m_hunkbytes; + break; - case V34_MAP_ENTRY_TYPE_MINI: - compressor = CHD_CODEC_MINI; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_MINI: + compressor = CHD_CODEC_MINI; + compbytes = 0; + break; - case V34_MAP_ENTRY_TYPE_SELF_HUNK: - compressor = CHD_CODEC_SELF; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + compressor = CHD_CODEC_SELF; + compbytes = 0; + break; - case V34_MAP_ENTRY_TYPE_PARENT_HUNK: - compressor = CHD_CODEC_PARENT; - compbytes = 0; - break; + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + compressor = CHD_CODEC_PARENT; + compbytes = 0; + break; } - break; + } + break; - // v5 map entries - case 5: - rawmap = &m_rawmap[m_mapentrybytes * hunknum]; + // v5 map entries + case 5: + { + uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum]; - // uncompressed case if (!compressed()) { - if (be_read(&rawmap[0], 4) == 0) + // uncompressed case + if (get_u32be(&rawmap[0]) == 0) { compressor = CHD_CODEC_PARENT; compbytes = 0; @@ -476,18 +493,18 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint compressor = CHD_CODEC_NONE; compbytes = m_hunkbytes; } - break; } - - // compressed case - switch (rawmap[0]) + else { + // compressed case + switch (rawmap[0]) + { case COMPRESSION_TYPE_0: case COMPRESSION_TYPE_1: case COMPRESSION_TYPE_2: case COMPRESSION_TYPE_3: compressor = m_compression[rawmap[0]]; - compbytes = be_read(&rawmap[1], 3); + compbytes = get_u24be(&rawmap[1]); break; case COMPRESSION_NONE: @@ -506,11 +523,14 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint break; default: - return CHDERR_UNKNOWN_COMPRESSION; + return error::UNKNOWN_COMPRESSION; + } } - break; + } + break; } - return CHDERR_NONE; + + return std::error_condition(); } /** @@ -523,20 +543,36 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint * @param rawdata The rawdata. */ -void chd_file::set_raw_sha1(util::sha1_t rawdata) +std::error_condition chd_file::set_raw_sha1(util::sha1_t rawdata) noexcept { + uint64_t const offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset; + assert(offset != 0); + // create a big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; be_write_sha1(rawbuf, rawdata); // write to the header - uint64_t offset = (m_rawsha1_offset != 0) ? m_rawsha1_offset : m_sha1_offset; - assert(offset != 0); - file_write(offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_write(offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + return err; - // if we have a separate rawsha1_offset, update the full sha1 as well - if (m_rawsha1_offset != 0) - metadata_update_hash(); + try + { + // if we have a separate rawsha1_offset, update the full sha1 as well + if (m_rawsha1_offset != 0) + metadata_update_hash(); + } + catch (std::error_condition const &err) + { + return err; + } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } + + return std::error_condition(); } /** @@ -551,23 +587,24 @@ void chd_file::set_raw_sha1(util::sha1_t rawdata) * @param parent The parent. */ -void chd_file::set_parent_sha1(util::sha1_t parent) +std::error_condition chd_file::set_parent_sha1(util::sha1_t parent) noexcept { // if no file, fail - if (m_file == nullptr) - throw CHDERR_INVALID_FILE; + if (UNEXPECTED(!m_file)) + return std::error_condition(error::INVALID_FILE); + + assert(m_parentsha1_offset != 0); // create a big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; be_write_sha1(rawbuf, parent); // write to the header - assert(m_parentsha1_offset != 0); - file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); + return file_write(m_parentsha1_offset, rawbuf, sizeof(rawbuf)); } /** - * @fn chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) + * @fn std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4]) * * @brief ------------------------------------------------- * create - create a new file with no parent using an existing opened file handle @@ -579,30 +616,36 @@ void chd_file::set_parent_sha1(util::sha1_t parent) * @param unitbytes The unitbytes. * @param compression The compression. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) +std::error_condition chd_file::create( + util::random_read_write::ptr &&file, + uint64_t logicalbytes, + uint32_t hunkbytes, + uint32_t unitbytes, + const chd_codec_type (&compression)[4]) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; + else if (UNEXPECTED(!file)) + return std::errc::invalid_argument; // set the header parameters m_logicalbytes = logicalbytes; m_hunkbytes = hunkbytes; m_unitbytes = unitbytes; memcpy(m_compression, compression, sizeof(m_compression)); - m_parent = nullptr; + m_parent.reset(); // take ownership of the file - m_file = &file; - m_owns_file = false; + m_file = std::move(file); return create_common(); } /** - * @fn chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) + * @fn std::error_condition chd_file::create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent) * * @brief ------------------------------------------------- * create - create a new file with a parent using an existing opened file handle @@ -614,30 +657,36 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_ * @param compression The compression. * @param [in,out] parent The parent. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) +std::error_condition chd_file::create( + util::random_read_write::ptr &&file, + uint64_t logicalbytes, + uint32_t hunkbytes, + const chd_codec_type (&compression)[4], + chd_file &parent) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; + else if (UNEXPECTED(!file)) + return std::errc::invalid_argument; // set the header parameters m_logicalbytes = logicalbytes; m_hunkbytes = hunkbytes; m_unitbytes = parent.unit_bytes(); memcpy(m_compression, compression, sizeof(m_compression)); - m_parent = &parent; + m_parent = std::shared_ptr<chd_file>(std::shared_ptr<chd_file>(), &parent); // take ownership of the file - m_file = &file; - m_owns_file = false; + m_file = std::move(file); return create_common(); } /** - * @fn chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) + * @fn std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, const chd_codec_type (&compression)[4]) * * @brief ------------------------------------------------- * create - create a new file with no parent using a filename @@ -649,40 +698,40 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_ * @param unitbytes The unitbytes. * @param compression The compression. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]) +std::error_condition chd_file::create( + std::string_view filename, + uint64_t logicalbytes, + uint32_t hunkbytes, + uint32_t unitbytes, + const chd_codec_type (&compression)[4]) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; // create the new file util::core_file::ptr file; - const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr != osd_file::error::NONE) - return CHDERR_FILE_NOT_FOUND; + std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); + if (UNEXPECTED(filerr)) + return filerr; // create the file normally, then claim the file - const chd_error chderr = create(*file, logicalbytes, hunkbytes, unitbytes, compression); - m_owns_file = true; + std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, unitbytes, compression); // if an error happened, close and delete the file - if (chderr != CHDERR_NONE) + if (UNEXPECTED(chderr)) { file.reset(); - osd_file::remove(filename); - } - else - { - file.release(); + osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view } return chderr; } /** - * @fn chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) + * @fn std::error_condition chd_file::create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, const chd_codec_type (&compression)[4], chd_file &parent) * * @brief ------------------------------------------------- * create - create a new file with a parent using a filename @@ -694,40 +743,40 @@ chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t * @param compression The compression. * @param [in,out] parent The parent. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent) +std::error_condition chd_file::create( + std::string_view filename, + uint64_t logicalbytes, + uint32_t hunkbytes, + const chd_codec_type (&compression)[4], + chd_file &parent) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; // create the new file util::core_file::ptr file; - const osd_file::error filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); - if (filerr != osd_file::error::NONE) - return CHDERR_FILE_NOT_FOUND; + std::error_condition filerr = util::core_file::open(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); + if (UNEXPECTED(filerr)) + return filerr; // create the file normally, then claim the file - const chd_error chderr = create(*file, logicalbytes, hunkbytes, compression, parent); - m_owns_file = true; + std::error_condition chderr = create(std::move(file), logicalbytes, hunkbytes, compression, parent); // if an error happened, close and delete the file - if (chderr != CHDERR_NONE) + if (UNEXPECTED(chderr)) { file.reset(); - osd_file::remove(filename); - } - else - { - file.release(); + osd_file::remove(std::string(filename)); // FIXME: allow osd_file to use std::string_view } return chderr; } /** - * @fn chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent) + * @fn std::error_condition chd_file::open(std::string_view filename, bool writeable, chd_file *parent) * * @brief ------------------------------------------------- * open - open an existing file for read or read/write @@ -737,35 +786,32 @@ chd_error chd_file::create(const char *filename, uint64_t logicalbytes, uint32_t * @param writeable true if writeable. * @param [in,out] parent If non-null, the parent. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent) +std::error_condition chd_file::open( + std::string_view filename, + bool writeable, + chd_file *parent, + const open_parent_func &open_parent) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; // open the file const uint32_t openflags = writeable ? (OPEN_FLAG_READ | OPEN_FLAG_WRITE) : OPEN_FLAG_READ; util::core_file::ptr file; - const osd_file::error filerr = util::core_file::open(filename, openflags, file); - if (filerr != osd_file::error::NONE) - return CHDERR_FILE_NOT_FOUND; + std::error_condition filerr = util::core_file::open(filename, openflags, file); + if (UNEXPECTED(filerr)) + return filerr; // now open the CHD - chd_error err = open(*file, writeable, parent); - if (err != CHDERR_NONE) - return err; - - // we now own this file - file.release(); - m_owns_file = true; - return err; + return open(std::move(file), writeable, parent, open_parent); } /** - * @fn chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent) + * @fn std::error_condition chd_file::open(util::random_read_write::ptr &&file, bool writeable, chd_file *parent) * * @brief ------------------------------------------------- * open - open an existing file for read or read/write @@ -775,21 +821,26 @@ chd_error chd_file::open(const char *filename, bool writeable, chd_file *parent) * @param writeable true if writeable. * @param [in,out] parent If non-null, the parent. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent) +std::error_condition chd_file::open( + util::random_read_write::ptr &&file, + bool writeable, + chd_file *parent, + const open_parent_func &open_parent) { // make sure we don't already have a file open - if (m_file != nullptr) - return CHDERR_ALREADY_OPEN; + if (UNEXPECTED(m_file)) + return error::ALREADY_OPEN; + else if (UNEXPECTED(!file)) + return std::errc::invalid_argument; // open the file - m_file = &file; - m_owns_file = false; - m_parent = parent; + m_file = std::move(file); + m_parent = std::shared_ptr<chd_file>(std::shared_ptr<chd_file>(), parent); m_cachehunk = ~0; - return open_common(writeable); + return open_common(writeable, open_parent); } /** @@ -803,10 +854,7 @@ chd_error chd_file::open(util::core_file &file, bool writeable, chd_file *parent void chd_file::close() { // reset file characteristics - if (m_owns_file && m_file) - delete m_file; - m_file = nullptr; - m_owns_file = false; + m_file.reset(); m_allow_reads = false; m_allow_writes = false; @@ -819,8 +867,8 @@ void chd_file::close() m_hunkcount = 0; m_unitbytes = 0; m_unitcount = 0; - memset(m_compression, 0, sizeof(m_compression)); - m_parent = nullptr; + std::fill(std::begin(m_compression), std::end(m_compression), 0); + m_parent.reset(); m_parent_missing = false; // reset key offsets within the header @@ -836,10 +884,7 @@ void chd_file::close() // reset compression management for (auto & elem : m_decompressor) - { - delete elem; - elem = nullptr; - } + elem.reset(); m_compressed.clear(); // reset caching @@ -847,8 +892,107 @@ void chd_file::close() m_cachehunk = ~0; } +std::error_condition chd_file::codec_process_hunk(uint32_t hunknum) +{ + // punt if no file + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); + + // return an error if out of range + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); + + // wrap this for clean reporting + try + { + // get a pointer to the map entry + switch (m_version) + { + // v3/v4 map entries + case 3: + case 4: + { + uint8_t const *const rawmap = &m_rawmap[16 * hunknum]; + uint64_t const blockoffs = get_u64be(&rawmap[0]); + switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) + { + case V34_MAP_ENTRY_TYPE_COMPRESSED: + { + uint32_t const blocklen = get_u16be(&rawmap[12]) | (uint32_t(rawmap[14]) << 16); + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; + m_decompressor[0]->process(&m_compressed[0], blocklen); + return std::error_condition(); + } + + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + case V34_MAP_ENTRY_TYPE_MINI: + return std::error_condition(error::UNSUPPORTED_FORMAT); + + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + return codec_process_hunk(blockoffs); + + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + return m_parent->codec_process_hunk(blockoffs); + } + } + break; + + // v5 map entries + case 5: + { + if (UNEXPECTED(!compressed())) + return std::error_condition(error::UNSUPPORTED_FORMAT); + + // compressed case + uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum]; + uint32_t const blocklen = get_u24be(&rawmap[1]); + uint64_t const blockoffs = get_u48be(&rawmap[4]); + switch (rawmap[0]) + { + case COMPRESSION_TYPE_0: + case COMPRESSION_TYPE_1: + case COMPRESSION_TYPE_2: + case COMPRESSION_TYPE_3: + { + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; + auto &decompressor = *m_decompressor[rawmap[0]]; + decompressor.process(&m_compressed[0], blocklen); + return std::error_condition(); + } + + case COMPRESSION_NONE: + return std::error_condition(error::UNSUPPORTED_FORMAT); + + case COMPRESSION_SELF: + return codec_process_hunk(blockoffs); + + case COMPRESSION_PARENT: + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + return m_parent->codec_process_hunk(blockoffs / (m_parent->hunk_bytes() / m_parent->unit_bytes())); + } + break; + } + } + + // if we get here, the map contained an unsupported block type + return std::error_condition(error::INVALID_DATA); + } + catch (std::error_condition const &err) + { + // just return errors + return err; + } +} + /** - * @fn chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) + * @fn std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) * * @brief ------------------------------------------------- * read - read a single hunk from the CHD file @@ -867,137 +1011,158 @@ void chd_file::close() * @param hunknum The hunknum. * @param [in,out] buffer If non-null, the buffer. * - * @return The hunk. + * @return An error condition. */ -chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) +std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) { + // punt if no file + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); + + // return an error if out of range + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); + + auto *const dest = reinterpret_cast<uint8_t *>(buffer); + // wrap this for clean reporting try { - // punt if no file - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; - - // return an error if out of range - if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; - // get a pointer to the map entry - uint64_t blockoffs; - uint32_t blocklen; - util::crc32_t blockcrc; - uint8_t *rawmap; - uint8_t *dest = reinterpret_cast<uint8_t *>(buffer); switch (m_version) { - // v3/v4 map entries - case 3: - case 4: - rawmap = &m_rawmap[16 * hunknum]; - blockoffs = be_read(&rawmap[0], 8); - blockcrc = be_read(&rawmap[8], 4); + // v3/v4 map entries + case 3: + case 4: + { + uint8_t const *const rawmap = &m_rawmap[16 * hunknum]; + uint64_t const blockoffs = get_u64be(&rawmap[0]); + util::crc32_t const blockcrc = get_u32be(&rawmap[8]); + bool const nocrc = rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC; switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) { - case V34_MAP_ENTRY_TYPE_COMPRESSED: - blocklen = be_read(&rawmap[12], 2) + (rawmap[14] << 16); - file_read(blockoffs, &m_compressed[0], blocklen); + case V34_MAP_ENTRY_TYPE_COMPRESSED: + { + uint32_t const blocklen = get_u16be(&rawmap[12]) | (uint32_t(rawmap[14]) << 16); + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; m_decompressor[0]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && dest != nullptr && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; - - case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: - file_read(blockoffs, dest, m_hunkbytes); - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; - - case V34_MAP_ENTRY_TYPE_MINI: - be_write(dest, blockoffs, 8); - for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++) - dest[bytes] = dest[bytes - 8]; - if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; - - case V34_MAP_ENTRY_TYPE_SELF_HUNK: - return read_hunk(blockoffs, dest); + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } - case V34_MAP_ENTRY_TYPE_PARENT_HUNK: - if (m_parent_missing) - throw CHDERR_REQUIRES_PARENT; - return m_parent->read_hunk(blockoffs, dest); + case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: + { + std::error_condition err = file_read(blockoffs, dest, m_hunkbytes); + if (UNEXPECTED(err)) + return err; + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } + + case V34_MAP_ENTRY_TYPE_MINI: + put_u64be(dest, blockoffs); + for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + if (UNEXPECTED(!nocrc && (util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc))) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + + case V34_MAP_ENTRY_TYPE_SELF_HUNK: + return read_hunk(blockoffs, dest); + + case V34_MAP_ENTRY_TYPE_PARENT_HUNK: + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + return m_parent->read_hunk(blockoffs, dest); } - break; + } + break; - // v5 map entries - case 5: - rawmap = &m_rawmap[m_mapentrybytes * hunknum]; + // v5 map entries + case 5: + { + uint8_t const *const rawmap = &m_rawmap[m_mapentrybytes * hunknum]; - // uncompressed case if (!compressed()) { - blockoffs = uint64_t(be_read(rawmap, 4)) * uint64_t(m_hunkbytes); + // uncompressed case + uint64_t const blockoffs = mulu_32x32(get_u32be(rawmap), m_hunkbytes); if (blockoffs != 0) - file_read(blockoffs, dest, m_hunkbytes); - else if (m_parent_missing) - throw CHDERR_REQUIRES_PARENT; - else if (m_parent != nullptr) - m_parent->read_hunk(hunknum, dest); + return file_read(blockoffs, dest, m_hunkbytes); + else if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + else if (m_parent) + return m_parent->read_hunk(hunknum, dest); else memset(dest, 0, m_hunkbytes); - return CHDERR_NONE; + return std::error_condition(); } - - // compressed case - blocklen = be_read(&rawmap[1], 3); - blockoffs = be_read(&rawmap[4], 6); - blockcrc = be_read(&rawmap[10], 2); - switch (rawmap[0]) + else { + // compressed case + uint32_t const blocklen = get_u24be(&rawmap[1]); + uint64_t const blockoffs = get_u48be(&rawmap[4]); + util::crc16_t const blockcrc = get_u16be(&rawmap[10]); + switch (rawmap[0]) + { case COMPRESSION_TYPE_0: case COMPRESSION_TYPE_1: case COMPRESSION_TYPE_2: case COMPRESSION_TYPE_3: - file_read(blockoffs, &m_compressed[0], blocklen); - m_decompressor[rawmap[0]]->decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); - if (!m_decompressor[rawmap[0]]->lossy() && dest != nullptr && util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - if (m_decompressor[rawmap[0]]->lossy() && util::crc16_creator::simple(&m_compressed[0], blocklen) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; + { + std::error_condition err = file_read(blockoffs, &m_compressed[0], blocklen); + if (UNEXPECTED(err)) + return err; + auto &decompressor = *m_decompressor[rawmap[0]]; + decompressor.decompress(&m_compressed[0], blocklen, dest, m_hunkbytes); + util::crc16_t const calculated = !decompressor.lossy() + ? util::crc16_creator::simple(dest, m_hunkbytes) + : util::crc16_creator::simple(&m_compressed[0], blocklen); + if (UNEXPECTED(calculated != blockcrc)) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } case COMPRESSION_NONE: - file_read(blockoffs, dest, m_hunkbytes); - if (util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; + { + std::error_condition err = file_read(blockoffs, dest, m_hunkbytes); + if (UNEXPECTED(err)) + return err; + if (UNEXPECTED(util::crc16_creator::simple(dest, m_hunkbytes) != blockcrc)) + return std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); + } case COMPRESSION_SELF: return read_hunk(blockoffs, dest); case COMPRESSION_PARENT: - if (m_parent_missing) - throw CHDERR_REQUIRES_PARENT; - return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); + if (UNEXPECTED(m_parent_missing)) + return std::error_condition(error::REQUIRES_PARENT); + return m_parent->read_bytes(blockoffs * m_parent->unit_bytes(), dest, m_hunkbytes); + } } break; + } } - // if we get here, something was wrong - throw CHDERR_READ_ERROR; + // if we get here, the map contained an unsupported block type + return std::error_condition(error::INVALID_DATA); } - - // just return errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @fn chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer) + * @fn std::error_condition chd_file::write_hunk(uint32_t hunknum, const void *buffer) * * @brief ------------------------------------------------- * write - write a single hunk to the CHD file @@ -1012,78 +1177,83 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) * @param hunknum The hunknum. * @param buffer The buffer. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer) +std::error_condition chd_file::write_hunk(uint32_t hunknum, const void *buffer) { - // wrap this for clean reporting - try - { - // punt if no file - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + // punt if no file + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); - // return an error if out of range - if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; + // return an error if out of range + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); - // if not writeable, fail - if (!m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + // if not writeable, fail + if (UNEXPECTED(!m_allow_writes)) + return std::error_condition(error::FILE_NOT_WRITEABLE); - // uncompressed writes only via this interface - if (compressed()) - throw CHDERR_FILE_NOT_WRITEABLE; + // uncompressed writes only via this interface + if (UNEXPECTED(compressed())) + return std::error_condition(error::FILE_NOT_WRITEABLE); - // see if we have allocated the space on disk for this hunk - uint8_t *rawmap = &m_rawmap[hunknum * 4]; - uint32_t rawentry = be_read(rawmap, 4); + // see if we have allocated the space on disk for this hunk + uint8_t *const rawmap = &m_rawmap[hunknum * 4]; + uint32_t rawentry = get_u32be(rawmap); - // if not, allocate one now - if (rawentry == 0) + // if not, allocate one now + if (rawentry == 0) + { + // first make sure we need to allocate it + bool all_zeros = true; + const auto *scan = reinterpret_cast<const uint32_t *>(buffer); + for (uint32_t index = 0; index < m_hunkbytes / 4; index++) { - // first make sure we need to allocate it - bool all_zeros = true; - const uint32_t *scan = reinterpret_cast<const uint32_t *>(buffer); - for (uint32_t index = 0; index < m_hunkbytes / 4; index++) - if (scan[index] != 0) - { - all_zeros = false; - break; - } + if (scan[index] != 0) + { + all_zeros = false; + break; + } + } - // if it's all zeros, do nothing more - if (all_zeros) - return CHDERR_NONE; + // if it's all zeros, do nothing more + if (all_zeros) + return std::error_condition(); + // wrap this for clean reporting + try + { // append new data to the end of the file, aligning the first chunk rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes; + } + catch (std::error_condition const &err) + { + // just return errors + return err; + } - // write the map entry back - be_write(rawmap, rawentry, 4); - file_write(m_mapoffset + hunknum * 4, rawmap, 4); + // write the map entry back + put_u32be(rawmap, rawentry); + std::error_condition err = file_write(m_mapoffset + hunknum * 4, rawmap, 4); + if (UNEXPECTED(err)) + return err; - // update the cached hunk if we just wrote it - if (hunknum == m_cachehunk && buffer != &m_cache[0]) - memcpy(&m_cache[0], buffer, m_hunkbytes); - } + // update the cached hunk if we just wrote it + if (hunknum == m_cachehunk && buffer != &m_cache[0]) + memcpy(&m_cache[0], buffer, m_hunkbytes); - // otherwise, just overwrite - else - file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes); - return CHDERR_NONE; + return std::error_condition(); } - - // just return errors - catch (chd_error &err) + else { - return err; + // otherwise, just overwrite + return file_write(uint64_t(rawentry) * uint64_t(m_hunkbytes), buffer, m_hunkbytes); } } /** - * @fn chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count) + * @fn std::error_condition chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count) * * @brief ------------------------------------------------- * read_units - read the given number of units from the CHD @@ -1096,13 +1266,13 @@ chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer) * @return The units. */ -chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count) +std::error_condition chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count) { return read_bytes(unitnum * uint64_t(m_unitbytes), buffer, count * m_unitbytes); } /** - * @fn chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count) + * @fn std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count) * * @brief ------------------------------------------------- * write_units - write the given number of units to the CHD @@ -1112,16 +1282,16 @@ chd_error chd_file::read_units(uint64_t unitnum, void *buffer, uint32_t count) * @param buffer The buffer. * @param count Number of. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count) +std::error_condition chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t count) { return write_bytes(unitnum * uint64_t(m_unitbytes), buffer, count * m_unitbytes); } /** - * @fn chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) + * @fn std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) * * @brief ------------------------------------------------- * read_bytes - read from the CHD at a byte level, using the cache to handle partial @@ -1135,46 +1305,46 @@ chd_error chd_file::write_units(uint64_t unitnum, const void *buffer, uint32_t c * @return The bytes. */ -chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) +std::error_condition chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) { // iterate over hunks - uint32_t first_hunk = offset / m_hunkbytes; - uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes; - uint8_t *dest = reinterpret_cast<uint8_t *>(buffer); + uint32_t const first_hunk = offset / m_hunkbytes; + uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes; + auto *dest = reinterpret_cast<uint8_t *>(buffer); for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++) { // determine start/end boundaries - uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; - uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); - // if it's a full block, just read directly from disk unless it's the cached hunk - chd_error err = CHDERR_NONE; - if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) - err = read_hunk(curhunk, dest); - - // otherwise, read from the cache + if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk)) + { + // if it's a full block, just read directly from disk unless it's the cached hunk + std::error_condition err = read_hunk(curhunk, dest); + if (UNEXPECTED(err)) + return err; + } else { + // otherwise, read from the cache if (curhunk != m_cachehunk) { - err = read_hunk(curhunk, &m_cache[0]); - if (err != CHDERR_NONE) + std::error_condition err = read_hunk(curhunk, &m_cache[0]); + if (UNEXPECTED(err)) return err; m_cachehunk = curhunk; } memcpy(dest, &m_cache[startoffs], endoffs + 1 - startoffs); } - // handle errors and advance - if (err != CHDERR_NONE) - return err; + // advance dest += endoffs + 1 - startoffs; } - return CHDERR_NONE; + return std::error_condition(); } /** - * @fn chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes) + * @fn std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes) * * @brief ------------------------------------------------- * write_bytes - write to the CHD at a byte level, using the cache to handle partial @@ -1185,33 +1355,34 @@ chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) * @param buffer The buffer. * @param bytes The bytes. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes) +std::error_condition chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t bytes) { // iterate over hunks - uint32_t first_hunk = offset / m_hunkbytes; - uint32_t last_hunk = (offset + bytes - 1) / m_hunkbytes; - const uint8_t *source = reinterpret_cast<const uint8_t *>(buffer); + uint32_t const first_hunk = offset / m_hunkbytes; + uint32_t const last_hunk = (offset + bytes - 1) / m_hunkbytes; + auto const *source = reinterpret_cast<uint8_t const *>(buffer); for (uint32_t curhunk = first_hunk; curhunk <= last_hunk; curhunk++) { // determine start/end boundaries - uint32_t startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; - uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); + uint32_t const startoffs = (curhunk == first_hunk) ? (offset % m_hunkbytes) : 0; + uint32_t const endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); - // if it's a full block, just write directly to disk unless it's the cached hunk - chd_error err = CHDERR_NONE; - if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) + std::error_condition err; + if ((startoffs == 0) && (endoffs == m_hunkbytes - 1) && (curhunk != m_cachehunk)) + { + // if it's a full block, just write directly to disk unless it's the cached hunk err = write_hunk(curhunk, source); - - // otherwise, write from the cache + } else { + // otherwise, write from the cache if (curhunk != m_cachehunk) { err = read_hunk(curhunk, &m_cache[0]); - if (err != CHDERR_NONE) + if (UNEXPECTED(err)) return err; m_cachehunk = curhunk; } @@ -1220,15 +1391,15 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by } // handle errors and advance - if (err != CHDERR_NONE) + if (UNEXPECTED(err)) return err; source += endoffs + 1 - startoffs; } - return CHDERR_NONE; + return std::error_condition(); } /** - * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output) + * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output) * * @brief ------------------------------------------------- * read_metadata - read the indexed metadata of the given type @@ -1241,34 +1412,24 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by * @param searchindex The searchindex. * @param [in,out] output The output. * - * @return The metadata. + * @return An error condition. */ -chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output) +std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output) { - // wrap this for clean reporting - try - { - // if we didn't find it, just return - metadata_entry metaentry; - if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; - - // read the metadata - output.assign(metaentry.length, '\0'); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return CHDERR_NONE; - } - - // just return errors - catch (chd_error &err) - { + // if we didn't find it, just return + metadata_entry metaentry; + if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - } + + // read the metadata + try { output.assign(metaentry.length, '\0'); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); } /** - * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output) + * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output) * * @brief Reads a metadata. * @@ -1279,34 +1440,24 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @param searchindex The searchindex. * @param [in,out] output The output. * - * @return The metadata. + * @return An error condition. */ -chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output) +std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output) { - // wrap this for clean reporting - try - { - // if we didn't find it, just return - metadata_entry metaentry; - if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; - - // read the metadata - output.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return CHDERR_NONE; - } - - // just return errors - catch (chd_error &err) - { + // if we didn't find it, just return + metadata_entry metaentry; + if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - } + + // read the metadata + try { output.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + return file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); } /** - * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen) + * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen) * * @brief Reads a metadata. * @@ -1319,34 +1470,23 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @param outputlen The outputlen. * @param [in,out] resultlen The resultlen. * - * @return The metadata. + * @return An error condition. */ -chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen) +std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen) { - // wrap this for clean reporting - try - { - // if we didn't find it, just return - metadata_entry metaentry; - if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; - - // read the metadata - resultlen = metaentry.length; - file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen)); - return CHDERR_NONE; - } - - // just return errors - catch (chd_error &err) - { + // if we didn't find it, just return + metadata_entry metaentry; + if (std::error_condition err = metadata_find(searchtag, searchindex, metaentry)) return err; - } + + // read the metadata + resultlen = metaentry.length; + return file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen)); } /** - * @fn chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags) + * @fn std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags) * * @brief Reads a metadata. * @@ -1359,36 +1499,32 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @param [in,out] resulttag The resulttag. * @param [in,out] resultflags The resultflags. * - * @return The metadata. + * @return An error condition. */ -chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags) +std::error_condition chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags) { - // wrap this for clean reporting - try - { - // if we didn't find it, just return - metadata_entry metaentry; - if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; - - // read the metadata - output.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - resulttag = metaentry.metatag; - resultflags = metaentry.flags; - return CHDERR_NONE; - } + std::error_condition err; - // just return errors - catch (chd_error &err) - { + // if we didn't find it, just return + metadata_entry metaentry; + err = metadata_find(searchtag, searchindex, metaentry); + if (err) return err; - } + + // read the metadata + try { output.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); + if (UNEXPECTED(err)) + return err; + resulttag = metaentry.metatag; + resultflags = metaentry.flags; + return std::error_condition(); } /** - * @fn chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags) + * @fn std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags) * * @brief ------------------------------------------------- * write_metadata - write the indexed metadata of the given type @@ -1400,77 +1536,94 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @param inputlen The inputlen. * @param flags The flags. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags) +std::error_condition chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags) { - // wrap this for clean reporting - try + // must write at least 1 byte and no more than 16MB + if (UNEXPECTED((inputlen < 1) || (inputlen >= 16 * 1024 * 1024))) + return std::error_condition(std::errc::invalid_argument); + + // find the entry if it already exists + metadata_entry metaentry; + bool finished = false; + std::error_condition err = metadata_find(metatag, metaindex, metaentry); + if (!err) { - // must write at least 1 byte and no more than 16MB - if (inputlen < 1 || inputlen >= 16 * 1024 * 1024) - return CHDERR_INVALID_PARAMETER; - - // find the entry if it already exists - metadata_entry metaentry; - bool finished = false; - if (metadata_find(metatag, metaindex, metaentry)) + if (inputlen <= metaentry.length) { // if the new data fits over the old data, just overwrite - if (inputlen <= metaentry.length) - { - file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen); - - // if the lengths don't match, we need to update the length in our header - if (inputlen != metaentry.length) - { - uint8_t length[3]; - be_write(length, inputlen, 3); - file_write(metaentry.offset + 5, length, sizeof(length)); - } + err = file_write(metaentry.offset + METADATA_HEADER_SIZE, inputbuf, inputlen); + if (UNEXPECTED(err)) + return err; - // indicate we did everything - finished = true; + // if the lengths don't match, we need to update the length in our header + if (inputlen != metaentry.length) + { + uint8_t length[3]; + put_u24be(length, inputlen); + err = file_write(metaentry.offset + 5, length, sizeof(length)); + if (UNEXPECTED(err)) + return err; } + // indicate we did everything + finished = true; + } + else + { // if it doesn't fit, unlink the current entry - else - metadata_set_previous_next(metaentry.prev, metaentry.next); + err = metadata_set_previous_next(metaentry.prev, metaentry.next); + if (UNEXPECTED(err)) + return err; } + } + else if (UNEXPECTED(err != error::METADATA_NOT_FOUND)) + { + return err; + } + // wrap this for clean reporting + try + { // if not yet done, create a new entry and append if (!finished) { // now build us a new entry uint8_t raw_meta_header[METADATA_HEADER_SIZE]; - be_write(&raw_meta_header[0], metatag, 4); + put_u32be(&raw_meta_header[0], metatag); raw_meta_header[4] = flags; - be_write(&raw_meta_header[5], (inputlen & 0x00ffffff) | (flags << 24), 3); - be_write(&raw_meta_header[8], 0, 8); + put_u24be(&raw_meta_header[5], inputlen & 0x00ffffff); + put_u64be(&raw_meta_header[8], 0); // append the new header, then the data uint64_t offset = file_append(raw_meta_header, sizeof(raw_meta_header)); file_append(inputbuf, inputlen); // set the previous entry to point to us - metadata_set_previous_next(metaentry.prev, offset); + err = metadata_set_previous_next(metaentry.prev, offset); + if (UNEXPECTED(err)) + return err; } // update the hash metadata_update_hash(); - return CHDERR_NONE; + return std::error_condition(); } - - // return any errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // return any errors return err; } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } } /** - * @fn chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex) + * @fn std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex) * * @brief ------------------------------------------------- * delete_metadata - remove the given metadata from the list @@ -1482,33 +1635,22 @@ chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, * @param metatag The metatag. * @param metaindex The metaindex. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex) +std::error_condition chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex) { - // wrap this for clean reporting - try - { - // find the entry - metadata_entry metaentry; - if (!metadata_find(metatag, metaindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; - - // point the previous to the next, unlinking us - metadata_set_previous_next(metaentry.prev, metaentry.next); - return CHDERR_NONE; - } - - // return any errors - catch (chd_error &err) - { + // find the entry + metadata_entry metaentry; + if (std::error_condition err = metadata_find(metatag, metaindex, metaentry)) return err; - } + + // point the previous to the next, unlinking us + return metadata_set_previous_next(metaentry.prev, metaentry.next); } /** - * @fn chd_error chd_file::clone_all_metadata(chd_file &source) + * @fn std::error_condition chd_file::clone_all_metadata(chd_file &source) * * @brief ------------------------------------------------- * clone_all_metadata - clone the metadata from one CHD to a second @@ -1518,40 +1660,37 @@ chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex * * @param [in,out] source Another instance to copy. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::clone_all_metadata(chd_file &source) +std::error_condition chd_file::clone_all_metadata(chd_file &source) { - // wrap this for clean reporting - try - { - // iterate over metadata entries in the source - std::vector<uint8_t> filedata; - metadata_entry metaentry; - metaentry.metatag = 0; - metaentry.length = 0; - metaentry.next = 0; - metaentry.flags = 0; - for (bool has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) - { - // read the metadata item - filedata.resize(metaentry.length); - source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); - - // write it to the destination - chd_error err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags); - if (err != CHDERR_NONE) - throw err; - } - return CHDERR_NONE; - } + // iterate over metadata entries in the source + std::vector<uint8_t> filedata; + metadata_entry metaentry; + metaentry.metatag = 0; + metaentry.length = 0; + metaentry.next = 0; + metaentry.flags = 0; + std::error_condition err; + for (err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = source.metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) + { + // read the metadata item + try { filedata.resize(metaentry.length); } + catch (std::bad_alloc const &) { return std::errc::not_enough_memory; } + err = source.file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + if (UNEXPECTED(err)) + return err; - // return any errors - catch (chd_error &err) - { - return err; + // write it to the destination + err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags); + if (UNEXPECTED(err)) + return err; } + if (err == error::METADATA_NOT_FOUND) + return std::error_condition(); + else + return err; } /** @@ -1577,22 +1716,27 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1) std::vector<uint8_t> filedata; std::vector<metadata_hash> hasharray; metadata_entry metaentry; - for (bool has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); has_data; has_data = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) + std::error_condition err; + for (err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry); !err; err = metadata_find(CHDMETATAG_WILDCARD, 0, metaentry, true)) { // if not checksumming, continue - if ((metaentry.flags & CHD_MDFLAGS_CHECKSUM) == 0) + if (!(metaentry.flags & CHD_MDFLAGS_CHECKSUM)) continue; // allocate memory and read the data filedata.resize(metaentry.length); - file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + err = file_read(metaentry.offset + METADATA_HEADER_SIZE, &filedata[0], metaentry.length); + if (UNEXPECTED(err)) + throw err; // create an entry for this metadata and add it metadata_hash hashentry; - be_write(hashentry.tag, metaentry.metatag, 4); + put_u32be(hashentry.tag, metaentry.metatag); hashentry.sha1 = util::sha1_creator::simple(&filedata[0], metaentry.length); hasharray.push_back(hashentry); } + if (err != error::METADATA_NOT_FOUND) + throw err; // sort the array if (!hasharray.empty()) @@ -1607,7 +1751,7 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1) } /** - * @fn chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config) + * @fn std::error_condition chd_file::codec_configure(chd_codec_type codec, int param, void *config) * * @brief ------------------------------------------------- * codec_config - set internal codec parameters @@ -1617,27 +1761,26 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1) * @param param The parameter. * @param [in,out] config If non-null, the configuration. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *config) +std::error_condition chd_file::codec_configure(chd_codec_type codec, int param, void *config) { // wrap this for clean reporting try { // find the codec and call its configuration - for (int codecnum = 0; codecnum < ARRAY_LENGTH(m_compression); codecnum++) + for (int codecnum = 0; codecnum < std::size(m_compression); codecnum++) if (m_compression[codecnum] == codec) { m_decompressor[codecnum]->configure(param, config); - return CHDERR_NONE; + return std::error_condition(); } - return CHDERR_INVALID_PARAMETER; + return std::errc::invalid_argument; } - - // return any errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // return any errors return err; } } @@ -1654,44 +1797,49 @@ chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *confi * @return null if it fails, else a char*. */ -const char *chd_file::error_string(chd_error err) +std::error_category const &chd_category() noexcept { - switch (err) + class chd_category_impl : public std::error_category { - case CHDERR_NONE: return "no error"; - case CHDERR_NO_INTERFACE: return "no drive interface"; - case CHDERR_OUT_OF_MEMORY: return "out of memory"; - case CHDERR_NOT_OPEN: return "file not open"; - case CHDERR_ALREADY_OPEN: return "file already open"; - case CHDERR_INVALID_FILE: return "invalid file"; - case CHDERR_INVALID_PARAMETER: return "invalid parameter"; - case CHDERR_INVALID_DATA: return "invalid data"; - case CHDERR_FILE_NOT_FOUND: return "file not found"; - case CHDERR_REQUIRES_PARENT: return "requires parent"; - case CHDERR_FILE_NOT_WRITEABLE: return "file not writeable"; - case CHDERR_READ_ERROR: return "read error"; - case CHDERR_WRITE_ERROR: return "write error"; - case CHDERR_CODEC_ERROR: return "codec error"; - case CHDERR_INVALID_PARENT: return "invalid parent"; - case CHDERR_HUNK_OUT_OF_RANGE: return "hunk out of range"; - case CHDERR_DECOMPRESSION_ERROR: return "decompression error"; - case CHDERR_COMPRESSION_ERROR: return "compression error"; - case CHDERR_CANT_CREATE_FILE: return "can't create file"; - case CHDERR_CANT_VERIFY: return "can't verify file"; - case CHDERR_NOT_SUPPORTED: return "operation not supported"; - case CHDERR_METADATA_NOT_FOUND: return "can't find metadata"; - case CHDERR_INVALID_METADATA_SIZE: return "invalid metadata size"; - case CHDERR_UNSUPPORTED_VERSION: return "mismatched DIFF and CHD or unsupported CHD version"; - case CHDERR_VERIFY_INCOMPLETE: return "incomplete verify"; - case CHDERR_INVALID_METADATA: return "invalid metadata"; - case CHDERR_INVALID_STATE: return "invalid state"; - case CHDERR_OPERATION_PENDING: return "operation pending"; - case CHDERR_UNSUPPORTED_FORMAT: return "unsupported format"; - case CHDERR_UNKNOWN_COMPRESSION: return "unknown compression type"; - case CHDERR_WALKING_PARENT: return "currently examining parent"; - case CHDERR_COMPRESSING: return "currently compressing"; - default: return "undocumented error"; - } + virtual char const *name() const noexcept override { return "chd"; } + + virtual std::string message(int condition) const override + { + using namespace std::literals; + static std::string_view const s_messages[] = { + "No error"sv, + "No drive interface"sv, + "File not open"sv, + "File already open"sv, + "Invalid file"sv, + "Invalid data"sv, + "Requires parent"sv, + "File not writeable"sv, + "Codec error"sv, + "Invalid parent"sv, + "Hunk out of range"sv, + "Decompression error"sv, + "Compression error"sv, + "Can't verify file"sv, + "Can't find metadata"sv, + "Invalid metadata size"sv, + "Mismatched DIFF and CHD or unsupported CHD version"sv, + "Incomplete verify"sv, + "Invalid metadata"sv, + "Invalid state"sv, + "Operation pending"sv, + "Unsupported format"sv, + "Unknown compression type"sv, + "Currently examining parent"sv, + "Currently compressing"sv }; + if ((0 <= condition) && (std::size(s_messages) > condition)) + return std::string(s_messages[condition]); + else + return "Unknown error"s; + } + }; + static chd_category_impl const s_chd_category_instance; + return s_chd_category_instance; } @@ -1716,16 +1864,16 @@ uint32_t chd_file::guess_unitbytes() // look for hard disk metadata; if found, then the unit size == sector size std::string metadata; int i0, i1, i2, i3; - if (read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) == CHDERR_NONE && sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) + if (!read_metadata(HARD_DISK_METADATA_TAG, 0, metadata) && sscanf(metadata.c_str(), HARD_DISK_METADATA_FORMAT, &i0, &i1, &i2, &i3) == 4) return i3; // look for CD-ROM metadata; if found, then the unit size == CD frame size - if (read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) == CHDERR_NONE || - read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata) == CHDERR_NONE) - return CD_FRAME_SIZE; + if (!read_metadata(CDROM_OLD_METADATA_TAG, 0, metadata) || + !read_metadata(CDROM_TRACK_METADATA_TAG, 0, metadata) || + !read_metadata(CDROM_TRACK_METADATA2_TAG, 0, metadata) || + !read_metadata(GDROM_OLD_METADATA_TAG, 0, metadata) || + !read_metadata(GDROM_TRACK_METADATA_TAG, 0, metadata)) + return cdrom_file::FRAME_SIZE; // otherwise, just map 1:1 with the hunk size return m_hunkbytes; @@ -1750,28 +1898,28 @@ uint32_t chd_file::guess_unitbytes() void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (be_read(&rawheader[8], 4) != V3_HEADER_SIZE) - throw CHDERR_INVALID_FILE; + if (UNEXPECTED(get_u32be(&rawheader[8]) != V3_HEADER_SIZE)) + throw std::error_condition(error::INVALID_FILE); // extract core info - m_logicalbytes = be_read(&rawheader[28], 8); + m_logicalbytes = get_u64be(&rawheader[28]); m_mapoffset = 120; - m_metaoffset = be_read(&rawheader[36], 8); - m_hunkbytes = be_read(&rawheader[76], 4); - m_hunkcount = be_read(&rawheader[24], 4); + m_metaoffset = get_u64be(&rawheader[36]); + m_hunkbytes = get_u32be(&rawheader[76]); + m_hunkcount = get_u32be(&rawheader[24]); // extract parent SHA-1 - uint32_t flags = be_read(&rawheader[16], 4); + uint32_t flags = get_u32be(&rawheader[16]); m_allow_writes = (flags & 2) == 0; // determine compression - switch (be_read(&rawheader[20], 4)) + switch (get_u32be(&rawheader[20])) { case 0: m_compression[0] = CHD_CODEC_NONE; break; case 1: m_compression[0] = CHD_CODEC_ZLIB; break; case 2: m_compression[0] = CHD_CODEC_ZLIB; break; case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; - default: throw CHDERR_UNKNOWN_COMPRESSION; + default: throw std::error_condition(error::UNKNOWN_COMPRESSION); } m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; @@ -1813,28 +1961,28 @@ void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1) void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (be_read(&rawheader[8], 4) != V4_HEADER_SIZE) - throw CHDERR_INVALID_FILE; + if (UNEXPECTED(get_u32be(&rawheader[8]) != V4_HEADER_SIZE)) + throw std::error_condition(error::INVALID_FILE); // extract core info - m_logicalbytes = be_read(&rawheader[28], 8); + m_logicalbytes = get_u64be(&rawheader[28]); m_mapoffset = 108; - m_metaoffset = be_read(&rawheader[36], 8); - m_hunkbytes = be_read(&rawheader[44], 4); - m_hunkcount = be_read(&rawheader[24], 4); + m_metaoffset = get_u64be(&rawheader[36]); + m_hunkbytes = get_u32be(&rawheader[44]); + m_hunkcount = get_u32be(&rawheader[24]); // extract parent SHA-1 - uint32_t flags = be_read(&rawheader[16], 4); + uint32_t flags = get_u32be(&rawheader[16]); m_allow_writes = (flags & 2) == 0; // determine compression - switch (be_read(&rawheader[20], 4)) + switch (get_u32be(&rawheader[20])) { case 0: m_compression[0] = CHD_CODEC_NONE; break; case 1: m_compression[0] = CHD_CODEC_ZLIB; break; case 2: m_compression[0] = CHD_CODEC_ZLIB; break; case 3: m_compression[0] = CHD_CODEC_AVHUFF; break; - default: throw CHDERR_UNKNOWN_COMPRESSION; + default: throw std::error_condition(error::UNKNOWN_COMPRESSION); } m_compression[1] = m_compression[2] = m_compression[3] = CHD_CODEC_NONE; @@ -1873,23 +2021,23 @@ void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1) void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1) { // verify header length - if (be_read(&rawheader[8], 4) != V5_HEADER_SIZE) - throw CHDERR_INVALID_FILE; + if (UNEXPECTED(get_u32be(&rawheader[8]) != V5_HEADER_SIZE)) + throw std::error_condition(error::INVALID_FILE); // extract core info - m_logicalbytes = be_read(&rawheader[32], 8); - m_mapoffset = be_read(&rawheader[40], 8); - m_metaoffset = be_read(&rawheader[48], 8); - m_hunkbytes = be_read(&rawheader[56], 4); + m_logicalbytes = get_u64be(&rawheader[32]); + m_mapoffset = get_u64be(&rawheader[40]); + m_metaoffset = get_u64be(&rawheader[48]); + m_hunkbytes = get_u32be(&rawheader[56]); m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes; - m_unitbytes = be_read(&rawheader[60], 4); + m_unitbytes = get_u32be(&rawheader[60]); m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes; // determine compression - m_compression[0] = be_read(&rawheader[16], 4); - m_compression[1] = be_read(&rawheader[20], 4); - m_compression[2] = be_read(&rawheader[24], 4); - m_compression[3] = be_read(&rawheader[28], 4); + m_compression[0] = get_u32be(&rawheader[16]); + m_compression[1] = get_u32be(&rawheader[20]); + m_compression[2] = get_u32be(&rawheader[24]); + m_compression[3] = get_u32be(&rawheader[28]); m_allow_writes = !compressed(); @@ -1908,7 +2056,7 @@ void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1) } /** - * @fn chd_error chd_file::compress_v5_map() + * @fn std::error_condition chd_file::compress_v5_map() * * @brief ------------------------------------------------- * compress_v5_map - compress the v5 map and write it to the end of the file @@ -1917,10 +2065,10 @@ void chd_file::parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1) * @exception CHDERR_COMPRESSION_ERROR Thrown when a chderr compression error error * condition occurs. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::compress_v5_map() +std::error_condition chd_file::compress_v5_map() { try { @@ -1943,14 +2091,14 @@ chd_error chd_file::compress_v5_map() uint32_t max_complen = 0; uint8_t lastcomp = 0; int count = 0; - for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++) { uint8_t curcomp = m_rawmap[hunknum * 12 + 0]; - // promote self block references to more compact forms if (curcomp == COMPRESSION_SELF) { - uint32_t refhunk = be_read(&m_rawmap[hunknum * 12 + 4], 6); + // promote self block references to more compact forms + uint32_t refhunk = get_u48be(&m_rawmap[hunknum * 12 + 4]); if (refhunk == last_self) curcomp = COMPRESSION_SELF_0; else if (refhunk == last_self + 1) @@ -1959,12 +2107,11 @@ chd_error chd_file::compress_v5_map() max_self = std::max(max_self, refhunk); last_self = refhunk; } - - // promote parent block references to more compact forms else if (curcomp == COMPRESSION_PARENT) { - uint32_t refunit = be_read(&m_rawmap[hunknum * 12 + 4], 6); - if (refunit == (uint64_t(hunknum) * uint64_t(m_hunkbytes)) / m_unitbytes) + // promote parent block references to more compact forms + uint32_t refunit = get_u48be(&m_rawmap[hunknum * 12 + 4]); + if (refunit == mulu_32x32(hunknum, m_hunkbytes) / m_unitbytes) curcomp = COMPRESSION_PARENT_SELF; else if (refunit == last_parent) curcomp = COMPRESSION_PARENT_0; @@ -1977,7 +2124,7 @@ chd_error chd_file::compress_v5_map() // track maximum compressed length else //if (curcomp >= COMPRESSION_TYPE_0 && curcomp <= COMPRESSION_TYPE_3) - max_complen = std::max(max_complen, uint32_t(be_read(&m_rawmap[hunknum * 12 + 1], 3))); + max_complen = std::max<uint32_t>(max_complen, get_u24be(&m_rawmap[hunknum * 12 + 1])); // track repeats if (curcomp == lastcomp) @@ -2010,37 +2157,48 @@ chd_error chd_file::compress_v5_map() } } - // compute a tree and export it to the buffer - std::vector<uint8_t> compressed(m_hunkcount * 6); + // determine the number of bits we need to hold the a length and a hunk index + const uint8_t lengthbits = bits_for_value(max_complen); + const uint8_t selfbits = bits_for_value(max_self); + const uint8_t parentbits = bits_for_value(max_parent); + + // determine the needed size of the output buffer + // 16 bytes is required for the header + // max len per entry given to huffman encoder at instantiation is 8 bits + // this corresponds to worst-case max 12 bits per entry when RLE encoded. + // max additional bits per entry after RLE encoded tree is + // for COMPRESSION_TYPE_0-3: lengthbits+16 + // for COMPRESSION_NONE: 16 + // for COMPRESSION_SELF: selfbits + // for COMPRESSION_PARENT: parentbits + // the overall size is clamped later with bitbuf.flush() + int nbits_needed = (8*16) + (12 + std::max<int>({lengthbits+16, selfbits, parentbits}))*m_hunkcount; + std::vector<uint8_t> compressed(nbits_needed / 8 + 1); bitstream_out bitbuf(&compressed[16], compressed.size() - 16); + + // compute a tree and export it to the buffer huffman_error err = encoder.compute_tree_from_histo(); - if (err != HUFFERR_NONE) - throw CHDERR_COMPRESSION_ERROR; + if (UNEXPECTED(err != HUFFERR_NONE)) + throw std::error_condition(error::COMPRESSION_ERROR); err = encoder.export_tree_rle(bitbuf); - if (err != HUFFERR_NONE) - throw CHDERR_COMPRESSION_ERROR; + if (UNEXPECTED(err != HUFFERR_NONE)) + throw std::error_condition(error::COMPRESSION_ERROR); // encode the data for (uint8_t *src = &compression_rle[0]; src < dest; src++) encoder.encode_one(bitbuf, *src); - // determine the number of bits we need to hold the a length - // and a hunk index - uint8_t lengthbits = bits_for_value(max_complen); - uint8_t selfbits = bits_for_value(max_self); - uint8_t parentbits = bits_for_value(max_parent); - // for each compression type, output the relevant data lastcomp = 0; count = 0; uint8_t *src = &compression_rle[0]; uint64_t firstoffs = 0; - for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++) { uint8_t *rawmap = &m_rawmap[hunknum * 12]; - uint32_t length = be_read(&rawmap[1], 3); - uint64_t offset = be_read(&rawmap[4], 6); - uint16_t crc = be_read(&rawmap[10], 2); + uint32_t length = get_u24be(&rawmap[1]); + uint64_t offset = get_u48be(&rawmap[4]); + uint16_t crc = get_u16be(&rawmap[10]); // if no count remaining, fetch the next entry if (count == 0) @@ -2098,9 +2256,9 @@ chd_error chd_file::compress_v5_map() // write the map header uint32_t complen = bitbuf.flush(); assert(!bitbuf.overflow()); - be_write(&compressed[0], complen, 4); - be_write(&compressed[4], firstoffs, 6); - be_write(&compressed[10], mapcrc, 2); + put_u32be(&compressed[0], complen); + put_u48be(&compressed[4], firstoffs); + put_u16be(&compressed[10], mapcrc); compressed[12] = lengthbits; compressed[13] = selfbits; compressed[14] = parentbits; @@ -2111,14 +2269,17 @@ chd_error chd_file::compress_v5_map() // then write the map offset uint8_t rawbuf[sizeof(uint64_t)]; - be_write(rawbuf, m_mapoffset, 8); - file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); - return CHDERR_NONE; + put_u64be(rawbuf, m_mapoffset); + return file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); } - catch (chd_error &err) + catch (std::error_condition const &err) { return err; } + catch (std::bad_alloc const &) + { + return std::errc::not_enough_memory; + } } /** @@ -2143,27 +2304,32 @@ void chd_file::decompress_v5_map() // read the reader uint8_t rawbuf[16]; - file_read(m_mapoffset, rawbuf, sizeof(rawbuf)); - uint32_t const mapbytes = be_read(&rawbuf[0], 4); - uint64_t const firstoffs = be_read(&rawbuf[4], 6); - util::crc16_t const mapcrc = be_read(&rawbuf[10], 2); + std::error_condition ioerr; + ioerr = file_read(m_mapoffset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(ioerr)) + throw ioerr; + uint32_t const mapbytes = get_u32be(&rawbuf[0]); + uint64_t const firstoffs = get_u48be(&rawbuf[4]); + util::crc16_t const mapcrc = get_u16be(&rawbuf[10]); uint8_t const lengthbits = rawbuf[12]; uint8_t const selfbits = rawbuf[13]; uint8_t const parentbits = rawbuf[14]; // now read the map std::vector<uint8_t> compressed(mapbytes); - file_read(m_mapoffset + 16, &compressed[0], mapbytes); + ioerr = file_read(m_mapoffset + 16, &compressed[0], mapbytes); + if (UNEXPECTED(ioerr)) + throw ioerr; bitstream_in bitbuf(&compressed[0], compressed.size()); // first decode the compression types huffman_decoder<16, 8> decoder; - huffman_error err = decoder.import_tree_rle(bitbuf); - if (err != HUFFERR_NONE) - throw CHDERR_DECOMPRESSION_ERROR; + huffman_error const huferr = decoder.import_tree_rle(bitbuf); + if (UNEXPECTED(huferr != HUFFERR_NONE)) + throw std::error_condition(error::DECOMPRESSION_ERROR); uint8_t lastcomp = 0; int repcount = 0; - for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++) { uint8_t *rawmap = &m_rawmap[hunknum * 12]; if (repcount > 0) @@ -2184,7 +2350,7 @@ void chd_file::decompress_v5_map() uint64_t curoffset = firstoffs; uint32_t last_self = 0; uint64_t last_parent = 0; - for (int hunknum = 0; hunknum < m_hunkcount; hunknum++) + for (uint32_t hunknum = 0; hunknum < m_hunkcount; hunknum++) { uint8_t *rawmap = &m_rawmap[hunknum * 12]; uint64_t offset = curoffset; @@ -2218,6 +2384,7 @@ void chd_file::decompress_v5_map() // pseudo-types; convert into base types case COMPRESSION_SELF_1: last_self++; + [[fallthrough]]; case COMPRESSION_SELF_0: rawmap[0] = COMPRESSION_SELF; offset = last_self; @@ -2225,28 +2392,29 @@ void chd_file::decompress_v5_map() case COMPRESSION_PARENT_SELF: rawmap[0] = COMPRESSION_PARENT; - last_parent = offset = (uint64_t(hunknum) * uint64_t(m_hunkbytes)) / m_unitbytes; + last_parent = offset = mulu_32x32(hunknum, m_hunkbytes) / m_unitbytes; break; case COMPRESSION_PARENT_1: last_parent += m_hunkbytes / m_unitbytes; + [[fallthrough]]; case COMPRESSION_PARENT_0: rawmap[0] = COMPRESSION_PARENT; offset = last_parent; break; } - be_write(&rawmap[1], length, 3); - be_write(&rawmap[4], offset, 6); - be_write(&rawmap[10], crc, 2); + put_u24be(&rawmap[1], length); + put_u48be(&rawmap[4], offset); + put_u16be(&rawmap[10], crc); } // verify the final CRC - if (util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc) - throw CHDERR_DECOMPRESSION_ERROR; + if (UNEXPECTED(util::crc16_creator::simple(&m_rawmap[0], m_hunkcount * 12) != mapcrc)) + throw std::error_condition(error::DECOMPRESSION_ERROR); } /** - * @fn chd_error chd_file::create_common() + * @fn std::error_condition chd_file::create_common() * * @brief ------------------------------------------------- * create_common - command path when creating a new CHD file @@ -2262,7 +2430,7 @@ void chd_file::decompress_v5_map() * @return The new common. */ -chd_error chd_file::create_common() +std::error_condition chd_file::create_common() { // wrap in try for proper error handling try @@ -2271,14 +2439,14 @@ chd_error chd_file::create_common() m_metaoffset = 0; // if we have a parent, it must be V3 or later - if (m_parent != nullptr && m_parent->version() < 3) - throw CHDERR_UNSUPPORTED_VERSION; + if (UNEXPECTED(m_parent && m_parent->version() < 3)) + throw std::error_condition(error::UNSUPPORTED_VERSION); // must be an even number of units per hunk - if (m_hunkbytes % m_unitbytes != 0) - throw CHDERR_INVALID_PARAMETER; - if (m_parent != nullptr && m_unitbytes != m_parent->unit_bytes()) - throw CHDERR_INVALID_PARAMETER; + if (UNEXPECTED(m_hunkbytes % m_unitbytes != 0)) + throw std::error_condition(std::errc::invalid_argument); + if (UNEXPECTED(m_parent && m_unitbytes != m_parent->unit_bytes())) + throw std::error_condition(std::errc::invalid_argument); // verify the compression types bool found_zero = false; @@ -2288,31 +2456,33 @@ chd_error chd_file::create_common() if (elem == CHD_CODEC_NONE) found_zero = true; else if (found_zero) - throw CHDERR_INVALID_PARAMETER; + throw std::error_condition(std::errc::invalid_argument); else if (!chd_codec_list::codec_exists(elem)) - throw CHDERR_UNKNOWN_COMPRESSION; + throw std::error_condition(error::UNKNOWN_COMPRESSION); } // create our V5 header uint8_t rawheader[V5_HEADER_SIZE]; memcpy(&rawheader[0], "MComprHD", 8); - be_write(&rawheader[8], V5_HEADER_SIZE, 4); - be_write(&rawheader[12], m_version, 4); - be_write(&rawheader[16], m_compression[0], 4); - be_write(&rawheader[20], m_compression[1], 4); - be_write(&rawheader[24], m_compression[2], 4); - be_write(&rawheader[28], m_compression[3], 4); - be_write(&rawheader[32], m_logicalbytes, 8); - be_write(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE, 8); - be_write(&rawheader[48], m_metaoffset, 8); - be_write(&rawheader[56], m_hunkbytes, 4); - be_write(&rawheader[60], m_unitbytes, 4); + put_u32be(&rawheader[8], V5_HEADER_SIZE); + put_u32be(&rawheader[12], m_version); + put_u32be(&rawheader[16], m_compression[0]); + put_u32be(&rawheader[20], m_compression[1]); + put_u32be(&rawheader[24], m_compression[2]); + put_u32be(&rawheader[28], m_compression[3]); + put_u64be(&rawheader[32], m_logicalbytes); + put_u64be(&rawheader[40], compressed() ? 0 : V5_HEADER_SIZE); + put_u64be(&rawheader[48], m_metaoffset); + put_u32be(&rawheader[56], m_hunkbytes); + put_u32be(&rawheader[60], m_unitbytes); be_write_sha1(&rawheader[64], util::sha1_t::null); be_write_sha1(&rawheader[84], util::sha1_t::null); - be_write_sha1(&rawheader[104], (m_parent != nullptr) ? m_parent->sha1() : util::sha1_t::null); + be_write_sha1(&rawheader[104], m_parent ? m_parent->sha1() : util::sha1_t::null); // write the resulting header - file_write(0, rawheader, sizeof(rawheader)); + std::error_condition err = file_write(0, rawheader, sizeof(rawheader)); + if (UNEXPECTED(err)) + throw err; // parse it back out to set up fields appropriately util::sha1_t parentsha1; @@ -2330,8 +2500,10 @@ chd_error chd_file::create_common() uint64_t offset = m_mapoffset; while (mapsize != 0) { - uint32_t bytes_to_write = (std::min<size_t>)(mapsize, sizeof(buffer)); - file_write(offset, buffer, bytes_to_write); + uint32_t const bytes_to_write = std::min<size_t>(mapsize, sizeof(buffer)); + err = file_write(offset, buffer, bytes_to_write); + if (UNEXPECTED(err)) + throw err; offset += bytes_to_write; mapsize -= bytes_to_write; } @@ -2340,10 +2512,9 @@ chd_error chd_file::create_common() // finish opening the file create_open_common(); } - - // handle errors by closing ourself - catch (chd_error &err) + catch (std::error_condition const &err) { + // handle errors by closing ourself close(); return err; } @@ -2352,11 +2523,11 @@ chd_error chd_file::create_common() close(); throw; } - return CHDERR_NONE; + return std::error_condition(); } /** - * @fn chd_error chd_file::open_common(bool writeable) + * @fn std::error_condition chd_file::open_common(bool writeable) * * @brief ------------------------------------------------- * open_common - common path when opening an existing CHD file for input @@ -2375,10 +2546,10 @@ chd_error chd_file::create_common() * * @param writeable true if writeable. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file::open_common(bool writeable) +std::error_condition chd_file::open_common(bool writeable, const open_parent_func &open_parent) { // wrap in try for proper error handling try @@ -2388,13 +2559,15 @@ chd_error chd_file::open_common(bool writeable) // read the raw header uint8_t rawheader[MAX_HEADER_SIZE]; - file_read(0, rawheader, sizeof(rawheader)); + std::error_condition err = file_read(0, rawheader, sizeof(rawheader)); + if (UNEXPECTED(err)) + throw err; // verify the signature - if (memcmp(rawheader, "MComprHD", 8) != 0) - throw CHDERR_INVALID_FILE; + if (UNEXPECTED(memcmp(rawheader, "MComprHD", 8) != 0)) + throw std::error_condition(error::INVALID_FILE); - m_version = be_read(&rawheader[12], 4); + m_version = get_u32be(&rawheader[12]); // read the header if we support it util::sha1_t parentsha1 = util::sha1_t::null; @@ -2403,35 +2576,39 @@ chd_error chd_file::open_common(bool writeable) case 3: parse_v3_header(rawheader, parentsha1); break; case 4: parse_v4_header(rawheader, parentsha1); break; case 5: parse_v5_header(rawheader, parentsha1); break; - default: throw CHDERR_UNSUPPORTED_VERSION; + default: throw std::error_condition(error::UNSUPPORTED_VERSION); } // only allow writes to the most recent version if (m_version < HEADER_VERSION) m_allow_writes = false; - if (writeable && !m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + if (UNEXPECTED(writeable && !m_allow_writes)) + throw std::error_condition(error::FILE_NOT_WRITEABLE); // make sure we have a parent if we need one (and don't if we don't) if (parentsha1 != util::sha1_t::null) { - if (m_parent == nullptr) + if (!m_parent && open_parent) + m_parent = open_parent(parentsha1); + + if (!m_parent) m_parent_missing = true; else if (m_parent->sha1() != parentsha1) - throw CHDERR_INVALID_PARENT; + throw std::error_condition(error::INVALID_PARENT); + } + else if (UNEXPECTED(m_parent)) + { + throw std::error_condition(std::errc::invalid_argument); } - else if (m_parent != nullptr) - throw CHDERR_INVALID_PARAMETER; // finish opening the file create_open_common(); - return CHDERR_NONE; + return std::error_condition(); } - - // handle errors by closing ourself - catch (chd_error &err) + catch (std::error_condition const &err) { + // handle errors by closing ourself close(); return err; } @@ -2451,19 +2628,25 @@ chd_error chd_file::open_common(bool writeable) void chd_file::create_open_common() { // verify the compression types and initialize the codecs - for (int decompnum = 0; decompnum < ARRAY_LENGTH(m_compression); decompnum++) + for (int decompnum = 0; decompnum < std::size(m_compression); decompnum++) { m_decompressor[decompnum] = chd_codec_list::new_decompressor(m_compression[decompnum], *this); - if (m_decompressor[decompnum] == nullptr && m_compression[decompnum] != 0) - throw CHDERR_UNKNOWN_COMPRESSION; + if (UNEXPECTED(!m_decompressor[decompnum] && (m_compression[decompnum] != 0))) + throw std::error_condition(error::UNKNOWN_COMPRESSION); } // read the map; v5+ compressed drives need to read and decompress their map m_rawmap.resize(m_hunkcount * m_mapentrybytes); if (m_version >= 5 && compressed()) + { decompress_v5_map(); + } else - file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size()); + { + std::error_condition err = file_read(m_mapoffset, &m_rawmap[0], m_rawmap.size()); + if (UNEXPECTED(err)) + throw err; + } // allocate the temporary compressed buffer and a buffer for caching m_compressed.resize(m_hunkbytes); @@ -2478,44 +2661,37 @@ void chd_file::create_open_common() * for appending to a compressed CHD * -------------------------------------------------. * - * @exception CHDERR_NOT_OPEN Thrown when a chderr not open error condition occurs. - * @exception CHDERR_HUNK_OUT_OF_RANGE Thrown when a chderr hunk out of range error - * condition occurs. - * @exception CHDERR_FILE_NOT_WRITEABLE Thrown when a chderr file not writeable error - * condition occurs. - * @exception CHDERR_COMPRESSION_ERROR Thrown when a chderr compression error error - * condition occurs. - * * @param hunknum The hunknum. */ -void chd_file::verify_proper_compression_append(uint32_t hunknum) +std::error_condition chd_file::verify_proper_compression_append(uint32_t hunknum) const noexcept { // punt if no file - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (UNEXPECTED(!m_file)) + return std::error_condition(error::NOT_OPEN); // return an error if out of range - if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; + if (UNEXPECTED(hunknum >= m_hunkcount)) + return std::error_condition(error::HUNK_OUT_OF_RANGE); // if not writeable, fail - if (!m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + if (UNEXPECTED(!m_allow_writes)) + return std::error_condition(error::FILE_NOT_WRITEABLE); // compressed writes only via this interface - if (!compressed()) - throw CHDERR_FILE_NOT_WRITEABLE; + if (UNEXPECTED(!compressed())) + return std::error_condition(error::FILE_NOT_WRITEABLE); // only permitted to write new blocks - uint8_t *rawmap = &m_rawmap[hunknum * 12]; - if (rawmap[0] != 0xff) - throw CHDERR_COMPRESSION_ERROR; + uint8_t const *const rawmap = &m_rawmap[hunknum * 12]; + if (UNEXPECTED(rawmap[0] != 0xff)) + return std::error_condition(error::COMPRESSION_ERROR); + + // if this isn't the first block, only permitted to write immediately after the previous one + if (UNEXPECTED((hunknum != 0) && (rawmap[-12] == 0xff))) + return std::error_condition(error::COMPRESSION_ERROR); - // if this isn't the first block, only permitted to write immediately - // after the previous one - if (hunknum != 0 && rawmap[-12] == 0xff) - throw CHDERR_COMPRESSION_ERROR; + return std::error_condition(); } /** @@ -2536,7 +2712,9 @@ void chd_file::verify_proper_compression_append(uint32_t hunknum) void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // write the final result uint64_t offset = file_append(compressed, complength); @@ -2544,9 +2722,9 @@ void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const // update the map entry uint8_t *rawmap = &m_rawmap[hunknum * 12]; rawmap[0] = (compression == -1) ? COMPRESSION_NONE : compression; - be_write(&rawmap[1], complength, 3); - be_write(&rawmap[4], offset, 6); - be_write(&rawmap[10], crc16, 2); + put_u24be(&rawmap[1], complength); + put_u48be(&rawmap[4], offset); + put_u16be(&rawmap[10], crc16); } /** @@ -2566,18 +2744,20 @@ void chd_file::hunk_write_compressed(uint32_t hunknum, int8_t compression, const void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // only permitted to reference prior hunks - if (otherhunk >= hunknum) - throw CHDERR_INVALID_PARAMETER; + if (UNEXPECTED(otherhunk >= hunknum)) + throw std::error_condition(error::HUNK_OUT_OF_RANGE); // update the map entry uint8_t *rawmap = &m_rawmap[hunknum * 12]; rawmap[0] = COMPRESSION_SELF; - be_write(&rawmap[1], 0, 3); - be_write(&rawmap[4], otherhunk, 6); - be_write(&rawmap[10], 0, 2); + put_u24be(&rawmap[1], 0); + put_u48be(&rawmap[4], otherhunk); + put_u16be(&rawmap[10], 0); } /** @@ -2594,18 +2774,20 @@ void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk) void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit) { // verify that we are appending properly to a compressed file - verify_proper_compression_append(hunknum); + std::error_condition err = verify_proper_compression_append(hunknum); + if (UNEXPECTED(err)) + throw err; // update the map entry - uint8_t *rawmap = &m_rawmap[hunknum * 12]; + uint8_t *const rawmap = &m_rawmap[hunknum * 12]; rawmap[0] = COMPRESSION_PARENT; - be_write(&rawmap[1], 0, 3); - be_write(&rawmap[4], parentunit, 6); - be_write(&rawmap[10], 0, 2); + put_u24be(&rawmap[1], 0); + put_u48be(&rawmap[4], parentunit); + put_u16be(&rawmap[10], 0); } /** - * @fn bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) + * @fn std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) * * @brief ------------------------------------------------- * metadata_find - find a metadata entry @@ -2616,10 +2798,10 @@ void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit) * @param [in,out] metaentry The metaentry. * @param resume true to resume. * - * @return true if it succeeds, false if it fails. + * @return A std::error_condition (error::METADATA_NOT_FOUND if the search fails). */ -bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) +std::error_condition chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) const noexcept { // start at the beginning unless we're resuming a previous search if (!resume) @@ -2638,18 +2820,20 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada { // read the raw header uint8_t raw_meta_header[METADATA_HEADER_SIZE]; - file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); + std::error_condition err = file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); + if (UNEXPECTED(err)) + return err; // extract the data - metaentry.metatag = be_read(&raw_meta_header[0], 4); + metaentry.metatag = get_u32be(&raw_meta_header[0]); metaentry.flags = raw_meta_header[4]; - metaentry.length = be_read(&raw_meta_header[5], 3); - metaentry.next = be_read(&raw_meta_header[8], 8); + metaentry.length = get_u24be(&raw_meta_header[5]); + metaentry.next = get_u64be(&raw_meta_header[8]); // if we got a match, proceed if (metatag == CHDMETATAG_WILDCARD || metaentry.metatag == metatag) if (metaindex-- == 0) - return true; + return std::error_condition(); // no match, fetch the next link metaentry.prev = metaentry.offset; @@ -2657,7 +2841,7 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada } // if we get here, we didn't find it - return false; + return error::METADATA_NOT_FOUND; } /** @@ -2671,27 +2855,28 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada * @param nextoffset The nextoffset. */ -void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) +std::error_condition chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffset) noexcept { uint64_t offset = 0; - // if we were the first entry, make the next entry the first if (prevoffset == 0) { + // if we were the first entry, make the next entry the first offset = m_metaoffset_offset; m_metaoffset = nextoffset; } - - // otherwise, update the link in the previous header else + { + // otherwise, update the link in the previous header offset = prevoffset + 8; + } // create a big-endian version uint8_t rawbuf[sizeof(uint64_t)]; - be_write(rawbuf, nextoffset, 8); + put_u64be(rawbuf, nextoffset); // write to the header and update our local copy - file_write(offset, rawbuf, sizeof(rawbuf)); + return file_write(offset, rawbuf, sizeof(rawbuf)); } /** @@ -2705,7 +2890,7 @@ void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffs void chd_file::metadata_update_hash() { // only works for V4 and above, and only for compressed CHDs - if (m_version < 4 || !compressed()) + if ((m_version < 4) || !compressed()) return; // compute the new overall hash @@ -2716,7 +2901,9 @@ void chd_file::metadata_update_hash() be_write_sha1(&rawbuf[0], fullsha1); // write to the header - file_write(m_sha1_offset, rawbuf, sizeof(rawbuf)); + std::error_condition err = file_write(m_sha1_offset, rawbuf, sizeof(rawbuf)); + if (UNEXPECTED(err)) + throw err; } /** @@ -2751,19 +2938,18 @@ int CLIB_DECL chd_file::metadata_hash_compare(const void *elem1, const void *ele * -------------------------------------------------. */ -chd_file_compressor::chd_file_compressor() - : m_walking_parent(false), - m_total_in(0), - m_total_out(0), - m_read_queue(nullptr), - m_read_queue_offset(0), - m_read_done_offset(0), - m_read_error(false), - m_work_queue(nullptr), - m_write_hunk(0) +chd_file_compressor::chd_file_compressor() : + m_walking_parent(false), + m_total_in(0), + m_total_out(0), + m_read_queue(nullptr), + m_read_queue_offset(0), + m_read_done_offset(0), + m_work_queue(nullptr), + m_write_hunk(0) { // zap arrays - memset(m_codecs, 0, sizeof(m_codecs)); + std::fill(std::begin(m_codecs), std::end(m_codecs), nullptr); // allocate work queues m_read_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO); @@ -2800,7 +2986,7 @@ chd_file_compressor::~chd_file_compressor() void chd_file_compressor::compress_begin() { // reset state - m_walking_parent = (m_parent != nullptr); + m_walking_parent = bool(m_parent); m_total_in = 0; m_total_out = 0; m_compsha1.reset(); @@ -2812,7 +2998,7 @@ void chd_file_compressor::compress_begin() // reset read state m_read_queue_offset = 0; m_read_done_offset = 0; - m_read_error = false; + m_read_error.clear(); // reset work item state m_work_buffer.resize(hunk_bytes() * (WORK_BUFFER_HUNKS + 1)); @@ -2839,7 +3025,7 @@ void chd_file_compressor::compress_begin() } /** - * @fn chd_error chd_file_compressor::compress_continue(double &progress, double &ratio) + * @fn std::error_condition chd_file_compressor::compress_continue(double &progress, double &ratio) * * @brief ------------------------------------------------- * compress_continue - continue compression @@ -2848,25 +3034,24 @@ void chd_file_compressor::compress_begin() * @param [in,out] progress The progress. * @param [in,out] ratio The ratio. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chd_file_compressor::compress_continue(double &progress, double &ratio) +std::error_condition chd_file_compressor::compress_continue(double &progress, double &ratio) { - // if we got an error, return an error - if (m_read_error) - return CHDERR_READ_ERROR; + // if we got an error, return the error + if (UNEXPECTED(m_read_error)) + return m_read_error; // if done reading, queue some more while (m_read_queue_offset < m_logicalbytes && osd_work_queue_items(m_read_queue) < 2) { // see if we have enough free work items to read the next half of a buffer - uint32_t startitem = m_read_queue_offset / hunk_bytes(); - uint32_t enditem = startitem + WORK_BUFFER_HUNKS / 2; - uint32_t curitem; - for (curitem = startitem; curitem < enditem; curitem++) - if (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status != WS_READY) - break; + uint32_t const startitem = m_read_queue_offset / hunk_bytes(); + uint32_t const enditem = startitem + WORK_BUFFER_HUNKS / 2; + uint32_t curitem = startitem; + while ((curitem < enditem) && (m_work_item[curitem % WORK_BUFFER_HUNKS].m_status == WS_READY)) + ++curitem; // if it's not all clear, defer if (curitem != enditem) @@ -2890,64 +3075,60 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio work_item &item = m_work_item[m_write_hunk % WORK_BUFFER_HUNKS]; // free any OSD work item - if (item.m_osd != nullptr) + if (item.m_osd) + { osd_work_item_release(item.m_osd); - item.m_osd = nullptr; + item.m_osd = nullptr; + } - // for parent walking, just add to the hashmap if (m_walking_parent) { - uint32_t uph = hunk_bytes() / unit_bytes(); + // for parent walking, just add to the hashmap + uint32_t const uph = hunk_bytes() / unit_bytes(); uint32_t units = uph; if (item.m_hunknum == hunk_count() - 1 || !compressed()) units = 1; for (uint32_t unit = 0; unit < units; unit++) + { if (m_parent_map.find(item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1) == hashmap::NOT_FOUND) m_parent_map.add(item.m_hunknum * uph + unit, item.m_hash[unit].m_crc16, item.m_hash[unit].m_sha1); + } } - - // if we're uncompressed, use regular writes else if (!compressed()) { - chd_error err = write_hunk(item.m_hunknum, item.m_data); - if (err != CHDERR_NONE) + // if we're uncompressed, use regular writes + std::error_condition err = write_hunk(item.m_hunknum, item.m_data); + if (UNEXPECTED(err)) return err; // writes of all-0 data don't actually take space, so see if we count this chd_codec_type codec = CHD_CODEC_NONE; uint32_t complen; - hunk_info(item.m_hunknum, codec, complen); - if (codec == CHD_CODEC_NONE) + err = hunk_info(item.m_hunknum, codec, complen); + if (!err && codec == CHD_CODEC_NONE) // TODO: report error? m_total_out += m_hunkbytes; } - - // for compressing, process the result - else do + else if (uint64_t const selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); selfhunk != hashmap::NOT_FOUND) + { + // the hunk is in the self map + hunk_copy_from_self(item.m_hunknum, selfhunk); + } + else { - // first see if the hunk is in the parent or self maps - uint64_t selfhunk = m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); - if (selfhunk != hashmap::NOT_FOUND) + // if not, see if it's in the parent map + uint64_t const parentunit = m_parent ? m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) : hashmap::NOT_FOUND; + if (parentunit != hashmap::NOT_FOUND) { - hunk_copy_from_self(item.m_hunknum, selfhunk); - break; + hunk_copy_from_parent(item.m_hunknum, parentunit); } - - // if not, see if it's in the parent map - if (m_parent != nullptr) + else { - uint64_t parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); - if (parentunit != hashmap::NOT_FOUND) - { - hunk_copy_from_parent(item.m_hunknum, parentunit); - break; - } + // otherwise, append it compressed and add to the self map + hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16); + m_total_out += item.m_complen; + m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); } - - // otherwise, append it compressed and add to the self map - hunk_write_compressed(item.m_hunknum, item.m_compression, item.m_compressed, item.m_complen, item.m_hash[0].m_crc16); - m_total_out += item.m_complen; - m_current_map.add(item.m_hunknum, item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); - } while (0); + } // reset the item and advance item.m_status = WS_READY; @@ -2956,23 +3137,24 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio // if we hit the end, finalize if (m_write_hunk == m_hunkcount) { - // if this is just walking the parent, reset and get ready for compression if (m_walking_parent) { + // if this is just walking the parent, reset and get ready for compression m_walking_parent = false; m_read_queue_offset = m_read_done_offset = 0; m_write_hunk = 0; - for (auto & elem : m_work_item) + for (auto &elem : m_work_item) elem.m_status = WS_READY; } - - // wait for all reads to finish and if we're compressed, write the final SHA1 and map else { + // wait for all reads to finish and if we're compressed, write the final SHA1 and map osd_work_queue_wait(m_read_queue, 30 * osd_ticks_per_second()); if (!compressed()) - return CHDERR_NONE; - set_raw_sha1(m_compsha1.finish()); + return std::error_condition(); + std::error_condition err = set_raw_sha1(m_compsha1.finish()); + if (UNEXPECTED(err)) + return err; return compress_v5_map(); } } @@ -2987,12 +3169,12 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio // if we're waiting for work, wait // sometimes code can get here with .m_status == WS_READY and .m_osd != nullptr, TODO find out why this happens - while (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY && - m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE && - m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != nullptr) + while ((m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_READY) && + (m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_status != WS_COMPLETE) && + m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd) osd_work_item_wait(m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd, osd_ticks_per_second()); - return m_walking_parent ? CHDERR_WALKING_PARENT : CHDERR_COMPRESSING; + return m_walking_parent ? error::WALKING_PARENT : error::COMPRESSING; } /** @@ -3010,7 +3192,7 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio void *chd_file_compressor::async_walk_parent_static(void *param, int threadid) { - work_item *item = reinterpret_cast<work_item *>(param); + auto *const item = reinterpret_cast<work_item *>(param); item->m_compressor->async_walk_parent(*item); return nullptr; } @@ -3052,7 +3234,7 @@ void chd_file_compressor::async_walk_parent(work_item &item) void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid) { - work_item *item = reinterpret_cast<work_item *>(param); + auto *const item = reinterpret_cast<work_item *>(param); item->m_compressor->async_compress_hunk(*item, threadid); return nullptr; } @@ -3069,7 +3251,7 @@ void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid) void chd_file_compressor::async_compress_hunk(work_item &item, int threadid) { // use our thread's codec - assert(threadid < ARRAY_LENGTH(m_codecs)); + assert(threadid < std::size(m_codecs)); item.m_codecs = m_codecs[threadid]; // compute CRC-16 and SHA-1 hashes @@ -3079,8 +3261,8 @@ void chd_file_compressor::async_compress_hunk(work_item &item, int threadid) // find the best compression scheme, unless we already have a self or parent match // (note we may miss a self match from blocks not yet added, but this just results in extra work) // TODO: data race - if (m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND && - m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND) + if ((m_current_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND) && + (m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1) == hashmap::NOT_FOUND)) item.m_compression = item.m_codecs->find_best_compressor(item.m_data, item.m_compressed, item.m_complen); // mark us complete @@ -3115,37 +3297,45 @@ void *chd_file_compressor::async_read_static(void *param, int threadid) void chd_file_compressor::async_read() { // if in the error or complete state, stop - if (m_read_error) + if (UNEXPECTED(m_read_error)) return; // determine parameters for the read - uint32_t work_buffer_bytes = WORK_BUFFER_HUNKS * hunk_bytes(); + uint32_t const work_buffer_bytes = WORK_BUFFER_HUNKS * hunk_bytes(); uint32_t numbytes = work_buffer_bytes / 2; - if (m_read_done_offset + numbytes > logical_bytes()) + if ((m_read_done_offset + numbytes) > logical_bytes()) numbytes = logical_bytes() - m_read_done_offset; + uint8_t *const dest = &m_work_buffer[0] + (m_read_done_offset % work_buffer_bytes); + assert((&m_work_buffer[0] == dest) || (&m_work_buffer[work_buffer_bytes / 2] == dest)); + assert(!(m_read_done_offset % hunk_bytes())); + uint64_t const end_offset = m_read_done_offset + numbytes; + // catch any exceptions coming out of here try { // do the read - uint8_t *dest = &m_work_buffer[0] + (m_read_done_offset % work_buffer_bytes); - assert(dest == &m_work_buffer[0] || dest == &m_work_buffer[work_buffer_bytes/2]); - uint64_t end_offset = m_read_done_offset + numbytes; - - // if walking the parent, read in hunks from the parent CHD if (m_walking_parent) { + // if walking the parent, read in hunks from the parent CHD + uint64_t curoffs = m_read_done_offset; uint8_t *curdest = dest; - for (uint64_t curoffs = m_read_done_offset; curoffs < end_offset + 1; curoffs += hunk_bytes()) + uint32_t curhunk = m_read_done_offset / hunk_bytes(); + while (curoffs < end_offset + 1) { - m_parent->read_hunk(curoffs / hunk_bytes(), curdest); + std::error_condition err = m_parent->read_hunk(curhunk, curdest); + if (err && (error::HUNK_OUT_OF_RANGE != err)) // FIXME: fix the code so it doesn't depend on trying to read past the end of the parent CHD + throw err; + curoffs += hunk_bytes(); curdest += hunk_bytes(); + ++curhunk; } } - - // otherwise, call the virtual function else + { + // otherwise, call the virtual function read_data(dest, m_read_done_offset, numbytes); + } // spawn off work for each hunk for (uint64_t curoffs = m_read_done_offset; curoffs < end_offset; curoffs += hunk_bytes()) @@ -3169,15 +3359,15 @@ void chd_file_compressor::async_read() // advance the read pointer m_read_done_offset += numbytes; } - catch (chd_error& err) + catch (std::error_condition const &err) { - fprintf(stderr, "CHD error occurred: %s\n", chd_file::error_string(err)); - m_read_error = true; + fprintf(stderr, "CHD error occurred: %s\n", err.message().c_str()); + m_read_error = err; } - catch (std::exception& ex) + catch (std::exception const &ex) { fprintf(stderr, "exception occurred: %s\n", ex.what()); - m_read_error = true; + m_read_error = std::errc::io_error; // TODO: revisit this error code } } @@ -3195,8 +3385,8 @@ void chd_file_compressor::async_read() * -------------------------------------------------. */ -chd_file_compressor::hashmap::hashmap() - : m_block_list(new entry_block(nullptr)) +chd_file_compressor::hashmap::hashmap() : + m_block_list(new entry_block(nullptr)) { // initialize the map to empty memset(m_map, 0, sizeof(m_map)); @@ -3252,10 +3442,10 @@ void chd_file_compressor::hashmap::reset() * @return An uint64_t. */ -uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sha1) +uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sha1) const noexcept { // look up the entry in the map - for (entry_t *entry = m_map[crc16]; entry != nullptr; entry = entry->m_next) + for (entry_t *entry = m_map[crc16]; entry; entry = entry->m_next) if (entry->m_sha1 == sha1) return entry->m_itemnum; return NOT_FOUND; @@ -3276,7 +3466,7 @@ uint64_t chd_file_compressor::hashmap::find(util::crc16_t crc16, util::sha1_t sh void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, util::sha1_t sha1) { // add to the appropriate map - if (m_block_list->m_nextalloc == ARRAY_LENGTH(m_block_list->m_array)) + if (m_block_list->m_nextalloc == std::size(m_block_list->m_array)) m_block_list = new entry_block(m_block_list); entry_t *entry = &m_block_list->m_array[m_block_list->m_nextalloc++]; entry->m_itemnum = itemnum; @@ -3284,3 +3474,41 @@ void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, ut entry->m_next = m_map[crc16]; m_map[crc16] = entry; } + +std::error_condition chd_file::check_is_hd() const noexcept +{ + metadata_entry metaentry; + return metadata_find(HARD_DISK_METADATA_TAG, 0, metaentry); +} + +std::error_condition chd_file::check_is_cd() const noexcept +{ + metadata_entry metaentry; + std::error_condition err = metadata_find(CDROM_OLD_METADATA_TAG, 0, metaentry); + if (err == error::METADATA_NOT_FOUND) + err = metadata_find(CDROM_TRACK_METADATA_TAG, 0, metaentry); + if (err == error::METADATA_NOT_FOUND) + err = metadata_find(CDROM_TRACK_METADATA2_TAG, 0, metaentry); + return err; +} + +std::error_condition chd_file::check_is_gd() const noexcept +{ + metadata_entry metaentry; + std::error_condition err = metadata_find(GDROM_OLD_METADATA_TAG, 0, metaentry); + if (err == error::METADATA_NOT_FOUND) + err = metadata_find(GDROM_TRACK_METADATA_TAG, 0, metaentry); + return err; +} + +std::error_condition chd_file::check_is_dvd() const noexcept +{ + metadata_entry metaentry; + return metadata_find(DVD_METADATA_TAG, 0, metaentry); +} + +std::error_condition chd_file::check_is_av() const noexcept +{ + metadata_entry metaentry; + return metadata_find(AV_METADATA_TAG, 0, metaentry); +} |