diff options
Diffstat (limited to 'src/lib/util/chd.cpp')
-rw-r--r-- | src/lib/util/chd.cpp | 1085 |
1 files changed, 578 insertions, 507 deletions
diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index daad423147a..f5e5ec8e0cc 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -8,20 +8,26 @@ ***************************************************************************/ -#include <cassert> - #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 <ctime> + +#include <cassert> #include <cstddef> #include <cstdlib> +#include <ctime> #include <new> -#include "eminline.h" +#include <tuple> //************************************************************************** @@ -101,12 +107,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 +120,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 +131,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 { util::sha1_t result; memcpy(&result.m_raw[0], base, sizeof(result.m_raw)); @@ -183,17 +159,23 @@ inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) // offset; on failure throw an error //------------------------------------------------- -inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length) +inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length) const { // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (!m_file) + throw 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 (err) + throw err; + size_t count; + std::tie(err, count) = read(*m_file, dest, length); + if (err) + throw err; + else if (count != length) + throw std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is truncated) } @@ -205,14 +187,17 @@ inline void chd_file::file_read(uint64_t offset, void *dest, uint32_t length) inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t length) { // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (!m_file) + throw 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 (err) + throw err; + std::tie(err, std::ignore) = write(*m_file, source, length); + if (err) + throw err; } @@ -224,15 +209,22 @@ inline void chd_file::file_write(uint64_t offset, const void *source, uint32_t l inline uint64_t chd_file::file_append(const void *source, uint32_t length, uint32_t alignment) { + std::error_condition err; + // no file = failure - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (!m_file) + throw std::error_condition(error::NOT_OPEN); // seek to the end and align if necessary - m_file->seek(0, SEEK_END); + err = m_file->seek(0, SEEK_END); + if (err) + throw err; if (alignment != 0) { - uint64_t offset = m_file->tell(); + uint64_t offset; + err = m_file->tell(offset); + if (err) + throw err; uint32_t delta = offset % alignment; if (delta != 0) { @@ -242,20 +234,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 (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 (err) + throw err; + std::tie(err, std::ignore) = write(*m_file, source, length); + if (err) + throw err; return offset; } @@ -288,11 +284,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 +304,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 ------------------------------------------------- @@ -329,7 +348,7 @@ util::sha1_t chd_file::sha1() file_read(m_sha1_offset, rawbuf, sizeof(rawbuf)); return be_read_sha1(rawbuf); } - catch (chd_error &) + catch (std::error_condition const &) { // on failure, return nullptr return util::sha1_t::null; @@ -354,15 +373,15 @@ util::sha1_t chd_file::raw_sha1() try { // determine offset within the file for data-only - if (m_rawsha1_offset == 0) - throw CHDERR_UNSUPPORTED_VERSION; + if (!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)); return be_read_sha1(rawbuf); } - catch (chd_error &) + catch (std::error_condition const &) { // on failure, return nullptr return util::sha1_t::null; @@ -387,15 +406,15 @@ util::sha1_t chd_file::parent_sha1() try { // determine offset within the file - if (m_parentsha1_offset == 0) - throw CHDERR_UNSUPPORTED_VERSION; + if (!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)); return be_read_sha1(rawbuf); } - catch (chd_error &) + catch (std::error_condition const &) { // on failure, return nullptr return util::sha1_t::null; @@ -403,7 +422,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,14 +432,14 @@ 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; @@ -434,7 +453,7 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint { case V34_MAP_ENTRY_TYPE_COMPRESSED: compressor = CHD_CODEC_ZLIB; - compbytes = be_read(&rawmap[12], 2) + (rawmap[14] << 16); + compbytes = get_u16be(&rawmap[12]) + (rawmap[14] << 16); break; case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: @@ -466,7 +485,7 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint // uncompressed case if (!compressed()) { - if (be_read(&rawmap[0], 4) == 0) + if (get_u32be(&rawmap[0]) == 0) { compressor = CHD_CODEC_PARENT; compbytes = 0; @@ -487,7 +506,7 @@ chd_error chd_file::hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint 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 +525,11 @@ 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; } - return CHDERR_NONE; + return std::error_condition(); } /** @@ -554,8 +573,8 @@ void chd_file::set_raw_sha1(util::sha1_t rawdata) void chd_file::set_parent_sha1(util::sha1_t parent) { // if no file, fail - if (m_file == nullptr) - throw CHDERR_INVALID_FILE; + if (!m_file) + throw std::error_condition(error::INVALID_FILE); // create a big-endian version uint8_t rawbuf[sizeof(util::sha1_t)]; @@ -567,7 +586,7 @@ void chd_file::set_parent_sha1(util::sha1_t parent) } /** - * @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 +598,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 (m_file) + return error::ALREADY_OPEN; + else if (!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 +639,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 (m_file) + return error::ALREADY_OPEN; + else if (!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 +680,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 (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 (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 (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 +725,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 (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 (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 (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 +768,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 (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 (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 +803,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 (m_file) + return error::ALREADY_OPEN; + else if (!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 +836,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; @@ -820,7 +850,7 @@ void chd_file::close() m_unitbytes = 0; m_unitcount = 0; memset(m_compression, 0, sizeof(m_compression)); - m_parent = nullptr; + m_parent.reset(); m_parent_missing = false; // reset key offsets within the header @@ -836,10 +866,7 @@ void chd_file::close() // reset compression management for (auto & elem : m_decompressor) - { - delete elem; - elem = nullptr; - } + elem.reset(); m_compressed.clear(); // reset caching @@ -848,7 +875,7 @@ void chd_file::close() } /** - * @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 @@ -870,18 +897,18 @@ void chd_file::close() * @return The hunk. */ -chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) +std::error_condition chd_file::read_hunk(uint32_t hunknum, void *buffer) { // wrap this for clean reporting try { // punt if no file - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (!m_file) + throw std::error_condition(error::NOT_OPEN); // return an error if out of range if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; + throw std::error_condition(error::HUNK_OUT_OF_RANGE); // get a pointer to the map entry uint64_t blockoffs; @@ -895,38 +922,38 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) case 3: case 4: rawmap = &m_rawmap[16 * hunknum]; - blockoffs = be_read(&rawmap[0], 8); - blockcrc = be_read(&rawmap[8], 4); + blockoffs = get_u64be(&rawmap[0]); + blockcrc = get_u32be(&rawmap[8]); switch (rawmap[15] & V34_MAP_ENTRY_FLAG_TYPE_MASK) { case V34_MAP_ENTRY_TYPE_COMPRESSED: - blocklen = be_read(&rawmap[12], 2) + (rawmap[14] << 16); + blocklen = get_u16be(&rawmap[12]) + (rawmap[14] << 16); file_read(blockoffs, &m_compressed[0], blocklen); 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; + throw std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); case V34_MAP_ENTRY_TYPE_UNCOMPRESSED: file_read(blockoffs, dest, m_hunkbytes); if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; + throw std::error_condition(error::DECOMPRESSION_ERROR); + return std::error_condition(); case V34_MAP_ENTRY_TYPE_MINI: - be_write(dest, blockoffs, 8); + put_u64be(dest, blockoffs); for (uint32_t bytes = 8; bytes < m_hunkbytes; bytes++) dest[bytes] = dest[bytes - 8]; if (!(rawmap[15] & V34_MAP_ENTRY_FLAG_NO_CRC) && util::crc32_creator::simple(dest, m_hunkbytes) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; + throw 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 (m_parent_missing) - throw CHDERR_REQUIRES_PARENT; + throw std::error_condition(error::REQUIRES_PARENT); return m_parent->read_hunk(blockoffs, dest); } break; @@ -938,22 +965,22 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) // uncompressed case if (!compressed()) { - blockoffs = uint64_t(be_read(rawmap, 4)) * uint64_t(m_hunkbytes); + 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) + throw std::error_condition(error::REQUIRES_PARENT); + else if (m_parent) 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); + blocklen = get_u24be(&rawmap[1]); + blockoffs = get_u48be(&rawmap[4]); + blockcrc = get_u16be(&rawmap[10]); switch (rawmap[0]) { case COMPRESSION_TYPE_0: @@ -963,41 +990,40 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) 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; + throw std::error_condition(error::DECOMPRESSION_ERROR); if (m_decompressor[rawmap[0]]->lossy() && util::crc16_creator::simple(&m_compressed[0], blocklen) != blockcrc) - throw CHDERR_DECOMPRESSION_ERROR; - return CHDERR_NONE; + throw 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; + throw 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; + throw std::error_condition(error::REQUIRES_PARENT); return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes); } break; } // if we get here, something was wrong - throw CHDERR_READ_ERROR; + throw std::error_condition(std::errc::io_error); } - - // 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,33 +1038,33 @@ 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; + if (!m_file) + throw std::error_condition(error::NOT_OPEN); // return an error if out of range if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; + throw std::error_condition(error::HUNK_OUT_OF_RANGE); // if not writeable, fail if (!m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + throw std::error_condition(error::FILE_NOT_WRITEABLE); // uncompressed writes only via this interface if (compressed()) - throw CHDERR_FILE_NOT_WRITEABLE; + throw 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); + uint32_t rawentry = get_u32be(rawmap); // if not, allocate one now if (rawentry == 0) @@ -1055,35 +1081,35 @@ chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer) // if it's all zeros, do nothing more if (all_zeros) - return CHDERR_NONE; + return std::error_condition(); // append new data to the end of the file, aligning the first chunk rawentry = file_append(buffer, m_hunkbytes, m_hunkbytes) / m_hunkbytes; // write the map entry back - be_write(rawmap, rawentry, 4); + put_u32be(rawmap, rawentry); file_write(m_mapoffset + hunknum * 4, rawmap, 4); // update the cached hunk if we just wrote it if (hunknum == m_cachehunk && buffer != &m_cache[0]) memcpy(&m_cache[0], buffer, m_hunkbytes); } - - // otherwise, just overwrite else + { + // otherwise, just overwrite 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) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @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 +1122,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 +1138,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,7 +1161,7 @@ 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; @@ -1148,7 +1174,7 @@ chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) uint32_t endoffs = (curhunk == last_hunk) ? ((offset + bytes - 1) % m_hunkbytes) : (m_hunkbytes - 1); // if it's a full block, just read directly from disk unless it's the cached hunk - chd_error err = CHDERR_NONE; + std::error_condition err; if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) err = read_hunk(curhunk, dest); @@ -1158,7 +1184,7 @@ chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) if (curhunk != m_cachehunk) { err = read_hunk(curhunk, &m_cache[0]); - if (err != CHDERR_NONE) + if (err) return err; m_cachehunk = curhunk; } @@ -1166,15 +1192,15 @@ chd_error chd_file::read_bytes(uint64_t offset, void *buffer, uint32_t bytes) } // handle errors and advance - if (err != CHDERR_NONE) + if (err) return err; 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,10 +1211,10 @@ 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; @@ -1201,7 +1227,7 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by uint32_t 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; + std::error_condition err; if (startoffs == 0 && endoffs == m_hunkbytes - 1 && curhunk != m_cachehunk) err = write_hunk(curhunk, source); @@ -1211,7 +1237,7 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by if (curhunk != m_cachehunk) { err = read_hunk(curhunk, &m_cache[0]); - if (err != CHDERR_NONE) + if (err) return err; m_cachehunk = curhunk; } @@ -1220,15 +1246,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 (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 @@ -1244,7 +1270,7 @@ chd_error chd_file::write_bytes(uint64_t offset, const void *buffer, uint32_t by * @return The metadata. */ -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 @@ -1252,23 +1278,22 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind // if we didn't find it, just return metadata_entry metaentry; if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; + return std::error_condition(error::METADATA_NOT_FOUND); // read the metadata output.assign(metaentry.length, '\0'); file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return CHDERR_NONE; + return std::error_condition(); } - - // just return errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @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. * @@ -1282,7 +1307,7 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @return The metadata. */ -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 @@ -1290,23 +1315,22 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind // if we didn't find it, just return metadata_entry metaentry; if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; + throw std::error_condition(error::METADATA_NOT_FOUND); // read the metadata output.resize(metaentry.length); file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); - return CHDERR_NONE; + return std::error_condition(); } - - // just return errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @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. * @@ -1322,7 +1346,7 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @return The metadata. */ -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 @@ -1330,23 +1354,22 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind // if we didn't find it, just return metadata_entry metaentry; if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; + throw std::error_condition(error::METADATA_NOT_FOUND); // read the metadata resultlen = metaentry.length; file_read(metaentry.offset + METADATA_HEADER_SIZE, output, std::min(outputlen, resultlen)); - return CHDERR_NONE; + return std::error_condition(); } - - // just return errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @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. * @@ -1362,7 +1385,7 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind * @return The metadata. */ -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 @@ -1370,25 +1393,24 @@ chd_error chd_file::read_metadata(chd_metadata_tag searchtag, uint32_t searchind // if we didn't find it, just return metadata_entry metaentry; if (!metadata_find(searchtag, searchindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; + throw std::error_condition(error::METADATA_NOT_FOUND); // read the metadata output.resize(metaentry.length); file_read(metaentry.offset + METADATA_HEADER_SIZE, &output[0], metaentry.length); resulttag = metaentry.metatag; resultflags = metaentry.flags; - return CHDERR_NONE; + return std::error_condition(); } - - // just return errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // just return errors return err; } } /** - * @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,17 +1422,17 @@ 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 (inputlen < 1 || inputlen >= 16 * 1024 * 1024) - return CHDERR_INVALID_PARAMETER; + return std::error_condition(std::errc::invalid_argument); // find the entry if it already exists metadata_entry metaentry; @@ -1426,7 +1448,7 @@ chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, if (inputlen != metaentry.length) { uint8_t length[3]; - be_write(length, inputlen, 3); + put_u24be(length, inputlen); file_write(metaentry.offset + 5, length, sizeof(length)); } @@ -1444,10 +1466,10 @@ chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, { // 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)); @@ -1459,18 +1481,17 @@ chd_error chd_file::write_metadata(chd_metadata_tag metatag, uint32_t metaindex, // 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; } } /** - * @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,10 +1503,10 @@ 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 @@ -1493,22 +1514,21 @@ chd_error chd_file::delete_metadata(chd_metadata_tag metatag, uint32_t metaindex // find the entry metadata_entry metaentry; if (!metadata_find(metatag, metaindex, metaentry)) - throw CHDERR_METADATA_NOT_FOUND; + throw std::error_condition(error::METADATA_NOT_FOUND); // point the previous to the next, unlinking us metadata_set_previous_next(metaentry.prev, metaentry.next); - return CHDERR_NONE; + return std::error_condition(); } - - // return any errors - catch (chd_error &err) + catch (std::error_condition const &err) { + // return any errors return err; } } /** - * @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,10 +1538,10 @@ 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 @@ -1540,16 +1560,15 @@ chd_error chd_file::clone_all_metadata(chd_file &source) 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) + std::error_condition err = write_metadata(metaentry.metatag, (uint32_t)-1, &filedata[0], metaentry.length, metaentry.flags); + if (err) throw err; } - 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; } } @@ -1589,7 +1608,7 @@ util::sha1_t chd_file::compute_overall_sha1(util::sha1_t rawsha1) // 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); } @@ -1607,7 +1626,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 +1636,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 +1672,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 +1739,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 +1773,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 (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 +1836,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 (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 +1896,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 (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 +1931,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 +1940,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 +1966,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 +1982,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 +1999,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 +2032,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; + throw std::error_condition(error::COMPRESSION_ERROR); err = encoder.export_tree_rle(bitbuf); if (err != HUFFERR_NONE) - throw CHDERR_COMPRESSION_ERROR; + 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 +2131,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,11 +2144,11 @@ chd_error chd_file::compress_v5_map() // then write the map offset uint8_t rawbuf[sizeof(uint64_t)]; - be_write(rawbuf, m_mapoffset, 8); + put_u64be(rawbuf, m_mapoffset); file_write(m_mapoffset_offset, rawbuf, sizeof(rawbuf)); - return CHDERR_NONE; + return std::error_condition(); } - catch (chd_error &err) + catch (std::error_condition const &err) { return err; } @@ -2144,9 +2177,9 @@ 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); + 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]; @@ -2160,10 +2193,10 @@ void chd_file::decompress_v5_map() huffman_decoder<16, 8> decoder; huffman_error err = decoder.import_tree_rle(bitbuf); if (err != HUFFERR_NONE) - throw CHDERR_DECOMPRESSION_ERROR; + 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 +2217,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 +2251,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 +2259,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; + 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 +2297,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 +2306,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 (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; + throw std::error_condition(std::errc::invalid_argument); + if (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,28 +2323,28 @@ 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)); @@ -2340,10 +2375,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 +2386,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 +2409,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 @@ -2392,9 +2426,9 @@ chd_error chd_file::open_common(bool writeable) // verify the signature if (memcmp(rawheader, "MComprHD", 8) != 0) - throw CHDERR_INVALID_FILE; + 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,7 +2437,7 @@ 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 @@ -2411,27 +2445,31 @@ chd_error chd_file::open_common(bool writeable) m_allow_writes = false; if (writeable && !m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + 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 (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,11 +2489,11 @@ 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; + throw std::error_condition(error::UNKNOWN_COMPRESSION); } // read the map; v5+ compressed drives need to read and decompress their map @@ -2492,30 +2530,30 @@ void chd_file::create_open_common() void chd_file::verify_proper_compression_append(uint32_t hunknum) { // punt if no file - if (m_file == nullptr) - throw CHDERR_NOT_OPEN; + if (!m_file) + throw std::error_condition(error::NOT_OPEN); // return an error if out of range if (hunknum >= m_hunkcount) - throw CHDERR_HUNK_OUT_OF_RANGE; + throw std::error_condition(error::HUNK_OUT_OF_RANGE); // if not writeable, fail if (!m_allow_writes) - throw CHDERR_FILE_NOT_WRITEABLE; + throw std::error_condition(error::FILE_NOT_WRITEABLE); // compressed writes only via this interface if (!compressed()) - throw CHDERR_FILE_NOT_WRITEABLE; + throw 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; + throw 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; + throw std::error_condition(error::COMPRESSION_ERROR); } /** @@ -2544,9 +2582,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); } /** @@ -2570,14 +2608,14 @@ void chd_file::hunk_copy_from_self(uint32_t hunknum, uint32_t otherhunk) // only permitted to reference prior hunks if (otherhunk >= hunknum) - throw CHDERR_INVALID_PARAMETER; + throw std::error_condition(std::errc::invalid_argument); // 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); } /** @@ -2599,9 +2637,9 @@ void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit) // update the map entry uint8_t *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); } /** @@ -2619,7 +2657,7 @@ void chd_file::hunk_copy_from_parent(uint32_t hunknum, uint64_t parentunit) * @return true if it succeeds, false if it fails. */ -bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) +bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metadata_entry &metaentry, bool resume) const { // start at the beginning unless we're resuming a previous search if (!resume) @@ -2641,10 +2679,10 @@ bool chd_file::metadata_find(chd_metadata_tag metatag, int32_t metaindex, metada file_read(metaentry.offset, raw_meta_header, sizeof(raw_meta_header)); // 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) @@ -2688,7 +2726,7 @@ void chd_file::metadata_set_previous_next(uint64_t prevoffset, uint64_t nextoffs // 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)); @@ -2800,7 +2838,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(); @@ -2839,7 +2877,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,14 +2886,14 @@ 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; + return std::errc::io_error; // if done reading, queue some more while (m_read_queue_offset < m_logicalbytes && osd_work_queue_items(m_read_queue) < 2) @@ -2894,9 +2932,9 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio osd_work_item_release(item.m_osd); item.m_osd = nullptr; - // for parent walking, just add to the hashmap if (m_walking_parent) { + // for parent walking, just add to the hashmap uint32_t uph = hunk_bytes() / unit_bytes(); uint32_t units = uph; if (item.m_hunknum == hunk_count() - 1 || !compressed()) @@ -2905,12 +2943,11 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio 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 (err) return err; // writes of all-0 data don't actually take space, so see if we count this @@ -2920,10 +2957,10 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio if (codec == CHD_CODEC_NONE) m_total_out += m_hunkbytes; } - - // for compressing, process the result else do { + // for compressing, process the result + // 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) @@ -2933,7 +2970,7 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio } // if not, see if it's in the parent map - if (m_parent != nullptr) + if (m_parent) { uint64_t parentunit = m_parent_map.find(item.m_hash[0].m_crc16, item.m_hash[0].m_sha1); if (parentunit != hashmap::NOT_FOUND) @@ -2971,7 +3008,7 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio { osd_work_queue_wait(m_read_queue, 30 * osd_ticks_per_second()); if (!compressed()) - return CHDERR_NONE; + return std::error_condition(); set_raw_sha1(m_compsha1.finish()); return compress_v5_map(); } @@ -2992,7 +3029,7 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio m_work_item[m_write_hunk % WORK_BUFFER_HUNKS].m_osd != nullptr) 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; } /** @@ -3052,7 +3089,7 @@ void chd_file_compressor::async_walk_parent(work_item &item) void *chd_file_compressor::async_compress_hunk_static(void *param, int threadid) { - auto *item = reinterpret_cast<work_item *>(param); + auto *const item = reinterpret_cast<work_item *>(param); item->m_compressor->async_compress_hunk(*item, threadid); return nullptr; } @@ -3069,7 +3106,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 @@ -3169,12 +3206,12 @@ 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)); + fprintf(stderr, "CHD error occurred: %s\n", err.message().c_str()); m_read_error = true; } - catch (std::exception& ex) + catch (std::exception const &ex) { fprintf(stderr, "exception occurred: %s\n", ex.what()); m_read_error = true; @@ -3276,7 +3313,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 +3321,37 @@ void chd_file_compressor::hashmap::add(uint64_t itemnum, util::crc16_t crc16, ut entry->m_next = m_map[crc16]; m_map[crc16] = entry; } + +bool chd_file::is_hd() const +{ + metadata_entry metaentry; + return metadata_find(HARD_DISK_METADATA_TAG, 0, metaentry); +} + +bool chd_file::is_cd() const +{ + metadata_entry metaentry; + return metadata_find(CDROM_OLD_METADATA_TAG, 0, metaentry) + || metadata_find(CDROM_TRACK_METADATA_TAG, 0, metaentry) + || metadata_find(CDROM_TRACK_METADATA2_TAG, 0, metaentry); +} + +bool chd_file::is_gd() const +{ + metadata_entry metaentry; + return metadata_find(GDROM_OLD_METADATA_TAG, 0, metaentry) + || metadata_find(GDROM_TRACK_METADATA_TAG, 0, metaentry); +} + +bool chd_file::is_dvd() const +{ + metadata_entry metaentry; + return metadata_find(DVD_METADATA_TAG, 0, metaentry); +} + +bool chd_file::is_av() const +{ + metadata_entry metaentry; + return metadata_find(AV_METADATA_TAG, 0, metaentry); +} + |