diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/unarchiver')
13 files changed, 1572 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/archive.h b/Src/external_dependencies/openmpt-trunk/unarchiver/archive.h new file mode 100644 index 00000000..87b508b1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/archive.h @@ -0,0 +1,96 @@ +/* + * archive.h + * --------- + * Purpose: archive loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "../common/FileReader.h" +#include <string> +#include <vector> + +OPENMPT_NAMESPACE_BEGIN + +enum class ArchiveFileType +{ + Invalid, + Normal, + Special, +}; + +struct ArchiveFileInfo +{ + mpt::PathString name; + ArchiveFileType type = ArchiveFileType::Invalid; + uint64 size = 0; + mpt::ustring comment; + uint64 cookie1 = 0; + uint64 cookie2 = 0; +}; + +class IArchive +{ +public: + using const_iterator = std::vector<ArchiveFileInfo>::const_iterator; +protected: + IArchive() {} +public: + virtual ~IArchive() {} + +public: + virtual bool IsArchive() const = 0; + virtual mpt::ustring GetComment() const = 0; + virtual bool ExtractFile(std::size_t index) = 0; + virtual FileReader GetOutputFile() const = 0; + virtual std::size_t size() const = 0; + virtual IArchive::const_iterator begin() const = 0; + virtual IArchive::const_iterator end() const = 0; + virtual const ArchiveFileInfo & operator [] (std::size_t index) const = 0; +}; + +class ArchiveBase + : public IArchive +{ +protected: + FileReader inFile; + mpt::ustring comment; + std::vector<ArchiveFileInfo> contents; + std::vector<char> data; +public: + ArchiveBase(const FileReader &inFile) + : inFile(inFile) + { + return; + } + ~ArchiveBase() override + { + return; + } + bool ExtractFile(std::size_t index) override { MPT_UNREFERENCED_PARAMETER(index); return false; } // overwrite this +public: + bool IsArchive() const override + { + return !contents.empty(); + } + mpt::ustring GetComment() const override + { + return comment; + } + FileReader GetOutputFile() const override + { + return FileReader(mpt::byte_cast<mpt::const_byte_span>(mpt::as_span(data))); + } + std::size_t size() const override { return contents.size(); } + IArchive::const_iterator begin() const override { return contents.begin(); } + IArchive::const_iterator end() const override { return contents.end(); } + const ArchiveFileInfo & operator [] (std::size_t index) const override { return contents[index]; } +}; + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.cpp new file mode 100644 index 00000000..a2b8001e --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.cpp @@ -0,0 +1,83 @@ +/* + * unancient.cpp + * ------------- + * Purpose: Implementation file for extracting modules from compressed files supported by libancient + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "unancient.h" + +#ifdef MPT_WITH_ANCIENT +#include <ancient/ancient.hpp> +#endif // MPT_WITH_ANCIENT + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef MPT_WITH_ANCIENT + + +CAncientArchive::CAncientArchive(FileReader &file) + : ArchiveBase(file) +{ + inFile.Rewind(); + try + { + auto dataView = inFile.GetPinnedView(); + if(!ancient::Decompressor::detect(mpt::byte_cast<const std::uint8_t*>(dataView.data()), dataView.size())) + { + return; + } + ancient::Decompressor decompressor{mpt::byte_cast<const std::uint8_t*>(dataView.data()), dataView.size(), true, true}; + if(decompressor.getImageSize() || decompressor.getImageOffset()) + { + // skip disk images + return; + } + ArchiveFileInfo fileInfo; + fileInfo.name = inFile.GetOptionalFileName().value_or(P_("")); + fileInfo.type = ArchiveFileType::Normal; + fileInfo.size = decompressor.getRawSize().value_or(0); + contents.push_back(fileInfo); + } catch(const ancient::Error &) + { + return; + } +} + + +CAncientArchive::~CAncientArchive() +{ + return; +} + + +bool CAncientArchive::ExtractFile(std::size_t index) +{ + if(index >= contents.size()) + { + return false; + } + data.clear(); + inFile.Rewind(); + try + { + auto dataView = inFile.GetPinnedView(); + ancient::Decompressor decompressor{mpt::byte_cast<const std::uint8_t*>(dataView.data()), dataView.size(), true, true}; + data = mpt::buffer_cast<std::vector<char>>(decompressor.decompress(true)); + } catch (const ancient::Error &) + { + return false; + } + return (data.size() > 0); +} + + +#endif // MPT_WITH_ANCIENT + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.h b/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.h new file mode 100644 index 00000000..dff2e944 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unancient.h @@ -0,0 +1,32 @@ +/* + * ununancient.h + * ------------- + * Purpose: Header file extracting modules from compressed files supported by libancient + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "archive.h" + +OPENMPT_NAMESPACE_BEGIN + +#ifdef MPT_WITH_ANCIENT + +class CAncientArchive + : public ArchiveBase +{ +public: + CAncientArchive(FileReader &file); + virtual ~CAncientArchive(); +public: + bool ExtractFile(std::size_t index) override; +}; + +#endif // MPT_WITH_ANCIENT + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.cpp new file mode 100644 index 00000000..8351be38 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.cpp @@ -0,0 +1,173 @@ +/* + * unarchiver.cpp + * -------------- + * Purpose: archive loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "unarchiver.h" +#include "../common/FileReader.h" + + +OPENMPT_NAMESPACE_BEGIN + + +CUnarchiver::CUnarchiver(FileReader &file) + : impl(nullptr) + , inFile(file) + , emptyArchive(inFile) +#if (defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)) || defined(MPT_WITH_MINIZ) + , zipArchive(inFile) +#endif +#ifdef MPT_WITH_LHASA + , lhaArchive(inFile) +#endif +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + , gzipArchive(inFile) +#endif +#ifdef MPT_WITH_UNRAR + , rarArchive(inFile) +#endif +#ifdef MPT_WITH_ANCIENT + , ancientArchive(inFile) +#endif +{ + inFile.Rewind(); +#if (defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)) || defined(MPT_WITH_MINIZ) + if(zipArchive.IsArchive()) { impl = &zipArchive; return; } +#endif +#ifdef MPT_WITH_LHASA + if(lhaArchive.IsArchive()) { impl = &lhaArchive; return; } +#endif +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + if(gzipArchive.IsArchive()) { impl = &gzipArchive; return; } +#endif +#ifdef MPT_WITH_UNRAR + if(rarArchive.IsArchive()) { impl = &rarArchive; return; } +#endif +#ifdef MPT_WITH_ANCIENT + if(ancientArchive.IsArchive()) { impl = &ancientArchive; return; } +#endif + impl = &emptyArchive; +} + + +CUnarchiver::~CUnarchiver() +{ + return; +} + + +static inline std::string GetExtension(const std::string &filename) +{ + if(filename.find_last_of(".") != std::string::npos) + { + return mpt::ToLowerCaseAscii(filename.substr(filename.find_last_of(".") + 1)); + } + return std::string(); +} + + +std::size_t CUnarchiver::FindBestFile(const std::vector<const char *> &extensions) +{ + if(!IsArchive()) + { + return failIndex; + } + uint64 biggestSize = 0; + std::size_t bestIndex = failIndex; + for(std::size_t i = 0; i < size(); ++i) + { + if(operator[](i).type != ArchiveFileType::Normal) + { + continue; + } + const std::string ext = GetExtension(operator[](i).name.ToUTF8()); + + if(ext == "diz" || ext == "nfo" || ext == "txt") + { + // we do not want these + continue; + } + + // Compare with list of preferred extensions + if(mpt::contains(extensions, ext)) + { + bestIndex = i; + break; + } + + if(operator[](i).size >= biggestSize) + { + biggestSize = operator[](i).size; + bestIndex = i; + } + } + return bestIndex; +} + + +bool CUnarchiver::ExtractBestFile(const std::vector<const char *> &extensions) +{ + std::size_t bestFile = FindBestFile(extensions); + if(bestFile == failIndex) + { + return false; + } + return ExtractFile(bestFile); +} + + +bool CUnarchiver::IsArchive() const +{ + return impl->IsArchive(); +} + + +mpt::ustring CUnarchiver::GetComment() const +{ + return impl->GetComment(); +} + + +bool CUnarchiver::ExtractFile(std::size_t index) +{ + return impl->ExtractFile(index); +} + + +FileReader CUnarchiver::GetOutputFile() const +{ + return impl->GetOutputFile(); +} + + +std::size_t CUnarchiver::size() const +{ + return impl->size(); +} + + +IArchive::const_iterator CUnarchiver::begin() const +{ + return impl->begin(); +} + + +IArchive::const_iterator CUnarchiver::end() const +{ + return impl->end(); +} + + +const ArchiveFileInfo & CUnarchiver::operator [] (std::size_t index) const +{ + return impl->operator[](index); +} + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.h b/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.h new file mode 100644 index 00000000..83d73701 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unarchiver.h @@ -0,0 +1,88 @@ +/* + * unarchiver.h + * ------------ + * Purpose: archive loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "../common/FileReader.h" + +#include "archive.h" + +#if (defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)) || defined(MPT_WITH_MINIZ) +#include "unzip.h" +#endif +#ifdef MPT_WITH_LHASA +#include "unlha.h" +#endif +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) +#include "ungzip.h" +#endif +#ifdef MPT_WITH_UNRAR +#include "unrar.h" +#endif +#ifdef MPT_WITH_ANCIENT +#include "unancient.h" +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +class CUnarchiver : public IArchive +{ + +private: + + IArchive *impl; + + FileReader inFile; + + ArchiveBase emptyArchive; +#if (defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP)) || defined(MPT_WITH_MINIZ) + CZipArchive zipArchive; +#endif +#ifdef MPT_WITH_LHASA + CLhaArchive lhaArchive; +#endif +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + CGzipArchive gzipArchive; +#endif +#ifdef MPT_WITH_UNRAR + CRarArchive rarArchive; +#endif +#ifdef MPT_WITH_ANCIENT + CAncientArchive ancientArchive; +#endif + +public: + + CUnarchiver(FileReader &file); + ~CUnarchiver() override; + + bool IsArchive() const override; + mpt::ustring GetComment() const override; + bool ExtractFile(std::size_t index) override; + FileReader GetOutputFile() const override; + std::size_t size() const override; + IArchive::const_iterator begin() const override; + IArchive::const_iterator end() const override; + const ArchiveFileInfo & operator [] (std::size_t index) const override; + +public: + + static const std::size_t failIndex = (std::size_t)-1; + + std::size_t FindBestFile(const std::vector<const char *> &extensions); + bool ExtractBestFile(const std::vector<const char *> &extensions); + +}; + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.cpp new file mode 100644 index 00000000..0731a0ef --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.cpp @@ -0,0 +1,147 @@ +/* + * ungzip.cpp + * ---------- + * Purpose: Implementation file for extracting modules from .gz archives + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "../common/FileReader.h" +#include "ungzip.h" + +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + +#if defined(MPT_WITH_ZLIB) +#include <zlib.h> +#elif defined(MPT_WITH_MINIZ) +#include <miniz/miniz.h> +#endif + +#endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + + +CGzipArchive::CGzipArchive(const FileReader &file) : ArchiveBase(file) +{ + inFile.Rewind(); + inFile.ReadStruct(header); + + // Check header data + file size + if(header.magic1 != GZ_HMAGIC1 || header.magic2 != GZ_HMAGIC2 || header.method != GZ_HMDEFLATE || (header.flags & GZ_FRESERVED) != 0 + || inFile.GetLength() <= sizeof(GZheader) + sizeof(GZtrailer)) + { + return; + } + ArchiveFileInfo info; + info.type = ArchiveFileType::Normal; + contents.push_back(info); +} + + +CGzipArchive::~CGzipArchive() +{ + return; +} + + +bool CGzipArchive::ExtractFile(std::size_t index) +{ + if(index >= contents.size()) + { + return false; + } + + // Read trailer + GZtrailer trailer; + inFile.Seek(inFile.GetLength() - sizeof(GZtrailer)); + inFile.ReadStruct(trailer); + + // Continue reading header + inFile.Seek(sizeof(GZheader)); + + // Extra block present? (skip the extra data) + if(header.flags & GZ_FEXTRA) + { + inFile.Skip(inFile.ReadUint16LE()); + } + + // Filename present? (ignore) + if(header.flags & GZ_FNAME) + { + while(inFile.ReadUint8() != 0); + } + + // Comment present? (ignore) + if(header.flags & GZ_FCOMMENT) + { + while(inFile.ReadUint8() != 0); + } + + // CRC16 present? (ignore) + if(header.flags & GZ_FHCRC) + { + inFile.Skip(2); + } + + // Well, this is a bit small when deflated. + if(!inFile.CanRead(sizeof(GZtrailer))) + { + return false; + } + + try + { + data.reserve(inFile.BytesLeft()); + } catch(...) + { + return false; + } + + // Inflate! + z_stream strm{}; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + if(inflateInit2(&strm, -15) != Z_OK) + return false; + int retVal = Z_OK; + uint32 crc = 0; + auto bytesLeft = inFile.BytesLeft() - sizeof(GZtrailer); + do + { + std::array<char, mpt::IO::BUFFERSIZE_SMALL> inBuffer, outBuffer; + strm.avail_in = static_cast<uInt>(std::min(static_cast<FileReader::pos_type>(inBuffer.size()), bytesLeft)); + inFile.ReadStructPartial(inBuffer, strm.avail_in); + strm.next_in = reinterpret_cast<Bytef *>(inBuffer.data()); + bytesLeft -= strm.avail_in; + do + { + strm.avail_out = static_cast<uInt>(outBuffer.size()); + strm.next_out = reinterpret_cast<Bytef *>(outBuffer.data()); + retVal = inflate(&strm, Z_NO_FLUSH); + const auto output = mpt::as_span(outBuffer.data(), outBuffer.data() + outBuffer.size() - strm.avail_out); + crc = crc32(crc, reinterpret_cast<Bytef *>(output.data()), static_cast<uInt>(output.size())); + data.insert(data.end(), output.begin(), output.end()); + } while(strm.avail_out == 0); + } while(retVal == Z_OK && bytesLeft); + inflateEnd(&strm); + + // Everything went OK? Check return code, number of written bytes and CRC32. + return retVal == Z_STREAM_END && trailer.isize == static_cast<uint32>(strm.total_out) && trailer.crc32_ == crc; +} + + +#endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.h b/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.h new file mode 100644 index 00000000..85019141 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.h @@ -0,0 +1,74 @@ +/* + * ungzip.h + * -------- + * Purpose: Header file for .gz loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" +#include "openmpt/base/Endian.hpp" + +#include "archive.h" + +OPENMPT_NAMESPACE_BEGIN + +#if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) + +class CGzipArchive : public ArchiveBase +{ +public: + struct GZheader + { + uint8le magic1; // 0x1F + uint8le magic2; // 0x8B + uint8le method; // 0-7 = reserved, 8 = deflate + uint8le flags; // See GZ_F* constants + uint32le mtime; // UNIX time + uint8le xflags; // Available for use by specific compression methods. We ignore this. + uint8le os; // Which OS was used to compress the file? We also ignore this. + }; + + struct GZtrailer + { + uint32le crc32_; // CRC32 of decompressed data + uint32le isize; // Size of decompressed data modulo 2^32 + }; + +protected: + enum MagicBytes + { + GZ_HMAGIC1 = 0x1F, + GZ_HMAGIC2 = 0x8B, + GZ_HMDEFLATE = 0x08, + }; + + enum HeaderFlags + { + GZ_FTEXT = 0x01, // File is probably ASCII text (who cares) + GZ_FHCRC = 0x02, // CRC16 present + GZ_FEXTRA = 0x04, // Extra fields present + GZ_FNAME = 0x08, // Original filename present + GZ_FCOMMENT = 0x10, // Comment is present + GZ_FRESERVED = (~(GZ_FTEXT | GZ_FHCRC | GZ_FEXTRA | GZ_FNAME | GZ_FCOMMENT)) + }; + + GZheader header; + +public: + + bool ExtractFile(std::size_t index) override; + + CGzipArchive(const FileReader &file); + ~CGzipArchive() override; +}; + +MPT_BINARY_STRUCT(CGzipArchive::GZheader, 10) +MPT_BINARY_STRUCT(CGzipArchive::GZtrailer, 8) + +#endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.cpp new file mode 100644 index 00000000..e243d1d1 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.cpp @@ -0,0 +1,162 @@ +/* + * unlha.cpp + * --------- + * Purpose: Implementation file for extracting modules from .lha archives, making use of lhasa + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "unlha.h" + +#ifdef MPT_WITH_LHASA +#include "lhasa.h" +#endif // MPT_WITH_LHASA + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef MPT_WITH_LHASA + + +static int LHAreadFileReader(void *handle, void *buf, size_t buf_len) +{ + FileReader *f = reinterpret_cast<FileReader*>(handle); + int read_len = mpt::saturate_cast<int>(buf_len); + int result = mpt::saturate_cast<int>(f->ReadRaw(mpt::span(mpt::void_cast<std::byte*>(buf), read_len)).size()); + if(result == 0) + { + return -1; + } + return result; +} + +static int LHAskipFileReader(void *handle, size_t bytes) +{ + FileReader *f = reinterpret_cast<FileReader*>(handle); + if(f->CanRead(bytes)) + { + f->Skip(bytes); + return 1; + } + return 0; +} + +static void LHAcloseFileReader(void * /*handle*/) +{ + return; +} + +static LHAInputStreamType vtable = +{ + LHAreadFileReader, + LHAskipFileReader, + LHAcloseFileReader +}; + + +CLhaArchive::CLhaArchive(FileReader &file) : ArchiveBase(file), inputstream(nullptr), reader(nullptr), firstfile(nullptr) +{ + OpenArchive(); + for(LHAFileHeader *fileheader = firstfile; fileheader; fileheader = lha_reader_next_file(reader)) + { + ArchiveFileInfo info; + info.name = mpt::PathString::FromUnicode(mpt::ToUnicode(mpt::Charset::Amiga_no_C1, fileheader->filename)); + info.size = fileheader->length; + info.type = ArchiveFileType::Normal; + contents.push_back(info); + } + CloseArchive(); +} + + +CLhaArchive::~CLhaArchive() +{ + return; +} + + +void CLhaArchive::OpenArchive() +{ + inFile.Rewind(); + inputstream = lha_input_stream_new(&vtable, &inFile); + if(inputstream) + { + reader = lha_reader_new(inputstream); + } + if(reader) + { + lha_reader_set_dir_policy(reader, LHA_READER_DIR_END_OF_DIR); + firstfile = lha_reader_next_file(reader); + } +} + + +void CLhaArchive::CloseArchive() +{ + if(reader) + { + lha_reader_free(reader); + reader = nullptr; + } + if(inputstream) + { + lha_input_stream_free(inputstream); + inputstream = nullptr; + } +} + + +bool CLhaArchive::ExtractFile(std::size_t index) +{ + if(index >= contents.size()) + { + return false; + } + data.clear(); + OpenArchive(); + const std::size_t bufSize = 4096; + std::size_t i = 0; + for(LHAFileHeader *fileheader = firstfile; fileheader; fileheader = lha_reader_next_file(reader)) + { + if(index == i) + { + data.clear(); + std::size_t countRead = 0; + do + { + try + { + data.resize(data.size() + bufSize); + } catch(...) + { + CloseArchive(); + return false; + } + countRead = lha_reader_read(reader, &data[data.size() - bufSize], bufSize); + if(countRead < bufSize) + { + try + { + data.resize(data.size() - (bufSize - countRead)); + } catch(...) + { + CloseArchive(); + return false; + } + } + } while(countRead > 0); + } + ++i; + } + CloseArchive(); + return data.size() > 0; +} + + +#endif // MPT_WITH_LHASA + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.h b/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.h new file mode 100644 index 00000000..cf330255 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unlha.h @@ -0,0 +1,46 @@ +/* + * unlha.h + * ------- + * Purpose: Header file for .lha loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "archive.h" + + +#ifdef MPT_WITH_LHASA + +typedef struct _LHAInputStream LHAInputStream; +typedef struct _LHAReader LHAReader; +typedef struct _LHAFileHeader LHAFileHeader; + +#endif // MPT_WITH_LHASA + +OPENMPT_NAMESPACE_BEGIN + +#ifdef MPT_WITH_LHASA + +class CLhaArchive : public ArchiveBase +{ +private: + LHAInputStream *inputstream; + LHAReader *reader; + LHAFileHeader *firstfile; + void OpenArchive(); + void CloseArchive(); +public: + CLhaArchive(FileReader &file); + virtual ~CLhaArchive(); +public: + bool ExtractFile(std::size_t index) override; +}; + +#endif // MPT_WITH_LHASA + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp new file mode 100644 index 00000000..5aa0491f --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.cpp @@ -0,0 +1,279 @@ +/* + * unrar.cpp + * --------- + * Purpose: Implementation file for extracting modules from .rar archives + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" +#include "unrar.h" + +#ifdef MPT_WITH_UNRAR + +#include "../common/mptFileIO.h" + +#if MPT_OS_WINDOWS + #include <windows.h> +#else // !MPT_OS_WINDOWS + #ifdef _UNIX + #define MPT_UNRAR_UNIX_WAS_DEFINED + #else + #define _UNIX + #endif +#endif // MPT_OS_WINDOWS + +#include "unrar/dll.hpp" + +#if !MPT_OS_WINDOWS + #ifndef MPT_UNRAR_UNIX_WAS_DEFINED + #undef _UNIX + #undef MPT_UNRAR_UNIX_WAS_DEFINED + #endif +#endif // !MPT_OS_WINDOWS + +#endif // MPT_WITH_UNRAR + + +OPENMPT_NAMESPACE_BEGIN + + +#ifdef MPT_WITH_UNRAR + + +struct RARHandle // RAII +{ + HANDLE rar = nullptr; + explicit RARHandle(HANDLE rar_) : rar(rar_) { return; } + RARHandle(const RARHandle &) = delete; + ~RARHandle() { if(rar) RARCloseArchive(rar); } + + operator HANDLE () const { return rar; } +}; + + +static int CALLBACK RARCallback(unsigned int msg, LPARAM userData, LPARAM p1, LPARAM p2) +{ + int result = 0; + CRarArchive *that = reinterpret_cast<CRarArchive *>(userData); + switch(msg) + { + case UCM_PROCESSDATA: + // Receive extracted data + that->RARCallbackProcessData(reinterpret_cast<const char *>(p1), p2); + result = 1; + break; + default: + // No support for passwords or volumes + result = -1; + break; + } + return result; +} + + +void CRarArchive::RARCallbackProcessData(const char * buf, std::size_t size) +{ + if(!captureCurrentFile) + { + return; + } + mpt::append(data, buf, buf + size); +} + + +CRarArchive::CRarArchive(FileReader &file) + : ArchiveBase(file) +{ + // NOTE: + // We open the archive twice, once for listing the contents in the + // constructor and once for actual decompression in ExtractFile. + // For solid archives, listing the contents via RAR_OM_LIST is way faster. + // The overhead of opening twice for non-solid archives is negligable if the + // archive does not contain a lot of files (and archives with large amount of + // files are pretty useless for OpenMPT anyway). + + // Early reject files with no Rar! magic + // so that we do not have to instantiate OnDiskFileWrapper. + inFile.Rewind(); + if(!inFile.ReadMagic("Rar!\x1A")) + { + return; + } + inFile.Rewind(); + + diskFile = std::make_unique<OnDiskFileWrapper>(inFile); + if(!diskFile->IsValid()) + { + return; + } + + std::wstring ArcName = diskFile->GetFilename().ToWide(); + std::vector<wchar_t> ArcNameBuf(ArcName.c_str(), ArcName.c_str() + ArcName.length() + 1); + std::vector<wchar_t> CmtBuf(65536); + RAROpenArchiveDataEx ArchiveData = {}; + ArchiveData.OpenMode = RAR_OM_LIST; + ArchiveData.ArcNameW = ArcNameBuf.data(); + ArchiveData.CmtBufW = CmtBuf.data(); + ArchiveData.CmtBufSize = static_cast<unsigned int>(CmtBuf.size()); + RARHandle rar(RAROpenArchiveEx(&ArchiveData)); + if(!rar) + { + Reset(); + return; + } + + switch(ArchiveData.CmtState) + { + case 1: + if(ArchiveData.CmtSize) + { + comment = mpt::ToUnicode(std::wstring(ArchiveData.CmtBufW, ArchiveData.CmtBufW + ArchiveData.CmtSize - 1)); + break; + } + [[fallthrough]]; + case 0: + comment = mpt::ustring(); + break; + default: + Reset(); + return; + break; + } + + bool eof = false; + int RARResult = 0; + while(!eof) + { + RARHeaderDataEx HeaderData = {}; + RARResult = RARReadHeaderEx(rar, &HeaderData); + switch(RARResult) + { + case ERAR_SUCCESS: + break; + case ERAR_END_ARCHIVE: + eof = true; + continue; + break; + default: + Reset(); + return; + break; + } + ArchiveFileInfo fileInfo; + fileInfo.name = mpt::PathString::FromWide(HeaderData.FileNameW); + fileInfo.type = ArchiveFileType::Normal; + fileInfo.size = HeaderData.UnpSize; + contents.push_back(fileInfo); + RARResult = RARProcessFileW(rar, RAR_SKIP, NULL, NULL); + switch(RARResult) + { + case ERAR_SUCCESS: + break; + default: + Reset(); + return; + break; + } + } + +} + + +CRarArchive::~CRarArchive() +{ +} + + +bool CRarArchive::ExtractFile(std::size_t index) +{ + + if(!diskFile || !diskFile->IsValid()) + { + return false; + } + + if(index >= contents.size()) + { + return false; + } + + std::wstring ArcName = diskFile->GetFilename().ToWide(); + std::vector<wchar_t> ArcNameBuf(ArcName.c_str(), ArcName.c_str() + ArcName.length() + 1); + RAROpenArchiveDataEx ArchiveData = {}; + ArchiveData.OpenMode = RAR_OM_EXTRACT; + ArchiveData.ArcNameW = ArcNameBuf.data(); + ArchiveData.Callback = RARCallback; + ArchiveData.UserData = reinterpret_cast<LPARAM>(this); + RARHandle rar(RAROpenArchiveEx(&ArchiveData)); + if(!rar) + { + ResetFile(); + return false; + } + + std::size_t i = 0; + int RARResult = 0; + bool eof = false; + while(!eof) + { + RARHeaderDataEx HeaderData = {}; + RARResult = RARReadHeaderEx(rar, &HeaderData); + switch(RARResult) + { + case ERAR_SUCCESS: + break; + case ERAR_END_ARCHIVE: + eof = true; + continue; + break; + default: + ResetFile(); + return false; + break; + } + captureCurrentFile = (i == index); + RARResult = RARProcessFileW(rar, captureCurrentFile ? RAR_TEST : RAR_SKIP, NULL, NULL); + switch(RARResult) + { + case ERAR_SUCCESS: + break; + default: + ResetFile(); + return false; + break; + } + if(captureCurrentFile) + { // done + return true; + } + captureCurrentFile = false; + ++i; + } + + return false; + +} + + +void CRarArchive::Reset() +{ + captureCurrentFile = false; + comment = mpt::ustring(); + contents = std::vector<ArchiveFileInfo>(); + data = std::vector<char>(); +} + + +void CRarArchive::ResetFile() +{ + captureCurrentFile = false; + data = std::vector<char>(); +} + + +#endif // MPT_WITH_UNRAR + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.h b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.h new file mode 100644 index 00000000..056a9475 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unrar.h @@ -0,0 +1,48 @@ +/* + * unrar.h + * ------- + * Purpose: Header file for .rar loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "archive.h" + +OPENMPT_NAMESPACE_BEGIN + +#ifdef MPT_WITH_UNRAR + +class CRarArchive + : public ArchiveBase +{ + +protected: + + std::unique_ptr<OnDiskFileWrapper> diskFile; + bool captureCurrentFile = false; + +public: + CRarArchive(FileReader &file); + ~CRarArchive() override; + + bool ExtractFile(std::size_t index) override; + +public: + + void RARCallbackProcessData(const char * data, std::size_t size); + +private: + + void Reset(); + void ResetFile(); + +}; + +#endif // MPT_WITH_UNRAR + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp b/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp new file mode 100644 index 00000000..6094f630 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp @@ -0,0 +1,315 @@ +/* + * unzip.cpp + * --------- + * Purpose: Implementation file for extracting modules from .zip archives, making use of MiniZip (from the zlib contrib package) + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#include "stdafx.h" + +#include "../common/FileReader.h" +#include "unzip.h" +#include "../common/misc_util.h" +#include <algorithm> +#include <vector> + +#if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP) +#include <contrib/minizip/unzip.h> +#elif defined(MPT_WITH_MINIZ) +#include <miniz/miniz.h> +#endif + + +OPENMPT_NAMESPACE_BEGIN + + +#if defined(MPT_WITH_ZLIB) && defined(MPT_WITH_MINIZIP) + + +// Low-level file abstractions for in-memory file handling +struct ZipFileAbstraction +{ + static voidpf ZCALLBACK fopen64_mem(voidpf opaque, const void *, int mode) + { + FileReader &file = *static_cast<FileReader *>(opaque); + if((mode & ZLIB_FILEFUNC_MODE_READWRITEFILTER) == ZLIB_FILEFUNC_MODE_WRITE) + { + return nullptr; + } else + { + file.Rewind(); + return opaque; + } + } + + static uLong ZCALLBACK fread_mem(voidpf opaque, voidpf, void *buf, uLong size) + { + FileReader &file = *static_cast<FileReader *>(opaque); + return mpt::saturate_cast<uLong>(file.ReadRaw(mpt::span(mpt::void_cast<std::byte*>(buf), size)).size()); + } + + static uLong ZCALLBACK fwrite_mem(voidpf, voidpf, const void *, uLong) + { + return 0; + } + + static ZPOS64_T ZCALLBACK ftell64_mem(voidpf opaque, voidpf) + { + FileReader &file = *static_cast<FileReader *>(opaque); + return file.GetPosition(); + } + + static long ZCALLBACK fseek64_mem(voidpf opaque, voidpf, ZPOS64_T offset, int origin) + { + FileReader &file = *static_cast<FileReader *>(opaque); + ZPOS64_T destination = 0; + switch(origin) + { + case ZLIB_FILEFUNC_SEEK_CUR: + destination = static_cast<ZPOS64_T>(file.GetPosition()) + offset; + break; + case ZLIB_FILEFUNC_SEEK_END: + destination = static_cast<ZPOS64_T>(file.GetLength()) + offset; + break; + case ZLIB_FILEFUNC_SEEK_SET: + destination = offset; + break; + default: + return -1; + } + if(!mpt::in_range<FileReader::off_t>(destination)) + { + return 1; + } + return (file.Seek(static_cast<FileReader::off_t>(destination)) ? 0 : 1); + } + + static int ZCALLBACK fclose_mem(voidpf, voidpf) + { + return 0; + } + + static int ZCALLBACK ferror_mem(voidpf, voidpf) + { + return 0; + } +}; + + +CZipArchive::CZipArchive(FileReader &file) + : ArchiveBase(file) + , zipFile(nullptr) +{ + zlib_filefunc64_def functions = + { + ZipFileAbstraction::fopen64_mem, + ZipFileAbstraction::fread_mem, + ZipFileAbstraction::fwrite_mem, + ZipFileAbstraction::ftell64_mem, + ZipFileAbstraction::fseek64_mem, + ZipFileAbstraction::fclose_mem, + ZipFileAbstraction::ferror_mem, + &inFile + }; + + zipFile = unzOpen2_64(nullptr, &functions); + + if(zipFile == nullptr) + { + return; + } + + // read comment + { + unz_global_info info; + if(unzGetGlobalInfo(zipFile, &info) == UNZ_OK) + { + if(info.size_comment > 0) + { + if(info.size_comment < Util::MaxValueOfType(info.size_comment)) + { + info.size_comment++; + } + std::vector<char> commentData(info.size_comment); + if(unzGetGlobalComment(zipFile, commentData.data(), info.size_comment) >= 0) + { + commentData[info.size_comment - 1] = '\0'; + comment = mpt::ToUnicode(mpt::IsUTF8(commentData.data()) ? mpt::Charset::UTF8 : mpt::Charset::CP437, commentData.data()); + } + } + } + } + + // read contents + unz_file_pos curFile; + int status = unzGoToFirstFile(zipFile); + unzGetFilePos(zipFile, &curFile); + + while(status == UNZ_OK) + { + ArchiveFileInfo fileinfo; + + fileinfo.type = ArchiveFileType::Normal; + + unz_file_info info; + char name[256]; + unzGetCurrentFileInfo(zipFile, &info, name, sizeof(name), nullptr, 0, nullptr, 0); + fileinfo.name = mpt::PathString::FromUnicode(mpt::ToUnicode((info.flag & (1<<11)) ? mpt::Charset::UTF8 : mpt::Charset::CP437, std::string(name))); + fileinfo.size = info.uncompressed_size; + + unzGetFilePos(zipFile, &curFile); + fileinfo.cookie1 = curFile.pos_in_zip_directory; + fileinfo.cookie2 = curFile.num_of_file; + + contents.push_back(fileinfo); + + status = unzGoToNextFile(zipFile); + } + +} + + +CZipArchive::~CZipArchive() +{ + unzClose(zipFile); +} + + +bool CZipArchive::ExtractFile(std::size_t index) +{ + if(index >= contents.size()) + { + return false; + } + + data.clear(); + + unz_file_pos bestFile; + unz_file_info info; + + bestFile.pos_in_zip_directory = static_cast<uLong>(contents[index].cookie1); + bestFile.num_of_file = static_cast<uLong>(contents[index].cookie2); + + if(unzGoToFilePos(zipFile, &bestFile) == UNZ_OK && unzOpenCurrentFile(zipFile) == UNZ_OK) + { + unzGetCurrentFileInfo(zipFile, &info, nullptr, 0, nullptr, 0, nullptr, 0); + + try + { + data.resize(info.uncompressed_size); + } catch(...) + { + unzCloseCurrentFile(zipFile); + return false; + } + unzReadCurrentFile(zipFile, data.data(), info.uncompressed_size); + unzCloseCurrentFile(zipFile); + + return true; + } + + return false; +} + + +#elif defined(MPT_WITH_MINIZ) + + +CZipArchive::CZipArchive(FileReader &file) : ArchiveBase(file) +{ + zipFile = new mz_zip_archive(); + + mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); + + (*zip) = {}; + const auto fileData = file.GetRawData(); + if(!mz_zip_reader_init_mem(zip, fileData.data(), fileData.size(), 0)) + { + delete zip; + zip = nullptr; + zipFile = nullptr; + } + + if(!zip) + { + return; + } + + for(mz_uint i = 0; i < mz_zip_reader_get_num_files(zip); ++i) + { + ArchiveFileInfo info; + info.type = ArchiveFileType::Invalid; + mz_zip_archive_file_stat stat = {}; + if(mz_zip_reader_file_stat(zip, i, &stat)) + { + info.type = ArchiveFileType::Normal; + info.name = mpt::PathString::FromUnicode(mpt::ToUnicode((stat.m_bit_flag & (1<<11)) ? mpt::Charset::UTF8 : mpt::Charset::CP437, stat.m_filename)); + info.size = stat.m_uncomp_size; + } + if(mz_zip_reader_is_file_a_directory(zip, i)) + { + info.type = ArchiveFileType::Special; + } else if(mz_zip_reader_is_file_encrypted(zip, i)) + { + info.type = ArchiveFileType::Special; + } + contents.push_back(info); + } + +} + + +CZipArchive::~CZipArchive() +{ + mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); + + if(zip) + { + mz_zip_reader_end(zip); + + delete zip; + zipFile = nullptr; + } + +} + + +bool CZipArchive::ExtractFile(std::size_t index) +{ + mz_zip_archive *zip = static_cast<mz_zip_archive*>(zipFile); + + if(index >= contents.size()) + { + return false; + } + + mz_uint bestFile = index; + + mz_zip_archive_file_stat stat = {}; + mz_zip_reader_file_stat(zip, bestFile, &stat); + if(stat.m_uncomp_size >= std::numeric_limits<std::size_t>::max()) + { + return false; + } + try + { + data.resize(static_cast<std::size_t>(stat.m_uncomp_size)); + } catch(...) + { + return false; + } + if(!mz_zip_reader_extract_to_mem(zip, bestFile, data.data(), static_cast<std::size_t>(stat.m_uncomp_size), 0)) + { + return false; + } + comment = mpt::ToUnicode(mpt::Charset::CP437, std::string(stat.m_comment, stat.m_comment + stat.m_comment_size)); + return true; +} + + +#endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ + + +OPENMPT_NAMESPACE_END diff --git a/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.h b/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.h new file mode 100644 index 00000000..dc080624 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/unarchiver/unzip.h @@ -0,0 +1,29 @@ +/* + * unzip.h + * ------- + * Purpose: Header file for .zip loader + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + +#pragma once + +#include "openmpt/all/BuildSettings.hpp" + +#include "archive.h" + +OPENMPT_NAMESPACE_BEGIN + +class CZipArchive : public ArchiveBase +{ +protected: + void *zipFile; +public: + CZipArchive(FileReader &file); + ~CZipArchive() override; +public: + bool ExtractFile(std::size_t index) override; +}; + +OPENMPT_NAMESPACE_END |