diff options
author | 2021-08-22 09:06:15 +1000 | |
---|---|---|
committer | 2021-08-22 09:06:15 +1000 | |
commit | e8bbea1fc6e94e14768509d322f6c624403ffb36 (patch) | |
tree | 74dd1606a900d83de8aecff17a6737af4113308d /src/lib/util/unzip.cpp | |
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/unzip.cpp')
-rw-r--r-- | src/lib/util/unzip.cpp | 1050 |
1 files changed, 612 insertions, 438 deletions
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 |