diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/unarchiver/unzip.cpp | 315 |
1 files changed, 315 insertions, 0 deletions
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 |