[Scummvm-git-logs] scummvm master -> 69d0293172d30588184ac69e2a8f0e37753ec99c

elasota noreply at scummvm.org
Sat Oct 28 15:46:01 UTC 2023


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:
69d0293172 MTROPOLIS: Add support for 'Unit Re-Booted'


Commit: 69d0293172d30588184ac69e2a8f0e37753ec99c
    https://github.com/scummvm/scummvm/commit/69d0293172d30588184ac69e2a8f0e37753ec99c
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2023-10-28T11:45:58-04:00

Commit Message:
MTROPOLIS: Add support for 'Unit Re-Booted'

This is not much of a game, but uses some variants of the data format which are
nice to support and should help with future projects.

Changed paths:
    engines/mtropolis/boot.cpp
    engines/mtropolis/data.cpp
    engines/mtropolis/data.h
    engines/mtropolis/detection.cpp
    engines/mtropolis/detection.h
    engines/mtropolis/detection_tables.h
    engines/mtropolis/elements.cpp
    engines/mtropolis/miniscript.cpp
    engines/mtropolis/mtropolis.cpp
    engines/mtropolis/plugin/standard_data.cpp
    engines/mtropolis/runtime.cpp
    engines/mtropolis/runtime.h


diff --git a/engines/mtropolis/boot.cpp b/engines/mtropolis/boot.cpp
index e9220f17a8a..380ab5169f0 100644
--- a/engines/mtropolis/boot.cpp
+++ b/engines/mtropolis/boot.cpp
@@ -1025,6 +1025,20 @@ const ManifestFile sttgsDemoWinFiles[] = {
 	{nullptr, MTFT_AUTO}
 };
 
+const ManifestFile unitWinFiles[] = {
+	{"UNIT32.EXE", MTFT_PLAYER},
+	{"DATA.MFX", MTFT_MAIN},
+	{"CURSORS.C32", MTFT_EXTENSION},
+	{"BASIC.X32", MTFT_SPECIAL},
+	{"EXTRAS.R32", MTFT_SPECIAL},
+	{nullptr, MTFT_AUTO}
+};
+
+const char *unitWinDirectories[] = {
+	"MPLUGINS",
+	nullptr
+};
+
 const Game games[] = {
 	// Obsidian - Retail - Macintosh - English
 	{
@@ -1210,6 +1224,14 @@ const Game games[] = {
 		nullptr,
 		GameDataHandlerFactory<STTGSGameDataHandler>::create
 	},
+	// Unit: Rebooted
+	{
+		MTBOOT_UNIT_REBOOTED_WIN,
+		unitWinFiles,
+		unitWinDirectories,
+		nullptr,
+		GameDataHandlerFactory<STTGSGameDataHandler>::create
+	},
 };
 
 } // End of namespace Games
diff --git a/engines/mtropolis/data.cpp b/engines/mtropolis/data.cpp
index 72db1a11257..c1258808d1b 100644
--- a/engines/mtropolis/data.cpp
+++ b/engines/mtropolis/data.cpp
@@ -157,8 +157,10 @@ bool isAsset(DataObjectType type) {
 
 } // End of namespace DataObjectTypes
 
-DataReader::DataReader(int64 globalPosition, Common::SeekableReadStreamEndian &stream, ProjectFormat projectFormat)
-	: _globalPosition(globalPosition), _stream(stream), _projectFormat(projectFormat), _permitDamagedStrings(false) {
+DataReader::DataReader(int64 globalPosition, Common::SeekableReadStreamEndian &stream, ProjectFormat projectFormat,
+					   ProjectEngineVersion projectEngineVersion)
+	: _globalPosition(globalPosition), _stream(stream), _projectFormat(projectFormat), _permitDamagedStrings(false),
+	  _projectEngineVersion(projectEngineVersion) {
 }
 
 bool DataReader::readU8(uint8 &value) {
@@ -303,10 +305,18 @@ ProjectFormat DataReader::getProjectFormat() const {
 	return _projectFormat;
 }
 
+ProjectEngineVersion DataReader::getProjectEngineVersion() const {
+	return _projectEngineVersion;
+}
+
 bool DataReader::isBigEndian() const {
 	return _stream.isBE();
 }
 
+bool DataReader::isV2Project() const {
+	return _projectEngineVersion == kProjectEngineVersion2;
+}
+
 void DataReader::setPermitDamagedStrings(bool permit) {
 	_permitDamagedStrings = permit;
 }
@@ -443,7 +453,7 @@ bool InternalTypeTaggedValue::load(DataReader &reader) {
 
 	Common::MemoryReadStreamEndian contentsStream(contents, sizeof(contents), reader.isBigEndian());
 
-	DataReader valueReader(valueGlobalPos, contentsStream, reader.getProjectFormat());
+	DataReader valueReader(valueGlobalPos, contentsStream, reader.getProjectFormat(), reader.getProjectEngineVersion());
 
 	switch (type) {
 	case kNull:
@@ -731,7 +741,7 @@ PresentationSettings::PresentationSettings()
 }
 
 DataReadErrorCode PresentationSettings::load(DataReader &reader) {
-	if (_revision != 2)
+	if (_revision != 2 && _revision != 3)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(persistFlags) ||
@@ -798,6 +808,34 @@ DataReadErrorCode Unknown19::load(DataReader &reader) {
 	if (!reader.readU32(persistFlags) || !reader.readU32(sizeIncludingTag) || !reader.readBytes(unknown1))
 		return kDataReadErrorReadFailed;
 
+	if (sizeIncludingTag != 16)
+		return kDataReadErrorUnrecognized;
+
+	return kDataReadErrorNone;
+}
+
+Unknown2B::Unknown2B() : persistFlags(0), sizeIncludingTag(0) {
+}
+
+DataReadErrorCode Unknown2B::load(DataReader &reader) {
+	if (_revision != 1)
+		return kDataReadErrorUnsupportedRevision;
+
+	if (!reader.readU32(persistFlags) || !reader.readU32(sizeIncludingTag))
+		return kDataReadErrorReadFailed;
+
+	if (sizeIncludingTag > 100000)
+		return kDataReadErrorUnrecognized;
+
+	// So far read type (4) + revision (2) + persist (4) + size (4) = 14 bytes
+	uint8 *buf = static_cast<uint8 *>(malloc(sizeIncludingTag - 14));
+	if (!reader.read(buf, sizeIncludingTag - 14))
+		return kDataReadErrorReadFailed;
+
+	// TODO: Use this data.
+
+	free(buf);
+
 	return kDataReadErrorNone;
 }
 
@@ -1002,13 +1040,16 @@ GlobalObjectInfo::GlobalObjectInfo()
 }
 
 DataReadErrorCode GlobalObjectInfo::load(DataReader &reader) {
-	if (_revision != 0)
+	if (_revision != 0 && _revision != 1)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(persistFlags) || !reader.readU32(sizeIncludingTag) || !reader.readU16(numGlobalModifiers)
 		|| !reader.readBytes(unknown1))
 		return kDataReadErrorReadFailed;
 
+	if (sizeIncludingTag != 20)
+		return kDataReadErrorUnrecognized;
+
 	return kDataReadErrorNone;
 }
 
@@ -1112,7 +1153,7 @@ BehaviorModifier::BehaviorModifier()
 }
 
 DataReadErrorCode BehaviorModifier::load(DataReader& reader) {
-	if (_revision != 1)
+	if (_revision != 1 && _revision != 2)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(modifierFlags) || !reader.readU32(sizeIncludingTag)
@@ -1122,6 +1163,9 @@ DataReadErrorCode BehaviorModifier::load(DataReader& reader) {
 		|| !reader.readU16(lengthOfName) || !reader.readU16(numChildren))
 		return kDataReadErrorReadFailed;
 
+	if (_revision == 2 && !reader.readU32(unknown8))
+		return kDataReadErrorReadFailed;
+
 	if (lengthOfName > 0 && !reader.readTerminatedStr(name, lengthOfName))
 		return kDataReadErrorReadFailed;
 
@@ -1142,7 +1186,8 @@ MiniscriptProgram::Attribute::Attribute()
 
 MiniscriptProgram::MiniscriptProgram()
 	: unknown1(0), sizeOfInstructions(0), numOfInstructions(0), numLocalRefs(0), numAttributes(0),
-	  projectFormat(kProjectFormatUnknown), isBigEndian(false) {
+	  projectFormat(kProjectFormatUnknown), isBigEndian(false),
+	  projectEngineVersion(kProjectEngineVersionUnknown) {
 }
 
 bool MiniscriptProgram::load(DataReader &reader) {
@@ -1186,7 +1231,8 @@ bool MiniscriptProgram::load(DataReader &reader) {
 }
 
 TypicalModifierHeader::TypicalModifierHeader()
-	: modifierFlags(0), sizeIncludingTag(0), guid(0), unknown3{0, 0, 0, 0, 0, 0}, unknown4(0), lengthOfName(0) {
+	: modifierFlags(0), sizeIncludingTag(0), guid(0), unknown3{0, 0, 0, 0, 0, 0}, unknown4(0),
+	  unknown5(0), lengthOfName(0) {
 }
 
 bool TypicalModifierHeader::load(DataReader& reader) {
@@ -1195,6 +1241,9 @@ bool TypicalModifierHeader::load(DataReader& reader) {
 		|| !reader.readU16(lengthOfName))
 		return false;
 
+	if (reader.isV2Project() && !reader.readU32(unknown5))
+		return false;
+
 	if (lengthOfName > 0 && !reader.readTerminatedStr(name, lengthOfName))
 		return false;
 
@@ -1206,7 +1255,7 @@ MiniscriptModifier::MiniscriptModifier()
 }
 
 DataReadErrorCode MiniscriptModifier::load(DataReader &reader) {
-	if (_revision != 1003)
+	if (_revision != 1003 && _revision != 2003)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !enableWhen.load(reader) || !reader.readBytes(unknown6) || !reader.readU8(unknown7) || !program.load(reader))
@@ -1271,7 +1320,7 @@ MessengerModifier::MessengerModifier()
 }
 
 DataReadErrorCode MessengerModifier::load(DataReader &reader) {
-	if (_revision != 1002)
+	if (_revision != 1002 && _revision != 2002)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader))
@@ -1315,17 +1364,23 @@ AliasModifier::AliasModifier()
 }
 
 DataReadErrorCode AliasModifier::load(DataReader& reader) {
-	if (_revision > 2)
+	if (_revision > 2 && _revision != 4)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(modifierFlags)
 		|| !reader.readU32(sizeIncludingTag)
 		|| !reader.readU16(aliasIndexPlusOne)
 		|| !reader.readU32(unknown1)
-		|| !reader.readU32(unknown2)
-		|| !reader.readU16(lengthOfName)
-		|| !editorLayoutPosition.load(reader))
+		|| !reader.readU32(unknown2))
+		return kDataReadErrorReadFailed;
+
+	if (_revision <= 2 && (!reader.readU16(lengthOfName)
+		|| !editorLayoutPosition.load(reader)))
+		return kDataReadErrorReadFailed;
+	else if (_revision == 4 && !editorLayoutPosition.load(reader)) {
+		// v4 puts the name len just before it.
 		return kDataReadErrorReadFailed;
+	}
 
 	if (_revision >= 2) {
 		haveGUID = true;
@@ -1336,6 +1391,13 @@ DataReadErrorCode AliasModifier::load(DataReader& reader) {
 		guid = 0;
 	}
 
+	if (_revision == 4) {
+		uint32 nameLen = 0;
+		if (!reader.readU32(nameLen))
+			return kDataReadErrorReadFailed;
+		lengthOfName = static_cast<uint16>(nameLen);
+	}
+
 	if (!reader.readTerminatedStr(name, lengthOfName))
 		return kDataReadErrorReadFailed;
 
@@ -1347,7 +1409,7 @@ ChangeSceneModifier::ChangeSceneModifier()
 }
 
 DataReadErrorCode ChangeSceneModifier::load(DataReader &reader) {
-	if (_revision != 1001)
+	if (_revision != 1001 && _revision != 2001)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readU32(changeSceneFlags) || !executeWhen.load(reader)
@@ -1599,7 +1661,7 @@ TimerMessengerModifier::TimerMessengerModifier()
 }
 
 DataReadErrorCode TimerMessengerModifier::load(DataReader &reader) {
-	if (_revision != 1002)
+	if (_revision != 1002 && _revision != 2002)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader))
@@ -1665,7 +1727,7 @@ KeyboardMessengerModifier::KeyboardMessengerModifier()
 }
 
 DataReadErrorCode KeyboardMessengerModifier::load(DataReader &reader) {
-	if (_revision != 1003)
+	if (_revision != 1003 && _revision != 2003)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readU32(messageFlagsAndKeyStates) || !reader.readU16(unknown2)
@@ -1706,7 +1768,7 @@ GraphicModifier::GraphicModifier()
 }
 
 DataReadErrorCode GraphicModifier::load(DataReader &reader) {
-	if (_revision != 1001)
+	if (_revision != 1001 && _revision != 2001)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readU16(unknown1) || !applyWhen.load(reader)
@@ -1833,7 +1895,7 @@ IntegerVariableModifier::IntegerVariableModifier() : unknown1{0, 0, 0, 0}, value
 }
 
 DataReadErrorCode IntegerVariableModifier::load(DataReader &reader) {
-	if (_revision != 1000)
+	if (_revision != 1000 && _revision != 2000)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readBytes(unknown1) || !reader.readS32(value))
@@ -1846,7 +1908,7 @@ IntegerRangeVariableModifier::IntegerRangeVariableModifier() : unknown1{0, 0, 0,
 }
 
 DataReadErrorCode IntegerRangeVariableModifier::load(DataReader &reader) {
-	if (_revision != 1000)
+	if (_revision != 1000 && _revision != 2000)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readBytes(unknown1) || !range.load(reader))
@@ -1859,7 +1921,7 @@ VectorVariableModifier::VectorVariableModifier() : unknown1{0, 0, 0, 0} {
 }
 
 DataReadErrorCode VectorVariableModifier::load(DataReader &reader) {
-	if (_revision != 1000)
+	if (_revision != 1000 && _revision != 2000)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!modHeader.load(reader) || !reader.readBytes(unknown1) || !this->vector.load(reader))
@@ -1930,7 +1992,7 @@ PlugInModifier::PlugInModifier()
 }
 
 DataReadErrorCode PlugInModifier::load(DataReader &reader) {
-	if (_revision != 1001)
+	if (_revision != 1001 && _revision != 2001)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(modifierFlags) || !reader.readU32(codedSize) || !reader.read(modifierName, 16)
@@ -2170,7 +2232,7 @@ ImageAsset::ImageAsset()
 }
 
 DataReadErrorCode ImageAsset::load(DataReader &reader) {
-	if (_revision != 1)
+	if (_revision != 1 && _revision != 2)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!reader.readU32(persistFlags) || !reader.readU32(unknown1) || !reader.readBytes(unknown2)
@@ -2430,6 +2492,9 @@ DataReadErrorCode loadDataObject(const PlugInModifierRegistry &registry, DataRea
 	case DataObjectTypes::kUnknown19:
 		dataObject = new Unknown19();
 		break;
+	case DataObjectTypes::kUnknown2B:
+		dataObject = new Unknown2B();
+		break;
 	case DataObjectTypes::kProjectStructuralDef:
 		dataObject = new ProjectStructuralDef();
 		break;
@@ -2627,7 +2692,7 @@ DataReadErrorCode loadDataObject(const PlugInModifierRegistry &registry, DataRea
 	Common::SharedPtr<DataObject> sharedPtr(dataObject);
 	DataReadErrorCode errorCode = dataObject->load(static_cast<DataObjectTypes::DataObjectType>(type), revision, reader);
 	if (errorCode != kDataReadErrorNone) {
-		warning("Data object type 0x%x failed to load", static_cast<int>(type));
+		warning("Data object type 0x%x failed to load (err %d)", static_cast<int>(type), static_cast<int>(errorCode));
 		outObject.reset();
 		return errorCode;
 	}
diff --git a/engines/mtropolis/data.h b/engines/mtropolis/data.h
index a678bdb3649..7f68e638701 100644
--- a/engines/mtropolis/data.h
+++ b/engines/mtropolis/data.h
@@ -54,6 +54,12 @@ enum ProjectFormat {
 	kProjectFormatNeutral,
 };
 
+enum ProjectEngineVersion {
+	kProjectEngineVersionUnknown,
+	kProjectEngineVersion1,
+	kProjectEngineVersion2,
+};
+
 enum DataReadErrorCode {
 	kDataReadErrorNone = 0,
 
@@ -88,6 +94,7 @@ enum DataObjectType : uint {
 	kAssetCatalog							= 0xd,
 	kGlobalObjectInfo						= 0x17,
 	kUnknown19								= 0x19,
+	kUnknown2B								= 0x2b,
 
 	kProjectStructuralDef					= 0x2,
 	kSectionStructuralDef					= 0x3,
@@ -174,7 +181,7 @@ namespace StructuralFlags {
 
 class DataReader {
 public:
-	DataReader(int64 globalPosition, Common::SeekableReadStreamEndian &stream, ProjectFormat projectFormat);
+	DataReader(int64 globalPosition, Common::SeekableReadStreamEndian &stream, ProjectFormat projectFormat, ProjectEngineVersion projectEngineVersion);
 
 	bool readU8(uint8 &value);
 	bool readU16(uint16 &value);
@@ -208,7 +215,9 @@ public:
 	inline int64 tellGlobal() const { return _globalPosition + tell(); }
 
 	ProjectFormat getProjectFormat() const;
+	ProjectEngineVersion getProjectEngineVersion() const;
 	bool isBigEndian() const;
+	bool isV2Project() const;
 
 	void setPermitDamagedStrings(bool permit);
 
@@ -217,6 +226,7 @@ private:
 
 	Common::SeekableReadStreamEndian &_stream;
 	ProjectFormat _projectFormat;
+	ProjectEngineVersion _projectEngineVersion;
 	int64 _globalPosition;
 
 	bool _permitDamagedStrings;
@@ -541,6 +551,16 @@ protected:
 	DataReadErrorCode load(DataReader &reader) override;
 };
 
+struct Unknown2B : public DataObject {
+	Unknown2B();
+
+	uint32 persistFlags;
+	uint32 sizeIncludingTag;
+
+protected:
+	DataReadErrorCode load(DataReader &reader) override;
+};
+
 struct StructuralDef : public DataObject {
 	StructuralDef();
 
@@ -883,6 +903,7 @@ struct BehaviorModifier : public DataObject {
 	uint32 unknown4;
 	uint16 unknown5;
 	uint32 unknown6;
+	uint32 unknown8; // revision 2 only.
 	Point editorLayoutPosition;
 	uint16 lengthOfName;
 	uint16 numChildren;
@@ -930,6 +951,7 @@ struct MiniscriptProgram {
 	Common::Array<Attribute> attributes;
 
 	ProjectFormat projectFormat;
+	ProjectEngineVersion projectEngineVersion;
 	bool isBigEndian;
 
 	bool load(DataReader &reader);
@@ -944,6 +966,7 @@ struct TypicalModifierHeader {
 	uint32 guid;
 	uint8 unknown3[6];
 	uint32 unknown4;
+	uint32 unknown5;  // V2 header only
 	Point editorLayoutPosition;
 	uint16 lengthOfName;
 
diff --git a/engines/mtropolis/detection.cpp b/engines/mtropolis/detection.cpp
index 54c19bbf7c9..fb6f0113f5b 100644
--- a/engines/mtropolis/detection.cpp
+++ b/engines/mtropolis/detection.cpp
@@ -34,6 +34,7 @@ static const PlainGameDescriptor mTropolisGames[] = {
 	{"albert3", "Uncle Albert's Mysterious Island"},
 	{"spqr", "SPQR: The Empire's Darkest Hour"},
 	{"sttgs", "Star Trek: The Game Show"},
+	{"unit", "Unit: Re-Booted"},
 	{nullptr, nullptr}
 };
 
diff --git a/engines/mtropolis/detection.h b/engines/mtropolis/detection.h
index 507c8e6743e..9818a3ea0b7 100644
--- a/engines/mtropolis/detection.h
+++ b/engines/mtropolis/detection.h
@@ -35,6 +35,7 @@ enum MTropolisGameID {
 	GID_ALBERT3				= 5,
 	GID_SPQR				= 6,
 	GID_STTGS				= 7,
+	GID_UNIT				= 8,
 };
 
 // Boot IDs - These can be shared across different variants if the file list and other properties are identical.
@@ -68,6 +69,8 @@ enum MTropolisGameBootID {
 	MTBOOT_SPQR_RETAIL_MAC,
 
 	MTBOOT_STTGS_DEMO_WIN,
+
+	MTBOOT_UNIT_REBOOTED_WIN,
 };
 
 enum MTGameFlag {
diff --git a/engines/mtropolis/detection_tables.h b/engines/mtropolis/detection_tables.h
index e31ec9460f7..141dd6ceae7 100644
--- a/engines/mtropolis/detection_tables.h
+++ b/engines/mtropolis/detection_tables.h
@@ -666,6 +666,25 @@ static const MTropolisGameDescription gameDescriptions[] = {
 		MTBOOT_STTGS_DEMO_WIN,
 	},
 
+	{ // Unit: Rebooted (Music Videos)
+		{
+			"unit",
+			"",
+			{
+				// { "UNIT32.EXE", 0, "c23dccd2b7a525a9f7bb8505f7c7f2d4", 1085952 },
+				{ "DATA.MFX", 0, "9a3a0c2f11173c7af3f16d42a2b7c1b7", 194625739 },
+				AD_LISTEND
+			},
+			Common::EN_ANY,
+			Common::kPlatformWindows,
+			ADGF_UNSTABLE,
+			GUIO0()
+		},
+		GID_UNIT,
+		0,
+		MTBOOT_UNIT_REBOOTED_WIN,
+	},
+
 	{ AD_TABLE_END_MARKER, 0, 0, MTBOOT_INVALID }
 };
 
diff --git a/engines/mtropolis/elements.cpp b/engines/mtropolis/elements.cpp
index ecba035daf5..9bbb185ec36 100644
--- a/engines/mtropolis/elements.cpp
+++ b/engines/mtropolis/elements.cpp
@@ -536,6 +536,10 @@ bool MovieElement::readAttribute(MiniscriptThread *thread, DynamicValue &result,
 		result.setInt(_currentTimestamp);
 		return true;
 	}
+	if (attrib == "timescale") {
+		result.setInt(_timeScale);
+		return true;
+	}
 
 	return VisualElement::readAttribute(thread, result, attrib);
 }
@@ -633,13 +637,21 @@ void MovieElement::activate() {
 		}
 
 		Video::QuickTimeDecoder *qtDecoder = new Video::QuickTimeDecoder();
-		qtDecoder->setChunkBeginOffset(movieAsset->getMovieDataPos());
 		qtDecoder->setVolume(_volume * 255 / 100);
 
 		_videoDecoder.reset(qtDecoder);
 		_damagedFrames = movieAsset->getDamagedFrames();
 
-		Common::SafeSeekableSubReadStream *movieDataStream = new Common::SafeSeekableSubReadStream(stream, movieAsset->getMovieDataPos(), movieAsset->getMovieDataPos() + movieAsset->getMovieDataSize(), DisposeAfterUse::NO);
+		Common::SafeSeekableSubReadStream *movieDataStream;
+
+		if (movieAsset->getMovieDataSize() > 0) {
+			qtDecoder->setChunkBeginOffset(movieAsset->getMoovAtomPos());
+			movieDataStream = new Common::SafeSeekableSubReadStream(stream, movieAsset->getMovieDataPos(), movieAsset->getMovieDataPos() + movieAsset->getMovieDataSize(), DisposeAfterUse::NO);
+		} else {
+			// If no data size, the movie data is all over the file and the MOOV atom may be after it.
+			movieDataStream = new Common::SafeSeekableSubReadStream(stream, 0, stream->size(), DisposeAfterUse::NO);
+			movieDataStream->seek(movieAsset->getMoovAtomPos());
+		}
 
 		if (!_videoDecoder->loadStream(movieDataStream))
 			_videoDecoder.reset();
diff --git a/engines/mtropolis/miniscript.cpp b/engines/mtropolis/miniscript.cpp
index 50a2afa3dc5..bafba72ef7d 100644
--- a/engines/mtropolis/miniscript.cpp
+++ b/engines/mtropolis/miniscript.cpp
@@ -380,7 +380,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
 	}
 
 	Common::MemoryReadStreamEndian stream(&program.bytecode[0], program.bytecode.size(), program.isBigEndian);
-	Data::DataReader reader(0, stream, program.projectFormat);
+	Data::DataReader reader(0, stream, program.projectFormat, program.projectEngineVersion);
 
 	Common::Array<InstructionData> rawInstructions;
 	rawInstructions.resize(program.numOfInstructions);
@@ -450,7 +450,7 @@ bool MiniscriptParser::parse(const Data::MiniscriptProgram &program, Common::Sha
 			dataLoc = &rawInstruction.contents[0];
 
 		Common::MemoryReadStreamEndian instrContentsStream(static_cast<const byte *>(dataLoc), rawInstruction.contents.size(), reader.isBigEndian());
-		Data::DataReader instrContentsReader(0, instrContentsStream, reader.getProjectFormat());
+		Data::DataReader instrContentsReader(0, instrContentsStream, reader.getProjectFormat(), reader.getProjectEngineVersion());
 
 		if (!rawInstruction.instrFactory->create(&programData[baseOffset + rawInstruction.pdPosition], rawInstruction.flags, instrContentsReader, miniscriptInstructions[i], parserFeedback)) {
 			// Destroy any already-created instructions
diff --git a/engines/mtropolis/mtropolis.cpp b/engines/mtropolis/mtropolis.cpp
index 494584397df..99475c22d29 100644
--- a/engines/mtropolis/mtropolis.cpp
+++ b/engines/mtropolis/mtropolis.cpp
@@ -161,22 +161,23 @@ Common::Error MTropolisEngine::run() {
 			preferredHeight = 360;
 			HackSuites::addObsidianImprovedWidescreen(*_gameDescription, _runtime->getHacks());
 		}
-	}
-
-	if (_gameDescription->gameID == GID_MTI) {
+	} else if (_gameDescription->gameID == GID_MTI) {
 		preferredWidth = 640;
 		preferredHeight = 480;
 		preferredColorDepthMode = kColorDepthMode8Bit;
 		enhancedColorDepthMode = kColorDepthMode32Bit;
 
 		HackSuites::addMTIQuirks(*_gameDescription, _runtime->getHacks());
-	}
-
-	if (_gameDescription->gameID == GID_SPQR) {
+	} else if (_gameDescription->gameID == GID_SPQR) {
 		preferredWidth = 640;
 		preferredHeight = 480;
 		preferredColorDepthMode = kColorDepthMode8Bit;
 		enhancedColorDepthMode = kColorDepthMode32Bit;
+	} else if (_gameDescription->gameID == GID_UNIT) {
+		preferredWidth = 640;
+		preferredHeight = 480;
+		preferredColorDepthMode = kColorDepthMode32Bit;
+		enhancedColorDepthMode = kColorDepthMode32Bit;
 	}
 
 	if (ConfMan.getBool("mtropolis_mod_minimum_transition_duration"))
diff --git a/engines/mtropolis/plugin/standard_data.cpp b/engines/mtropolis/plugin/standard_data.cpp
index 1cb6038ed0f..b7ee40404a5 100644
--- a/engines/mtropolis/plugin/standard_data.cpp
+++ b/engines/mtropolis/plugin/standard_data.cpp
@@ -32,7 +32,7 @@ CursorModifier::CursorModifier() : haveRemoveWhen(false) {
 }
 
 DataReadErrorCode CursorModifier::load(PlugIn &plugIn, const PlugInModifier &prefix, DataReader &reader) {
-	if (prefix.plugInRevision != 0 && prefix.plugInRevision != 1)
+	if (prefix.plugInRevision != 0 && prefix.plugInRevision != 1 && prefix.plugInRevision != 2)
 		return kDataReadErrorUnsupportedRevision;
 
 	if (!applyWhen.load(reader))
diff --git a/engines/mtropolis/runtime.cpp b/engines/mtropolis/runtime.cpp
index 6c6dfb4a53a..48882b93779 100644
--- a/engines/mtropolis/runtime.cpp
+++ b/engines/mtropolis/runtime.cpp
@@ -6971,6 +6971,7 @@ void Project::loadFromDescription(const ProjectDescription &desc, const Hacks &h
 	const Data::PlugInModifierRegistry &plugInDataLoaderRegistry = _plugInRegistry.getDataLoaderRegistry();
 
 	size_t numSegments = desc.getSegments().size();
+	debug(1, "Loading %d segments", (int)numSegments);
 	_segments.resize(numSegments);
 
 	for (size_t i = 0; i < numSegments; i++) {
@@ -6992,15 +6993,24 @@ void Project::loadFromDescription(const ProjectDescription &desc, const Hacks &h
 		_isBigEndian = true;
 		_projectFormat = Data::kProjectFormatMacintosh;
 	} else {
-		error("Unrecognized project segment header");
+		warning("Unrecognized project segment header (startValue: %d)", startValue);
+		_projectFormat = Data::kProjectFormatWindows;
 	}
 
 	Common::SeekableSubReadStreamEndian stream(baseStream, 2, baseStream->size(), _isBigEndian);
-	if (stream.readUint32() != 0xaa55a5a5 || stream.readUint32() != 0 || stream.readUint32() != 14) {
-		error("Unrecognized project segment header");
+	const uint32 magic = stream.readUint32();
+	const uint32 hdr1 = stream.readUint32();
+	const uint32 hdr2 = stream.readUint32();
+	if (magic != 0xaa55a5a5 || (hdr1 != 0 && hdr1 != 0x2000000) || hdr2 != 14) {
+		error("Unrecognized project segment header (%x, %x, %d)", magic, hdr1, hdr2);
 	}
 
-	Data::DataReader reader(2, stream, _projectFormat);
+	if (hdr1 == 0)
+		_projectEngineVersion = Data::kProjectEngineVersion1;
+	else
+		_projectEngineVersion = Data::kProjectEngineVersion2;
+
+	Data::DataReader reader(2, stream, _projectFormat, _projectEngineVersion);
 
 	Common::SharedPtr<Data::DataObject> dataObject;
 	Data::loadDataObject(_plugInRegistry.getDataLoaderRegistry(), reader, dataObject);
@@ -7027,11 +7037,11 @@ void Project::loadFromDescription(const ProjectDescription &desc, const Hacks &h
 		StreamDesc &streamDesc = _streams[i];
 		const Data::ProjectCatalog::StreamDesc &srcStream = catalog->streams[i];
 
-		if (!strcmp(srcStream.streamType, "assetStream"))
+		if (!strcmp(srcStream.streamType, "assetStream") || !strcmp(srcStream.streamType, "assetstream"))
 			streamDesc.streamType = kStreamTypeAsset;
-		else if (!strcmp(srcStream.streamType, "bootStream"))
+		else if (!strcmp(srcStream.streamType, "bootStream") || !strcmp(srcStream.streamType, "bootstream"))
 			streamDesc.streamType = kStreamTypeBoot;
-		else if (!strcmp(srcStream.streamType, "sceneStream"))
+		else if (!strcmp(srcStream.streamType, "sceneStream") || !strcmp(srcStream.streamType, "scenestream"))
 			streamDesc.streamType = kStreamTypeScene;
 		else
 			streamDesc.streamType = kStreamTypeUnknown;
@@ -7075,7 +7085,7 @@ void Project::loadSceneFromStream(const Common::SharedPtr<Structural> &scene, ui
 	openSegmentStream(segmentIndex);
 
 	Common::SeekableSubReadStreamEndian stream(_segments[segmentIndex].weakStream, streamDesc.pos, streamDesc.pos + streamDesc.size, _isBigEndian);
-	Data::DataReader reader(streamDesc.pos, stream, _projectFormat);
+	Data::DataReader reader(streamDesc.pos, stream, _projectFormat, _projectEngineVersion);
 
 	if (getRuntime()->getHacks().mtiHispaniolaDamagedStringHack && scene->getName() == "C01b : Main Deck Helm Kidnap")
 		reader.setPermitDamagedStrings(true);
@@ -7203,7 +7213,7 @@ void Project::forceLoadAsset(uint32 assetID, Common::Array<Common::SharedPtr<Ass
 	openSegmentStream(segmentIndex);
 
 	Common::SeekableSubReadStreamEndian stream(_segments[segmentIndex].weakStream, streamDesc.pos, streamDesc.pos + streamDesc.size, _isBigEndian);
-	Data::DataReader reader(streamDesc.pos, stream, _projectFormat);
+	Data::DataReader reader(streamDesc.pos, stream, _projectFormat, _projectEngineVersion);
 
 	const Data::PlugInModifierRegistry &plugInDataLoaderRegistry = _plugInRegistry.getDataLoaderRegistry();
 
@@ -7363,7 +7373,7 @@ void Project::loadBootStream(size_t streamIndex, const Hacks &hacks) {
 	openSegmentStream(segmentIndex);
 
 	Common::SeekableSubReadStreamEndian stream(_segments[segmentIndex].weakStream, streamDesc.pos, streamDesc.pos + streamDesc.size, _isBigEndian);
-	Data::DataReader reader(streamDesc.pos, stream, _projectFormat);
+	Data::DataReader reader(streamDesc.pos, stream, _projectFormat, _projectEngineVersion);
 
 	ChildLoaderStack loaderStack;
 	AssetDefLoaderContext assetDefLoader;
@@ -7423,6 +7433,7 @@ void Project::loadBootStream(size_t streamIndex, const Hacks &hacks) {
 				} break;
 			case Data::DataObjectTypes::kStreamHeader:
 			case Data::DataObjectTypes::kUnknown19:
+			case Data::DataObjectTypes::kUnknown2B:
 				// Ignore
 				break;
 			default:
diff --git a/engines/mtropolis/runtime.h b/engines/mtropolis/runtime.h
index 9c5c7e1b650..7213f761aaf 100644
--- a/engines/mtropolis/runtime.h
+++ b/engines/mtropolis/runtime.h
@@ -2522,6 +2522,7 @@ private:
 	Common::Array<LabelTree> _labelTree;
 	Common::Array<LabelSuperGroup> _labelSuperGroups;
 	Data::ProjectFormat _projectFormat;
+	Data::ProjectEngineVersion _projectEngineVersion;
 	bool _isBigEndian;
 
 	Common::Array<AssetDesc *> _assetsByID;




More information about the Scummvm-git-logs mailing list