123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315 |
- /*
- * 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
|