[Scummvm-git-logs] scummvm master -> fa66f887f33f0dcc0cdc47255b7def7e6b00044e

elasota noreply at scummvm.org
Wed Nov 2 23:31:54 UTC 2022


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
fa66f887f3 MTROPOLIS: Add some stub VISE 3 stuff to boot SPQR Mac.


Commit: fa66f887f33f0dcc0cdc47255b7def7e6b00044e
    https://github.com/scummvm/scummvm/commit/fa66f887f33f0dcc0cdc47255b7def7e6b00044e
Author: elasota (ejlasota at gmail.com)
Date: 2022-11-02T19:31:16-04:00

Commit Message:
MTROPOLIS: Add some stub VISE 3 stuff to boot SPQR Mac.

Changed paths:
    engines/mtropolis/boot.cpp


diff --git a/engines/mtropolis/boot.cpp b/engines/mtropolis/boot.cpp
index f1c43cf11db..0a3d6e73d7c 100644
--- a/engines/mtropolis/boot.cpp
+++ b/engines/mtropolis/boot.cpp
@@ -22,8 +22,10 @@
 #include "common/crc.h"
 #include "common/file.h"
 #include "common/macresman.h"
+#include "common/memstream.h"
 #include "common/stuffit.h"
 #include "common/winexe.h"
+#include "common/zlib.h"
 
 #include "graphics/maccursor.h"
 #include "graphics/wincursor.h"
@@ -196,6 +198,13 @@ struct MacInstallerUnpackRequest {
 	uint32 creator;
 };
 
+struct MacVISE3InstallerUnpackRequest {
+	const char *fileName;
+	bool extractData;
+	bool extractResources;
+	ManifestFileType fileType;
+};
+
 void ObsidianGameDataHandler::unpackAdditionalFiles(Common::Array<Common::SharedPtr<ProjectPersistentResource> > &persistentResources, Common::Array<FileIdentification> &files) {
 	if (_isMac && _isRetail)
 		unpackMacRetailInstaller(persistentResources, files);
@@ -396,6 +405,266 @@ void MTIGameDataHandler::addPlugIns(ProjectDescription &projectDesc, const Commo
 	projectDesc.addPlugIn(standardPlugIn);
 }
 
+class SPQRGameDataHandler : public GameDataHandler {
+public:
+	SPQRGameDataHandler(const Game &game, const MTropolisGameDescription &gameDesc);
+
+	void unpackAdditionalFiles(Common::Array<Common::SharedPtr<ProjectPersistentResource> > &persistentResources, Common::Array<FileIdentification> &files) override;
+
+private:
+	struct VISE3FileDesc {
+		VISE3FileDesc();
+
+		char type[4];
+		char creator[4];
+		uint32 compressedDataSize;
+		uint32 uncompressedDataSize;
+		uint32 compressedResSize;
+		uint32 uncompressedResSize;
+		uint32 positionInArchive;
+
+		Common::String fileName;
+	};
+
+	class FakeFileArchive : public Common::Archive {
+	public:
+		Common::Array<byte> &getDataFork() { return _dataFork; }
+		Common::Array<byte> &getResFork() { return _resFork; }
+
+		bool hasFile(const Common::Path &path) const override;
+		int listMembers(Common::ArchiveMemberList &list) const override;
+		const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
+		Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
+
+	private:
+		Common::Array<byte> _dataFork;
+		Common::Array<byte> _resFork;
+	};
+
+	bool _isMac;
+};
+
+SPQRGameDataHandler::VISE3FileDesc::VISE3FileDesc() : type{ 0, 0, 0, 0 }, creator{ 0, 0, 0, 0 }, compressedDataSize(0), uncompressedDataSize(0), compressedResSize(0), uncompressedResSize(0), positionInArchive(0) {
+}
+
+bool SPQRGameDataHandler::FakeFileArchive::hasFile(const Common::Path &path) const {
+	return false;
+}
+
+int SPQRGameDataHandler::FakeFileArchive::listMembers(Common::ArchiveMemberList &list) const {
+	return 0;
+}
+
+const Common::ArchiveMemberPtr SPQRGameDataHandler::FakeFileArchive::getMember(const Common::Path &path) const {
+	return nullptr;
+}
+
+Common::SeekableReadStream *SPQRGameDataHandler::FakeFileArchive::createReadStreamForMember(const Common::Path &path) const {
+	const Common::Array<byte> *forkData = nullptr;
+	if (path.rawString() == "file")
+		forkData = &_dataFork;
+	else if (path.rawString() == "file.rsrc")
+		forkData = &_resFork;
+	else
+		return nullptr;
+
+	if (forkData->size() == 0)
+		return nullptr;
+
+	byte *clonedData = static_cast<byte *>(malloc(forkData->size()));
+	if (!clonedData)
+		return nullptr;
+
+	memcpy(clonedData, &(*forkData)[0], forkData->size());
+
+	return new Common::MemoryReadStream(clonedData, forkData->size(), DisposeAfterUse::YES);
+}
+
+SPQRGameDataHandler::SPQRGameDataHandler(const Game &game, const MTropolisGameDescription &gameDesc) : GameDataHandler(game, gameDesc), _isMac(gameDesc.desc.platform == kProjectPlatformMacintosh) {
+}
+
+void SPQRGameDataHandler::unpackAdditionalFiles(Common::Array<Common::SharedPtr<ProjectPersistentResource> > &persistentResources, Common::Array<FileIdentification> &files) {
+	const MacVISE3InstallerUnpackRequest unpackRequests[] = {
+		{"Basic.rPP", false, true, MTFT_EXTENSION},
+		{"Extras.rPP", false, true, MTFT_EXTENSION},
+		{"mCursors.cPP", false, true, MTFT_EXTENSION},
+		{"SPQR PPC Start", false, true, MTFT_PLAYER},
+		{"Data File SPQR", true, false, MTFT_MAIN},
+	};
+
+	static const uint8 vl3DeobfuscationTable[] = {
+		0x6a, 0xb7, 0x36, 0xec, 0x15, 0xd9, 0xc8, 0x73, 0xe8, 0x38, 0x9a, 0xdf, 0x21, 0x25, 0xd0, 0xcc,
+		0xfd, 0xdc, 0x16, 0xd7, 0xe3, 0x43, 0x05, 0xc5, 0x8f, 0x48, 0xda, 0xf2, 0x3f, 0x10, 0x23, 0x6c,
+		0x77, 0x7c, 0xf9, 0xa0, 0xa3, 0xe9, 0xed, 0x46, 0x8b, 0xd8, 0xac, 0x54, 0xce, 0x2d, 0x19, 0x5e,
+		0x6d, 0x7d, 0x87, 0x5d, 0xfa, 0x5b, 0x9b, 0xe0, 0xc7, 0xee, 0x9f, 0x52, 0xa9, 0xb9, 0x0a, 0xd1,
+		0xfe, 0x78, 0x76, 0x4a, 0x3d, 0x44, 0x5a, 0x96, 0x90, 0x1f, 0x26, 0x9d, 0x58, 0x1b, 0x8e, 0x57,
+		0x59, 0xc3, 0x0b, 0x6b, 0xfc, 0x1d, 0xe6, 0xa2, 0x7f, 0x92, 0x4f, 0x40, 0xb4, 0x06, 0x72, 0x4d,
+		0xf4, 0x34, 0xaa, 0xd2, 0x49, 0xad, 0xef, 0x22, 0x1a, 0xb5, 0xba, 0xbf, 0x29, 0x68, 0x89, 0x93,
+		0x3e, 0x32, 0x04, 0xf5, 0xde, 0xe1, 0x6f, 0xfb, 0x67, 0xe4, 0x7e, 0x08, 0xaf, 0xf0, 0xab, 0x41,
+		0x82, 0xea, 0x50, 0x0f, 0x2a, 0xc6, 0x35, 0xb3, 0xa8, 0xca, 0xe5, 0x4c, 0x45, 0x8a, 0x97, 0xae,
+		0xd6, 0x66, 0x27, 0x53, 0xc9, 0x1c, 0x3c, 0x03, 0x99, 0xc1, 0x09, 0x2e, 0x69, 0x37, 0x8d, 0x2f,
+		0x60, 0xc2, 0xa6, 0x18, 0x4e, 0x7a, 0xb8, 0xcf, 0xa7, 0x3a, 0x17, 0xd5, 0x9e, 0xf1, 0x84, 0x51,
+		0x0d, 0xa4, 0x64, 0xc4, 0x1e, 0xb1, 0x30, 0x98, 0xbb, 0x79, 0x01, 0xf6, 0x62, 0x0e, 0xb2, 0x63,
+		0x91, 0xcb, 0xff, 0x80, 0x71, 0xe7, 0xd4, 0x00, 0xdb, 0x75, 0x2c, 0xbd, 0x39, 0x33, 0x94, 0xbc,
+		0x8c, 0x3b, 0xb6, 0x20, 0x85, 0x24, 0x88, 0x2b, 0x70, 0x83, 0x6e, 0x7b, 0x9c, 0xbe, 0x14, 0x47,
+		0x65, 0x4b, 0x56, 0x81, 0xf8, 0x12, 0x11, 0x28, 0xeb, 0x55, 0x74, 0xa1, 0x31, 0xf7, 0xb0, 0x13,
+		0x86, 0xdd, 0x5f, 0x42, 0xd3, 0x02, 0x61, 0x95, 0x0c, 0x5c, 0xa5, 0xcd, 0xc0, 0x07, 0xe2, 0xf3,
+	};
+
+	Common::SharedPtr<Common::MacResManager> installerResMan(new Common::MacResManager());
+
+	if (!installerResMan->open("Install.vct"))
+		error("Failed to open SPQR installer");
+
+	if (!installerResMan->hasDataFork())
+		error("SPQR installer has no data fork");
+
+	Common::SharedPtr<Common::SeekableReadStream> installerDataForkStream(installerResMan->getDataFork());
+
+	uint8 vl3Header[44];
+	if (installerDataForkStream->read(vl3Header, 44) != 44 || memcmp(vl3Header, "SVCT", 4))
+		error("Failed to read VISE 3 header");
+
+	uint32 catalogPosition = READ_BE_UINT32(vl3Header + 36);
+
+	if (!installerDataForkStream->seek(catalogPosition))
+		error("Failed to seek to VISE 3 catalog");
+
+	uint8 vl3Catalog[20];
+	if (installerDataForkStream->read(vl3Catalog, 20) != 20 || memcmp(vl3Catalog, "CVCT", 4))
+		error("Failed to read VISE 3 catalog");
+
+	uint16 numEntries = READ_BE_UINT16(vl3Catalog + 16);
+
+	Common::Array<VISE3FileDesc> archiveFiles;
+
+	for (uint16 i = 0; i < numEntries; i++) {
+		uint8 entryMagic[4];
+		if (installerDataForkStream->read(entryMagic, 4) != 4 || memcmp(entryMagic + 1, "VCT", 3))
+			error("Failed to read VISE 3 catalog item");
+
+		if (entryMagic[0] == 'D') {
+			uint8 directoryData[78];
+			if (installerDataForkStream->read(directoryData, 78) != 78)
+				error("Failed to read VISE 3 directory");
+
+			uint8 nameLength = directoryData[76];
+			installerDataForkStream->seek(nameLength, SEEK_CUR);
+		} else if (entryMagic[0] == 'F') {
+			uint8 fileData[120];
+			if (installerDataForkStream->read(fileData, 120) != 120)
+				error("Failed to read VISE 3 file");
+
+			VISE3FileDesc desc;
+			memcpy(desc.type, fileData + 40, 4);
+			memcpy(desc.creator, fileData + 44, 4);
+			desc.compressedDataSize = READ_BE_UINT32(fileData + 64);
+			desc.uncompressedDataSize = READ_BE_UINT32(fileData + 68);
+			desc.compressedResSize = READ_BE_UINT32(fileData + 72);
+			desc.uncompressedResSize = READ_BE_UINT32(fileData + 76);
+			desc.positionInArchive = READ_BE_UINT32(fileData + 96);
+
+			uint8 nameLength = fileData[118];
+
+			if (nameLength > 0) {
+				char fileNameChars[256];
+				if (installerDataForkStream->read(fileNameChars, nameLength) != nameLength)
+					error("Failed to read VISE 3 file name");
+				desc.fileName = Common::String(fileNameChars, nameLength);
+			}
+
+			archiveFiles.push_back(desc);
+		} else {
+			error("Unknown VISE 3 catalog entry item type");
+		}
+	}
+
+	debug(1, "Unpacking files...");
+
+	for (const MacVISE3InstallerUnpackRequest &request : unpackRequests) {
+		const VISE3FileDesc *fileDesc = nullptr;
+		for (const VISE3FileDesc &candidateDesc : archiveFiles) {
+			if (candidateDesc.fileName == request.fileName) {
+				fileDesc = &candidateDesc;
+				break;
+			}
+		}
+
+		if (!fileDesc)
+			error("Couldn't find file '%s' in VISE 3 archive", request.fileName);
+
+		FileIdentification ident;
+		ident.fileName = fileDesc->fileName;
+		ident.macCreator.value = MKTAG(fileDesc->creator[0], fileDesc->creator[1], fileDesc->creator[2], fileDesc->creator[3]);
+		ident.macType.value = MKTAG(fileDesc->type[0], fileDesc->type[1], fileDesc->type[2], fileDesc->type[3]);
+		ident.category = request.fileType;
+
+		Common::SharedPtr<FakeFileArchive> fakeArchive(new FakeFileArchive());
+
+		bool forksNeeded[2] = {request.extractData,
+							   request.extractResources};
+		uint32 forkCompressedDataLoc[2] = {fileDesc->positionInArchive,
+										   fileDesc->positionInArchive + fileDesc->compressedDataSize};
+		uint32 forkCompressedSize[2] = {fileDesc->compressedDataSize,
+										fileDesc->compressedResSize};
+		uint32 forkUncompressedSize[2] = { fileDesc->uncompressedDataSize,
+										  fileDesc->uncompressedResSize};
+
+		Common::Array<byte> *forkDecompressedData[2] = {&fakeArchive->getDataFork(),
+														&fakeArchive->getResFork()};
+
+		for (int fi = 0; fi < 2; fi++) {
+			if (!forksNeeded[fi])
+				continue;
+
+			assert(forkCompressedSize[fi] != 0);
+			if (forkCompressedSize[fi] == 0)
+				continue;
+
+			installerDataForkStream->seek(forkCompressedDataLoc[fi]);
+
+			Common::Array<byte> compressedData;
+			compressedData.resize(forkCompressedSize[fi]);
+
+			uint8 *compressedBytes = &compressedData[0];
+			uint32 compressedSize = forkCompressedSize[fi];
+			uint32 uncompressedSize = forkUncompressedSize[fi];
+			installerDataForkStream->read(compressedBytes, forkCompressedSize[fi]);
+
+			// Undo obfuscation
+			for (uint32 i = 0; i < compressedSize; i++)
+				compressedBytes[i] = vl3DeobfuscationTable[compressedBytes[i]];
+
+			// Undo 16-bit byte swaps
+			for (uint32 i = 1; i < compressedSize; i += 2) {
+				uint8 temp = compressedBytes[i];
+				compressedBytes[i] = compressedBytes[i - 1];
+				compressedBytes[i - 1] = temp;
+			}
+
+			Common::Array<byte> &decompressedData = *forkDecompressedData[fi];
+			decompressedData.resize(uncompressedSize);
+
+			if (!Common::inflateZlibHeaderless(&decompressedData[0], uncompressedSize, &compressedBytes[0], compressedSize))
+				error("Failed to decompress file '%s'", request.fileName);
+		}
+
+		if (request.extractResources) {
+			Common::SharedPtr<Common::MacResManager> resMan(new Common::MacResManager());
+			if (!resMan->open("file", *fakeArchive))
+				error("Failed to open Mac res manager for file '%s'", request.fileName);
+
+			ident.resMan = resMan;
+		}
+
+		if (request.extractData)
+			ident.stream.reset(fakeArchive->createReadStreamForMember("file"));
+
+		files.push_back(ident);
+	}
+}
+
+
 static bool getMacTypesForMacBinary(const char *fileName, uint32 &outType, uint32 &outCreator) {
 	Common::SharedPtr<Common::SeekableReadStream> stream(SearchMan.createReadStreamForMember(fileName));
 
@@ -460,37 +729,6 @@ static bool fileSortCompare(const FileIdentification &a, const FileIdentificatio
 	return aSize < b.fileName.size();
 }
 
-static int resolveFileSegmentID(const Common::String &fileName) {
-	size_t lengthWithoutExtension = fileName.size();
-
-	size_t dotPos = fileName.findLastOf('.');
-	if (dotPos != Common::String::npos)
-		lengthWithoutExtension = dotPos;
-
-	int numDigits = 0;
-	int segmentID = 0;
-	int multiplier = 1;
-
-	for (size_t i = 0; i < lengthWithoutExtension; i++) {
-		size_t charPos = lengthWithoutExtension - 1 - i;
-		char c = fileName[charPos];
-
-		if (c >= '0' && c <= '9') {
-			int digit = c - '0';
-			segmentID += digit * multiplier;
-			multiplier *= 10;
-			numDigits++;
-		} else {
-			break;
-		}
-	}
-
-	if (numDigits == 0)
-		error("Unusual segment naming scheme");
-
-	return segmentID;
-}
-
 static void loadCursorsMac(FileIdentification &f, CursorGraphicCollection &cursorGraphics) {
 	initResManForFile(f);
 
@@ -930,7 +1168,7 @@ const Game games[] = {
 		spqrRetailMacEnFiles,
 		spqrRetailMacDirectories,
 		nullptr,
-		GameDataHandlerFactory<GameDataHandler>::create
+		GameDataHandlerFactory<SPQRGameDataHandler>::create
 	},
 };
 
@@ -1074,6 +1312,8 @@ Common::SharedPtr<ProjectDescription> bootProject(const MTropolisGameDescription
 		Boot::FileIdentification *mainSegmentFile = nullptr;
 		Common::Array<Boot::FileIdentification *> segmentFiles;
 
+		int addlSegments = 0;
+
 		// Bin segments
 		for (Boot::FileIdentification &macFile : macFiles) {
 			switch (macFile.category) {
@@ -1087,9 +1327,8 @@ Common::SharedPtr<ProjectDescription> bootProject(const MTropolisGameDescription
 				mainSegmentFile = &macFile;
 				break;
 			case Boot::MTFT_ADDITIONAL: {
-					int segmentID = Boot::resolveFileSegmentID(macFile.fileName);
-					if (segmentID < 2)
-						error("Unusual segment numbering scheme");
+					addlSegments++;
+					int segmentID = addlSegments + 1;
 
 					size_t segmentIndex = static_cast<size_t>(segmentID - 1);
 					while (segmentFiles.size() <= segmentIndex)




More information about the Scummvm-git-logs mailing list