summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util/unzip.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/unzip.cpp')
-rw-r--r--src/lib/util/unzip.cpp794
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()
+{
}