summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util')
-rw-r--r--src/lib/util/chdcodec.cpp29
-rw-r--r--src/lib/util/chdcodec.h1
-rw-r--r--src/lib/util/delegate.h2
-rw-r--r--src/lib/util/nanosvg.cpp6
-rw-r--r--src/lib/util/options.cpp18
-rw-r--r--src/lib/util/options.h5
-rw-r--r--src/lib/util/plaparse.h2
-rw-r--r--src/lib/util/un7z.cpp152
-rw-r--r--src/lib/util/unzip.cpp1103
-rw-r--r--src/lib/util/vbiparse.h1
-rw-r--r--src/lib/util/zippath.cpp98
11 files changed, 991 insertions, 426 deletions
diff --git a/src/lib/util/chdcodec.cpp b/src/lib/util/chdcodec.cpp
index be1d2022cd7..ba1840489d6 100644
--- a/src/lib/util/chdcodec.cpp
+++ b/src/lib/util/chdcodec.cpp
@@ -1128,20 +1128,35 @@ chd_lzma_decompressor::chd_lzma_decompressor(chd_file &chd, UINT32 hunkbytes, bo
// construct the decoder
LzmaDec_Construct(&m_decoder);
+ // FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK
+ // This code assumes that the current version of the encoder imposes the same requirements on the
+ // decoder as the encoder used to produce the file. This is not necessarily true. The format
+ // needs to be changed so the encoder properties are written to the file.
+
// configure the properties like the compressor did
CLzmaEncProps encoder_props;
chd_lzma_compressor::configure_properties(encoder_props, hunkbytes);
// convert to decoder properties
- CLzmaProps decoder_props;
- decoder_props.lc = encoder_props.lc;
- decoder_props.lp = encoder_props.lp;
- decoder_props.pb = encoder_props.pb;
- decoder_props.dicSize = encoder_props.dictSize;
+ CLzmaEncHandle enc = LzmaEnc_Create(&m_allocator);
+ if (!enc)
+ throw CHDERR_DECOMPRESSION_ERROR;
+ if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)
+ {
+ LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
+ throw CHDERR_DECOMPRESSION_ERROR;
+ }
+ Byte decoder_props[LZMA_PROPS_SIZE];
+ SizeT props_size = sizeof(decoder_props);
+ if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)
+ {
+ LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
+ throw CHDERR_DECOMPRESSION_ERROR;
+ }
+ LzmaEnc_Destroy(enc, &m_allocator, &m_allocator);
// do memory allocations
- SRes res = LzmaDec_Allocate_MAME(&m_decoder, &decoder_props, &m_allocator);
- if (res != SZ_OK)
+ if (LzmaDec_Allocate(&m_decoder, decoder_props, LZMA_PROPS_SIZE, &m_allocator) != SZ_OK)
throw CHDERR_DECOMPRESSION_ERROR;
}
diff --git a/src/lib/util/chdcodec.h b/src/lib/util/chdcodec.h
index f517cdedeeb..bfde8d2cde7 100644
--- a/src/lib/util/chdcodec.h
+++ b/src/lib/util/chdcodec.h
@@ -14,6 +14,7 @@
#define __CHDCODEC_H__
#include "osdcore.h"
+#include "coretmpl.h"
#define CHDCODEC_VERIFY_COMPRESSION 0
diff --git a/src/lib/util/delegate.h b/src/lib/util/delegate.h
index 6b4f6a01f58..ae469f86ed7 100644
--- a/src/lib/util/delegate.h
+++ b/src/lib/util/delegate.h
@@ -83,7 +83,7 @@
#include <typeinfo>
#include <utility>
#include <cstring>
-
+#include <cstdint>
//**************************************************************************
// MACROS
diff --git a/src/lib/util/nanosvg.cpp b/src/lib/util/nanosvg.cpp
new file mode 100644
index 00000000000..09973865675
--- /dev/null
+++ b/src/lib/util/nanosvg.cpp
@@ -0,0 +1,6 @@
+#define NANOSVG_IMPLEMENTATION
+#define NANOSVGRAST_IMPLEMENTATION
+#define NANOSVG_ALL_COLOR_KEYWORDS
+
+#include <nanosvg/src/nanosvg.h>
+#include <nanosvg/src/nanosvgrast.h>
diff --git a/src/lib/util/options.cpp b/src/lib/util/options.cpp
index 7e8b993c0b2..f8ef33b0d39 100644
--- a/src/lib/util/options.cpp
+++ b/src/lib/util/options.cpp
@@ -146,13 +146,13 @@ void core_options::entry::set_flag(UINT32 mask, UINT32 flag)
//-------------------------------------------------
// revert - revert back to our default if we are
-// at or below the given priority
+// within the given priority range
//-------------------------------------------------
-void core_options::entry::revert(int priority)
+void core_options::entry::revert(int priority_hi, int priority_lo)
{
- // if our priority is low enough, revert to the default
- if (m_priority <= priority)
+ // if our priority is within the range, revert to the default
+ if (m_priority <= priority_hi && m_priority >= priority_lo)
{
m_data = m_defdata;
m_priority = OPTION_PRIORITY_DEFAULT;
@@ -465,11 +465,11 @@ bool core_options::parse_ini_file(util::core_file &inifile, int priority, int ig
// priority back to their defaults
//-------------------------------------------------
-void core_options::revert(int priority)
+void core_options::revert(int priority_hi, int priority_lo)
{
// iterate over options and revert to defaults if below the given priority
for (entry &curentry : m_entrylist)
- curentry.revert(priority);
+ curentry.revert(priority_hi, priority_lo);
}
@@ -836,3 +836,9 @@ bool core_options::validate_and_set_data(core_options::entry &curentry, const ch
curentry.set_value(data.c_str(), priority);
return true;
}
+
+core_options::entry *core_options::get_entry(const char *name) const
+{
+ auto curentry = m_entrymap.find(name);
+ return (curentry != m_entrymap.end()) ? curentry->second : nullptr;
+} \ No newline at end of file
diff --git a/src/lib/util/options.h b/src/lib/util/options.h
index 9af9c61233e..ff1cad115de 100644
--- a/src/lib/util/options.h
+++ b/src/lib/util/options.h
@@ -97,7 +97,7 @@ public:
void set_description(const char *description);
void set_flag(UINT32 mask, UINT32 flag);
void mark_changed() { m_changed = true; }
- void revert(int priority);
+ void revert(int priority_hi, int priority_lo);
private:
// internal state
@@ -131,6 +131,7 @@ public:
// getters
entry *first() const { return m_entrylist.first(); }
const char *command() const { return m_command.c_str(); }
+ entry *get_entry(const char *name) const;
// range iterators
using auto_iterator = simple_list<entry>::auto_iterator;
@@ -150,7 +151,7 @@ public:
bool parse_ini_file(util::core_file &inifile, int priority, int ignore_priority, std::string &error_string);
// reverting
- void revert(int priority = OPTION_PRIORITY_MAXIMUM);
+ void revert(int priority_hi = OPTION_PRIORITY_MAXIMUM, int priority_lo = OPTION_PRIORITY_DEFAULT);
// output
std::string output_ini(const core_options *diff = nullptr) const;
diff --git a/src/lib/util/plaparse.h b/src/lib/util/plaparse.h
index db85fac6ffd..e3bfcaf0e2a 100644
--- a/src/lib/util/plaparse.h
+++ b/src/lib/util/plaparse.h
@@ -12,7 +12,7 @@
#define __PLAPARSE_H__
#include "osdcore.h"
-
+#include "jedparse.h"
/***************************************************************************
diff --git a/src/lib/util/un7z.cpp b/src/lib/util/un7z.cpp
index b3d18dcdb8e..95f34179ea0 100644
--- a/src/lib/util/un7z.cpp
+++ b/src/lib/util/un7z.cpp
@@ -16,8 +16,9 @@
#include "unicode.h"
#include "lzma/C/7z.h"
+#include "lzma/C/7zAlloc.h"
#include "lzma/C/7zCrc.h"
-#include "lzma/C/7zVersion.h"
+#include "lzma/C/7zTypes.h"
#include <algorithm>
#include <array>
@@ -40,41 +41,41 @@ struct CSzFile
{
CSzFile() : currfpos(0), length(0), osdfile() {}
- long currfpos;
+ std::uint64_t currfpos;
std::uint64_t length;
osd_file::ptr osdfile;
- WRes read(void *data, std::size_t &size)
+ SRes read(void *data, std::size_t &size)
{
if (!osdfile)
{
- std::printf("un7z.c: called File_Read without file\n");
- return 1;
+ osd_printf_error("un7z: called CSzFile::read without file\n");
+ return SZ_ERROR_READ;
}
- if (!size) return 0;
- size_t originalSize = size;
+ if (!size)
+ return SZ_OK;
- std::uint32_t read_length;
- //osd_file::error err =
- osdfile->read(data, currfpos, originalSize, read_length);
+ // TODO: this casts a size_t to a uint32_t, so it will fail if >=4GB is requested at once (need a loop)
+ std::uint32_t read_length(0);
+ auto const err = osdfile->read(data, currfpos, size, read_length);
size = read_length;
currfpos += read_length;
- if (size == originalSize)
- return 0;
-
- return 0;
+ return (osd_file::error::NONE == err) ? SZ_OK : SZ_ERROR_READ;
}
- WRes seek(Int64 &pos, ESzSeek origin)
+ SRes seek(Int64 &pos, ESzSeek origin)
{
- if (origin == 0) currfpos = pos;
- if (origin == 1) currfpos = currfpos + pos;
- if (origin == 2) currfpos = length -pos;
-
+ switch (origin)
+ {
+ case SZ_SEEK_CUR: currfpos += pos; break;
+ case SZ_SEEK_SET: currfpos = pos; break;
+ case SZ_SEEK_END: currfpos = length + pos; break;
+ default: return SZ_ERROR_READ;
+ }
pos = currfpos;
- return 0;
+ return SZ_OK;
}
};
@@ -109,6 +110,7 @@ public:
{
ptr result;
std::swap(s_cache[cachenum], result);
+ osd_printf_verbose("un7z: found %s in cache\n", filename.c_str());
return result;
}
}
@@ -244,17 +246,6 @@ std::mutex m7z_file_impl::s_cache_mutex;
/* ---------- FileInStream ---------- */
extern "C" {
-static void *SZipAlloc(void *p, std::size_t size)
-{
- return (size == 0) ? nullptr : std::malloc(size);
-}
-
-static void SZipFree(void *p, void *address)
-{
- std::free(address);
-}
-
-
static SRes FileInStream_Read(void *pp, void *buf, size_t *size)
{
return (reinterpret_cast<CFileInStream *>(pp)->read(buf, *size) == 0) ? SZ_OK : SZ_ERROR_READ;
@@ -291,15 +282,19 @@ m7z_file_impl::m7z_file_impl(const std::string &filename)
, m_uchar_buf(128)
, m_utf8_buf(512)
, m_inited(false)
- , m_block_index(0xffffffff) // it can have any value before first call (if outBuffer = 0)
- , m_out_buffer(nullptr) // it must be 0 before first call for each new archive
- , m_out_buffer_size(0) // it can have any value before first call (if outBuffer = 0)
+ , m_block_index(0)
+ , m_out_buffer(nullptr)
+ , m_out_buffer_size(0)
{
- m_alloc_imp.Alloc = SZipAlloc;
- m_alloc_imp.Free = SZipFree;
+ m_alloc_imp.Alloc = &SzAlloc;
+ m_alloc_imp.Free = &SzFree;
+
+ m_alloc_temp_imp.Alloc = &SzAllocTemp;
+ m_alloc_temp_imp.Free = &SzFreeTemp;
- m_alloc_temp_imp.Alloc = SZipAlloc;
- m_alloc_temp_imp.Free = SZipFree;
+ LookToRead_CreateVTable(&m_look_stream, False);
+ m_look_stream.realStream = &m_archive_stream;
+ LookToRead_Init(&m_look_stream);
}
@@ -309,18 +304,24 @@ archive_file::error m7z_file_impl::initialize()
if (err != osd_file::error::NONE)
return archive_file::error::FILE_ERROR;
- LookToRead_CreateVTable(&m_look_stream, False);
- m_look_stream.realStream = &m_archive_stream;
- LookToRead_Init(&m_look_stream);
+ osd_printf_verbose("un7z: opened archive file %s\n", m_filename.c_str());
- CrcGenerateTable();
+ CrcGenerateTable(); // FIXME: doesn't belong here - it should be called once statically
SzArEx_Init(&m_db);
m_inited = true;
-
SRes const res = SzArEx_Open(&m_db, &m_look_stream.s, &m_alloc_imp, &m_alloc_temp_imp);
if (res != SZ_OK)
- return archive_file::error::FILE_ERROR;
+ {
+ osd_printf_error("un7z: error opening %s as 7z archive (%d)\n", m_filename.c_str(), int(res));
+ switch (res)
+ {
+ case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED;
+ case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY;
+ case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED;
+ default: return archive_file::error::FILE_ERROR;
+ }
+ }
return archive_file::error::NONE;
}
@@ -336,6 +337,7 @@ void m7z_file_impl::close(ptr &&archive)
if (!archive) return;
// close the open files
+ osd_printf_verbose("un7z: closing archive file %s and sending to cache\n", archive->m_filename.c_str());
archive->m_archive_stream.osdfile.reset();
// find the first NULL entry in the cache
@@ -347,7 +349,11 @@ void m7z_file_impl::close(ptr &&archive)
// if no room left in the cache, free the bottommost entry
if (cachenum == s_cache.size())
- s_cache[--cachenum].reset();
+ {
+ cachenum--;
+ osd_printf_verbose("un7z: removing %s from cache to make space\n", s_cache[cachenum]->m_filename.c_str());
+ s_cache[cachenum].reset();
+ }
// move everyone else down and place us at the top
for ( ; cachenum > 0; cachenum--)
@@ -368,30 +374,47 @@ void m7z_file_impl::close(ptr &&archive)
archive_file::error m7z_file_impl::decompress(void *buffer, std::uint32_t length)
{
+ // if we don't have enough buffer, error
+ if (length < m_curr_length)
+ {
+ osd_printf_error("un7z: buffer too small to decompress %s from %s\n", m_curr_name.c_str(), m_filename.c_str());
+ return archive_file::error::BUFFER_TOO_SMALL;
+ }
+
// make sure the file is open..
if (!m_archive_stream.osdfile)
{
m_archive_stream.currfpos = 0;
osd_file::error const err = osd_file::open(m_filename, OPEN_FLAG_READ, m_archive_stream.osdfile, m_archive_stream.length);
if (err != osd_file::error::NONE)
+ {
+ osd_printf_error("un7z: error reopening archive file %s (%d)\n", m_filename.c_str(), int(err));
return archive_file::error::FILE_ERROR;
+ }
+ osd_printf_verbose("un7z: reopened archive file %s\n", m_filename.c_str());
}
- size_t offset = 0;
- size_t out_size_processed = 0;
-
+ std::size_t offset(0);
+ std::size_t out_size_processed(0);
SRes const res = SzArEx_Extract(
- &m_db, &m_look_stream.s, m_curr_file_idx,
- &m_block_index,
- &m_out_buffer, &m_out_buffer_size,
- &offset, &out_size_processed,
- &m_alloc_imp, &m_alloc_temp_imp);
-
+ &m_db, &m_look_stream.s, m_curr_file_idx, // requested file
+ &m_block_index, &m_out_buffer, &m_out_buffer_size, // solid block caching
+ &offset, &out_size_processed, // data size/offset
+ &m_alloc_imp, &m_alloc_temp_imp); // allocator helpers
if (res != SZ_OK)
- return archive_file::error::FILE_ERROR;
-
- std::memcpy(buffer, m_out_buffer + offset, length);
+ {
+ osd_printf_error("un7z: error decompressing %s from %s (%d)\n", m_curr_name.c_str(), m_filename.c_str(), int(res));
+ switch (res)
+ {
+ case SZ_ERROR_UNSUPPORTED: return archive_file::error::UNSUPPORTED;
+ case SZ_ERROR_MEM: return archive_file::error::OUT_OF_MEMORY;
+ case SZ_ERROR_INPUT_EOF: return archive_file::error::FILE_TRUNCATED;
+ default: return archive_file::error::DECOMPRESS_ERROR;
+ }
+ }
+ // copy to destination buffer
+ std::memcpy(buffer, m_out_buffer + offset, (std::min<std::size_t>)(length, out_size_processed));
return archive_file::error::NONE;
}
@@ -404,25 +427,24 @@ int m7z_file_impl::search(
bool matchname,
bool partialpath)
{
- for ( ; i < m_db.db.NumFiles; i++)
+ for ( ; i < m_db.NumFiles; i++)
{
- const CSzFileItem &f(m_db.db.Files[i]);
-
make_utf8_name(i);
- const std::uint64_t size(f.Size);
- const std::uint32_t crc(f.Crc);
- const bool crcmatch(crc == search_crc);
+ bool const is_dir(SzArEx_IsDir(&m_db, i));
+ const std::uint64_t size(SzArEx_GetFileSize(&m_db, i));
+ const std::uint32_t crc(m_db.CRCs.Vals[i]);
+ const bool crcmatch(SzBitArray_Check(m_db.CRCs.Defs, i) && (crc == search_crc));
auto const partialoffset(m_utf8_buf.size() - 1 - search_filename.length());
bool const partialpossible((m_utf8_buf.size() > (search_filename.length() + 1)) && (m_utf8_buf[partialoffset - 1] == '/'));
const bool namematch(
!core_stricmp(search_filename.c_str(), &m_utf8_buf[0]) ||
(partialpath && partialpossible && !core_stricmp(search_filename.c_str(), &m_utf8_buf[partialoffset])));
- const bool found = ((!matchcrc && !matchname) || !f.IsDir) && (!matchcrc || crcmatch) && (!matchname || namematch);
+ const bool found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch);
if (found)
{
m_curr_file_idx = i;
- m_curr_is_dir = bool(f.IsDir);
+ m_curr_is_dir = is_dir;
m_curr_name = &m_utf8_buf[0];
m_curr_length = size;
m_curr_crc = crc;
diff --git a/src/lib/util/unzip.cpp b/src/lib/util/unzip.cpp
index 6a9e407252d..cce6c812a76 100644
--- a/src/lib/util/unzip.cpp
+++ b/src/lib/util/unzip.cpp
@@ -11,9 +11,9 @@
#include "unzip.h"
#include "corestr.h"
+#include "hashing.h"
#include "osdcore.h"
-
#include <algorithm>
#include <array>
#include <cassert>
@@ -25,63 +25,12 @@
#include <zlib.h>
+#include "lzma/C/LzmaDec.h"
+
namespace util {
namespace {
/***************************************************************************
- CONSTANTS
-***************************************************************************/
-
-/* offsets in end of central directory structure */
-#define ZIPESIG 0x00
-#define ZIPEDSK 0x04
-#define ZIPECEN 0x06
-#define ZIPENUM 0x08
-#define ZIPECENN 0x0a
-#define ZIPECSZ 0x0c
-#define ZIPEOFST 0x10
-#define ZIPECOML 0x14
-#define ZIPECOM 0x16
-
-/* offsets in central directory entry structure */
-#define ZIPCENSIG 0x00
-#define ZIPCVER 0x04
-#define ZIPCOS 0x05
-#define ZIPCVXT 0x06
-#define ZIPCEXOS 0x07
-#define ZIPCFLG 0x08
-#define ZIPCMTHD 0x0a
-#define ZIPCTIM 0x0c
-#define ZIPCDAT 0x0e
-#define ZIPCCRC 0x10
-#define ZIPCSIZ 0x14
-#define ZIPCUNC 0x18
-#define ZIPCFNL 0x1c
-#define ZIPCXTL 0x1e
-#define ZIPCCML 0x20
-#define ZIPDSK 0x22
-#define ZIPINT 0x24
-#define ZIPEXT 0x26
-#define ZIPOFST 0x2a
-#define ZIPCFN 0x2e
-
-/* offsets in local file header structure */
-#define ZIPLOCSIG 0x00
-#define ZIPVER 0x04
-#define ZIPGENFLG 0x06
-#define ZIPMTHD 0x08
-#define ZIPTIME 0x0a
-#define ZIPDATE 0x0c
-#define ZIPCRC 0x0e
-#define ZIPSIZE 0x12
-#define ZIPUNCMP 0x16
-#define ZIPFNLN 0x1a
-#define ZIPXTRALN 0x1c
-#define ZIPNAME 0x1e
-
-
-
-/***************************************************************************
TYPE DEFINITIONS
***************************************************************************/
@@ -99,10 +48,8 @@ public:
, m_cd_pos(0)
, m_header()
, m_curr_is_dir(false)
- , m_curr_name()
, m_buffer()
{
- std::memset(&m_header, 0, sizeof(m_header));
std::fill(m_buffer.begin(), m_buffer.end(), 0);
}
@@ -116,6 +63,7 @@ public:
{
ptr result;
std::swap(s_cache[cachenum], result);
+ osd_printf_verbose("unzip: found %s in cache\n", filename.c_str());
return result;
}
}
@@ -137,18 +85,52 @@ public:
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))
+ if (m_ecd.disk_number != m_ecd.cd_start_disk_number)
+ {
+ osd_printf_error("unzip: %s central directory starts in another segment\n", m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
+ if (m_ecd.cd_disk_entries != m_ecd.cd_total_entries)
+ {
+ osd_printf_error("unzip: %s not all central directory entries reside in this segment\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (std::size_t(m_ecd.cd_size) != m_ecd.cd_size)
+ {
+ osd_printf_error("unzip: %s central directory too large to load\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
// allocate memory for the central directory
- try { m_cd.resize(m_ecd.cd_size + 1); }
- catch (...) { return archive_file::error::OUT_OF_MEMORY; }
+ try { m_cd.resize(std::size_t(m_ecd.cd_size)); }
+ catch (...)
+ {
+ osd_printf_error("unzip: %s failed to allocate memory for central directory\n", m_filename.c_str());
+ return archive_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) ? archive_file::error::FILE_TRUNCATED : archive_file::error::FILE_ERROR;
+ auto cd_remaining(m_ecd.cd_size);
+ 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)
+ {
+ osd_printf_error("unzip: %s error reading central directory (%d)\n", m_filename.c_str(), int(filerr));
+ return archive_file::error::FILE_ERROR;
+ }
+ if (!read_length)
+ {
+ osd_printf_error("unzip: %s unexpectedly reached end-of-file while reading central directory\n", m_filename.c_str());
+ return archive_file::error::FILE_TRUNCATED;
+ }
+ cd_remaining -= read_length;
+ cd_offs += read_length;
+ }
+ osd_printf_verbose("unzip: read %s central directory\n", m_filename.c_str());
return archive_file::error::NONE;
}
@@ -180,7 +162,7 @@ public:
}
bool current_is_directory() const { return m_curr_is_dir; }
- const std::string &current_name() const { return m_curr_name; }
+ const std::string &current_name() const { return m_header.file_name; }
std::uint64_t current_uncompressed_length() const { return m_header.uncompressed_length; }
std::uint32_t current_crc() const { return m_header.crc; }
@@ -200,7 +182,15 @@ private:
{
auto const filerr = osd_file::open(m_filename, OPEN_FLAG_READ, m_file, m_length);
if (filerr != osd_file::error::NONE)
+ {
+ // 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.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
+ else
+ {
+ osd_printf_verbose("unzip: opened archive file %s\n", m_filename.c_str());
+ }
}
return archive_file::error::NONE;
}
@@ -212,44 +202,31 @@ private:
// 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);
struct file_header
{
- std::uint32_t signature; // central file header signature
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
std::uint16_t compression; // compression method
- std::uint16_t file_time; // last mod file time
- std::uint16_t file_date; // last mod file date
std::uint32_t crc; // crc-32
- std::uint32_t compressed_length; // compressed size
- std::uint32_t uncompressed_length; // uncompressed size
- std::uint16_t filename_length; // filename length
- std::uint16_t extra_field_length; // extra field length
- std::uint16_t file_comment_length; // file comment length
- std::uint16_t start_disk_number; // disk number start
- std::uint16_t internal_attributes; // internal file attributes
- std::uint32_t external_attributes; // external file attributes
- std::uint32_t local_header_offset; // relative offset of local header
- const char * filename; // filename
+ std::uint64_t compressed_length; // compressed size
+ std::uint64_t uncompressed_length; // uncompressed size
+ std::uint32_t start_disk_number; // disk number start
+ std::uint64_t local_header_offset; // relative offset of local header
+ std::string file_name; // file name
};
// 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
+ std::uint32_t disk_number; // number of this disk
+ std::uint32_t cd_start_disk_number; // number of the disk with the start of the central directory
+ std::uint64_t cd_disk_entries; // total number of entries in the central directory on this disk
+ std::uint64_t cd_total_entries; // total number of entries in the central directory
+ std::uint64_t cd_size; // size of the central directory
+ std::uint64_t cd_start_disk_offset; // offset of start of central directory with respect to the starting disk number
};
static constexpr std::size_t DECOMPRESS_BUFSIZE = 16384;
@@ -267,7 +244,6 @@ private:
std::uint32_t m_cd_pos; // position in central directory
file_header m_header; // current file header
bool m_curr_is_dir; // current file is directory
- std::string m_curr_name; // current file name
std::array<std::uint8_t, DECOMPRESS_BUFSIZE> m_buffer; // buffer for decompression
};
@@ -307,40 +283,282 @@ private:
};
+class reader_base
+{
+protected:
+ reader_base(void const *buf) : m_buffer(reinterpret_cast<std::uint8_t const *>(buf)) { }
+
+ std::uint8_t read_byte(std::size_t offs) const
+ {
+ return m_buffer[offs];
+ }
+ std::uint16_t read_word(std::size_t offs) const
+ {
+ 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
+ {
+ return
+ (std::uint32_t(m_buffer[offs + 3]) << 24) |
+ (std::uint32_t(m_buffer[offs + 2]) << 16) |
+ (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
+ {
+ return
+ (std::uint64_t(m_buffer[offs + 7]) << 56) |
+ (std::uint64_t(m_buffer[offs + 6]) << 48) |
+ (std::uint64_t(m_buffer[offs + 5]) << 40) |
+ (std::uint64_t(m_buffer[offs + 4]) << 32) |
+ (std::uint64_t(m_buffer[offs + 3]) << 24) |
+ (std::uint64_t(m_buffer[offs + 2]) << 16) |
+ (std::uint64_t(m_buffer[offs + 1]) << 8) |
+ (std::uint64_t(m_buffer[offs + 0]) << 0);
+ }
+ std::string read_string(std::size_t offs, std::string::size_type len) const
+ {
+ return std::string(reinterpret_cast<char const *>(m_buffer + offs), len);
+ }
+ void read_string(std::string &result, std::size_t offs, std::string::size_type len) const
+ {
+ result.assign(reinterpret_cast<char const *>(m_buffer + offs), len);
+ }
+
+ std::uint8_t const *m_buffer;
+};
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-/**
- * @fn static inline UINT16 read_word(UINT8 *buf)
- *
- * @brief Reads a word.
- *
- * @param [in,out] buf If non-null, the buffer.
- *
- * @return The word.
- */
-
-inline std::uint16_t read_word(std::uint8_t const *buf)
+class extra_field_reader : private reader_base
{
- return (buf[1] << 8) | buf[0];
-}
+public:
+ extra_field_reader(void const *buf, std::size_t len) : 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()); }
+
+ bool length_sufficient() const { 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; }
+
+private:
+ std::size_t m_length;
+};
+
-/**
- * @fn static inline UINT32 read_dword(UINT8 *buf)
- *
- * @brief Reads a double word.
- *
- * @param [in,out] buf If non-null, the buffer.
- *
- * @return The double word.
- */
-
-inline std::uint32_t read_dword(std::uint8_t const *buf)
+class local_file_header_reader : private reader_base
{
- return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
-}
+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); }
+ 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()); }
+
+ bool signature_correct() const { 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; }
+};
+
+
+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); }
+ 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()); }
+ 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; }
+
+ 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; }
+};
+
+
+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; }
+};
+
+
+class ecd64_locator_reader : private reader_base
+{
+public:
+ ecd64_locator_reader(void const *buf) : 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); }
+
+ bool signature_correct() const { return signature() == 0x07064b50; }
+
+ std::size_t total_length() const { return minimum_length(); }
+ static 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); }
+ 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; }
+
+ std::size_t total_length() const { return minimum_length() + comment_length(); }
+ static std::size_t minimum_length() { return 0x16; }
+};
+
+
+class zip64_ext_info_reader : private reader_base
+{
+public:
+ template <typename T>
+ zip64_ext_info_reader(
+ T const &header,
+ extra_field_reader const &field)
+ : reader_base(field.data())
+ , m_uncompressed_size(header.uncompressed_size())
+ , m_compressed_size(header.compressed_size())
+ , m_header_offset(header.header_offset())
+ , m_start_disk(header.start_disk())
+ , m_offs_compressed_size(~m_uncompressed_size ? 0 : 8)
+ , m_offs_header_offset(m_offs_compressed_size + (~m_compressed_size ? 0 : 8))
+ , m_offs_start_disk(m_offs_header_offset + (~m_header_offset ? 0 : 8))
+ , m_offs_end(m_offs_start_disk + (~m_start_disk ? 0 : 4))
+ {
+ }
+
+ 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::size_t total_length() const { return minimum_length() + m_offs_end; }
+ static std::size_t minimum_length() { return 0x00; }
+
+private:
+ std::uint32_t m_uncompressed_size;
+ std::uint32_t m_compressed_size;
+ std::uint32_t m_header_offset;
+ std::uint16_t m_start_disk;
+
+ std::size_t m_offs_compressed_size;
+ std::size_t m_offs_header_offset;
+ std::size_t m_offs_start_disk;
+ std::size_t m_offs_end;
+};
+
+
+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()) { }
+
+ std::uint8_t version() const { return m_buffer[0]; }
+ std::uint32_t name_crc32() const { 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; }
+
+private:
+ std::size_t m_length;
+};
+
+
+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); }
+
+private:
+ std::uint16_t m_value;
+};
@@ -359,19 +577,12 @@ std::mutex zip_file_impl::s_cache_mutex;
to the cache
-------------------------------------------------*/
-/**
- * @fn void zip_file_close(zip_file *zip)
- *
- * @brief Zip file close.
- *
- * @param [in,out] zip If non-null, the zip.
- */
-
void zip_file_impl::close(ptr &&zip)
{
if (!zip) return;
// close the open files
+ osd_printf_verbose("unzip: closing archive file %s and sending to cache\n", zip->m_filename.c_str());
zip->m_file.reset();
// find the first NULL entry in the cache
@@ -383,7 +594,11 @@ void zip_file_impl::close(ptr &&zip)
// if no room left in the cache, free the bottommost entry
if (cachenum == s_cache.size())
- s_cache[--cachenum].reset();
+ {
+ cachenum--;
+ osd_printf_verbose("unzip: removing %s from cache to make space\n", s_cache[cachenum]->m_filename.c_str());
+ s_cache[cachenum].reset();
+ }
// move everyone else down and place us at the top
for ( ; cachenum > 0; cachenum--)
@@ -397,67 +612,90 @@ void zip_file_impl::close(ptr &&zip)
***************************************************************************/
/*-------------------------------------------------
- zip_file_first_entry - return the first entry
- in the ZIP
--------------------------------------------------*/
-
-/*-------------------------------------------------
- zip_file_next_entry - return the next entry
- in the ZIP
+ zip_file_search - return the next matching
+ 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)
{
// if we're at or past the end, we're done
- std::string filename;
- while ((m_cd_pos + ZIPCFN) <= m_ecd.cd_size)
+ while ((m_cd_pos + central_dir_entry_reader::minimum_length()) <= m_ecd.cd_size)
{
- // extract file header info
- std::uint8_t const *const raw = &m_cd[0] + m_cd_pos;
- m_header.signature = read_dword(raw + ZIPCENSIG);
- m_header.version_created = read_word (raw + ZIPCVER);
- m_header.version_needed = read_word (raw + ZIPCVXT);
- m_header.bit_flag = read_word (raw + ZIPCFLG);
- m_header.compression = read_word (raw + ZIPCMTHD);
- m_header.file_time = read_word (raw + ZIPCTIM);
- m_header.file_date = read_word (raw + ZIPCDAT);
- m_header.crc = read_dword(raw + ZIPCCRC);
- m_header.compressed_length = read_dword(raw + ZIPCSIZ);
- m_header.uncompressed_length = read_dword(raw + ZIPCUNC);
- m_header.filename_length = read_word (raw + ZIPCFNL);
- m_header.extra_field_length = read_word (raw + ZIPCXTL);
- m_header.file_comment_length = read_word (raw + ZIPCCML);
- m_header.start_disk_number = read_word (raw + ZIPDSK);
- m_header.internal_attributes = read_word (raw + ZIPINT);
- m_header.external_attributes = read_dword(raw + ZIPEXT);
- m_header.local_header_offset = read_dword(raw + ZIPOFST);
- m_header.filename = reinterpret_cast<const char *>(raw + ZIPCFN);
-
// make sure we have enough data
- std::uint32_t const rawlength = ZIPCFN + m_header.filename_length + m_header.extra_field_length + m_header.file_comment_length;
- if ((m_cd_pos + rawlength) > m_ecd.cd_size)
+ central_dir_entry_reader const reader(&m_cd[0] + m_cd_pos);
+ 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();
+
// advance the position
- m_cd_pos += rawlength;
+ m_cd_pos += reader.total_length();
- // copy the filename filename
- bool const is_dir((m_header.filename_length > 0) && (m_header.filename[m_header.filename_length - 1] == '/'));
- filename.assign(m_header.filename, m_header.filename_length - (is_dir ? 1 : 0));
+ // 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())
+ {
+ // look for ZIP64 extended info
+ if ((extra.header_id() == 0x0001) && (extra.data_size() >= zip64_ext_info_reader::minimum_length()))
+ {
+ zip64_ext_info_reader const ext64(reader, extra);
+ if (extra.data_size() >= ext64.total_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();
+ }
+ }
+
+ // 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)
+ {
+ 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)
+ {
+ utf8path.unicode_name(m_header.file_name);
+ is_utf8 = true;
+ }
+ }
+ }
+ }
+
+ // 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);
// check to see if it matches query
bool const crcmatch(search_crc == m_header.crc);
- auto const partialoffset(filename.length() - search_filename.length());
- bool const partialpossible((filename.length() > search_filename.length()) && (filename[partialoffset - 1] == '/'));
+ 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(), filename.c_str()) ||
- (partialpath && partialpossible && !core_stricmp(search_filename.c_str(), filename.c_str() + partialoffset)));
+ !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)));
bool const found = ((!matchcrc && !matchname) || !is_dir) && (!matchcrc || crcmatch) && (!matchname || namematch);
if (found)
{
m_curr_is_dir = is_dir;
- m_curr_name = std::move(filename);
return 0;
}
}
@@ -470,18 +708,6 @@ int zip_file_impl::search(std::uint32_t search_crc, const std::string &search_fi
from a ZIP into the target buffer
-------------------------------------------------*/
-/**
- * @fn zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length)
- *
- * @brief Zip file decompress.
- *
- * @param [in,out] zip If non-null, the zip.
- * @param [in,out] buffer If non-null, the buffer.
- * @param length The length.
- *
- * @return A zip_error.
- */
-
archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length)
{
archive_file::error ziperr;
@@ -489,11 +715,17 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
// if we don't have enough buffer, error
if (length < m_header.uncompressed_length)
+ {
+ osd_printf_error("unzip: buffer too small to decompress %s from %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::BUFFER_TOO_SMALL;
+ }
// make sure the info in the header aligns with what we know
if (m_header.start_disk_number != m_ecd.disk_number)
+ {
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
return archive_file::error::UNSUPPORTED;
+ }
// get the compressed data offset
ziperr = get_compressed_data_offset(offset);
@@ -511,7 +743,14 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
ziperr = decompress_data_type_8(offset, buffer, length);
break;
+ case 14:
+ ziperr = decompress_data_type_14(offset, buffer, length);
+ break;
+
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;
}
@@ -528,16 +767,6 @@ archive_file::error zip_file_impl::decompress(void *buffer, std::uint32_t length
read_ecd - read the ECD data
-------------------------------------------------*/
-/**
- * @fn static zip_error read_ecd(zip_file *zip)
- *
- * @brief Reads an ecd.
- *
- * @param [in,out] zip If non-null, the zip.
- *
- * @return The ecd.
- */
-
archive_file::error zip_file_impl::read_ecd()
{
// make sure the file handle is open
@@ -546,6 +775,8 @@ archive_file::error zip_file_impl::read_ecd()
return ziperr;
// we may need multiple tries
+ osd_file::error error;
+ std::uint32_t read_length;
std::uint32_t buflen = 1024;
while (buflen < 65536)
{
@@ -556,50 +787,136 @@ 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 (...) { return archive_file::error::OUT_OF_MEMORY; }
+ catch (...)
+ {
+ osd_printf_error("unzip: %s failed to allocate memory for ECD search\n", m_filename.c_str());
+ return archive_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)
+ error = m_file->read(&buffer[0], m_length - buflen, buflen, read_length);
+ if ((error != osd_file::error::NONE) || (read_length != buflen))
+ {
+ osd_printf_error("unzip: %s error reading file to search for ECD (%d)\n", m_filename.c_str(), int(error));
return archive_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)
+ for (offset = buflen - ecd_reader::minimum_length(); offset >= 0; offset--)
+ {
+ ecd_reader reader(buffer.get() + offset);
+ if (reader.signature_correct() && ((reader.total_length() + offset) <= buflen))
break;
+ }
// if we found it, fill out the data
if (offset >= 0)
{
- // 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(&m_ecd.raw[0], &m_ecd.raw[offset], m_ecd.rawlength);
- m_ecd.raw[m_ecd.rawlength] = 0;
-
- /* extract ecd info */
- 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]);
+ osd_printf_verbose("unzip: found %s ECD\n", m_filename.c_str());
+
+ // extract ECD info
+ ecd_reader const ecd_rd(buffer.get() + offset);
+ m_ecd.disk_number = ecd_rd.this_disk_no();
+ m_ecd.cd_start_disk_number = ecd_rd.dir_start_disk();
+ m_ecd.cd_disk_entries = ecd_rd.dir_disk_entries();
+ m_ecd.cd_total_entries = ecd_rd.dir_total_entries();
+ m_ecd.cd_size = ecd_rd.dir_size();
+ m_ecd.cd_start_disk_offset = ecd_rd.dir_offset();
+
+ // is the file too small to contain a ZIP64 ECD locator?
+ 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.c_str());
+ return archive_file::error::NONE;
+ }
+
+ // try to read the ZIP64 ECD locator
+ error = m_file->read(
+ &buffer[0],
+ m_length - buflen + offset - ecd64_locator_reader::minimum_length(),
+ ecd64_locator_reader::minimum_length(),
+ read_length);
+ if ((error != osd_file::error::NONE) || (read_length != ecd64_locator_reader::minimum_length()))
+ {
+ osd_printf_error("unzip: error reading %s to search for ZIP64 ECD locator (%d)\n", m_filename.c_str(), int(error));
+ return archive_file::error::FILE_ERROR;
+ }
+
+ // if the signature isn't correct, it's not a ZIP64 archive
+ ecd64_locator_reader const ecd64_loc_rd(buffer.get());
+ if (!ecd64_loc_rd.signature_correct())
+ {
+ osd_printf_verbose("unzip: %s has no ZIP64 ECD locator\n", m_filename.c_str());
+ return archive_file::error::NONE;
+ }
+
+ // check that the ZIP64 ECD is in this segment (assuming this segment is the last segment)
+ if ((ecd64_loc_rd.ecd64_disk() + 1) != ecd64_loc_rd.total_disks())
+ {
+ osd_printf_error("unzip: %s ZIP64 ECD resides in a different segment\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+
+ // 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)
+ {
+ osd_printf_error("unzip: error reading %s ZIP64 ECD (%d)\n", m_filename.c_str(), int(error));
+ return archive_file::error::FILE_ERROR;
+ }
+ if (read_length != ecd64_reader::minimum_length())
+ {
+ osd_printf_error("unzip: unexpectedly reached end-of-file while reading %s ZIP64 ECD\n", m_filename.c_str());
+ return archive_file::error::FILE_TRUNCATED;
+ }
+
+ // check ZIP64 ECD
+ ecd64_reader const ecd64_rd(buffer.get());
+ 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()));
+ return archive_file::error::BAD_SIGNATURE;
+ }
+ if (ecd64_rd.total_length() < ecd64_reader::minimum_length())
+ {
+ osd_printf_error("unzip: %s ZIP64 ECD appears to be too small\n", m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (ecd64_rd.version_needed() > 63)
+ {
+ 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));
+ return archive_file::error::UNSUPPORTED;
+ }
+ osd_printf_verbose("unzip: found %s ZIP64 ECD\n", m_filename.c_str());
+
+ // extract ZIP64 ECD info
+ m_ecd.disk_number = ecd64_rd.this_disk_no();
+ m_ecd.cd_start_disk_number = ecd64_rd.dir_start_disk();
+ m_ecd.cd_disk_entries = ecd64_rd.dir_disk_entries();
+ m_ecd.cd_total_entries = ecd64_rd.dir_total_entries();
+ m_ecd.cd_size = ecd64_rd.dir_size();
+ m_ecd.cd_start_disk_offset = ecd64_rd.dir_offset();
+
return archive_file::error::NONE;
}
// didn't find it; free this buffer and expand our search
if (buflen < m_length)
+ {
buflen *= 2;
+ }
else
+ {
+ osd_printf_error("unzip: %s couldn't find ECD\n", m_filename.c_str());
return archive_file::error::BAD_SIGNATURE;
+ }
}
+ osd_printf_error("unzip: %s couldn't find ECD in last 64KiB\n", m_filename.c_str());
return archive_file::error::OUT_OF_MEMORY;
}
@@ -609,19 +926,33 @@ archive_file::error zip_file_impl::read_ecd()
offset of the compressed data
-------------------------------------------------*/
-/**
- * @fn static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset)
- *
- * @brief Gets compressed data offset.
- *
- * @param [in,out] zip If non-null, the zip.
- * @param [in,out] offset If non-null, the offset.
- *
- * @return The compressed data offset.
- */
-
archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &offset)
{
+ // don't support a number of features
+ general_flag_reader const flags(m_header.bit_flag);
+ if (m_header.start_disk_number != m_ecd.disk_number)
+ {
+ osd_printf_error("unzip: %s does not reside in segment %s\n", m_header.file_name.c_str(), m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (m_header.version_needed > 63)
+ {
+ 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));
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (flags.encrypted() || flags.strong_encryption())
+ {
+ osd_printf_error("unzip: %s in %s is encrypted\n", m_header.file_name.c_str(), m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+ if (flags.patch_data())
+ {
+ osd_printf_error("unzip: %s in %s is compressed patch data\n", m_header.file_name.c_str(), m_filename.c_str());
+ return archive_file::error::UNSUPPORTED;
+ }
+
// make sure the file handle is open
auto const ziperr = reopen();
if (ziperr != archive_file::error::NONE)
@@ -629,14 +960,32 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
// 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) ? archive_file::error::FILE_TRUNCATED : archive_file::error::FILE_ERROR;
+ 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)
+ {
+ 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;
+ }
+ 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());
+ return archive_file::error::FILE_TRUNCATED;
+ }
- /* compute the final offset */
- offset = m_header.local_header_offset + ZIPNAME;
- offset += read_word(&m_buffer[ZIPFNLN]);
- offset += read_word(&m_buffer[ZIPXTRALN]);
+ // compute the final offset
+ local_file_header_reader reader(&m_buffer[0]);
+ 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()));
+ return archive_file::error::BAD_SIGNATURE;
+ }
+ offset = m_header.local_header_offset + reader.total_length();
return archive_file::error::NONE;
}
@@ -652,31 +1001,25 @@ archive_file::error zip_file_impl::get_compressed_data_offset(std::uint64_t &off
type 0 data (which is uncompressed)
-------------------------------------------------*/
-/**
- * @fn static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length)
- *
- * @brief Decompress the data type 0.
- *
- * @param [in,out] zip If non-null, the zip.
- * @param offset The offset.
- * @param [in,out] buffer If non-null, the buffer.
- * @param length The length.
- *
- * @return A zip_error.
- */
-
archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset, void *buffer, std::uint32_t length)
{
- std::uint32_t read_length;
-
// 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)
+ {
+ osd_printf_error("unzip: error reading %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(filerr));
return archive_file::error::FILE_ERROR;
+ }
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.c_str(), m_filename.c_str());
return archive_file::error::FILE_TRUNCATED;
+ }
else
+ {
return archive_file::error::NONE;
+ }
}
@@ -685,55 +1028,56 @@ archive_file::error zip_file_impl::decompress_data_type_0(std::uint64_t offset,
type 8 data (which is deflated)
-------------------------------------------------*/
-/**
- * @fn static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length)
- *
- * @brief Decompress the data type 8.
- *
- * @param [in,out] zip If non-null, the zip.
- * @param offset The offset.
- * @param [in,out] buffer If non-null, the buffer.
- * @param length The length.
- *
- * @return A zip_error.
- */
-
archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset, void *buffer, std::uint32_t length)
{
- std::uint32_t input_remaining = m_header.compressed_length;
+ std::uint64_t input_remaining = m_header.compressed_length;
int zerr;
- // make sure we don't need a newer mechanism
- if (m_header.version_needed > 0x14)
- return archive_file::error::UNSUPPORTED;
-
- /* reset the stream */
+ // reset the stream
z_stream stream;
- memset(&stream, 0, sizeof(stream));
- stream.next_out = (Bytef *)buffer;
+ stream.zalloc = Z_NULL;
+ stream.zfree = Z_NULL;
+ stream.opaque = Z_NULL;
+ stream.avail_in = 0;
+ stream.next_out = reinterpret_cast<Bytef *>(buffer);
stream.avail_out = length;
// initialize the decompressor
zerr = inflateInit2(&stream, -MAX_WBITS);
if (zerr != Z_OK)
+ {
+ 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;
+ }
// loop until we're done
while (1)
{
// 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);
+ std::uint32_t read_length(0);
+ auto const filerr = m_file->read(
+ &m_buffer[0],
+ offset,
+ std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())),
+ read_length);
if (filerr != osd_file::error::NONE)
{
+ 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));
inflateEnd(&stream);
return archive_file::error::FILE_ERROR;
}
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 == 0) && (input_remaining > 0))
{
+ 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());
inflateEnd(&stream);
return archive_file::error::FILE_TRUNCATED;
}
@@ -750,9 +1094,12 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// now inflate
zerr = inflate(&stream, Z_NO_FLUSH);
if (zerr == Z_STREAM_END)
+ {
break;
- if (zerr != Z_OK)
+ }
+ else if (zerr != Z_OK)
{
+ osd_printf_error("unzip: error inflating %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
inflateEnd(&stream);
return archive_file::error::DECOMPRESS_ERROR;
}
@@ -761,15 +1108,210 @@ archive_file::error zip_file_impl::decompress_data_type_8(std::uint64_t offset,
// finish decompression
zerr = inflateEnd(&stream);
if (zerr != Z_OK)
+ {
+ osd_printf_error("unzip: error finishing inflation of %s from %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), zerr);
return archive_file::error::DECOMPRESS_ERROR;
+ }
- /* if anything looks funny, report an error */
- if (stream.avail_out > 0 || input_remaining > 0)
+ // if anything looks funny, report an error
+ if ((stream.avail_out > 0) || (input_remaining > 0))
+ {
+ 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());
return archive_file::error::DECOMPRESS_ERROR;
+ }
return archive_file::error::NONE;
}
+
+/*-------------------------------------------------
+ decompress_data_type_14 - decompress
+ type 14 data (LZMA)
+-------------------------------------------------*/
+
+archive_file::error zip_file_impl::decompress_data_type_14(std::uint64_t offset, void *buffer, std::uint32_t length)
+{
+ // two-byte version
+ // two-byte properties size (little-endian)
+ // properties
+ // compressed data
+
+ assert(4 <= m_buffer.size());
+ assert(LZMA_PROPS_SIZE <= m_buffer.size());
+
+ bool const eos_mark(general_flag_reader(m_header.bit_flag).lzma_eos_mark());
+ Byte *const output(reinterpret_cast<Byte *>(buffer));
+ SizeT output_remaining(length);
+ std::uint64_t input_remaining(m_header.compressed_length);
+
+ std::uint32_t read_length;
+ osd_file::error 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); };
+ CLzmaDec stream;
+ LzmaDec_Construct(&stream);
+
+ // need to read LZMA properties before we can initialise the decompressor
+ if (4 > input_remaining)
+ {
+ 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());
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ filerr = m_file->read(&m_buffer[0], offset, 4, read_length);
+ if (filerr != osd_file::error::NONE)
+ {
+ 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;
+ }
+ offset += read_length;
+ input_remaining -= read_length;
+ if (4 != read_length)
+ {
+ 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());
+ 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]));
+ if (props_size > m_buffer.size())
+ {
+ osd_printf_error(
+ "unzip: %s in %s has excessively large LZMA properties\n",
+ m_header.file_name.c_str(), m_filename.c_str());
+ 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());
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ filerr = m_file->read(&m_buffer[0], offset, props_size, read_length);
+ if (filerr != osd_file::error::NONE)
+ {
+ 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;
+ }
+ offset += read_length;
+ input_remaining -= read_length;
+ if (props_size != read_length)
+ {
+ 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());
+ return archive_file::error::FILE_TRUNCATED;
+ }
+
+ // initialize the decompressor
+ lzerr = LzmaDec_Allocate(&stream, &m_buffer[0], props_size, &alloc_imp);
+ if (SZ_ERROR_MEM == lzerr)
+ {
+ 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;
+ }
+ 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));
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ LzmaDec_Init(&stream);
+
+ // loop until we're done
+ while (0 < input_remaining)
+ {
+ // read in the next chunk of data
+ filerr = m_file->read(
+ &m_buffer[0],
+ offset,
+ std::uint32_t((std::min<std::uint64_t>)(input_remaining, m_buffer.size())),
+ read_length);
+ if (filerr != osd_file::error::NONE)
+ {
+ 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));
+ LzmaDec_Free(&stream, &alloc_imp);
+ return archive_file::error::FILE_ERROR;
+ }
+ 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))
+ {
+ 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());
+ LzmaDec_Free(&stream, &alloc_imp);
+ return archive_file::error::FILE_TRUNCATED;
+ }
+
+ // now decompress
+ SizeT len(read_length);
+ // FIXME: is the input length really an in/out parameter or not?
+ // At least on reaching the end of the input, the input length doesn't seem to get updated
+ lzerr = LzmaDec_DecodeToBuf(
+ &stream,
+ output + length - output_remaining,
+ &output_remaining,
+ reinterpret_cast<Byte const *>(&m_buffer[0]),
+ &len,
+ ((0 == input_remaining) && eos_mark) ? LZMA_FINISH_END : LZMA_FINISH_ANY,
+ &lzstatus);
+ if (SZ_OK != lzerr)
+ {
+ osd_printf_error("unzip: error decoding LZMA data for %s in %s (%d)\n", m_header.file_name.c_str(), m_filename.c_str(), int(lzerr));
+ LzmaDec_Free(&stream, &alloc_imp);
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ }
+
+ // finish decompression
+ LzmaDec_Free(&stream, &alloc_imp);
+
+ // if anything looks funny, report an error
+ if (LZMA_STATUS_FINISHED_WITH_MARK == lzstatus)
+ {
+ return archive_file::error::NONE;
+ }
+ 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));
+ 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));
+ return archive_file::error::DECOMPRESS_ERROR;
+ }
+ else
+ {
+ return archive_file::error::NONE;
+ }
+}
+
} // anonymous namespace
@@ -790,17 +1332,6 @@ void m7z_file_cache_clear();
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.
- */
-
archive_file::error archive_file::open_zip(const std::string &filename, ptr &result)
{
// ensure we start with a NULL result
diff --git a/src/lib/util/vbiparse.h b/src/lib/util/vbiparse.h
index 486d17c5978..687f984622c 100644
--- a/src/lib/util/vbiparse.h
+++ b/src/lib/util/vbiparse.h
@@ -13,6 +13,7 @@
#ifndef __VBIPARSE_H__
#define __VBIPARSE_H__
+#include "osdcomm.h"
/***************************************************************************
CONSTANTS
diff --git a/src/lib/util/zippath.cpp b/src/lib/util/zippath.cpp
index f16dcf824e6..1e4fb6dd293 100644
--- a/src/lib/util/zippath.cpp
+++ b/src/lib/util/zippath.cpp
@@ -17,6 +17,7 @@
#include <cassert>
#include <cctype>
+#include <forward_list>
#include <new>
@@ -26,20 +27,6 @@ namespace util {
***************************************************************************/
/**
- * @struct zippath_returned_directory
- *
- * @brief A zippath returned directory.
- */
-
-struct zippath_returned_directory
-{
- /** @brief The next. */
- zippath_returned_directory *next;
- /** @brief The name. */
- std::string name;
-};
-
-/**
* @class zippath_directory
*
* @brief A zippath directory.
@@ -49,11 +36,13 @@ class zippath_directory
{
public:
zippath_directory()
- : returned_parent(false),
- directory(nullptr),
- called_zip_first(false),
- zipfile(nullptr),
- returned_dirlist(nullptr) { }
+ : returned_parent(false)
+ , directory(nullptr)
+ , called_zip_first(false)
+ , zipfile(nullptr)
+ , returned_dirlist()
+ {
+ }
/* common */
/** @brief true to returned parent. */
@@ -73,7 +62,7 @@ public:
/** @brief The zipprefix. */
std::string zipprefix;
/** @brief The returned dirlist. */
- zippath_returned_directory *returned_dirlist;
+ std::forward_list<std::string> returned_dirlist;
};
@@ -856,12 +845,7 @@ void zippath_closedir(zippath_directory *directory)
if (directory->zipfile != nullptr)
directory->zipfile.reset();
- while (directory->returned_dirlist != nullptr)
- {
- zippath_returned_directory *dirlist = directory->returned_dirlist;
- directory->returned_dirlist = directory->returned_dirlist->next;
- delete dirlist;
- }
+ directory->returned_dirlist.clear();
delete directory;
}
@@ -887,24 +871,28 @@ void zippath_closedir(zippath_directory *directory)
static const char *get_relative_path(zippath_directory const &directory)
{
auto len = directory.zipprefix.length();
- const char *prefix = directory.zipprefix.c_str();
+ char const *prefix = directory.zipprefix.c_str();
while (is_zip_file_separator(*prefix))
{
len--;
prefix++;
}
- if ((len <= directory.zipfile->current_name().length()) &&
- !strncmp(prefix, directory.zipfile->current_name().c_str(), len))
+ std::string const &current(directory.zipfile->current_name());
+ char const *result = directory.zipfile->current_name().c_str() + len;
+ if ((current.length() >= len) &&
+ !strncmp(prefix, current.c_str(), len) &&
+ (!*prefix || is_zip_file_separator(*result) || is_zip_file_separator(directory.zipprefix.back())))
{
- const char *result = &directory.zipfile->current_name().c_str()[len];
while (is_zip_file_separator(*result))
result++;
return *result ? result : nullptr;
}
-
- return nullptr;
+ else
+ {
+ return nullptr;
+ }
}
@@ -925,11 +913,6 @@ static const char *get_relative_path(zippath_directory const &directory)
const osd_directory_entry *zippath_readdir(zippath_directory *directory)
{
const osd_directory_entry *result = nullptr;
- int header;
- const char *relpath;
- const char *separator;
- const char *s;
- zippath_returned_directory *rdent;
if (!directory->returned_parent)
{
@@ -947,10 +930,10 @@ const osd_directory_entry *zippath_readdir(zippath_directory *directory)
{
result = osd_readdir(directory->directory);
}
- while((result != nullptr) && (!strcmp(result->name, ".") || !strcmp(result->name, "..")));
+ while (result && (!strcmp(result->name, ".") || !strcmp(result->name, "..")));
/* special case - is this entry a ZIP file? if so we need to return it as a "directory" */
- if ((result != nullptr) && (is_zip_file(result->name) || is_7z_file(result->name)))
+ if (result && (is_zip_file(result->name) || is_7z_file(result->name)))
{
/* copy; but change the entry type */
directory->returned_entry = *result;
@@ -960,47 +943,46 @@ const osd_directory_entry *zippath_readdir(zippath_directory *directory)
}
else if (directory->zipfile)
{
+ char const *relpath;
do
{
/* a zip file read */
+ int header;
do
{
- if (!directory->called_zip_first)
- header = directory->zipfile->first_file();
- else
- header = directory->zipfile->next_file();
+ header = directory->called_zip_first ? directory->zipfile->next_file() : directory->zipfile->first_file();
directory->called_zip_first = true;
relpath = nullptr;
}
- while((header >= 0) && ((relpath = get_relative_path(*directory)) == nullptr));
+ while ((header >= 0) && ((relpath = get_relative_path(*directory)) == nullptr));
- if (relpath != nullptr)
+ if (relpath)
{
/* we've found a ZIP entry; but this may be an entry deep within the target directory */
- for (s = relpath; *s && !is_zip_file_separator(*s); s++)
- ;
- separator = *s ? s : nullptr;
+ char const *separator = relpath;
+ while (*separator && !is_zip_file_separator(*separator)) separator++;
- if (separator != nullptr)
+ if (*separator || directory->zipfile->current_is_directory())
{
/* a nested entry; loop through returned_dirlist to see if we've returned the parent directory */
- for (rdent = directory->returned_dirlist; rdent != nullptr; rdent = rdent->next)
+ auto const len(separator - relpath);
+ auto rdent = directory->returned_dirlist.begin();
+ while (directory->returned_dirlist.end() != rdent)
{
- if (!core_strnicmp(rdent->name.c_str(), relpath, separator - relpath))
+ if ((rdent->length() == len) && !core_strnicmp(rdent->c_str(), relpath, len))
break;
+ else
+ ++rdent;
}
- if (rdent == nullptr)
+ if (directory->returned_dirlist.end() == rdent)
{
/* we've found a new directory; add this to returned_dirlist */
- rdent = new zippath_returned_directory;
- rdent->next = directory->returned_dirlist;
- rdent->name.assign(relpath, separator - relpath);
- directory->returned_dirlist = rdent;
+ directory->returned_dirlist.emplace_front(relpath, separator - relpath);
/* ...and return it */
memset(&directory->returned_entry, 0, sizeof(directory->returned_entry));
- directory->returned_entry.name = rdent->name.c_str();
+ directory->returned_entry.name = directory->returned_dirlist.front().c_str();
directory->returned_entry.type = ENTTYPE_DIR;
result = &directory->returned_entry;
}
@@ -1016,7 +998,7 @@ const osd_directory_entry *zippath_readdir(zippath_directory *directory)
}
}
}
- while((relpath != nullptr) && (result == nullptr));
+ while (relpath && !result);
}
return result;
}