[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