diff options
author | 2016-03-13 13:54:57 +1100 | |
---|---|---|
committer | 2016-03-14 18:55:00 +1100 | |
commit | 42fbb9c3967fcda37f2d9ae17d5796a79cf027b9 (patch) | |
tree | a43047ee00431e5e22bfc5f8f612ff775586e415 /src/lib/util/unzip.cpp | |
parent | 5fc27747031b6ed6f6c0582502098ebfb960be67 (diff) |
Make osd_file a polymorphic class that's held with smart pointers
Make avi_file a class that's held with smart pointers, encapsulate various AVI I/O structures
Make zip_file and _7z_file classes rather than having free functions everywhere
Hide zip/7z class implementation behind an interface, no longer need to call close() to send back to the cache
Don't dump as much crap in global namespace
Add solaris PTY implementation
Improve variable expansion for SDL OSD - supports ~/$FOO/${BAR} syntax
Rearrange stuff so the same things are in file module for all OSDs
Move file stuff into its own module
7z/zip open and destruct are still not thread-safe due to lack of interlocks around cache access
Directory functions still need to be moved to file module
SDL OSD may not initialise WinSock on Windows
Diffstat (limited to 'src/lib/util/unzip.cpp')
-rw-r--r-- | src/lib/util/unzip.cpp | 794 |
1 files changed, 417 insertions, 377 deletions
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp index 876d8ae90c5..321f4e0b69f 100644 --- a/src/lib/util/unzip.cpp +++ b/src/lib/util/unzip.cpp @@ -1,5 +1,5 @@ // license:BSD-3-Clause -// copyright-holders:Aaron Giles +// copyright-holders:Aaron Giles, Vas Crabb /*************************************************************************** unzip.c @@ -11,19 +11,23 @@ #include "osdcore.h" #include "unzip.h" -#include <ctype.h> -#include <stdlib.h> +#include <algorithm> +#include <array> +#include <cassert> +#include <cstring> +#include <cstdlib> +#include <utility> +#include <vector> + #include <zlib.h> +namespace { /*************************************************************************** CONSTANTS ***************************************************************************/ -/* number of open files to cache */ -#define ZIP_CACHE_SIZE 8 - /* offsets in end of central directory structure */ #define ZIPESIG 0x00 #define ZIPEDSK 0x04 @@ -95,6 +99,162 @@ /*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +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_buffer() + { + std::memset(&m_header, 0, sizeof(m_header)); + std::fill(m_buffer.begin(), m_buffer.end(), 0); + } + + static ptr find_cached(const std::string &filename) + { + for (std::size_t cachenum = 0; cachenum < s_cache.size(); cachenum++) + { + // 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)) + { + ptr result; + std::swap(s_cache[cachenum], result); + return result; + } + } + return ptr(); + } + static void close(ptr &&zip); + static void cache_clear() + { + // clear call cache entries + for (std::size_t cachenum = 0; cachenum < s_cache.size(); s_cache[cachenum++].reset()) { } + } + + zip_file::error initialize() + { + // read ecd data + auto const ziperr = read_ecd(); + if (ziperr != zip_file::error::NONE) + return ziperr; + + // verify that we can work with this zipfile (no disk spanning allowed) + if ((m_ecd.disk_number != m_ecd.cd_start_disk_number) || (m_ecd.cd_disk_entries != m_ecd.cd_total_entries)) + return zip_file::error::UNSUPPORTED; + + // allocate memory for the central directory + try { m_cd.resize(m_ecd.cd_size + 1); } + catch (...) { return zip_file::error::OUT_OF_MEMORY; } + + // read the central directory + std::uint32_t read_length; + auto const filerr = m_file->read(&m_cd[0], m_ecd.cd_start_disk_offset, m_ecd.cd_size, read_length); + if ((filerr != osd_file::error::NONE) || (read_length != m_ecd.cd_size)) + return (filerr == osd_file::error::NONE) ? zip_file::error::FILE_TRUNCATED : zip_file::error::FILE_ERROR; + + return zip_file::error::NONE; + } + + // contained file access + const zip_file::file_header *first_file(); + const zip_file::file_header *next_file(); + zip_file::error decompress(void *buffer, std::uint32_t length); + +private: + zip_file_impl(const zip_file_impl &) = delete; + zip_file_impl(zip_file_impl &&) = delete; + zip_file_impl &operator=(const zip_file_impl &) = delete; + zip_file_impl &operator=(zip_file_impl &&) = delete; + + zip_file::error reopen() + { + if (!m_file) + { + auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length); + if (filerr != osd_file::error::NONE) + return zip_file::error::FILE_ERROR; + } + return zip_file::error::NONE; + } + + // ZIP file parsing + zip_file::error read_ecd(); + zip_file::error get_compressed_data_offset(std::uint64_t &offset); + + // decompression interfaces + zip_file::error decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length); + zip_file::error decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length); + + struct file_header_int : zip_file::file_header + { + std::uint8_t * raw; // pointer to the raw data + std::uint32_t rawlength; // length of the raw data + std::uint8_t saved; // saved byte from after filename + }; + + // contains extracted end of central directory information + struct ecd + { + std::uint32_t signature; // end of central dir signature + std::uint16_t disk_number; // number of this disk + std::uint16_t cd_start_disk_number; // number of the disk with the start of the central directory + std::uint16_t cd_disk_entries; // total number of entries in the central directory on this disk + std::uint16_t cd_total_entries; // total number of entries in the central directory + std::uint32_t cd_size; // size of the central directory + std::uint32_t cd_start_disk_offset; // offset of start of central directory with respect to the starting disk number + std::uint16_t comment_length; // .ZIP file comment length + const char * comment; // .ZIP file comment + + std::unique_ptr<std::uint8_t []> raw; // pointer to the raw data + std::uint32_t rawlength; // length of the raw data + }; + + static constexpr std::size_t DECOMPRESS_BUFSIZE = 16384; + static constexpr std::size_t CACHE_SIZE = 8; // number of open files to cache + static std::array<ptr, CACHE_SIZE> s_cache; + + 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 + + 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 + file_header_int m_header; // current file header + + std::array<std::uint8_t, DECOMPRESS_BUFSIZE> m_buffer; // buffer for decompression +}; + + +class zip_file_wrapper : public zip_file +{ +public: + zip_file_wrapper(zip_file_impl::ptr &&impl) : m_impl(std::move(impl)) { assert(m_impl); } + virtual ~zip_file_wrapper() { zip_file_impl::close(std::move(m_impl)); } + + virtual const file_header *first_file() override { return m_impl->first_file(); } + virtual const file_header *next_file() override { return m_impl->next_file(); } + virtual error decompress(void *buffer, std::uint32_t length) override { return m_impl->decompress(buffer, length); } + +private: + zip_file_impl::ptr m_impl; +}; + + + +/*************************************************************************** INLINE FUNCTIONS ***************************************************************************/ @@ -108,7 +268,7 @@ * @return The word. */ -static inline UINT16 read_word(UINT8 *buf) +inline UINT16 read_word(UINT8 *buf) { return (buf[1] << 8) | buf[0]; } @@ -123,7 +283,7 @@ static inline UINT16 read_word(UINT8 *buf) * @return The double word. */ -static inline UINT32 read_dword(UINT8 *buf) +inline UINT32 read_dword(UINT8 *buf) { return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; } @@ -135,130 +295,8 @@ static inline UINT32 read_dword(UINT8 *buf) ***************************************************************************/ /** @brief The zip cache[ zip cache size]. */ -static zip_file *zip_cache[ZIP_CACHE_SIZE]; - - - -/*************************************************************************** - FUNCTION PROTOTYPES -***************************************************************************/ - -/* cache management */ -static void free_zip_file(zip_file *zip); +std::array<zip_file_impl::ptr, zip_file_impl::CACHE_SIZE> zip_file_impl::s_cache; -/* ZIP file parsing */ -static zip_error read_ecd(zip_file *zip); -static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset); - -/* decompression interfaces */ -static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); -static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); - - - -/*************************************************************************** - ZIP FILE ACCESS -***************************************************************************/ - -/*------------------------------------------------- - zip_file_open - opens a ZIP file for reading --------------------------------------------------*/ - -/** - * @fn zip_error zip_file_open(const char *filename, zip_file **zip) - * - * @brief Queries if a given zip file open. - * - * @param filename Filename of the file. - * @param [in,out] zip If non-null, the zip. - * - * @return A zip_error. - */ - -zip_error zip_file_open(const char *filename, zip_file **zip) -{ - zip_error ziperr = ZIPERR_NONE; - file_error filerr; - UINT32 read_length; - zip_file *newzip; - char *string; - int cachenum; - - /* ensure we start with a NULL result */ - *zip = nullptr; - - /* see if we are in the cache, and reopen if so */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - { - zip_file *cached = zip_cache[cachenum]; - - /* if we have a valid entry and it matches our filename, use it and remove from the cache */ - if (cached != nullptr && cached->filename != nullptr && strcmp(filename, cached->filename) == 0) - { - *zip = cached; - zip_cache[cachenum] = nullptr; - return ZIPERR_NONE; - } - } - - /* allocate memory for the zip_file structure */ - newzip = (zip_file *)malloc(sizeof(*newzip)); - if (newzip == nullptr) - return ZIPERR_OUT_OF_MEMORY; - memset(newzip, 0, sizeof(*newzip)); - - /* open the file */ - filerr = osd_open(filename, OPEN_FLAG_READ, &newzip->file, &newzip->length); - if (filerr != FILERR_NONE) - { - ziperr = ZIPERR_FILE_ERROR; - goto error; - } - - /* read ecd data */ - ziperr = read_ecd(newzip); - if (ziperr != ZIPERR_NONE) - goto error; - - /* verify that we can work with this zipfile (no disk spanning allowed) */ - if (newzip->ecd.disk_number != newzip->ecd.cd_start_disk_number || newzip->ecd.cd_disk_entries != newzip->ecd.cd_total_entries) - { - ziperr = ZIPERR_UNSUPPORTED; - goto error; - } - - /* allocate memory for the central directory */ - newzip->cd = (UINT8 *)malloc(newzip->ecd.cd_size + 1); - if (newzip->cd == nullptr) - { - ziperr = ZIPERR_OUT_OF_MEMORY; - goto error; - } - - /* read the central directory */ - filerr = osd_read(newzip->file, newzip->cd, newzip->ecd.cd_start_disk_offset, newzip->ecd.cd_size, &read_length); - if (filerr != FILERR_NONE || read_length != newzip->ecd.cd_size) - { - ziperr = (filerr == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; - goto error; - } - - /* make a copy of the filename for caching purposes */ - string = (char *)malloc(strlen(filename) + 1); - if (string == nullptr) - { - ziperr = ZIPERR_OUT_OF_MEMORY; - goto error; - } - strcpy(string, filename); - newzip->filename = string; - *zip = newzip; - return ZIPERR_NONE; - -error: - free_zip_file(newzip); - return ziperr; -} /*------------------------------------------------- @@ -274,57 +312,30 @@ error: * @param [in,out] zip If non-null, the zip. */ -void zip_file_close(zip_file *zip) +void zip_file_impl::close(ptr &&zip) { - int cachenum; + if (!zip) return; - /* close the open files */ - if (zip->file != nullptr) - osd_close(zip->file); - zip->file = nullptr; + // close the open files + zip->m_file.reset(); - /* find the first NULL entry in the cache */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - if (zip_cache[cachenum] == nullptr) + // find the first NULL entry in the cache + 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 == ARRAY_LENGTH(zip_cache)) - free_zip_file(zip_cache[--cachenum]); - - /* move everyone else down and place us at the top */ - if (cachenum != 0) - memmove(&zip_cache[1], &zip_cache[0], cachenum * sizeof(zip_cache[0])); - zip_cache[0] = zip; -} - - -/*------------------------------------------------- - zip_file_cache_clear - clear the ZIP file - cache and free all memory --------------------------------------------------*/ - -/** - * @fn void zip_file_cache_clear(void) - * - * @brief Zip file cache clear. - */ - -void zip_file_cache_clear(void) -{ - int cachenum; + // if no room left in the cache, free the bottommost entry + if (cachenum == s_cache.size()) + s_cache[--cachenum].reset(); - /* clear call cache entries */ - for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) - if (zip_cache[cachenum] != nullptr) - { - free_zip_file(zip_cache[cachenum]); - zip_cache[cachenum] = nullptr; - } + // 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); } - /*************************************************************************** CONTAINED FILE ACCESS ***************************************************************************/ @@ -344,11 +355,11 @@ void zip_file_cache_clear(void) * @return null if it fails, else a zip_file_header*. */ -const zip_file_header *zip_file_first_file(zip_file *zip) +const zip_file::file_header *zip_file_impl::first_file() { /* reset the position and go from there */ - zip->cd_pos = 0; - return zip_file_next_file(zip); + m_cd_pos = 0; + return next_file(); } @@ -367,55 +378,55 @@ const zip_file_header *zip_file_first_file(zip_file *zip) * @return null if it fails, else a zip_file_header*. */ -const zip_file_header *zip_file_next_file(zip_file *zip) +const zip_file::file_header *zip_file_impl::next_file() { - /* fix up any modified data */ - if (zip->header.raw != nullptr) + // fix up any modified data + if (m_header.raw) { - zip->header.raw[ZIPCFN + zip->header.filename_length] = zip->header.saved; - zip->header.raw = nullptr; + m_header.raw[ZIPCFN + m_header.filename_length] = m_header.saved; + m_header.raw = nullptr; } - /* if we're at or past the end, we're done */ - if (zip->cd_pos >= zip->ecd.cd_size) + // if we're at or past the end, we're done + if (m_cd_pos >= m_ecd.cd_size) return nullptr; - /* extract file header info */ - zip->header.raw = zip->cd + zip->cd_pos; - zip->header.rawlength = ZIPCFN; - zip->header.signature = read_dword(zip->header.raw + ZIPCENSIG); - zip->header.version_created = read_word (zip->header.raw + ZIPCVER); - zip->header.version_needed = read_word (zip->header.raw + ZIPCVXT); - zip->header.bit_flag = read_word (zip->header.raw + ZIPCFLG); - zip->header.compression = read_word (zip->header.raw + ZIPCMTHD); - zip->header.file_time = read_word (zip->header.raw + ZIPCTIM); - zip->header.file_date = read_word (zip->header.raw + ZIPCDAT); - zip->header.crc = read_dword(zip->header.raw + ZIPCCRC); - zip->header.compressed_length = read_dword(zip->header.raw + ZIPCSIZ); - zip->header.uncompressed_length = read_dword(zip->header.raw + ZIPCUNC); - zip->header.filename_length = read_word (zip->header.raw + ZIPCFNL); - zip->header.extra_field_length = read_word (zip->header.raw + ZIPCXTL); - zip->header.file_comment_length = read_word (zip->header.raw + ZIPCCML); - zip->header.start_disk_number = read_word (zip->header.raw + ZIPDSK); - zip->header.internal_attributes = read_word (zip->header.raw + ZIPINT); - zip->header.external_attributes = read_dword(zip->header.raw + ZIPEXT); - zip->header.local_header_offset = read_dword(zip->header.raw + ZIPOFST); - zip->header.filename = (char *)zip->header.raw + ZIPCFN; - - /* make sure we have enough data */ - zip->header.rawlength += zip->header.filename_length; - zip->header.rawlength += zip->header.extra_field_length; - zip->header.rawlength += zip->header.file_comment_length; - if (zip->cd_pos + zip->header.rawlength > zip->ecd.cd_size) + // extract file header info + m_header.raw = &m_cd[0] + m_cd_pos; + m_header.rawlength = ZIPCFN; + m_header.signature = read_dword(m_header.raw + ZIPCENSIG); + m_header.version_created = read_word (m_header.raw + ZIPCVER); + m_header.version_needed = read_word (m_header.raw + ZIPCVXT); + m_header.bit_flag = read_word (m_header.raw + ZIPCFLG); + m_header.compression = read_word (m_header.raw + ZIPCMTHD); + m_header.file_time = read_word (m_header.raw + ZIPCTIM); + m_header.file_date = read_word (m_header.raw + ZIPCDAT); + m_header.crc = read_dword(m_header.raw + ZIPCCRC); + m_header.compressed_length = read_dword(m_header.raw + ZIPCSIZ); + m_header.uncompressed_length = read_dword(m_header.raw + ZIPCUNC); + m_header.filename_length = read_word (m_header.raw + ZIPCFNL); + m_header.extra_field_length = read_word (m_header.raw + ZIPCXTL); + m_header.file_comment_length = read_word (m_header.raw + ZIPCCML); + m_header.start_disk_number = read_word (m_header.raw + ZIPDSK); + m_header.internal_attributes = read_word (m_header.raw + ZIPINT); + m_header.external_attributes = read_dword(m_header.raw + ZIPEXT); + m_header.local_header_offset = read_dword(m_header.raw + ZIPOFST); + m_header.filename = reinterpret_cast<const char *>(m_header.raw + ZIPCFN); + + // make sure we have enough data + m_header.rawlength += m_header.filename_length; + m_header.rawlength += m_header.extra_field_length; + m_header.rawlength += m_header.file_comment_length; + if (m_cd_pos + m_header.rawlength > m_ecd.cd_size) return nullptr; - /* NULL terminate the filename */ - zip->header.saved = zip->header.raw[ZIPCFN + zip->header.filename_length]; - zip->header.raw[ZIPCFN + zip->header.filename_length] = 0; + // NULL terminate the filename + m_header.saved = m_header.raw[ZIPCFN + m_header.filename_length]; + m_header.raw[ZIPCFN + m_header.filename_length] = 0; - /* advance the position */ - zip->cd_pos += zip->header.rawlength; - return &zip->header; + // advance the position + m_cd_pos += m_header.rawlength; + return &m_header; } @@ -436,38 +447,38 @@ const zip_file_header *zip_file_next_file(zip_file *zip) * @return A zip_error. */ -zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress(void *buffer, UINT32 length) { - zip_error ziperr; - UINT64 offset; + zip_file::error ziperr; + std::uint64_t offset; - /* if we don't have enough buffer, error */ - if (length < zip->header.uncompressed_length) - return ZIPERR_BUFFER_TOO_SMALL; + // if we don't have enough buffer, error + if (length < m_header.uncompressed_length) + return zip_file::error::BUFFER_TOO_SMALL; - /* make sure the info in the header aligns with what we know */ - if (zip->header.start_disk_number != zip->ecd.disk_number) - return ZIPERR_UNSUPPORTED; + // make sure the info in the header aligns with what we know + if (m_header.start_disk_number != m_ecd.disk_number) + return zip_file::error::UNSUPPORTED; - /* get the compressed data offset */ - ziperr = get_compressed_data_offset(zip, &offset); - if (ziperr != ZIPERR_NONE) + // get the compressed data offset + ziperr = get_compressed_data_offset(offset); + if (ziperr != zip_file::error::NONE) return ziperr; - /* handle compression types */ - switch (zip->header.compression) + // handle compression types + switch (m_header.compression) { - case 0: - ziperr = decompress_data_type_0(zip, offset, buffer, length); - break; + case 0: + ziperr = decompress_data_type_0(offset, buffer, length); + break; - case 8: - ziperr = decompress_data_type_8(zip, offset, buffer, length); - break; + case 8: + ziperr = decompress_data_type_8(offset, buffer, length); + break; - default: - ziperr = ZIPERR_UNSUPPORTED; - break; + default: + ziperr = zip_file::error::UNSUPPORTED; + break; } return ziperr; } @@ -475,41 +486,6 @@ zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length) /*************************************************************************** - CACHE MANAGEMENT -***************************************************************************/ - -/*------------------------------------------------- - free_zip_file - free all the data for a - zip_file --------------------------------------------------*/ - -/** - * @fn static void free_zip_file(zip_file *zip) - * - * @brief Free zip file. - * - * @param [in,out] zip If non-null, the zip. - */ - -static void free_zip_file(zip_file *zip) -{ - if (zip != nullptr) - { - if (zip->file != nullptr) - osd_close(zip->file); - if (zip->filename != nullptr) - free((void *)zip->filename); - if (zip->ecd.raw != nullptr) - free(zip->ecd.raw); - if (zip->cd != nullptr) - free(zip->cd); - free(zip); - } -} - - - -/*************************************************************************** ZIP FILE PARSING ***************************************************************************/ @@ -527,72 +503,69 @@ static void free_zip_file(zip_file *zip) * @return The ecd. */ -static zip_error read_ecd(zip_file *zip) +zip_file::error zip_file_impl::read_ecd() { - UINT32 buflen = 1024; - UINT8 *buffer; + // make sure the file handle is open + auto const ziperr = reopen(); + if (ziperr != zip_file::error::NONE) + return ziperr; - /* we may need multiple tries */ + // we may need multiple tries + std::uint32_t buflen = 1024; while (buflen < 65536) { - file_error error; - UINT32 read_length; - INT32 offset; - - /* max out the buffer length at the size of the file */ - if (buflen > zip->length) - buflen = zip->length; - - /* allocate buffer */ - buffer = (UINT8 *)malloc(buflen + 1); - if (buffer == nullptr) - return ZIPERR_OUT_OF_MEMORY; - - /* read in one buffers' worth of data */ - error = osd_read(zip->file, buffer, zip->length - buflen, buflen, &read_length); - if (error != FILERR_NONE || read_length != buflen) - { - free(buffer); - return ZIPERR_FILE_ERROR; - } - - /* find the ECD signature */ + // max out the buffer length at the size of the file + if (buflen > m_length) + buflen = m_length; + + // allocate buffer + std::unique_ptr<std::uint8_t []> buffer; + try { buffer.reset(new std::uint8_t[buflen + 1]); } + catch (...) { return zip_file::error::OUT_OF_MEMORY; } + + // read in one buffers' worth of data + std::uint32_t read_length; + auto const error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length); + if (error != osd_file::error::NONE || read_length != buflen) + return zip_file::error::FILE_ERROR; + + // find the ECD signature + std::int32_t offset; for (offset = buflen - 22; offset >= 0; offset--) if (buffer[offset + 0] == 'P' && buffer[offset + 1] == 'K' && buffer[offset + 2] == 0x05 && buffer[offset + 3] == 0x06) break; - /* if we found it, fill out the data */ + // if we found it, fill out the data if (offset >= 0) { - /* reuse the buffer as our ECD buffer */ - zip->ecd.raw = buffer; - zip->ecd.rawlength = buflen - offset; + // reuse the buffer as our ECD buffer + m_ecd.raw = std::move(buffer); + m_ecd.rawlength = buflen - offset; /* append a NULL terminator to the comment */ - memmove(&buffer[0], &buffer[offset], zip->ecd.rawlength); - zip->ecd.raw[zip->ecd.rawlength] = 0; + memmove(&m_ecd.raw[0], &m_ecd.raw[offset], m_ecd.rawlength); + m_ecd.raw[m_ecd.rawlength] = 0; /* extract ecd info */ - zip->ecd.signature = read_dword(zip->ecd.raw + ZIPESIG); - zip->ecd.disk_number = read_word (zip->ecd.raw + ZIPEDSK); - zip->ecd.cd_start_disk_number = read_word (zip->ecd.raw + ZIPECEN); - zip->ecd.cd_disk_entries = read_word (zip->ecd.raw + ZIPENUM); - zip->ecd.cd_total_entries = read_word (zip->ecd.raw + ZIPECENN); - zip->ecd.cd_size = read_dword(zip->ecd.raw + ZIPECSZ); - zip->ecd.cd_start_disk_offset = read_dword(zip->ecd.raw + ZIPEOFST); - zip->ecd.comment_length = read_word (zip->ecd.raw + ZIPECOML); - zip->ecd.comment = (const char *)(zip->ecd.raw + ZIPECOM); - return ZIPERR_NONE; + m_ecd.signature = read_dword(&m_ecd.raw[ZIPESIG]); + m_ecd.disk_number = read_word (&m_ecd.raw[ZIPEDSK]); + m_ecd.cd_start_disk_number = read_word (&m_ecd.raw[ZIPECEN]); + m_ecd.cd_disk_entries = read_word (&m_ecd.raw[ZIPENUM]); + m_ecd.cd_total_entries = read_word (&m_ecd.raw[ZIPECENN]); + m_ecd.cd_size = read_dword(&m_ecd.raw[ZIPECSZ]); + m_ecd.cd_start_disk_offset = read_dword(&m_ecd.raw[ZIPEOFST]); + m_ecd.comment_length = read_word (&m_ecd.raw[ZIPECOML]); + m_ecd.comment = reinterpret_cast<const char *>(&m_ecd.raw[ZIPECOM]); + return zip_file::error::NONE; } - /* didn't find it; free this buffer and expand our search */ - free(buffer); - if (buflen < zip->length) + // didn't find it; free this buffer and expand our search + if (buflen < m_length) buflen *= 2; else - return ZIPERR_BAD_SIGNATURE; + return zip_file::error::BAD_SIGNATURE; } - return ZIPERR_OUT_OF_MEMORY; + return zip_file::error::OUT_OF_MEMORY; } @@ -612,30 +585,25 @@ static zip_error read_ecd(zip_file *zip) * @return The compressed data offset. */ -static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset) +zip_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &offset) { - file_error error; - UINT32 read_length; - - /* make sure the file handle is open */ - if (zip->file == nullptr) - { - int filerr = osd_open(zip->filename, OPEN_FLAG_READ, &zip->file, &zip->length); - if (filerr != FILERR_NONE) - return ZIPERR_FILE_ERROR; - } + // make sure the file handle is open + auto const ziperr = reopen(); + if (ziperr != zip_file::error::NONE) + return ziperr; - /* now go read the fixed-sized part of the local file header */ - error = osd_read(zip->file, zip->buffer, zip->header.local_header_offset, ZIPNAME, &read_length); - if (error != FILERR_NONE || read_length != ZIPNAME) - return (error == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; + // 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, ZIPNAME, read_length); + if (error != osd_file::error::NONE || read_length != ZIPNAME) + return (error == osd_file::error::NONE) ? zip_file::error::FILE_TRUNCATED : zip_file::error::FILE_ERROR; /* compute the final offset */ - *offset = zip->header.local_header_offset + ZIPNAME; - *offset += read_word(zip->buffer + ZIPFNLN); - *offset += read_word(zip->buffer + ZIPXTRALN); + offset = m_header.local_header_offset + ZIPNAME; + offset += read_word(&m_buffer[ZIPFNLN]); + offset += read_word(&m_buffer[ZIPXTRALN]); - return ZIPERR_NONE; + return zip_file::error::NONE; } @@ -662,19 +630,18 @@ static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset) * @return A zip_error. */ -static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length) { - file_error filerr; - UINT32 read_length; - - /* the data is uncompressed; just read it */ - filerr = osd_read(zip->file, buffer, offset, zip->header.compressed_length, &read_length); - if (filerr != FILERR_NONE) - return ZIPERR_FILE_ERROR; - else if (read_length != zip->header.compressed_length) - return ZIPERR_FILE_TRUNCATED; + std::uint32_t read_length; + + // the data is uncompressed; just read it + auto const filerr = m_file->read(buffer, offset, m_header.compressed_length, read_length); + if (filerr != osd_file::error::NONE) + return zip_file::error::FILE_ERROR; + else if (read_length != m_header.compressed_length) + return zip_file::error::FILE_TRUNCATED; else - return ZIPERR_NONE; + return zip_file::error::NONE; } @@ -696,75 +663,148 @@ static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buff * @return A zip_error. */ -static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +zip_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length) { - UINT32 input_remaining = zip->header.compressed_length; - UINT32 read_length; - z_stream stream; - int filerr; + std::uint32_t input_remaining = m_header.compressed_length; int zerr; - /* make sure we don't need a newer mechanism */ - if (zip->header.version_needed > 0x14) - return ZIPERR_UNSUPPORTED; + // make sure we don't need a newer mechanism + if (m_header.version_needed > 0x14) + return zip_file::error::UNSUPPORTED; /* reset the stream */ + z_stream stream; memset(&stream, 0, sizeof(stream)); stream.next_out = (Bytef *)buffer; stream.avail_out = length; - /* initialize the decompressor */ + // initialize the decompressor zerr = inflateInit2(&stream, -MAX_WBITS); if (zerr != Z_OK) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; - /* loop until we're done */ + // loop until we're done while (1) { - /* read in the next chunk of data */ - filerr = osd_read(zip->file, zip->buffer, offset, MIN(input_remaining, sizeof(zip->buffer)), &read_length); - if (filerr != FILERR_NONE) + // read in the next chunk of data + std::uint32_t read_length; + auto const filerr = m_file->read(&m_buffer[0], offset, (std::min<std::uint32_t>)(input_remaining, m_buffer.size()), read_length); + if (filerr != osd_file::error::NONE) { inflateEnd(&stream); - return ZIPERR_FILE_ERROR; + return zip_file::error::FILE_ERROR; } offset += read_length; - /* if we read nothing, but still have data left, the file is truncated */ + // if we read nothing, but still have data left, the file is truncated if (read_length == 0 && input_remaining > 0) { inflateEnd(&stream); - return ZIPERR_FILE_TRUNCATED; + return zip_file::error::FILE_TRUNCATED; } - /* fill out the input data */ - stream.next_in = zip->buffer; + // fill out the input data + stream.next_in = &m_buffer[0]; stream.avail_in = read_length; input_remaining -= read_length; - /* add a dummy byte at end of compressed data */ + // add a dummy byte at end of compressed data if (input_remaining == 0) stream.avail_in++; - /* now inflate */ + // now inflate zerr = inflate(&stream, Z_NO_FLUSH); if (zerr == Z_STREAM_END) break; if (zerr != Z_OK) { inflateEnd(&stream); - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; } } - /* finish decompression */ + // finish decompression zerr = inflateEnd(&stream); if (zerr != Z_OK) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; /* if anything looks funny, report an error */ if (stream.avail_out > 0 || input_remaining > 0) - return ZIPERR_DECOMPRESS_ERROR; + return zip_file::error::DECOMPRESS_ERROR; + + return zip_file::error::NONE; +} - return ZIPERR_NONE; +} // anonymous namespace + + + +/*************************************************************************** + ZIP FILE ACCESS +***************************************************************************/ + +/*------------------------------------------------- + zip_file_open - opens a ZIP file for reading +-------------------------------------------------*/ + +/** + * @fn zip_error zip_file_open(const char *filename, zip_file **zip) + * + * @brief Queries if a given zip file open. + * + * @param filename Filename of the file. + * @param [in,out] zip If non-null, the zip. + * + * @return A zip_error. + */ + +zip_file::error zip_file::open(const std::string &filename, ptr &zip) +{ + // ensure we start with a NULL result + zip.reset(); + + // see if we are in the cache, and reopen if so + zip_file_impl::ptr newimpl(zip_file_impl::find_cached(filename)); + + 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 + { + zip = std::make_unique<zip_file_wrapper>(std::move(newimpl)); + return error::NONE; + } + catch (...) + { + zip_file_impl::close(std::move(newimpl)); + return error::OUT_OF_MEMORY; + } +} + + +/*------------------------------------------------- + zip_file_cache_clear - clear the ZIP file + cache and free all memory +-------------------------------------------------*/ + +/** + * @fn void zip_file_cache_clear(void) + * + * @brief Zip file cache clear. + */ + +void zip_file::cache_clear() +{ + zip_file_impl::cache_clear(); +} + + +zip_file::~zip_file() +{ } |