Initial community commit

This commit is contained in:
Jef 2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit fc06254474
16440 changed files with 4239995 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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