[Scummvm-git-logs] scummvm master -> ffa580109454b91d90fead23bc8d4578c2e0f079
bluegr
noreply at scummvm.org
Sat Apr 26 14:51:31 UTC 2025
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
6b21ef7463 COMMON: Fix Gentee Installer loader interpreting EOF as size
57603482fc COMMON: Add support for loading Gentee Installer embedded PAKs
ffa5801094 COMMON: Normalize Gentee Installer paths before checking prefix
Commit: 6b21ef746361245a5539b998b5011604e129be97
https://github.com/scummvm/scummvm/commit/6b21ef746361245a5539b998b5011604e129be97
Author: elasota (1137273+elasota at users.noreply.github.com)
Date: 2025-04-26T17:51:27+03:00
Commit Message:
COMMON: Fix Gentee Installer loader interpreting EOF as size
This is necessary to extract PAKs embedded in the setup executable, which start at a non-zero position
Changed paths:
common/compression/gentee_installer.cpp
diff --git a/common/compression/gentee_installer.cpp b/common/compression/gentee_installer.cpp
index 7393012e675..749e77fa488 100644
--- a/common/compression/gentee_installer.cpp
+++ b/common/compression/gentee_installer.cpp
@@ -658,23 +658,30 @@ PackageArchive::~PackageArchive() {
}
bool PackageArchive::load(const char *prefix) {
- byte pakFileSizeBytes[4];
+ byte pakFileEOFBytes[4];
int64 pakFileStartPos = _stream->pos();
int64 maxPakFileSize = _stream->size() - pakFileStartPos;
- if (_stream->read(pakFileSizeBytes, 4) != 4) {
+ if (_stream->read(pakFileEOFBytes, 4) != 4) {
warning("GenteeInstaller::PackageArchive::load: Couldn't read pak file size declaration");
return false;
}
- uint32 pakFileSize = READ_LE_UINT32(pakFileSizeBytes);
+ uint32 pakFileEOF = READ_LE_UINT32(pakFileEOFBytes);
- if (static_cast<int64>(pakFileSize) < maxPakFileSize) {
+ if (static_cast<int64>(pakFileEOF) > _stream->size()) {
warning("GenteeInstaller::PackageArchive::load: 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");
+ return false;
+ }
+
+ uint32 pakFileSize = static_cast<uint32>(static_cast<int64>(pakFileEOF) - pakFileStartPos);
+
if (pakFileStartPos != 0 || maxPakFileSize != pakFileSize) {
_stream = new Common::SeekableSubReadStream(_stream, pakFileStartPos, static_cast<uint32>(pakFileStartPos) + pakFileSize, DisposeAfterUse::YES);
if (!_stream->seek(4)) {
@@ -761,7 +768,6 @@ bool PackageArchive::load(const char *prefix) {
if (fileCtx->decompressBytes(skipBuf, amountToSkip) != amountToSkip) {
warning("GenteeInstaller::PackageArchive::load: Couldn't decompress file data to skip it");
return false;
-
}
skipRemaining -= amountToSkip;
Commit: 57603482fc629dbf431932ec36576d09f7b94dea
https://github.com/scummvm/scummvm/commit/57603482fc629dbf431932ec36576d09f7b94dea
Author: elasota (1137273+elasota at users.noreply.github.com)
Date: 2025-04-26T17:51:27+03:00
Commit Message:
COMMON: Add support for loading Gentee Installer embedded PAKs
Changed paths:
common/compression/gentee_installer.cpp
common/compression/gentee_installer.h
diff --git a/common/compression/gentee_installer.cpp b/common/compression/gentee_installer.cpp
index 749e77fa488..7060ea074ca 100644
--- a/common/compression/gentee_installer.cpp
+++ b/common/compression/gentee_installer.cpp
@@ -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;
}
diff --git a/common/compression/gentee_installer.h b/common/compression/gentee_installer.h
index 80d9e79f714..6310defae3e 100644
--- a/common/compression/gentee_installer.h
+++ b/common/compression/gentee_installer.h
@@ -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
Commit: ffa580109454b91d90fead23bc8d4578c2e0f079
https://github.com/scummvm/scummvm/commit/ffa580109454b91d90fead23bc8d4578c2e0f079
Author: elasota (1137273+elasota at users.noreply.github.com)
Date: 2025-04-26T17:51:27+03:00
Commit Message:
COMMON: Normalize Gentee Installer paths before checking prefix
This works around inconsistent separators in the Schizm Japanese version
Changed paths:
common/compression/gentee_installer.cpp
engines/vcruise/vcruise.cpp
diff --git a/common/compression/gentee_installer.cpp b/common/compression/gentee_installer.cpp
index 7060ea074ca..bcc1f430b4d 100644
--- a/common/compression/gentee_installer.cpp
+++ b/common/compression/gentee_installer.cpp
@@ -835,9 +835,10 @@ bool PackageArchive::loadPAK(const char *prefix) {
debug(3, "GenteeInstaller: Detected %s item '%s' size %u at pos %u .. %u", (isCompressed ? "compressed" : "stored"), fileName.c_str(), static_cast<uint>(decompressedSize), static_cast<uint>(dataStart), static_cast<uint>(dataEnd));
+ fileName.replace('\\', '/');
+
if (fileName.hasPrefix(prefix)) {
fileName = fileName.substr(strlen(prefix));
- fileName.replace('\\', '/');
Common::Path path(fileName, '/');
Common::SharedPtr<ArchiveItem> item(new ArchiveItem(_stream, getGuardMutex(), path, dataStart, static_cast<uint>(dataEnd - dataStart), decompressedSize, isCompressed));
diff --git a/engines/vcruise/vcruise.cpp b/engines/vcruise/vcruise.cpp
index 0267f94e8aa..78d798f696e 100644
--- a/engines/vcruise/vcruise.cpp
+++ b/engines/vcruise/vcruise.cpp
@@ -125,7 +125,7 @@ Common::Error VCruiseEngine::run() {
if (!f->open(_gameDescription->desc.filesDescriptions[0].fileName))
error("Couldn't open installer package '%s'", _gameDescription->desc.filesDescriptions[0].fileName);
- Common::Archive *installerPackageArchive = Common::createGenteeInstallerArchive(f, "#setuppath#\\", true);
+ Common::Archive *installerPackageArchive = Common::createGenteeInstallerArchive(f, "#setuppath#/", false, true);
if (!installerPackageArchive)
error("Couldn't load installer package '%s'", _gameDescription->desc.filesDescriptions[0].fileName);
More information about the Scummvm-git-logs
mailing list