diff options
author | Vas Crabb <cuavas@users.noreply.github.com> | 2021-08-22 09:06:15 +1000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-22 09:06:15 +1000 |
commit | e8bbea1fc6e94e14768509d322f6c624403ffb36 (patch) | |
tree | 74dd1606a900d83de8aecff17a6737af4113308d /src/lib/util | |
parent | e319bde5fc3696d7f48f62b15b6366c4377fe5d1 (diff) |
formats, osd, util: Started refactoring file I/O stuff. (#8456)
Added more modern generic I/O interfaces with implementation backed by stdio, osd_file and core_file, replacing io_generic. Also replaced core_file's build-in zlib compression with a filter.
unzip.cpp, un7z.cpp: Added option to supply abstract I/O interface rather than filename.
Converted osd_file, core_file, archive_file, chd_file and device_image_interface to use std::error_condition rather than their own error enums.
Allow mounting TI-99 RPK from inside archives.
Diffstat (limited to 'src/lib/util')
31 files changed, 4550 insertions, 2081 deletions
diff --git a/src/lib/util/avhuff.cpp b/src/lib/util/avhuff.cpp index ac5cea71526..0689fe25b80 100644 --- a/src/lib/util/avhuff.cpp +++ b/src/lib/util/avhuff.cpp @@ -896,9 +896,9 @@ avhuff_error avhuff_decoder::decode_audio(int channels, int samples, const uint8 { // reset and decode if (!m_flac_decoder.reset(48000, 1, samples, source, size)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); if (!m_flac_decoder.decode_interleaved(reinterpret_cast<int16_t *>(curdest), samples, swap_endian)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // finish up m_flac_decoder.finish(); diff --git a/src/lib/util/aviio.cpp b/src/lib/util/aviio.cpp index 03014611873..3a3a1a412f6 100644 --- a/src/lib/util/aviio.cpp +++ b/src/lib/util/aviio.cpp @@ -1710,8 +1710,8 @@ avi_file::error avi_file_impl::read_uncompressed_video_frame(std::uint32_t frame /* read in the data */ std::uint32_t bytes_read; - osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); - if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length) + std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); + if (filerr || bytes_read != stream->chunk(framenum).length) return error::READ_ERROR; /* validate this is good data */ @@ -1765,8 +1765,8 @@ avi_file::error avi_file_impl::read_video_frame(std::uint32_t framenum, bitmap_y /* read in the data */ std::uint32_t bytes_read; - osd_file::error const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); - if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(framenum).length) + std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(framenum).offset, stream->chunk(framenum).length, bytes_read); + if (filerr || bytes_read != stream->chunk(framenum).length) return error::READ_ERROR; /* validate this is good data */ @@ -1857,8 +1857,8 @@ avi_file::error avi_file_impl::read_sound_samples(int channel, std::uint32_t fir return avierr; /* read in the data */ - auto const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read); - if (filerr != osd_file::error::NONE || bytes_read != stream->chunk(chunknum).length) + std::error_condition const filerr = m_file->read(&m_tempbuffer[0], stream->chunk(chunknum).offset, stream->chunk(chunknum).length, bytes_read); + if (filerr || bytes_read != stream->chunk(chunknum).length) return error::READ_ERROR; /* validate this is good data */ @@ -2085,14 +2085,15 @@ avi_file::error avi_file_impl::append_sound_samples(int channel, const std::int1 avi_file::error avi_file_impl::read_chunk_data(avi_chunk const &chunk, std::unique_ptr<std::uint8_t []> &buffer) { - /* allocate memory for the data */ - try { buffer.reset(new std::uint8_t[chunk.size]); } - catch (...) { return error::NO_MEMORY; } + // allocate memory for the data + buffer.reset(new (std::nothrow) std::uint8_t[chunk.size]); + if (!buffer) + return error::NO_MEMORY; - /* read from the file */ + // read from the file std::uint32_t bytes_read; - osd_file::error const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read); - if (filerr != osd_file::error::NONE || bytes_read != chunk.size) + std::error_condition const filerr = m_file->read(&buffer[0], chunk.offset + 8, chunk.size, bytes_read); + if (filerr || bytes_read != chunk.size) { buffer.reset(); return error::READ_ERROR; @@ -2292,35 +2293,33 @@ avi_file::error avi_file_impl::find_next_list(std::uint32_t findme, const avi_ch avi_file::error avi_file_impl::get_next_chunk_internal(const avi_chunk *parent, avi_chunk &newchunk, std::uint64_t offset) { - osd_file::error filerr; - std::uint8_t buffer[12]; - std::uint32_t bytesread; - - /* nullptr parent implies the root */ - if (parent == nullptr) + // nullptr parent implies the root + if (!parent) parent = &m_rootchunk; - /* start at the current offset */ + // start at the current offset newchunk.offset = offset; - /* if we're past the bounds of the parent, bail */ + // if we're past the bounds of the parent, bail if (newchunk.offset + 8 >= parent->offset + 8 + parent->size) return error::END; - /* read the header */ - filerr = m_file->read(buffer, newchunk.offset, 8, bytesread); - if (filerr != osd_file::error::NONE || bytesread != 8) + // read the header + std::uint8_t buffer[12]; + std::uint32_t bytesread; + std::error_condition filerr = m_file->read(buffer, newchunk.offset, 8, bytesread); + if (filerr || bytesread != 8) return error::INVALID_DATA; - /* fill in the new chunk */ + // fill in the new chunk newchunk.type = fetch_32bits(&buffer[0]); newchunk.size = fetch_32bits(&buffer[4]); - /* if we are a list, fetch the list type */ + // if we are a list, fetch the list type if (newchunk.type == CHUNKTYPE_LIST || newchunk.type == CHUNKTYPE_RIFF) { filerr = m_file->read(&buffer[8], newchunk.offset + 8, 4, bytesread); - if (filerr != osd_file::error::NONE || bytesread != 4) + if (filerr || bytesread != 4) return error::INVALID_DATA; newchunk.listtype = fetch_32bits(&buffer[8]); } @@ -2643,16 +2642,15 @@ avi_file::error avi_file_impl::parse_indx_chunk(avi_stream &stream, avi_chunk co /* loop over entries and create subchunks for each */ for (std::uint32_t entry = 0; entry < entries; entry++) { - const std::uint8_t *base = &chunkdata[24 + entry * 16]; - osd_file::error filerr; + std::uint8_t const *const base = &chunkdata[24 + entry * 16]; avi_chunk subchunk; - std::uint32_t bytes_read; - std::uint8_t buffer[8]; /* go read the subchunk */ subchunk.offset = fetch_64bits(&base[0]); - filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read); - if (filerr != osd_file::error::NONE || bytes_read != sizeof(buffer)) + std::uint32_t bytes_read; + std::uint8_t buffer[8]; + std::error_condition const filerr = m_file->read(buffer, subchunk.offset, sizeof(buffer), bytes_read); + if (filerr || bytes_read != sizeof(buffer)) { avierr = error::READ_ERROR; break; @@ -2788,8 +2786,8 @@ avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t list /* write the header */ std::uint32_t written; - osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); - if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + std::error_condition const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr || written != sizeof(buffer)) return error::WRITE_ERROR; m_writeoffs += written; } @@ -2806,8 +2804,8 @@ avi_file::error avi_file_impl::chunk_open(std::uint32_t type, std::uint32_t list /* write the header */ std::uint32_t written; - osd_file::error const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); - if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + std::error_condition const filerr = m_file->write(buffer, m_writeoffs, sizeof(buffer), written); + if (filerr || written != sizeof(buffer)) return error::WRITE_ERROR; m_writeoffs += written; } @@ -2846,8 +2844,8 @@ avi_file::error avi_file_impl::chunk_close() put_32bits(&buffer[0], std::uint32_t(chunksize)); std::uint32_t written; - osd_file::error const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written); - if (filerr != osd_file::error::NONE || written != sizeof(buffer)) + std::error_condition const filerr = m_file->write(buffer, chunk.offset + 4, sizeof(buffer), written); + if (filerr || written != sizeof(buffer)) return error::WRITE_ERROR; } @@ -2925,8 +2923,8 @@ avi_file::error avi_file_impl::chunk_write(std::uint32_t type, const void *data, /* write the data */ std::uint32_t written; - osd_file::error const filerr = m_file->write(data, m_writeoffs, length, written); - if (filerr != osd_file::error::NONE || written != length) + std::error_condition const filerr = m_file->write(data, m_writeoffs, length, written); + if (filerr || written != length) return error::WRITE_ERROR; m_writeoffs += written; @@ -3678,7 +3676,7 @@ avi_file::error avi_file::open(std::string const &filename, ptr &file) /* open the file */ osd_file::ptr f; std::uint64_t length; - if (osd_file::open(filename, OPEN_FLAG_READ, f, length) != osd_file::error::NONE) + if (osd_file::open(filename, OPEN_FLAG_READ, f, length)) return error::CANT_OPEN_FILE; /* allocate the file */ @@ -3731,8 +3729,8 @@ avi_file::error avi_file::create(std::string const &filename, movie_info const & /* open the file */ osd_file::ptr f; std::uint64_t length; - osd_file::error const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length); - if (filerr != osd_file::error::NONE) + std::error_condition const filerr = osd_file::open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, f, length); + if (filerr) return error::CANT_OPEN_FILE; /* allocate the file */ diff --git a/src/lib/util/cdrom.cpp b/src/lib/util/cdrom.cpp index 4ac1ab6e865..7510a1f9b47 100644 --- a/src/lib/util/cdrom.cpp +++ b/src/lib/util/cdrom.cpp @@ -19,6 +19,7 @@ #include "cdrom.h" #include "chdcd.h" +#include "corefile.h" #include <cassert> #include <cstdlib> @@ -223,10 +224,10 @@ cdrom_file *cdrom_open(const char *inputfile) return nullptr; // set up the CD-ROM module and get the disc info - chd_error err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info); - if (err != CHDERR_NONE) + std::error_condition err = chdcd_parse_toc(inputfile, file->cdtoc, file->track_info); + if (err) { - fprintf(stderr, "Error reading input file: %s\n", chd_file::error_string(err)); + fprintf(stderr, "Error reading input file: %s\n", err.message().c_str()); delete file; return nullptr; } @@ -238,14 +239,15 @@ cdrom_file *cdrom_open(const char *inputfile) for (i = 0; i < file->cdtoc.numtrks; i++) { - osd_file::error filerr = util::core_file::open(file->track_info.track[i].fname, OPEN_FLAG_READ, file->fhandle[i]); - if (filerr != osd_file::error::NONE) + std::error_condition const filerr = util::core_file::open(file->track_info.track[i].fname, OPEN_FLAG_READ, file->fhandle[i]); + if (filerr) { fprintf(stderr, "Unable to open file: %s\n", file->track_info.track[i].fname.c_str()); cdrom_close(file); return nullptr; } } + /* calculate the starting frame for each track, keeping in mind that CHDMAN pads tracks out with extra frames to fit 4-frame size boundries */ @@ -336,8 +338,8 @@ cdrom_file *cdrom_open(chd_file *chd) file->chd = chd; /* read the CD-ROM metadata */ - chd_error err = cdrom_parse_metadata(chd, &file->cdtoc); - if (err != CHDERR_NONE) + std::error_condition err = cdrom_parse_metadata(chd, &file->cdtoc); + if (err) { delete file; return nullptr; @@ -446,7 +448,7 @@ void cdrom_close(cdrom_file *file) ***************************************************************************/ /** - * @fn chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length) + * @fn std::error_condition read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length) * * @brief Reads partial sector. * @@ -461,9 +463,9 @@ void cdrom_close(cdrom_file *file) * @return The partial sector. */ -chd_error read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys=false) +std::error_condition read_partial_sector(cdrom_file *file, void *dest, uint32_t lbasector, uint32_t chdsector, uint32_t tracknum, uint32_t startoffs, uint32_t length, bool phys=false) { - chd_error result = CHDERR_NONE; + std::error_condition result; bool needswap = false; // if this is pregap info that isn't actually in the file, just return blank data @@ -570,14 +572,14 @@ uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uin if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE)) { - return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize, phys) == CHDERR_NONE); + return !read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 0, file->cdtoc.tracks[tracknum].datasize, phys); } else { // return 2048 bytes of mode 1 data from a 2352 byte mode 1 raw sector if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE1_RAW)) { - return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048, phys) == CHDERR_NONE); + return !read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2048, phys); } // return 2352 byte mode 1 raw sector from 2048 bytes of mode 1 data @@ -593,25 +595,25 @@ uint32_t cdrom_read_data(cdrom_file *file, uint32_t lbasector, void *buffer, uin bufptr[14] = msf&0xff; bufptr[15] = 1; // mode 1 LOG(("CDROM: promotion of mode1/form1 sector to mode1 raw is not complete!\n")); - return (read_partial_sector(file, bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys) == CHDERR_NONE); + return !read_partial_sector(file, bufptr+16, lbasector, chdsector, tracknum, 0, 2048, phys); } // return 2048 bytes of mode 1 data from a mode2 form1 or raw sector if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE2_FORM1)||(tracktype == CD_TRACK_MODE2_RAW))) { - return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 24, 2048, phys) == CHDERR_NONE); + return !read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 24, 2048, phys); } // return 2048 bytes of mode 1 data from a mode2 form2 or XA sector if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE2_FORM_MIX)) { - return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 8, 2048, phys) == CHDERR_NONE); + return !read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 8, 2048, phys); } // return mode 2 2336 byte data from a 2352 byte mode 1 or 2 raw sector (skip the header) if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW))) { - return (read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336, phys) == CHDERR_NONE); + return !read_partial_sector(file, buffer, lbasector, chdsector, tracknum, 16, 2336, phys); } LOG(("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype)); @@ -660,8 +662,8 @@ uint32_t cdrom_read_subcode(cdrom_file *file, uint32_t lbasector, void *buffer, return 0; // read the data - chd_error err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize); - return (err == CHDERR_NONE); + std::error_condition err = read_partial_sector(file, buffer, lbasector, chdsector, tracknum, file->cdtoc.tracks[tracknum].datasize, file->cdtoc.tracks[tracknum].subsize); + return !err; } @@ -1139,21 +1141,20 @@ const char *cdrom_get_subtype_string(uint32_t subtype) -------------------------------------------------*/ /** - * @fn chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) + * @fn std::error_condition cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) * * @brief Cdrom parse metadata. * * @param [in,out] chd If non-null, the chd. * @param [in,out] toc If non-null, the TOC. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) +std::error_condition cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) { std::string metadata; - chd_error err; - int i; + std::error_condition err; toc->flags = 0; @@ -1168,49 +1169,49 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) /* fetch the metadata for this track */ err = chd->read_metadata(CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); - if (err == CHDERR_NONE) + if (!err) { /* parse the metadata */ type[0] = subtype[0] = 0; pgtype[0] = pgsub[0] = 0; if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; if (tracknum == 0 || tracknum > CD_MAX_TRACKS) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; track = &toc->tracks[tracknum - 1]; } else { err = chd->read_metadata(CDROM_TRACK_METADATA2_TAG, toc->numtrks, metadata); - if (err == CHDERR_NONE) + if (!err) { /* parse the metadata */ type[0] = subtype[0] = 0; pregap = postgap = 0; if (sscanf(metadata.c_str(), CDROM_TRACK_METADATA2_FORMAT, &tracknum, type, subtype, &frames, &pregap, pgtype, pgsub, &postgap) != 8) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; if (tracknum == 0 || tracknum > CD_MAX_TRACKS) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; track = &toc->tracks[tracknum - 1]; } else { err = chd->read_metadata(GDROM_OLD_METADATA_TAG, toc->numtrks, metadata); - if (err == CHDERR_NONE) + if (!err) /* legacy GDROM track was detected */ toc->flags |= CD_FLAG_GDROMLE; else err = chd->read_metadata(GDROM_TRACK_METADATA_TAG, toc->numtrks, metadata); - if (err == CHDERR_NONE) + if (!err) { /* parse the metadata */ type[0] = subtype[0] = 0; pregap = postgap = 0; if (sscanf(metadata.c_str(), GDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames, &padframes, &pregap, pgtype, pgsub, &postgap) != 9) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; if (tracknum == 0 || tracknum > CD_MAX_TRACKS) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; track = &toc->tracks[tracknum - 1]; toc->flags |= CD_FLAG_GDROM; } @@ -1226,7 +1227,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) track->datasize = 0; cdrom_convert_type_string_to_track_info(type, track); if (track->datasize == 0) - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; /* extract the subtype and determine the subcode data size */ track->subtype = CD_SUB_NONE; @@ -1261,21 +1262,21 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) /* if we got any tracks this way, we're done */ if (toc->numtrks > 0) - return CHDERR_NONE; + return std::error_condition(); printf("toc->numtrks = %u?!\n", toc->numtrks); /* look for old-style metadata */ std::vector<uint8_t> oldmetadata; err = chd->read_metadata(CDROM_OLD_METADATA_TAG, 0, oldmetadata); - if (err != CHDERR_NONE) + if (err) return err; /* reconstruct the TOC from it */ auto *mrp = reinterpret_cast<uint32_t *>(&oldmetadata[0]); toc->numtrks = *mrp++; - for (i = 0; i < CD_MAX_TRACKS; i++) + for (int i = 0; i < CD_MAX_TRACKS; i++) { toc->tracks[i].trktype = *mrp++; toc->tracks[i].subtype = *mrp++; @@ -1295,7 +1296,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) if (toc->numtrks > CD_MAX_TRACKS) { toc->numtrks = swapendian_int32(toc->numtrks); - for (i = 0; i < CD_MAX_TRACKS; i++) + for (int i = 0; i < CD_MAX_TRACKS; i++) { toc->tracks[i].trktype = swapendian_int32(toc->tracks[i].trktype); toc->tracks[i].subtype = swapendian_int32(toc->tracks[i].subtype); @@ -1307,7 +1308,7 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) } } - return CHDERR_NONE; + return std::error_condition(); } @@ -1316,19 +1317,19 @@ chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc) -------------------------------------------------*/ /** - * @fn chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) + * @fn std::error_condition cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) * * @brief Cdrom write metadata. * * @param [in,out] chd If non-null, the chd. * @param toc The TOC. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) +std::error_condition cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) { - chd_error err; + std::error_condition err; /* write the metadata */ for (int i = 0; i < toc->numtrks; i++) @@ -1363,10 +1364,10 @@ chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc) err = chd->write_metadata(GDROM_TRACK_METADATA_TAG, i, metadata); } - if (err != CHDERR_NONE) + if (err) return err; } - return CHDERR_NONE; + return std::error_condition(); } /** diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h index 27d41da845e..1847a1db83a 100644 --- a/src/lib/util/cdrom.h +++ b/src/lib/util/cdrom.h @@ -7,15 +7,15 @@ Generic MAME cd-rom implementation ***************************************************************************/ - -#ifndef MAME_UTIL_CDROM_H -#define MAME_UTIL_CDROM_H +#ifndef MAME_LIB_UTIL_CDROM_H +#define MAME_LIB_UTIL_CDROM_H #pragma once -#include "osdcore.h" #include "chd.h" +#include "osdcore.h" + /*************************************************************************** @@ -137,8 +137,8 @@ void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_tr void cdrom_convert_subtype_string_to_pregap_info(const char *typestring, cdrom_track_info *info); const char *cdrom_get_type_string(uint32_t trktype); const char *cdrom_get_subtype_string(uint32_t subtype); -chd_error cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc); -chd_error cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc); +std::error_condition cdrom_parse_metadata(chd_file *chd, cdrom_toc *toc); +std::error_condition cdrom_write_metadata(chd_file *chd, const cdrom_toc *toc); // ECC utilities bool ecc_verify(const uint8_t *sector); @@ -184,4 +184,4 @@ static inline uint32_t lba_to_msf_alt(int lba) return ret; } -#endif // MAME_UTIL_CDROM_H +#endif // MAME_LIB_UTIL_CDROM_H diff --git a/src/lib/util/chd.cpp b/src/lib/util/chd.cpp index 0de05849cdd..29792fc36df 100644 --- a/src/lib/util/chd.cpp +++ b/src/lib/util/chd.cpp @@ -8,20 +8,24 @@ ***************************************************************************/ -#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 "eminline.h" + #include <zlib.h> -#include <ctime> + +#include <cassert> #include <cstddef> #include <cstdlib> +#include <ctime> #include <new> -#include "eminline.h" //************************************************************************** @@ -186,14 +190,17 @@ inline void chd_file::be_write_sha1(uint8_t *base, util::sha1_t value) inline void chd_file::file_read(uint64_t offset, void *dest, 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 read m_file->seek(offset, SEEK_SET); - uint32_t count = m_file->read(dest, length); - if (count != length) - throw CHDERR_READ_ERROR; + size_t count; + std::error_condition err = m_file->read(dest, length, count); + if (err) + throw err; + else if (count != length) + throw std::error_condition(std::errc::io_error); // TODO: revisit this error code (happens if file is cut off) } @@ -205,14 +212,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; + size_t count; + std::error_condition err = m_file->write(source, length, count); + if (err) + throw err; + else if (count != length) + throw std::error_condition(std::errc::interrupted); // can theoretically happen if write is inuterrupted by a signal } @@ -224,15 +234,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 +259,27 @@ 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; + err = m_file->write(buffer, bytes_to_write, count); + 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; + size_t count; + err = m_file->write(source, length, count); + if (err) + throw err; + else if (count != length) + throw std::error_condition(std::errc::interrupted); // can theoretically happen if write is interrupted by a signal return offset; } @@ -288,11 +312,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 +332,22 @@ 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; +} + +/** * @fn util::sha1_t chd_file::sha1() * * @brief ------------------------------------------------- @@ -329,7 +366,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 +391,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 +424,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 +440,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 +450,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; @@ -506,11 +543,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 +591,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 +604,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, chd_codec_type compression[4]) * * @brief ------------------------------------------------- * create - create a new file with no parent using an existing opened file handle @@ -579,14 +616,16 @@ 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, 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; @@ -596,13 +635,12 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_ m_parent = nullptr; // 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, chd_codec_type compression[4], chd_file &parent) * * @brief ------------------------------------------------- * create - create a new file with a parent using an existing opened file handle @@ -614,14 +652,16 @@ 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, 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; @@ -631,13 +671,12 @@ chd_error chd_file::create(util::core_file &file, uint64_t logicalbytes, uint32_ m_parent = &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, chd_codec_type compression[4]) * * @brief ------------------------------------------------- * create - create a new file with no parent using a filename @@ -649,40 +688,38 @@ 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, 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; + util::random_read_write::ptr io = util::core_file_read_write(std::move(file)); + if (!io) + return std::errc::not_enough_memory; // create the file normally, then claim the file - const chd_error chderr = create(*file, logicalbytes, hunkbytes, unitbytes, compression); - m_owns_file = true; + std::error_condition chderr = create(std::move(io), 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(); + io.reset(); + 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, chd_codec_type compression[4], chd_file &parent) * * @brief ------------------------------------------------- * create - create a new file with a parent using a filename @@ -694,40 +731,38 @@ 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, 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; + util::random_read_write::ptr io = util::core_file_read_write(std::move(file)); + if (!io) + return std::errc::not_enough_memory; // create the file normally, then claim the file - const chd_error chderr = create(*file, logicalbytes, hunkbytes, compression, parent); - m_owns_file = true; + std::error_condition chderr = create(std::move(io), logicalbytes, hunkbytes, compression, parent); // if an error happened, close and delete the file - if (chderr != CHDERR_NONE) - { - file.reset(); - osd_file::remove(filename); - } - else + if (chderr) { - file.release(); + io.reset(); + 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 +772,36 @@ 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) { // 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; + util::random_read_write::ptr io = util::core_file_read_write(std::move(file)); + if (!io) + return std::errc::not_enough_memory; // now open the CHD - chd_error err = open(*file, writeable, parent); - if (err != CHDERR_NONE) + std::error_condition err = open(std::move(io), writeable, parent); + if (err) return err; // we now own this file - file.release(); - m_owns_file = true; return err; } /** - * @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,18 +811,19 @@ 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) { // 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_file = std::move(file); m_parent = parent; m_cachehunk = ~0; return open_common(writeable); @@ -803,10 +840,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; @@ -836,10 +870,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 +879,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 +901,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; @@ -904,29 +935,29 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) 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); 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; @@ -942,12 +973,12 @@ chd_error chd_file::read_hunk(uint32_t hunknum, void *buffer) if (blockoffs != 0) file_read(blockoffs, dest, m_hunkbytes); else if (m_parent_missing) - throw CHDERR_REQUIRES_PARENT; + throw std::error_condition(error::REQUIRES_PARENT); else if (m_parent != nullptr) m_parent->read_hunk(hunknum, dest); else memset(dest, 0, m_hunkbytes); - return CHDERR_NONE; + return std::error_condition(); } // compressed case @@ -963,41 +994,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,29 +1042,29 @@ 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]; @@ -1055,7 +1085,7 @@ 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; @@ -1068,22 +1098,22 @@ chd_error chd_file::write_hunk(uint32_t hunknum, const void *buffer) 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 +1126,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 +1142,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 +1165,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 +1178,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 +1188,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 +1196,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 +1215,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 +1231,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 +1241,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 +1250,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 +1274,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 +1282,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.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 +1311,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 +1319,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 +1350,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 +1358,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 +1389,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 +1397,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 +1426,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; @@ -1459,18 +1485,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 +1507,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 +1518,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 +1542,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 +1564,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; } } @@ -1607,7 +1630,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,10 +1640,10 @@ 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 @@ -1630,14 +1653,13 @@ chd_error chd_file::codec_configure(chd_codec_type codec, int param, void *confi 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 +1676,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,15 +1743,15 @@ 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) + 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 CD_FRAME_SIZE; // otherwise, just map 1:1 with the hunk size @@ -1751,7 +1778,7 @@ 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; + throw std::error_condition(error::INVALID_FILE); // extract core info m_logicalbytes = be_read(&rawheader[28], 8); @@ -1771,7 +1798,7 @@ void chd_file::parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1) 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; @@ -1814,7 +1841,7 @@ 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; + throw std::error_condition(error::INVALID_FILE); // extract core info m_logicalbytes = be_read(&rawheader[28], 8); @@ -1834,7 +1861,7 @@ void chd_file::parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1) 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; @@ -1874,7 +1901,7 @@ 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; + throw std::error_condition(error::INVALID_FILE); // extract core info m_logicalbytes = be_read(&rawheader[32], 8); @@ -1908,7 +1935,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 +1944,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 { @@ -2015,10 +2042,10 @@ chd_error chd_file::compress_v5_map() bitstream_out bitbuf(&compressed[16], compressed.size() - 16); 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++) @@ -2113,9 +2140,9 @@ chd_error chd_file::compress_v5_map() uint8_t rawbuf[sizeof(uint64_t)]; be_write(rawbuf, m_mapoffset, 8); 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; } @@ -2160,7 +2187,7 @@ 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++) @@ -2244,11 +2271,11 @@ void chd_file::decompress_v5_map() // 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 @@ -2264,7 +2291,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 @@ -2273,14 +2300,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; @@ -2290,9 +2317,9 @@ 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 @@ -2342,10 +2369,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; } @@ -2354,11 +2380,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 @@ -2377,10 +2403,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) { // wrap in try for proper error handling try @@ -2394,7 +2420,7 @@ 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); @@ -2405,7 +2431,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 @@ -2413,27 +2439,26 @@ 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) 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 != nullptr) - throw CHDERR_INVALID_PARAMETER; + else if (m_parent) + throw std::error_condition(std::errc::invalid_argument); // 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; } @@ -2457,7 +2482,7 @@ void chd_file::create_open_common() { 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 @@ -2494,30 +2519,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); } /** @@ -2572,7 +2597,7 @@ 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]; @@ -2841,7 +2866,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 @@ -2850,14 +2875,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) @@ -2911,8 +2936,8 @@ chd_error chd_file_compressor::compress_continue(double &progress, double &ratio // 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) + 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 @@ -2973,7 +2998,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(); } @@ -2994,7 +3019,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; } /** @@ -3171,12 +3196,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; diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h index 705967d3dda..8d3e639438b 100644 --- a/src/lib/util/chd.h +++ b/src/lib/util/chd.h @@ -7,18 +7,22 @@ MAME Compressed Hunks of Data file format ***************************************************************************/ - -#ifndef MAME_UTIL_CHD_H -#define MAME_UTIL_CHD_H +#ifndef MAME_LIB_UTIL_CHD_H +#define MAME_LIB_UTIL_CHD_H #pragma once -#include "osdcore.h" -#include <string> -#include "corefile.h" -#include "hashing.h" #include "chdcodec.h" +#include "hashing.h" +#include "ioprocs.h" + +#include "osdcore.h" + #include <atomic> +#include <string> +#include <string_view> +#include <system_error> + /*************************************************************************** @@ -190,86 +194,49 @@ //************************************************************************** // pseudo-codecs returned by hunk_info -const chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk -const chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk -const chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat +constexpr chd_codec_type CHD_CODEC_SELF = 1; // copy of another hunk +constexpr chd_codec_type CHD_CODEC_PARENT = 2; // copy of a parent's hunk +constexpr chd_codec_type CHD_CODEC_MINI = 3; // legacy "mini" 8-byte repeat // core types typedef uint32_t chd_metadata_tag; // metadata parameters -const chd_metadata_tag CHDMETATAG_WILDCARD = 0; -const uint32_t CHDMETAINDEX_APPEND = ~0; +constexpr chd_metadata_tag CHDMETATAG_WILDCARD = 0; +constexpr uint32_t CHDMETAINDEX_APPEND = ~0; // metadata flags -const uint8_t CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed +constexpr uint8_t CHD_MDFLAGS_CHECKSUM = 0x01; // indicates data is checksummed // standard hard disk metadata -const chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'); +constexpr chd_metadata_tag HARD_DISK_METADATA_TAG = CHD_MAKE_TAG('G','D','D','D'); extern const char *HARD_DISK_METADATA_FORMAT; // hard disk identify information -const chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T'); +constexpr chd_metadata_tag HARD_DISK_IDENT_METADATA_TAG = CHD_MAKE_TAG('I','D','N','T'); // hard disk key information -const chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' '); +constexpr chd_metadata_tag HARD_DISK_KEY_METADATA_TAG = CHD_MAKE_TAG('K','E','Y',' '); // pcmcia CIS information -const chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' '); +constexpr chd_metadata_tag PCMCIA_CIS_METADATA_TAG = CHD_MAKE_TAG('C','I','S',' '); // standard CD-ROM metadata -const chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D'); -const chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'); +constexpr chd_metadata_tag CDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','C','D'); +constexpr chd_metadata_tag CDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C','H','T','R'); extern const char *CDROM_TRACK_METADATA_FORMAT; -const chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'); +constexpr chd_metadata_tag CDROM_TRACK_METADATA2_TAG = CHD_MAKE_TAG('C','H','T','2'); extern const char *CDROM_TRACK_METADATA2_FORMAT; -const chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); -const chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D'); +constexpr chd_metadata_tag GDROM_OLD_METADATA_TAG = CHD_MAKE_TAG('C','H','G','T'); +constexpr chd_metadata_tag GDROM_TRACK_METADATA_TAG = CHD_MAKE_TAG('C', 'H', 'G', 'D'); extern const char *GDROM_TRACK_METADATA_FORMAT; // standard A/V metadata -const chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V'); +constexpr chd_metadata_tag AV_METADATA_TAG = CHD_MAKE_TAG('A','V','A','V'); extern const char *AV_METADATA_FORMAT; // A/V laserdisc frame metadata -const chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D'); - -// error types -enum chd_error -{ - CHDERR_NONE, - CHDERR_NO_INTERFACE, - CHDERR_OUT_OF_MEMORY, - CHDERR_NOT_OPEN, - CHDERR_ALREADY_OPEN, - CHDERR_INVALID_FILE, - CHDERR_INVALID_PARAMETER, - CHDERR_INVALID_DATA, - CHDERR_FILE_NOT_FOUND, - CHDERR_REQUIRES_PARENT, - CHDERR_FILE_NOT_WRITEABLE, - CHDERR_READ_ERROR, - CHDERR_WRITE_ERROR, - CHDERR_CODEC_ERROR, - CHDERR_INVALID_PARENT, - CHDERR_HUNK_OUT_OF_RANGE, - CHDERR_DECOMPRESSION_ERROR, - CHDERR_COMPRESSION_ERROR, - CHDERR_CANT_CREATE_FILE, - CHDERR_CANT_VERIFY, - CHDERR_NOT_SUPPORTED, - CHDERR_METADATA_NOT_FOUND, - CHDERR_INVALID_METADATA_SIZE, - CHDERR_UNSUPPORTED_VERSION, - CHDERR_VERIFY_INCOMPLETE, - CHDERR_INVALID_METADATA, - CHDERR_INVALID_STATE, - CHDERR_OPERATION_PENDING, - CHDERR_UNSUPPORTED_FORMAT, - CHDERR_UNKNOWN_COMPRESSION, - CHDERR_WALKING_PARENT, - CHDERR_COMPRESSING -}; +constexpr chd_metadata_tag AV_LD_METADATA_TAG = CHD_MAKE_TAG('A','V','L','D'); @@ -277,9 +244,6 @@ enum chd_error // TYPE DEFINITIONS //************************************************************************** -class chd_codec; - - // ======================> chd_file // core file class @@ -289,22 +253,49 @@ class chd_file friend class chd_verifier; // constants - static const uint32_t HEADER_VERSION = 5; - static const uint32_t V3_HEADER_SIZE = 120; - static const uint32_t V4_HEADER_SIZE = 108; - static const uint32_t V5_HEADER_SIZE = 124; - static const uint32_t MAX_HEADER_SIZE = V5_HEADER_SIZE; + static constexpr uint32_t HEADER_VERSION = 5; + static constexpr uint32_t V3_HEADER_SIZE = 120; + static constexpr uint32_t V4_HEADER_SIZE = 108; + static constexpr uint32_t V5_HEADER_SIZE = 124; + static constexpr uint32_t MAX_HEADER_SIZE = V5_HEADER_SIZE; public: + // error types + enum class error + { + NO_INTERFACE = 1, + NOT_OPEN, + ALREADY_OPEN, + INVALID_FILE, + INVALID_DATA, + REQUIRES_PARENT, + FILE_NOT_WRITEABLE, + CODEC_ERROR, + INVALID_PARENT, + HUNK_OUT_OF_RANGE, + DECOMPRESSION_ERROR, + COMPRESSION_ERROR, + CANT_VERIFY, + METADATA_NOT_FOUND, + INVALID_METADATA_SIZE, + UNSUPPORTED_VERSION, + VERIFY_INCOMPLETE, + INVALID_METADATA, + INVALID_STATE, + OPERATION_PENDING, + UNSUPPORTED_FORMAT, + UNKNOWN_COMPRESSION, + WALKING_PARENT, + COMPRESSING + }; + // construction/destruction chd_file(); virtual ~chd_file(); - // operators - operator util::core_file &() { return *m_file; } - // getters - bool opened() const { return (m_file != nullptr); } + util::random_read &file(); + bool opened() const { return bool(m_file); } uint32_t version() const { return m_version; } uint64_t logical_bytes() const { return m_logicalbytes; } uint32_t hunk_bytes() const { return m_hunkbytes; } @@ -317,52 +308,49 @@ public: util::sha1_t sha1(); util::sha1_t raw_sha1(); util::sha1_t parent_sha1(); - chd_error hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes); + std::error_condition hunk_info(uint32_t hunknum, chd_codec_type &compressor, uint32_t &compbytes); // setters void set_raw_sha1(util::sha1_t rawdata); void set_parent_sha1(util::sha1_t parent); // file create - chd_error create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]); - chd_error create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]); - chd_error create(const char *filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent); - chd_error create(util::core_file &file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent); + std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]); + std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, uint32_t unitbytes, chd_codec_type compression[4]); + std::error_condition create(std::string_view filename, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent); + std::error_condition create(util::random_read_write::ptr &&file, uint64_t logicalbytes, uint32_t hunkbytes, chd_codec_type compression[4], chd_file &parent); // file open - chd_error open(const char *filename, bool writeable = false, chd_file *parent = nullptr); - chd_error open(util::core_file &file, bool writeable = false, chd_file *parent = nullptr); + std::error_condition open(std::string_view filename, bool writeable = false, chd_file *parent = nullptr); + std::error_condition open(util::random_read_write::ptr &&file, bool writeable = false, chd_file *parent = nullptr); // file close void close(); // read/write - chd_error read_hunk(uint32_t hunknum, void *buffer); - chd_error write_hunk(uint32_t hunknum, const void *buffer); - chd_error read_units(uint64_t unitnum, void *buffer, uint32_t count = 1); - chd_error write_units(uint64_t unitnum, const void *buffer, uint32_t count = 1); - chd_error read_bytes(uint64_t offset, void *buffer, uint32_t bytes); - chd_error write_bytes(uint64_t offset, const void *buffer, uint32_t bytes); + std::error_condition read_hunk(uint32_t hunknum, void *buffer); + std::error_condition write_hunk(uint32_t hunknum, const void *buffer); + std::error_condition read_units(uint64_t unitnum, void *buffer, uint32_t count = 1); + std::error_condition write_units(uint64_t unitnum, const void *buffer, uint32_t count = 1); + std::error_condition read_bytes(uint64_t offset, void *buffer, uint32_t bytes); + std::error_condition write_bytes(uint64_t offset, const void *buffer, uint32_t bytes); // metadata management - chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output); - chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output); - chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen); - chd_error read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags); - chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags = CHD_MDFLAGS_CHECKSUM); - chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::string &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.c_str(), input.length() + 1, flags); } - chd_error write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::vector<uint8_t> &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, &input[0], input.size(), flags); } - chd_error delete_metadata(chd_metadata_tag metatag, uint32_t metaindex); - chd_error clone_all_metadata(chd_file &source); + std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::string &output); + std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output); + std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, void *output, uint32_t outputlen, uint32_t &resultlen); + std::error_condition read_metadata(chd_metadata_tag searchtag, uint32_t searchindex, std::vector<uint8_t> &output, chd_metadata_tag &resulttag, uint8_t &resultflags); + std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const void *inputbuf, uint32_t inputlen, uint8_t flags = CHD_MDFLAGS_CHECKSUM); + std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::string &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, input.c_str(), input.length() + 1, flags); } + std::error_condition write_metadata(chd_metadata_tag metatag, uint32_t metaindex, const std::vector<uint8_t> &input, uint8_t flags = CHD_MDFLAGS_CHECKSUM) { return write_metadata(metatag, metaindex, &input[0], input.size(), flags); } + std::error_condition delete_metadata(chd_metadata_tag metatag, uint32_t metaindex); + std::error_condition clone_all_metadata(chd_file &source); // hashing helper util::sha1_t compute_overall_sha1(util::sha1_t rawsha1); // codec interfaces - chd_error codec_configure(chd_codec_type codec, int param, void *config); - - // static helpers - static const char *error_string(chd_error err); + std::error_condition codec_configure(chd_codec_type codec, int param, void *config); private: struct metadata_entry; @@ -383,10 +371,10 @@ private: void parse_v3_header(uint8_t *rawheader, util::sha1_t &parentsha1); void parse_v4_header(uint8_t *rawheader, util::sha1_t &parentsha1); void parse_v5_header(uint8_t *rawheader, util::sha1_t &parentsha1); - chd_error compress_v5_map(); + std::error_condition compress_v5_map(); void decompress_v5_map(); - chd_error create_common(); - chd_error open_common(bool writeable); + std::error_condition create_common(); + std::error_condition open_common(bool writeable); void create_open_common(); void verify_proper_compression_append(uint32_t hunknum); void hunk_write_compressed(uint32_t hunknum, int8_t compression, const uint8_t *compressed, uint32_t complength, util::crc16_t crc16); @@ -398,42 +386,41 @@ private: static int CLIB_DECL metadata_hash_compare(const void *elem1, const void *elem2); // file characteristics - util::core_file * m_file; // handle to the open core file - bool m_owns_file; // flag indicating if this file should be closed on chd_close() + util::random_read_write::ptr m_file; // handle to the open core file bool m_allow_reads; // permit reads from this CHD? bool m_allow_writes; // permit writes to this CHD? // core parameters from the header - uint32_t m_version; // version of the header - uint64_t m_logicalbytes; // logical size of the raw CHD data in bytes - uint64_t m_mapoffset; // offset of map - uint64_t m_metaoffset; // offset to first metadata bit - uint32_t m_hunkbytes; // size of each raw hunk in bytes - uint32_t m_hunkcount; // number of hunks represented - uint32_t m_unitbytes; // size of each unit in bytes - uint64_t m_unitcount; // number of units represented + uint32_t m_version; // version of the header + uint64_t m_logicalbytes; // logical size of the raw CHD data in bytes + uint64_t m_mapoffset; // offset of map + uint64_t m_metaoffset; // offset to first metadata bit + uint32_t m_hunkbytes; // size of each raw hunk in bytes + uint32_t m_hunkcount; // number of hunks represented + uint32_t m_unitbytes; // size of each unit in bytes + uint64_t m_unitcount; // number of units represented chd_codec_type m_compression[4]; // array of compression types used chd_file * m_parent; // pointer to parent file, or nullptr if none bool m_parent_missing; // are we missing our parent? // key offsets within the header - uint64_t m_mapoffset_offset; // offset of map offset field - uint64_t m_metaoffset_offset;// offset of metaoffset field - uint64_t m_sha1_offset; // offset of SHA1 field - uint64_t m_rawsha1_offset; // offset of raw SHA1 field - uint64_t m_parentsha1_offset;// offset of paren SHA1 field + uint64_t m_mapoffset_offset; // offset of map offset field + uint64_t m_metaoffset_offset;// offset of metaoffset field + uint64_t m_sha1_offset; // offset of SHA1 field + uint64_t m_rawsha1_offset; // offset of raw SHA1 field + uint64_t m_parentsha1_offset;// offset of paren SHA1 field // map information - uint32_t m_mapentrybytes; // length of each entry in a map - std::vector<uint8_t> m_rawmap; // raw map data + uint32_t m_mapentrybytes; // length of each entry in a map + std::vector<uint8_t> m_rawmap; // raw map data // compression management - chd_decompressor * m_decompressor[4]; // array of decompression codecs - std::vector<uint8_t> m_compressed; // temporary buffer for compressed data + chd_decompressor::ptr m_decompressor[4]; // array of decompression codecs + std::vector<uint8_t> m_compressed; // temporary buffer for compressed data // caching - std::vector<uint8_t> m_cache; // single-hunk cache for partial reads/writes - uint32_t m_cachehunk; // which hunk is in the cache? + std::vector<uint8_t> m_cache; // single-hunk cache for partial reads/writes + uint32_t m_cachehunk; // which hunk is in the cache? }; @@ -449,7 +436,7 @@ public: // compression management void compress_begin(); - chd_error compress_continue(double &progress, double &ratio); + std::error_condition compress_continue(double &progress, double &ratio); protected: // required override: read more data @@ -470,24 +457,24 @@ private: void add(uint64_t itemnum, util::crc16_t crc16, util::sha1_t sha1); // constants - static const uint64_t NOT_FOUND = ~uint64_t(0); + static constexpr uint64_t NOT_FOUND = ~uint64_t(0); + private: // internal entry struct entry_t { entry_t * m_next; // next entry in list - uint64_t m_itemnum; // item number + uint64_t m_itemnum; // item number util::sha1_t m_sha1; // SHA-1 of the block }; // block of entries struct entry_block { - entry_block(entry_block *prev) - : m_next(prev), m_nextalloc(0) { } + entry_block(entry_block *prev) : m_next(prev), m_nextalloc(0) { } entry_block * m_next; // next block in list - uint32_t m_nextalloc; // next to be allocated + uint32_t m_nextalloc; // next to be allocated entry_t m_array[16384]; // array of entries }; @@ -551,8 +538,8 @@ private: // current compression status bool m_walking_parent; // are we building the parent map? - uint64_t m_total_in; // total bytes in - uint64_t m_total_out; // total bytes out + uint64_t m_total_in; // total bytes in + uint64_t m_total_out; // total bytes out util::sha1_creator m_compsha1; // running SHA-1 on raw data // hash lookup maps @@ -561,20 +548,31 @@ private: // read I/O thread osd_work_queue * m_read_queue; // work queue for reading - uint64_t m_read_queue_offset;// next offset to enqueue - uint64_t m_read_done_offset; // next offset that will complete + uint64_t m_read_queue_offset;// next offset to enqueue + uint64_t m_read_done_offset; // next offset that will complete bool m_read_error; // error during reading? // work item thread - static const int WORK_BUFFER_HUNKS = 256; + static constexpr int WORK_BUFFER_HUNKS = 256; osd_work_queue * m_work_queue; // queue for doing work on other threads - std::vector<uint8_t> m_work_buffer; // buffer containing hunk data to work on - std::vector<uint8_t> m_compressed_buffer;// buffer containing compressed data + std::vector<uint8_t> m_work_buffer; // buffer containing hunk data to work on + std::vector<uint8_t> m_compressed_buffer;// buffer containing compressed data work_item m_work_item[WORK_BUFFER_HUNKS]; // status of each hunk chd_compressor_group * m_codecs[WORK_MAX_THREADS]; // codecs to use // output state - uint32_t m_write_hunk; // next hunk to write + uint32_t m_write_hunk; // next hunk to write }; -#endif // MAME_UTIL_CHD_H + +// error category for CHD errors +std::error_category const &chd_category() noexcept; +inline std::error_condition make_error_condition(chd_file::error err) noexcept { return std::error_condition(int(err), chd_category()); } + +namespace std { + +template <> struct is_error_condition_enum<chd_file::error> : public std::true_type { }; + +} // namespace std + +#endif // MAME_LIB_UTIL_CHD_H diff --git a/src/lib/util/chdcd.cpp b/src/lib/util/chdcd.cpp index ef013b41f48..63b633536bf 100644 --- a/src/lib/util/chdcd.cpp +++ b/src/lib/util/chdcd.cpp @@ -7,15 +7,19 @@ ***************************************************************************/ -#include <cctype> -#include <cstdlib> -#include <cassert> -#include "osdcore.h" -#include "chd.h" #include "chdcd.h" + +#include "chd.h" #include "corefile.h" #include "corestr.h" +#include "osdcore.h" + +#include <cassert> +#include <cctype> +#include <cerrno> +#include <cstdlib> + /*************************************************************************** @@ -230,8 +234,8 @@ static uint32_t parse_wav_sample(const char *filename, uint32_t *dataoffs) uint64_t fsize = 0; std::uint32_t actual; - osd_file::error filerr = osd_file::open(filename, OPEN_FLAG_READ, file, fsize); - if (filerr != osd_file::error::NONE) + std::error_condition const filerr = osd_file::open(filename, OPEN_FLAG_READ, file, fsize); + if (filerr) { printf("ERROR: could not open (%s)\n", filename); return 0; @@ -449,7 +453,7 @@ uint64_t read_uint64(FILE *infile) -------------------------------------------------*/ /** - * @fn chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn std::error_condition chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse nero. * @@ -457,26 +461,25 @@ uint64_t read_uint64(FILE *infile) * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +std::error_condition chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; unsigned char buffer[12]; uint32_t chain_offs, chunk_size; int done = 0; std::string path = std::string(tocfname); - infile = fopen(tocfname, "rb"); - path = get_file_path(path); - - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rb"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -489,7 +492,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ { printf("ERROR: Not a Nero 5.5 or later image!\n"); fclose(infile); - return CHDERR_UNSUPPORTED_VERSION; + return chd_file::error::UNSUPPORTED_FORMAT; } chain_offs = buffer[11] | (buffer[10]<<8) | (buffer[9]<<16) | (buffer[8]<<24); @@ -498,7 +501,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ { printf("ERROR: File size is > 4GB, this version of CHDMAN cannot handle it."); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } // printf("NER5 detected, chain offset: %x\n", chain_offs); @@ -559,12 +562,12 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ case 0x0300: // Mode 2 Form 1 printf("ERROR: Mode 2 Form 1 tracks not supported\n"); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; case 0x0500: // raw data printf("ERROR: Raw data tracks not supported\n"); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; case 0x0600: // 2352 byte mode 2 raw outtoc.tracks[track-1].trktype = CD_TRACK_MODE2_RAW; @@ -579,22 +582,22 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ case 0x0f00: // raw data with sub-channel printf("ERROR: Raw data tracks with sub-channel not supported\n"); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; case 0x1000: // audio with sub-channel printf("ERROR: Audio tracks with sub-channel not supported\n"); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; case 0x1100: // raw Mode 2 Form 1 with sub-channel printf("ERROR: Raw Mode 2 Form 1 tracks with sub-channel not supported\n"); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; default: printf("ERROR: Unknown track type %x, contact MAMEDEV!\n", mode); fclose(infile); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } outtoc.tracks[track-1].datasize = size; @@ -627,7 +630,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ fclose(infile); - return CHDERR_NONE; + return std::error_condition(); } /*------------------------------------------------- @@ -635,7 +638,7 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ -------------------------------------------------*/ /** - * @fn chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn std::error_condition chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse ISO. * @@ -643,22 +646,21 @@ chd_error chdcd_parse_nero(const char *tocfname, cdrom_toc &outtoc, chdcd_track_ * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +std::error_condition chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; std::string path = std::string(tocfname); - infile = fopen(tocfname, "rb"); - path = get_file_path(path); - - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rb"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -693,7 +695,7 @@ chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i outinfo.track[0].swap = false; } else { printf("ERROR: Unrecognized track type\n"); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } outtoc.tracks[0].subtype = CD_SUB_NONE; @@ -709,7 +711,7 @@ chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i outtoc.tracks[0].padframes = 0; - return CHDERR_NONE; + return std::error_condition(); } /*------------------------------------------------- @@ -717,7 +719,7 @@ chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i -------------------------------------------------*/ /** - * @fn static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn static std::error_condition chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse GDI. * @@ -725,24 +727,23 @@ chd_error chdcd_parse_iso(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. */ -static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +static std::error_condition chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; int i, numtracks; std::string path = std::string(tocfname); - infile = fopen(tocfname, "rt"); - path = get_file_path(path); - - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rt"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -841,7 +842,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_ /* store the number of tracks found */ outtoc.numtrks = numtracks; - return CHDERR_NONE; + return std::error_condition(); } /*------------------------------------------------- @@ -849,7 +850,7 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_ -------------------------------------------------*/ /** - * @fn chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn std::error_condition chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse cue. * @@ -857,25 +858,25 @@ static chd_error chdcd_parse_gdi(const char *tocfname, cdrom_toc &outtoc, chdcd_ * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +std::error_condition chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; int i, trknum; static char token[512]; std::string lastfname; uint32_t wavlen, wavoffs; std::string path = std::string(tocfname); - infile = fopen(tocfname, "rt"); - path = get_file_path(path); - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rt"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -921,14 +922,14 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i { fclose(infile); printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str()); - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; } } else { fclose(infile); printf("ERROR: Unhandled track type %s\n", token); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } } else if (!strcmp(token, "TRACK")) @@ -970,7 +971,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i { fclose(infile); printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } /* next (optional) token on the line is the subcode type */ @@ -1083,7 +1084,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize); outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); @@ -1094,7 +1095,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); outtoc.tracks[trknum].frames = tlen; @@ -1120,7 +1121,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i if (!outtoc.tracks[trknum].frames) { printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1); - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; } } else /* data files are different */ @@ -1129,7 +1130,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); outtoc.tracks[trknum].frames = tlen; @@ -1140,7 +1141,7 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i //printf("trk %d: %d frames @ offset %d\n", trknum+1, outtoc.tracks[trknum].frames, outinfo.track[trknum].offset); } - return CHDERR_NONE; + return std::error_condition(); } /*--------------------------------------------------------------------------------------- @@ -1161,18 +1162,18 @@ chd_error chdcd_parse_cue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i bool chdcd_is_gdicue(const char *tocfname) { - FILE *infile; bool has_rem_singledensity = false; bool has_rem_highdensity = false; std::string path = std::string(tocfname); - infile = fopen(tocfname, "rt"); - path = get_file_path(path); - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rt"); + if (!infile) { return false; } + path = get_file_path(path); + while (!feof(infile)) { fgets(linebuffer, 511, infile); @@ -1195,7 +1196,7 @@ bool chdcd_is_gdicue(const char *tocfname) ------------------------------------------------------------------*/ /** - * @fn chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn std::error_condition chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse cue. * @@ -1203,7 +1204,7 @@ bool chdcd_is_gdicue(const char *tocfname) * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. * * Dreamcast discs have two images on a single disc. The first image is SINGLE-DENSITY and the second image * is HIGH-DENSITY. The SINGLE-DENSITY area starts 0 LBA and HIGH-DENSITY area starts 45000 LBA. @@ -1218,9 +1219,8 @@ bool chdcd_is_gdicue(const char *tocfname) * layout from a TOSEC .gdi. */ -chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +std::error_condition chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; int i, trknum; static char token[512]; std::string lastfname; @@ -1229,13 +1229,14 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac enum gdi_area current_area = SINGLE_DENSITY; enum gdi_pattern disc_pattern = TYPE_UNKNOWN; - infile = fopen(tocfname, "rt"); - path = get_file_path(path); - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rt"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -1297,14 +1298,14 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac { fclose(infile); printf("ERROR: couldn't read [%s] or not a valid .WAV\n", lastfname.c_str()); - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; } } else { fclose(infile); printf("ERROR: Unhandled track type %s\n", token); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } } else if (!strcmp(token, "TRACK")) @@ -1349,7 +1350,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac { fclose(infile); printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } /* next (optional) token on the line is the subcode type */ @@ -1462,7 +1463,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } outinfo.track[trknum].offset = outinfo.track[trknum-1].offset + outtoc.tracks[trknum-1].frames * (outtoc.tracks[trknum-1].datasize + outtoc.tracks[trknum-1].subsize); outtoc.tracks[trknum].frames = (tlen - outinfo.track[trknum].offset) / (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); @@ -1473,7 +1474,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum-1].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); outtoc.tracks[trknum].frames = tlen; @@ -1499,7 +1500,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac if (!outtoc.tracks[trknum].frames) { printf("ERROR: unable to determine size of track %d, missing INDEX 01 markers?\n", trknum+1); - return CHDERR_INVALID_DATA; + return chd_file::error::INVALID_DATA; } } else /* data files are different */ @@ -1508,7 +1509,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac if (tlen == 0) { printf("ERROR: couldn't find bin file [%s]\n", outinfo.track[trknum].fname.c_str()); - return CHDERR_FILE_NOT_FOUND; + return std::errc::no_such_file_or_directory; } tlen /= (outtoc.tracks[trknum].datasize + outtoc.tracks[trknum].subsize); outtoc.tracks[trknum].frames = tlen; @@ -1638,7 +1639,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac } #endif - return CHDERR_NONE; + return std::error_condition(); } /*------------------------------------------------- @@ -1646,7 +1647,7 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac -------------------------------------------------*/ /** - * @fn chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) + * @fn std::error_condition chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) * * @brief Chdcd parse TOC. * @@ -1654,12 +1655,11 @@ chd_error chdcd_parse_gdicue(const char *tocfname, cdrom_toc &outtoc, chdcd_trac * @param [in,out] outtoc The outtoc. * @param [in,out] outinfo The outinfo. * - * @return A chd_error. + * @return A std::error_condition. */ -chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) +std::error_condition chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo) { - FILE *infile; int i, trknum; static char token[512]; char tocftemp[512]; @@ -1695,14 +1695,14 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i std::string path = std::string(tocfname); - infile = fopen(tocfname, "rt"); - path = get_file_path(path); - - if (infile == (FILE *)nullptr) + FILE *infile = fopen(tocfname, "rt"); + if (!infile) { - return CHDERR_FILE_NOT_FOUND; + return std::error_condition(errno, std::generic_category()); } + path = get_file_path(path); + /* clear structures */ memset(&outtoc, 0, sizeof(outtoc)); outinfo.reset(); @@ -1817,7 +1817,7 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i { fclose(infile); printf("ERROR: Unknown track type [%s]. Contact MAMEDEV.\n", token); - return CHDERR_UNSUPPORTED_FORMAT; + return chd_file::error::UNSUPPORTED_FORMAT; } /* next (optional) token on the line is the subcode type */ @@ -1844,5 +1844,5 @@ chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_i /* store the number of tracks found */ outtoc.numtrks = trknum + 1; - return CHDERR_NONE; + return std::error_condition(); } diff --git a/src/lib/util/chdcd.h b/src/lib/util/chdcd.h index 65e4b943964..95f214e5831 100644 --- a/src/lib/util/chdcd.h +++ b/src/lib/util/chdcd.h @@ -5,14 +5,16 @@ CDRDAO TOC parser for CHD compression frontend ***************************************************************************/ - -#ifndef MAME_UTIL_CHDCD_H -#define MAME_UTIL_CHDCD_H +#ifndef MAME_LIB_UTIL_CHDCD_H +#define MAME_LIB_UTIL_CHDCD_H #pragma once #include "cdrom.h" +#include <system_error> + + struct chdcd_track_input_entry { chdcd_track_input_entry() { reset(); } @@ -33,6 +35,6 @@ struct chdcd_track_input_info }; -chd_error chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo); +std::error_condition chdcd_parse_toc(const char *tocfname, cdrom_toc &outtoc, chdcd_track_input_info &outinfo); -#endif // MAME_UTIL_CHDCD_H +#endif // MAME_LIB_UTIL_CHDCD_H diff --git a/src/lib/util/chdcodec.cpp b/src/lib/util/chdcodec.cpp index 4f5c23b3104..eca9403b56c 100644 --- a/src/lib/util/chdcodec.cpp +++ b/src/lib/util/chdcodec.cpp @@ -8,24 +8,29 @@ ***************************************************************************/ -#include <cassert> +#include "chdcodec.h" -#include "chd.h" -#include "hashing.h" #include "avhuff.h" -#include "flac.h" #include "cdrom.h" -#include <zlib.h> -#include "lzma/C/LzmaEnc.h" +#include "chd.h" +#include "flac.h" +#include "hashing.h" + #include "lzma/C/LzmaDec.h" +#include "lzma/C/LzmaEnc.h" + +#include <zlib.h> + #include <new> +namespace { + //************************************************************************** // GLOBAL VARIABLES //************************************************************************** -static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; +constexpr uint8_t f_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 }; @@ -35,7 +40,7 @@ static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff // ======================> chd_zlib_allocator -// allocation helper clas for zlib +// allocation helper class for zlib class chd_zlib_allocator { public: @@ -51,7 +56,8 @@ private: static voidpf fast_alloc(voidpf opaque, uInt items, uInt size); static void fast_free(voidpf opaque, voidpf address); - static const int MAX_ZLIB_ALLOCS = 64; + static constexpr int MAX_ZLIB_ALLOCS = 64; + uint32_t * m_allocptr[MAX_ZLIB_ALLOCS]; }; @@ -111,7 +117,7 @@ private: static void *fast_alloc(void *p, size_t size); static void fast_free(void *p, void *address); - static const int MAX_LZMA_ALLOCS = 64; + static constexpr int MAX_LZMA_ALLOCS = 64; uint32_t * m_allocptr[MAX_LZMA_ALLOCS]; }; @@ -287,7 +293,7 @@ private: // ======================> chd_cd_compressor -template<class _BaseCompressor, class _SubcodeCompressor> +template<class BaseCompressor, class SubcodeCompressor> class chd_cd_compressor : public chd_compressor { public: @@ -300,7 +306,7 @@ public: { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } // core functionality @@ -323,10 +329,10 @@ public: // clear out ECC data if we can uint8_t *sector = &m_buffer[framenum * CD_MAX_SECTOR_DATA]; - if (memcmp(sector, s_cd_sync_header, sizeof(s_cd_sync_header)) == 0 && ecc_verify(sector)) + if (memcmp(sector, f_cd_sync_header, sizeof(f_cd_sync_header)) == 0 && ecc_verify(sector)) { dest[framenum / 8] |= 1 << (framenum % 8); - memset(sector, 0, sizeof(s_cd_sync_header)); + memset(sector, 0, sizeof(f_cd_sync_header)); ecc_clear(sector); } } @@ -334,7 +340,7 @@ public: // encode the base portion uint32_t complen = m_base_compressor.compress(&m_buffer[0], frames * CD_MAX_SECTOR_DATA, &dest[header_bytes]); if (complen >= srclen) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // write compressed length dest[ecc_bytes + 0] = complen >> ((complen_bytes - 1) * 8); @@ -348,15 +354,15 @@ public: private: // internal state - _BaseCompressor m_base_compressor; - _SubcodeCompressor m_subcode_compressor; + BaseCompressor m_base_compressor; + SubcodeCompressor m_subcode_compressor; std::vector<uint8_t> m_buffer; }; // ======================> chd_cd_decompressor -template<class _BaseDecompressor, class _SubcodeDecompressor> +template<class BaseDecompressor, class SubcodeDecompressor> class chd_cd_decompressor : public chd_decompressor { public: @@ -369,7 +375,7 @@ public: { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } // core functionality @@ -400,7 +406,7 @@ public: uint8_t *sector = &dest[framenum * CD_FRAME_SIZE]; if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) { - memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); + memcpy(sector, f_cd_sync_header, sizeof(f_cd_sync_header)); ecc_generate(sector); } } @@ -408,8 +414,8 @@ public: private: // internal state - _BaseDecompressor m_base_decompressor; - _SubcodeDecompressor m_subcode_decompressor; + BaseDecompressor m_base_decompressor; + SubcodeDecompressor m_subcode_decompressor; std::vector<uint8_t> m_buffer; }; @@ -460,25 +466,65 @@ private: // CODEC LIST //************************************************************************** +// an entry in the list +struct codec_entry +{ + chd_codec_type m_type; + bool m_lossy; + const char * m_name; + chd_compressor::ptr (*m_construct_compressor)(chd_file &, uint32_t, bool); + chd_decompressor::ptr (*m_construct_decompressor)(chd_file &, uint32_t, bool); + + template <class CompressorClass> + static chd_compressor::ptr construct_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy) + { + return std::make_unique<CompressorClass>(chd, hunkbytes, lossy); + } + + template <class DecompressorClass> + static chd_decompressor::ptr construct_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy) + { + return std::make_unique<DecompressorClass>(chd, hunkbytes, lossy); + } +}; + + // static list of available known codecs -const chd_codec_list::codec_entry chd_codec_list::s_codec_list[] = +const codec_entry f_codec_list[] = { // general codecs - { CHD_CODEC_ZLIB, false, "Deflate", &chd_codec_list::construct_compressor<chd_zlib_compressor>, &chd_codec_list::construct_decompressor<chd_zlib_decompressor> }, - { CHD_CODEC_LZMA, false, "LZMA", &chd_codec_list::construct_compressor<chd_lzma_compressor>, &chd_codec_list::construct_decompressor<chd_lzma_decompressor> }, - { CHD_CODEC_HUFFMAN, false, "Huffman", &chd_codec_list::construct_compressor<chd_huffman_compressor>, &chd_codec_list::construct_decompressor<chd_huffman_decompressor> }, - { CHD_CODEC_FLAC, false, "FLAC", &chd_codec_list::construct_compressor<chd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_flac_decompressor> }, + { CHD_CODEC_ZLIB, false, "Deflate", &codec_entry::construct_compressor<chd_zlib_compressor>, &codec_entry::construct_decompressor<chd_zlib_decompressor> }, + { CHD_CODEC_LZMA, false, "LZMA", &codec_entry::construct_compressor<chd_lzma_compressor>, &codec_entry::construct_decompressor<chd_lzma_decompressor> }, + { CHD_CODEC_HUFFMAN, false, "Huffman", &codec_entry::construct_compressor<chd_huffman_compressor>, &codec_entry::construct_decompressor<chd_huffman_decompressor> }, + { CHD_CODEC_FLAC, false, "FLAC", &codec_entry::construct_compressor<chd_flac_compressor>, &codec_entry::construct_decompressor<chd_flac_decompressor> }, // general codecs with CD frontend - { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > }, - { CHD_CODEC_CD_LZMA, false, "CD LZMA", &chd_codec_list::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &chd_codec_list::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > }, - { CHD_CODEC_CD_FLAC, false, "CD FLAC", &chd_codec_list::construct_compressor<chd_cd_flac_compressor>, &chd_codec_list::construct_decompressor<chd_cd_flac_decompressor> }, + { CHD_CODEC_CD_ZLIB, false, "CD Deflate", &codec_entry::construct_compressor<chd_cd_compressor<chd_zlib_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_zlib_decompressor, chd_zlib_decompressor> > }, + { CHD_CODEC_CD_LZMA, false, "CD LZMA", &codec_entry::construct_compressor<chd_cd_compressor<chd_lzma_compressor, chd_zlib_compressor> >, &codec_entry::construct_decompressor<chd_cd_decompressor<chd_lzma_decompressor, chd_zlib_decompressor> > }, + { CHD_CODEC_CD_FLAC, false, "CD FLAC", &codec_entry::construct_compressor<chd_cd_flac_compressor>, &codec_entry::construct_decompressor<chd_cd_flac_decompressor> }, // A/V codecs - { CHD_CODEC_AVHUFF, false, "A/V Huffman", &chd_codec_list::construct_compressor<chd_avhuff_compressor>, &chd_codec_list::construct_decompressor<chd_avhuff_decompressor> }, + { CHD_CODEC_AVHUFF, false, "A/V Huffman", &codec_entry::construct_compressor<chd_avhuff_compressor>, &codec_entry::construct_decompressor<chd_avhuff_decompressor> }, }; +//------------------------------------------------- +// find_in_list - create a new compressor +// instance of the given type +//------------------------------------------------- + +const codec_entry *find_in_list(chd_codec_type type) +{ + // find in the list and construct the class + for (auto & elem : f_codec_list) + if (elem.m_type == type) + return &elem; + return nullptr; +} + +} // anonymous namespace + + //************************************************************************** // CHD CODEC @@ -512,7 +558,7 @@ chd_codec::~chd_codec() void chd_codec::configure(int param, void *config) { // if not overridden, it is always a failure - throw CHDERR_INVALID_PARAMETER; + throw std::error_condition(std::errc::invalid_argument); } @@ -556,11 +602,11 @@ chd_decompressor::chd_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy // instance of the given type //------------------------------------------------- -chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &chd) +chd_compressor::ptr chd_codec_list::new_compressor(chd_codec_type type, chd_file &chd) { // find in the list and construct the class - const codec_entry *entry = find_in_list(type); - return (entry == nullptr) ? nullptr : (*entry->m_construct_compressor)(chd, chd.hunk_bytes(), entry->m_lossy); + codec_entry const *const entry = find_in_list(type); + return entry ? (*entry->m_construct_compressor)(chd, chd.hunk_bytes(), entry->m_lossy) : nullptr; } @@ -569,39 +615,36 @@ chd_compressor *chd_codec_list::new_compressor(chd_codec_type type, chd_file &ch // instance of the given type //------------------------------------------------- -chd_decompressor *chd_codec_list::new_decompressor(chd_codec_type type, chd_file &chd) +chd_decompressor::ptr chd_codec_list::new_decompressor(chd_codec_type type, chd_file &chd) { // find in the list and construct the class const codec_entry *entry = find_in_list(type); - return (entry == nullptr) ? nullptr : (*entry->m_construct_decompressor)(chd, chd.hunk_bytes(), entry->m_lossy); + return entry ? (*entry->m_construct_decompressor)(chd, chd.hunk_bytes(), entry->m_lossy) : nullptr; } //------------------------------------------------- -// codec_name - return the name of the given -// codec +// codec_exists - determine whether a codec type +// corresponds to a supported codec //------------------------------------------------- -const char *chd_codec_list::codec_name(chd_codec_type type) +bool chd_codec_list::codec_exists(chd_codec_type type) { // find in the list and construct the class - const codec_entry *entry = find_in_list(type); - return (entry == nullptr) ? nullptr : entry->m_name; + return bool(find_in_list(type)); } //------------------------------------------------- -// find_in_list - create a new compressor -// instance of the given type +// codec_name - return the name of the given +// codec //------------------------------------------------- -const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type type) +const char *chd_codec_list::codec_name(chd_codec_type type) { // find in the list and construct the class - for (auto & elem : s_codec_list) - if (elem.m_type == type) - return &elem; - return nullptr; + const codec_entry *entry = find_in_list(type); + return entry ? entry->m_name : nullptr; } @@ -615,25 +658,24 @@ const chd_codec_list::codec_entry *chd_codec_list::find_in_list(chd_codec_type t //------------------------------------------------- chd_compressor_group::chd_compressor_group(chd_file &chd, uint32_t compressor_list[4]) - : m_hunkbytes(chd.hunk_bytes()), - m_compress_test(m_hunkbytes) + : m_hunkbytes(chd.hunk_bytes()) + , m_compress_test(m_hunkbytes) #if CHDCODEC_VERIFY_COMPRESSION - ,m_decompressed(m_hunkbytes) + , m_decompressed(m_hunkbytes) #endif { // verify the compression types and initialize the codecs for (int codecnum = 0; codecnum < std::size(m_compressor); codecnum++) { - m_compressor[codecnum] = nullptr; if (compressor_list[codecnum] != CHD_CODEC_NONE) { m_compressor[codecnum] = chd_codec_list::new_compressor(compressor_list[codecnum], chd); - if (m_compressor[codecnum] == nullptr) - throw CHDERR_UNKNOWN_COMPRESSION; + if (!m_compressor[codecnum]) + throw std::error_condition(chd_file::error::UNKNOWN_COMPRESSION); #if CHDCODEC_VERIFY_COMPRESSION m_decompressor[codecnum] = chd_codec_list::new_decompressor(compressor_list[codecnum], chd); - if (m_decompressor[codecnum] == nullptr) - throw CHDERR_UNKNOWN_COMPRESSION; + if (!m_decompressor[codecnum]) + throw std::error_condition(chd_file::error::UNKNOWN_COMPRESSION); #endif } } @@ -646,9 +688,7 @@ chd_compressor_group::chd_compressor_group(chd_file &chd, uint32_t compressor_li chd_compressor_group::~chd_compressor_group() { - // delete the codecs and the test buffer - for (auto & elem : m_compressor) - delete elem; + // codecs and test buffer deleted automatically } @@ -664,7 +704,7 @@ int8_t chd_compressor_group::find_best_compressor(const uint8_t *src, uint8_t *c complen = m_hunkbytes; int8_t compression = -1; for (int codecnum = 0; codecnum < std::size(m_compressor); codecnum++) - if (m_compressor[codecnum] != nullptr) + if (m_compressor[codecnum]) { // attempt to compress, swallowing errors try @@ -702,7 +742,9 @@ printf(" codec%d=%d bytes \n", codecnum, compbytes); memcpy(compressed, &m_compress_test[0], compbytes); } } - catch (...) { } + catch (...) + { + } } // if the best is none, copy it over @@ -835,7 +877,7 @@ chd_zlib_compressor::chd_zlib_compressor(chd_file &chd, uint32_t hunkbytes, bool if (zerr == Z_MEM_ERROR) throw std::bad_alloc(); else if (zerr != Z_OK) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } @@ -864,14 +906,14 @@ uint32_t chd_zlib_compressor::compress(const uint8_t *src, uint32_t srclen, uint m_deflater.total_out = 0; int zerr = deflateReset(&m_deflater); if (zerr != Z_OK) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // do it zerr = deflate(&m_deflater, Z_FINISH); // if we ended up with more data than we started with, return an error if (zerr != Z_STREAM_END || m_deflater.total_out >= srclen) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // otherwise, return the length return m_deflater.total_out; @@ -900,7 +942,7 @@ chd_zlib_decompressor::chd_zlib_decompressor(chd_file &chd, uint32_t hunkbytes, if (zerr == Z_MEM_ERROR) throw std::bad_alloc(); else if (zerr != Z_OK) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } @@ -930,14 +972,14 @@ void chd_zlib_decompressor::decompress(const uint8_t *src, uint32_t complen, uin m_inflater.total_out = 0; int zerr = inflateReset(&m_inflater); if (zerr != Z_OK) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // do it zerr = inflate(&m_inflater, Z_FINISH); if (zerr != Z_STREAM_END) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); if (m_inflater.total_out != destlen) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); } @@ -1071,20 +1113,20 @@ uint32_t chd_lzma_compressor::compress(const uint8_t *src, uint32_t srclen, uint // allocate the encoder CLzmaEncHandle encoder = LzmaEnc_Create(&m_allocator); if (encoder == nullptr) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); try { // configure the encoder SRes res = LzmaEnc_SetProps(encoder, &m_props); if (res != SZ_OK) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // run it SizeT complen = srclen; res = LzmaEnc_MemEncode(encoder, dest, &complen, src, srclen, 0, nullptr, &m_allocator, &m_allocator); if (res != SZ_OK) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // clean up LzmaEnc_Destroy(encoder, &m_allocator, &m_allocator); @@ -1140,24 +1182,24 @@ chd_lzma_decompressor::chd_lzma_decompressor(chd_file &chd, uint32_t hunkbytes, // convert to decoder properties CLzmaEncHandle enc = LzmaEnc_Create(&m_allocator); if (!enc) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK) { LzmaEnc_Destroy(enc, &m_allocator, &m_allocator); - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); } Byte decoder_props[LZMA_PROPS_SIZE]; SizeT props_size = sizeof(decoder_props); if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK) { LzmaEnc_Destroy(enc, &m_allocator, &m_allocator); - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); } LzmaEnc_Destroy(enc, &m_allocator, &m_allocator); // do memory allocations if (LzmaDec_Allocate(&m_decoder, decoder_props, LZMA_PROPS_SIZE, &m_allocator) != SZ_OK) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); } @@ -1188,7 +1230,7 @@ void chd_lzma_decompressor::decompress(const uint8_t *src, uint32_t complen, uin ELzmaStatus status; SRes res = LzmaDec_DecodeToBuf(&m_decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status); if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); } @@ -1216,7 +1258,7 @@ uint32_t chd_huffman_compressor::compress(const uint8_t *src, uint32_t srclen, u { uint32_t complen; if (m_encoder.encode(src, srclen, dest, srclen, complen) != HUFFERR_NONE) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); return complen; } @@ -1244,7 +1286,7 @@ chd_huffman_decompressor::chd_huffman_decompressor(chd_file &chd, uint32_t hunkb void chd_huffman_decompressor::decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) { if (m_decoder.decode(src, complen, dest, destlen) != HUFFERR_NONE) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); } @@ -1282,19 +1324,19 @@ uint32_t chd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, uint // reset and encode big-endian m_encoder.reset(dest + 1, hunkbytes() - 1); if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, !m_big_endian)) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); uint32_t complen_be = m_encoder.finish(); // reset and encode little-endian m_encoder.reset(dest + 1, hunkbytes() - 1); if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, m_big_endian)) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); uint32_t complen_le = m_encoder.finish(); // pick the best one and add a byte uint32_t complen = std::min(complen_le, complen_be); if (complen + 1 >= hunkbytes()) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // if big-endian was better, re-do it dest[0] = 'L'; @@ -1303,7 +1345,7 @@ uint32_t chd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, uint dest[0] = 'B'; m_encoder.reset(dest + 1, hunkbytes() - 1); if (!m_encoder.encode_interleaved(reinterpret_cast<const int16_t *>(src), srclen / 4, !m_big_endian)) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); m_encoder.finish(); } return complen + 1; @@ -1358,13 +1400,13 @@ void chd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, uin else if (src[0] == 'B') swap_endian = !m_big_endian; else - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // reset and decode if (!m_decoder.reset(44100, 2, chd_flac_compressor::blocksize(destlen), src + 1, complen - 1)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); if (!m_decoder.decode_interleaved(reinterpret_cast<int16_t *>(dest), destlen / 4, swap_endian)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // finish up m_decoder.finish(); @@ -1386,7 +1428,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, uint32_t hunkbytes { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); // determine whether we want native or swapped samples uint16_t native_endian = 0; @@ -1409,7 +1451,7 @@ chd_cd_flac_compressor::chd_cd_flac_compressor(chd_file &chd, uint32_t hunkbytes if (zerr == Z_MEM_ERROR) throw std::bad_alloc(); else if (zerr != Z_OK) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } @@ -1442,7 +1484,7 @@ uint32_t chd_cd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, u m_encoder.reset(dest, hunkbytes()); uint8_t *buffer = &m_buffer[0]; if (!m_encoder.encode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // finish up uint32_t complen = m_encoder.finish(); @@ -1456,7 +1498,7 @@ uint32_t chd_cd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, u m_deflater.total_out = 0; int zerr = deflateReset(&m_deflater); if (zerr != Z_OK) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); // do it zerr = deflate(&m_deflater, Z_FINISH); @@ -1464,7 +1506,7 @@ uint32_t chd_cd_flac_compressor::compress(const uint8_t *src, uint32_t srclen, u // if we ended up with more data than we started with, return an error complen += m_deflater.total_out; if (zerr != Z_STREAM_END || complen >= srclen) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); return complen; } @@ -1515,7 +1557,7 @@ chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, uint32_t hunkb { // make sure the CHD's hunk size is an even multiple of the frame size if (hunkbytes % CD_FRAME_SIZE != 0) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); // determine whether we want native or swapped samples uint16_t native_endian = 0; @@ -1532,7 +1574,7 @@ chd_cd_flac_decompressor::chd_cd_flac_decompressor(chd_file &chd, uint32_t hunkb if (zerr == Z_MEM_ERROR) throw std::bad_alloc(); else if (zerr != Z_OK) - throw CHDERR_CODEC_ERROR; + throw std::error_condition(chd_file::error::CODEC_ERROR); } /** @@ -1569,10 +1611,10 @@ void chd_cd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, // reset and decode uint32_t frames = destlen / CD_FRAME_SIZE; if (!m_decoder.reset(44100, 2, chd_cd_flac_compressor::blocksize(frames * CD_MAX_SECTOR_DATA), src, complen)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); uint8_t *buffer = &m_buffer[0]; if (!m_decoder.decode_interleaved(reinterpret_cast<int16_t *>(buffer), frames * CD_MAX_SECTOR_DATA/4, m_swap_endian)) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // inflate the subcode data uint32_t offset = m_decoder.finish(); @@ -1584,14 +1626,14 @@ void chd_cd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, m_inflater.total_out = 0; int zerr = inflateReset(&m_inflater); if (zerr != Z_OK) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // do it zerr = inflate(&m_inflater, Z_FINISH); if (zerr != Z_STREAM_END) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); if (m_inflater.total_out != frames * CD_MAX_SUBCODE_DATA) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // reassemble the data for (uint32_t framenum = 0; framenum < frames; framenum++) @@ -1620,15 +1662,15 @@ void chd_cd_flac_decompressor::decompress(const uint8_t *src, uint32_t complen, */ chd_avhuff_compressor::chd_avhuff_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy) - : chd_compressor(chd, hunkbytes, lossy), - m_postinit(false) + : chd_compressor(chd, hunkbytes, lossy) + , m_postinit(false) { try { // attempt to do a post-init now postinit(); } - catch (chd_error &) + catch (std::error_condition const &) { // if we're creating a new CHD, it won't work but that's ok } @@ -1665,14 +1707,14 @@ uint32_t chd_avhuff_compressor::compress(const uint8_t *src, uint32_t srclen, ui int size = avhuff_encoder::raw_data_size(src); while (size < srclen) if (src[size++] != 0) - throw CHDERR_INVALID_DATA; + throw std::error_condition(chd_file::error::INVALID_DATA); } // encode the audio and video uint32_t complen; avhuff_error averr = m_encoder.encode_data(src, dest, complen); if (averr != AVHERR_NONE || complen > srclen) - throw CHDERR_COMPRESSION_ERROR; + throw std::error_condition(chd_file::error::COMPRESSION_ERROR); return complen; } @@ -1693,21 +1735,21 @@ void chd_avhuff_compressor::postinit() { // get the metadata std::string metadata; - chd_error err = chd().read_metadata(AV_METADATA_TAG, 0, metadata); - if (err != CHDERR_NONE) + std::error_condition err = chd().read_metadata(AV_METADATA_TAG, 0, metadata); + if (err) throw err; // extract the info int fps, fpsfrac, width, height, interlaced, channels, rate; if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) - throw CHDERR_INVALID_METADATA; + throw std::error_condition(chd_file::error::INVALID_METADATA); // compute the bytes per frame uint32_t fps_times_1million = fps * 1000000 + fpsfrac; uint32_t max_samples_per_frame = (uint64_t(rate) * 1000000 + fps_times_1million - 1) / fps_times_1million; uint32_t bytes_per_frame = 12 + channels * max_samples_per_frame * 2 + width * height * 2; if (bytes_per_frame > hunkbytes()) - throw CHDERR_INVALID_METADATA; + throw std::error_condition(chd_file::error::INVALID_METADATA); // done with post-init m_postinit = true; @@ -1757,7 +1799,7 @@ void chd_avhuff_decompressor::decompress(const uint8_t *src, uint32_t complen, u // decode the audio and video avhuff_error averr = m_decoder.decode_data(src, complen, dest); if (averr != AVHERR_NONE) - throw CHDERR_DECOMPRESSION_ERROR; + throw std::error_condition(chd_file::error::DECOMPRESSION_ERROR); // pad short frames with 0 if (dest != nullptr) @@ -1790,5 +1832,5 @@ void chd_avhuff_decompressor::configure(int param, void *config) // anything else is invalid else - throw CHDERR_INVALID_PARAMETER; + throw std::error_condition(std::errc::invalid_argument); } diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h index dc9c074fd37..700ae8f8979 100644 --- a/src/lib/util/chdcodec.h +++ b/src/lib/util/chdcodec.h @@ -7,13 +7,15 @@ Codecs used by the CHD format ***************************************************************************/ - -#ifndef MAME_UTIL_CHDCODEC_H -#define MAME_UTIL_CHDCODEC_H +#ifndef MAME_LIB_UTIL_CHDCODEC_H +#define MAME_LIB_UTIL_CHDCODEC_H #pragma once +#include "utilfwd.h" + #include <cstdint> +#include <memory> #include <vector> @@ -21,20 +23,9 @@ //************************************************************************** -// MACROS -//************************************************************************** - -#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) - - - -//************************************************************************** // TYPE DEFINITIONS //************************************************************************** -// forward references -class chd_file; - // base types typedef uint32_t chd_codec_type; @@ -63,7 +54,7 @@ public: private: // internal state chd_file & m_chd; - uint32_t m_hunkbytes; + uint32_t m_hunkbytes; bool m_lossy; }; @@ -78,6 +69,8 @@ protected: chd_compressor(chd_file &file, uint32_t hunkbytes, bool lossy); public: + using ptr = std::unique_ptr<chd_compressor>; + // implementation virtual uint32_t compress(const uint8_t *src, uint32_t srclen, uint8_t *dest) = 0; }; @@ -93,6 +86,8 @@ protected: chd_decompressor(chd_file &file, uint32_t hunkbytes, bool lossy); public: + using ptr = std::unique_ptr<chd_decompressor>; + // implementation virtual void decompress(const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) = 0; }; @@ -105,35 +100,12 @@ class chd_codec_list { public: // create compressors or decompressors - static chd_compressor *new_compressor(chd_codec_type type, chd_file &file); - static chd_decompressor *new_decompressor(chd_codec_type type, chd_file &file); + static chd_compressor::ptr new_compressor(chd_codec_type type, chd_file &file); + static chd_decompressor::ptr new_decompressor(chd_codec_type type, chd_file &file); // utilities - static bool codec_exists(chd_codec_type type) { return (find_in_list(type) != nullptr); } + static bool codec_exists(chd_codec_type type); static const char *codec_name(chd_codec_type type); - -private: - // an entry in the list - struct codec_entry - { - chd_codec_type m_type; - bool m_lossy; - const char * m_name; - chd_compressor * (*m_construct_compressor)(chd_file &, uint32_t, bool); - chd_decompressor * (*m_construct_decompressor)(chd_file &, uint32_t, bool); - }; - - // internal helper functions - static const codec_entry *find_in_list(chd_codec_type type); - - template<class _CompressorClass> - static chd_compressor *construct_compressor(chd_file &chd, uint32_t hunkbytes, bool lossy) { return new _CompressorClass(chd, hunkbytes, lossy); } - - template<class _DecompressorClass> - static chd_decompressor *construct_decompressor(chd_file &chd, uint32_t hunkbytes, bool lossy) { return new _DecompressorClass(chd, hunkbytes, lossy); } - - // the static list - static const codec_entry s_codec_list[]; }; @@ -152,37 +124,48 @@ public: private: // internal state - uint32_t m_hunkbytes; // number of bytes in a hunk - chd_compressor * m_compressor[4]; // array of active codecs - std::vector<uint8_t> m_compress_test; // test buffer for compression + uint32_t m_hunkbytes; // number of bytes in a hunk + chd_compressor::ptr m_compressor[4]; // array of active codecs + std::vector<uint8_t> m_compress_test; // test buffer for compression #if CHDCODEC_VERIFY_COMPRESSION - chd_decompressor * m_decompressor[4]; // array of active codecs - std::vector<uint8_t> m_decompressed; // verification buffer + chd_decompressor::ptr m_decompressor[4]; // array of active codecs + std::vector<uint8_t> m_decompressed; // verification buffer #endif }; //************************************************************************** +// MACROS +//************************************************************************** + +constexpr chd_codec_type CHD_MAKE_TAG(char a, char b, char c, char d) +{ + return (uint32_t(uint8_t(a)) << 24) | uint32_t(uint8_t((b)) << 16) | uint32_t(uint8_t((c)) << 8) | uint32_t(uint8_t(d)); +} + + + +//************************************************************************** // CONSTANTS //************************************************************************** // currently-defined codecs -const chd_codec_type CHD_CODEC_NONE = 0; +constexpr chd_codec_type CHD_CODEC_NONE = 0; // general codecs -const chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b'); -const chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); -const chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); -const chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c'); +constexpr chd_codec_type CHD_CODEC_ZLIB = CHD_MAKE_TAG('z','l','i','b'); +constexpr chd_codec_type CHD_CODEC_LZMA = CHD_MAKE_TAG('l','z','m','a'); +constexpr chd_codec_type CHD_CODEC_HUFFMAN = CHD_MAKE_TAG('h','u','f','f'); +constexpr chd_codec_type CHD_CODEC_FLAC = CHD_MAKE_TAG('f','l','a','c'); // general codecs with CD frontend -const chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'); -const chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z'); -const chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); +constexpr chd_codec_type CHD_CODEC_CD_ZLIB = CHD_MAKE_TAG('c','d','z','l'); +constexpr chd_codec_type CHD_CODEC_CD_LZMA = CHD_MAKE_TAG('c','d','l','z'); +constexpr chd_codec_type CHD_CODEC_CD_FLAC = CHD_MAKE_TAG('c','d','f','l'); // A/V codecs -const chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); +constexpr chd_codec_type CHD_CODEC_AVHUFF = CHD_MAKE_TAG('a','v','h','u'); // A/V codec configuration parameters enum @@ -190,4 +173,4 @@ enum AVHUFF_CODEC_DECOMPRESS_CONFIG = 1 }; -#endif // MAME_UTIL_CHDCODEC_H +#endif // MAME_LIB_UTIL_CHDCODEC_H diff --git a/src/lib/util/corefile.cpp b/src/lib/util/corefile.cpp index e681c0f1fac..0c72e1337be 100644 --- a/src/lib/util/corefile.cpp +++ b/src/lib/util/corefile.cpp @@ -12,18 +12,16 @@ #include "coretmpl.h" #include "osdcore.h" +#include "path.h" #include "unicode.h" #include "vecstream.h" -#include <zlib.h> - #include <algorithm> #include <cassert> +#include <cctype> #include <cstring> #include <iterator> -#include <cctype> - namespace util { @@ -43,110 +41,11 @@ namespace { TYPE DEFINITIONS ***************************************************************************/ -class zlib_data -{ -public: - typedef std::unique_ptr<zlib_data> ptr; - - static int start_compression(int level, std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - result->reset_output(); - auto const zerr = deflateInit(&result->m_stream, level); - result->m_compress = (Z_OK == zerr); - if (result->m_compress) data = std::move(result); - return zerr; - } - static int start_decompression(std::uint64_t offset, ptr &data) - { - ptr result(new zlib_data(offset)); - auto const zerr = inflateInit(&result->m_stream); - result->m_decompress = (Z_OK == zerr); - if (result->m_decompress) data = std::move(result); - return zerr; - } - - ~zlib_data() - { - if (m_compress) deflateEnd(&m_stream); - else if (m_decompress) inflateEnd(&m_stream); - } - - std::size_t buffer_size() const { return sizeof(m_buffer); } - void const *buffer_data() const { return m_buffer; } - void *buffer_data() { return m_buffer; } - - // general-purpose output buffer manipulation - bool output_full() const { return 0 == m_stream.avail_out; } - std::size_t output_space() const { return m_stream.avail_out; } - void set_output(void *data, std::uint32_t size) - { - m_stream.next_out = reinterpret_cast<Bytef *>(data); - m_stream.avail_out = size; - } - - // working with output to the internal buffer - bool has_output() const { return m_stream.avail_out != sizeof(m_buffer); } - std::size_t output_size() const { return sizeof(m_buffer) - m_stream.avail_out; } - void reset_output() - { - m_stream.next_out = m_buffer; - m_stream.avail_out = sizeof(m_buffer); - } - - // general-purpose input buffer manipulation - bool has_input() const { return 0 != m_stream.avail_in; } - std::size_t input_size() const { return m_stream.avail_in; } - void set_input(void const *data, std::uint32_t size) - { - m_stream.next_in = const_cast<Bytef *>(reinterpret_cast<Bytef const *>(data)); - m_stream.avail_in = size; - } - - // working with input from the internal buffer - void reset_input(std::uint32_t size) - { - m_stream.next_in = m_buffer; - m_stream.avail_in = size; - } - - int compress() { assert(m_compress); return deflate(&m_stream, Z_NO_FLUSH); } - int finalise() { assert(m_compress); return deflate(&m_stream, Z_FINISH); } - int decompress() { assert(m_decompress); return inflate(&m_stream, Z_SYNC_FLUSH); } - - std::uint64_t realoffset() const { return m_realoffset; } - void add_realoffset(std::uint32_t increment) { m_realoffset += increment; } - - bool is_nextoffset(std::uint64_t value) const { return m_nextoffset == value; } - void add_nextoffset(std::uint32_t increment) { m_nextoffset += increment; } - -private: - zlib_data(std::uint64_t offset) - : m_compress(false) - , m_decompress(false) - , m_realoffset(offset) - , m_nextoffset(offset) - { - m_stream.zalloc = Z_NULL; - m_stream.zfree = Z_NULL; - m_stream.opaque = Z_NULL; - m_stream.avail_in = m_stream.avail_out = 0; - } - - bool m_compress, m_decompress; - z_stream m_stream; - std::uint8_t m_buffer[1024]; - std::uint64_t m_realoffset; - std::uint64_t m_nextoffset; -}; - - class core_proxy_file : public core_file { public: - core_proxy_file(core_file &file) : m_file(file) { } + core_proxy_file(core_file &file) noexcept : m_file(file) { } virtual ~core_proxy_file() override { } - virtual osd_file::error compress(int level) override { return m_file.compress(level); } virtual int seek(std::int64_t offset, int whence) override { return m_file.seek(offset, whence); } virtual std::uint64_t tell() const override { return m_file.tell(); } @@ -162,9 +61,9 @@ public: virtual std::uint32_t write(const void *buffer, std::uint32_t length) override { return m_file.write(buffer, length); } virtual int puts(std::string_view s) override { return m_file.puts(s); } virtual int vprintf(util::format_argument_pack<std::ostream> const &args) override { return m_file.vprintf(args); } - virtual osd_file::error truncate(std::uint64_t offset) override { return m_file.truncate(offset); } + virtual std::error_condition truncate(std::uint64_t offset) override { return m_file.truncate(offset); } - virtual osd_file::error flush() override { return m_file.flush(); } + virtual std::error_condition flush() override { return m_file.flush(); } private: core_file &m_file; @@ -230,12 +129,12 @@ public: if (copy) { void *const buf = allocate(); - if (buf) std::memcpy(buf, data, length); + if (buf) + std::memcpy(buf, data, length); } } ~core_in_memory_file() override { purge(); } - virtual osd_file::error compress(int level) override { return osd_file::error::INVALID_ACCESS; } virtual int seek(std::int64_t offset, int whence) override; virtual std::uint64_t tell() const override { return m_offset; } @@ -246,8 +145,8 @@ public: virtual void const *buffer() override { return m_data; } virtual std::uint32_t write(void const *buffer, std::uint32_t length) override { return 0; } - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override { clear_putback(); return osd_file::error::NONE; } + virtual std::error_condition truncate(std::uint64_t offset) override; + virtual std::error_condition flush() override { clear_putback(); return std::error_condition(); } protected: core_in_memory_file(std::uint32_t openflags, std::uint64_t length) @@ -262,7 +161,8 @@ protected: bool is_loaded() const { return nullptr != m_data; } void *allocate() { - if (m_data) return nullptr; + if (m_data) + return nullptr; void *data = malloc(m_length); if (data) { @@ -273,7 +173,8 @@ protected: } void purge() { - if (m_data && m_data_allocated) free(const_cast<void *>(m_data)); + if (m_data && m_data_allocated) + free(const_cast<void *>(m_data)); m_data_allocated = false; m_data = nullptr; } @@ -301,23 +202,18 @@ public: core_osd_file(std::uint32_t openmode, osd_file::ptr &&file, std::uint64_t length) : core_in_memory_file(openmode, length) , m_file(std::move(file)) - , m_zdata() , m_bufferbase(0) , m_bufferbytes(0) { } ~core_osd_file() override; - virtual osd_file::error compress(int level) override; - - virtual int seek(std::int64_t offset, int whence) override; - virtual std::uint32_t read(void *buffer, std::uint32_t length) override; virtual void const *buffer() override; virtual std::uint32_t write(void const *buffer, std::uint32_t length) override; - virtual osd_file::error truncate(std::uint64_t offset) override; - virtual osd_file::error flush() override; + virtual std::error_condition truncate(std::uint64_t offset) override; + virtual std::error_condition flush() override; protected: @@ -326,11 +222,7 @@ protected: private: static constexpr std::size_t FILE_BUFFER_SIZE = 512; - osd_file::error osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual); - osd_file::ptr m_file; // OSD file handle - zlib_data::ptr m_zdata; // compression data std::uint64_t m_bufferbase; // base offset of internal buffer std::uint32_t m_bufferbytes; // bytes currently loaded into buffer std::uint8_t m_buffer[FILE_BUFFER_SIZE]; // buffer data @@ -673,14 +565,14 @@ std::uint32_t core_in_memory_file::read(void *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -osd_file::error core_in_memory_file::truncate(std::uint64_t offset) +std::error_condition core_in_memory_file::truncate(std::uint64_t offset) { if (m_length < offset) - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code // adjust to new length and offset set_length(offset); - return osd_file::error::NONE; + return std::error_condition(); } @@ -719,91 +611,6 @@ std::size_t core_in_memory_file::safe_buffer_copy( core_osd_file::~core_osd_file() { // close files and free memory - if (m_zdata) - core_osd_file::compress(FCOMPRESS_NONE); -} - - -/*------------------------------------------------- - compress - enable/disable streaming file - compression via zlib; level is 0 to disable - compression, or up to 9 for max compression --------------------------------------------------*/ - -osd_file::error core_osd_file::compress(int level) -{ - osd_file::error result = osd_file::error::NONE; - - // can only do this for read-only and write-only cases - if (read_access() && write_access()) - return osd_file::error::INVALID_ACCESS; - - // if we have been compressing, flush and free the data - if (m_zdata && (level == FCOMPRESS_NONE)) - { - int zerr = Z_OK; - - // flush any remaining data if we are writing - while (write_access() && (zerr != Z_STREAM_END)) - { - // deflate some more - zerr = m_zdata->finalise(); - if ((zerr != Z_STREAM_END) && (zerr != Z_OK)) - { - result = osd_file::error::INVALID_DATA; - break; - } - - // write the data - if (m_zdata->has_output()) - { - std::uint32_t actualdata; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } - - // free memory - m_zdata.reset(); - } - - // if we are just starting to compress, allocate a new buffer - if (!m_zdata && (level > FCOMPRESS_NONE)) - { - int zerr; - - // initialize the stream and compressor - if (write_access()) - zerr = zlib_data::start_compression(level, offset(), m_zdata); - else - zerr = zlib_data::start_decompression(offset(), m_zdata); - - // on error, return an error - if (zerr != Z_OK) - return osd_file::error::OUT_OF_MEMORY; - - // flush buffers - m_bufferbytes = 0; - } - - return result; -} - - -/*------------------------------------------------- - seek - seek within a file --------------------------------------------------*/ - -int core_osd_file::seek(std::int64_t offset, int whence) -{ - // error if compressing - if (m_zdata) - return 1; - else - return core_in_memory_file::seek(offset, whence); } @@ -833,7 +640,7 @@ std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length) // read as much as makes sense into the buffer m_bufferbase = offset() + bytes_read; m_bufferbytes = 0; - osd_or_zlib_read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes); + m_file->read(m_buffer, m_bufferbase, sizeof(m_buffer), m_bufferbytes); // do a bounded copy from the buffer to the destination bytes_read += safe_buffer_copy(m_buffer, 0, m_bufferbytes, buffer, bytes_read, length); @@ -842,7 +649,7 @@ std::uint32_t core_osd_file::read(void *buffer, std::uint32_t length) { // read the remainder directly from the file std::uint32_t new_bytes_read = 0; - osd_or_zlib_read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); + m_file->read(reinterpret_cast<std::uint8_t *>(buffer) + bytes_read, offset() + bytes_read, length - bytes_read, new_bytes_read); bytes_read += new_bytes_read; } } @@ -866,18 +673,16 @@ void const *core_osd_file::buffer() { // allocate some memory void *buf = allocate(); - if (!buf) return nullptr; + if (!buf) + return nullptr; // read the file std::uint32_t read_length = 0; - auto const filerr = osd_or_zlib_read(buf, 0, length(), read_length); - if ((filerr != osd_file::error::NONE) || (read_length != length())) + auto const filerr = m_file->read(buf, 0, length(), read_length); + if (filerr || (read_length != length())) purge(); else - { - // close the file because we don't need it anymore - m_file.reset(); - } + m_file.reset(); // close the file because we don't need it anymore } return core_in_memory_file::buffer(); } @@ -901,7 +706,7 @@ std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) // do the write std::uint32_t bytes_written = 0; - osd_or_zlib_write(buffer, offset(), length, bytes_written); + m_file->write(buffer, offset(), length, bytes_written); // return the number of bytes written add_offset(bytes_written); @@ -913,19 +718,19 @@ std::uint32_t core_osd_file::write(void const *buffer, std::uint32_t length) truncate - truncate a file -------------------------------------------------*/ -osd_file::error core_osd_file::truncate(std::uint64_t offset) +std::error_condition core_osd_file::truncate(std::uint64_t offset) { if (is_loaded()) return core_in_memory_file::truncate(offset); // truncate file auto const err = m_file->truncate(offset); - if (err != osd_file::error::NONE) + if (err) return err; // and adjust to new length and offset set_length(offset); - return osd_file::error::NONE; + return std::error_condition(); } @@ -933,7 +738,7 @@ osd_file::error core_osd_file::truncate(std::uint64_t offset) flush - flush file buffers -------------------------------------------------*/ -osd_file::error core_osd_file::flush() +std::error_condition core_osd_file::flush() { if (is_loaded()) return core_in_memory_file::flush(); @@ -947,116 +752,6 @@ osd_file::error core_osd_file::flush() return m_file->flush(); } - -/*------------------------------------------------- - osd_or_zlib_read - wrapper for osd_read that - handles zlib-compressed data --------------------------------------------------*/ - -osd_file::error core_osd_file::osd_or_zlib_read(void *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) -{ - // if no compression, just pass through - if (!m_zdata) - return m_file->read(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the destination - osd_file::error filerr = osd_file::error::NONE; - m_zdata->set_output(buffer, length); - while (!m_zdata->output_full()) - { - // if we didn't make progress, report an error or the end - if (m_zdata->has_input()) - { - auto const zerr = m_zdata->decompress(); - if (Z_OK != zerr) - { - if (Z_STREAM_END != zerr) filerr = osd_file::error::INVALID_DATA; - break; - } - } - - // fetch more data if needed - if (!m_zdata->has_input()) - { - std::uint32_t actualdata = 0; - filerr = m_file->read(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->buffer_size(), actualdata); - if (filerr != osd_file::error::NONE) break; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_input(actualdata); - if (!m_zdata->has_input()) break; - } - } - - // adjust everything - actual = length - m_zdata->output_space(); - m_zdata->add_nextoffset(actual); - return filerr; -} - - -/*------------------------------------------------- - osd_or_zlib_write - wrapper for osd_write that - handles zlib-compressed data --------------------------------------------------*/ - -/** - * @fn osd_file::error osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t actual) - * - * @brief OSD or zlib write. - * - * @param buffer The buffer. - * @param offset The offset. - * @param length The length. - * @param [in,out] actual The actual. - * - * @return A osd_file::error. - */ - -osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64_t offset, std::uint32_t length, std::uint32_t &actual) -{ - // if no compression, just pass through - if (!m_zdata) - return m_file->write(buffer, offset, length, actual); - - // if the offset doesn't match the next offset, fail - if (!m_zdata->is_nextoffset(offset)) - return osd_file::error::INVALID_ACCESS; - - // set up the source - m_zdata->set_input(buffer, length); - while (m_zdata->has_input()) - { - // if we didn't make progress, report an error or the end - auto const zerr = m_zdata->compress(); - if (zerr != Z_OK) - { - actual = length - m_zdata->input_size(); - m_zdata->add_nextoffset(actual); - return osd_file::error::INVALID_DATA; - } - - // write more data if we are full up - if (m_zdata->output_full()) - { - std::uint32_t actualdata = 0; - auto const filerr = m_file->write(m_zdata->buffer_data(), m_zdata->realoffset(), m_zdata->output_size(), actualdata); - if (filerr != osd_file::error::NONE) - return filerr; - m_zdata->add_realoffset(actualdata); - m_zdata->reset_output(); - } - } - - // we wrote everything - actual = length; - m_zdata->add_nextoffset(actual); - return osd_file::error::NONE; -} - } // anonymous namespace @@ -1070,23 +765,23 @@ osd_file::error core_osd_file::osd_or_zlib_write(void const *buffer, std::uint64 return an error code -------------------------------------------------*/ -osd_file::error core_file::open(std::string const &filename, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open(std::string_view filename, std::uint32_t openflags, ptr &file) { try { // attempt to open the file osd_file::ptr f; std::uint64_t length = 0; - auto const filerr = osd_file::open(filename, openflags, f, length); - if (filerr != osd_file::error::NONE) + auto const filerr = osd_file::open(std::string(filename), openflags, f, length); // FIXME: allow osd_file to accept std::string_view + if (filerr) return filerr; file = std::make_unique<core_osd_file>(openflags, std::move(f), length); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1096,20 +791,20 @@ osd_file::error core_file::open(std::string const &filename, std::uint32_t openf like access and return an error code -------------------------------------------------*/ -osd_file::error core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open_ram(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return osd_file::error::INVALID_ACCESS; + return std::errc::invalid_argument; try { file.reset(new core_in_memory_file(openflags, data, length, false)); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1120,24 +815,24 @@ osd_file::error core_file::open_ram(void const *data, std::size_t length, std::u error code -------------------------------------------------*/ -osd_file::error core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) +std::error_condition core_file::open_ram_copy(void const *data, std::size_t length, std::uint32_t openflags, ptr &file) { // can only do this for read access if ((openflags & OPEN_FLAG_WRITE) || (openflags & OPEN_FLAG_CREATE)) - return osd_file::error::INVALID_ACCESS; + return std::errc::invalid_argument; try { ptr result(new core_in_memory_file(openflags, data, length, true)); if (!result->buffer()) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; file = std::move(result); - return osd_file::error::NONE; + return std::error_condition(); } catch (...) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1147,17 +842,14 @@ osd_file::error core_file::open_ram_copy(void const *data, std::size_t length, s object and return an error code -------------------------------------------------*/ -osd_file::error core_file::open_proxy(core_file &file, ptr &proxy) +std::error_condition core_file::open_proxy(core_file &file, ptr &proxy) { - try - { - proxy = std::make_unique<core_proxy_file>(file); - return osd_file::error::NONE; - } - catch (...) - { - return osd_file::error::OUT_OF_MEMORY; - } + ptr result(new (std::nothrow) core_proxy_file(file)); + if (!result) + return std::errc::not_enough_memory; + + proxy = std::move(result); + return std::error_condition(); } @@ -1176,61 +868,64 @@ core_file::~core_file() pointer -------------------------------------------------*/ -osd_file::error core_file::load(std::string const &filename, void **data, std::uint32_t &length) +std::error_condition core_file::load(std::string_view filename, void **data, std::uint32_t &length) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != osd_file::error::NONE) + if (err) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::file_too_large; // allocate memory *data = malloc(size); + if (!*data) + return std::errc::not_enough_memory; length = std::uint32_t(size); // read the data if (file->read(*data, size) != size) { free(*data); - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code } // close the file and return data - return osd_file::error::NONE; + return std::error_condition(); } -osd_file::error core_file::load(std::string const &filename, std::vector<uint8_t> &data) +std::error_condition core_file::load(std::string_view filename, std::vector<uint8_t> &data) { ptr file; // attempt to open the file auto const err = open(filename, OPEN_FLAG_READ, file); - if (err != osd_file::error::NONE) + if (err) return err; // get the size auto const size = file->size(); if (std::uint32_t(size) != size) - return osd_file::error::OUT_OF_MEMORY; + return std::errc::file_too_large; // allocate memory - data.resize(size); + try { data.resize(size); } + catch (...) { return std::errc::not_enough_memory; } // read the data if (file->read(&data[0], size) != size) { data.clear(); - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: revisit this error code } // close the file and return data - return osd_file::error::NONE; + return std::error_condition(); } @@ -1256,7 +951,7 @@ core_file::core_file() // assumptions about path separators // ------------------------------------------------- -std::string_view core_filename_extract_base(std::string_view name, bool strip_extension) +std::string_view core_filename_extract_base(std::string_view name, bool strip_extension) noexcept { // find the start of the basename auto const start = std::find_if(name.rbegin(), name.rend(), &util::is_directory_separator); @@ -1279,7 +974,7 @@ std::string_view core_filename_extract_base(std::string_view name, bool strip_ex // core_filename_extract_extension // ------------------------------------------------- -std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period) +std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period) noexcept { auto loc = filename.find_last_of('.'); if (loc != std::string_view::npos) @@ -1294,7 +989,7 @@ std::string_view core_filename_extract_extension(std::string_view filename, bool // filename end with the specified extension? // ------------------------------------------------- -bool core_filename_ends_with(std::string_view filename, std::string_view extension) +bool core_filename_ends_with(std::string_view filename, std::string_view extension) noexcept { auto namelen = filename.length(); auto extlen = extension.length(); diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h index 6aaa8f609e1..19febbb46f1 100644 --- a/src/lib/util/corefile.h +++ b/src/lib/util/corefile.h @@ -12,14 +12,13 @@ #pragma once - #include "osdfile.h" #include "strformat.h" #include <cstdint> #include <memory> -#include <string> #include <string_view> +#include <system_error> namespace util { @@ -30,11 +29,6 @@ namespace util { #define OPEN_FLAG_NO_BOM 0x0100 /* don't output BOM */ -#define FCOMPRESS_NONE 0 /* no compression */ -#define FCOMPRESS_MIN 1 /* minimal compression */ -#define FCOMPRESS_MEDIUM 6 /* standard compression */ -#define FCOMPRESS_MAX 9 /* maximum compression */ - /*************************************************************************** TYPE DEFINITIONS @@ -49,23 +43,20 @@ public: // ----- file open/close ----- // open a file with the specified filename - static osd_file::error open(std::string const &filename, std::uint32_t openflags, ptr &file); + static std::error_condition open(std::string_view filename, std::uint32_t openflags, ptr &file); // open a RAM-based "file" using the given data and length (read-only) - static osd_file::error open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); + static std::error_condition open_ram(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); // open a RAM-based "file" using the given data and length (read-only), copying the data - static osd_file::error open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); + static std::error_condition open_ram_copy(const void *data, std::size_t length, std::uint32_t openflags, ptr &file); // open a proxy "file" that forwards requests to another file object - static osd_file::error open_proxy(core_file &file, ptr &proxy); + static std::error_condition open_proxy(core_file &file, ptr &proxy); // close an open file virtual ~core_file(); - // enable/disable streaming file compression via zlib; level is 0 to disable compression, or up to 9 for max compression - virtual osd_file::error compress(int level) = 0; - // ----- file positioning ----- @@ -101,8 +92,8 @@ public: virtual const void *buffer() = 0; // open a file with the specified filename, read it into memory, and return a pointer - static osd_file::error load(std::string const &filename, void **data, std::uint32_t &length); - static osd_file::error load(std::string const &filename, std::vector<uint8_t> &data); + static std::error_condition load(std::string_view filename, void **data, std::uint32_t &length); + static std::error_condition load(std::string_view filename, std::vector<uint8_t> &data); // ----- file write ----- @@ -121,32 +112,16 @@ public: } // file truncation - virtual osd_file::error truncate(std::uint64_t offset) = 0; + virtual std::error_condition truncate(std::uint64_t offset) = 0; // flush file buffers - virtual osd_file::error flush() = 0; + virtual std::error_condition flush() = 0; protected: core_file(); }; - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -// is a given character a directory separator? - -constexpr bool is_directory_separator(char c) -{ -#if defined(WIN32) - return ('\\' == c) || ('/' == c) || (':' == c); -#else - return '/' == c; -#endif -} - } // namespace util @@ -157,13 +132,13 @@ constexpr bool is_directory_separator(char c) /* ----- filename utilities ----- */ // extract the base part of a filename (remove extensions and paths) -std::string_view core_filename_extract_base(std::string_view name, bool strip_extension = false); +std::string_view core_filename_extract_base(std::string_view name, bool strip_extension = false) noexcept; // extracts the file extension from a filename -std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period = false); +std::string_view core_filename_extract_extension(std::string_view filename, bool strip_period = false) noexcept; // true if the given filename ends with a particular extension -bool core_filename_ends_with(std::string_view filename, std::string_view extension); +bool core_filename_ends_with(std::string_view filename, std::string_view extension) noexcept; #endif // MAME_LIB_UTIL_COREFILE_H diff --git a/src/lib/util/harddisk.cpp b/src/lib/util/harddisk.cpp index e91759c44f2..2e8d14f9a8a 100644 --- a/src/lib/util/harddisk.cpp +++ b/src/lib/util/harddisk.cpp @@ -8,11 +8,13 @@ ***************************************************************************/ -#include <cassert> #include "harddisk.h" -#include "osdcore.h" + +#include "corefile.h" + #include <cstdlib> + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -40,7 +42,7 @@ hard_disk_file *hard_disk_open(chd_file *chd) int cylinders, heads, sectors, sectorbytes; hard_disk_file *file; std::string metadata; - chd_error err; + std::error_condition err; /* punt if no CHD */ if (chd == nullptr) @@ -48,7 +50,7 @@ hard_disk_file *hard_disk_open(chd_file *chd) /* read the hard disk metadata */ err = chd->read_metadata(HARD_DISK_METADATA_TAG, 0, metadata); - if (err != CHDERR_NONE) + if (err) return nullptr; /* parse the metadata */ @@ -177,8 +179,8 @@ uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer) { if (file->chd) { - chd_error err = file->chd->read_units(lbasector, buffer); - return (err == CHDERR_NONE); + std::error_condition err = file->chd->read_units(lbasector, buffer); + return !err; } else { @@ -211,8 +213,8 @@ uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *b { if (file->chd) { - chd_error err = file->chd->write_units(lbasector, buffer); - return (err == CHDERR_NONE); + std::error_condition err = file->chd->write_units(lbasector, buffer); + return !err; } else { diff --git a/src/lib/util/harddisk.h b/src/lib/util/harddisk.h index 2a4b727e7e7..e6fae165f19 100644 --- a/src/lib/util/harddisk.h +++ b/src/lib/util/harddisk.h @@ -8,13 +8,15 @@ ***************************************************************************/ -#ifndef MAME_UTIL_HARDDISK_H -#define MAME_UTIL_HARDDISK_H +#ifndef MAME_LIB_UTIL_HARDDISK_H +#define MAME_LIB_UTIL_HARDDISK_H #pragma once -#include "osdcore.h" #include "chd.h" +#include "utilfwd.h" + +#include "osdcore.h" /*************************************************************************** @@ -49,4 +51,4 @@ hard_disk_info *hard_disk_get_info(hard_disk_file *file); uint32_t hard_disk_read(hard_disk_file *file, uint32_t lbasector, void *buffer); uint32_t hard_disk_write(hard_disk_file *file, uint32_t lbasector, const void *buffer); -#endif // MAME_UTIL_HARDDISK_H +#endif // MAME_LIB_UTIL_HARDDISK_H diff --git a/src/lib/util/ioprocs.cpp b/src/lib/util/ioprocs.cpp new file mode 100644 index 00000000000..1f52a3daf97 --- /dev/null +++ b/src/lib/util/ioprocs.cpp @@ -0,0 +1,1248 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocs.h + + I/O interfaces + +***************************************************************************/ + +#include "ioprocs.h" + +#include "corefile.h" +#include "ioprocsfill.h" + +#include "osdfile.h" + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstring> +#include <iterator> +#include <limits> +#include <type_traits> + + +namespace util { + +namespace { + +// helper for holding a block of memory and deallocating it (or not) as necessary + +template <typename T, bool Owned> +class ram_adapter_base : public virtual random_access +{ +public: + virtual ~ram_adapter_base() + { + if constexpr (Owned) + std::free(m_data); + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + switch (whence) + { + case SEEK_SET: + if (0 > offset) + return std::errc::invalid_argument; + m_pointer = std::uint64_t(offset); + return std::error_condition(); + + case SEEK_CUR: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_pointer) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer) + { + return std::errc::invalid_argument; + } + m_pointer += offset; + return std::error_condition(); + + case SEEK_END: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_size) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size) + { + return std::errc::invalid_argument; + } + m_pointer = std::uint64_t(m_size) + offset; + return std::error_condition(); + + default: + return std::errc::invalid_argument; + } + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_pointer; + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + if (std::numeric_limits<uint64_t>::max() < m_size) + return std::errc::file_too_large; + + result = m_size; + return std::error_condition(); + } + +protected: + template <typename U> + ram_adapter_base(U *data, std::size_t size) noexcept : m_data(reinterpret_cast<T>(data)), m_size(size) + { + static_assert(sizeof(*m_data) == 1U, "Element type must be byte-sized"); + assert(m_data || !m_size); + } + + T m_data; + std::uint64_t m_pointer = 0U; + std::size_t m_size; +}; + + +// RAM read implementation + +template <typename T, bool Owned> +class ram_read_adapter : public ram_adapter_base<T, Owned>, public virtual random_read +{ +public: + template <typename U> + ram_read_adapter(U *data, std::size_t size) noexcept : ram_adapter_base<T, Owned>(data, size) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(this->m_pointer, buffer, length, actual); + this->m_pointer += actual; + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(offset, buffer, length, actual); + return std::error_condition(); + } + +private: + void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) const noexcept + { + if ((offset < this->m_size) && length) + { + actual = std::min(std::size_t(this->m_size - offset), length); + if constexpr (Owned) + std::memcpy(buffer, this->m_data + offset, actual); + else + std::memmove(buffer, this->m_data + offset, actual); + } + else + { + actual = 0U; + } + } +}; + + +// helper for holding a stdio FILE and closing it (or not) as necessary + +class stdio_adapter_base : public virtual random_access +{ +public: + virtual ~stdio_adapter_base() + { + if (m_close) + std::fclose(m_file); + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + if ((std::numeric_limits<long>::max() < offset) || (std::numeric_limits<long>::min() > offset)) + return std::errc::invalid_argument; + else if (!std::fseek(m_file, long(offset), whence)) + return std::error_condition(); + else + return std::error_condition(errno, std::generic_category()); + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + long const pos = std::ftell(m_file); + if (0 > pos) + return std::error_condition(errno, std::generic_category()); + result = static_cast<unsigned long>(pos); + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + std::fpos_t oldpos; + if (std::fgetpos(m_file, &oldpos)) + return std::error_condition(errno, std::generic_category()); + + long endpos = -1; + if (!std::fseek(m_file, 0, SEEK_END)) + { + m_dangling_read = m_dangling_write = false; + endpos = std::ftell(m_file); + } + std::error_condition err; + if (0 > endpos) + err.assign(errno, std::generic_category()); + else if (std::numeric_limits<std::uint64_t>::max() < static_cast<unsigned long>(endpos)) + err = std::errc::file_too_large; + else + result = static_cast<unsigned long>(endpos); + + if (!std::fsetpos(m_file, &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } + +protected: + stdio_adapter_base(FILE *file, bool close) noexcept : m_file(file), m_close(close) + { + assert(m_file); + } + + FILE *file() noexcept + { + return m_file; + } + +private: + FILE *const m_file; + bool const m_close; + +protected: + bool m_dangling_read = true, m_dangling_write = true; +}; + + +// stdio read implementation + +class stdio_read_adapter : public stdio_adapter_base, public virtual random_read +{ +public: + stdio_read_adapter(FILE *file, bool close) noexcept : stdio_adapter_base(file, close) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + if (m_dangling_write) + { + if (std::fflush(file())) + { + std::clearerr(file()); + actual = 0U; + return std::error_condition(errno, std::generic_category()); + } + m_dangling_write = false; + } + + actual = std::fread(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_read = true; + if (length != actual) + { + if (std::ferror(file())) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + else if (std::feof(file())) + { + m_dangling_read = false; + } + } + + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_write = false; + actual = std::fread(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_read = true; + if (length != actual) + { + if (std::ferror(file())) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + else if (std::feof(file())) + { + m_dangling_read = false; + } + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// stdio read/write implementation + +class stdio_read_write_adapter : public stdio_read_adapter, public random_read_write +{ +public: + using stdio_read_adapter::stdio_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + if (std::fflush(file())) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + m_dangling_write = false; + return std::error_condition(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + if (m_dangling_read) + { + if (std::fseek(file(), 0, SEEK_CUR)) + { + actual = 0U; + return std::error_condition(errno, std::generic_category()); + } + m_dangling_read = false; + } + + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_read = false; + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// stdio helper that fills space when writing past the end-of-file + +class stdio_read_write_filler : public random_read_fill_wrapper<stdio_read_write_adapter> +{ +public: + stdio_read_write_filler(FILE *file, bool close, std::uint8_t fill) noexcept : random_read_fill_wrapper<stdio_read_write_adapter>(file, close) + { + set_filler(fill); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + long const offset = std::ftell(file()); + if (0 > offset) + return std::error_condition(errno, std::generic_category()); + + if (std::fseek(file(), 0, SEEK_END)) + { + std::error_condition err(errno, std::generic_category()); + if (!std::fseek(file(), offset, SEEK_SET)) + m_dangling_read = m_dangling_write = false; + return err; + } + m_dangling_read = m_dangling_write = false; + long endpos = std::ftell(file()); + if (0 > endpos) + { + std::error_condition err(errno, std::generic_category()); + std::fseek(file(), offset, SEEK_SET); + return err; + } + + if (offset > endpos) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos); + std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file()); + endpos += filled; + m_dangling_write = true; + if (chunk != filled) + { + std::error_condition err(errno, std::generic_category()); + std::clearerr(file()); + if (!std::fseek(file(), offset, SEEK_SET)) + m_dangling_write = false; + return err; + } + } + while (static_cast<unsigned long>(endpos) < offset); + } + else if ((offset < endpos) && std::fseek(file(), offset, SEEK_SET)) + { + return std::error_condition(errno, std::generic_category()); + } + + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + std::clearerr(file()); + return std::error_condition(errno, std::generic_category()); + } + + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + if (static_cast<unsigned long>(std::numeric_limits<long>::max()) < offset) + return std::errc::invalid_argument; + + std::fpos_t oldpos; + if (std::fgetpos(file(), &oldpos)) + return std::error_condition(errno, std::generic_category()); + + std::error_condition err; + if (std::fseek(file(), 0, SEEK_END)) + { + err.assign(errno, std::generic_category()); + } + else + { + m_dangling_read = m_dangling_write = false; + long endpos = std::ftell(file()); + if (0 > endpos) + { + err.assign(errno, std::generic_category()); + } + else if (static_cast<unsigned long>(endpos) > offset) + { + if (std::fseek(file(), long(static_cast<unsigned long>(offset)), SEEK_SET)) + err.assign(errno, std::generic_category()); + } + else if (static_cast<unsigned long>(endpos) < offset) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t, long> >(std::size(block), offset - endpos); + std::size_t const filled = std::fwrite(block, sizeof(block[0]), chunk, file()); + endpos += filled; + m_dangling_write = true; + if (chunk != filled) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + while (!err && (static_cast<unsigned long>(endpos) < offset)); + } + } + + if (!err) + { + actual = std::fwrite(buffer, sizeof(std::uint8_t), length, file()); + m_dangling_write = true; + if (length != actual) + { + err.assign(errno, std::generic_category()); + std::clearerr(file()); + } + } + + if (!std::fsetpos(file(), &oldpos)) + m_dangling_read = m_dangling_write = false; + else if (!err) + err.assign(errno, std::generic_category()); + + return err; + } +}; + + +// helper class for holding an osd_file and closing it (or not) as necessary + +class osd_file_adapter_base : public virtual random_access +{ +public: + virtual ~osd_file_adapter_base() + { + if (m_close) + delete m_file; + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + switch (whence) + { + case SEEK_SET: + if (0 > offset) + return std::errc::invalid_argument; + m_pointer = std::uint64_t(offset); + return std::error_condition(); + + case SEEK_CUR: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_pointer) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer) + { + return std::errc::invalid_argument; + } + m_pointer += offset; + return std::error_condition(); + + // TODO: add SEEK_END when osd_file can support it - should it return a different error? + default: + return std::errc::invalid_argument; + } + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_pointer; + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + // not supported by osd_file + return std::errc::not_supported; // TODO: revisit this error code + } + +protected: + osd_file_adapter_base(osd_file::ptr &&file) noexcept : m_file(file.release()), m_close(true) + { + assert(m_file); + } + + osd_file_adapter_base(osd_file &file) noexcept : m_file(&file), m_close(false) + { + } + + osd_file &file() noexcept + { + return *m_file; + } + + std::uint64_t m_pointer = 0U; + +private: + osd_file *const m_file; + bool const m_close; +}; + + +// osd_file read implementation + +class osd_file_read_adapter : public osd_file_adapter_base, public virtual random_read +{ +public: + osd_file_read_adapter(osd_file::ptr &&file) noexcept : osd_file_adapter_base(std::move(file)) + { + } + + osd_file_read_adapter(osd_file &file) noexcept : osd_file_adapter_base(file) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // actual length not valid on error + std::uint32_t count; + std::error_condition err = file().read(buffer, m_pointer, std::uint32_t(length), count); + if (!err) + { + m_pointer += count; + actual = std::size_t(count); + } + else + { + actual = 0U; + } + return err; + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // actual length not valid on error + std::uint32_t count; + std::error_condition err = file().read(buffer, offset, std::uint32_t(length), count); + if (!err) + actual = std::size_t(count); + else + actual = 0U; + return err; + } +}; + + +// osd_file read/write implementation + +class osd_file_read_write_adapter : public osd_file_read_adapter, public random_read_write +{ +public: + using osd_file_read_adapter::osd_file_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return file().flush(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + // actual length not valid on error + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t written; + std::error_condition err = file().write(buffer, m_pointer, chunk, written); + if (err) + return err; + m_pointer += written; + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + actual += written; + } + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + // actual length not valid on error + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t written; + std::error_condition err = file().write(buffer, offset, chunk, written); + if (err) + return err; + offset += written; + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + actual += written; + } + return std::error_condition(); + } +}; + + +// helper class for holding a core_file and closing it (or not) as necessary + +class core_file_adapter_base : public virtual random_access +{ +public: + virtual ~core_file_adapter_base() + { + if (m_close) + delete m_file; + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + if (!m_file->seek(offset, whence)) + return std::error_condition(); + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + // no error reporting + result = m_file->tell(); + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + // no error reporting + result = m_file->size(); + return std::error_condition(); + } + +protected: + core_file_adapter_base(core_file::ptr &&file) noexcept : m_file(file.release()), m_close(true) + { + assert(m_file); + } + + core_file_adapter_base(core_file &file) noexcept : m_file(&file), m_close(false) + { + } + + core_file &file() noexcept + { + return *m_file; + } + +private: + core_file *const m_file; + bool const m_close; +}; + + +// core_file read implementation + +class core_file_read_adapter : public core_file_adapter_base, public virtual random_read +{ +public: + core_file_read_adapter(core_file::ptr &&file) noexcept : core_file_adapter_base(std::move(file)) + { + } + + core_file_read_adapter(core_file &file) noexcept : core_file_adapter_base(file) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + { + actual = 0U; + return std::errc::invalid_argument; + } + + // no way to distinguish between EOF and error + actual = file().read(buffer, std::uint32_t(length)); + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // TODO: should the client have to deal with reading less than expected even if EOF isn't hit? + if (std::numeric_limits<std::uint32_t>::max() < length) + return std::errc::invalid_argument; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(offset, SEEK_SET)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + // no way to distinguish between EOF and error + actual = file().read(buffer, std::uint32_t(length)); + + if (!file().seek(oldpos, SEEK_SET)) + return std::error_condition(); + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + + +// core_file read/write implementation + +class core_file_read_write_adapter : public core_file_read_adapter, public random_read_write +{ +public: + using core_file_read_adapter::core_file_read_adapter; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return file().flush(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + while (length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + return std::errc::io_error; // TODO: better error reporting for core_file + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + return std::error_condition(); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(offset, SEEK_SET)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + + std::error_condition err; + while (!err && length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + { + err = std::errc::io_error; // TODO: better error reporting for core_file + } + else + { + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + } + + if (!file().seek(oldpos, SEEK_SET) || err) + return err; + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + + +// core_file helper that fills space when writing past the end-of-file + +class core_file_read_write_filler : public random_read_fill_wrapper<core_file_read_write_adapter> +{ +public: + core_file_read_write_filler(core_file::ptr &&file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(std::move(file)) + { + set_filler(fill); + } + + core_file_read_write_filler(core_file &file, std::uint8_t fill) noexcept : random_read_fill_wrapper<core_file_read_write_adapter>(file) + { + set_filler(fill); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + // no error reporting for tell or size + std::uint64_t const offset = file().tell(); + std::uint64_t endpos = file().size(); + if (endpos < offset) + { + if (file().seek(endpos, SEEK_SET)) + { + file().seek(offset, SEEK_SET); + actual = 0U; + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos); + std::uint32_t const filled = file().write(block, chunk); + endpos += filled; + if (chunk != filled) + { + file().seek(offset, SEEK_SET); + actual = 0U; + return std::errc::io_error; // TODO: better error reporting for core_file + } + } + while (endpos < offset); + } + + return core_file_read_write_adapter::write(buffer, length, actual); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = 0U; + + // no error reporting for tell + std::uint64_t const oldpos = file().tell(); + if (file().seek(0, SEEK_END)) + { + file().seek(oldpos, SEEK_SET); + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } + std::uint64_t endpos = file().tell(); + + std::error_condition err; + if (endpos > offset) + { + if (file().seek(offset, SEEK_SET)) + err = std::errc::invalid_argument; // TODO: better error reporting for core_file + } + else if (endpos < offset) + { + std::uint8_t block[1024]; + std::fill_n( + block, + std::min<std::common_type_t<std::size_t, std::uint64_t> >(std::size(block), offset - endpos), + get_filler()); + do + { + std::uint32_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(sizeof(block), offset - endpos); + std::uint32_t const filled = file().write(block, chunk); + endpos += filled; + if (chunk != filled) + err = std::errc::io_error; // TODO: better error reporting for core_file + } + while (!err && (endpos < offset)); + } + + while (!err && length) + { + std::uint32_t const chunk = std::min<std::common_type_t<std::uint32_t, std::size_t> >(std::numeric_limits<std::uint32_t>::max(), length); + std::uint32_t const written = file().write(buffer, chunk); + actual += written; + if (written < chunk) + { + err = std::errc::io_error; // TODO: better error reporting for core_file + } + else + { + buffer = reinterpret_cast<std::uint8_t const *>(buffer) + written; + length -= written; + } + } + + if (!file().seek(oldpos, SEEK_SET) || err) + return err; + else + return std::errc::invalid_argument; // TODO: better error reporting for core_file + } +}; + +} // anonymous namespace + + +// creating RAM read adapters + +random_read::ptr ram_read(void const *data, std::size_t size) noexcept +{ + random_read::ptr result; + if (data || !size) + result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t const *const, false>(data, size)); + return result; +} + +random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t const *const, false> > > result; + if (data || !size) + result.reset(new (std::nothrow) decltype(result)::element_type(data, size)); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept +{ + random_read::ptr result; + void *const copy = size ? std::malloc(size) : nullptr; + if (copy) + std::memcpy(copy, data, size); + if (copy || !size) + result.reset(new (std::nothrow) ram_read_adapter<std::uint8_t *const, true>(copy, size)); + if (!result) + std::free(copy); + return result; +} + +random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<ram_read_adapter<std::uint8_t *const, true> > > result; + void *const copy = size ? std::malloc(size) : nullptr; + if (copy) + std::memcpy(copy, data, size); + if (copy || !size) + result.reset(new (std::nothrow) decltype(result)::element_type(copy, size)); + if (!result) + std::free(copy); + return result; +} + + +// creating stdio read adapters + +random_read::ptr stdio_read(FILE *file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_adapter(file, true)); + return result; +} + +random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(file, true)); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr stdio_read_noclose(FILE *file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_adapter(file, false)); + return result; +} + +random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<stdio_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(file, false)); + if (result) + result->set_filler(filler); + return result; +} + + +// creating stdio read/write adapters + +random_read_write::ptr stdio_read_write(FILE *file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_adapter(file, true)); + return result; +} + +random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_filler(file, true, filler)); + return result; +} + +random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_adapter(file, false)); + return result; +} + +random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) stdio_read_write_filler(file, false, filler)); + return result; +} + + +// creating osd_file read adapters + +random_read::ptr osd_file_read(osd_file::ptr &&file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) osd_file_read_adapter(std::move(file))); + return result; +} + +random_read::ptr osd_file_read(osd_file &file) noexcept +{ + return random_read::ptr(new (std::nothrow) osd_file_read_adapter(file)); +} + + +// creating osd_file read/write adapters + +random_read_write::ptr osd_file_read_write(osd_file::ptr &&file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) osd_file_read_write_adapter(std::move(file))); + return result; +} + +random_read_write::ptr osd_file_read_write(osd_file &file) noexcept +{ + return random_read_write::ptr(new (std::nothrow) osd_file_read_write_adapter(file)); +} + + +// creating core_file read adapters + +random_read::ptr core_file_read(core_file::ptr &&file) noexcept +{ + random_read::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_adapter(std::move(file))); + return result; +} + +random_read::ptr core_file_read(core_file::ptr &&file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result; + if (file) + result.reset(new (std::nothrow) decltype(result)::element_type(std::move(file))); + if (result) + result->set_filler(filler); + return result; +} + +random_read::ptr core_file_read(core_file &file) noexcept +{ + return random_read::ptr(new (std::nothrow) core_file_read_adapter(file)); +} + +random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept +{ + std::unique_ptr<random_read_fill_wrapper<core_file_read_adapter> > result; + result.reset(new (std::nothrow) decltype(result)::element_type(file)); + if (result) + result->set_filler(filler); + return result; +} + + +// creating core_file read/write adapters + +random_read_write::ptr core_file_read_write(core_file::ptr &&file) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_write_adapter(std::move(file))); + return result; +} + +random_read_write::ptr core_file_read_write(core_file::ptr &&file, std::uint8_t filler) noexcept +{ + random_read_write::ptr result; + if (file) + result.reset(new (std::nothrow) core_file_read_write_filler(std::move(file), filler)); + return result; +} + +random_read_write::ptr core_file_read_write(core_file &file) noexcept +{ + return random_read_write::ptr(new (std::nothrow) core_file_read_write_adapter(file)); +} + +random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept +{ + return random_read_write::ptr(new (std::nothrow) core_file_read_write_filler(file, filler)); +} + +} // namespace util diff --git a/src/lib/util/ioprocs.h b/src/lib/util/ioprocs.h new file mode 100644 index 00000000000..78c06e71318 --- /dev/null +++ b/src/lib/util/ioprocs.h @@ -0,0 +1,130 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocs.h + + I/O interfaces + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_IOPROCS_H +#define MAME_LIB_UTIL_IOPROCS_H + +#pragma once + +#include "utilfwd.h" + +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <system_error> + + +// FIXME: make a proper place for OSD forward declarations +class osd_file; + + +namespace util { + +class read_stream +{ +public: + using ptr = std::unique_ptr<read_stream>; + + virtual ~read_stream() = default; + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept = 0; +}; + + +class write_stream +{ +public: + using ptr = std::unique_ptr<write_stream>; + + virtual ~write_stream() = default; + + virtual std::error_condition finalize() noexcept = 0; + virtual std::error_condition flush() noexcept = 0; + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0; +}; + + +class read_write_stream : public virtual read_stream, public virtual write_stream +{ +public: + using ptr = std::unique_ptr<read_write_stream>; +}; + + +class random_access +{ +public: + virtual ~random_access() = default; + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept = 0; + virtual std::error_condition tell(std::uint64_t &result) noexcept = 0; + virtual std::error_condition length(std::uint64_t &result) noexcept = 0; +}; + + +class random_read : public virtual read_stream, public virtual random_access +{ +public: + using ptr = std::unique_ptr<random_read>; + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept = 0; +}; + + +class random_write : public virtual write_stream, public virtual random_access +{ +public: + using ptr = std::unique_ptr<random_write>; + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept = 0; +}; + + +class random_read_write : public read_write_stream, public virtual random_read, public virtual random_write +{ +public: + using ptr = std::unique_ptr<random_read_write>; +}; + + +random_read::ptr ram_read(void const *data, std::size_t size) noexcept; +random_read::ptr ram_read(void const *data, std::size_t size, std::uint8_t filler) noexcept; +random_read::ptr ram_read_copy(void const *data, std::size_t size) noexcept; +random_read::ptr ram_read_copy(void const *data, std::size_t size, std::uint8_t filler) noexcept; + +random_read::ptr stdio_read(FILE *file) noexcept; +random_read::ptr stdio_read(FILE *file, std::uint8_t filler) noexcept; +random_read::ptr stdio_read_noclose(FILE *file) noexcept; +random_read::ptr stdio_read_noclose(FILE *file, std::uint8_t filler) noexcept; + +random_read_write::ptr stdio_read_write(FILE *file) noexcept; +random_read_write::ptr stdio_read_write(FILE *file, std::uint8_t filler) noexcept; +random_read_write::ptr stdio_read_write_noclose(FILE *file) noexcept; +random_read_write::ptr stdio_read_write_noclose(FILE *file, std::uint8_t filler) noexcept; + +random_read::ptr osd_file_read(std::unique_ptr<osd_file> &&file) noexcept; +random_read::ptr osd_file_read(osd_file &file) noexcept; + +random_read_write::ptr osd_file_read_write(std::unique_ptr<osd_file> &&file) noexcept; +random_read_write::ptr osd_file_read_write(osd_file &file) noexcept; + +random_read::ptr core_file_read(std::unique_ptr<core_file> &&file) noexcept; +random_read::ptr core_file_read(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept; +random_read::ptr core_file_read(core_file &file) noexcept; +random_read::ptr core_file_read(core_file &file, std::uint8_t filler) noexcept; + +random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file) noexcept; +random_read_write::ptr core_file_read_write(std::unique_ptr<core_file> &&file, std::uint8_t filler) noexcept; +random_read_write::ptr core_file_read_write(core_file &file) noexcept; +random_read_write::ptr core_file_read_write(core_file &file, std::uint8_t filler) noexcept; + +} // namespace util + +#endif // MAME_LIB_UTIL_IOPROCS_H diff --git a/src/lib/util/ioprocsfill.h b/src/lib/util/ioprocsfill.h new file mode 100644 index 00000000000..e49b10687f0 --- /dev/null +++ b/src/lib/util/ioprocsfill.h @@ -0,0 +1,163 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocsfill.h + + Wrappers that automatically fill extra space + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_IOPROCSFILL_H +#define MAME_LIB_UTIL_IOPROCSFILL_H + +#pragma once + +#include <algorithm> +#include <cassert> +#include <cstdint> +#include <system_error> + + +namespace util { + +template <std::uint8_t DefaultFiller> +class fill_wrapper_base +{ +public: + std::uint8_t get_filler() const noexcept + { + return m_filler; + } + + void set_filler(std::uint8_t filler) noexcept + { + m_filler = filler; + } + +private: + std::uint8_t m_filler = DefaultFiller; +}; + + +template <typename Base, std::uint8_t DefaultFiller = 0U> +class read_stream_fill_wrapper : public Base, public virtual fill_wrapper_base<DefaultFiller> +{ +public: + using Base::Base; + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err = Base::read(buffer, length, actual); + assert(length >= actual); + std::fill( + reinterpret_cast<std::uint8_t *>(buffer) + actual, + reinterpret_cast<std::uint8_t *>(buffer) + length, + fill_wrapper_base<DefaultFiller>::get_filler()); + return err; + } +}; + + +template <typename Base, std::uint8_t DefaultFiller = 0U> +class random_read_fill_wrapper : public read_stream_fill_wrapper<Base, DefaultFiller> +{ +public: + using read_stream_fill_wrapper<Base, DefaultFiller>::read_stream_fill_wrapper; + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err = Base::read_at(offset, buffer, length, actual); + assert(length >= actual); + std::fill( + reinterpret_cast<std::uint8_t *>(buffer) + actual, + reinterpret_cast<std::uint8_t *>(buffer) + length, + fill_wrapper_base<DefaultFiller>::get_filler()); + return err; + } +}; + + +template <typename Base, std::uint8_t DefaultFiller = 0U, std::size_t FillBlock = 1024U> +class random_write_fill_wrapper : public Base, public virtual fill_wrapper_base<DefaultFiller> +{ +public: + using Base::Base; + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err; + actual = 0U; + + std::uint64_t offset; + std::uint64_t current; + err = Base::tell(offset); + if (err) + return err; + err = Base::length(current); + if (err) + return err; + + if (current < offset) + { + std::uint64_t unfilled = offset - current; + std::uint8_t fill_buffer[FillBlock]; + std::fill( + fill_buffer, + fill_buffer + std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled), + fill_wrapper_base<DefaultFiller>::get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled); + err = Base::write_at(current, fill_buffer, chunk, actual); + if (err) + { + actual = 0U; + return err; + } + current += chunk; + unfilled -= chunk; + } + while (unfilled); + } + + return Base::write(buffer, length, actual); + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err; + std::uint64_t current; + err = Base::length(current); + if (!err && (current < offset)) + { + std::uint64_t unfilled = offset - current; + std::uint8_t fill_buffer[FillBlock]; + std::fill( + fill_buffer, + fill_buffer + std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled), + fill_wrapper_base<DefaultFiller>::get_filler()); + do + { + std::size_t const chunk = std::min<std::common_type_t<std::size_t, std::uint64_t> >(FillBlock, unfilled); + err = Base::write_at(current, fill_buffer, chunk, actual); + current += chunk; + unfilled -= chunk; + } + while (unfilled && !err); + } + if (err) + { + actual = 0U; + return err; + } + return Base::write_at(offset, buffer, length, actual); + } +}; + + +template <typename Base, std::uint8_t DefaultFiller = 0U, std::size_t FillBlock = 1024U> +using random_read_write_fill_wrapper = random_write_fill_wrapper<random_read_fill_wrapper<Base, DefaultFiller>, DefaultFiller, FillBlock>; + +} // namespace util + +#endif // MAME_LIB_UTIL_IOPROCSFILL_H diff --git a/src/lib/util/ioprocsfilter.cpp b/src/lib/util/ioprocsfilter.cpp new file mode 100644 index 00000000000..da67b8c41f2 --- /dev/null +++ b/src/lib/util/ioprocsfilter.cpp @@ -0,0 +1,598 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocs.h + + I/O filters + +***************************************************************************/ + +#include "ioprocsfilter.h" + +#include "ioprocs.h" + +#include <zlib.h> + +#include <algorithm> +#include <cassert> +#include <cerrno> +#include <cstdint> +#include <limits> +#include <system_error> +#include <type_traits> +#include <utility> + + +namespace util { + +namespace { + +// helper class for holding zlib data + +class zlib_data +{ +protected: + zlib_data(std::size_t buffer_size) noexcept : + m_buffer_size(std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), buffer_size)) + { + assert(buffer_size); + } + + z_stream &get_z_stream() noexcept { return m_z_stream; } + z_stream const &get_z_stream() const noexcept { return m_z_stream; } + Bytef *get_buffer() noexcept { return m_buffer.get(); } + Bytef const *get_buffer() const noexcept { return m_buffer.get(); } + std::size_t get_buffer_size() const noexcept { return m_buffer_size; } + + bool input_empty() const noexcept { return !m_z_stream.avail_in; } + bool output_available() const noexcept { return bool(m_z_stream.avail_out); } + + void initialize_z_stream() noexcept + { + m_z_stream.zalloc = Z_NULL; + m_z_stream.zfree = Z_NULL; + m_z_stream.opaque = Z_NULL; + } + + std::error_condition allocate_buffer() noexcept + { + if (!m_buffer) + m_buffer.reset(new (std::nothrow) Bytef [m_buffer_size]); + return m_buffer ? std::error_condition() : std::errc::not_enough_memory; + } + + void release_buffer() noexcept + { + m_buffer.reset(); + } + + static std::error_condition convert_z_error(int err) noexcept + { + switch (err) + { + case Z_OK: + case Z_STREAM_END: + return std::error_condition(); + case Z_NEED_DICT: + return std::errc::invalid_argument; + case Z_ERRNO: + return std::error_condition(errno, std::generic_category()); + case Z_STREAM_ERROR: + return std::errc::invalid_argument; + case Z_DATA_ERROR: + return std::errc::invalid_argument; // TODO: revisit this error code + case Z_MEM_ERROR: + return std::errc::not_enough_memory; + case Z_BUF_ERROR: + return std::errc::operation_would_block; // TODO: revisit this error code (should be handled internally) + case Z_VERSION_ERROR: + return std::errc::invalid_argument; // TODO: revisit this error code (library ABI mismatch) + default: + return std::errc::io_error; // TODO: better default error code + } + } + +private: + z_stream m_z_stream; + std::unique_ptr<Bytef []> m_buffer; + std::size_t const m_buffer_size; +}; + + +// helper class for decompressing deflated data + +class inflate_data : private zlib_data +{ +protected: + using zlib_data::zlib_data; + + ~inflate_data() + { + if (m_inflate_initialized) + inflateEnd(&get_z_stream()); + } + + using zlib_data::input_empty; + + bool stream_initialized() const noexcept + { + return m_inflate_initialized; + } + + std::error_condition initialize_z_stream() noexcept + { + if (m_inflate_initialized) + return std::errc::invalid_argument; + std::error_condition err = allocate_buffer(); + if (err) + return err; + zlib_data::initialize_z_stream(); + int const zerr = inflateInit(&get_z_stream()); + if (Z_OK == zerr) + m_inflate_initialized = true; + return convert_z_error(zerr); + } + + std::error_condition close_z_stream() noexcept + { + if (!m_inflate_initialized) + return std::errc::invalid_argument; + int const zerr = inflateEnd(&get_z_stream()); + m_inflate_initialized = false; + return convert_z_error(zerr); + } + + std::error_condition decompress_some(bool &stream_end) noexcept + { + assert(m_inflate_initialized); + int const zerr = inflate(&get_z_stream(), Z_SYNC_FLUSH); + if (!get_z_stream().avail_in) + reset_input(); + stream_end = Z_STREAM_END == zerr; + return convert_z_error(zerr); + } + + std::size_t input_available() const noexcept + { + return std::size_t(get_z_stream().avail_in); + } + + void reset_input() noexcept + { + get_z_stream().next_in = get_buffer(); + get_z_stream().avail_in = 0U; + } + + std::pair<void *, std::size_t> get_unfilled_input() noexcept + { + assert(get_buffer()); + Bytef *const base = get_z_stream().next_in + get_z_stream().avail_in; + return std::make_pair(base, get_buffer() + get_buffer_size() - base); + } + + void add_input(std::size_t length) noexcept + { + assert(get_buffer()); + get_z_stream().avail_in += length; + assert((get_buffer() + get_buffer_size()) >= (get_z_stream().next_in + get_z_stream().avail_in)); + } + + std::size_t output_produced() const noexcept + { + return m_output_max - get_z_stream().avail_out; + } + + void set_output(void *buffer, std::size_t length) noexcept + { + m_output_max = std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), length); + get_z_stream().next_out = reinterpret_cast<Bytef *>(buffer); + get_z_stream().avail_out = uInt(m_output_max); + } + +private: + std::error_condition allocate_buffer() noexcept + { + std::error_condition err; + if (!get_buffer()) + { + err = zlib_data::allocate_buffer(); + reset_input(); + } + return err; + } + + bool m_inflate_initialized = false; + std::size_t m_output_max = 0U; +}; + + +// helper class for deflating data + +class deflate_data : private zlib_data +{ +protected: + deflate_data(int level, std::size_t buffer_size) noexcept : zlib_data(buffer_size), m_level(level) + { + } + + ~deflate_data() + { + if (m_deflate_initialized) + deflateEnd(&get_z_stream()); + } + + using zlib_data::input_empty; + using zlib_data::output_available; + + bool stream_initialized() const noexcept + { + return m_deflate_initialized; + } + + bool compression_finished() const noexcept + { + return m_compression_finished; + } + + std::error_condition initialize_z_stream() noexcept + { + if (m_deflate_initialized) + return std::errc::invalid_argument; + std::error_condition err = allocate_buffer(); + if (err) + return err; + zlib_data::initialize_z_stream(); + int const zerr = deflateInit(&get_z_stream(), m_level); + reset_output(); + if (Z_OK == zerr) + m_deflate_initialized = true; + return convert_z_error(zerr); + } + + std::error_condition close_z_stream() noexcept + { + if (!m_deflate_initialized) + return std::errc::invalid_argument; + m_deflate_initialized = m_compression_finished = false; + return convert_z_error(deflateEnd(&get_z_stream())); + } + + std::error_condition reset_z_stream() noexcept + { + if (!m_deflate_initialized) + return std::errc::invalid_argument; + assert(get_buffer()); + reset_output(); + m_compression_finished = false; + return convert_z_error(deflateReset(&get_z_stream())); + } + + std::error_condition compress_some() noexcept + { + assert(m_deflate_initialized); + return convert_z_error(deflate(&get_z_stream(), Z_NO_FLUSH)); + } + + std::error_condition finish_compression() noexcept + { + assert(m_deflate_initialized); + int const zerr = deflate(&get_z_stream(), Z_FINISH); + if (Z_STREAM_END == zerr) + m_compression_finished = true; + return convert_z_error(zerr); + } + + std::size_t input_used() const noexcept + { + return m_input_max - get_z_stream().avail_in; + } + + void set_input(void const *data, std::size_t length) noexcept + { + m_input_max = std::min<std::common_type_t<uInt, std::size_t> >(std::numeric_limits<uInt>::max(), length); + get_z_stream().next_in = const_cast<Bytef z_const *>(reinterpret_cast<Bytef const *>(data)); + get_z_stream().avail_in = uInt(m_input_max); + } + + std::pair<void const *, std::size_t> get_output() const noexcept + { + assert(get_buffer()); + return std::make_pair( + &get_buffer()[m_output_used], + get_buffer_size() - get_z_stream().avail_out - m_output_used); + } + + void consume_output(std::size_t length) noexcept + { + m_output_used += length; + assert(m_output_used <= (get_buffer_size() - get_z_stream().avail_out)); + if (m_output_used == (get_buffer_size() - get_z_stream().avail_out)) + reset_output(); + } + +private: + void reset_output() noexcept + { + assert(get_buffer()); + m_output_used = 0U; + get_z_stream().next_out = get_buffer(); + get_z_stream().avail_out = uInt(get_buffer_size()); + } + + int const m_level; + bool m_deflate_initialized = false; + bool m_compression_finished = false; + std::size_t m_output_used = 0U; + std::size_t m_input_max = 0U; +}; + + +// helper for holding an object and deleting it (or not) as necessary + +template <typename T> +class filter_base +{ +protected: + filter_base(std::unique_ptr<T> &&object) noexcept : m_object(object.release()), m_owned(true) + { + assert(m_object); + } + + filter_base(T &object) noexcept : m_object(&object), m_owned(false) + { + } + + ~filter_base() + { + if (m_owned) + delete m_object; + } + + T &object() noexcept + { + return *m_object; + } + +private: + T *const m_object; + bool const m_owned; +}; + + +// filter for decompressing deflated data + +template <typename Stream> +class zlib_read_filter : public read_stream, protected filter_base<Stream>, protected inflate_data +{ +public: + zlib_read_filter(std::unique_ptr<Stream> &&stream, std::size_t read_chunk) noexcept : + filter_base<Stream>(std::move(stream)), + inflate_data(read_chunk) + { + } + + zlib_read_filter(Stream &stream, std::size_t read_chunk) noexcept : + filter_base<Stream>(stream), + inflate_data(read_chunk) + { + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err; + actual = 0U; + + if (!stream_initialized()) + err = initialize_z_stream(); + + if (!err && (length > actual)) + { + bool short_input = false; + do + { + if (input_empty()) + { + auto const space = get_unfilled_input(); + std::size_t filled; + err = this->object().read(space.first, space.second, filled); + add_input(filled); + short_input = space.second > filled; + } + + if (!err && !input_empty()) + { + set_output(reinterpret_cast<std::uint8_t *>(buffer) + actual, length - actual); + bool stream_end; + err = decompress_some(stream_end); + actual += output_produced(); + + if (stream_end) + { + assert(!err); + if constexpr (std::is_base_of_v<random_read, Stream>) + { + if (!input_empty()) + { + std::int64_t const overshoot = std::uint64_t(input_available()); + reset_input(); + return this->object().seek(-overshoot, SEEK_CUR); + } + } + else + { + return std::error_condition(); + } + } + } + } + while (!err && (length > actual) && !short_input); + } + + return err; + } +}; + + +// filter for deflating data + +class zlib_write_filter : public write_stream, protected filter_base<write_stream>, protected deflate_data +{ +public: + zlib_write_filter(write_stream::ptr &&stream, int level, std::size_t buffer_size) noexcept : + filter_base<write_stream>(std::move(stream)), + deflate_data(level, buffer_size) + { + } + + zlib_write_filter(write_stream &stream, int level, std::size_t buffer_size) noexcept : + filter_base<write_stream>(stream), + deflate_data(level, buffer_size) + { + } + + ~zlib_write_filter() + { + finalize(); + } + + virtual std::error_condition finalize() noexcept override + { + if (!stream_initialized()) + return std::error_condition(); + + do + { + if (!compression_finished() && output_available()) + { + std::error_condition err = finish_compression(); + if (err) + return err; + } + + while ((compression_finished() && get_output().second) || !output_available()) + { + std::error_condition err = write_some(); + if (err) + return err; + } + } + while (!compression_finished()); + + return close_z_stream(); + } + + virtual std::error_condition flush() noexcept override + { + if (stream_initialized()) + { + auto const output = get_output(); + if (output.second) + { + std::size_t written; + std::error_condition err = object().write(output.first, output.second, written); + consume_output(written); + if (err) + { + object().flush(); + return err; + } + } + } + return object().flush(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition err; + actual = 0U; + + if (!stream_initialized()) + { + err = initialize_z_stream(); + } + else if (compression_finished()) + { + while (!err && get_output().second) + err = write_some(); + if (!err) + err = reset_z_stream(); + } + + while (!err && (length > actual)) + { + set_input(reinterpret_cast<std::uint8_t const *>(buffer) + actual, length - actual); + do + { + if (output_available()) + err = compress_some(); + + while (!err && !output_available()) + err = write_some(); + } + while (!err && !input_empty()); + actual += input_used(); + } + + return err; + } + +private: + std::error_condition write_some() noexcept + { + auto const output = get_output(); + std::size_t written; + std::error_condition err = object().write(output.first, output.second, written); + consume_output(written); + return err; + } +}; + +} // anonymous namespace + + +// creating decompressing filters + +read_stream::ptr zlib_read(read_stream::ptr &&stream, std::size_t read_chunk) noexcept +{ + read_stream::ptr result; + if (stream) + result.reset(new (std::nothrow) zlib_read_filter<read_stream>(std::move(stream), read_chunk)); + return result; +} + +read_stream::ptr zlib_read(random_read::ptr &&stream, std::size_t read_chunk) noexcept +{ + read_stream::ptr result; + if (stream) + result.reset(new (std::nothrow) zlib_read_filter<random_read>(std::move(stream), read_chunk)); + return result; +} + +read_stream::ptr zlib_read(read_stream &stream, std::size_t read_chunk) noexcept +{ + return read_stream::ptr(new (std::nothrow) zlib_read_filter<read_stream>(stream, read_chunk)); +} + +read_stream::ptr zlib_read(random_read &stream, std::size_t read_chunk) noexcept +{ + return read_stream::ptr(new (std::nothrow) zlib_read_filter<random_read>(stream, read_chunk)); +} + + +// creating compressing filters + +write_stream::ptr zlib_write(write_stream::ptr &&stream, int level, std::size_t buffer_size) noexcept +{ + write_stream::ptr result; + if (stream) + result.reset(new (std::nothrow) zlib_write_filter(std::move(stream), level, buffer_size)); + return result; +} + +write_stream::ptr zlib_write(write_stream &stream, int level, std::size_t buffer_size) noexcept +{ + return write_stream::ptr(new (std::nothrow) zlib_write_filter(stream, level, buffer_size)); +} + +} // namespace util diff --git a/src/lib/util/ioprocsfilter.h b/src/lib/util/ioprocsfilter.h new file mode 100644 index 00000000000..a146dd737ce --- /dev/null +++ b/src/lib/util/ioprocsfilter.h @@ -0,0 +1,31 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocs.h + + I/O filters + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_IOPROCSFILTER_H +#define MAME_LIB_UTIL_IOPROCSFILTER_H + +#include "utilfwd.h" + +#include <cstdlib> +#include <memory> + + +namespace util { + +std::unique_ptr<read_stream> zlib_read(std::unique_ptr<read_stream> &&stream, std::size_t read_chunk) noexcept; +std::unique_ptr<read_stream> zlib_read(std::unique_ptr<random_read> &&stream, std::size_t read_chunk) noexcept; +std::unique_ptr<read_stream> zlib_read(read_stream &stream, std::size_t read_chunk) noexcept; +std::unique_ptr<read_stream> zlib_read(random_read &stream, std::size_t read_chunk) noexcept; + +std::unique_ptr<write_stream> zlib_write(std::unique_ptr<write_stream> &&stream, int level, std::size_t buffer_size) noexcept; +std::unique_ptr<write_stream> zlib_write(write_stream &stream, int level, std::size_t buffer_size) noexcept; + +} // namespace util + +#endif // MAME_LIB_UTIL_IOPROCSFILTER_H diff --git a/src/lib/util/ioprocsvec.h b/src/lib/util/ioprocsvec.h new file mode 100644 index 00000000000..27dda095ad3 --- /dev/null +++ b/src/lib/util/ioprocsvec.h @@ -0,0 +1,211 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + ioprocsvec.h + + Helper for using a vector as an in-memory file + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_IOPROCSVEC_H +#define MAME_LIB_UTIL_IOPROCSVEC_H + +#include "ioprocs.h" + +#include <cstdint> +#include <cstdlib> +#include <cstring> +#include <limits> +#include <system_error> +#include <type_traits> +#include <vector> + + +namespace util { + +template <typename T = std::uint8_t> +class vector_read_write_adapter : public random_read_write +{ +public: + vector_read_write_adapter(std::vector<T> &storage) noexcept : m_storage(storage) + { + check_resized(); + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + switch (whence) + { + case SEEK_SET: + if (0 > offset) + return std::errc::invalid_argument; + m_pointer = std::uint64_t(offset); + return std::error_condition(); + + case SEEK_CUR: + if (0 > offset) + { + if (std::uint64_t(-offset) > m_pointer) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_pointer) + { + return std::errc::invalid_argument; + } + m_pointer += offset; + return std::error_condition(); + + case SEEK_END: + check_resized(); + if (0 > offset) + { + if (std::uint64_t(-offset) > m_size) + return std::errc::invalid_argument; + } + else if ((std::numeric_limits<std::uint64_t>::max() - offset) < m_size) + { + return std::errc::invalid_argument; + } + m_pointer = std::uint64_t(m_size) + offset; + return std::error_condition(); + + default: + return std::errc::invalid_argument; + } + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_pointer; + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + check_resized(); + if (std::numeric_limits<uint64_t>::max() < m_size) + return std::errc::file_too_large; + + result = m_size; + return std::error_condition(); + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(m_pointer, buffer, length, actual); + m_pointer += actual; + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + do_read(offset, buffer, length, actual); + return std::error_condition(); + } + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::error_condition result = do_write(m_pointer, buffer, length, actual); + m_pointer += actual; + return result; + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + return do_write(offset, buffer, length, actual); + } + +private: + using elements_type = typename std::vector<T>::size_type; + using common_size_type = std::common_type_t<std::uint64_t, std::size_t>; + + void check_resized() noexcept + { + if (m_storage.size() != m_elements) + { + m_elements = m_storage.size(); + m_size = m_elements * sizeof(T); + } + } + + void do_read(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept + { + check_resized(); + if ((offset < m_size) && length) + { + actual = std::min(std::size_t(m_size - offset), length); + std::memmove(buffer, reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, actual); + } + else + { + actual = 0U; + } + } + + std::error_condition do_write(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept + { + check_resized(); + std::size_t allocated = m_elements * sizeof(T); + std::uint64_t const target = ((std::numeric_limits<std::uint64_t>::max() - offset) >= length) + ? (offset + length) + : std::numeric_limits<std::uint64_t>::max(); + + std::error_condition result; + if (target > allocated) + { + elements_type const required = std::min<std::common_type_t<elements_type, std::uint64_t> >( + std::numeric_limits<elements_type>::max(), + (target + sizeof(T) - 1) / sizeof(T)); // technically can overflow but unlikely + try { m_storage.resize(required); } // unpredictable if constructing/assigning T can throw + catch (...) { result = std::errc::not_enough_memory; } + m_elements = m_storage.size(); + allocated = m_elements * sizeof(T); + assert(allocated >= m_size); + + if (offset > m_size) + { + std::size_t const new_size = std::size_t(std::min<common_size_type>(allocated, offset)); + std::memset( + reinterpret_cast<std::uint8_t *>(m_storage.data()) + m_size, + 0x00, // POSIX says unwritten areas of files should read as zero + new_size - m_size); + m_size = new_size; + } + } + + std::size_t const limit = std::min<common_size_type>(target, allocated); + if (limit > offset) + { + actual = std::size_t(limit - offset); + std::memmove(reinterpret_cast<std::uint8_t *>(m_storage.data()) + offset, buffer, actual); + m_size = limit; + } + else + { + actual = 0U; + } + if (!result && (actual < length)) + result = std::errc::no_space_on_device; + + return result; + } + + std::vector<T> &m_storage; + std::uint64_t m_pointer = 0U; + elements_type m_elements = 0U; + std::size_t m_size = 0U; +}; + +} // namespace util + +#endif // MAME_LIB_UTIL_IOPROCSVEC_H diff --git a/src/lib/util/path.cpp b/src/lib/util/path.cpp new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/src/lib/util/path.cpp diff --git a/src/lib/util/path.h b/src/lib/util/path.h new file mode 100644 index 00000000000..1291d7b17d3 --- /dev/null +++ b/src/lib/util/path.h @@ -0,0 +1,65 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + path.h + + Filesystem path utilties + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_PATH_H +#define MAME_LIB_UTIL_PATH_H + +#include "osdfile.h" // for PATH_SEPARATOR + +#include <string> +#include <utility> + + +namespace util { + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +// is a given character a directory separator? + +constexpr bool is_directory_separator(char c) +{ +#if defined(WIN32) + return ('\\' == c) || ('/' == c) || (':' == c); +#else + return '/' == c; +#endif +} + + +// append to a path + +template <typename T, typename... U> +inline std::string &path_append(std::string &path, T &&next, U &&... more) +{ + if (!path.empty() && !is_directory_separator(path.back())) + path.append(PATH_SEPARATOR); + path.append(std::forward<T>(next)); + if constexpr (sizeof...(U)) + return path_append(std::forward<U>(more)...); + else + return path; +} + + +// concatenate paths + +template <typename T, typename... U> +inline std::string path_concat(T &&first, U &&... more) +{ + std::string result(std::forward<T>(first)); + if constexpr (sizeof...(U)) + path_append(result, std::forward<U>(more)...); + return result; +} + +} // namespace util + +#endif // MAME_LIB_UTIL_PATH_H diff --git a/src/lib/util/strformat.h b/src/lib/util/strformat.h index b8c9b4f1a99..a3ba1d11261 100644 --- a/src/lib/util/strformat.h +++ b/src/lib/util/strformat.h @@ -578,16 +578,16 @@ template <typename Stream, typename T> class format_output { private: - template <typename U> struct string_semantics - { static constexpr bool value = false; }; - template <typename CharT, typename Traits, typename Allocator> struct string_semantics<std::basic_string<CharT, Traits, Allocator> > - { static constexpr bool value = true; }; - template <typename CharT, typename Traits> struct string_semantics<std::basic_string_view<CharT, Traits> > - { static constexpr bool value = true; }; - template <typename U> struct signed_integer_semantics - { static constexpr bool value = std::is_integral_v<U>&& std::is_signed_v<U>; }; - template <typename U> struct unsigned_integer_semantics - { static constexpr bool value = std::is_integral_v<U>&& !std::is_signed_v<U>; }; + template <typename U> + struct string_semantics : public std::false_type { }; + template <typename CharT, typename Traits, typename Allocator> + struct string_semantics<std::basic_string<CharT, Traits, Allocator> > : public std::true_type { }; + template <typename CharT, typename Traits> + struct string_semantics<std::basic_string_view<CharT, Traits> > : public std::true_type { }; + template <typename U> + using signed_integer_semantics = std::bool_constant<std::is_integral_v<U> && std::is_signed_v<U> >; + template <typename U> + using unsigned_integer_semantics = std::bool_constant<std::is_integral_v<U> && !std::is_signed_v<U> >; static void apply_signed(Stream &str, char16_t const &value) { @@ -865,8 +865,8 @@ template <typename Stream, typename T> class format_output<Stream, T *> { protected: - template <typename U> struct string_semantics - { static constexpr bool value = std::is_same<std::remove_const_t<U>, typename Stream::char_type>::value; }; + template <typename U> + using string_semantics = std::bool_constant<std::is_same_v<std::remove_const_t<U>, typename Stream::char_type> >; public: template <typename U> @@ -940,10 +940,10 @@ template <typename T> class format_make_integer { private: - template <typename U> struct use_unsigned_cast - { static constexpr bool value = std::is_convertible<U const, unsigned>::value && std::is_unsigned<U>::value; }; - template <typename U> struct use_signed_cast - { static constexpr bool value = !use_unsigned_cast<U>::value && std::is_convertible<U const, int>::value; }; + template <typename U> + using use_unsigned_cast = std::bool_constant<std::is_convertible_v<U const, unsigned> && std::is_unsigned_v<U> >; + template <typename U> + using use_signed_cast = std::bool_constant<!use_unsigned_cast<U>::value && std::is_convertible_v<U const, int> >; public: template <typename U> static bool apply(U const &value, int &result) @@ -974,14 +974,14 @@ template <typename T> class format_store_integer { private: - template <typename U> struct is_non_const_ptr - { static constexpr bool value = std::is_pointer<U>::value && !std::is_const<std::remove_pointer_t<U> >::value; }; - template <typename U> struct is_unsigned_ptr - { static constexpr bool value = std::is_pointer<U>::value && std::is_unsigned<std::remove_pointer_t<U> >::value; }; - template <typename U> struct use_unsigned_cast - { static constexpr bool value = is_non_const_ptr<U>::value && is_unsigned_ptr<U>::value && std::is_convertible<std::make_unsigned_t<std::streamoff>, std::remove_pointer_t<U> >::value; }; - template <typename U> struct use_signed_cast - { static constexpr bool value = is_non_const_ptr<U>::value && !use_unsigned_cast<U>::value && std::is_convertible<std::streamoff, std::remove_pointer_t<U> >::value; }; + template <typename U> + using is_non_const_ptr = std::bool_constant<std::is_pointer_v<U> && !std::is_const_v<std::remove_pointer_t<U> > >; + template <typename U> + using is_unsigned_ptr = std::bool_constant<std::is_pointer_v<U> && std::is_unsigned_v<std::remove_pointer_t<U> > >; + template <typename U> + using use_unsigned_cast = std::bool_constant<is_non_const_ptr<U>::value && is_unsigned_ptr<U>::value && std::is_convertible_v<std::make_unsigned_t<std::streamoff>, std::remove_pointer_t<U> > >; + template <typename U> + using use_signed_cast = std::bool_constant<is_non_const_ptr<U>::value && !use_unsigned_cast<U>::value && std::is_convertible_v<std::streamoff, std::remove_pointer_t<U> > >; public: template <typename U> static bool apply(U const &value, std::streamoff data) @@ -1091,11 +1091,11 @@ public: protected: template <typename T> - struct handle_char_ptr { static constexpr bool value = std::is_pointer<T>::value && std::is_same<std::remove_cv_t<std::remove_pointer_t<T> >, char_type>::value; }; + using handle_char_ptr = std::bool_constant<std::is_pointer_v<T> && std::is_same_v<std::remove_cv_t<std::remove_pointer_t<T> >, char_type> >; template <typename T> - struct handle_char_array { static constexpr bool value = std::is_array<T>::value && std::is_same<std::remove_cv_t<std::remove_extent_t<T> >, char_type>::value; }; + using handle_char_array = std::bool_constant<std::is_array_v<T> && std::is_same_v<std::remove_cv_t<std::remove_extent_t<T> >, char_type> >; template <typename T> - struct handle_container { static constexpr bool value = !handle_char_ptr<T>::value && !handle_char_array<T>::value; }; + using handle_container = std::bool_constant<!handle_char_ptr<T>::value && !handle_char_array<T>::value>; template <typename Format> format_argument_pack( diff --git a/src/lib/util/timeconv.cpp b/src/lib/util/timeconv.cpp index 99953712003..469b6b592a2 100644 --- a/src/lib/util/timeconv.cpp +++ b/src/lib/util/timeconv.cpp @@ -14,42 +14,10 @@ namespace util { -/*************************************************************************** - PROTOTYPES -***************************************************************************/ - -static std::chrono::system_clock::duration calculate_system_clock_adjustment(); - - -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ - -std::chrono::system_clock::duration system_clock_adjustment(calculate_system_clock_adjustment()); - - -/*************************************************************************** - IMPLEMENTATION -***************************************************************************/ - -arbitrary_datetime arbitrary_datetime::now() -{ - time_t sec; - time(&sec); - auto t = *localtime(&sec); - - arbitrary_datetime dt; - dt.year = t.tm_year + 1900; - dt.month = t.tm_mon + 1; - dt.day_of_month = t.tm_mday; - dt.hour = t.tm_hour; - dt.minute = t.tm_min; - dt.second = t.tm_sec; - return dt; -} +namespace { -static std::chrono::system_clock::duration calculate_system_clock_adjustment() +std::chrono::system_clock::duration calculate_system_clock_adjustment() { constexpr auto days_in_year(365); constexpr auto days_in_four_years((days_in_year * 4) + 1); @@ -84,6 +52,39 @@ static std::chrono::system_clock::duration calculate_system_clock_adjustment() return result - std::chrono::system_clock::from_time_t(0).time_since_epoch(); } +} // anonymous namespace + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +std::chrono::system_clock::duration system_clock_adjustment(calculate_system_clock_adjustment()); + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +arbitrary_datetime arbitrary_datetime::now() +{ + time_t sec; + time(&sec); + auto t = *localtime(&sec); + + arbitrary_datetime dt; + dt.year = t.tm_year + 1900; + dt.month = t.tm_mon + 1; + dt.day_of_month = t.tm_mday; + dt.hour = t.tm_hour; + dt.minute = t.tm_min; + dt.second = t.tm_sec; + + return dt; +} + // ------------------------------------------------- diff --git a/src/lib/util/timeconv.h b/src/lib/util/timeconv.h index f9d1b070012..91327dbac16 100644 --- a/src/lib/util/timeconv.h +++ b/src/lib/util/timeconv.h @@ -22,6 +22,7 @@ namespace util { + /*************************************************************************** GLOBAL VARIABLES ***************************************************************************/ @@ -58,7 +59,7 @@ struct arbitrary_datetime // date of the epoch's begining //--------------------------------------------------------- -template<typename Rep, int Y, int M, int D, int H, int N, int S, typename Ratio> +template <typename Rep, int Y, int M, int D, int H, int N, int S, typename Ratio> class arbitrary_clock { public: @@ -89,7 +90,7 @@ public: // with a different scale to this arbitrary_clock's scale //--------------------------------------------------------- - template<typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2> + template <typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2> static time_point from_arbitrary_time_point(const std::chrono::time_point<arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2> > &tp) { arbitrary_datetime dt; @@ -111,7 +112,7 @@ public: // of this scale to one of different scale //--------------------------------------------------------- - template<typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2> + template <typename Rep2, int YY, int MM, int DD, int HH, int NN, int SS, typename Ratio2> static std::chrono::time_point<arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2> > to_arbitrary_time_point(const time_point &tp) { return arbitrary_clock<Rep2, YY, MM, DD, HH, NN, SS, Ratio2>::from_arbitrary_time_point(tp); @@ -125,14 +126,14 @@ public: static struct tm to_tm(const time_point &tp) { std::chrono::time_point<tm_conversion_clock> normalized_tp = to_arbitrary_time_point< - std::int64_t, - tm_conversion_clock::base_year, - tm_conversion_clock::base_month, - tm_conversion_clock::base_day, - tm_conversion_clock::base_hour, - tm_conversion_clock::base_minute, - tm_conversion_clock::base_second, - tm_conversion_clock::period>(tp); + std::int64_t, + tm_conversion_clock::base_year, + tm_conversion_clock::base_month, + tm_conversion_clock::base_day, + tm_conversion_clock::base_hour, + tm_conversion_clock::base_minute, + tm_conversion_clock::base_second, + tm_conversion_clock::period>(tp); return internal_to_tm(normalized_tp.time_since_epoch()); } @@ -144,14 +145,14 @@ public: static std::chrono::time_point<std::chrono::system_clock> to_system_clock(const time_point &tp) { auto normalized_tp = to_arbitrary_time_point< - std::int64_t, - system_conversion_clock::base_year, - system_conversion_clock::base_month, - system_conversion_clock::base_day, - system_conversion_clock::base_hour, - system_conversion_clock::base_minute, - system_conversion_clock::base_second, - system_conversion_clock::period>(tp); + std::int64_t, + system_conversion_clock::base_year, + system_conversion_clock::base_month, + system_conversion_clock::base_day, + system_conversion_clock::base_hour, + system_conversion_clock::base_minute, + system_conversion_clock::base_second, + system_conversion_clock::period>(tp); return std::chrono::time_point<std::chrono::system_clock>(normalized_tp.time_since_epoch() + system_clock_adjustment); } diff --git a/src/lib/util/un7z.cpp b/src/lib/util/un7z.cpp index 879da294c69..b35af479d5d 100644 --- a/src/lib/util/un7z.cpp +++ b/src/lib/util/un7z.cpp @@ -13,11 +13,13 @@ #include "unzip.h" #include "corestr.h" -#include "osdcore.h" -#include "osdfile.h" +#include "ioprocs.h" #include "unicode.h" #include "timeconv.h" +#include "osdcore.h" +#include "osdfile.h" + #include "lzma/C/7z.h" #include "lzma/C/7zAlloc.h" #include "lzma/C/7zCrc.h" @@ -38,47 +40,71 @@ namespace util { + namespace { + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ -struct CSzFile +struct CFileInStream : public ISeekInStream { - CSzFile() : currfpos(0), length(0), osdfile() {} + CFileInStream() noexcept + { + Read = [] (void *pp, void *buf, size_t *size) { return reinterpret_cast<CFileInStream *>(pp)->read(buf, *size); }; + Seek = [] (void *pp, Int64 *pos, ESzSeek origin) { return reinterpret_cast<CFileInStream *>(pp)->seek(*pos, origin); }; + } - std::uint64_t currfpos; - std::uint64_t length; - osd_file::ptr osdfile; + random_read::ptr file; + std::uint64_t currfpos = 0; + std::uint64_t length = 0; - SRes read(void *data, std::size_t &size) +private: + SRes read(void *data, std::size_t &size) noexcept { - if (!osdfile) + if (!file) { - osd_printf_error("un7z: called CSzFile::read without file\n"); + osd_printf_error("un7z: called CFileInStream::read without file\n"); return SZ_ERROR_READ; } if (!size) return SZ_OK; - // TODO: this casts a size_t to a uint32_t, so it will fail if >=4GB is requested at once (need a loop) - std::uint32_t read_length(0); - auto const err = osdfile->read(data, currfpos, size, read_length); + std::size_t read_length(0); + std::error_condition const err = file->read_at(currfpos, data, size, read_length); size = read_length; currfpos += read_length; - return (osd_file::error::NONE == err) ? SZ_OK : SZ_ERROR_READ; + return !err ? SZ_OK : SZ_ERROR_READ; } - SRes seek(Int64 &pos, ESzSeek origin) + SRes seek(Int64 &pos, ESzSeek origin) noexcept { + // need to synthesise this because the OSD file wrapper doesn't implement SEEK_END switch (origin) { - case SZ_SEEK_CUR: currfpos += pos; break; - case SZ_SEEK_SET: currfpos = pos; break; - case SZ_SEEK_END: currfpos = length + pos; break; - default: return SZ_ERROR_READ; + case SZ_SEEK_CUR: + if ((0 > pos) && (-pos > currfpos)) + { + osd_printf_error("un7z: attemped to seek back %d bytes when current offset is %u\n", -pos, currfpos); + return SZ_ERROR_READ; + } + currfpos += pos; + break; + case SZ_SEEK_SET: + currfpos = pos; + break; + case SZ_SEEK_END: + if ((0 > pos) && (-pos > length)) + { + osd_printf_error("un7z: attemped to seek %d bytes before end of file %u\n", -pos, currfpos); + return SZ_ERROR_READ; + } + currfpos = length + pos; + break; + default: + return SZ_ERROR_READ; } pos = currfpos; return SZ_OK; @@ -86,18 +112,18 @@ struct CSzFile }; -struct CFileInStream : public ISeekInStream, public CSzFile -{ - CFileInStream(); -}; - - -class m7z_file_impl +class m7z_file_impl { public: typedef std::unique_ptr<m7z_file_impl> ptr; - m7z_file_impl(const std::string &filename); + m7z_file_impl(std::string &&filename) noexcept; + + m7z_file_impl(random_read::ptr &&file) noexcept + : m7z_file_impl(std::string()) + { + m_archive_stream.file = std::move(file); + } virtual ~m7z_file_impl() { @@ -107,7 +133,7 @@ public: SzArEx_Free(&m_db, &m_alloc_imp); } - static ptr find_cached(const std::string &filename) + static ptr find_cached(std::string_view filename) noexcept { std::lock_guard<std::mutex> guard(s_cache_mutex); for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++) @@ -115,47 +141,60 @@ public: // if we have a valid entry and it matches our filename, use it and remove from the cache if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename)) { + using std::swap; ptr result; - std::swap(s_cache[cachenum], result); + swap(s_cache[cachenum], result); osd_printf_verbose("un7z: found %s in cache\n", filename); return result; } } return ptr(); } - static void close(ptr &&archive); - static void cache_clear() + + static void close(ptr &&archive) noexcept; + + static void cache_clear() noexcept { // clear call cache entries std::lock_guard<std::mutex> guard(s_cache_mutex); - for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { } + for (auto &cached : s_cache) + cached.reset(); } - archive_file::error initialize(); + std::error_condition initialize() noexcept; - int first_file() { return search(0, 0, std::string(), false, false, false); } - int next_file() { return (m_curr_file_idx < 0) ? -1 : search(m_curr_file_idx + 1, 0, std::string(), false, false, false); } + int first_file() noexcept + { + return search(0, 0, std::string_view(), false, false, false); + } - int search(std::uint32_t crc) + int next_file() noexcept { - return search(0, crc, std::string(), true, false, false); + return (m_curr_file_idx < 0) ? -1 : search(m_curr_file_idx + 1, 0, std::string_view(), false, false, false); } - int search(const std::string &filename, bool partialpath) + + int search(std::uint32_t crc) noexcept + { + return search(0, crc, std::string_view(), true, false, false); + } + + int search(std::string_view filename, bool partialpath) noexcept { return search(0, 0, filename, false, true, partialpath); } - int search(std::uint32_t crc, const std::string &filename, bool partialpath) + + int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept { return search(0, crc, filename, true, true, partialpath); } - bool current_is_directory() const { return m_curr_is_dir; } - const std::string ¤t_name() const { return m_curr_name; } - std::uint64_t current_uncompressed_length() const { return m_curr_length; } - virtual std::chrono::system_clock::time_point current_last_modified() const { return m_curr_modified; } - std::uint32_t current_crc() const { return m_curr_crc; } + bool current_is_directory() const noexcept { return m_curr_is_dir; } + const std::string ¤t_name() const noexcept { return m_curr_name; } + std::uint64_t current_uncompressed_length() const noexcept { return m_curr_length; } + virtual std::chrono::system_clock::time_point current_last_modified() const noexcept { return m_curr_modified; } + std::uint32_t current_crc() const noexcept { return m_curr_crc; } - archive_file::error decompress(void *buffer, std::uint32_t length); + std::error_condition decompress(void *buffer, std::size_t length) noexcept; private: m7z_file_impl(const m7z_file_impl &) = delete; @@ -166,12 +205,12 @@ private: int search( int i, std::uint32_t search_crc, - const std::string &search_filename, + std::string_view search_filename, bool matchcrc, bool matchname, - bool partialpath); + bool partialpath) noexcept; void make_utf8_name(int index); - void set_curr_modified(); + void set_curr_modified() noexcept; static constexpr std::size_t CACHE_SIZE = 8; static std::array<ptr, CACHE_SIZE> s_cache; @@ -187,7 +226,7 @@ private: std::uint32_t m_curr_crc; // current file crc std::vector<UInt16> m_utf16_buf; - std::vector<char32_t> m_uchar_buf; + std::vector<char32_t> m_uchar_buf; std::vector<char> m_utf8_buf; CFileInStream m_archive_stream; @@ -201,39 +240,38 @@ private: UInt32 m_block_index; Byte * m_out_buffer; std::size_t m_out_buffer_size; - }; class m7z_file_wrapper : public archive_file { public: - m7z_file_wrapper(m7z_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); } + m7z_file_wrapper(m7z_file_impl::ptr &&impl) noexcept : m_impl(std::move(impl)) { assert(m_impl); } virtual ~m7z_file_wrapper() override { m7z_file_impl::close(std::move(m_impl)); } - virtual int first_file() override { return m_impl->first_file(); } - virtual int next_file() override { return m_impl->next_file(); } + virtual int first_file() noexcept override { return m_impl->first_file(); } + virtual int next_file() noexcept override { return m_impl->next_file(); } - virtual int search(std::uint32_t crc) override + virtual int search(std::uint32_t crc) noexcept override { return m_impl->search(crc); } - virtual int search(const std::string &filename, bool partialpath) override + virtual int search(std::string_view filename, bool partialpath) noexcept override { return m_impl->search(filename, partialpath); } - virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) override + virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept override { return m_impl->search(crc, filename, partialpath); } - virtual bool current_is_directory() const override { return m_impl->current_is_directory(); } - virtual const std::string ¤t_name() const override { return m_impl->current_name(); } - virtual std::uint64_t current_uncompressed_length() const override { return m_impl->current_uncompressed_length(); } - virtual std::chrono::system_clock::time_point current_last_modified() const override { return m_impl->current_last_modified(); } - virtual std::uint32_t current_crc() const override { return m_impl->current_crc(); } + virtual bool current_is_directory() const noexcept override { return m_impl->current_is_directory(); } + virtual const std::string ¤t_name() const noexcept override { return m_impl->current_name(); } + virtual std::uint64_t current_uncompressed_length() const noexcept override { return m_impl->current_uncompressed_length(); } + virtual std::chrono::system_clock::time_point current_last_modified() const noexcept override { return m_impl->current_last_modified(); } + virtual std::uint32_t current_crc() const noexcept override { return m_impl->current_crc(); } - virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); } + virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept override { return m_impl->decompress(buffer, length); } private: m7z_file_impl::ptr m_impl; @@ -251,48 +289,20 @@ std::mutex m7z_file_impl::s_cache_mutex; /*************************************************************************** - 7Zip Memory / File handling (adapted from 7zfile.c/.h and 7zalloc.c/.h) -***************************************************************************/ - -/* ---------- FileInStream ---------- */ - -extern "C" { -static SRes FileInStream_Read(void *pp, void *buf, size_t *size) -{ - return (reinterpret_cast<CFileInStream *>(pp)->read(buf, *size) == 0) ? SZ_OK : SZ_ERROR_READ; -} - -static SRes FileInStream_Seek(void *pp, Int64 *pos, ESzSeek origin) -{ - return reinterpret_cast<CFileInStream *>(pp)->seek(*pos, origin); -} - -} // extern "C" - - -CFileInStream::CFileInStream() -{ - Read = &FileInStream_Read; - Seek = &FileInStream_Seek; -} - - - -/*************************************************************************** CACHE MANAGEMENT ***************************************************************************/ -m7z_file_impl::m7z_file_impl(const std::string &filename) - : m_filename(filename) +m7z_file_impl::m7z_file_impl(std::string &&filename) noexcept + : m_filename(std::move(filename)) , m_curr_file_idx(-1) , m_curr_is_dir(false) , m_curr_name() , m_curr_length(0) , m_curr_modified() , m_curr_crc(0) - , m_utf16_buf(128) - , m_uchar_buf(128) - , m_utf8_buf(512) + , m_utf16_buf() + , m_uchar_buf() + , m_utf8_buf() , m_inited(false) , m_block_index(0) , m_out_buffer(nullptr) @@ -310,15 +320,45 @@ m7z_file_impl::m7z_file_impl(const std::string &filename) } -archive_file::error m7z_file_impl::initialize() +std::error_condition m7z_file_impl::initialize() noexcept { - osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length); - if (err != osd_file::error::NONE) - return archive_file::error::FILE_ERROR; + try + { + if (m_utf16_buf.size() < 128) + m_utf16_buf.resize(128); + if (m_uchar_buf.size() < 128) + m_uchar_buf.resize(128); + m_utf8_buf.reserve(512); + } + catch (...) + { + return std::errc::not_enough_memory; + } - osd_printf_verbose("un7z: opened archive file %s\n", m_filename); + if (!m_archive_stream.file) + { + osd_file::ptr file; + std::error_condition const err = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_archive_stream.length); + if (err) + return err; + m_archive_stream.file = osd_file_read(std::move(file)); + osd_printf_verbose("un7z: opened archive file %s\n", m_filename); + } + else if (!m_archive_stream.length) + { + std::error_condition const err = m_archive_stream.file->length(m_archive_stream.length); + if (err) + { + osd_printf_verbose( + "un7z: error getting length of archive file %s (%s:%d %s)\n", + m_filename, err.category().name(), err.value(), err.message()); + return err; + } + } - CrcGenerateTable(); // FIXME: doesn't belong here - it should be called once statically + // TODO: coordinate this with other LZMA users in the codebase? + struct crc_table_generator { crc_table_generator() { CrcGenerateTable(); } }; + static crc_table_generator crc_table; SzArEx_Init(&m_db); m_inited = true; @@ -329,13 +369,13 @@ archive_file::error m7z_file_impl::initialize() switch (res) { case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED; - case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY; + case SZ_ERROR_MEM: return std::errc::not_enough_memory; case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED; - default: return archive_file::error::FILE_ERROR; + default: return std::errc::io_error; // TODO: better default error? } } - return archive_file::error::NONE; + return std::error_condition(); } @@ -344,33 +384,38 @@ archive_file::error m7z_file_impl::initialize() to the cache -------------------------------------------------*/ -void m7z_file_impl::close(ptr &&archive) +void m7z_file_impl::close(ptr &&archive) noexcept { - if (!archive) return; + // if the filename isn't empty, the implementation can be cached + if (archive && !archive->m_filename.empty()) + { + // close the open files + osd_printf_verbose("un7z: closing archive file %s and sending to cache\n", archive->m_filename); + archive->m_archive_stream.file.reset(); - // close the open files - osd_printf_verbose("un7z: closing archive file %s and sending to cache\n", archive->m_filename); - archive->m_archive_stream.osdfile.reset(); + // find the first nullptr entry in the cache + std::lock_guard<std::mutex> guard(s_cache_mutex); + std::size_t cachenum; + for (cachenum = 0; cachenum < s_cache.size(); cachenum++) + if (!s_cache[cachenum]) + break; - // find the first nullptr entry in the cache - std::lock_guard<std::mutex> guard(s_cache_mutex); - std::size_t cachenum; - for (cachenum = 0; cachenum < s_cache.size(); cachenum++) - if (!s_cache[cachenum]) - break; + // if no room left in the cache, free the bottommost entry + if (cachenum == s_cache.size()) + { + cachenum--; + osd_printf_verbose("un7z: removing %s from cache to make space\n", s_cache[cachenum]->m_filename); + s_cache[cachenum].reset(); + } - // if no room left in the cache, free the bottommost entry - if (cachenum == s_cache.size()) - { - cachenum--; - osd_printf_verbose("un7z: removing %s from cache to make space\n", s_cache[cachenum]->m_filename); - s_cache[cachenum].reset(); + // move everyone else down and place us at the top + for ( ; cachenum > 0; cachenum--) + s_cache[cachenum] = std::move(s_cache[cachenum - 1]); + s_cache[0] = std::move(archive); } - // move everyone else down and place us at the top - for ( ; cachenum > 0; cachenum--) - s_cache[cachenum] = std::move(s_cache[cachenum - 1]); - s_cache[0] = std::move(archive); + // make sure it's cleaned up + archive.reset(); } @@ -384,7 +429,7 @@ void m7z_file_impl::close(ptr &&archive) from a _7Z into the target buffer -------------------------------------------------*/ -archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length) +std::error_condition m7z_file_impl::decompress(void *buffer, std::size_t length) noexcept { // if we don't have enough buffer, error if (length < m_curr_length) @@ -394,15 +439,19 @@ archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length } // make sure the file is open.. - if (!m_archive_stream.osdfile) + if (!m_archive_stream.file) { - m_archive_stream.currfpos = 0; - osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length); - if (err != osd_file::error::NONE) + m_archive_stream.currfpos = 0; // FIXME: should it really be changing the file pointer out from under LZMA? + osd_file::ptr file; + std::error_condition const err = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_archive_stream.length); + if (err) { - osd_printf_error("un7z: error reopening archive file %s (%d)\n", m_filename, int(err)); - return archive_file::error::FILE_ERROR; + osd_printf_error( + "un7z: error reopening archive file %s (%s:%d %s)\n", + m_filename, err.category().name(), err.value(), err.message()); + return err; } + m_archive_stream.file = osd_file_read(std::move(file)); osd_printf_verbose("un7z: reopened archive file %s\n", m_filename); } @@ -419,7 +468,7 @@ archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length switch (res) { case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED; - case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY; + case SZ_ERROR_MEM: return std::errc::not_enough_memory; case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED; default: return archive_file::error::DECOMPRESS_ERROR; } @@ -427,45 +476,64 @@ archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length // copy to destination buffer std::memcpy(buffer, m_out_buffer + offset, (std::min<std::size_t>)(length, out_size_processed)); - return archive_file::error::NONE; + return std::error_condition(); } int m7z_file_impl::search( int i, std::uint32_t search_crc, - const std::string &search_filename, + std::string_view search_filename, bool matchcrc, bool matchname, - bool partialpath) + bool partialpath) noexcept { - for ( ; i < m_db.NumFiles; i++) - { - make_utf8_name(i); - bool const is_dir(SzArEx_IsDir(&m_db, i)); - const std::uint64_t size(SzArEx_GetFileSize(&m_db, i)); - const std::uint32_t crc(m_db.CRCs.Vals[i]); - const bool crcmatch(SzBitArray_Check(m_db.CRCs.Defs, i) && (crc == search_crc)); - auto const partialoffset(m_utf8_buf.size() - 1 - search_filename.length()); - bool const partialpossible((m_utf8_buf.size() > (search_filename.length() + 1)) && (m_utf8_buf[partialoffset - 1] == '/')); - const bool namematch( - !core_stricmp(search_filename.c_str(), &m_utf8_buf[0]) || - (partialpath && partialpossible && !core_stricmp(search_filename.c_str(), &m_utf8_buf[partialoffset]))); - - const bool found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch); - if (found) + try + { + for ( ; i < m_db.NumFiles; i++) { - m_curr_file_idx = i; - m_curr_is_dir = is_dir; - m_curr_name = &m_utf8_buf[0]; - m_curr_length = size; - set_curr_modified(); - m_curr_crc = crc; - - return i; + make_utf8_name(i); + bool const is_dir(SzArEx_IsDir(&m_db, i)); + const std::uint64_t size(SzArEx_GetFileSize(&m_db, i)); + const std::uint32_t crc(m_db.CRCs.Vals[i]); + + const bool crcmatch(SzBitArray_Check(m_db.CRCs.Defs, i) && (crc == search_crc)); + bool found; + if (!matchname) + { + found = !matchcrc || (crcmatch && !is_dir); + } + else + { + auto const partialoffset = m_utf8_buf.size() - search_filename.length(); + const bool namematch = + (search_filename.length() == m_utf8_buf.size()) && + (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_utf8_buf[0], search_filename.length())); + bool const partialmatch = + partialpath && + ((m_utf8_buf.size() > search_filename.length()) && (m_utf8_buf[partialoffset - 1] == '/')) && + (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_utf8_buf[partialoffset], search_filename.length())); + found = (!matchcrc || crcmatch) && (namematch || partialmatch); + } + + if (found) + { + // set the name first - resizing it can throw an exception, and we want the state to be consistent + m_curr_name.assign(m_utf8_buf.begin(), m_utf8_buf.end()); + m_curr_file_idx = i; + m_curr_is_dir = is_dir; + m_curr_length = size; + set_curr_modified(); + m_curr_crc = crc; + + return i; + } } } - + catch (...) + { + // allocation error handling name + } return -1; } @@ -475,14 +543,14 @@ void m7z_file_impl::make_utf8_name(int index) std::size_t len, out_pos; len = SzArEx_GetFileNameUtf16(&m_db, index, nullptr); - m_utf16_buf.resize((std::max<std::size_t>)(m_utf16_buf.size(), len)); + m_utf16_buf.resize(std::max<std::size_t>(m_utf16_buf.size(), len)); SzArEx_GetFileNameUtf16(&m_db, index, &m_utf16_buf[0]); - m_uchar_buf.resize((std::max<std::size_t>)(m_uchar_buf.size(), len)); + m_uchar_buf.resize(std::max<std::size_t>(m_uchar_buf.size(), len)); out_pos = 0; for (std::size_t in_pos = 0; in_pos < (len - 1); ) { - const int used = uchar_from_utf16(&m_uchar_buf[out_pos], (char16_t *)&m_utf16_buf[in_pos], len - in_pos); + const int used = uchar_from_utf16(&m_uchar_buf[out_pos], reinterpret_cast<char16_t const *>(&m_utf16_buf[in_pos]), len - in_pos); if (used < 0) { in_pos++; @@ -497,7 +565,7 @@ void m7z_file_impl::make_utf8_name(int index) } len = out_pos; - m_utf8_buf.resize((std::max<std::size_t>)(m_utf8_buf.size(), 4 * len + 1)); + m_utf8_buf.resize((std::max<std::size_t>)(m_utf8_buf.size(), 4 * len)); out_pos = 0; for (std::size_t in_pos = 0; in_pos < len; in_pos++) { @@ -508,30 +576,35 @@ void m7z_file_impl::make_utf8_name(int index) out_pos += produced; assert(out_pos < m_utf8_buf.size()); } - m_utf8_buf[out_pos++] = '\0'; m_utf8_buf.resize(out_pos); } -void m7z_file_impl::set_curr_modified() +void m7z_file_impl::set_curr_modified() noexcept { if (SzBitWithVals_Check(&m_db.MTime, m_curr_file_idx)) { CNtfsFileTime const &file_time(m_db.MTime.Vals[m_curr_file_idx]); - auto ticks = ntfs_duration_from_filetime(file_time.High, file_time.Low); - m_curr_modified = system_clock_time_point_from_ntfs_duration(ticks); - } - else - { - // FIXME: what do we do about a lack of time? + try + { + auto ticks = ntfs_duration_from_filetime(file_time.High, file_time.Low); + m_curr_modified = system_clock_time_point_from_ntfs_duration(ticks); + return; + } + catch (...) + { + } } + + // no modification time available, or out-of-range exception + m_curr_modified = std::chrono::system_clock::from_time_t(std::time_t(0)); } } // anonymous namespace -archive_file::error archive_file::open_7z(const std::string &filename, ptr &result) +std::error_condition archive_file::open_7z(std::string_view filename, ptr &result) noexcept { // ensure we start with a nullptr result result.reset(); @@ -542,21 +615,49 @@ archive_file::error archive_file::open_7z(const std::string &filename, ptr &resu if (!newimpl) { // allocate memory for the 7z file structure - try { newimpl = std::make_unique<m7z_file_impl>(filename); } - catch (...) { return error::OUT_OF_MEMORY; } - error const err = newimpl->initialize(); - if (err != error::NONE) return err; + try { newimpl = std::make_unique<m7z_file_impl>(std::string(filename)); } + catch (...) { return std::errc::not_enough_memory; } + auto const err = newimpl->initialize(); + if (err) + return err; } - try + // allocate the archive API wrapper + result.reset(new (std::nothrow) m7z_file_wrapper(std::move(newimpl))); + if (result) { - result = std::make_unique<m7z_file_wrapper>(std::move(newimpl)); - return error::NONE; + return std::error_condition(); } - catch (...) + else + { + m7z_file_impl::close(std::move(newimpl)); + return std::errc::not_enough_memory; + } +} + +std::error_condition archive_file::open_7z(random_read::ptr &&file, ptr &result) noexcept +{ + // ensure we start with a nullptr result + result.reset(); + + // allocate memory for the zip_file structure + m7z_file_impl::ptr newimpl(new (std::nothrow) m7z_file_impl(std::move(file))); + if (!newimpl) + return std::errc::not_enough_memory; + auto const err = newimpl->initialize(); + if (err) + return err; + + // allocate the archive API wrapper + result.reset(new (std::nothrow) m7z_file_wrapper(std::move(newimpl))); + if (result) + { + return std::error_condition(); + } + else { m7z_file_impl::close(std::move(newimpl)); - return error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -566,7 +667,7 @@ archive_file::error archive_file::open_7z(const std::string &filename, ptr &resu cache and free all memory -------------------------------------------------*/ -void m7z_file_cache_clear() +void m7z_file_cache_clear() noexcept { // This is a trampoline called from unzip.cpp to avoid the need to have the zip and 7zip code in one file m7z_file_impl::cache_clear(); diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp index a275cc7df06..d7df8fa5ce9 100644 --- a/src/lib/util/unzip.cpp +++ b/src/lib/util/unzip.cpp @@ -12,9 +12,11 @@ #include "corestr.h" #include "hashing.h" +#include "ioprocs.h" +#include "timeconv.h" + #include "osdcore.h" #include "osdfile.h" -#include "timeconv.h" #include "lzma/C/LzmaDec.h" @@ -23,6 +25,7 @@ #include <algorithm> #include <array> #include <cassert> +#include <cerrno> #include <chrono> #include <cstring> #include <cstdlib> @@ -34,31 +37,55 @@ namespace util { + namespace { + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ +class archive_category_impl : public std::error_category +{ +public: + virtual char const *name() const noexcept override { return "archive"; } + + virtual std::string message(int condition) const override + { + using namespace std::literals; + static std::string_view const s_messages[] = { + "No error"sv, + "Bad archive signature"sv, + "Decompression error"sv, + "Archive file truncated"sv, + "Archive file corrupt"sv, + "Archive file uses unsupported features"sv, + "Buffer too small for data"sv }; + if ((0 <= condition) && (std::size(s_messages) > condition)) + return std::string(s_messages[condition]); + else + return "Unknown error"s; + } +}; + + class zip_file_impl { public: - typedef std::unique_ptr<zip_file_impl> ptr; - - zip_file_impl(const std::string &filename) - : m_filename(filename) - , m_file() - , m_length(0) - , m_ecd() - , m_cd() - , m_cd_pos(0) - , m_header() - , m_curr_is_dir(false) - , m_buffer() + using ptr = std::unique_ptr<zip_file_impl>; + + zip_file_impl(std::string &&filename) noexcept + : m_filename(std::move(filename)) { std::fill(m_buffer.begin(), m_buffer.end(), 0); } - static ptr find_cached(const std::string &filename) + zip_file_impl(random_read::ptr &&file) noexcept + : zip_file_impl(std::string()) + { + m_file = std::move(file); + } + + static ptr find_cached(std::string_view filename) noexcept { std::lock_guard<std::mutex> guard(s_cache_mutex); for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++) @@ -66,27 +93,31 @@ public: // if we have a valid entry and it matches our filename, use it and remove from the cache if (s_cache[cachenum] && (filename == s_cache[cachenum]->m_filename)) { + using std::swap; ptr result; - std::swap(s_cache[cachenum], result); + swap(s_cache[cachenum], result); osd_printf_verbose("unzip: found %s in cache\n", filename); return result; } } return ptr(); } - static void close(ptr &&zip); - static void cache_clear() + + static void close(ptr &&zip) noexcept; + + static void cache_clear() noexcept { // clear call cache entries std::lock_guard<std::mutex> guard(s_cache_mutex); - for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { } + for (auto &cached : s_cache) + cached.reset(); } - archive_file::error initialize() + std::error_condition initialize() noexcept { // read ecd data auto const ziperr = read_ecd(); - if (ziperr != archive_file::error::NONE) + if (ziperr) return ziperr; // verify that we can work with this zipfile (no disk spanning allowed) @@ -111,7 +142,7 @@ public: catch (...) { osd_printf_error("unzip: %s failed to allocate memory for central directory\n", m_filename); - return archive_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } // read the central directory @@ -119,13 +150,15 @@ public: std::size_t cd_offs(0); while (cd_remaining) { - std::uint32_t const chunk(std::uint32_t((std::min<std::uint64_t>)(std::numeric_limits<std::uint32_t>::max(), cd_remaining))); - std::uint32_t read_length(0); - auto const filerr = m_file->read(&m_cd[cd_offs], m_ecd.cd_start_disk_offset + cd_offs, chunk, read_length); - if (filerr != osd_file::error::NONE) + std::size_t const chunk(std::size_t(std::min<std::uint64_t>(std::numeric_limits<std::size_t>::max(), cd_remaining))); + std::size_t read_length(0); + std::error_condition const filerr = m_file->read_at(m_ecd.cd_start_disk_offset + cd_offs, &m_cd[cd_offs], chunk, read_length); + if (filerr) { - osd_printf_error("unzip: %s error reading central directory (%d)\n", m_filename, int(filerr)); - return archive_file::error::FILE_ERROR; + osd_printf_error( + "unzip: %s error reading central directory (%s:%d %s)\n", + m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } if (!read_length) { @@ -137,39 +170,45 @@ public: } osd_printf_verbose("unzip: read %s central directory\n", m_filename); - return archive_file::error::NONE; + return std::error_condition(); } - int first_file() + int first_file() noexcept { m_cd_pos = 0; - return search(0, std::string(), false, false, false); + return search(0, std::string_view(), false, false, false); } - int next_file() + + int next_file() noexcept { - return search(0, std::string(), false, false, false); + return search(0, std::string_view(), false, false, false); } - int search(std::uint32_t crc) + int search(std::uint32_t crc) noexcept { m_cd_pos = 0; - return search(crc, std::string(), true, false, false); + return search(crc, std::string_view(), true, false, false); } - int search(const std::string &filename, bool partialpath) + + int search(std::string_view filename, bool partialpath) noexcept { m_cd_pos = 0; return search(0, filename, false, true, partialpath); } - int search(std::uint32_t crc, const std::string &filename, bool partialpath) + + int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept { m_cd_pos = 0; return search(crc, filename, true, true, partialpath); } - bool current_is_directory() const { return m_curr_is_dir; } - const std::string ¤t_name() const { return m_header.file_name; } - std::uint64_t current_uncompressed_length() const { return m_header.uncompressed_length; } - std::chrono::system_clock::time_point current_last_modified() const + bool current_is_directory() const noexcept { return m_curr_is_dir; } + + const std::string ¤t_name() const noexcept { return m_header.file_name; } + + std::uint64_t current_uncompressed_length() const noexcept { return m_header.uncompressed_length; } + + std::chrono::system_clock::time_point current_last_modified() const noexcept { if (!m_header.modified_cached) { @@ -178,9 +217,10 @@ public: } return m_header.modified; } - std::uint32_t current_crc() const { return m_header.crc; } - archive_file::error decompress(void *buffer, std::uint32_t length); + std::uint32_t current_crc() const noexcept { return m_header.crc; } + + std::error_condition decompress(void *buffer, std::size_t length) noexcept; private: zip_file_impl(const zip_file_impl &) = delete; @@ -188,28 +228,43 @@ private: zip_file_impl &operator=(const zip_file_impl &) = delete; zip_file_impl &operator=(zip_file_impl &&) = delete; - int search(std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname, bool partialpath); + int search(std::uint32_t search_crc, std::string_view search_filename, bool matchcrc, bool matchname, bool partialpath) noexcept; - archive_file::error reopen() + std::error_condition reopen() noexcept { if (!m_file) { - auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length); - if (filerr != osd_file::error::NONE) + osd_file::ptr file; + auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, file, m_length); + if (filerr) { // this would spam every time it looks for a non-existent archive, which is a lot - //osd_printf_error("unzip: error reopening archive file %s (%d)\n", m_filename, int(filerr)); - return archive_file::error::FILE_ERROR; + //osd_printf_error("unzip: error reopening archive file %s (%s:%d %s)\n", m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } - else + m_file = osd_file_read(std::move(file)); + if (!m_file) { - osd_printf_verbose("unzip: opened archive file %s\n", m_filename); + osd_printf_error("unzip: not enough memory to open archive file %s\n", m_filename); + return std::errc::not_enough_memory; } + osd_printf_verbose("unzip: opened archive file %s\n", m_filename); } - return archive_file::error::NONE; + else if (!m_length) + { + auto const filerr = m_file->length(m_length); + if (filerr) + { + osd_printf_verbose( + "unzip: error getting length of archive file %s (%s:%d %s)\n", + m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; + } + } + return std::error_condition(); } - static std::chrono::system_clock::time_point decode_dos_time(std::uint16_t date, std::uint16_t time) + static std::chrono::system_clock::time_point decode_dos_time(std::uint16_t date, std::uint16_t time) noexcept { // FIXME: work out why this doesn't always work // negative tm_isdst should automatically determine whether DST is in effect for the date, @@ -228,16 +283,22 @@ private: } // ZIP file parsing - archive_file::error read_ecd(); - archive_file::error get_compressed_data_offset(std::uint64_t &offset); + std::error_condition read_ecd() noexcept; + std::error_condition get_compressed_data_offset(std::uint64_t &offset) noexcept; // decompression interfaces - archive_file::error decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length); - archive_file::error decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length); - archive_file::error decompress_data_type_14(std::uint64_t offset, void *buffer, std::uint32_t length); + std::error_condition decompress_data_type_0(std::uint64_t offset, void *buffer, std::size_t length) noexcept; + std::error_condition decompress_data_type_8(std::uint64_t offset, void *buffer, std::size_t length) noexcept; + std::error_condition decompress_data_type_14(std::uint64_t offset, void *buffer, std::size_t length) noexcept; struct file_header { + file_header() noexcept { } + file_header(file_header const &) = default; + file_header(file_header &&) noexcept = default; + file_header &operator=(file_header const &) = default; + file_header &operator=(file_header &&) noexcept = default; + std::uint16_t version_created; // version made by std::uint16_t version_needed; // version needed to extract std::uint16_t bit_flag; // general purpose bit flag @@ -271,15 +332,15 @@ private: static std::mutex s_cache_mutex; const std::string m_filename; // copy of ZIP filename (for caching) - osd_file::ptr m_file; // OSD file handle - std::uint64_t m_length; // length of zip file + random_read::ptr m_file; // file handle + std::uint64_t m_length = 0; // length of zip file ecd m_ecd; // end of central directory std::vector<std::uint8_t> m_cd; // central directory raw data - std::uint32_t m_cd_pos; // position in central directory + std::uint32_t m_cd_pos = 0; // position in central directory file_header m_header; // current file header - bool m_curr_is_dir; // current file is directory + bool m_curr_is_dir = false; // current file is directory std::array<std::uint8_t, DECOMPRESS_BUFSIZE> m_buffer; // buffer for decompression }; @@ -288,32 +349,32 @@ private: class zip_file_wrapper : public archive_file { public: - zip_file_wrapper(zip_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); } + zip_file_wrapper(zip_file_impl::ptr &&impl) noexcept : m_impl(std::move(impl)) { assert(m_impl); } virtual ~zip_file_wrapper() { zip_file_impl::close(std::move(m_impl)); } - virtual int first_file() override { return m_impl->first_file(); } - virtual int next_file() override { return m_impl->next_file(); } + virtual int first_file() noexcept override { return m_impl->first_file(); } + virtual int next_file() noexcept override { return m_impl->next_file(); } - virtual int search(std::uint32_t crc) override + virtual int search(std::uint32_t crc) noexcept override { return m_impl->search(crc); } - virtual int search(const std::string &filename, bool partialpath) override + virtual int search(std::string_view filename, bool partialpath) noexcept override { return m_impl->search(filename, partialpath); } - virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) override + virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept override { return m_impl->search(crc, filename, partialpath); } - virtual bool current_is_directory() const override { return m_impl->current_is_directory(); } - virtual const std::string ¤t_name() const override { return m_impl->current_name(); } - virtual std::uint64_t current_uncompressed_length() const override { return m_impl->current_uncompressed_length(); } - virtual std::chrono::system_clock::time_point current_last_modified() const override { return m_impl->current_last_modified(); } - virtual std::uint32_t current_crc() const override { return m_impl->current_crc(); } + virtual bool current_is_directory() const noexcept override { return m_impl->current_is_directory(); } + virtual const std::string ¤t_name() const noexcept override { return m_impl->current_name(); } + virtual std::uint64_t current_uncompressed_length() const noexcept override { return m_impl->current_uncompressed_length(); } + virtual std::chrono::system_clock::time_point current_last_modified() const noexcept override { return m_impl->current_last_modified(); } + virtual std::uint32_t current_crc() const noexcept override { return m_impl->current_crc(); } - virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); } + virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept override { return m_impl->decompress(buffer, length); } private: zip_file_impl::ptr m_impl; @@ -323,19 +384,19 @@ private: class reader_base { protected: - reader_base(void const *buf) : m_buffer(reinterpret_cast<std::uint8_t const *>(buf)) { } + reader_base(void const *buf) noexcept : m_buffer(reinterpret_cast<std::uint8_t const *>(buf)) { } - std::uint8_t read_byte(std::size_t offs) const + std::uint8_t read_byte(std::size_t offs) const noexcept { return m_buffer[offs]; } - std::uint16_t read_word(std::size_t offs) const + std::uint16_t read_word(std::size_t offs) const noexcept { return (std::uint16_t(m_buffer[offs + 1]) << 8) | (std::uint16_t(m_buffer[offs + 0]) << 0); } - std::uint32_t read_dword(std::size_t offs) const + std::uint32_t read_dword(std::size_t offs) const noexcept { return (std::uint32_t(m_buffer[offs + 3]) << 24) | @@ -343,7 +404,7 @@ protected: (std::uint32_t(m_buffer[offs + 1]) << 8) | (std::uint32_t(m_buffer[offs + 0]) << 0); } - std::uint64_t read_qword(std::size_t offs) const + std::uint64_t read_qword(std::size_t offs) const noexcept { return (std::uint64_t(m_buffer[offs + 7]) << 56) | @@ -371,17 +432,17 @@ protected: class extra_field_reader : private reader_base { public: - extra_field_reader(void const *buf, std::size_t len) : reader_base(buf), m_length(len) { } + extra_field_reader(void const *buf, std::size_t len) noexcept : reader_base(buf), m_length(len) { } - std::uint16_t header_id() const { return read_word(0x00); } - std::uint16_t data_size() const { return read_word(0x02); } - void const * data() const { return m_buffer + 0x04; } - extra_field_reader next() const { return extra_field_reader(m_buffer + total_length(), m_length - total_length()); } + std::uint16_t header_id() const noexcept { return read_word(0x00); } + std::uint16_t data_size() const noexcept { return read_word(0x02); } + void const * data() const noexcept { return m_buffer + 0x04; } + extra_field_reader next() const noexcept { return extra_field_reader(m_buffer + total_length(), m_length - total_length()); } - bool length_sufficient() const { return (m_length >= minimum_length()) && (m_length >= total_length()); } + bool length_sufficient() const noexcept { return (m_length >= minimum_length()) && (m_length >= total_length()); } - std::size_t total_length() const { return minimum_length() + data_size(); } - static std::size_t minimum_length() { return 0x04; } + std::size_t total_length() const noexcept { return minimum_length() + data_size(); } + static constexpr std::size_t minimum_length() { return 0x04; } private: std::size_t m_length; @@ -391,131 +452,131 @@ private: class local_file_header_reader : private reader_base { public: - local_file_header_reader(void const *buf) : reader_base(buf) { } - - std::uint32_t signature() const { return read_dword(0x00); } - std::uint8_t version_needed() const { return m_buffer[0x04]; } - std::uint8_t os_needed() const { return m_buffer[0x05]; } - std::uint16_t general_flag() const { return read_word(0x06); } - std::uint16_t compression_method() const { return read_word(0x08); } - std::uint16_t modified_time() const { return read_word(0x0a); } - std::uint16_t modified_date() const { return read_word(0x0c); } - std::uint32_t crc32() const { return read_dword(0x0e); } - std::uint32_t compressed_size() const { return read_dword(0x12); } - std::uint32_t uncompressed_size() const { return read_dword(0x16); } - std::uint16_t file_name_length() const { return read_word(0x1a); } - std::uint16_t extra_field_length() const { return read_word(0x1c); } + local_file_header_reader(void const *buf) noexcept : reader_base(buf) { } + + std::uint32_t signature() const noexcept { return read_dword(0x00); } + std::uint8_t version_needed() const noexcept { return m_buffer[0x04]; } + std::uint8_t os_needed() const noexcept { return m_buffer[0x05]; } + std::uint16_t general_flag() const noexcept { return read_word(0x06); } + std::uint16_t compression_method() const noexcept { return read_word(0x08); } + std::uint16_t modified_time() const noexcept { return read_word(0x0a); } + std::uint16_t modified_date() const noexcept { return read_word(0x0c); } + std::uint32_t crc32() const noexcept { return read_dword(0x0e); } + std::uint32_t compressed_size() const noexcept { return read_dword(0x12); } + std::uint32_t uncompressed_size() const noexcept { return read_dword(0x16); } + std::uint16_t file_name_length() const noexcept { return read_word(0x1a); } + std::uint16_t extra_field_length() const noexcept { return read_word(0x1c); } std::string file_name() const { return read_string(0x1e, file_name_length()); } void file_name(std::string &result) const { read_string(result, 0x1e, file_name_length()); } - extra_field_reader extra_field() const { return extra_field_reader(m_buffer + 0x1e + file_name_length(), extra_field_length()); } + extra_field_reader extra_field() const noexcept { return extra_field_reader(m_buffer + 0x1e + file_name_length(), extra_field_length()); } - bool signature_correct() const { return signature() == 0x04034b50; } + bool signature_correct() const noexcept { return signature() == 0x04034b50; } - std::size_t total_length() const { return minimum_length() + file_name_length() + extra_field_length(); } - static std::size_t minimum_length() { return 0x1e; } + std::size_t total_length() const noexcept { return minimum_length() + file_name_length() + extra_field_length(); } + static constexpr std::size_t minimum_length() { return 0x1e; } }; class central_dir_entry_reader : private reader_base { public: - central_dir_entry_reader(void const *buf) : reader_base(buf) { } - - std::uint32_t signature() const { return read_dword(0x00); } - std::uint8_t version_created() const { return m_buffer[0x04]; } - std::uint8_t os_created() const { return m_buffer[0x05]; } - std::uint8_t version_needed() const { return m_buffer[0x06]; } - std::uint8_t os_needed() const { return m_buffer[0x07]; } - std::uint16_t general_flag() const { return read_word(0x08); } - std::uint16_t compression_method() const { return read_word(0x0a); } - std::uint16_t modified_time() const { return read_word(0x0c); } - std::uint16_t modified_date() const { return read_word(0x0e); } - std::uint32_t crc32() const { return read_dword(0x10); } - std::uint32_t compressed_size() const { return read_dword(0x14); } - std::uint32_t uncompressed_size() const { return read_dword(0x18); } - std::uint16_t file_name_length() const { return read_word(0x1c); } - std::uint16_t extra_field_length() const { return read_word(0x1e); } - std::uint16_t file_comment_length() const { return read_word(0x20); } - std::uint16_t start_disk() const { return read_word(0x22); } - std::uint16_t int_file_attr() const { return read_word(0x24); } - std::uint32_t ext_file_attr() const { return read_dword(0x26); } - std::uint32_t header_offset() const { return read_dword(0x2a); } + central_dir_entry_reader(void const *buf) noexcept : reader_base(buf) { } + + std::uint32_t signature() const noexcept { return read_dword(0x00); } + std::uint8_t version_created() const noexcept { return m_buffer[0x04]; } + std::uint8_t os_created() const noexcept { return m_buffer[0x05]; } + std::uint8_t version_needed() const noexcept { return m_buffer[0x06]; } + std::uint8_t os_needed() const noexcept { return m_buffer[0x07]; } + std::uint16_t general_flag() const noexcept { return read_word(0x08); } + std::uint16_t compression_method() const noexcept { return read_word(0x0a); } + std::uint16_t modified_time() const noexcept { return read_word(0x0c); } + std::uint16_t modified_date() const noexcept { return read_word(0x0e); } + std::uint32_t crc32() const noexcept { return read_dword(0x10); } + std::uint32_t compressed_size() const noexcept { return read_dword(0x14); } + std::uint32_t uncompressed_size() const noexcept { return read_dword(0x18); } + std::uint16_t file_name_length() const noexcept { return read_word(0x1c); } + std::uint16_t extra_field_length() const noexcept { return read_word(0x1e); } + std::uint16_t file_comment_length() const noexcept { return read_word(0x20); } + std::uint16_t start_disk() const noexcept { return read_word(0x22); } + std::uint16_t int_file_attr() const noexcept { return read_word(0x24); } + std::uint32_t ext_file_attr() const noexcept { return read_dword(0x26); } + std::uint32_t header_offset() const noexcept { return read_dword(0x2a); } std::string file_name() const { return read_string(0x2e, file_name_length()); } void file_name(std::string &result) const { read_string(result, 0x2e, file_name_length()); } - extra_field_reader extra_field() const { return extra_field_reader(m_buffer + 0x2e + file_name_length(), extra_field_length()); } + extra_field_reader extra_field() const noexcept { return extra_field_reader(m_buffer + 0x2e + file_name_length(), extra_field_length()); } std::string file_comment() const { return read_string(0x2e + file_name_length() + extra_field_length(), file_comment_length()); } void file_comment(std::string &result) const { read_string(result, 0x2e + file_name_length() + extra_field_length(), file_comment_length()); } - bool signature_correct() const { return signature() == 0x02014b50; } + bool signature_correct() const noexcept { return signature() == 0x02014b50; } - std::size_t total_length() const { return minimum_length() + file_name_length() + extra_field_length() + file_comment_length(); } - static std::size_t minimum_length() { return 0x2e; } + std::size_t total_length() const noexcept { return minimum_length() + file_name_length() + extra_field_length() + file_comment_length(); } + static constexpr std::size_t minimum_length() { return 0x2e; } }; class ecd64_reader : private reader_base { public: - ecd64_reader(void const *buf) : reader_base(buf) { } - - std::uint32_t signature() const { return read_dword(0x00); } - std::uint64_t ecd64_size() const { return read_qword(0x04); } - std::uint8_t version_created() const { return m_buffer[0x0c]; } - std::uint8_t os_created() const { return m_buffer[0x0d]; } - std::uint8_t version_needed() const { return m_buffer[0x0e]; } - std::uint8_t os_needed() const { return m_buffer[0x0f]; } - std::uint32_t this_disk_no() const { return read_dword(0x10); } - std::uint32_t dir_start_disk() const { return read_dword(0x14); } - std::uint64_t dir_disk_entries() const { return read_qword(0x18); } - std::uint64_t dir_total_entries() const { return read_qword(0x20); } - std::uint64_t dir_size() const { return read_qword(0x28); } - std::uint64_t dir_offset() const { return read_qword(0x30); } - void const * extensible_data() const { return m_buffer + 0x38; } - - bool signature_correct() const { return signature() == 0x06064b50; } - - std::size_t total_length() const { return 0x0c + ecd64_size(); } - static std::size_t minimum_length() { return 0x38; } + ecd64_reader(void const *buf) noexcept : reader_base(buf) { } + + std::uint32_t signature() const noexcept { return read_dword(0x00); } + std::uint64_t ecd64_size() const noexcept { return read_qword(0x04); } + std::uint8_t version_created() const noexcept { return m_buffer[0x0c]; } + std::uint8_t os_created() const noexcept { return m_buffer[0x0d]; } + std::uint8_t version_needed() const noexcept { return m_buffer[0x0e]; } + std::uint8_t os_needed() const noexcept { return m_buffer[0x0f]; } + std::uint32_t this_disk_no() const noexcept { return read_dword(0x10); } + std::uint32_t dir_start_disk() const noexcept { return read_dword(0x14); } + std::uint64_t dir_disk_entries() const noexcept { return read_qword(0x18); } + std::uint64_t dir_total_entries() const noexcept { return read_qword(0x20); } + std::uint64_t dir_size() const noexcept { return read_qword(0x28); } + std::uint64_t dir_offset() const noexcept { return read_qword(0x30); } + void const * extensible_data() const noexcept { return m_buffer + 0x38; } + + bool signature_correct() const noexcept { return signature() == 0x06064b50; } + + std::size_t total_length() const noexcept { return 0x0c + ecd64_size(); } + static constexpr std::size_t minimum_length() { return 0x38; } }; class ecd64_locator_reader : private reader_base { public: - ecd64_locator_reader(void const *buf) : reader_base(buf) { } + ecd64_locator_reader(void const *buf) noexcept : reader_base(buf) { } - std::uint32_t signature() const { return read_dword(0x00); } - std::uint32_t ecd64_disk() const { return read_dword(0x04); } - std::uint64_t ecd64_offset() const { return read_qword(0x08); } - std::uint32_t total_disks() const { return read_dword(0x10); } + std::uint32_t signature() const noexcept { return read_dword(0x00); } + std::uint32_t ecd64_disk() const noexcept { return read_dword(0x04); } + std::uint64_t ecd64_offset() const noexcept { return read_qword(0x08); } + std::uint32_t total_disks() const noexcept { return read_dword(0x10); } - bool signature_correct() const { return signature() == 0x07064b50; } + bool signature_correct() const noexcept { return signature() == 0x07064b50; } - std::size_t total_length() const { return minimum_length(); } - static std::size_t minimum_length() { return 0x14; } + std::size_t total_length() const noexcept { return minimum_length(); } + static constexpr std::size_t minimum_length() { return 0x14; } }; class ecd_reader : private reader_base { public: - ecd_reader(void const *buf) : reader_base(buf) { } - - std::uint32_t signature() const { return read_dword(0x00); } - std::uint16_t this_disk_no() const { return read_word(0x04); } - std::uint16_t dir_start_disk() const { return read_word(0x06); } - std::uint16_t dir_disk_entries() const { return read_word(0x08); } - std::uint16_t dir_total_entries() const { return read_word(0x0a); } - std::uint32_t dir_size() const { return read_dword(0x0c); } - std::uint32_t dir_offset() const { return read_dword(0x10); } - std::uint16_t comment_length() const { return read_word(0x14); } + ecd_reader(void const *buf) noexcept : reader_base(buf) { } + + std::uint32_t signature() const noexcept { return read_dword(0x00); } + std::uint16_t this_disk_no() const noexcept { return read_word(0x04); } + std::uint16_t dir_start_disk() const noexcept { return read_word(0x06); } + std::uint16_t dir_disk_entries() const noexcept { return read_word(0x08); } + std::uint16_t dir_total_entries() const noexcept { return read_word(0x0a); } + std::uint32_t dir_size() const noexcept { return read_dword(0x0c); } + std::uint32_t dir_offset() const noexcept { return read_dword(0x10); } + std::uint16_t comment_length() const noexcept { return read_word(0x14); } std::string comment() const { return read_string(0x16, comment_length()); } void comment(std::string &result) const { read_string(result, 0x16, comment_length()); } - bool signature_correct() const { return signature() == 0x06054b50; } + bool signature_correct() const noexcept { return signature() == 0x06054b50; } - std::size_t total_length() const { return minimum_length() + comment_length(); } - static std::size_t minimum_length() { return 0x16; } + std::size_t total_length() const noexcept { return minimum_length() + comment_length(); } + static constexpr std::size_t minimum_length() { return 0x16; } }; @@ -525,7 +586,7 @@ public: template <typename T> zip64_ext_info_reader( T const &header, - extra_field_reader const &field) + extra_field_reader const &field) noexcept : reader_base(field.data()) , m_uncompressed_size(header.uncompressed_size()) , m_compressed_size(header.compressed_size()) @@ -538,13 +599,13 @@ public: { } - std::uint64_t uncompressed_size() const { return ~m_uncompressed_size ? m_uncompressed_size : read_qword(0x00); } - std::uint64_t compressed_size() const { return ~m_compressed_size ? m_compressed_size : read_qword(m_offs_compressed_size); } - std::uint64_t header_offset() const { return ~m_header_offset ? m_header_offset : read_qword(m_offs_header_offset); } - std::uint32_t start_disk() const { return ~m_start_disk ? m_start_disk : read_dword(m_offs_start_disk); } + std::uint64_t uncompressed_size() const noexcept { return ~m_uncompressed_size ? m_uncompressed_size : read_qword(0x00); } + std::uint64_t compressed_size() const noexcept { return ~m_compressed_size ? m_compressed_size : read_qword(m_offs_compressed_size); } + std::uint64_t header_offset() const noexcept { return ~m_header_offset ? m_header_offset : read_qword(m_offs_header_offset); } + std::uint32_t start_disk() const noexcept { return ~m_start_disk ? m_start_disk : read_dword(m_offs_start_disk); } - std::size_t total_length() const { return minimum_length() + m_offs_end; } - static std::size_t minimum_length() { return 0x00; } + std::size_t total_length() const noexcept { return minimum_length() + m_offs_end; } + static constexpr std::size_t minimum_length() { return 0x00; } private: std::uint32_t m_uncompressed_size; @@ -562,15 +623,15 @@ private: class utf8_path_reader : private reader_base { public: - utf8_path_reader(extra_field_reader const &field) : reader_base(field.data()), m_length(field.data_size()) { } + utf8_path_reader(extra_field_reader const &field) noexcept : reader_base(field.data()), m_length(field.data_size()) { } - std::uint8_t version() const { return m_buffer[0]; } - std::uint32_t name_crc32() const { return read_dword(0x01); } + std::uint8_t version() const noexcept { return m_buffer[0]; } + std::uint32_t name_crc32() const noexcept { return read_dword(0x01); } std::string unicode_name() const { return read_string(0x05, m_length - 0x05); } void unicode_name(std::string &result) const { return read_string(result, 0x05, m_length - 0x05); } - std::size_t total_length() const { return m_length; } - static std::size_t minimum_length() { return 0x05; } + std::size_t total_length() const noexcept { return m_length; } + static constexpr std::size_t minimum_length() { return 0x05; } private: std::size_t m_length; @@ -580,17 +641,17 @@ private: class ntfs_tag_reader : private reader_base { public: - ntfs_tag_reader(void const *buf, std::size_t len) : reader_base(buf), m_length(len) { } + ntfs_tag_reader(void const *buf, std::size_t len) noexcept : reader_base(buf), m_length(len) { } - std::uint16_t tag() const { return read_word(0x00); } - std::uint16_t size() const { return read_word(0x02); } - void const * data() const { return m_buffer + 0x04; } - ntfs_tag_reader next() const { return ntfs_tag_reader(m_buffer + total_length(), m_length - total_length()); } + std::uint16_t tag() const noexcept { return read_word(0x00); } + std::uint16_t size() const noexcept { return read_word(0x02); } + void const * data() const noexcept { return m_buffer + 0x04; } + ntfs_tag_reader next() const noexcept { return ntfs_tag_reader(m_buffer + total_length(), m_length - total_length()); } - bool length_sufficient() const { return (m_length >= minimum_length()) && (m_length >= total_length()); } + bool length_sufficient() const noexcept { return (m_length >= minimum_length()) && (m_length >= total_length()); } - std::size_t total_length() const { return minimum_length() + size(); } - static std::size_t minimum_length() { return 0x04; } + std::size_t total_length() const noexcept { return minimum_length() + size(); } + static constexpr std::size_t minimum_length() { return 0x04; } private: std::size_t m_length; @@ -600,13 +661,13 @@ private: class ntfs_reader : private reader_base { public: - ntfs_reader(extra_field_reader const &field) : reader_base(field.data()), m_length(field.data_size()) { } + ntfs_reader(extra_field_reader const &field) noexcept : reader_base(field.data()), m_length(field.data_size()) { } - std::uint32_t reserved() const { return read_dword(0x00); } - ntfs_tag_reader tag1() const { return ntfs_tag_reader(m_buffer + 0x04, m_length - 4); } + std::uint32_t reserved() const noexcept { return read_dword(0x00); } + ntfs_tag_reader tag1() const noexcept { return ntfs_tag_reader(m_buffer + 0x04, m_length - 4); } - std::size_t total_length() const { return m_length; } - static std::size_t minimum_length() { return 0x08; } + std::size_t total_length() const noexcept { return m_length; } + static constexpr std::size_t minimum_length() { return 0x08; } private: std::size_t m_length; @@ -616,14 +677,14 @@ private: class ntfs_times_reader : private reader_base { public: - ntfs_times_reader(ntfs_tag_reader const &tag) : reader_base(tag.data()) { } + ntfs_times_reader(ntfs_tag_reader const &tag) noexcept : reader_base(tag.data()) { } - std::uint64_t mtime() const { return read_qword(0x00); } - std::uint64_t atime() const { return read_qword(0x08); } - std::uint64_t ctime() const { return read_qword(0x10); } + std::uint64_t mtime() const noexcept { return read_qword(0x00); } + std::uint64_t atime() const noexcept { return read_qword(0x08); } + std::uint64_t ctime() const noexcept { return read_qword(0x10); } - std::size_t total_length() const { return minimum_length(); } - static std::size_t minimum_length() { return 0x18; } + std::size_t total_length() const noexcept { return minimum_length(); } + static constexpr std::size_t minimum_length() { return 0x18; } }; @@ -632,16 +693,16 @@ class general_flag_reader public: general_flag_reader(std::uint16_t val) : m_value(val) { } - bool encrypted() const { return bool(m_value & 0x0001); } - bool implode_8k_dict() const { return bool(m_value & 0x0002); } - bool implode_3_trees() const { return bool(m_value & 0x0004); } - unsigned deflate_option() const { return unsigned((m_value >> 1) & 0x0003); } - bool lzma_eos_mark() const { return bool(m_value & 0x0002); } - bool use_descriptor() const { return bool(m_value & 0x0008); } - bool patch_data() const { return bool(m_value & 0x0020); } - bool strong_encryption() const { return bool(m_value & 0x0040); } - bool utf8_encoding() const { return bool(m_value & 0x0800); } - bool directory_encryption() const { return bool(m_value & 0x2000); } + bool encrypted() const noexcept { return bool(m_value & 0x0001); } + bool implode_8k_dict() const noexcept { return bool(m_value & 0x0002); } + bool implode_3_trees() const noexcept { return bool(m_value & 0x0004); } + unsigned deflate_option() const noexcept { return unsigned((m_value >> 1) & 0x0003); } + bool lzma_eos_mark() const noexcept { return bool(m_value & 0x0002); } + bool use_descriptor() const noexcept { return bool(m_value & 0x0008); } + bool patch_data() const noexcept { return bool(m_value & 0x0020); } + bool strong_encryption() const noexcept { return bool(m_value & 0x0040); } + bool utf8_encoding() const noexcept { return bool(m_value & 0x0800); } + bool directory_encryption() const noexcept { return bool(m_value & 0x2000); } private: std::uint16_t m_value; @@ -653,6 +714,8 @@ private: GLOBAL VARIABLES ***************************************************************************/ +archive_category_impl const f_archive_category_instance; + std::array<zip_file_impl::ptr, zip_file_impl::CACHE_SIZE> zip_file_impl::s_cache; std::mutex zip_file_impl::s_cache_mutex; @@ -663,33 +726,38 @@ std::mutex zip_file_impl::s_cache_mutex; to the cache -------------------------------------------------*/ -void zip_file_impl::close(ptr &&zip) +void zip_file_impl::close(ptr &&zip) noexcept { - if (!zip) return; + // if the filename isn't empty, the cached directory can be reused + if (zip && !zip->m_filename.empty()) + { + // close the open files + osd_printf_verbose("unzip: closing archive file %s and sending to cache\n", zip->m_filename); + zip->m_file.reset(); - // close the open files - osd_printf_verbose("unzip: closing archive file %s and sending to cache\n", zip->m_filename); - zip->m_file.reset(); + // find the first nullptr entry in the cache + std::lock_guard<std::mutex> guard(s_cache_mutex); + std::size_t cachenum; + for (cachenum = 0; cachenum < s_cache.size(); cachenum++) + if (!s_cache[cachenum]) + break; - // find the first nullptr entry in the cache - std::lock_guard<std::mutex> guard(s_cache_mutex); - std::size_t cachenum; - for (cachenum = 0; cachenum < s_cache.size(); cachenum++) - if (!s_cache[cachenum]) - break; + // if no room left in the cache, free the bottommost entry + if (cachenum == s_cache.size()) + { + cachenum--; + osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename); + s_cache[cachenum].reset(); + } - // if no room left in the cache, free the bottommost entry - if (cachenum == s_cache.size()) - { - cachenum--; - osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename); - s_cache[cachenum].reset(); + // move everyone else down and place us at the top + for ( ; cachenum > 0; cachenum--) + s_cache[cachenum] = std::move(s_cache[cachenum - 1]); + s_cache[0] = std::move(zip); } - // move everyone else down and place us at the top - for ( ; cachenum > 0; cachenum--) - s_cache[cachenum] = std::move(s_cache[cachenum - 1]); - s_cache[0] = std::move(zip); + // make sure it's cleaned up + zip.reset(); } @@ -702,7 +770,7 @@ void zip_file_impl::close(ptr &&zip) entry in the ZIP -------------------------------------------------*/ -int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_filename, bool matchcrc, bool matchname, bool partialpath) +int zip_file_impl::search(std::uint32_t search_crc, std::string_view search_filename, bool matchcrc, bool matchname, bool partialpath) noexcept { // if we're at or past the end, we're done while ((m_cd_pos + central_dir_entry_reader::minimum_length()) <= m_ecd.cd_size) @@ -712,98 +780,123 @@ int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_fi if (!reader.signature_correct() || ((m_cd_pos + reader.total_length()) > m_ecd.cd_size)) break; - // extract file header info - m_header.version_created = reader.version_created(); - m_header.version_needed = reader.version_needed(); - m_header.bit_flag = reader.general_flag(); - m_header.compression = reader.compression_method(); - m_header.crc = reader.crc32(); - m_header.compressed_length = reader.compressed_size(); - m_header.uncompressed_length = reader.uncompressed_size(); - m_header.start_disk_number = reader.start_disk(); - m_header.local_header_offset = reader.header_offset(); - - // don't immediately decode DOS timestamp - it's expensive - m_header.modified_date = reader.modified_date(); - m_header.modified_time = reader.modified_time(); - m_header.modified_cached = false; - - // advance the position - m_cd_pos += reader.total_length(); - - // copy the filename - bool is_utf8(general_flag_reader(m_header.bit_flag).utf8_encoding()); - reader.file_name(m_header.file_name); - - // walk the extra data - for (auto extra = reader.extra_field(); extra.length_sufficient(); extra = extra.next()) + // setting std::string can raise allocation exceptions + try { - // look for ZIP64 extended info - if ((extra.header_id() == 0x0001) && (extra.data_size() >= zip64_ext_info_reader::minimum_length())) + // extract file header info + file_header header; + header.version_created = reader.version_created(); + header.version_needed = reader.version_needed(); + header.bit_flag = reader.general_flag(); + header.compression = reader.compression_method(); + header.crc = reader.crc32(); + header.compressed_length = reader.compressed_size(); + header.uncompressed_length = reader.uncompressed_size(); + header.start_disk_number = reader.start_disk(); + header.local_header_offset = reader.header_offset(); + + // don't immediately decode DOS timestamp - it's expensive + header.modified_date = reader.modified_date(); + header.modified_time = reader.modified_time(); + header.modified_cached = false; + + // copy the filename + bool is_utf8(general_flag_reader(header.bit_flag).utf8_encoding()); + reader.file_name(header.file_name); + + // walk the extra data + for (auto extra = reader.extra_field(); extra.length_sufficient(); extra = extra.next()) { - zip64_ext_info_reader const ext64(reader, extra); - if (extra.data_size() >= ext64.total_length()) + // look for ZIP64 extended info + if ((extra.header_id() == 0x0001) && (extra.data_size() >= zip64_ext_info_reader::minimum_length())) { - m_header.compressed_length = ext64.compressed_size(); - m_header.uncompressed_length = ext64.uncompressed_size(); - m_header.start_disk_number = ext64.start_disk(); - m_header.local_header_offset = ext64.header_offset(); + zip64_ext_info_reader const ext64(reader, extra); + if (extra.data_size() >= ext64.total_length()) + { + header.compressed_length = ext64.compressed_size(); + header.uncompressed_length = ext64.uncompressed_size(); + header.start_disk_number = ext64.start_disk(); + header.local_header_offset = ext64.header_offset(); + } } - } - // look for Info-ZIP UTF-8 path - if (!is_utf8 && (extra.header_id() == 0x7075) && (extra.data_size() >= utf8_path_reader::minimum_length())) - { - utf8_path_reader const utf8path(extra); - if (utf8path.version() == 1) + // look for Info-ZIP UTF-8 path + if (!is_utf8 && (extra.header_id() == 0x7075) && (extra.data_size() >= utf8_path_reader::minimum_length())) { - auto const addr(m_header.file_name.empty() ? nullptr : &m_header.file_name[0]); - auto const length(m_header.file_name.empty() ? 0 : m_header.file_name.length() * sizeof(m_header.file_name[0])); - auto const crc(crc32_creator::simple(addr, length)); - if (utf8path.name_crc32() == crc.m_raw) + utf8_path_reader const utf8path(extra); + if (utf8path.version() == 1) { - utf8path.unicode_name(m_header.file_name); - is_utf8 = true; + auto const addr(header.file_name.empty() ? nullptr : &header.file_name[0]); + auto const length(header.file_name.empty() ? 0 : header.file_name.length() * sizeof(header.file_name[0])); + auto const crc(crc32_creator::simple(addr, length)); + if (utf8path.name_crc32() == crc.m_raw) + { + utf8path.unicode_name(header.file_name); + is_utf8 = true; + } } } - } - // look for NTFS extra field - if ((extra.header_id() == 0x000a) && (extra.data_size() >= ntfs_reader::minimum_length())) - { - ntfs_reader const ntfs(extra); - for (auto tag = ntfs.tag1(); tag.length_sufficient(); tag = tag.next()) + // look for NTFS extra field + if ((extra.header_id() == 0x000a) && (extra.data_size() >= ntfs_reader::minimum_length())) { - if ((tag.tag() == 0x0001) && (tag.size() >= ntfs_times_reader::minimum_length())) + ntfs_reader const ntfs(extra); + for (auto tag = ntfs.tag1(); tag.length_sufficient(); tag = tag.next()) { - ntfs_times_reader const times(tag); - ntfs_duration const ticks(times.mtime()); - m_header.modified = system_clock_time_point_from_ntfs_duration(ticks); - m_header.modified_cached = true; + if ((tag.tag() == 0x0001) && (tag.size() >= ntfs_times_reader::minimum_length())) + { + ntfs_times_reader const times(tag); + ntfs_duration const ticks(times.mtime()); + try + { + header.modified = system_clock_time_point_from_ntfs_duration(ticks); + header.modified_cached = true; + } + catch (...) + { + // out-of-range exception - let it fall back to DOS-style timestamp + } + } } } } - } - // FIXME: if (!is_utf8) convert filename to UTF8 (assume CP437 or something) + // FIXME: if (!is_utf8) convert filename to UTF8 (assume CP437 or something) - // chop off trailing slash for directory entries - bool const is_dir(!m_header.file_name.empty() && (m_header.file_name.back() == '/')); - if (is_dir) m_header.file_name.resize(m_header.file_name.length() - 1); + // chop off trailing slash for directory entries + bool const is_dir(!header.file_name.empty() && (header.file_name.back() == '/')); + if (is_dir) + header.file_name.resize(header.file_name.length() - 1); - // check to see if it matches query - bool const crcmatch(search_crc == m_header.crc); - auto const partialoffset(m_header.file_name.length() - search_filename.length()); - bool const partialpossible((m_header.file_name.length() > search_filename.length()) && (m_header.file_name[partialoffset - 1] == '/')); - const bool namematch( - !core_stricmp(search_filename.c_str(), m_header.file_name.c_str()) || - (partialpath && partialpossible && !core_stricmp(search_filename.c_str(), m_header.file_name.c_str() + partialoffset))); + // advance the position + m_header = std::move(header); + m_curr_is_dir = is_dir; + m_cd_pos += reader.total_length(); + } + catch (...) + { + break; + } - bool const found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch); - if (found) + // quick return if not required to match on name + if (!matchname) { - m_curr_is_dir = is_dir; - return 0; + if (!matchcrc || ((search_crc == m_header.crc) && !m_curr_is_dir)) + return 0; + } + else if (!m_curr_is_dir) + { + // check to see if it matches query + auto const partialoffset = m_header.file_name.length() - search_filename.length(); + const bool namematch = + (search_filename.length() == m_header.file_name.length()) && + (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_header.file_name[0], search_filename.length())); + bool const partialmatch = + partialpath && + ((m_header.file_name.length() > search_filename.length()) && (m_header.file_name[partialoffset - 1] == '/')) && + (search_filename.empty() || !core_strnicmp(&search_filename[0], &m_header.file_name[partialoffset], search_filename.length())); + if ((!matchcrc || (search_crc == m_header.crc)) && (namematch || partialmatch)) + return 0; } } return -1; @@ -815,11 +908,8 @@ int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_fi from a ZIP into the target buffer -------------------------------------------------*/ -archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length) +std::error_condition zip_file_impl::decompress(void *buffer, std::size_t length) noexcept { - archive_file::error ziperr; - std::uint64_t offset; - // if we don't have enough buffer, error if (length < m_header.uncompressed_length) { @@ -835,33 +925,29 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length } // get the compressed data offset - ziperr = get_compressed_data_offset(offset); - if (ziperr != archive_file::error::NONE) + std::uint64_t offset; + auto const ziperr = get_compressed_data_offset(offset); + if (ziperr) return ziperr; // handle compression types switch (m_header.compression) { case 0: - ziperr = decompress_data_type_0(offset, buffer, length); - break; + return decompress_data_type_0(offset, buffer, length); case 8: - ziperr = decompress_data_type_8(offset, buffer, length); - break; + return decompress_data_type_8(offset, buffer, length); case 14: - ziperr = decompress_data_type_14(offset, buffer, length); - break; + return decompress_data_type_14(offset, buffer, length); default: osd_printf_error( "unzip: %s in %s uses unsupported compression method %u\n", - m_header.file_name.c_str(), m_filename.c_str(), m_header.compression); - ziperr = archive_file::error::UNSUPPORTED; - break; + m_header.file_name, m_filename, m_header.compression); + return archive_file::error::UNSUPPORTED; } - return ziperr; } @@ -874,16 +960,16 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length read_ecd - read the ECD data -------------------------------------------------*/ -archive_file::error zip_file_impl::read_ecd() +std::error_condition zip_file_impl::read_ecd() noexcept { // make sure the file handle is open auto const ziperr = reopen(); - if (ziperr != archive_file::error::NONE) + if (ziperr) return ziperr; // we may need multiple tries - osd_file::error error; - std::uint32_t read_length; + std::error_condition filerr; + std::size_t read_length; std::uint32_t buflen = 1024; while (buflen < 65536) { @@ -893,19 +979,26 @@ archive_file::error zip_file_impl::read_ecd() // allocate buffer std::unique_ptr<std::uint8_t []> buffer; - try { buffer.reset(new std::uint8_t[buflen + 1]); } - catch (...) + buffer.reset(new (std::nothrow) std::uint8_t[buflen + 1]); + if (!buffer) { osd_printf_error("unzip: %s failed to allocate memory for ECD search\n", m_filename); - return archive_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } // read in one buffers' worth of data - error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length); - if ((error != osd_file::error::NONE) || (read_length != buflen)) + filerr = m_file->read_at(m_length - buflen, &buffer[0], buflen, read_length); + if (filerr) + { + osd_printf_error( + "unzip: error reading %s to search for ECD (%s:%d %s)\n", + m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; + } + if (read_length != buflen) { - osd_printf_error("unzip: %s error reading file to search for ECD (%d)\n", m_filename, int(error)); - return archive_file::error::FILE_ERROR; + osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s to search for ECD\n", m_filename); + return archive_file::error::FILE_TRUNCATED; } // find the ECD signature @@ -935,19 +1028,26 @@ archive_file::error zip_file_impl::read_ecd() if ((m_length - buflen + offset) < ecd64_locator_reader::minimum_length()) { osd_printf_verbose("unzip: %s too small to contain ZIP64 ECD locator\n", m_filename); - return archive_file::error::NONE; + return std::error_condition(); } // try to read the ZIP64 ECD locator - error = m_file->read( - &buffer[0], + filerr = m_file->read_at( m_length - buflen + offset - ecd64_locator_reader::minimum_length(), + &buffer[0], ecd64_locator_reader::minimum_length(), read_length); - if ((error != osd_file::error::NONE) || (read_length != ecd64_locator_reader::minimum_length())) + if (filerr) + { + osd_printf_error( + "unzip: error reading %s to search for ZIP64 ECD locator (%s:%d %s)\n", + m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; + } + if (read_length != ecd64_locator_reader::minimum_length()) { - osd_printf_error("unzip: error reading %s to search for ZIP64 ECD locator (%d)\n", m_filename, int(error)); - return archive_file::error::FILE_ERROR; + osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s to search for ZIP64 ECD locator\n", m_filename); + return archive_file::error::FILE_TRUNCATED; } // if the signature isn't correct, it's not a ZIP64 archive @@ -955,7 +1055,7 @@ archive_file::error zip_file_impl::read_ecd() if (!ecd64_loc_rd.signature_correct()) { osd_printf_verbose("unzip: %s has no ZIP64 ECD locator\n", m_filename); - return archive_file::error::NONE; + return std::error_condition(); } // check that the ZIP64 ECD is in this segment (assuming this segment is the last segment) @@ -966,11 +1066,13 @@ archive_file::error zip_file_impl::read_ecd() } // try to read the ZIP64 ECD - error = m_file->read(&buffer[0], ecd64_loc_rd.ecd64_offset(), ecd64_reader::minimum_length(), read_length); - if (error != osd_file::error::NONE) + filerr = m_file->read_at(ecd64_loc_rd.ecd64_offset(), &buffer[0], ecd64_reader::minimum_length(), read_length); + if (filerr) { - osd_printf_error("unzip: error reading %s ZIP64 ECD (%d)\n", m_filename, int(error)); - return archive_file::error::FILE_ERROR; + osd_printf_error( + "unzip: error reading %s ZIP64 ECD (%s:%d %s)\n", + m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } if (read_length != ecd64_reader::minimum_length()) { @@ -983,8 +1085,8 @@ archive_file::error zip_file_impl::read_ecd() if (!ecd64_rd.signature_correct()) { osd_printf_error( - "unzip: %s ZIP64 ECD has incorrect signature 0x%08lx\n", - m_filename.c_str(), long(ecd64_rd.signature())); + "unzip: %s ZIP64 ECD has incorrect signature 0x%08x\n", + m_filename, ecd64_rd.signature()); return archive_file::error::BAD_SIGNATURE; } if (ecd64_rd.total_length() < ecd64_reader::minimum_length()) @@ -996,7 +1098,7 @@ archive_file::error zip_file_impl::read_ecd() { osd_printf_error( "unzip: %s ZIP64 ECD requires unsupported version %u.%u\n", - m_filename.c_str(), unsigned(ecd64_rd.version_needed() / 10), unsigned(ecd64_rd.version_needed() % 10)); + m_filename, ecd64_rd.version_needed() / 10, ecd64_rd.version_needed() % 10); return archive_file::error::UNSUPPORTED; } osd_printf_verbose("unzip: found %s ZIP64 ECD\n", m_filename); @@ -1009,7 +1111,7 @@ archive_file::error zip_file_impl::read_ecd() m_ecd.cd_size = ecd64_rd.dir_size(); m_ecd.cd_start_disk_offset = ecd64_rd.dir_offset(); - return archive_file::error::NONE; + return std::error_condition(); } // didn't find it; free this buffer and expand our search @@ -1024,7 +1126,7 @@ archive_file::error zip_file_impl::read_ecd() } } osd_printf_error("unzip: %s couldn't find ECD in last 64KiB\n", m_filename); - return archive_file::error::OUT_OF_MEMORY; + return archive_file::error::UNSUPPORTED; // TODO: revisit this error code } @@ -1033,7 +1135,7 @@ archive_file::error zip_file_impl::read_ecd() offset of the compressed data -------------------------------------------------*/ -archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &offset) +std::error_condition zip_file_impl::get_compressed_data_offset(std::uint64_t &offset) noexcept { // don't support a number of features general_flag_reader const flags(m_header.bit_flag); @@ -1046,7 +1148,7 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off { osd_printf_error( "unzip: %s in %s requires unsupported version %u.%u\n", - m_header.file_name.c_str(), m_filename.c_str(), unsigned(m_header.version_needed / 10), unsigned(m_header.version_needed % 10)); + m_header.file_name, m_filename, m_header.version_needed / 10, m_header.version_needed % 10); return archive_file::error::UNSUPPORTED; } if (flags.encrypted() || flags.strong_encryption()) @@ -1062,24 +1164,24 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off // make sure the file handle is open auto const ziperr = reopen(); - if (ziperr != archive_file::error::NONE) + if (ziperr) return ziperr; // now go read the fixed-sized part of the local file header - std::uint32_t read_length; - auto const error = m_file->read(&m_buffer[0], m_header.local_header_offset, local_file_header_reader::minimum_length(), read_length); - if (error != osd_file::error::NONE) + std::size_t read_length; + std::error_condition const filerr = m_file->read_at(m_header.local_header_offset, &m_buffer[0], local_file_header_reader::minimum_length(), read_length); + if (filerr) { osd_printf_error( - "unzip: error reading local file header for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(error)); - return archive_file::error::FILE_ERROR; + "unzip: error reading local file header for %s in %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } if (read_length != local_file_header_reader::minimum_length()) { osd_printf_error( "unzip: unexpectedly reached end-of-file while reading local file header for %s in %s\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::FILE_TRUNCATED; } @@ -1088,13 +1190,13 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off if (!reader.signature_correct()) { osd_printf_error( - "unzip: local file header for %s in %s has incorrect signature %04lx\n", - m_header.file_name.c_str(), m_filename.c_str(), long(reader.signature())); + "unzip: local file header for %s in %s has incorrect signature %04x\n", + m_header.file_name, m_filename, reader.signature()); return archive_file::error::BAD_SIGNATURE; } offset = m_header.local_header_offset + reader.total_length(); - return archive_file::error::NONE; + return std::error_condition(); } @@ -1108,24 +1210,28 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off type 0 data (which is uncompressed) -------------------------------------------------*/ -archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length) +std::error_condition zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::size_t length) noexcept { // the data is uncompressed; just read it - std::uint32_t read_length(0); - auto const filerr = m_file->read(buffer, offset, m_header.compressed_length, read_length); - if (filerr != osd_file::error::NONE) + std::size_t read_length(0); + std::error_condition const filerr = m_file->read_at(offset, buffer, m_header.compressed_length, read_length); + if (filerr) { - osd_printf_error("unzip: error reading %s from %s (%d)\n", m_header.file_name, m_filename, int(filerr)); - return archive_file::error::FILE_ERROR; + osd_printf_error( + "unzip: error reading %s from %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } else if (read_length != m_header.compressed_length) { - osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s from %s\n", m_header.file_name, m_filename); + osd_printf_error( + "unzip: unexpectedly reached end-of-file while reading %s from %s\n", + m_header.file_name, m_filename); return archive_file::error::FILE_TRUNCATED; } else { - return archive_file::error::NONE; + return std::error_condition(); } } @@ -1135,8 +1241,23 @@ archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, type 8 data (which is deflated) -------------------------------------------------*/ -archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length) +std::error_condition zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::size_t length) noexcept { + auto const convert_zerr = + [] (int e) -> std::error_condition + { + switch (e) + { + case Z_OK: + return std::error_condition(); + case Z_ERRNO: + return std::error_condition(errno, std::generic_category()); + case Z_MEM_ERROR: + return std::errc::not_enough_memory; + default: + return archive_file::error::DECOMPRESS_ERROR; + } + }; std::uint64_t input_remaining = m_header.compressed_length; int zerr; @@ -1153,38 +1274,39 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, zerr = inflateInit2(&stream, -MAX_WBITS); if (zerr != Z_OK) { + auto result = convert_zerr(zerr); osd_printf_error( "unzip: error allocating zlib stream to inflate %s from %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), zerr); - return archive_file::error::DECOMPRESS_ERROR; + m_header.file_name, m_filename, zerr); + return result; } // loop until we're done while (true) { // read in the next chunk of data - std::uint32_t read_length(0); - auto const filerr = m_file->read( - &m_buffer[0], + std::size_t read_length(0); + auto const filerr = m_file->read_at( offset, - std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())), + &m_buffer[0], + std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())), read_length); - if (filerr != osd_file::error::NONE) + if (filerr) { osd_printf_error( - "unzip: error reading compressed data for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(filerr)); + "unzip: error reading compressed data for %s in %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); inflateEnd(&stream); - return archive_file::error::FILE_ERROR; + return filerr; } offset += read_length; // if we read nothing, but still have data left, the file is truncated - if ((read_length == 0) && (input_remaining > 0)) + if (!read_length && input_remaining) { osd_printf_error( "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); inflateEnd(&stream); return archive_file::error::FILE_TRUNCATED; } @@ -1206,9 +1328,12 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, } else if (zerr != Z_OK) { - osd_printf_error("unzip: error inflating %s from %s (%d)\n", m_header.file_name, m_filename, zerr); + auto result = convert_zerr(zerr); + osd_printf_error( + "unzip: error inflating %s from %s (%d)\n", + m_header.file_name, m_filename, zerr); inflateEnd(&stream); - return archive_file::error::DECOMPRESS_ERROR; + return result; } } @@ -1216,20 +1341,23 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, zerr = inflateEnd(&stream); if (zerr != Z_OK) { - osd_printf_error("unzip: error finishing inflation of %s from %s (%d)\n", m_header.file_name, m_filename, zerr); - return archive_file::error::DECOMPRESS_ERROR; + auto result = convert_zerr(zerr); + osd_printf_error( + "unzip: error finishing inflation of %s from %s (%d)\n", + m_header.file_name, m_filename, zerr); + return result; } // if anything looks funny, report an error - if ((stream.avail_out > 0) || (input_remaining > 0)) + if (stream.avail_out || input_remaining) { osd_printf_error( "unzip: inflation of %s from %s doesn't appear to have completed correctly\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::DECOMPRESS_ERROR; } - return archive_file::error::NONE; + return std::error_condition(); } @@ -1238,7 +1366,7 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, type 14 data (LZMA) -------------------------------------------------*/ -archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, void *buffer, std::uint32_t length) +std::error_condition zip_file_impl::decompress_data_type_14(std::uint64_t offset, void *buffer, std::size_t length) noexcept { // two-byte version // two-byte properties size (little-endian) @@ -1253,15 +1381,15 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, SizeT output_remaining(length); std::uint64_t input_remaining(m_header.compressed_length); - std::uint32_t read_length; - osd_file::error filerr; + std::size_t read_length; + std::error_condition filerr; SRes lzerr; ELzmaStatus lzstatus(LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK); // reset the stream ISzAlloc alloc_imp; - alloc_imp.Alloc = [](void *p, std::size_t size) -> void * { return (size == 0) ? nullptr : std::malloc(size); }; - alloc_imp.Free = [](void *p, void *address) -> void { std::free(address); }; + alloc_imp.Alloc = [] (void *p, std::size_t size) -> void * { return size ? std::malloc(size) : nullptr; }; + alloc_imp.Free = [] (void *p, void *address) -> void { std::free(address); }; CLzmaDec stream; LzmaDec_Construct(&stream); @@ -1270,16 +1398,16 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, { osd_printf_error( "unzip:compressed data for %s in %s is too small to hold LZMA properties header\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::DECOMPRESS_ERROR; } - filerr = m_file->read(&m_buffer[0], offset, 4, read_length); - if (filerr != osd_file::error::NONE) + filerr = m_file->read_at(offset, &m_buffer[0], 4, read_length); + if (filerr) { osd_printf_error( - "unzip: error reading LZMA properties header for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(filerr)); - return archive_file::error::FILE_ERROR; + "unzip: error reading LZMA properties header for %s in %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } offset += read_length; input_remaining -= read_length; @@ -1287,7 +1415,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, { osd_printf_error( "unzip: unexpectedly reached end-of-file while reading LZMA properties header for %s in %s\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::FILE_TRUNCATED; } std::uint16_t const props_size((std::uint16_t(m_buffer[3]) << 8) | std::uint16_t(m_buffer[2])); @@ -1295,23 +1423,23 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, { osd_printf_error( "unzip: %s in %s has excessively large LZMA properties\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::UNSUPPORTED; } else if (props_size > input_remaining) { osd_printf_error( "unzip:compressed data for %s in %s is too small to hold LZMA properties\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::DECOMPRESS_ERROR; } - filerr = m_file->read(&m_buffer[0], offset, props_size, read_length); - if (filerr != osd_file::error::NONE) + filerr = m_file->read_at(offset, &m_buffer[0], props_size, read_length); + if (filerr) { osd_printf_error( - "unzip: error reading LZMA properties for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(filerr)); - return archive_file::error::FILE_ERROR; + "unzip: error reading LZMA properties for %s in %s (%s:%d %s)\n", + m_header.file_name, m_filename, filerr.category().name(), filerr.value(), filerr.message()); + return filerr; } offset += read_length; input_remaining -= read_length; @@ -1319,7 +1447,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, { osd_printf_error( "unzip: unexpectedly reached end-of-file while reading LZMA properties for %s in %s\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); return archive_file::error::FILE_TRUNCATED; } @@ -1329,14 +1457,21 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, { osd_printf_error( "unzip: memory error allocating LZMA decoder to decompress %s from %s\n", - m_header.file_name.c_str(), m_filename.c_str()); - return archive_file::error::OUT_OF_MEMORY; + m_header.file_name, m_filename); + return std::errc::not_enough_memory; + } + else if (SZ_ERROR_UNSUPPORTED) + { + osd_printf_error( + "unzip: LZMA decoder does not support properties for %s in %s\n", + m_header.file_name, m_filename); + return archive_file::error::UNSUPPORTED; } else if (SZ_OK != lzerr) { osd_printf_error( "unzip: error allocating LZMA decoder to decompress %s from %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(lzerr)); + m_header.file_name, m_filename, int(lzerr)); return archive_file::error::DECOMPRESS_ERROR; } LzmaDec_Init(&stream); @@ -1345,28 +1480,28 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, while (0 < input_remaining) { // read in the next chunk of data - filerr = m_file->read( - &m_buffer[0], + filerr = m_file->read_at( offset, - std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())), + &m_buffer[0], + std::size_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())), read_length); - if (filerr != osd_file::error::NONE) + if (filerr) { osd_printf_error( - "unzip: error reading compressed data for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(filerr)); + "unzip: error reading compressed data for %s in %s (%s)\n", + m_header.file_name, m_filename); LzmaDec_Free(&stream, &alloc_imp); - return archive_file::error::FILE_ERROR; + return filerr; } offset += read_length; input_remaining -= read_length; // if we read nothing, but still have data left, the file is truncated - if ((0 == read_length) && (0 < input_remaining)) + if (!read_length && input_remaining) { osd_printf_error( "unzip: unexpectedly reached end-of-file while reading compressed data for %s in %s\n", - m_header.file_name.c_str(), m_filename.c_str()); + m_header.file_name, m_filename); LzmaDec_Free(&stream, &alloc_imp); return archive_file::error::FILE_TRUNCATED; } @@ -1381,7 +1516,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, &output_remaining, reinterpret_cast<Byte const *>(&m_buffer[0]), &len, - ((0 == input_remaining) && eos_mark) ? LZMA_FINISH_END : LZMA_FINISH_ANY, + (!input_remaining && eos_mark) ? LZMA_FINISH_END : LZMA_FINISH_ANY, &lzstatus); if (SZ_OK != lzerr) { @@ -1397,25 +1532,25 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, // if anything looks funny, report an error if (LZMA_STATUS_FINISHED_WITH_MARK == lzstatus) { - return archive_file::error::NONE; + return std::error_condition(); } else if (eos_mark) { osd_printf_error( "unzip: LZMA end mark not found for %s in %s (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus)); + m_header.file_name, m_filename, int(lzstatus)); return archive_file::error::DECOMPRESS_ERROR; } else if (LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK != lzstatus) { osd_printf_error( "unzip: LZMA decompression of %s from %s doesn't appear to have completed correctly (%d)\n", - m_header.file_name.c_str(), m_filename.c_str(), int(lzstatus)); + m_header.file_name, m_filename, int(lzstatus)); return archive_file::error::DECOMPRESS_ERROR; } else { - return archive_file::error::NONE; + return std::error_condition(); } } @@ -1427,7 +1562,7 @@ archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, un7z.cpp TRAMPOLINES ***************************************************************************/ -void m7z_file_cache_clear(); +void m7z_file_cache_clear() noexcept; @@ -1439,7 +1574,7 @@ void m7z_file_cache_clear(); zip_file_open - opens a ZIP file for reading -------------------------------------------------*/ -archive_file::error archive_file::open_zip(const std::string &filename, ptr &result) +std::error_condition archive_file::open_zip(std::string_view filename, ptr &result) noexcept { // ensure we start with a nullptr result result.reset(); @@ -1450,21 +1585,49 @@ archive_file::error archive_file::open_zip(const std::string &filename, ptr &res if (!newimpl) { // allocate memory for the zip_file structure - try { newimpl = std::make_unique<zip_file_impl>(filename); } - catch (...) { return error::OUT_OF_MEMORY; } - auto const ziperr = newimpl->initialize(); - if (ziperr != error::NONE) return ziperr; + try { newimpl = std::make_unique<zip_file_impl>(std::string(filename)); } + catch (...) { return std::errc::not_enough_memory; } + auto const err = newimpl->initialize(); + if (err) + return err; + } + + // allocate the archive API wrapper + result.reset(new (std::nothrow) zip_file_wrapper(std::move(newimpl))); + if (result) + { + return std::error_condition(); + } + else + { + zip_file_impl::close(std::move(newimpl)); + return std::errc::not_enough_memory; } +} - try +std::error_condition archive_file::open_zip(random_read::ptr &&file, ptr &result) noexcept +{ + // ensure we start with a nullptr result + result.reset(); + + // allocate memory for the zip_file structure + zip_file_impl::ptr newimpl(new (std::nothrow) zip_file_impl(std::move(file))); + if (!newimpl) + return std::errc::not_enough_memory; + auto const err = newimpl->initialize(); + if (err) + return err; + + // allocate the archive API wrapper + result.reset(new (std::nothrow) zip_file_wrapper(std::move(newimpl))); + if (result) { - result = std::make_unique<zip_file_wrapper>(std::move(newimpl)); - return error::NONE; + return std::error_condition(); } - catch (...) + else { zip_file_impl::close(std::move(newimpl)); - return error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -1474,7 +1637,7 @@ archive_file::error archive_file::open_zip(const std::string &filename, ptr &res cache and free all memory -------------------------------------------------*/ -void archive_file::cache_clear() +void archive_file::cache_clear() noexcept { zip_file_impl::cache_clear(); m7z_file_cache_clear(); @@ -1485,4 +1648,15 @@ archive_file::~archive_file() { } + +/*------------------------------------------------- + archive_category - gets the archive error + category instance +-------------------------------------------------*/ + +std::error_category const &archive_category() noexcept +{ + return f_archive_category_instance; +} + } // namespace util diff --git a/src/lib/util/unzip.h b/src/lib/util/unzip.h index b1ddb7a27d1..2108eadee28 100644 --- a/src/lib/util/unzip.h +++ b/src/lib/util/unzip.h @@ -7,19 +7,24 @@ archive file management. ***************************************************************************/ +#ifndef MAME_LIB_UTIL_UNZIP_H +#define MAME_LIB_UTIL_UNZIP_H #pragma once -#ifndef MAME_LIB_UTIL_UNZIP_H -#define MAME_LIB_UTIL_UNZIP_H +#include "utilfwd.h" #include <chrono> #include <cstdint> #include <memory> #include <string> +#include <string_view> +#include <system_error> +#include <type_traits> namespace util { + /*************************************************************************** TYPE DEFINITIONS ***************************************************************************/ @@ -30,12 +35,9 @@ class archive_file public: // Error types - enum class error + enum class error : int { - NONE = 0, - OUT_OF_MEMORY, - FILE_ERROR, - BAD_SIGNATURE, + BAD_SIGNATURE = 1, DECOMPRESS_ERROR, FILE_TRUNCATED, FILE_CORRUPT, @@ -49,40 +51,54 @@ public: /* ----- archive file access ----- */ // open a ZIP file and parse its central directory - static error open_zip(const std::string &filename, ptr &zip); + static std::error_condition open_zip(std::string_view filename, ptr &result) noexcept; + static std::error_condition open_zip(std::unique_ptr<random_read> &&file, ptr &result) noexcept; // open a 7Z file and parse its central directory - static error open_7z(const std::string &filename, ptr &result); + static std::error_condition open_7z(std::string_view filename, ptr &result) noexcept; + static std::error_condition open_7z(std::unique_ptr<random_read> &&file, ptr &result) noexcept; // close an archive file (may actually be left open due to caching) virtual ~archive_file(); // clear out all open files from the cache - static void cache_clear(); + static void cache_clear() noexcept; /* ----- contained file access ----- */ // iterating over files - returns negative on reaching end - virtual int first_file() = 0; - virtual int next_file() = 0; + virtual int first_file() noexcept = 0; + virtual int next_file() noexcept = 0; // find a file index by crc, filename or both - returns non-negative on match - virtual int search(std::uint32_t crc) = 0; - virtual int search(const std::string &filename, bool partialpath) = 0; - virtual int search(std::uint32_t crc, const std::string &filename, bool partialpath) = 0; + virtual int search(std::uint32_t crc) noexcept = 0; + virtual int search(std::string_view filename, bool partialpath) noexcept = 0; + virtual int search(std::uint32_t crc, std::string_view filename, bool partialpath) noexcept = 0; // information on most recently found file - virtual bool current_is_directory() const = 0; - virtual const std::string ¤t_name() const = 0; - virtual std::uint64_t current_uncompressed_length() const = 0; - virtual std::chrono::system_clock::time_point current_last_modified() const = 0; - virtual std::uint32_t current_crc() const = 0; + virtual bool current_is_directory() const noexcept = 0; + virtual const std::string ¤t_name() const noexcept = 0; + virtual std::uint64_t current_uncompressed_length() const noexcept = 0; + virtual std::chrono::system_clock::time_point current_last_modified() const noexcept = 0; + virtual std::uint32_t current_crc() const noexcept = 0; // decompress the most recently found file in the ZIP - virtual error decompress(void *buffer, std::uint32_t length) = 0; + virtual std::error_condition decompress(void *buffer, std::size_t length) noexcept = 0; }; + +// error category for archive errors +std::error_category const &archive_category() noexcept; +inline std::error_condition make_error_condition(archive_file::error err) noexcept { return std::error_condition(int(err), archive_category()); } + } // namespace util + +namespace std { + +template <> struct is_error_condition_enum<util::archive_file::error> : public std::true_type { }; + +} // namespace std + #endif // MAME_LIB_UTIL_UNZIP_H diff --git a/src/lib/util/utilfwd.h b/src/lib/util/utilfwd.h new file mode 100644 index 00000000000..fefe0631c68 --- /dev/null +++ b/src/lib/util/utilfwd.h @@ -0,0 +1,45 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/********************************************************************** + + utilfwd.h + + Forward declarations of types + +**********************************************************************/ +#ifndef MAME_LIB_UTIL_UTILFWD_H +#define MAME_LIB_UTIL_UTILFWD_H + +// chd.h +class chd_file; + + +namespace util { + +// corefile.h +class core_file; + +// ioprocs.h +class read_stream; +class write_stream; +class read_write_stream; +class random_access; +class random_read; +class random_write; +class random_read_write; + +// unzip.h +class archive_file; + +} // namespace util + + +namespace util::xml { + +// xmlfile.h +class data_node; +class file; + +} // namespace util::xml + +#endif // MAME_LIB_UTIL_UTILFWD_H diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp index 023484010b8..f317319c8c3 100644 --- a/src/lib/util/zippath.cpp +++ b/src/lib/util/zippath.cpp @@ -9,13 +9,13 @@ ***************************************************************************/ #include "zippath.h" -#include "unzip.h" -#include "corestr.h" -#include <cstdlib> +#include "corestr.h" +#include "unzip.h" #include <cassert> #include <cctype> +#include <cstdlib> #include <forward_list> #include <new> @@ -192,7 +192,7 @@ int zippath_find_sub_path(archive_file &zipfile, std::string_view subpath, osd:: // true path and ZIP entry components // ------------------------------------------------- -osd_file::error zippath_resolve(std::string_view path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath) +std::error_condition zippath_resolve(std::string_view path, osd::directory::entry::entry_type &entry_type, archive_file::ptr &zipfile, std::string &newpath) { newpath.clear(); @@ -238,13 +238,13 @@ osd_file::error zippath_resolve(std::string_view path, osd::directory::entry::en if (current_entry_type == osd::directory::entry::entry_type::NONE) { entry_type = osd::directory::entry::entry_type::NONE; - return osd_file::error::NOT_FOUND; + return std::errc::no_such_file_or_directory; } // is this file a ZIP file? if ((current_entry_type == osd::directory::entry::entry_type::FILE) && - ((is_zip_file(apath_trimmed) && (archive_file::open_zip(apath_trimmed, zipfile) == archive_file::error::NONE)) || - (is_7z_file(apath_trimmed) && (archive_file::open_7z(apath_trimmed, zipfile) == archive_file::error::NONE)))) + ((is_zip_file(apath_trimmed) && !archive_file::open_zip(apath_trimmed, zipfile)) || + (is_7z_file(apath_trimmed) && !archive_file::open_7z(apath_trimmed, zipfile)))) { auto i = path.length() - apath.length(); while ((i > 0) && is_zip_path_separator(path[apath.length() + i - 1])) @@ -254,53 +254,20 @@ osd_file::error zippath_resolve(std::string_view path, osd::directory::entry::en // this was a true ZIP path - attempt to identify the type of path zippath_find_sub_path(*zipfile, newpath, current_entry_type); if (current_entry_type == osd::directory::entry::entry_type::NONE) - return osd_file::error::NOT_FOUND; + return std::errc::no_such_file_or_directory; } else { // this was a normal path if (went_up) - return osd_file::error::NOT_FOUND; + return std::errc::no_such_file_or_directory; newpath = path; } // success! entry_type = current_entry_type; - return osd_file::error::NONE; -} - - -// ------------------------------------------------- -// file_error_from_zip_error - translates a -// osd_file::error to a zip_error -// ------------------------------------------------- - -osd_file::error file_error_from_zip_error(archive_file::error ziperr) -{ - osd_file::error filerr; - switch(ziperr) - { - case archive_file::error::NONE: - filerr = osd_file::error::NONE; - break; - case archive_file::error::OUT_OF_MEMORY: - filerr = osd_file::error::OUT_OF_MEMORY; - break; - case archive_file::error::BAD_SIGNATURE: - case archive_file::error::DECOMPRESS_ERROR: - case archive_file::error::FILE_TRUNCATED: - case archive_file::error::FILE_CORRUPT: - case archive_file::error::UNSUPPORTED: - case archive_file::error::FILE_ERROR: - filerr = osd_file::error::INVALID_DATA; - break; - case archive_file::error::BUFFER_TOO_SMALL: - default: - filerr = osd_file::error::FAILURE; - break; - } - return filerr; + return std::error_condition(); } @@ -309,27 +276,24 @@ osd_file::error file_error_from_zip_error(archive_file::error ziperr) // from a zip file entry // ------------------------------------------------- -osd_file::error create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file) +std::error_condition create_core_file_from_zip(archive_file &zip, util::core_file::ptr &file) { - osd_file::error filerr; - archive_file::error ziperr; + // TODO: would be more efficient if we could open a memory-based core_file with uninitialised contents and decompress into it + std::error_condition filerr; void *ptr = malloc(zip.current_uncompressed_length()); if (!ptr) { - filerr = osd_file::error::OUT_OF_MEMORY; + filerr = std::errc::not_enough_memory; goto done; } - ziperr = zip.decompress(ptr, zip.current_uncompressed_length()); - if (ziperr != archive_file::error::NONE) - { - filerr = file_error_from_zip_error(ziperr); + filerr = zip.decompress(ptr, zip.current_uncompressed_length()); + if (filerr) goto done; - } filerr = util::core_file::open_ram_copy(ptr, zip.current_uncompressed_length(), OPEN_FLAG_READ, file); - if (filerr != osd_file::error::NONE) + if (filerr) goto done; done: @@ -545,7 +509,7 @@ private: // zippath_directory::open - opens a directory // ------------------------------------------------- -osd_file::error zippath_directory::open(std::string_view path, ptr &directory) +std::error_condition zippath_directory::open(std::string_view path, ptr &directory) { try { @@ -553,34 +517,34 @@ osd_file::error zippath_directory::open(std::string_view path, ptr &directory) osd::directory::entry::entry_type entry_type; archive_file::ptr zipfile; std::string zipprefix; - osd_file::error const err = zippath_resolve(path, entry_type, zipfile, zipprefix); - if (osd_file::error::NONE != err) + std::error_condition const err = zippath_resolve(path, entry_type, zipfile, zipprefix); + if (err) return err; // we have to be a directory if (osd::directory::entry::entry_type::DIR != entry_type) - return osd_file::error::NOT_FOUND; + return std::errc::not_a_directory; // was the result a ZIP? if (zipfile) { directory = std::make_unique<archive_directory_impl>(std::move(zipfile), std::move(zipprefix)); - return osd_file::error::NONE; + return std::error_condition(); } else { // a conventional directory std::unique_ptr<filesystem_directory_impl> result(new filesystem_directory_impl(path)); if (!*result) - return osd_file::error::FAILURE; + return std::errc::io_error; // TODO: any way to give a better error here? directory = std::move(result); - return osd_file::error::NONE; + return std::error_condition(); } } catch (std::bad_alloc const &) { - return osd_file::error::OUT_OF_MEMORY; + return std::errc::not_enough_memory; } } @@ -681,7 +645,7 @@ std::string zippath_combine(const std::string &path1, const std::string &path2) // ------------------------------------------------- /** - * @fn osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) + * @fn std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) * * @brief Zippath fopen. * @@ -693,38 +657,35 @@ std::string zippath_combine(const std::string &path1, const std::string &path2) * @return A osd_file::error. */ -osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) +std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path) { - osd_file::error filerr = osd_file::error::NOT_FOUND; - archive_file::error ziperr; + std::error_condition filerr = std::errc::no_such_file_or_directory; archive_file::ptr zip; - int header; - osd::directory::entry::entry_type entry_type; - int len; - /* first, set up the two types of paths */ + // first, set up the two types of paths std::string mainpath(filename); std::string subpath; file = nullptr; - /* loop through */ - while((file == nullptr) && (mainpath.length() > 0) - && ((openflags == OPEN_FLAG_READ) || subpath.empty())) + // loop through + while (!file && !mainpath.empty() && ((openflags == OPEN_FLAG_READ) || subpath.empty())) { - /* is the mainpath a ZIP path? */ + // is the mainpath a ZIP path? if (is_zip_file(mainpath) || is_7z_file(mainpath)) { - /* this file might be a zip file - lets take a look */ - ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip); - if (ziperr == archive_file::error::NONE) + // this file might be a zip file - lets take a look + std::error_condition const ziperr = is_zip_file(mainpath) ? archive_file::open_zip(mainpath, zip) : archive_file::open_7z(mainpath, zip); + if (!ziperr) { - /* it is a zip file - error if we're not opening for reading */ + // it is a zip file - error if we're not opening for reading if (openflags != OPEN_FLAG_READ) { - filerr = osd_file::error::ACCESS_DENIED; + filerr = std::errc::permission_denied; goto done; } + osd::directory::entry::entry_type entry_type; + int header; if (!subpath.empty()) header = zippath_find_sub_path(*zip, subpath, entry_type); else @@ -732,20 +693,20 @@ osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, uti if (header < 0) { - filerr = osd_file::error::NOT_FOUND; + filerr = std::errc::no_such_file_or_directory; goto done; } - /* attempt to read the file */ + // attempt to read the file filerr = create_core_file_from_zip(*zip, file); - if (filerr != osd_file::error::NONE) + if (filerr) goto done; - /* update subpath, if appropriate */ + // update subpath, if appropriate if (subpath.empty()) subpath.assign(zip->current_name()); - /* we're done */ + // we're done goto done; } } @@ -753,15 +714,15 @@ osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, uti if (subpath.empty()) filerr = util::core_file::open(std::string(filename), openflags, file); else - filerr = osd_file::error::NOT_FOUND; + filerr = std::errc::no_such_file_or_directory; - /* if we errored, then go up a directory */ - if (filerr != osd_file::error::NONE) + // if we errored, then go up a directory + if (filerr) { - /* go up a directory */ + // go up a directory auto temp = zippath_parent(mainpath); - /* append to the sub path */ + // append to the sub path if (!subpath.empty()) { std::string temp2; @@ -774,8 +735,8 @@ osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, uti mainpath = mainpath.substr(temp.length()); subpath.assign(mainpath); } - /* get the new main path, truncating path separators */ - len = temp.length(); + // get the new main path, truncating path separators + auto len = temp.length(); while (len > 0 && is_zip_file_separator(temp[len - 1])) len--; temp = temp.substr(0, len); @@ -784,14 +745,14 @@ osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, uti } done: - /* store the revised path */ + // store the revised path revised_path.clear(); - if (filerr == osd_file::error::NONE) + if (!filerr) { - /* canonicalize mainpath */ + // canonicalize mainpath std::string alloc_fullpath; filerr = osd_get_full_path(alloc_fullpath, mainpath); - if (filerr == osd_file::error::NONE) + if (!filerr) { revised_path = alloc_fullpath; if (!subpath.empty()) diff --git a/src/lib/util/zippath.h b/src/lib/util/zippath.h index 1eb92aa1c1d..955c75cfc8f 100644 --- a/src/lib/util/zippath.h +++ b/src/lib/util/zippath.h @@ -18,6 +18,7 @@ #include <string> #include <string_view> +#include <system_error> namespace util { @@ -32,7 +33,7 @@ public: typedef std::unique_ptr<zippath_directory> ptr; // opens a directory - static osd_file::error open(std::string_view path, ptr &directory); + static std::error_condition open(std::string_view path, ptr &directory); // closes a directory virtual ~zippath_directory(); @@ -63,7 +64,7 @@ std::string zippath_combine(const std::string &path1, const std::string &path2); // ----- file operations ----- // opens a zip path file -osd_file::error zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path); +std::error_condition zippath_fopen(std::string_view filename, uint32_t openflags, util::core_file::ptr &file, std::string &revised_path); } // namespace util |