mirror of
https://github.com/scummvm/scummvm.git
synced 2026-05-21 05:40:43 +00:00
COMMON: Add support for loading Gentee Installer embedded PAKs
This commit is contained in:
committed by
Filippos Karapetis
parent
6b21ef7463
commit
57603482fc
@@ -629,7 +629,8 @@ public:
|
||||
explicit PackageArchive(Common::SeekableReadStream *stream);
|
||||
~PackageArchive();
|
||||
|
||||
bool load(const char *prefix);
|
||||
bool loadPAK(const char *prefix);
|
||||
bool loadFromEXE(const char *prefix);
|
||||
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
@@ -657,26 +658,75 @@ PackageArchive::~PackageArchive() {
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
bool PackageArchive::load(const char *prefix) {
|
||||
bool PackageArchive::loadFromEXE(const char *prefix) {
|
||||
// Gentee Installer executables have up to three components:
|
||||
//
|
||||
// - The loader, which is a small loader about 7.5kb in size that contains
|
||||
// the decompression function. The PAK file name is embedded at 0x3a0
|
||||
// and the DLL payload locator is embedded at 0x3f0.
|
||||
//
|
||||
// - The DLL payload, which is a compressed DLL that gets decompressed to
|
||||
// a file named ginstall.dll and then loaded and used to execute most of
|
||||
// the installer interpreter logic.
|
||||
//
|
||||
// - Possibly an embedded PAK file.
|
||||
|
||||
if (_stream->size() < 1024) {
|
||||
warning("GenteeInstaller::PackageArchive::loadFromEXE: Couldn't read pak file size declaration");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_stream->seek(1024 - 16, SEEK_SET)) {
|
||||
warning("GenteeInstaller::PackageArchive::loadFromEXE: Couldn't seek to ginstall.dll payload locator");
|
||||
return false;
|
||||
}
|
||||
|
||||
byte payloadLocatorBytes[12];
|
||||
|
||||
if (_stream->read(payloadLocatorBytes, 12) != 12) {
|
||||
warning("GenteeInstaller::PackageArchive::loadFromEXE: Couldn't read ginstall.dll payload locator");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 payloadLocation = READ_LE_UINT32(payloadLocatorBytes + 0);
|
||||
uint32 payloadSizeCompressed = READ_LE_UINT32(payloadLocatorBytes + 4);
|
||||
//uint32 payloadSizeUncompressed = READ_LE_UINT32(payloadLocatorBytes + 8);
|
||||
|
||||
int64 endOfPayload = static_cast<int64>(payloadLocation) + static_cast<int64>(payloadSizeCompressed);
|
||||
|
||||
if (endOfPayload >= _stream->size()) {
|
||||
warning("GenteeInstaller::PackageArchive::loadFromEXE: Couldn't locate embedded PAK");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_stream->seek(endOfPayload, SEEK_SET)) {
|
||||
warning("GenteeInstaller::PackageArchive::loadFromEXE: Couldn't seek to embedded PAK");
|
||||
return false;
|
||||
}
|
||||
|
||||
return loadPAK(prefix);
|
||||
}
|
||||
|
||||
bool PackageArchive::loadPAK(const char *prefix) {
|
||||
byte pakFileEOFBytes[4];
|
||||
|
||||
int64 pakFileStartPos = _stream->pos();
|
||||
int64 maxPakFileSize = _stream->size() - pakFileStartPos;
|
||||
|
||||
if (_stream->read(pakFileEOFBytes, 4) != 4) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Couldn't read pak file size declaration");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Couldn't read pak file size declaration");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 pakFileEOF = READ_LE_UINT32(pakFileEOFBytes);
|
||||
|
||||
if (static_cast<int64>(pakFileEOF) > _stream->size()) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Pak file size was larger than would be possible");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Pak file size was larger than would be possible");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<int64>(pakFileEOF) < pakFileStartPos) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Pak file EOF preceded the start position");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Pak file EOF preceded the start position");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -685,14 +735,14 @@ bool PackageArchive::load(const char *prefix) {
|
||||
if (pakFileStartPos != 0 || maxPakFileSize != pakFileSize) {
|
||||
_stream = new Common::SeekableSubReadStream(_stream, pakFileStartPos, static_cast<uint32>(pakFileStartPos) + pakFileSize, DisposeAfterUse::YES);
|
||||
if (!_stream->seek(4)) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Couldn't reset pak position");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Couldn't reset pak position");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
byte pakFileHeader[16];
|
||||
if (_stream->read(pakFileHeader, 16) != 16) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Couldn't load pak header");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Couldn't load pak header");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -706,7 +756,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
return false;
|
||||
|
||||
if (firstChunk.size() < 3) {
|
||||
warning("GenteeInstaller::PackageArchive::load: First chunk is malformed");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: First chunk is malformed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -721,7 +771,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
return false;
|
||||
|
||||
if (commandletChunk.size() < 3) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Commandlet was malformed");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Commandlet was malformed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -730,7 +780,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
if (commandletCode == 0x87f4) {
|
||||
// Unpack file commandlet
|
||||
if (commandletChunk.size() < 36 || commandletChunk.back() != 0) {
|
||||
warning("GenteeInstaller::PackageArchive::load: File commandlet was malformed");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: File commandlet was malformed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -749,7 +799,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
|
||||
byte decompressedSizeBytes[4];
|
||||
if (_stream->read(decompressedSizeBytes, 4) != 4) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Decompressed file size was malformed");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Decompressed file size was malformed");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -766,7 +816,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
amountToSkip = kSkipBufSize;
|
||||
|
||||
if (fileCtx->decompressBytes(skipBuf, amountToSkip) != amountToSkip) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Couldn't decompress file data to skip it");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Couldn't decompress file data to skip it");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -777,7 +827,7 @@ bool PackageArchive::load(const char *prefix) {
|
||||
} else {
|
||||
decompressedSize = READ_LE_UINT32(&commandletChunk[7]);
|
||||
if (!_stream->skip(decompressedSize)) {
|
||||
warning("GenteeInstaller::PackageArchive::load: Failed to skip uncompressed file data");
|
||||
warning("GenteeInstaller::PackageArchive::loadPAK: Failed to skip uncompressed file data");
|
||||
return false;
|
||||
}
|
||||
dataEnd = _stream->pos();
|
||||
@@ -904,7 +954,7 @@ Common::Mutex *ThreadSafePackageArchive::getGuardMutex() {
|
||||
|
||||
|
||||
|
||||
Common::Archive *createGenteeInstallerArchive(Common::SeekableReadStream *stream, const char *prefixToRemove, bool threadSafe) {
|
||||
Common::Archive *createGenteeInstallerArchive(Common::SeekableReadStream *stream, const char *prefixToRemove, bool embedded, bool threadSafe) {
|
||||
if (!prefixToRemove)
|
||||
prefixToRemove = "";
|
||||
|
||||
@@ -915,7 +965,9 @@ Common::Archive *createGenteeInstallerArchive(Common::SeekableReadStream *stream
|
||||
else
|
||||
archive = new GenteeInstaller::PackageArchive(stream);
|
||||
|
||||
if (!archive->load(prefixToRemove)) {
|
||||
bool loaded = embedded ? archive->loadFromEXE(prefixToRemove) : archive->loadPAK(prefixToRemove);
|
||||
|
||||
if (!loaded) {
|
||||
delete archive;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -52,13 +52,16 @@ namespace Common {
|
||||
* other compression methods like PPMd. This version uses LZ77 with adaptive Huffman coding.
|
||||
*
|
||||
* @param stream Data stream to load
|
||||
* @param embedded If true, load an embedded package from an installer executable, otherwise load
|
||||
* a stand-alone package file.
|
||||
* @param prefixToRemove Specifies the prefix of extract directives to include, and removes the prefix
|
||||
* If you pass an empty string or null, all directives will be included
|
||||
* If you pass an empty string or null, all directives will be included.
|
||||
* File path separators will be normalized to '/' before checking the prefix.
|
||||
* @param threadSafe If true, all read operations will be wrapped in a mutex-guarded substream
|
||||
*
|
||||
* @return The Gentee Installer package archive
|
||||
*/
|
||||
Common::Archive *createGenteeInstallerArchive(Common::SeekableReadStream *stream, const char *prefixToRemove, bool threadSafe);
|
||||
Common::Archive *createGenteeInstallerArchive(Common::SeekableReadStream *stream, const char *prefixToRemove, bool embedded, bool threadSafe);
|
||||
|
||||
} // End of namespace Common
|
||||
|
||||
|
||||
Reference in New Issue
Block a user