[Scummvm-git-logs] scummvm master -> 06e1446d5e76bd394ea0c6e5bd35cab10c7e06fa

npjg noreply at scummvm.org
Sat May 10 21:25:50 UTC 2025


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

Summary:
100e7267a3 MEDIASTATION: Give datum types correct names
b53def9abf MEDIASTATION: Use stream methods to read typed fields
a43e097df1 MEDIASTATION: Simplify Boot stream parsing
1e76610489 MEDIASTATION: Correctly implement collection methods
a5c12ca646 MEDIASTATION: Make the screen asset actually an asset
06e1446d5e MEDIASTATION: Use one hashmap for event handlers


Commit: 100e7267a3e7dd40d8a87d2b7f674dee333b6a71
    https://github.com/scummvm/scummvm/commit/100e7267a3e7dd40d8a87d2b7f674dee333b6a71
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:15:44-04:00

Commit Message:
MEDIASTATION: Give datum types correct names

Changed paths:
    engines/mediastation/assetheader.cpp
    engines/mediastation/assets/sprite.cpp
    engines/mediastation/bitmap.cpp
    engines/mediastation/boot.cpp
    engines/mediastation/context.cpp
    engines/mediastation/datum.cpp
    engines/mediastation/datum.h
    engines/mediastation/mediascript/codechunk.cpp


diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
index c808d1a34c5..2d866cd05e8 100644
--- a/engines/mediastation/assetheader.cpp
+++ b/engines/mediastation/assetheader.cpp
@@ -167,29 +167,29 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 		//  - They might be in the same RIFF subfile as this header,
 		//  - They might be in a different RIFF subfile in the same CXT file,
 		//  - They might be in a different CXT file entirely.
-		_chunkReference = Datum(chunk, kDatumTypeReference).u.i;
+		_chunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
 		break;
 	}
 
 	case kAssetHeaderMovieAudioChunkReference: {
-		_audioChunkReference = Datum(chunk, kDatumTypeReference).u.i;
+		_audioChunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
 		break;
 	}
 
 	case kAssetHeaderMovieAnimationChunkReference: {
-		_animationChunkReference = Datum(chunk, kDatumTypeReference).u.i;
+		_animationChunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
 		break;
 	}
 
 	case kAssetHeaderBoundingBox: {
-		_boundingBox = Datum(chunk, kDatumTypeBoundingBox).u.bbox;
+		_boundingBox = Datum(chunk, kDatumTypeRect).u.bbox;
 		break;
 	}
 
 	case kAssetHeaderMouseActiveArea: {
-		uint16 total_points = Datum(chunk, kDatumTypeUint16_1).u.i;
+		uint16 total_points = Datum(chunk, kDatumTypeUint16).u.i;
 		for (int i = 0; i < total_points; i++) {
-			Common::Point *point = Datum(chunk, kDatumTypePoint2).u.point;
+			Common::Point *point = Datum(chunk, kDatumTypePoint).u.point;
 			_mouseActiveArea.push_back(point);
 		}
 		break;
@@ -226,7 +226,7 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 
 	case kAssetHeaderFrameRate: {
-		_frameRate = static_cast<uint32>(Datum(chunk, kDatumTypeFloat64_2).u.f);
+		_frameRate = static_cast<uint32>(Datum(chunk, kDatumTypeDouble).u.f);
 		break;
 	}
 
@@ -279,17 +279,17 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 
 	case kAssetHeaderStartPoint: {
-		_startPoint = Datum(chunk, kDatumTypePoint2).u.point;
+		_startPoint = Datum(chunk, kDatumTypePoint).u.point;
 		break;
 	}
 
 	case kAssetHeaderEndPoint: {
-		_endPoint = Datum(chunk, kDatumTypePoint2).u.point;
+		_endPoint = Datum(chunk, kDatumTypePoint).u.point;
 		break;
 	}
 
 	case kAssetHeaderStepRate: {
-		double _stepRateFloat = Datum(chunk, kDatumTypeFloat64_2).u.f;
+		double _stepRateFloat = Datum(chunk, kDatumTypeDouble).u.f;
 		// This should always be an integer anyway,
 		// so we'll cast away any fractional part.
 		_stepRate = static_cast<uint32>(_stepRateFloat);
@@ -373,7 +373,7 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 }
 
 AssetHeaderSectionType AssetHeader::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	AssetHeaderSectionType sectionType = static_cast<AssetHeaderSectionType>(datum.u.i);
 	return sectionType;
 }
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 6528a0c7826..9911e83c231 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -29,7 +29,7 @@ namespace MediaStation {
 SpriteFrameHeader::SpriteFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
 	_index = Datum(chunk).u.i;
 	debugC(5, kDebugLoading, "SpriteFrameHeader::SpriteFrameHeader(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
-	_boundingBox = Datum(chunk, kDatumTypePoint2).u.point;
+	_boundingBox = Datum(chunk, kDatumTypePoint).u.point;
 	debugC(5, kDebugLoading, "SpriteFrameHeader::SpriteFrameHeader(): _boundingBox (@0x%llx)", static_cast<long long int>(chunk.pos()));
 }
 
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index dd5ec9e044f..37153b32821 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -26,16 +26,16 @@
 namespace MediaStation {
 
 BitmapHeader::BitmapHeader(Chunk &chunk) {
-	uint headerSizeInBytes = Datum(chunk, kDatumTypeUint16_1).u.i;
+	uint headerSizeInBytes = Datum(chunk, kDatumTypeUint16).u.i;
 	debugC(5, kDebugLoading, "BitmapHeader::BitmapHeader(): headerSize = 0x%x", headerSizeInBytes);
 	_dimensions = Datum(chunk).u.point;
-	_compressionType = static_cast<BitmapCompressionType>(Datum(chunk, kDatumTypeUint16_1).u.i);
+	_compressionType = static_cast<BitmapCompressionType>(Datum(chunk, kDatumTypeUint16).u.i);
 	debugC(5, kDebugLoading, "BitmapHeader::BitmapHeader(): _compressionType = 0x%x", static_cast<uint>(_compressionType));
 	// TODO: Figure out what this is.
 	// This has something to do with the width of the bitmap but is always
 	// a few pixels off from the width. And in rare cases it seems to be
 	// the true width!
-	unk2 = Datum(chunk, kDatumTypeUint16_1).u.i;
+	unk2 = Datum(chunk, kDatumTypeUint16).u.i;
 }
 
 BitmapHeader::~BitmapHeader() {
diff --git a/engines/mediastation/boot.cpp b/engines/mediastation/boot.cpp
index 8e7aa9c16d0..7100cbbe7a8 100644
--- a/engines/mediastation/boot.cpp
+++ b/engines/mediastation/boot.cpp
@@ -27,9 +27,9 @@ namespace MediaStation {
 
 #pragma region VersionInfo
 VersionInfo::VersionInfo(Chunk &chunk) {
-	_majorVersion = Datum(chunk, kDatumTypeUint16_1).u.i;
-	_minorVersion = Datum(chunk, kDatumTypeUint16_1).u.i;
-	_revision = Datum(chunk, kDatumTypeUint16_1).u.i;
+	_majorVersion = Datum(chunk, kDatumTypeUint16).u.i;
+	_minorVersion = Datum(chunk, kDatumTypeUint16).u.i;
+	_revision = Datum(chunk, kDatumTypeUint16).u.i;
 	string = Datum(chunk, kDatumTypeString).u.string;
 }
 
@@ -108,7 +108,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 }
 
 ContextDeclarationSectionType ContextDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	ContextDeclarationSectionType sectionType = static_cast<ContextDeclarationSectionType>(datum.u.i);
 	return sectionType;
 }
@@ -133,13 +133,13 @@ UnknownDeclaration::UnknownDeclaration(Chunk &chunk) {
 
 	sectionType = getSectionType(chunk);
 	if (kUnknownDeclarationUnk1 == sectionType) {
-		_unk = Datum(chunk, kDatumTypeUint16_1).u.i;
+		_unk = Datum(chunk, kDatumTypeUint16).u.i;
 	} else {
 		error("UnknownDeclaration(): Expected section type UNK_1, got 0x%x", static_cast<uint>(sectionType));
 	}
 	sectionType = getSectionType(chunk);
 	if (kUnknownDeclarationUnk2 == sectionType) {
-		uint16 repeatedUnk = Datum(chunk, kDatumTypeUint16_1).u.i;
+		uint16 repeatedUnk = Datum(chunk, kDatumTypeUint16).u.i;
 		if (repeatedUnk != _unk) {
 			warning("UnknownDeclaration(): Expected unknown values to match, but 0x%x != 0x%x", _unk, repeatedUnk);
 		}
@@ -149,7 +149,7 @@ UnknownDeclaration::UnknownDeclaration(Chunk &chunk) {
 }
 
 UnknownDeclarationSectionType UnknownDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	UnknownDeclarationSectionType sectionType = static_cast<UnknownDeclarationSectionType>(datum.u.i);
 	return sectionType;
 }
@@ -170,7 +170,7 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 	// Read the file ID.
 	sectionType = getSectionType(chunk);
 	if (kFileDeclarationFileId == sectionType) {
-		_id = Datum(chunk, kDatumTypeUint16_1).u.i;
+		_id = Datum(chunk, kDatumTypeUint16).u.i;
 	} else {
 		error("FileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -178,7 +178,7 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 	// Read the intended file location.
 	sectionType = getSectionType(chunk);
 	if (kFileDeclarationFileNameAndType == sectionType) {
-		Datum datum = Datum(chunk, kDatumTypeUint16_1);
+		Datum datum = Datum(chunk, kDatumTypeUint16);
 		// TODO: Verify we actually read a valid enum member.
 		_intendedLocation = static_cast<IntendedFileLocation>(datum.u.i);
 	} else {
@@ -192,7 +192,7 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 }
 
 FileDeclarationSectionType FileDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	FileDeclarationSectionType sectionType = static_cast<FileDeclarationSectionType>(datum.u.i);
 	return sectionType;
 }
@@ -218,7 +218,7 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the asset ID.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationAssetId == sectionType) {
-		_assetId = Datum(chunk, kDatumTypeUint16_1).u.i;
+		_assetId = Datum(chunk, kDatumTypeUint16).u.i;
 	} else {
 		error("SubfileDeclaration(): Expected section type ASSET_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -226,7 +226,7 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the file ID.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationFileId == sectionType) {
-		_fileId = Datum(chunk, kDatumTypeUint16_1).u.i;
+		_fileId = Datum(chunk, kDatumTypeUint16).u.i;
 	} else {
 		error("SubfileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -234,14 +234,14 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the start offset from the absolute start of the file.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationStartOffset == sectionType) {
-		_startOffsetInFile = Datum(chunk, kDatumTypeUint32_1).u.i;
+		_startOffsetInFile = Datum(chunk, kDatumTypeUint32).u.i;
 	} else {
 		error("SubfileDeclaration(): Expected section type START_OFFSET, got 0x%x", static_cast<uint>(sectionType));
 	}
 }
 
 SubfileDeclarationSectionType SubfileDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	SubfileDeclarationSectionType sectionType = static_cast<SubfileDeclarationSectionType>(datum.u.i);
 	return sectionType;
 }
@@ -249,9 +249,9 @@ SubfileDeclarationSectionType SubfileDeclaration::getSectionType(Chunk &chunk) {
 
 #pragma region CursorDeclaration
 CursorDeclaration::CursorDeclaration(Chunk& chunk) {
-	uint16 unk1 = Datum(chunk, kDatumTypeUint16_1).u.i; // Always 0x0001
-	_id = Datum(chunk, kDatumTypeUint16_1).u.i;
-	_unk = Datum(chunk, kDatumTypeUint16_1).u.i;
+	uint16 unk1 = Datum(chunk, kDatumTypeUint16).u.i; // Always 0x0001
+	_id = Datum(chunk, kDatumTypeUint16).u.i;
+	_unk = Datum(chunk, kDatumTypeUint16).u.i;
 	_name = Datum(chunk, kDatumTypeFilename).u.string;
 	debugC(5, kDebugLoading, " - CursorDeclaration(): unk1 = 0x%x, id = 0x%x, unk = 0x%x, name = %s", unk1, _id, _unk, _name->c_str());
 }
@@ -276,7 +276,7 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 	Subfile subfile = getNextSubfile();
 	Chunk chunk = subfile.nextChunk();
 
-	uint32 beforeSectionTypeUnk = Datum(chunk, kDatumTypeUint16_1).u.i; // Usually 0x0001
+	uint32 beforeSectionTypeUnk = Datum(chunk, kDatumTypeUint16).u.i; // Usually 0x0001
 	debugC(5, kDebugLoading, "Boot::Boot(): unk1 = 0x%x", beforeSectionTypeUnk);
 
 	BootSectionType sectionType = getSectionType(chunk);
@@ -419,7 +419,7 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 }
 
 BootSectionType Boot::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16_1);
+	Datum datum = Datum(chunk, kDatumTypeUint16);
 	BootSectionType sectionType = static_cast<BootSectionType>(datum.u.i);
 	return sectionType;
 }
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 86f88d43ab8..dfe0af50948 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -160,20 +160,20 @@ void Context::registerActiveAssets() {
 }
 
 void Context::readParametersSection(Chunk &chunk) {
-	_fileNumber = Datum(chunk, kDatumTypeUint16_1).u.i;
+	_fileNumber = Datum(chunk, kDatumTypeUint16).u.i;
 
-	ContextParametersSectionType sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16_1).u.i);
+	ContextParametersSectionType sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16).u.i);
 	while (sectionType != kContextParametersEmptySection) {
 		debugC(5, kDebugLoading, "ContextParameters::ContextParameters: sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 		switch (sectionType) {
 		case kContextParametersName: {
-			uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16_1).u.i;
+			uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16).u.i;
 			if (repeatedFileNumber != _fileNumber) {
 				warning("ContextParameters::ContextParameters(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
 			}
 			_contextName = Datum(chunk, kDatumTypeString).u.string;
 
-			uint endingFlag = Datum(chunk, kDatumTypeUint16_1).u.i;
+			uint endingFlag = Datum(chunk, kDatumTypeUint16).u.i;
 			if (endingFlag != 0) {
 				warning("ContextParameters::ContextParameters(): Got non-zero ending flag 0x%x", endingFlag);
 			}
@@ -200,12 +200,12 @@ void Context::readParametersSection(Chunk &chunk) {
 			error("ContextParameters::ContextParameters(): Unknown section type 0x%x", static_cast<uint>(sectionType));
 		}
 
-		sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16_1).u.i);
+		sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16).u.i);
 	}
 }
 
 void Context::readVariable(Chunk &chunk) {
-	uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16_1).u.i;
+	uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16).u.i;
 	if (repeatedFileNumber != _fileNumber) {
 		warning("Context::readVariable(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
 	}
@@ -234,7 +234,7 @@ void Context::readNewStyleHeaderSections(Subfile &subfile, Chunk &chunk) {
 	while (moreSectionsToRead) {
 		// Verify this chunk is a header.
 		// TODO: What are the situations when it's not?
-		uint16 sectionType = Datum(chunk, kDatumTypeUint16_1).u.i;
+		uint16 sectionType = Datum(chunk, kDatumTypeUint16).u.i;
 		debugC(5, kDebugLoading, "Context::readNewStyleHeaderSections(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 		bool chunkIsHeader = (sectionType == 0x000d);
 		if (!chunkIsHeader) {
@@ -291,7 +291,7 @@ void Context::readAssetFromLaterSubfile(Subfile &subfile) {
 }
 
 bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
-	uint16 sectionType = Datum(chunk, kDatumTypeUint16_1).u.i;
+	uint16 sectionType = Datum(chunk, kDatumTypeUint16).u.i;
 	debugC(5, kDebugLoading, "Context::readHeaderSection(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 	switch (sectionType) {
 	case kContextParametersSection: {
@@ -318,7 +318,7 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 		delete[] buffer;
 		debugC(5, kDebugLoading, "Context::readHeaderSection(): Read palette");
 		// This is likely just an ending flag that we expect to be zero.
-		uint endingFlag = Datum(chunk, kDatumTypeUint16_1).u.i;
+		uint endingFlag = Datum(chunk, kDatumTypeUint16).u.i;
 		if (endingFlag != 0) {
 			warning("Context::readHeaderSection(): Got non-zero ending flag 0x%x", endingFlag);
 		}
diff --git a/engines/mediastation/datum.cpp b/engines/mediastation/datum.cpp
index 9dd46ad28f2..a2b0bf97fba 100644
--- a/engines/mediastation/datum.cpp
+++ b/engines/mediastation/datum.cpp
@@ -25,7 +25,7 @@
 namespace MediaStation {
 
 Datum::Datum() {
-	t = kDatumTypeInvalid;
+	t = kDatumTypeEmpty;
 	u.i = 0;
 }
 
@@ -47,43 +47,43 @@ void Datum::readWithType(Common::SeekableReadStream &chunk) {
 	if (kDatumTypeUint8 == t) {
 		u.i = chunk.readByte();
 
-	} else if (kDatumTypeUint16_1 == t || kDatumTypeUint16_2 == t) {
+	} else if (kDatumTypeUint16 == t || kDatumTypeVersion == t) {
 		u.i = chunk.readUint16LE();
 
-	} else if (kDatumTypeInt16_1 == t || kDatumTypeInt16_2 == t) {
+	} else if (kDatumTypeInt16 == t || kDatumTypeGraphicUnit == t) {
 		u.i = chunk.readSint16LE();
 
-	} else if (kDatumTypeUint32_1 == t || kDatumTypeUint32_2 == t) {
+	} else if (kDatumTypeUint32 == t || kDatumTypeInt32 == t) {
 		u.i = chunk.readUint32LE();
 
-	} else if (kDatumTypeFloat64_1 == t || kDatumTypeFloat64_2 == t) {
+	} else if (kDatumTypeTime == t || kDatumTypeDouble == t) {
 		u.f = chunk.readDoubleLE();
 
 	} else if (kDatumTypeString == t || kDatumTypeFilename == t) {
 		// TODO: This copies the string. Can we read it directly from the chunk?
-		int size = Datum(chunk, kDatumTypeUint32_1).u.i;
+		int size = Datum(chunk, kDatumTypeUint32).u.i;
 		char *buffer = new char[size + 1];
 		chunk.read(buffer, size);
 		buffer[size] = '\0';
 		u.string = new Common::String(buffer);
 		delete[] buffer;
 
-	} else if (kDatumTypePoint1 == t || kDatumTypePoint2 == t) {
-		uint16 x = Datum(chunk, kDatumTypeInt16_2).u.i;
-		uint16 y = Datum(chunk, kDatumTypeInt16_2).u.i;
+	} else if (kDatumTypeGraphicSize == t || kDatumTypePoint == t) {
+		uint16 x = Datum(chunk, kDatumTypeGraphicUnit).u.i;
+		uint16 y = Datum(chunk, kDatumTypeGraphicUnit).u.i;
 		u.point = new Common::Point(x, y);
 
-	} else if (kDatumTypeBoundingBox == t) {
-		Common::Point *leftTop = Datum(chunk, kDatumTypePoint2).u.point;
-		Common::Point *dimensions = Datum(chunk, kDatumTypePoint1).u.point;
+	} else if (kDatumTypeRect == t) {
+		Common::Point *leftTop = Datum(chunk, kDatumTypePoint).u.point;
+		Common::Point *dimensions = Datum(chunk, kDatumTypeGraphicSize).u.point;
 		u.bbox = new Common::Rect(*leftTop, dimensions->x, dimensions->y);
 		delete leftTop;
 		delete dimensions;
 
 	} else if (kDatumTypePolygon == t) {
-		uint16 totalPoints = Datum(chunk, kDatumTypeUint16_1).u.i;
+		uint16 totalPoints = Datum(chunk, kDatumTypeUint16).u.i;
 		for (int i = 0; i < totalPoints; i++) {
-			Common::Point *point = Datum(chunk, kDatumTypePoint1).u.point;
+			Common::Point *point = Datum(chunk, kDatumTypeGraphicSize).u.point;
 			u.polygon->push_back(point);
 		}
 
@@ -91,7 +91,7 @@ void Datum::readWithType(Common::SeekableReadStream &chunk) {
 		u.palette = new unsigned char[0x300];
 		chunk.read(u.palette, 0x300);
 
-	} else if (kDatumTypeReference == t) {
+	} else if (kDatumTypeChunkReference == t) {
 		u.chunkRef = chunk.readUint32BE();
 
 	} else {
diff --git a/engines/mediastation/datum.h b/engines/mediastation/datum.h
index 55dc6d025f1..a5b303c43bf 100644
--- a/engines/mediastation/datum.h
+++ b/engines/mediastation/datum.h
@@ -32,31 +32,26 @@
 namespace MediaStation {
 
 enum DatumType {
-	// This type isn't a type we see in data files; it is just a
-	// default initialization value.
-	kDatumTypeInvalid = 0x0000,
-
-	kDatumTypeUint8 = 0x0002,
-	// TODO: Understand why there are different (u)int16 type codes.
-	kDatumTypeUint16_1 = 0x0003,
-	kDatumTypeUint16_2 = 0x0013,
-	kDatumTypeInt16_1 = 0x0006,
-	kDatumTypeInt16_2 = 0x0010,
-	// TODO: Understand why there are two different uint32 type codes.
-	kDatumTypeUint32_1 = 0x0004,
-	kDatumTypeUint32_2 = 0x0007,
-	// TODO: Understand why there are two different float64 type codes.
-	kDatumTypeFloat64_1 = 0x0011,
-	kDatumTypeFloat64_2 = 0x0009,
-	kDatumTypeString = 0x0012,
-	kDatumTypeFilename = 0x000a,
-	kDatumTypePoint1 = 0x000f,
-	kDatumTypePoint2 = 0x000e,
-	kDatumTypeBoundingBox = 0x000d,
-	kDatumTypePolygon = 0x001d,
-	// These are other types.
-	kDatumTypePalette = 0x05aa,
-	kDatumTypeReference = 0x001b
+	kDatumTypeEmpty = 0x00,
+	kDatumTypeUint8 = 0x02,
+	kDatumTypeUint16 = 0x03,
+	kDatumTypeUint32 = 0x04,
+	kDatumTypeInt8 = 0x05,
+	kDatumTypeInt16 = 0x06,
+	kDatumTypeInt32 = 0x07,
+	kDatumTypeFloat = 0x08,
+	kDatumTypeDouble = 0x09,
+	kDatumTypeFilename = 0x0a,
+	kDatumTypeRect = 0x0d,
+	kDatumTypePoint = 0x0e,
+	kDatumTypeGraphicSize = 0x0f,
+	kDatumTypeGraphicUnit = 0x10,
+	kDatumTypeTime = 0x11,
+	kDatumTypeString = 0x12,
+	kDatumTypeVersion = 0x13,
+	kDatumTypeChunkReference = 0x1b,
+	kDatumTypePolygon = 0x1d,
+	kDatumTypePalette = 0x05aa
 };
 
 // It is the caller's responsibility to delete any heap items
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 5a065d1ba9f..5c95b000f77 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -30,13 +30,13 @@
 namespace MediaStation {
 
 CodeChunk::CodeChunk(Common::SeekableReadStream &chunk) {
-	uint lengthInBytes = Datum(chunk, kDatumTypeUint32_1).u.i;
+	uint lengthInBytes = Datum(chunk, kDatumTypeUint32).u.i;
 	debugC(5, kDebugLoading, "CodeChunk::CodeChunk(): Length 0x%x (@0x%llx)", lengthInBytes, static_cast<long long int>(chunk.pos()));
 	_bytecode = chunk.readStream(lengthInBytes);
 }
 
 ScriptValue CodeChunk::executeNextBlock() {
-	uint blockSize = Datum(*_bytecode, kDatumTypeUint32_1).u.i;
+	uint blockSize = Datum(*_bytecode, kDatumTypeUint32).u.i;
 	uint startingPos = _bytecode->pos();
 
 	ScriptValue returnValue;
@@ -57,7 +57,7 @@ ScriptValue CodeChunk::executeNextBlock() {
 }
 
 void CodeChunk::skipNextBlock() {
-	uint lengthInBytes = Datum(*_bytecode, kDatumTypeUint32_1).u.i;
+	uint lengthInBytes = Datum(*_bytecode, kDatumTypeUint32).u.i;
 	_bytecode->skip(lengthInBytes);
 }
 
@@ -219,7 +219,7 @@ ScriptValue CodeChunk::evaluateValue() {
 
 	case kOperandTypeString: {
 		// This is indeed a raw string, not a string wrapped in a datum!
-		uint size = Datum(*_bytecode, kDatumTypeUint16_1).u.i;
+		uint size = Datum(*_bytecode, kDatumTypeUint16).u.i;
 		Common::String string = _bytecode->readString('\0', size);
 		debugC(5, kDebugScript, "%s ", string.c_str());
 		returnValue.setToString(string);


Commit: b53def9abf0674100d0f6bda253837b049542d72
    https://github.com/scummvm/scummvm/commit/b53def9abf0674100d0f6bda253837b049542d72
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:24:32-04:00

Commit Message:
MEDIASTATION: Use stream methods to read typed fields

Instead of Datum class, which is now removed.

Changed paths:
  R engines/mediastation/datum.cpp
  R engines/mediastation/datum.h
    engines/mediastation/asset.cpp
    engines/mediastation/asset.h
    engines/mediastation/assetheader.cpp
    engines/mediastation/assetheader.h
    engines/mediastation/assets/font.cpp
    engines/mediastation/assets/hotspot.cpp
    engines/mediastation/assets/image.cpp
    engines/mediastation/assets/movie.cpp
    engines/mediastation/assets/sprite.cpp
    engines/mediastation/assets/sprite.h
    engines/mediastation/assets/text.cpp
    engines/mediastation/assets/text.h
    engines/mediastation/bitmap.cpp
    engines/mediastation/bitmap.h
    engines/mediastation/boot.cpp
    engines/mediastation/boot.h
    engines/mediastation/context.cpp
    engines/mediastation/context.h
    engines/mediastation/datafile.cpp
    engines/mediastation/datafile.h
    engines/mediastation/mediascript/codechunk.cpp
    engines/mediastation/mediascript/codechunk.h
    engines/mediastation/mediascript/collection.cpp
    engines/mediastation/mediascript/collection.h
    engines/mediastation/mediascript/eventhandler.cpp
    engines/mediastation/mediascript/eventhandler.h
    engines/mediastation/mediascript/function.cpp
    engines/mediastation/mediascript/scriptvalue.cpp
    engines/mediastation/mediascript/scriptvalue.h
    engines/mediastation/mediastation.cpp
    engines/mediastation/module.mk


diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index 04a3f5d3c54..a08f0a3d96e 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -46,7 +46,7 @@ int Asset::zIndex() const {
 	return _header->_zIndex;
 }
 
-Common::Rect *Asset::getBbox() {
+Common::Rect Asset::getBbox() const {
 	return _header->_boundingBox;
 }
 
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 1aa756333ec..9d84f597ecb 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -71,10 +71,10 @@ public:
 
 	AssetType type() const;
 	int zIndex() const;
+	Common::Rect getBbox() const;
 	AssetHeader *getHeader() const {
 		return _header;
 	}
-	Common::Rect *getBbox();
 
 protected:
 	AssetHeader *_header = nullptr;
diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
index 2d866cd05e8..6e9d8f87185 100644
--- a/engines/mediastation/assetheader.cpp
+++ b/engines/mediastation/assetheader.cpp
@@ -19,39 +19,27 @@
  *
  */
 
-#include "mediastation/datum.h"
 #include "mediastation/assetheader.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
 AssetHeader::AssetHeader(Chunk &chunk) {
-	// I arbitrarily chose the bitmap as the default union member,
-	// but they are all pointers so it doesn't matter.
-	_fileNumber = Datum(chunk).u.i;
-	// TODO: Cast to an asset type.
-	_type = static_cast<AssetType>(Datum(chunk).u.i);
-	_id = Datum(chunk).u.i;
+	_fileNumber = chunk.readTypedUint16();
+	_type = static_cast<AssetType>(chunk.readTypedUint16());
+	_id = chunk.readTypedUint16();
 	debugC(4, kDebugLoading, "AssetHeader::AssetHeader(): _type = 0x%x, _id = 0x%x (@0x%llx)", static_cast<uint>(_type), _id, static_cast<long long int>(chunk.pos()));
 
-	AssetHeaderSectionType sectionType = getSectionType(chunk);
+	AssetHeaderSectionType sectionType = static_cast<AssetHeaderSectionType>(chunk.readTypedUint16());
 	bool moreSectionsToRead = (kAssetHeaderEmptySection != sectionType);
 	while (moreSectionsToRead) {
 		readSection(sectionType, chunk);
-		sectionType = getSectionType(chunk);
+		sectionType = static_cast<AssetHeaderSectionType>(chunk.readTypedUint16());
 		moreSectionsToRead = (kAssetHeaderEmptySection != sectionType);
 	}
 }
 
 AssetHeader::~AssetHeader() {
-	delete _boundingBox;
-	_boundingBox = nullptr;
-
-	for (Common::Point *point : _mouseActiveArea) {
-		delete point;
-	}
-	_mouseActiveArea.clear();
-
 	for (auto it = _eventHandlers.begin(); it != _eventHandlers.end(); ++it) {
 		delete it->_value;
 	}
@@ -79,18 +67,6 @@ AssetHeader::~AssetHeader() {
 
 	delete _palette;
 	_palette = nullptr;
-
-	delete _name;
-	_name = nullptr;
-
-	delete _startPoint;
-	_startPoint = nullptr;
-
-	delete _endPoint;
-	_endPoint = nullptr;
-
-	delete _text;
-	_text = nullptr;
 }
 
 void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk) {
@@ -146,14 +122,14 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 
 	case kAssetHeaderStageId: {
-		_stageId = Datum(chunk).u.i;
+		_stageId = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderAssetId: {
 		// We already have this asset's ID, so we will just verify it is the same
 		// as the ID we have already read.
-		uint32 duplicateAssetId = Datum(chunk).u.i;
+		uint32 duplicateAssetId = chunk.readTypedUint16();
 		if (duplicateAssetId != _id) {
 			warning("AssetHeader::readSection(): AssetHeader ID %d does not match original asset ID %d", duplicateAssetId, _id);
 		}
@@ -167,87 +143,87 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 		//  - They might be in the same RIFF subfile as this header,
 		//  - They might be in a different RIFF subfile in the same CXT file,
 		//  - They might be in a different CXT file entirely.
-		_chunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
+		_chunkReference = chunk.readTypedChunkReference();
 		break;
 	}
 
 	case kAssetHeaderMovieAudioChunkReference: {
-		_audioChunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
+		_audioChunkReference = chunk.readTypedChunkReference();
 		break;
 	}
 
 	case kAssetHeaderMovieAnimationChunkReference: {
-		_animationChunkReference = Datum(chunk, kDatumTypeChunkReference).u.i;
+		_animationChunkReference = chunk.readTypedChunkReference();
 		break;
 	}
 
 	case kAssetHeaderBoundingBox: {
-		_boundingBox = Datum(chunk, kDatumTypeRect).u.bbox;
+		_boundingBox = chunk.readTypedRect();
 		break;
 	}
 
 	case kAssetHeaderMouseActiveArea: {
-		uint16 total_points = Datum(chunk, kDatumTypeUint16).u.i;
+		uint16 total_points = chunk.readTypedUint16();
 		for (int i = 0; i < total_points; i++) {
-			Common::Point *point = Datum(chunk, kDatumTypePoint).u.point;
+			Common::Point point = chunk.readTypedPoint();
 			_mouseActiveArea.push_back(point);
 		}
 		break;
 	}
 
 	case kAssetHeaderZIndex: {
-		_zIndex = Datum(chunk).u.i;
+		_zIndex = chunk.readTypedGraphicUnit();
 		break;
 	}
 
 	case kAssetHeaderAssetReference: {
-		_assetReference = Datum(chunk).u.i;
+		_assetReference = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderStartup: {
-		_startup = Datum(chunk).u.i;
+		_startup = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderTransparency: {
-		_transparency = Datum(chunk).u.i;
+		_transparency = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderHasOwnSubfile: {
-		_hasOwnSubfile = Datum(chunk).u.i;
+		_hasOwnSubfile = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderCursorResourceId: {
-		_cursorResourceId = Datum(chunk).u.i;
+		_cursorResourceId = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderFrameRate: {
-		_frameRate = static_cast<uint32>(Datum(chunk, kDatumTypeDouble).u.f);
+		_frameRate = static_cast<uint32>(chunk.readTypedDouble());
 		break;
 	}
 
 	case kAssetHeaderLoadType: {
-		_loadType = Datum(chunk).u.i;
+		_loadType = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderSoundInfo: {
-		_chunkCount = Datum(chunk).u.i;
-		_rate = Datum(chunk).u.i;
+		_chunkCount = chunk.readTypedUint16();
+		_rate = chunk.readTypedUint32();
 		break;
 	}
 
 	case kAssetHeaderMovieLoadType: {
-		_loadType = Datum(chunk).u.i;
+		_loadType = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderGetOffstageEvents: {
-		_getOffstageEvents = Datum(chunk).u.i;
+		_getOffstageEvents = chunk.readTypedByte();
 		break;
 	}
 
@@ -263,33 +239,33 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 
 	case kAssetHeaderDissolveFactor: {
-		_dissolveFactor = Datum(chunk).u.i;
+		_dissolveFactor = chunk.readTypedDouble();
 		break;
 	}
 
 	case kAssetHeaderSoundEncoding1:
 	case kAssetHeaderSoundEncoding2: {
-		_soundEncoding = static_cast<SoundEncoding>(Datum(chunk).u.i);
+		_soundEncoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
 		break;
 	}
 
 	case kAssetHeaderSpriteChunkCount: {
-		_chunkCount = Datum(chunk).u.i;
+		_chunkCount = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderStartPoint: {
-		_startPoint = Datum(chunk, kDatumTypePoint).u.point;
+		_startPoint = chunk.readTypedPoint();
 		break;
 	}
 
 	case kAssetHeaderEndPoint: {
-		_endPoint = Datum(chunk, kDatumTypePoint).u.point;
+		_endPoint = chunk.readTypedPoint();
 		break;
 	}
 
 	case kAssetHeaderStepRate: {
-		double _stepRateFloat = Datum(chunk, kDatumTypeDouble).u.f;
+		double _stepRateFloat = chunk.readTypedDouble();
 		// This should always be an integer anyway,
 		// so we'll cast away any fractional part.
 		_stepRate = static_cast<uint32>(_stepRateFloat);
@@ -299,62 +275,62 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	case kAssetHeaderDuration: {
 		// These are stored in the file as fractional seconds,
 		// but we want milliseconds.
-		_duration = (uint32)(Datum(chunk).u.f * 1000);
+		_duration = (uint32)(chunk.readTypedTime() * 1000);
 		break;
 	}
 
 	case kAssetHeaderX: {
-		_x = Datum(chunk).u.i;
+		_x = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderY: {
-		_y = Datum(chunk).u.i;
+		_y = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderEditable: {
-		_editable = Datum(chunk).u.i;
+		_editable = chunk.readTypedByte();
 		break;
 	}
 
 	case kAssetHeaderFontId: {
-		_fontAssetId = Datum(chunk).u.i;
+		_fontAssetId = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderTextMaxLength: {
-		_maxTextLength = Datum(chunk).u.i;
+		_maxTextLength = chunk.readTypedUint16();
 		break;
 	}
 
 	case kAssetHeaderInitialText: {
-		_text = Datum(chunk).u.string;
+		_text = chunk.readTypedString();
 		break;
 	}
 
 	case kAssetHeaderTextJustification: {
-		_justification = static_cast<TextJustification>(Datum(chunk).u.i);
+		_justification = static_cast<TextJustification>(chunk.readTypedUint16());
 		break;
 	}
 
 	case kAssetHeaderTextPosition: {
-		_position = static_cast<TextPosition>(Datum(chunk).u.i);
+		_position = static_cast<TextPosition>(chunk.readTypedUint16());
 		break;
 	}
 
 	case kAssetHeaderTextCharacterClass: {
 		CharacterClass characterClass;
-		characterClass.firstAsciiCode = Datum(chunk).u.i;
-		characterClass.lastAsciiCode = Datum(chunk).u.i;
+		characterClass.firstAsciiCode = chunk.readTypedUint16();
+		characterClass.lastAsciiCode = chunk.readTypedUint16();
 		_acceptedInput.push_back(characterClass);
 		break;
 	}
 
 	case kAssetHeaderSpriteFrameMapping: {
-		uint32 externalFrameId = Datum(chunk).u.i;
-		uint32 internalFrameId = Datum(chunk).u.i;
-		uint32 unk1 = Datum(chunk).u.i;
+		uint32 externalFrameId = chunk.readTypedUint16();
+		uint32 internalFrameId = chunk.readTypedUint16();
+		uint32 unk1 = chunk.readTypedUint16();
 		if (unk1 != internalFrameId) {
 			warning("AssetHeader::readSection(): Repeated internalFrameId doesn't match");
 		}
@@ -363,7 +339,7 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 
     case kAssetHeaderPathTotalSteps: {
-		_totalSteps = Datum(chunk).u.i;
+		_totalSteps = chunk.readTypedUint16();
 		break;
 	}
 
@@ -372,10 +348,4 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 	}
 }
 
-AssetHeaderSectionType AssetHeader::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	AssetHeaderSectionType sectionType = static_cast<AssetHeaderSectionType>(datum.u.i);
-	return sectionType;
-}
-
 } // end of namespace MediaStation
diff --git a/engines/mediastation/assetheader.h b/engines/mediastation/assetheader.h
index b8f327fbd55..958f4d8d27f 100644
--- a/engines/mediastation/assetheader.h
+++ b/engines/mediastation/assetheader.h
@@ -172,8 +172,8 @@ public:
 	// These two are only used in movies.
 	ChunkReference _audioChunkReference = 0;
 	ChunkReference _animationChunkReference = 0;
-	Common::Rect *_boundingBox = nullptr;
-	Common::Array<Common::Point *> _mouseActiveArea;
+	Common::Rect _boundingBox;
+	Common::Array<Common::Point> _mouseActiveArea;
 	int _zIndex = 0;
 	uint32 _assetReference = 0;
 	uint32 _startup = 0;
@@ -188,7 +188,7 @@ public:
 	bool _getOffstageEvents = 0;
 	uint32 _x = 0; // Image only.
 	uint32 _y = 0; // Image only.
-	Common::String *_name = nullptr;
+	Common::String _name;
 	uint32 _stageId = 0;
 	SoundEncoding _soundEncoding;
 	uint32 _chunkCount = 0;
@@ -196,8 +196,8 @@ public:
 
 	// PATH FIELDS.
 	uint32 _dissolveFactor = 0;
-	Common::Point *_startPoint = nullptr;
-	Common::Point *_endPoint = nullptr;
+	Common::Point _startPoint;
+	Common::Point _endPoint;
 	uint32 _stepRate = 0;
 	uint32 _duration = 0;
 	uint _totalSteps = 0;
@@ -210,7 +210,7 @@ public:
 	Common::Array<EventHandler *> _loadCompleteHandlers;
 
 	// TEXT FIELDS.
-	Common::String *_text = nullptr;
+	Common::String _text;
 	uint _maxTextLength = 0;
 	uint _fontAssetId = 0;
 	TextJustification _justification;
@@ -219,7 +219,6 @@ public:
 
 private:
 	void readSection(AssetHeaderSectionType sectionType, Chunk &chunk);
-	AssetHeaderSectionType getSectionType(Chunk &chunk);
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/assets/font.cpp b/engines/mediastation/assets/font.cpp
index a9f71e1c0be..a185a375f11 100644
--- a/engines/mediastation/assets/font.cpp
+++ b/engines/mediastation/assets/font.cpp
@@ -43,9 +43,9 @@ ScriptValue Font::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
 
 void Font::readChunk(Chunk &chunk) {
 	debugC(5, kDebugLoading, "Font::readChunk(): Reading font glyph (@0x%llx)", static_cast<long long int>(chunk.pos()));
-	uint asciiCode = Datum(chunk).u.i;
-	int unk1 = Datum(chunk).u.i;
-	int unk2 = Datum(chunk).u.i;
+	uint asciiCode = chunk.readTypedUint16();
+	int unk1 = chunk.readTypedUint16();
+	int unk2 = chunk.readTypedUint16();
 	BitmapHeader *header = new BitmapHeader(chunk);
 	FontGlyph *glyph = new FontGlyph(chunk, asciiCode, unk1, unk2, header);
 	if (_glyphs.getValOrDefault(asciiCode) != nullptr) {
diff --git a/engines/mediastation/assets/hotspot.cpp b/engines/mediastation/assets/hotspot.cpp
index da02b85261f..7f7a50dbfa7 100644
--- a/engines/mediastation/assets/hotspot.cpp
+++ b/engines/mediastation/assets/hotspot.cpp
@@ -32,7 +32,7 @@ Hotspot::Hotspot(AssetHeader *header) : Asset(header) {
 
 bool Hotspot::isInside(const Common::Point &pointToCheck) {
 	// No sense checking the polygon if we're not even in the bbox.
-	if (!_header->_boundingBox->contains(pointToCheck)) {
+	if (!_header->_boundingBox.contains(pointToCheck)) {
 		return false;
 	}
 
@@ -43,14 +43,14 @@ bool Hotspot::isInside(const Common::Point &pointToCheck) {
 
 	// Polygon intersection code adapted from HADESCH engine, might need more
 	// refinement once more testing is possible.
-	Common::Point point = pointToCheck - Common::Point(_header->_boundingBox->left, _header->_boundingBox->top);
+	Common::Point point = pointToCheck - Common::Point(_header->_boundingBox.left, _header->_boundingBox.top);
 	int rcross = 0; // Number of right-side overlaps
 
 	// Each edge is checked whether it cuts the outgoing stream from the point
-	Common::Array<Common::Point *> _polygon = _header->_mouseActiveArea;
+	Common::Array<Common::Point> _polygon = _header->_mouseActiveArea;
 	for (unsigned i = 0; i < _polygon.size(); i++) {
-		const Common::Point &edgeStart = *_polygon[i];
-		const Common::Point &edgeEnd = *_polygon[(i + 1) % _polygon.size()];
+		const Common::Point &edgeStart = _polygon[i];
+		const Common::Point &edgeEnd = _polygon[(i + 1) % _polygon.size()];
 
 		// A vertex is a point? Then it lies on one edge of the polygon
 		if (point == edgeStart)
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index 942ef1b2686..54771709580 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -118,7 +118,7 @@ void Image::spatialHide() {
 }
 
 Common::Point Image::getLeftTop() {
-	return Common::Point(_header->_x + _header->_boundingBox->left + _xAdjust, _header->_y + _header->_boundingBox->top + _yAdjust);
+	return Common::Point(_header->_x + _header->_boundingBox.left + _xAdjust, _header->_y + _header->_boundingBox.top + _yAdjust);
 }
 
 void Image::readChunk(Chunk &chunk) {
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index e0f50bac700..65c2daf8f4a 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -24,46 +24,45 @@
 
 #include "mediastation/mediastation.h"
 #include "mediastation/assets/movie.h"
-#include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
 MovieFrameHeader::MovieFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
-	_index = Datum(chunk).u.i;
+	_index = chunk.readTypedUint32();
 	debugC(5, kDebugLoading, "MovieFrameHeader::MovieFrameHeader(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
-	_keyframeEndInMilliseconds = Datum(chunk).u.i;
+	_keyframeEndInMilliseconds = chunk.readTypedUint32();
 }
 
 MovieFrameFooter::MovieFrameFooter(Chunk &chunk) {
-	_unk1 = Datum(chunk).u.i;
-	_unk2 = Datum(chunk).u.i;
-	if (g_engine->isFirstGenerationEngine()) { // TODO: Add the version number check.
-		_startInMilliseconds = Datum(chunk).u.i;
-		_endInMilliseconds = Datum(chunk).u.i;
-		_left = Datum(chunk).u.i;
-		_top = Datum(chunk).u.i;
-		_unk3 = Datum(chunk).u.i;
-		_unk4 = Datum(chunk).u.i;
-		_index = Datum(chunk).u.i;
+	_unk1 = chunk.readTypedUint16();
+	_unk2 = chunk.readTypedUint32();
+	if (g_engine->isFirstGenerationEngine()) {
+		_startInMilliseconds = chunk.readTypedUint32();
+		_endInMilliseconds = chunk.readTypedUint32();
+		_left = chunk.readTypedUint16();
+		_top = chunk.readTypedUint16();
+		_unk3 = chunk.readTypedUint16();
+		_unk4 = chunk.readTypedUint16();
+		_index = chunk.readTypedUint16();
 	} else {
-		_unk4 = Datum(chunk).u.i;
-		_startInMilliseconds = Datum(chunk).u.i;
-		_endInMilliseconds = Datum(chunk).u.i;
-		_left = Datum(chunk).u.i;
-		_top = Datum(chunk).u.i;
-		_zIndex = Datum(chunk).u.i;
+		_unk4 = chunk.readTypedUint16();
+		_startInMilliseconds = chunk.readTypedUint32();
+		_endInMilliseconds = chunk.readTypedUint32();
+		_left = chunk.readTypedUint16();
+		_top = chunk.readTypedUint16();
+		_zIndex = chunk.readTypedSint16();
 		// This represents the difference between the left coordinate of the
 		// keyframe (if applicable) and the left coordinate of this frame. Zero
 		// if there is no keyframe.
-		_diffBetweenKeyframeAndFrameX = Datum(chunk).u.i;
+		_diffBetweenKeyframeAndFrameX = chunk.readTypedSint16();
 		// This represents the difference between the top coordinate of the
 		// keyframe (if applicable) and the top coordinate of this frame. Zero
 		// if there is no keyframe.
-		_diffBetweenKeyframeAndFrameY = Datum(chunk).u.i;
-		_index = Datum(chunk).u.i;
-		_keyframeIndex = Datum(chunk).u.i;
-		_unk9 = Datum(chunk).u.i;
+		_diffBetweenKeyframeAndFrameY = chunk.readTypedSint16();
+		_index = chunk.readTypedUint32();
+		_keyframeIndex = chunk.readTypedUint32();
+		_unk9 = chunk.readTypedByte();
 		debugC(5, kDebugLoading, "MovieFrameFooter::MovieFrameFooter(): _startInMilliseconds = %d, _endInMilliseconds = %d, _left = %d, _top = %d, _index = %d, _keyframeIndex = %d (@0x%llx)",
 			_startInMilliseconds, _endInMilliseconds, _left, _top, _index, _keyframeIndex, static_cast<long long int>(chunk.pos()));
 		debugC(5, kDebugLoading, "MovieFrameFooter::MovieFrameFooter(): _zIndex = %d, _diffBetweenKeyframeAndFrameX = %d, _diffBetweenKeyframeAndFrameY = %d, _unk4 = %d, _unk9 = %d",
@@ -244,7 +243,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
 
 	case kXPositionMethod: {
 		assert(args.empty());
-		double left = static_cast<double>(_header->_boundingBox->left);
+		double left = static_cast<double>(_header->_boundingBox.left);
 		returnValue.setToFloat(left);
 		return returnValue;
 
@@ -252,7 +251,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
 
 	case kYPositionMethod: {
 		assert(args.empty());
-		double top = static_cast<double>(_header->_boundingBox->top);
+		double top = static_cast<double>(_header->_boundingBox.top);
 		returnValue.setToFloat(top);
 		return returnValue;
 	}
@@ -382,14 +381,14 @@ void Movie::spatialCenterMoveTo(int x, int y) {
 	}
 
 	// Calculate the center of the movie, and update location.
-	int frameWidth = _header->_boundingBox->width();
-	int frameHeight = _header->_boundingBox->height();
+	int frameWidth = _header->_boundingBox.width();
+	int frameHeight = _header->_boundingBox.height();
 	int centerX = x - frameWidth / 2;
 	int centerY = y - frameHeight / 2;
 
 	debugC(5, kDebugScript, "Movie::callMethod(): (%d) Moving movie center to (%d, %d)", _header->_id, x, y);
 	// Unlike the sprites, movie bounding boxes must be moved too.
-	_header->_boundingBox->moveTo(centerX, centerY);
+	_header->_boundingBox.moveTo(centerX, centerY);
 
 	// Mark the new location dirty.
 	for (MovieFrame *frame : _framesOnScreen) {
@@ -408,7 +407,7 @@ void Movie::spatialMoveTo(int x, int y) {
 	// Update the location and mark the new location dirty.
 	debugC(5, kDebugGraphics, "Movie::callMethod(): (%d) Moving movie to (%d, %d)", _header->_id, x, y);
 	// Unlike the sprites, movie bounding boxes must be moved too.
-	_header->_boundingBox->moveTo(x, y);
+	_header->_boundingBox.moveTo(x, y);
 
 	for (MovieFrame *frame : _framesOnScreen) {
 		Common::Rect bbox = getFrameBoundingBox(frame);
@@ -518,7 +517,7 @@ void Movie::redraw(Common::Rect &rect) {
 		Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
 		if (!areaToRedraw.isEmpty()) {
 			Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
-			areaToRedraw.translate(-frame->left() - _header->_boundingBox->left, -frame->top() - _header->_boundingBox->top);
+			areaToRedraw.translate(-frame->left() - _header->_boundingBox.left, -frame->top() - _header->_boundingBox.top);
 			areaToRedraw.clip(Common::Rect(0, 0, frame->width(), frame->height()));
 			g_engine->_screen->simpleBlitFrom(frame->_surface, areaToRedraw, originOnScreen);
 		}
@@ -527,13 +526,13 @@ void Movie::redraw(Common::Rect &rect) {
 
 Common::Rect Movie::getFrameBoundingBox(MovieFrame *frame) {
 	Common::Rect bbox = frame->boundingBox();
-	bbox.translate(_header->_boundingBox->left, _header->_boundingBox->top);
+	bbox.translate(_header->_boundingBox.left, _header->_boundingBox.top);
 	return bbox;
 }
 
 void Movie::readChunk(Chunk &chunk) {
 	// Individual chunks are "stills" and are stored in the first subfile.
-	uint sectionType = Datum(chunk).u.i;
+	uint sectionType = chunk.readTypedUint16();
 	switch ((MovieSectionType)sectionType) {
 	case kMovieFrameSection: {
 		debugC(5, kDebugLoading, "Movie::readStill(): Reading frame");
@@ -557,20 +556,18 @@ void Movie::readChunk(Chunk &chunk) {
 
 void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
 	// READ THE METADATA FOR THE WHOLE MOVIE.
-	uint expectedRootSectionType = Datum(chunk).u.i;
+	uint expectedRootSectionType = chunk.readTypedUint16();
 	debugC(5, kDebugLoading, "Movie::readSubfile(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(expectedRootSectionType), static_cast<long long int>(chunk.pos()));
 	if (kMovieRootSection != (MovieSectionType)expectedRootSectionType) {
 		error("Expected ROOT section type, got 0x%x", expectedRootSectionType);
 	}
-	uint chunkCount = Datum(chunk).u.i;
-	debugC(5, kDebugLoading, "Movie::readSubfile(): chunkCount = 0x%x (@0x%llx)", chunkCount, static_cast<long long int>(chunk.pos()));
-
-	uint dataStartOffset = Datum(chunk).u.i;
-	debugC(5, kDebugLoading, "Movie::readSubfile(): dataStartOffset = 0x%x (@0x%llx)", dataStartOffset, static_cast<long long int>(chunk.pos()));
+	uint chunkCount = chunk.readTypedUint16();
+	double unk1 = chunk.readTypedDouble();
+	debugC(5, kDebugLoading, "Movie::readSubfile(): chunkCount = 0x%x, unk1 = %f (@0x%llx)", chunkCount, unk1, static_cast<long long int>(chunk.pos()));
 
 	Common::Array<uint> chunkLengths;
 	for (uint i = 0; i < chunkCount; i++) {
-		uint chunkLength = Datum(chunk).u.i;
+		uint chunkLength = chunk.readTypedUint32();
 		debugC(5, kDebugLoading, "Movie::readSubfile(): chunkLength = 0x%x (@0x%llx)", chunkLength, static_cast<long long int>(chunk.pos()));
 		chunkLengths.push_back(chunkLength);
 	}
@@ -587,7 +584,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
 			warning("Movie::readSubfile(): (Frameset %d of %d) No animation chunks found (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
 		}
 		while (isAnimationChunk) {
-			uint sectionType = Datum(chunk).u.i;
+			uint sectionType = chunk.readTypedUint16();
 			debugC(5, kDebugLoading, "Movie::readSubfile(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 			switch (MovieSectionType(sectionType)) {
 			case kMovieFrameSection: {
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 9911e83c231..9d6cd5530c7 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -19,7 +19,6 @@
  *
  */
 
-#include "mediastation/datum.h"
 #include "mediastation/assets/sprite.h"
 #include "mediastation/debugchannels.h"
 #include "mediastation/mediastation.h"
@@ -27,17 +26,12 @@
 namespace MediaStation {
 
 SpriteFrameHeader::SpriteFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
-	_index = Datum(chunk).u.i;
+	_index = chunk.readTypedUint16();
 	debugC(5, kDebugLoading, "SpriteFrameHeader::SpriteFrameHeader(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
-	_boundingBox = Datum(chunk, kDatumTypePoint).u.point;
+	_boundingBox = chunk.readTypedPoint();
 	debugC(5, kDebugLoading, "SpriteFrameHeader::SpriteFrameHeader(): _boundingBox (@0x%llx)", static_cast<long long int>(chunk.pos()));
 }
 
-SpriteFrameHeader::~SpriteFrameHeader() {
-	delete _boundingBox;
-	_boundingBox = nullptr;
-}
-
 SpriteFrame::SpriteFrame(Chunk &chunk, SpriteFrameHeader *header) : Bitmap(chunk, header) {
 	_bitmapHeader = header;
 }
@@ -47,11 +41,11 @@ SpriteFrame::~SpriteFrame() {
 }
 
 uint32 SpriteFrame::left() {
-	return _bitmapHeader->_boundingBox->x;
+	return _bitmapHeader->_boundingBox.x;
 }
 
 uint32 SpriteFrame::top() {
-	return _bitmapHeader->_boundingBox->y;
+	return _bitmapHeader->_boundingBox.y;
 }
 
 Common::Point SpriteFrame::topLeft() {
@@ -325,7 +319,7 @@ void Sprite::redraw(Common::Rect &rect) {
 	Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
 	if (!areaToRedraw.isEmpty()) {
 		Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
-		areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox->left - _xAdjust, -_activeFrame->top() - _header->_boundingBox->top - _yAdjust);
+		areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox.left - _xAdjust, -_activeFrame->top() - _header->_boundingBox.top - _yAdjust);
 		areaToRedraw.clip(Common::Rect(0, 0, _activeFrame->width(), _activeFrame->height()));
 		g_engine->_screen->simpleBlitFrom(_activeFrame->_surface, areaToRedraw, originOnScreen);
 	}
@@ -348,7 +342,7 @@ Common::Rect Sprite::getActiveFrameBoundingBox() {
 	// The frame dimensions are relative to those of the sprite movie.
 	// So we must get the absolute coordinates.
 	Common::Rect bbox = _activeFrame->boundingBox();
-	bbox.translate(_header->_boundingBox->left + _xAdjust, _header->_boundingBox->top + _yAdjust);
+	bbox.translate(_header->_boundingBox.left + _xAdjust, _header->_boundingBox.top + _yAdjust);
 	return bbox;
 }
 
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index 2715ac12140..d90b8506349 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -37,10 +37,9 @@ namespace MediaStation {
 class SpriteFrameHeader : public BitmapHeader {
 public:
 	SpriteFrameHeader(Chunk &chunk);
-	virtual ~SpriteFrameHeader() override;
 
 	uint _index;
-	Common::Point *_boundingBox;
+	Common::Point _boundingBox;
 };
 
 class SpriteFrame : public Bitmap {
diff --git a/engines/mediastation/assets/text.cpp b/engines/mediastation/assets/text.cpp
index 16df7642ba3..2b7ef861b01 100644
--- a/engines/mediastation/assets/text.cpp
+++ b/engines/mediastation/assets/text.cpp
@@ -56,11 +56,11 @@ ScriptValue Text::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
 	}
 }
 
-Common::String *Text::text() const {
+Common::String Text::text() const {
 	return _header->_text;
 }
 
-void Text::setText(Common::String *text) {
+void Text::setText(Common::String text) {
 	error("Text::setText(): Setting text not implemented yet");
 }
 
diff --git a/engines/mediastation/assets/text.h b/engines/mediastation/assets/text.h
index 49e4065483c..5ba2f9be7a8 100644
--- a/engines/mediastation/assets/text.h
+++ b/engines/mediastation/assets/text.h
@@ -39,8 +39,8 @@ public:
 
 private:
 	// Method implementations.
-	Common::String *text() const;
-	void setText(Common::String *text);
+	Common::String text() const;
+	void setText(Common::String text);
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index 37153b32821..3d0f1f889ee 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -19,28 +19,22 @@
  *
  */
 
-#include "mediastation/datum.h"
 #include "mediastation/bitmap.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
 BitmapHeader::BitmapHeader(Chunk &chunk) {
-	uint headerSizeInBytes = Datum(chunk, kDatumTypeUint16).u.i;
+	uint headerSizeInBytes = chunk.readTypedUint16();
 	debugC(5, kDebugLoading, "BitmapHeader::BitmapHeader(): headerSize = 0x%x", headerSizeInBytes);
-	_dimensions = Datum(chunk).u.point;
-	_compressionType = static_cast<BitmapCompressionType>(Datum(chunk, kDatumTypeUint16).u.i);
+	_dimensions = chunk.readTypedGraphicSize();
+	_compressionType = static_cast<BitmapCompressionType>(chunk.readTypedUint16());
 	debugC(5, kDebugLoading, "BitmapHeader::BitmapHeader(): _compressionType = 0x%x", static_cast<uint>(_compressionType));
 	// TODO: Figure out what this is.
 	// This has something to do with the width of the bitmap but is always
 	// a few pixels off from the width. And in rare cases it seems to be
 	// the true width!
-	unk2 = Datum(chunk, kDatumTypeUint16).u.i;
-}
-
-BitmapHeader::~BitmapHeader() {
-	delete _dimensions;
-	_dimensions = nullptr;
+	unk2 = chunk.readTypedUint16();
 }
 
 bool BitmapHeader::isCompressed() {
@@ -50,8 +44,8 @@ bool BitmapHeader::isCompressed() {
 Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) :
 	_bitmapHeader(bitmapHeader) {
 	// The header must be constructed beforehand.
-	uint16 width = _bitmapHeader->_dimensions->x;
-	uint16 height = _bitmapHeader->_dimensions->y;
+	uint16 width = _bitmapHeader->_dimensions.x;
+	uint16 height = _bitmapHeader->_dimensions.y;
 	_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
 	_surface.setTransparentColor(0);
 	uint8 *pixels = (uint8 *)_surface.getPixels();
@@ -77,11 +71,11 @@ Bitmap::~Bitmap() {
 }
 
 uint16 Bitmap::width() {
-	return _bitmapHeader->_dimensions->x;
+	return _bitmapHeader->_dimensions.x;
 }
 
 uint16 Bitmap::height() {
-	return _bitmapHeader->_dimensions->y;
+	return _bitmapHeader->_dimensions.y;
 }
 
 void Bitmap::decompress(Chunk &chunk) {
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index 0691ba8a539..a1cb1add861 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -40,11 +40,10 @@ enum BitmapCompressionType {
 class BitmapHeader {
 public:
 	BitmapHeader(Chunk &chunk);
-	virtual ~BitmapHeader();
 
 	bool isCompressed();
 
-	Common::Point *_dimensions = nullptr;
+	Common::Point _dimensions;
 	BitmapCompressionType _compressionType;
 	uint unk2;
 };
diff --git a/engines/mediastation/boot.cpp b/engines/mediastation/boot.cpp
index 7100cbbe7a8..55acbfe80f0 100644
--- a/engines/mediastation/boot.cpp
+++ b/engines/mediastation/boot.cpp
@@ -20,26 +20,10 @@
  */
 
 #include "mediastation/boot.h"
-#include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
-#pragma region VersionInfo
-VersionInfo::VersionInfo(Chunk &chunk) {
-	_majorVersion = Datum(chunk, kDatumTypeUint16).u.i;
-	_minorVersion = Datum(chunk, kDatumTypeUint16).u.i;
-	_revision = Datum(chunk, kDatumTypeUint16).u.i;
-	string = Datum(chunk, kDatumTypeString).u.string;
-}
-
-VersionInfo::~VersionInfo() {
-	if (string != nullptr) {
-		delete string;
-	}
-}
-#pragma endregion
-
 #pragma region ContextDeclaration
 ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 	// Make sure this declaration isn't empty.
@@ -56,7 +40,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 		// Read the file number.
 		sectionType = getSectionType(chunk);
 		if (kContextDeclarationFileNumber1 == sectionType) {
-			_fileNumber = Datum(chunk).u.i;
+			_fileNumber = chunk.readTypedUint16();
 		} else {
 			error("ContextDeclaration(): Expected section type FILE_NUMBER_1, got 0x%x", static_cast<uint>(sectionType));
 		}
@@ -64,7 +48,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 		// Is it just for data integrity, or is there some other reason?
 		sectionType = getSectionType(chunk);
 		if (kContextDeclarationFileNumber2 == sectionType) {
-			uint32 repeatedFileNumber = Datum(chunk).u.i;
+			uint32 repeatedFileNumber = chunk.readTypedUint16();
 			if (repeatedFileNumber != _fileNumber) {
 				warning("ContextDeclaration(): Expected file numbers to match, but 0x%d != 0x%d", _fileNumber, repeatedFileNumber);
 			}
@@ -81,7 +65,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 		int rewindOffset = chunk.pos();
 		sectionType = getSectionType(chunk);
 		if (kContextDeclarationName == sectionType) {
-			_contextName = Datum(chunk, kDatumTypeString).u.string;
+			_contextName = chunk.readTypedString();
 		} else {
 			// There is no context name.
 			// We have instead read into the next declaration, so let's undo that.
@@ -99,7 +83,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 	int rewindOffset = 0;
 	sectionType = getSectionType(chunk);
 	while (kContextDeclarationFileReference == sectionType) {
-		int fileReference = Datum(chunk).u.i;
+		int fileReference = chunk.readTypedUint16();
 		_fileReferences.push_back(fileReference);
 		rewindOffset = chunk.pos();
 		sectionType = getSectionType(chunk);
@@ -108,14 +92,7 @@ ContextDeclaration::ContextDeclaration(Chunk &chunk) {
 }
 
 ContextDeclarationSectionType ContextDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	ContextDeclarationSectionType sectionType = static_cast<ContextDeclarationSectionType>(datum.u.i);
-	return sectionType;
-}
-
-ContextDeclaration::~ContextDeclaration() {
-	delete _contextName;
-	_contextName = nullptr;
+	return static_cast<ContextDeclarationSectionType>(chunk.readTypedUint16());
 }
 #pragma endregion
 
@@ -133,13 +110,13 @@ UnknownDeclaration::UnknownDeclaration(Chunk &chunk) {
 
 	sectionType = getSectionType(chunk);
 	if (kUnknownDeclarationUnk1 == sectionType) {
-		_unk = Datum(chunk, kDatumTypeUint16).u.i;
+		_unk = chunk.readTypedUint16();
 	} else {
 		error("UnknownDeclaration(): Expected section type UNK_1, got 0x%x", static_cast<uint>(sectionType));
 	}
 	sectionType = getSectionType(chunk);
 	if (kUnknownDeclarationUnk2 == sectionType) {
-		uint16 repeatedUnk = Datum(chunk, kDatumTypeUint16).u.i;
+		uint16 repeatedUnk = chunk.readTypedUint16();
 		if (repeatedUnk != _unk) {
 			warning("UnknownDeclaration(): Expected unknown values to match, but 0x%x != 0x%x", _unk, repeatedUnk);
 		}
@@ -149,9 +126,7 @@ UnknownDeclaration::UnknownDeclaration(Chunk &chunk) {
 }
 
 UnknownDeclarationSectionType UnknownDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	UnknownDeclarationSectionType sectionType = static_cast<UnknownDeclarationSectionType>(datum.u.i);
-	return sectionType;
+	return static_cast<UnknownDeclarationSectionType>(chunk.readTypedUint16());
 }
 #pragma endregion
 
@@ -170,7 +145,7 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 	// Read the file ID.
 	sectionType = getSectionType(chunk);
 	if (kFileDeclarationFileId == sectionType) {
-		_id = Datum(chunk, kDatumTypeUint16).u.i;
+		_id = chunk.readTypedUint16();
 	} else {
 		error("FileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -178,9 +153,7 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 	// Read the intended file location.
 	sectionType = getSectionType(chunk);
 	if (kFileDeclarationFileNameAndType == sectionType) {
-		Datum datum = Datum(chunk, kDatumTypeUint16);
-		// TODO: Verify we actually read a valid enum member.
-		_intendedLocation = static_cast<IntendedFileLocation>(datum.u.i);
+		_intendedLocation = static_cast<IntendedFileLocation>(chunk.readTypedUint16());
 	} else {
 		error("FileDeclaration(): Expected section type FILE_NAME_AND_TYPE, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -188,18 +161,11 @@ FileDeclaration::FileDeclaration(Chunk &chunk) {
 	// Since the platforms that Media Station originally targeted were case-insensitive,
 	// the case of these filenames might not match the case of the files actually in
 	// the directory. All files should be matched case-insensitively.
-	_name = Datum(chunk, kDatumTypeFilename).u.string;
+	_name = chunk.readTypedFilename();
 }
 
 FileDeclarationSectionType FileDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	FileDeclarationSectionType sectionType = static_cast<FileDeclarationSectionType>(datum.u.i);
-	return sectionType;
-}
-
-FileDeclaration::~FileDeclaration() {
-	delete _name;
-	_name = nullptr;
+	return static_cast<FileDeclarationSectionType>(chunk.readTypedUint16());
 }
 #pragma endregion
 
@@ -218,7 +184,7 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the asset ID.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationAssetId == sectionType) {
-		_assetId = Datum(chunk, kDatumTypeUint16).u.i;
+		_assetId = chunk.readTypedUint16();
 	} else {
 		error("SubfileDeclaration(): Expected section type ASSET_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -226,7 +192,7 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the file ID.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationFileId == sectionType) {
-		_fileId = Datum(chunk, kDatumTypeUint16).u.i;
+		_fileId = chunk.readTypedUint16();
 	} else {
 		error("SubfileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
@@ -234,40 +200,24 @@ SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
 	// Read the start offset from the absolute start of the file.
 	sectionType = getSectionType(chunk);
 	if (kSubfileDeclarationStartOffset == sectionType) {
-		_startOffsetInFile = Datum(chunk, kDatumTypeUint32).u.i;
+		_startOffsetInFile = chunk.readTypedUint32();
 	} else {
 		error("SubfileDeclaration(): Expected section type START_OFFSET, got 0x%x", static_cast<uint>(sectionType));
 	}
 }
 
 SubfileDeclarationSectionType SubfileDeclaration::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	SubfileDeclarationSectionType sectionType = static_cast<SubfileDeclarationSectionType>(datum.u.i);
-	return sectionType;
+	return static_cast<SubfileDeclarationSectionType>(chunk.readTypedUint16());
 }
 #pragma endregion
 
 #pragma region CursorDeclaration
 CursorDeclaration::CursorDeclaration(Chunk& chunk) {
-	uint16 unk1 = Datum(chunk, kDatumTypeUint16).u.i; // Always 0x0001
-	_id = Datum(chunk, kDatumTypeUint16).u.i;
-	_unk = Datum(chunk, kDatumTypeUint16).u.i;
-	_name = Datum(chunk, kDatumTypeFilename).u.string;
-	debugC(5, kDebugLoading, " - CursorDeclaration(): unk1 = 0x%x, id = 0x%x, unk = 0x%x, name = %s", unk1, _id, _unk, _name->c_str());
-}
-
-CursorDeclaration::~CursorDeclaration() {
-	delete _name;
-	_name = nullptr;
-}
-#pragma endregion
-
-#pragma region Engine Resource Declaration
-EngineResourceDeclaration::EngineResourceDeclaration(Common::String *resourceName, int resourceId) : _resourceName(resourceName), _resourceId(resourceId) {}
-
-EngineResourceDeclaration::~EngineResourceDeclaration() {
-	delete _resourceName;
-	_resourceName = nullptr;
+	uint16 unk1 = chunk.readTypedUint16(); // Always 0x0001
+	_id = chunk.readTypedUint16();
+	_unk = chunk.readTypedUint16();
+	_name = chunk.readTypedFilename();
+	debugC(5, kDebugLoading, " - CursorDeclaration(): unk1 = 0x%x, id = 0x%x, unk = 0x%x, name = %s", unk1, _id, _unk, _name.c_str());
 }
 #pragma endregion
 
@@ -276,7 +226,7 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 	Subfile subfile = getNextSubfile();
 	Chunk chunk = subfile.nextChunk();
 
-	uint32 beforeSectionTypeUnk = Datum(chunk, kDatumTypeUint16).u.i; // Usually 0x0001
+	uint32 beforeSectionTypeUnk = chunk.readTypedUint16(); // Usually 0x0001
 	debugC(5, kDebugLoading, "Boot::Boot(): unk1 = 0x%x", beforeSectionTypeUnk);
 
 	BootSectionType sectionType = getSectionType(chunk);
@@ -285,36 +235,36 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 		debugC(5, kDebugLoading, "Boot::Boot(): sectionType = 0x%x", static_cast<uint>(sectionType));
 		switch (sectionType) {
 		case kBootVersionInformation: {
-			_gameTitle = Datum(chunk, kDatumTypeString).u.string;
-			debugC(5, kDebugLoading, " - gameTitle = %s", _gameTitle->c_str());
-			uint32 unk = chunk.readUint16LE();
-			debugC(5, kDebugLoading, " - unk = 0x%x", unk);
-			_versionInfo = new VersionInfo(chunk);
-			debugC(5, kDebugLoading, " - versionInfo = %d.%d.%d (%s)", _versionInfo->_majorVersion, _versionInfo->_minorVersion, _versionInfo->_revision, _versionInfo->string->c_str());
-			_sourceString = Datum(chunk, kDatumTypeString).u.string;
-			debugC(5, kDebugLoading, " - sourceString = %s", _sourceString->c_str());
+			_gameTitle = chunk.readTypedString();
+			debugC(5, kDebugLoading, " - gameTitle = %s", _gameTitle.c_str());
+			_versionInfo = chunk.readTypedVersion();
+			_engineInfo = chunk.readTypedString();
+			debugC(5, kDebugLoading, " - versionInfo = %d.%d.%d (%s)",
+				_versionInfo.major, _versionInfo.minor, _versionInfo.patch, _engineInfo.c_str());
+			_sourceString = chunk.readTypedString();
+			debugC(5, kDebugLoading, " - sourceString = %s", _sourceString.c_str());
 			break;
 		}
 
 		case kBootUnk1:
 		case kBootUnk2:
 		case kBootUnk3: {
-			uint32 unk = Datum(chunk).u.i;
+			uint unk = chunk.readTypedUint16();
 			debugC(5, kDebugLoading, " - unk = 0x%x", unk);
 			break;
 		}
 
 		case kBootUnk4: {
-			double unk = Datum(chunk).u.f;
+			double unk = chunk.readTypedTime();
 			debugC(5, kDebugLoading, " - unk = %f", unk);
 			break;
 		}
 
 		case kBootEngineResource: {
-			Common::String *resourceName = Datum(chunk, kDatumTypeString).u.string;
+			Common::String resourceName = chunk.readTypedString();
 			sectionType = getSectionType(chunk);
 			if (sectionType == kBootEngineResourceId) {
-				int resourceId = Datum(chunk).u.i;
+				int resourceId = chunk.readTypedUint16();
 				EngineResourceDeclaration *resourceDeclaration = new EngineResourceDeclaration(resourceName, resourceId);
 				_engineResourceDeclarations.setVal(resourceId, resourceDeclaration);
 			} else {
@@ -385,26 +335,26 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 		}
 
 		case kBootEntryScreen: {
-			_entryContextId = Datum(chunk).u.i;
+			_entryContextId = chunk.readTypedUint16();
 			debugC(5, kDebugLoading, " - _entryContextId = %d", _entryContextId);
 			break;
 		}
 
 		case kBootAllowMultipleSounds: {
-			_allowMultipleSounds = (Datum(chunk).u.i == 1);
+			_allowMultipleSounds = (chunk.readTypedByte() == 1);
 			debugC(5, kDebugLoading, " - _allowMultipleSounds = %d", _allowMultipleSounds);
 			break;
 		}
 
 		case kBootAllowMultipleStreams: {
-			_allowMultipleStreams = (Datum(chunk).u.i == 1);
+			_allowMultipleStreams = (chunk.readTypedByte() == 1);
 			debugC(5, kDebugLoading, " - _allowMultipleStreams = %d", _allowMultipleStreams);
 			break;
 		}
 
 		case kBootUnk5: {
-			uint32 unk1 = Datum(chunk).u.i;
-			uint32 unk2 = Datum(chunk).u.i;
+			uint32 unk1 = chunk.readTypedUint16();
+			uint32 unk2 = chunk.readTypedUint16();
 			debugC(5, kDebugLoading, " - unk1 = 0x%x, unk2 = 0x%x", unk1, unk2);
 			break;
 		}
@@ -419,21 +369,10 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 }
 
 BootSectionType Boot::getSectionType(Chunk &chunk) {
-	Datum datum = Datum(chunk, kDatumTypeUint16);
-	BootSectionType sectionType = static_cast<BootSectionType>(datum.u.i);
-	return sectionType;
+	return static_cast<BootSectionType>(chunk.readTypedUint16());
 }
 
 Boot::~Boot() {
-	delete _gameTitle;
-	_gameTitle = nullptr;
-
-	delete _versionInfo;
-	_versionInfo = nullptr;
-
-	delete _sourceString;
-	_sourceString = nullptr;
-
 	for (auto it = _contextDeclarations.begin(); it != _contextDeclarations.end(); ++it) {
 		delete it->_value;
 	}
diff --git a/engines/mediastation/boot.h b/engines/mediastation/boot.h
index bd9ff4f23b8..47a79d7c5a6 100644
--- a/engines/mediastation/boot.h
+++ b/engines/mediastation/boot.h
@@ -31,27 +31,6 @@
 
 namespace MediaStation {
 
-// Contains information about the engine (also called
-//  "title compiler") used in this particular game.
-// Engine version information is not present in early games.
-class VersionInfo {
-public:
-	VersionInfo(Chunk &chunk);
-	~VersionInfo();
-
-	// The version number of this engine,
-	// in the form 4.0r8 (major . minor r revision).
-	uint32 _majorVersion = 0;
-	uint32 _minorVersion = 0;
-	uint32 _revision = 0;
-
-	// A textual description of this engine.
-	// Example: "Title Compiler T4.0r8 built Feb 13 1998 10:16:52"
-	//           ^^^^^^^^^^^^^^  ^^^^^
-	//           | Engine name   | Version number
-	Common::String *string = nullptr;
-};
-
 enum ContextDeclarationSectionType {
 	kContextDeclarationEmptySection = 0x0000,
 	kContextDeclarationPlaceholder = 0x0003,
@@ -64,11 +43,10 @@ enum ContextDeclarationSectionType {
 class ContextDeclaration {
 public:
 	ContextDeclaration(Chunk &chunk);
-	~ContextDeclaration();
 
 	Common::Array<uint32> _fileReferences;
 	uint32 _fileNumber = 0;
-	Common::String *_contextName = nullptr;
+	Common::String _contextName;
 	// Signal that there are no more declarations to read.
 	bool _isLast = false;
 
@@ -115,11 +93,10 @@ enum IntendedFileLocation {
 class FileDeclaration {
 public:
 	FileDeclaration(Chunk &chunk);
-	~FileDeclaration();
 
 	uint32 _id = 0;
 	IntendedFileLocation _intendedLocation;
-	Common::String *_name = nullptr;
+	Common::String _name;
 	// Signal that there are no more declarations to read.
 	bool _isLast = false;
 
@@ -152,20 +129,18 @@ private:
 class CursorDeclaration {
 public:
 	CursorDeclaration(Chunk &chunk);
-	~CursorDeclaration();
 
 	uint16 _id = 0;
 	uint16 _unk = 0;
-	Common::String *_name = nullptr;
+	Common::String _name;
 };
 
 class EngineResourceDeclaration {
 public:
-	Common::String *_resourceName = nullptr;
+	Common::String _resourceName;
 	int _resourceId = 0;
 
-	EngineResourceDeclaration(Common::String *resourceName, int resourceId);
-	~EngineResourceDeclaration();
+	EngineResourceDeclaration(Common::String resourceName, int resourceId) : _resourceName(resourceName), _resourceId(resourceId) {};
 };
 
 enum BootSectionType {
@@ -194,9 +169,10 @@ private:
 	BootSectionType getSectionType(Chunk &chunk);
 
 public:
-	Common::String *_gameTitle = nullptr;
-	VersionInfo *_versionInfo = nullptr;
-	Common::String *_sourceString = nullptr;
+	Common::String _gameTitle;
+	VersionInfo _versionInfo;
+	Common::String _engineInfo;
+	Common::String _sourceString;
 	Common::HashMap<uint32, ContextDeclaration *> _contextDeclarations;
 	Common::Array<UnknownDeclaration *> _unknownDeclarations;
 	Common::HashMap<uint32, FileDeclaration *> _fileDeclarations;
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index dfe0af50948..b21843b1202 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -21,7 +21,6 @@
 
 #include "mediastation/mediastation.h"
 #include "mediastation/context.h"
-#include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
 #include "mediastation/bitmap.h"
@@ -114,9 +113,6 @@ Context::~Context() {
 	delete _palette;
 	_palette = nullptr;
 
-	delete _contextName;
-	_contextName = nullptr;
-
 	for (auto it = _assets.begin(); it != _assets.end(); ++it) {
 		delete it->_value;
 	}
@@ -160,20 +156,20 @@ void Context::registerActiveAssets() {
 }
 
 void Context::readParametersSection(Chunk &chunk) {
-	_fileNumber = Datum(chunk, kDatumTypeUint16).u.i;
+	_fileNumber = chunk.readTypedUint16();
 
-	ContextParametersSectionType sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16).u.i);
+	ContextParametersSectionType sectionType = static_cast<ContextParametersSectionType>(chunk.readTypedUint16());
 	while (sectionType != kContextParametersEmptySection) {
 		debugC(5, kDebugLoading, "ContextParameters::ContextParameters: sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 		switch (sectionType) {
 		case kContextParametersName: {
-			uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16).u.i;
+			uint repeatedFileNumber = chunk.readTypedUint16();
 			if (repeatedFileNumber != _fileNumber) {
 				warning("ContextParameters::ContextParameters(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
 			}
-			_contextName = Datum(chunk, kDatumTypeString).u.string;
+			_contextName = chunk.readTypedString();
 
-			uint endingFlag = Datum(chunk, kDatumTypeUint16).u.i;
+			uint endingFlag = chunk.readTypedUint16();
 			if (endingFlag != 0) {
 				warning("ContextParameters::ContextParameters(): Got non-zero ending flag 0x%x", endingFlag);
 			}
@@ -200,17 +196,17 @@ void Context::readParametersSection(Chunk &chunk) {
 			error("ContextParameters::ContextParameters(): Unknown section type 0x%x", static_cast<uint>(sectionType));
 		}
 
-		sectionType = static_cast<ContextParametersSectionType>(Datum(chunk, kDatumTypeUint16).u.i);
+		sectionType = static_cast<ContextParametersSectionType>(chunk.readTypedUint16());
 	}
 }
 
 void Context::readVariable(Chunk &chunk) {
-	uint repeatedFileNumber = Datum(chunk, kDatumTypeUint16).u.i;
+	uint repeatedFileNumber = chunk.readTypedUint16();
 	if (repeatedFileNumber != _fileNumber) {
 		warning("Context::readVariable(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
 	}
 
-	uint id = Datum(chunk).u.i;
+	uint id = chunk.readTypedUint16();
 	if (g_engine->getVariable(id) != nullptr) {
 		error("Context::readVariable(): Global variable %d already exists", id);
 	}
@@ -234,7 +230,7 @@ void Context::readNewStyleHeaderSections(Subfile &subfile, Chunk &chunk) {
 	while (moreSectionsToRead) {
 		// Verify this chunk is a header.
 		// TODO: What are the situations when it's not?
-		uint16 sectionType = Datum(chunk, kDatumTypeUint16).u.i;
+		uint16 sectionType = chunk.readTypedUint16();
 		debugC(5, kDebugLoading, "Context::readNewStyleHeaderSections(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 		bool chunkIsHeader = (sectionType == 0x000d);
 		if (!chunkIsHeader) {
@@ -291,7 +287,7 @@ void Context::readAssetFromLaterSubfile(Subfile &subfile) {
 }
 
 bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
-	uint16 sectionType = Datum(chunk, kDatumTypeUint16).u.i;
+	uint16 sectionType = chunk.readTypedUint16();
 	debugC(5, kDebugLoading, "Context::readHeaderSection(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
 	switch (sectionType) {
 	case kContextParametersSection: {
@@ -318,7 +314,7 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 		delete[] buffer;
 		debugC(5, kDebugLoading, "Context::readHeaderSection(): Read palette");
 		// This is likely just an ending flag that we expect to be zero.
-		uint endingFlag = Datum(chunk, kDatumTypeUint16).u.i;
+		uint endingFlag = chunk.readTypedUint16();
 		if (endingFlag != 0) {
 			warning("Context::readHeaderSection(): Got non-zero ending flag 0x%x", endingFlag);
 		}
@@ -398,7 +394,7 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 			_assetsByChunkReference.setVal(header->_animationChunkReference, asset);
 		}
 		// TODO: This datum only appears sometimes.
-		uint unk2 = Datum(chunk).u.i;
+		uint unk2 = chunk.readTypedUint16();
 		debugC(5, kDebugLoading, "Context::readHeaderSection(): Got unknown value at end of asset header section 0x%x", unk2);
 		break;
 	}
@@ -407,7 +403,7 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 		Function *function = new Function(chunk);
 		_functions.setVal(function->_id, function);
 		if (!g_engine->isFirstGenerationEngine()) {
-			uint endingFlag = Datum(chunk).u.i;
+			uint endingFlag = chunk.readTypedUint16();
 			if (endingFlag != 0) {
 				warning("Context::readHeaderSection(): Got non-zero ending flag 0x%x in function section", endingFlag);
 			}
@@ -416,8 +412,8 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 	}
 
 	case kContextUnkAtEndSection: {
-		int unk1 = Datum(chunk).u.i;
-		int unk2 = Datum(chunk).u.i;
+		int unk1 = chunk.readTypedUint16();
+		int unk2 = chunk.readTypedUint16();
 		debugC(5, kDebugLoading, "Context::readHeaderSection(): unk1 = %d, unk2 = %d", unk1, unk2);
 		return false;
 	}
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index 0334138b259..649baa8e831 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -77,7 +77,7 @@ private:
 	// as it appears in the filename. For instance, the context in
 	// "100.cxt" would have file number 100.
 	uint _fileNumber = 0;
-	Common::String *_contextName = nullptr;
+	Common::String _contextName;
 
 	Common::HashMap<uint, Asset *> _assets;
 	Common::HashMap<uint, Function *> _functions;
diff --git a/engines/mediastation/datafile.cpp b/engines/mediastation/datafile.cpp
index 5e739126c0b..15ca2230751 100644
--- a/engines/mediastation/datafile.cpp
+++ b/engines/mediastation/datafile.cpp
@@ -24,6 +24,121 @@
 
 namespace MediaStation {
 
+void ParameterReadStream::readAndVerifyType(DatumType type) {
+	DatumType actualType = static_cast<DatumType>(readUint16LE());
+	if (actualType != type) {
+		error("Expected datum type %d, got %d (@0x%llx)", type, actualType, static_cast<long long int>(pos()));
+	}
+}
+
+byte ParameterReadStream::readTypedByte() {
+	readAndVerifyType(kDatumTypeUint8);
+	return readByte();
+}
+
+uint16 ParameterReadStream::readTypedUint16() {
+	readAndVerifyType(kDatumTypeUint16);
+	return readUint16LE();
+}
+
+uint32 ParameterReadStream::readTypedUint32() {
+	readAndVerifyType(kDatumTypeUint32);
+	return readUint32LE();
+}
+
+int8 ParameterReadStream::readTypedSByte() {
+	readAndVerifyType(kDatumTypeInt8);
+	return readSByte();
+}
+
+int16 ParameterReadStream::readTypedSint16() {
+	readAndVerifyType(kDatumTypeInt16);
+	return readSint16LE();
+}
+
+int32 ParameterReadStream::readTypedSint32() {
+	readAndVerifyType(kDatumTypeInt32);
+	return readSint32LE();
+}
+
+float ParameterReadStream::readTypedFloat() {
+	readAndVerifyType(kDatumTypeFloat);
+	return readFloatLE();
+}
+
+double ParameterReadStream::readTypedDouble() {
+	readAndVerifyType(kDatumTypeDouble);
+	return readDoubleLE();
+}
+
+Common::String ParameterReadStream::readTypedFilename() {
+	readAndVerifyType(kDatumTypeFilename);
+	uint size = readTypedUint32();
+	return readString('\0', size);
+}
+
+Common::Rect ParameterReadStream::readTypedRect() {
+	readAndVerifyType(kDatumTypeRect);
+	Common::Point leftTop = readTypedPoint();
+	Common::Point dimensions = readTypedGraphicSize();
+	return Common::Rect(leftTop, dimensions.x, dimensions.y);
+}
+
+Common::Point ParameterReadStream::readTypedPoint() {
+	readAndVerifyType(kDatumTypePoint);
+	int16 x = readTypedGraphicUnit();
+	int16 y = readTypedGraphicUnit();
+	return Common::Point(x, y);
+}
+
+Common::Point ParameterReadStream::readTypedGraphicSize() {
+	readAndVerifyType(kDatumTypeGraphicSize);
+	int16 width = readTypedGraphicUnit();
+	int16 height = readTypedGraphicUnit();
+	return Common::Point(width, height);
+}
+
+int16 ParameterReadStream::readTypedGraphicUnit() {
+	readAndVerifyType(kDatumTypeGraphicUnit);
+	return readSint16LE();
+}
+
+double ParameterReadStream::readTypedTime() {
+	readAndVerifyType(kDatumTypeTime);
+	return readDoubleLE();
+}
+
+Common::String ParameterReadStream::readTypedString() {
+	readAndVerifyType(kDatumTypeString);
+	uint size = readTypedUint32();
+	return readString('\0', size);
+}
+
+VersionInfo ParameterReadStream::readTypedVersion() {
+	readAndVerifyType(kDatumTypeVersion);
+	VersionInfo version;
+	version.major = readTypedUint16();
+	version.minor = readTypedUint16();
+	version.patch = readTypedUint16();
+	return version;
+}
+
+uint32 ParameterReadStream::readTypedChunkReference() {
+	readAndVerifyType(kDatumTypeChunkReference);
+	// This one is always BE.
+	return readUint32BE();
+}
+
+Polygon ParameterReadStream::readTypedPolygon() {
+	Polygon polygon;
+	uint totalPoints = readTypedUint16();
+	for (uint i = 0; i < totalPoints; ++i) {
+		Common::Point point = readTypedGraphicSize();
+		polygon.push_back(point);
+	}
+	return polygon;
+}
+
 Chunk::Chunk(Common::SeekableReadStream *stream) : _parentStream(stream) {
 	_id = _parentStream->readUint32BE();
 	_length = _parentStream->readUint32LE();
diff --git a/engines/mediastation/datafile.h b/engines/mediastation/datafile.h
index 98c7f6a2424..33d3c4c272a 100644
--- a/engines/mediastation/datafile.h
+++ b/engines/mediastation/datafile.h
@@ -25,9 +25,21 @@
 #include "common/file.h"
 #include "common/stream.h"
 #include "common/path.h"
+#include "common/rect.h"
+#include "common/str.h"
 
 namespace MediaStation {
 
+// The version number of this engine,
+// in the form 4.0r8 (major . minor r revision).
+struct VersionInfo {
+	uint16 major = 0;
+	uint16 minor = 0;
+	uint16 patch = 0;
+};
+
+typedef Common::Array<Common::Point> Polygon;
+
 // A Media Station datafile consists of one or more RIFF-style "subfiles". Aside
 // from some oddness at the start of the subfile, each subfile is basically
 // standard sequence of chunks inside a LIST chunk, like you'd see in any RIFF
@@ -36,7 +48,62 @@ namespace MediaStation {
 //  - a000, where 000 is a string that represents a 3-digit hexadecimal number.
 //          Indicates a chunk that contains actor data (sounds and bitmaps).
 
-class Chunk : public Common::SeekableReadStream {
+enum DatumType {
+	kDatumTypeEmpty = 0x00,
+	kDatumTypeUint8 = 0x02,
+	kDatumTypeUint16 = 0x03,
+	kDatumTypeUint32 = 0x04,
+	kDatumTypeInt8 = 0x05,
+	kDatumTypeInt16 = 0x06,
+	kDatumTypeInt32 = 0x07,
+	kDatumTypeFloat = 0x08,
+	kDatumTypeDouble = 0x09,
+	kDatumTypeFilename = 0x0a,
+	kDatumTypeRect = 0x0d,
+	kDatumTypePoint = 0x0e,
+	kDatumTypeGraphicSize = 0x0f,
+	kDatumTypeGraphicUnit = 0x10,
+	kDatumTypeTime = 0x11,
+	kDatumTypeString = 0x12,
+	kDatumTypeVersion = 0x13,
+	kDatumTypeChunkReference = 0x1b,
+	kDatumTypePolygon = 0x1d,
+	kDatumTypePalette = 0x05aa
+};
+
+class ParameterReadStream : public Common::SeekableReadStream {
+public:
+	// Data files are internally little-endian, even on game versions targeting
+	// big-endian systems. The original engine has code for swapping byte order
+	// at runtime when needed. All of these internally assume the data files are
+	// stored little-endian on disk.
+	byte readTypedByte();
+	uint16 readTypedUint16();
+	uint32 readTypedUint32();
+	int8 readTypedSByte();
+	int16 readTypedSint16();
+	int32 readTypedSint32();
+	float readTypedFloat();
+	double readTypedDouble();
+	Common::String readTypedFilename();
+	Common::Rect readTypedRect();
+	Common::Point readTypedPoint();
+	Common::Point readTypedGraphicSize();
+	int16 readTypedGraphicUnit();
+	double readTypedTime();
+	Common::String readTypedString();
+	VersionInfo readTypedVersion();
+	uint32 readTypedChunkReference();
+	Polygon readTypedPolygon();
+	// PALETTE:
+	// u.palette = new unsigned char[0x300];
+	// chunk.read(u.palette, 0x300);
+
+private:
+	void readAndVerifyType(DatumType type);
+};
+
+class Chunk : public ParameterReadStream {
 public:
 	Chunk() = default;
 	Chunk(Common::SeekableReadStream *stream);
diff --git a/engines/mediastation/datum.cpp b/engines/mediastation/datum.cpp
deleted file mode 100644
index a2b0bf97fba..00000000000
--- a/engines/mediastation/datum.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "mediastation/datum.h"
-#include "mediastation/debugchannels.h"
-
-namespace MediaStation {
-
-Datum::Datum() {
-	t = kDatumTypeEmpty;
-	u.i = 0;
-}
-
-Datum::Datum(Common::SeekableReadStream &chunk) {
-	t = static_cast<DatumType>(chunk.readUint16LE());
-	readWithType(chunk);
-}
-
-Datum::Datum(Common::SeekableReadStream &chunk, DatumType expectedType) {
-	t = static_cast<DatumType>(chunk.readUint16LE());
-	if (t != expectedType) {
-		error("Datum::Datum(): Expected datum type 0x%x, got 0x%x (@0x%llx)", expectedType, t, static_cast<long long int>(chunk.pos()));
-	}
-	readWithType(chunk);
-}
-
-void Datum::readWithType(Common::SeekableReadStream &chunk) {
-	debugC(9, kDebugLoading, "Datum::Datum(): Type 0x%x (@0x%llx)", static_cast<uint>(t), static_cast<long long int>(chunk.pos()));
-	if (kDatumTypeUint8 == t) {
-		u.i = chunk.readByte();
-
-	} else if (kDatumTypeUint16 == t || kDatumTypeVersion == t) {
-		u.i = chunk.readUint16LE();
-
-	} else if (kDatumTypeInt16 == t || kDatumTypeGraphicUnit == t) {
-		u.i = chunk.readSint16LE();
-
-	} else if (kDatumTypeUint32 == t || kDatumTypeInt32 == t) {
-		u.i = chunk.readUint32LE();
-
-	} else if (kDatumTypeTime == t || kDatumTypeDouble == t) {
-		u.f = chunk.readDoubleLE();
-
-	} else if (kDatumTypeString == t || kDatumTypeFilename == t) {
-		// TODO: This copies the string. Can we read it directly from the chunk?
-		int size = Datum(chunk, kDatumTypeUint32).u.i;
-		char *buffer = new char[size + 1];
-		chunk.read(buffer, size);
-		buffer[size] = '\0';
-		u.string = new Common::String(buffer);
-		delete[] buffer;
-
-	} else if (kDatumTypeGraphicSize == t || kDatumTypePoint == t) {
-		uint16 x = Datum(chunk, kDatumTypeGraphicUnit).u.i;
-		uint16 y = Datum(chunk, kDatumTypeGraphicUnit).u.i;
-		u.point = new Common::Point(x, y);
-
-	} else if (kDatumTypeRect == t) {
-		Common::Point *leftTop = Datum(chunk, kDatumTypePoint).u.point;
-		Common::Point *dimensions = Datum(chunk, kDatumTypeGraphicSize).u.point;
-		u.bbox = new Common::Rect(*leftTop, dimensions->x, dimensions->y);
-		delete leftTop;
-		delete dimensions;
-
-	} else if (kDatumTypePolygon == t) {
-		uint16 totalPoints = Datum(chunk, kDatumTypeUint16).u.i;
-		for (int i = 0; i < totalPoints; i++) {
-			Common::Point *point = Datum(chunk, kDatumTypeGraphicSize).u.point;
-			u.polygon->push_back(point);
-		}
-
-	} else if (kDatumTypePalette == t) {
-		u.palette = new unsigned char[0x300];
-		chunk.read(u.palette, 0x300);
-
-	} else if (kDatumTypeChunkReference == t) {
-		u.chunkRef = chunk.readUint32BE();
-
-	} else {
-		error("Unknown datum type: 0x%x (@0x%llx)", static_cast<uint>(t), static_cast<long long int>(chunk.pos()));
-	}
-}
-
-} // End of namespace MediaStation
diff --git a/engines/mediastation/datum.h b/engines/mediastation/datum.h
deleted file mode 100644
index a5b303c43bf..00000000000
--- a/engines/mediastation/datum.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef MEDIASTATION_DATUM_H
-#define MEDIASTATION_DATUM_H
-
-#include "common/str.h"
-#include "common/array.h"
-#include "common/rect.h"
-#include "common/stream.h"
-
-#include "mediastation/datafile.h"
-
-namespace MediaStation {
-
-enum DatumType {
-	kDatumTypeEmpty = 0x00,
-	kDatumTypeUint8 = 0x02,
-	kDatumTypeUint16 = 0x03,
-	kDatumTypeUint32 = 0x04,
-	kDatumTypeInt8 = 0x05,
-	kDatumTypeInt16 = 0x06,
-	kDatumTypeInt32 = 0x07,
-	kDatumTypeFloat = 0x08,
-	kDatumTypeDouble = 0x09,
-	kDatumTypeFilename = 0x0a,
-	kDatumTypeRect = 0x0d,
-	kDatumTypePoint = 0x0e,
-	kDatumTypeGraphicSize = 0x0f,
-	kDatumTypeGraphicUnit = 0x10,
-	kDatumTypeTime = 0x11,
-	kDatumTypeString = 0x12,
-	kDatumTypeVersion = 0x13,
-	kDatumTypeChunkReference = 0x1b,
-	kDatumTypePolygon = 0x1d,
-	kDatumTypePalette = 0x05aa
-};
-
-// It is the caller's responsibility to delete any heap items
-// that are created as part of a datum. The datum is really
-// just a container.
-class Datum {
-public:
-	DatumType t;
-	union {
-		int i;
-		double f;
-		uint32 chunkRef;
-		Common::String *string;
-		Common::Array<Common::Point *> *polygon;
-		Common::Point *point;
-		Common::Rect *bbox;
-		unsigned char *palette;
-	} u;
-
-	Datum();
-	Datum(Common::SeekableReadStream &chunk);
-	Datum(Common::SeekableReadStream &chunk, DatumType expectedType);
-
-private:
-	void readWithType(Common::SeekableReadStream &chunk);
-};
-
-} // End of namespace MediaStation
-
-#endif
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 5c95b000f77..08ae09347ad 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -24,26 +24,25 @@
 #include "mediastation/mediastation.h"
 #include "mediastation/mediascript/codechunk.h"
 #include "mediastation/mediascript/collection.h"
-#include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
-CodeChunk::CodeChunk(Common::SeekableReadStream &chunk) {
-	uint lengthInBytes = Datum(chunk, kDatumTypeUint32).u.i;
+CodeChunk::CodeChunk(Chunk &chunk) {
+	uint lengthInBytes = chunk.readTypedUint32();
 	debugC(5, kDebugLoading, "CodeChunk::CodeChunk(): Length 0x%x (@0x%llx)", lengthInBytes, static_cast<long long int>(chunk.pos()));
-	_bytecode = chunk.readStream(lengthInBytes);
+	_bytecode = static_cast<ParameterReadStream *>(chunk.readStream(lengthInBytes));
 }
 
 ScriptValue CodeChunk::executeNextBlock() {
-	uint blockSize = Datum(*_bytecode, kDatumTypeUint32).u.i;
+	uint blockSize = _bytecode->readTypedUint32();
 	uint startingPos = _bytecode->pos();
 
 	ScriptValue returnValue;
-	ExpressionType expressionType = static_cast<ExpressionType>(Datum(*_bytecode).u.i);
+	ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
 	while (expressionType != kExpressionTypeEmpty && !_returnImmediately) {
 		returnValue = evaluateExpression(expressionType);
-		expressionType = static_cast<ExpressionType>(Datum(*_bytecode).u.i);
+		expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
 	}
 
 	// Verify we consumed the right number of script bytes.
@@ -57,7 +56,7 @@ ScriptValue CodeChunk::executeNextBlock() {
 }
 
 void CodeChunk::skipNextBlock() {
-	uint lengthInBytes = Datum(*_bytecode, kDatumTypeUint32).u.i;
+	uint lengthInBytes = _bytecode->readTypedUint32();
 	_bytecode->skip(lengthInBytes);
 }
 
@@ -77,7 +76,7 @@ ScriptValue CodeChunk::execute(Common::Array<ScriptValue> *args) {
 }
 
 ScriptValue CodeChunk::evaluateExpression() {
-	ExpressionType expressionType = static_cast<ExpressionType>(Datum(*_bytecode).u.i);
+	ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
 	ScriptValue returnValue = evaluateExpression(expressionType);
 	return returnValue;
 }
@@ -110,7 +109,7 @@ ScriptValue CodeChunk::evaluateExpression(ExpressionType expressionType) {
 }
 
 ScriptValue CodeChunk::evaluateOperation() {
-	Opcode opcode = static_cast<Opcode>(Datum(*_bytecode).u.i);
+	Opcode opcode = static_cast<Opcode>(_bytecode->readTypedUint16());
 	debugCN(5, kDebugScript, "%s ", opcodeToStr(opcode));
 
 	ScriptValue returnValue;
@@ -187,13 +186,13 @@ ScriptValue CodeChunk::evaluateOperation() {
 }
 
 ScriptValue CodeChunk::evaluateValue() {
-	OperandType operandType = static_cast<OperandType>(Datum(*_bytecode).u.i);
+	OperandType operandType = static_cast<OperandType>(_bytecode->readTypedUint16());
 	debugCN(5, kDebugScript, "%s ", operandTypeToStr(operandType));
 
 	ScriptValue returnValue;
 	switch (operandType) {
 	case kOperandTypeBool: {
-		int b = Datum(*_bytecode).u.i;
+		int b = _bytecode->readTypedByte();
 		if (b != 0 && b != 1) {
 			error("Got invalid literal bool value %d", b);
 		}
@@ -203,14 +202,14 @@ ScriptValue CodeChunk::evaluateValue() {
 	}
 
 	case kOperandTypeFloat: {
-		double f = Datum(*_bytecode).u.f;
+		double f = _bytecode->readTypedDouble();
 		debugC(5, kDebugScript, "%f ", f);
 		returnValue.setToFloat(f);
 		return returnValue;
 	}
 
 	case kOperandTypeInt: {
-		int i = Datum(*_bytecode).u.i;
+		int i = _bytecode->readTypedSint32();
 		debugC(5, kDebugScript, "%d ", i);
 		// Ints are stored internally as doubles.
 		returnValue.setToFloat(static_cast<double>(i));
@@ -219,7 +218,7 @@ ScriptValue CodeChunk::evaluateValue() {
 
 	case kOperandTypeString: {
 		// This is indeed a raw string, not a string wrapped in a datum!
-		uint size = Datum(*_bytecode, kDatumTypeUint16).u.i;
+		uint size = _bytecode->readTypedUint16();
 		Common::String string = _bytecode->readString('\0', size);
 		debugC(5, kDebugScript, "%s ", string.c_str());
 		returnValue.setToString(string);
@@ -227,21 +226,21 @@ ScriptValue CodeChunk::evaluateValue() {
 	}
 
 	case kOperandTypeParamToken: {
-		uint literal = Datum(*_bytecode).u.i;
+		uint literal = _bytecode->readTypedUint16();
 		debugC(5, kDebugScript, "%d ", literal);
 		returnValue.setToParamToken(literal);
 		return returnValue;
 	}
 
 	case kOperandTypeAssetId: {
-		uint assetId = Datum(*_bytecode).u.i;
+		uint assetId = _bytecode->readTypedUint16();
 		debugC(5, kDebugScript, "%d ", assetId);
 		returnValue.setToAssetId(assetId);
 		return returnValue;
 	}
 
 	case kOperandTypeTime: {
-		double d = Datum(*_bytecode).u.f;
+		double d = _bytecode->readTypedTime();
 		debugC(5, kDebugScript, "%f ", d);
 		returnValue.setToTime(d);
 		return returnValue;
@@ -253,14 +252,14 @@ ScriptValue CodeChunk::evaluateValue() {
 	}
 
 	case kOperandTypeFunctionId: {
-		uint functionId = Datum(*_bytecode).u.i;
+		uint functionId = _bytecode->readTypedUint16();
 		debugC(5, kDebugScript, "%d ", functionId);
 		returnValue.setToFunctionId(functionId);
 		return returnValue;
 	}
 
 	case kOperandTypeMethodId: {
-		BuiltInMethod methodId = static_cast<BuiltInMethod>(Datum(*_bytecode).u.i);
+		BuiltInMethod methodId = static_cast<BuiltInMethod>(_bytecode->readTypedUint16());
 		debugC(5, kDebugScript, "%s ", builtInMethodToStr(methodId));
 		returnValue.setToMethodId(methodId);
 		return returnValue;
@@ -277,8 +276,8 @@ ScriptValue CodeChunk::evaluateVariable() {
 }
 
 ScriptValue *CodeChunk::readAndReturnVariable() {
-	uint id = Datum(*_bytecode).u.i;
-	VariableScope scope = static_cast<VariableScope>(Datum(*_bytecode).u.i);
+	uint id = _bytecode->readTypedUint16();
+	VariableScope scope = static_cast<VariableScope>(_bytecode->readTypedUint16());
 	debugC(5, kDebugScript, "%d (%s)", id, variableScopeToStr(scope));
 
 	ScriptValue returnValue;
@@ -444,12 +443,12 @@ ScriptValue CodeChunk::evaluateUnaryOperation() {
 ScriptValue CodeChunk::evaluateFunctionCall(bool isIndirect) {
 	uint functionId, paramCount = 0;
 	if (isIndirect) {
-		paramCount = Datum(*_bytecode).u.i;
+		paramCount = _bytecode->readTypedUint16();
 		ScriptValue value = evaluateExpression();
 		functionId = value.asFunctionId();
 	} else {
-		functionId = Datum(*_bytecode).u.i;
-		paramCount = Datum(*_bytecode).u.i;
+		functionId = _bytecode->readTypedUint16();
+		paramCount = _bytecode->readTypedUint16();
 	}
 
 	return evaluateFunctionCall(functionId, paramCount);
@@ -484,12 +483,12 @@ ScriptValue CodeChunk::evaluateMethodCall(bool isIndirect) {
 	BuiltInMethod method;
 	uint paramCount = 0;
 	if (isIndirect) {
-		paramCount = Datum(*_bytecode).u.i;
+		paramCount = _bytecode->readTypedUint16();
 		ScriptValue value = evaluateExpression();
 		method = value.asMethodId();
 	} else {
-		method = static_cast<BuiltInMethod>(Datum(*_bytecode).u.i);
-		paramCount = Datum(*_bytecode).u.i;
+		method = static_cast<BuiltInMethod>(_bytecode->readTypedUint16());
+		paramCount = _bytecode->readTypedUint16();
 	}
 
 	return evaluateMethodCall(method, paramCount);
@@ -552,7 +551,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
 }
 
 void CodeChunk::evaluateDeclareLocals() {
-	uint localVariableCount = Datum(*_bytecode).u.i;
+	uint localVariableCount = _bytecode->readTypedUint16();
 	if (localVariableCount <= 0) {
 		error("Got non-positive local variable count");
 	}
diff --git a/engines/mediastation/mediascript/codechunk.h b/engines/mediastation/mediascript/codechunk.h
index 3d6b593ca67..cc0798d56c0 100644
--- a/engines/mediastation/mediascript/codechunk.h
+++ b/engines/mediastation/mediascript/codechunk.h
@@ -23,7 +23,6 @@
 #define MEDIASTATION_MEDIASCRIPT_CODECHUNK_H
 
 #include "common/array.h"
-#include "common/stream.h"
 
 #include "mediastation/datafile.h"
 #include "mediastation/mediascript/scriptvalue.h"
@@ -33,7 +32,7 @@ namespace MediaStation {
 
 class CodeChunk {
 public:
-	CodeChunk(Common::SeekableReadStream &chunk);
+	CodeChunk(Chunk &chunk);
 	~CodeChunk();
 
 	ScriptValue executeNextBlock();
@@ -68,7 +67,7 @@ private:
 	bool _returnImmediately = false;
 	Common::Array<ScriptValue> _locals;
 	Common::Array<ScriptValue> *_args = nullptr;
-	Common::SeekableReadStream *_bytecode = nullptr;
+	ParameterReadStream *_bytecode = nullptr;
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/collection.cpp b/engines/mediastation/mediascript/collection.cpp
index 3f586f0f15d..bcb27940ac7 100644
--- a/engines/mediastation/mediascript/collection.cpp
+++ b/engines/mediastation/mediascript/collection.cpp
@@ -23,7 +23,6 @@
 #include "mediastation/mediascript/collection.h"
 #include "mediastation/mediascript/scriptvalue.h"
 #include "mediastation/mediascript/codechunk.h"
-#include "mediastation/datum.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
diff --git a/engines/mediastation/mediascript/collection.h b/engines/mediastation/mediascript/collection.h
index 8788b08d9d9..632597fc03a 100644
--- a/engines/mediastation/mediascript/collection.h
+++ b/engines/mediastation/mediascript/collection.h
@@ -27,7 +27,6 @@
 #include "common/array.h"
 
 #include "mediastation/datafile.h"
-#include "mediastation/datum.h"
 #include "mediastation/mediascript/scriptconstants.h"
 
 namespace MediaStation {
diff --git a/engines/mediastation/mediascript/eventhandler.cpp b/engines/mediastation/mediascript/eventhandler.cpp
index 0c73d969b77..fa9fbbb9e68 100644
--- a/engines/mediastation/mediascript/eventhandler.cpp
+++ b/engines/mediastation/mediascript/eventhandler.cpp
@@ -25,7 +25,7 @@
 namespace MediaStation {
 
 EventHandler::EventHandler(Chunk &chunk) {
-	_type = static_cast<EventType>(Datum(chunk).u.i);
+	_type = static_cast<EventType>(chunk.readTypedUint16());
 	debugC(5, kDebugLoading, "EventHandler::EventHandler(): Type %s (%d) (@0x%llx)",
 		eventTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
 
diff --git a/engines/mediastation/mediascript/eventhandler.h b/engines/mediastation/mediascript/eventhandler.h
index a72f7004af7..00a6c352f4c 100644
--- a/engines/mediastation/mediascript/eventhandler.h
+++ b/engines/mediastation/mediascript/eventhandler.h
@@ -25,7 +25,6 @@
 #include "common/str.h"
 
 #include "mediastation/datafile.h"
-#include "mediastation/datum.h"
 #include "mediastation/mediascript/codechunk.h"
 #include "mediastation/mediascript/scriptconstants.h"
 
diff --git a/engines/mediastation/mediascript/function.cpp b/engines/mediastation/mediascript/function.cpp
index 558fb165d0e..89570482602 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -19,18 +19,17 @@
  *
  */
 
-#include "mediastation/datum.h"
 #include "mediastation/mediascript/function.h"
 #include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
 Function::Function(Chunk &chunk) {
-	_fileId = Datum(chunk).u.i;
+	_fileId = chunk.readTypedUint16();
 	// In PROFILE._ST (only present in some titles), the function ID is reported
 	// with 19900 added, so function 100 would be reported as 20000. But in
 	// bytecode, the zero-based ID is used, so that's what we'll store here.
-	_id = Datum(chunk).u.i;
+	_id = chunk.readTypedUint16();
 	_code = new CodeChunk(chunk);
 }
 
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index 6e411caf0b6..d7ff04710b4 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -19,27 +19,26 @@
  *
  */
 
-#include "mediastation/datum.h"
 #include "mediastation/mediascript/scriptvalue.h"
 #include "mediastation/mediascript/function.h"
 
 namespace MediaStation {
 
-ScriptValue::ScriptValue(Common::SeekableReadStream *stream) {
-	_type = static_cast<ScriptValueType>(Datum(*stream).u.i);
+ScriptValue::ScriptValue(ParameterReadStream *stream) {
+	_type = static_cast<ScriptValueType>(stream->readTypedByte());
 
 	switch (_type) {
 	case kScriptValueTypeEmpty:
 		break;
 
 	case kScriptValueTypeFloat: {
-		double d = Datum(*stream).u.f;
+		double d = stream->readTypedDouble();
 		setToFloat(d);
 		break;
 	}
 
 	case kScriptValueTypeBool: {
-		uint rawValue = Datum(*stream, kDatumTypeUint8).u.i;
+		uint rawValue = stream->readTypedByte();
 		if (rawValue != 0 && rawValue != 1) {
 			error("Got invalid literal bool value %d", rawValue);
 		}
@@ -48,32 +47,32 @@ ScriptValue::ScriptValue(Common::SeekableReadStream *stream) {
 	}
 
 	case kScriptValueTypeTime: {
-		double d = Datum(*stream).u.f;
+		double d = stream->readTypedTime();
 		setToFloat(d);
 		break;
 	}
 
 	case kScriptValueTypeParamToken: {
-		uint paramToken = Datum(*stream).u.i;
+		uint paramToken = stream->readTypedUint16();
 		setToParamToken(paramToken);
 		break;
 	}
 
 	case kScriptValueTypeAssetId: {
-		uint assetId = Datum(*stream).u.i;
+		uint assetId = stream->readTypedUint16();
 		setToAssetId(assetId);
 		break;
 	}
 
 	case kScriptValueTypeString: {
-		uint size = Datum(*stream).u.i;
+		uint size = stream->readTypedUint16();
 		Common::String string = stream->readString('\0', size);
 		setToString(string);
 		break;
 	}
 
 	case kScriptValueTypeCollection: {
-		uint totalItems = Datum(*stream).u.i;
+		uint totalItems = stream->readTypedUint16();
 		Common::SharedPtr<Collection> collection(new Collection);
 		for (uint i = 0; i < totalItems; i++) {
 			ScriptValue collectionValue = ScriptValue(stream);
@@ -84,13 +83,13 @@ ScriptValue::ScriptValue(Common::SeekableReadStream *stream) {
 	}
 
 	case kScriptValueTypeFunctionId: {
-		uint functionId = Datum(*stream).u.i;
+		uint functionId = stream->readTypedUint16();
 		setToFunctionId(functionId);
 		break;
 	}
 
 	case kScriptValueTypeMethodId: {
-		BuiltInMethod methodId = static_cast<BuiltInMethod>(Datum(*stream).u.i);
+		BuiltInMethod methodId = static_cast<BuiltInMethod>(stream->readTypedUint16());
 		setToMethodId(methodId);
 		break;
 	}
diff --git a/engines/mediastation/mediascript/scriptvalue.h b/engines/mediastation/mediascript/scriptvalue.h
index abbd5da6711..75c70f6368c 100644
--- a/engines/mediastation/mediascript/scriptvalue.h
+++ b/engines/mediastation/mediascript/scriptvalue.h
@@ -24,8 +24,8 @@
 
 #include "common/ptr.h"
 #include "common/str.h"
-#include "common/stream.h"
 
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/scriptconstants.h"
 #include "mediastation/mediascript/collection.h"
 
@@ -36,7 +36,7 @@ class Asset;
 class ScriptValue {
 public:
 	ScriptValue() : _type(kScriptValueTypeEmpty) {}
-	ScriptValue(Common::SeekableReadStream *stream);
+	ScriptValue(ParameterReadStream *stream);
 
 	ScriptValueType getType() const { return _type; }
 
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 5f22fe6ea7c..92c29af6236 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -125,7 +125,7 @@ bool MediaStationEngine::isFirstGenerationEngine() {
 	if (_boot == nullptr) {
 		error("Attempted to get engine version before BOOT.STM was read");
 	} else {
-		return (_boot->_versionInfo == nullptr);
+		return (_boot->_versionInfo.major == 0);
 	}
 }
 
@@ -264,7 +264,7 @@ void MediaStationEngine::setCursor(uint id) {
 		if (cursorDeclaration == nullptr) {
 			error("MediaStationEngine::setCursor(): Cursor %d not declared", id);
 		}
-		_cursor->setCursor(*cursorDeclaration->_name);
+		_cursor->setCursor(cursorDeclaration->_name);
 	}
 }
 
@@ -303,9 +303,9 @@ void MediaStationEngine::redraw() {
 
 	for (Common::Rect dirtyRect : _dirtyRects) {
 		for (Asset *asset : _assetsPlaying) {
-			Common::Rect *bbox = asset->getBbox();
-			if (bbox != nullptr) {
-				if (dirtyRect.intersects(*bbox)) {
+			Common::Rect bbox = asset->getBbox();
+			if (!bbox.isEmpty()) {
+				if (dirtyRect.intersects(bbox)) {
 					asset->redraw(dirtyRect);
 				}
 			}
@@ -347,7 +347,7 @@ Context *MediaStationEngine::loadContext(uint32 contextId) {
 		warning("MediaStationEngine::loadContext(): Couldn't find file declaration with ID 0x%x", fileId);
 		return nullptr;
 	}
-	Common::Path entryCxtFilepath(*fileDeclaration->_name);
+	Common::Path entryCxtFilepath(fileDeclaration->_name);
 
 	// Load any child contexts before we actually load this one. The child
 	// contexts must be unloaded explicitly later.
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index da72f49bc9a..8c43dddd48c 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -20,7 +20,6 @@ MODULE_OBJS = \
 	context.o \
 	cursors.o \
 	datafile.o \
-	datum.o \
 	mediascript/codechunk.o \
 	mediascript/collection.o \
 	mediascript/eventhandler.o \


Commit: a43e097df18780d696d26e83e58c849534c5abc8
    https://github.com/scummvm/scummvm/commit/a43e097df18780d696d26e83e58c849534c5abc8
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:25:03-04:00

Commit Message:
MEDIASTATION: Simplify Boot stream parsing

Changed paths:
    engines/mediastation/boot.cpp
    engines/mediastation/boot.h
    engines/mediastation/mediastation.cpp


diff --git a/engines/mediastation/boot.cpp b/engines/mediastation/boot.cpp
index 55acbfe80f0..fb767591453 100644
--- a/engines/mediastation/boot.cpp
+++ b/engines/mediastation/boot.cpp
@@ -26,65 +26,35 @@ namespace MediaStation {
 
 #pragma region ContextDeclaration
 ContextDeclaration::ContextDeclaration(Chunk &chunk) {
-	// Make sure this declaration isn't empty.
+	// Read the file number.
 	ContextDeclarationSectionType sectionType = getSectionType(chunk);
-	if (kContextDeclarationEmptySection == sectionType) {
-		_isLast = true;
-		return;
-	} else {
-		// There may be more declarations in the stream.
-		_isLast = false;
+	if (kContextDeclarationContextId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_contextId = chunk.readTypedUint16();
 
-	if (kContextDeclarationPlaceholder == sectionType) {
-		// Read the file number.
-		sectionType = getSectionType(chunk);
-		if (kContextDeclarationFileNumber1 == sectionType) {
-			_fileNumber = chunk.readTypedUint16();
-		} else {
-			error("ContextDeclaration(): Expected section type FILE_NUMBER_1, got 0x%x", static_cast<uint>(sectionType));
-		}
-		// I don't know why the file number is always repeated.
-		// Is it just for data integrity, or is there some other reason?
-		sectionType = getSectionType(chunk);
-		if (kContextDeclarationFileNumber2 == sectionType) {
-			uint32 repeatedFileNumber = chunk.readTypedUint16();
-			if (repeatedFileNumber != _fileNumber) {
-				warning("ContextDeclaration(): Expected file numbers to match, but 0x%d != 0x%d", _fileNumber, repeatedFileNumber);
-			}
-		} else {
-			error("ContextDeclaration(): Expected section type FILE_NUMBER_2, got 0x%x", static_cast<uint>(sectionType));
-		}
+	sectionType = getSectionType(chunk);
+	if (kContextDeclarationStreamId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
+	}
+	_streamId = chunk.readTypedUint16();
 
-		// Read the context name.
-		// Only some titles have context names, and unfortunately we can't
-		// determine which just by relying on the title compiler version
-		// number.
-		// TODO: Find a better way to read the context name without relying
-		// on reading and rewinding.
-		int rewindOffset = chunk.pos();
+	// Read the context name. Only some titles have context names,
+	// and unfortunately we can't determine which just by relying
+	// on the title compiler version number.
+	sectionType = getSectionType(chunk);
+	if (kContextDeclarationName == sectionType) {
+		_name = chunk.readTypedString();
 		sectionType = getSectionType(chunk);
-		if (kContextDeclarationName == sectionType) {
-			_contextName = chunk.readTypedString();
-		} else {
-			// There is no context name.
-			// We have instead read into the next declaration, so let's undo that.
-			chunk.seek(rewindOffset);
-		}
-	} else if (kContextDeclarationEmptySection == sectionType) {
-		_isLast = true;
-	} else {
-		error("ContextDeclaration::ContextDeclaration(): Unknown section type 0x%x", static_cast<uint>(sectionType));
 	}
 
-	// Read the file references.
-	// We don't know how many file references there are beforehand, so we'll
-	// just read until we get something else.
-	int rewindOffset = 0;
-	sectionType = getSectionType(chunk);
-	while (kContextDeclarationFileReference == sectionType) {
+	// Read the parent context IDs. We don't know how many file
+	// references there are beforehand, so we'll just read until
+	// we get something else.
+	uint rewindOffset = chunk.pos();
+	while (kContextDeclarationParentContextId == sectionType) {
 		int fileReference = chunk.readTypedUint16();
-		_fileReferences.push_back(fileReference);
+		_parentContextIds.push_back(fileReference);
 		rewindOffset = chunk.pos();
 		sectionType = getSectionType(chunk);
 	}
@@ -96,67 +66,42 @@ ContextDeclarationSectionType ContextDeclaration::getSectionType(Chunk &chunk) {
 }
 #pragma endregion
 
-#pragma region UnknownDeclaration
-UnknownDeclaration::UnknownDeclaration(Chunk &chunk) {
+#pragma region ScreenDeclaration
+ScreenDeclaration::ScreenDeclaration(Chunk &chunk) {
 	// Make sure this declaration isn't empty.
-	UnknownDeclarationSectionType sectionType = getSectionType(chunk);
-	if (kUnknownDeclarationEmptySection == sectionType) {
-		_isLast = true;
-		return;
-	} else {
-		// There may be more declarations in the stream.
-		_isLast = false;
+	ScreenDeclarationSectionType sectionType = getSectionType(chunk);
+	if (kScreenDeclarationAssetId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_assetId = chunk.readTypedUint16();
 
 	sectionType = getSectionType(chunk);
-	if (kUnknownDeclarationUnk1 == sectionType) {
-		_unk = chunk.readTypedUint16();
-	} else {
-		error("UnknownDeclaration(): Expected section type UNK_1, got 0x%x", static_cast<uint>(sectionType));
-	}
-	sectionType = getSectionType(chunk);
-	if (kUnknownDeclarationUnk2 == sectionType) {
-		uint16 repeatedUnk = chunk.readTypedUint16();
-		if (repeatedUnk != _unk) {
-			warning("UnknownDeclaration(): Expected unknown values to match, but 0x%x != 0x%x", _unk, repeatedUnk);
-		}
-	} else {
-		error("UnknownDeclaration(): Expected section type UNK_2, got 0x%x", static_cast<uint>(sectionType));
+	if (kScreenDeclarationScreenId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_screenId = chunk.readTypedUint16();
 }
 
-UnknownDeclarationSectionType UnknownDeclaration::getSectionType(Chunk &chunk) {
-	return static_cast<UnknownDeclarationSectionType>(chunk.readTypedUint16());
+ScreenDeclarationSectionType ScreenDeclaration::getSectionType(Chunk &chunk) {
+	return static_cast<ScreenDeclarationSectionType>(chunk.readTypedUint16());
 }
 #pragma endregion
 
 #pragma region FileDeclaration
 FileDeclaration::FileDeclaration(Chunk &chunk) {
-	// Make sure this declaration isn't empty.
-	FileDeclarationSectionType sectionType = getSectionType(chunk);
-	if (kFileDeclarationEmptySection == sectionType) {
-		_isLast = true;
-		return;
-	} else {
-		// There may be more declarations in the stream.
-		_isLast = false;
-	}
-
 	// Read the file ID.
-	sectionType = getSectionType(chunk);
-	if (kFileDeclarationFileId == sectionType) {
-		_id = chunk.readTypedUint16();
-	} else {
-		error("FileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
+	FileDeclarationSectionType sectionType = getSectionType(chunk);
+	if (kFileDeclarationFileId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_id = chunk.readTypedUint16();
 
 	// Read the intended file location.
 	sectionType = getSectionType(chunk);
-	if (kFileDeclarationFileNameAndType == sectionType) {
-		_intendedLocation = static_cast<IntendedFileLocation>(chunk.readTypedUint16());
-	} else {
-		error("FileDeclaration(): Expected section type FILE_NAME_AND_TYPE, got 0x%x", static_cast<uint>(sectionType));
+	if (kFileDeclarationFileNameAndType != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_intendedLocation = static_cast<IntendedFileLocation>(chunk.readTypedUint16());
 
 	// Since the platforms that Media Station originally targeted were case-insensitive,
 	// the case of these filenames might not match the case of the files actually in
@@ -171,39 +116,26 @@ FileDeclarationSectionType FileDeclaration::getSectionType(Chunk &chunk) {
 
 #pragma region SubfileDeclaration
 SubfileDeclaration::SubfileDeclaration(Chunk &chunk) {
-	// Make sure this declaration isn't empty.
-	SubfileDeclarationSectionType sectionType = getSectionType(chunk);
-	if (kSubfileDeclarationEmptySection == sectionType) {
-		_isLast = true;
-		return;
-	} else {
-		// There may be more declarations in the stream.
-		_isLast = false;
-	}
-
 	// Read the asset ID.
-	sectionType = getSectionType(chunk);
-	if (kSubfileDeclarationAssetId == sectionType) {
-		_assetId = chunk.readTypedUint16();
-	} else {
-		error("SubfileDeclaration(): Expected section type ASSET_ID, got 0x%x", static_cast<uint>(sectionType));
+	SubfileDeclarationSectionType sectionType = getSectionType(chunk);
+	if (kSubfileDeclarationAssetId != sectionType) {
+		error("Got unexpected section type %d", static_cast<uint>(sectionType));
 	}
+	_assetId = chunk.readTypedUint16();
 
 	// Read the file ID.
 	sectionType = getSectionType(chunk);
-	if (kSubfileDeclarationFileId == sectionType) {
-		_fileId = chunk.readTypedUint16();
-	} else {
+	if (kSubfileDeclarationFileId != sectionType) {
 		error("SubfileDeclaration(): Expected section type FILE_ID, got 0x%x", static_cast<uint>(sectionType));
 	}
+	_fileId = chunk.readTypedUint16();
 
 	// Read the start offset from the absolute start of the file.
 	sectionType = getSectionType(chunk);
-	if (kSubfileDeclarationStartOffset == sectionType) {
-		_startOffsetInFile = chunk.readTypedUint32();
-	} else {
+	if (kSubfileDeclarationStartOffset != sectionType) {
 		error("SubfileDeclaration(): Expected section type START_OFFSET, got 0x%x", static_cast<uint>(sectionType));
 	}
+	_startOffsetInFile = chunk.readTypedUint32();
 }
 
 SubfileDeclarationSectionType SubfileDeclaration::getSectionType(Chunk &chunk) {
@@ -265,7 +197,7 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 			sectionType = getSectionType(chunk);
 			if (sectionType == kBootEngineResourceId) {
 				int resourceId = chunk.readTypedUint16();
-				EngineResourceDeclaration *resourceDeclaration = new EngineResourceDeclaration(resourceName, resourceId);
+				EngineResourceDeclaration resourceDeclaration = EngineResourceDeclaration(resourceName, resourceId);
 				_engineResourceDeclarations.setVal(resourceId, resourceDeclaration);
 			} else {
 				error("Boot::Boot(): Got section type 0x%x when expecting ENGINE_RESOURCE_ID", static_cast<uint>(sectionType));
@@ -274,56 +206,48 @@ Boot::Boot(const Common::Path &path) : Datafile(path){
 		}
 
 		case kBootContextDeclaration: {
-			ContextDeclaration *contextDeclaration = new ContextDeclaration(chunk);
-			while (!contextDeclaration->_isLast) {
-				_contextDeclarations.setVal(contextDeclaration->_fileNumber, contextDeclaration);
-				contextDeclaration = new ContextDeclaration(chunk);
+			uint flag = chunk.readTypedUint16();
+			while (flag != 0) {
+				ContextDeclaration contextDeclaration = ContextDeclaration(chunk);
+				_contextDeclarations.setVal(contextDeclaration._contextId, contextDeclaration);
+				flag = chunk.readTypedUint16();
 			}
-			// The very last declaration is just an empty flag, so delete it
-			// since it's not put in the map.
-			delete contextDeclaration;
 			break;
 		}
 
-		case kBootUnknownDeclaration: {
-			UnknownDeclaration *unknownDeclaration = new UnknownDeclaration(chunk);
-			while (!unknownDeclaration->_isLast) {
-				_unknownDeclarations.push_back(unknownDeclaration);
-				unknownDeclaration = new UnknownDeclaration(chunk);
+		case kBootScreenDeclaration: {
+			uint flag = chunk.readTypedUint16();
+			while (flag != 0) {
+				ScreenDeclaration screenDeclaration = ScreenDeclaration(chunk);
+				_screenDeclarations.setVal(screenDeclaration._assetId, screenDeclaration);
+				flag = chunk.readTypedUint16();
 			}
-			// The very last declaration is just an empty flag, so delete it
-			// since it's not put in the map.
-			delete unknownDeclaration;
 			break;
 		}
 
 		case kBootFileDeclaration: {
-			FileDeclaration *fileDeclaration = new FileDeclaration(chunk);
-			while (!fileDeclaration->_isLast) {
-				_fileDeclarations.setVal(fileDeclaration->_id, fileDeclaration);
-				fileDeclaration = new FileDeclaration(chunk);
-			}
-			// The very last declaration is just an empty flag, so delete it
-			// since it's not put in the map.
-			delete fileDeclaration;
+			uint flag = chunk.readTypedUint16();
+			while (flag != 0) {
+				FileDeclaration fileDeclaration = FileDeclaration(chunk);
+				_fileDeclarations.setVal(fileDeclaration._id, fileDeclaration);
+				flag = chunk.readTypedUint16();
+			};
 			break;
 		}
 
 		case kBootSubfileDeclaration: {
-			SubfileDeclaration *subfileDeclaration = new SubfileDeclaration(chunk);
-			while (!subfileDeclaration->_isLast) {
-				_subfileDeclarations.setVal(subfileDeclaration->_assetId, subfileDeclaration);
-				subfileDeclaration = new SubfileDeclaration(chunk);
+			uint flag = chunk.readTypedUint16();
+			while (flag != 0) {
+				SubfileDeclaration subfileDeclaration = SubfileDeclaration(chunk);
+				_subfileDeclarations.setVal(subfileDeclaration._assetId, subfileDeclaration);
+				flag = chunk.readTypedUint16();
 			}
-			// The very last declaration is just an empty flag, so delete it
-			// since it's not put in the map.
-			delete subfileDeclaration;
 			break;
 		}
 
 		case kBootCursorDeclaration: {
-			CursorDeclaration *cursorDeclaration = new CursorDeclaration(chunk);
-			_cursorDeclarations.setVal(cursorDeclaration->_id, cursorDeclaration);
+			CursorDeclaration cursorDeclaration = CursorDeclaration(chunk);
+			_cursorDeclarations.setVal(cursorDeclaration._id, cursorDeclaration);
 			break;
 		}
 
@@ -373,34 +297,11 @@ BootSectionType Boot::getSectionType(Chunk &chunk) {
 }
 
 Boot::~Boot() {
-	for (auto it = _contextDeclarations.begin(); it != _contextDeclarations.end(); ++it) {
-		delete it->_value;
-	}
 	_contextDeclarations.clear();
-
-	for (auto it = _subfileDeclarations.begin(); it != _subfileDeclarations.end(); ++it) {
-		delete it->_value;
-	}
 	_subfileDeclarations.clear();
-
-	for (auto it = _cursorDeclarations.begin(); it != _cursorDeclarations.end(); ++it) {
-		delete it->_value;
-	}
 	_cursorDeclarations.clear();
-
-	for (auto it = _engineResourceDeclarations.begin(); it != _engineResourceDeclarations.end(); ++it) {
-		delete it->_value;
-	}
 	_engineResourceDeclarations.clear();
-
-	for (auto unknownDeclaration : _unknownDeclarations) {
-		delete unknownDeclaration;
-	}
-	_unknownDeclarations.clear();
-
-	for (auto it = _fileDeclarations.begin(); it != _fileDeclarations.end(); ++it) {
-		delete it->_value;
-	}
+	_screenDeclarations.clear();
 	_fileDeclarations.clear();
 }
 #pragma endregion
diff --git a/engines/mediastation/boot.h b/engines/mediastation/boot.h
index 47a79d7c5a6..f77332bbdb9 100644
--- a/engines/mediastation/boot.h
+++ b/engines/mediastation/boot.h
@@ -34,42 +34,42 @@ namespace MediaStation {
 enum ContextDeclarationSectionType {
 	kContextDeclarationEmptySection = 0x0000,
 	kContextDeclarationPlaceholder = 0x0003,
-	kContextDeclarationFileNumber1 = 0x0004,
-	kContextDeclarationFileNumber2 = 0x0005,
-	kContextDeclarationFileReference = 0x0006,
+	kContextDeclarationContextId = 0x0004,
+	kContextDeclarationStreamId = 0x0005,
+	kContextDeclarationParentContextId = 0x0006,
 	kContextDeclarationName = 0x0bb8
 };
 
 class ContextDeclaration {
 public:
 	ContextDeclaration(Chunk &chunk);
+	ContextDeclaration() {};
 
-	Common::Array<uint32> _fileReferences;
-	uint32 _fileNumber = 0;
-	Common::String _contextName;
-	// Signal that there are no more declarations to read.
-	bool _isLast = false;
+	uint _contextId = 0;
+	uint _streamId = 0;
+	Common::String _name;
+	Common::Array<uint> _parentContextIds;
 
 private:
 	ContextDeclarationSectionType getSectionType(Chunk &chunk);
 };
 
-enum UnknownDeclarationSectionType {
-	kUnknownDeclarationEmptySection = 0x0000,
-	kUnknownDeclarationUnk1 = 0x0009,
-	kUnknownDeclarationUnk2 = 0x0004
+enum ScreenDeclarationSectionType {
+	kScreenDeclarationEmpty = 0x0000,
+	kScreenDeclarationAssetId = 0x0009,
+	kScreenDeclarationScreenId = 0x0004
 };
 
-class UnknownDeclaration {
+class ScreenDeclaration {
 public:
-	uint16 _unk = 0;
-	// Signal that there are no more declarations to read.
-	bool _isLast = false;
+	ScreenDeclaration(Chunk &chunk);
+	ScreenDeclaration() {};
 
-	UnknownDeclaration(Chunk &chunk);
+	uint _assetId = 0;
+	uint _screenId = 0;
 
 private:
-	UnknownDeclarationSectionType getSectionType(Chunk& chunk);
+	ScreenDeclarationSectionType getSectionType(Chunk& chunk);
 };
 
 enum FileDeclarationSectionType {
@@ -81,6 +81,7 @@ enum FileDeclarationSectionType {
 // Indicates where a file is intended to be stored.
 // NOTE: This might not be correct and this might be a more general "file type".
 enum IntendedFileLocation {
+	kFileLocationEmpty = 0x0000,
 	// Usually all files that have numbers remain on the CD-ROM.
 	kFileIntendedOnCdRom = 0x0007,
 	// These UNKs only appear in George Shrinks.
@@ -93,12 +94,11 @@ enum IntendedFileLocation {
 class FileDeclaration {
 public:
 	FileDeclaration(Chunk &chunk);
+	FileDeclaration() {};
 
-	uint32 _id = 0;
-	IntendedFileLocation _intendedLocation;
+	uint _id = 0;
+	IntendedFileLocation _intendedLocation = kFileLocationEmpty;
 	Common::String _name;
-	// Signal that there are no more declarations to read.
-	bool _isLast = false;
 
 private:
 	FileDeclarationSectionType getSectionType(Chunk &chunk);
@@ -114,12 +114,11 @@ enum SubfileDeclarationSectionType {
 class SubfileDeclaration {
 public:
 	SubfileDeclaration(Chunk &chunk);
+	SubfileDeclaration() {};
 
-	uint16 _assetId = 0;
-	uint16 _fileId = 0;
-	uint32 _startOffsetInFile = 0;
-	// Signal that there are no more context declarations to read.
-	bool _isLast = false;
+	uint _assetId = 0;
+	uint _fileId = 0;
+	uint _startOffsetInFile = 0;
 
 private:
 	SubfileDeclarationSectionType getSectionType(Chunk &chunk);
@@ -129,18 +128,20 @@ private:
 class CursorDeclaration {
 public:
 	CursorDeclaration(Chunk &chunk);
+	CursorDeclaration() {};
 
-	uint16 _id = 0;
-	uint16 _unk = 0;
+	uint _id = 0;
+	uint _unk = 0;
 	Common::String _name;
 };
 
 class EngineResourceDeclaration {
 public:
-	Common::String _resourceName;
-	int _resourceId = 0;
+	EngineResourceDeclaration(Common::String resourceName, int resourceId) : _name(resourceName), _id(resourceId) {};
+	EngineResourceDeclaration() {};
 
-	EngineResourceDeclaration(Common::String resourceName, int resourceId) : _resourceName(resourceName), _resourceId(resourceId) {};
+	Common::String _name;
+	int _id = 0;
 };
 
 enum BootSectionType {
@@ -153,7 +154,7 @@ enum BootSectionType {
 	kBootUnk3 = 0x0193,
 	kBootEngineResource = 0x0bba,
 	kBootEngineResourceId = 0x0bbb,
-	kBootUnknownDeclaration = 0x0007,
+	kBootScreenDeclaration = 0x0007,
 	kBootFileDeclaration = 0x000a,
 	kBootSubfileDeclaration = 0x000b,
 	kBootUnk5 = 0x000c,
@@ -173,12 +174,12 @@ public:
 	VersionInfo _versionInfo;
 	Common::String _engineInfo;
 	Common::String _sourceString;
-	Common::HashMap<uint32, ContextDeclaration *> _contextDeclarations;
-	Common::Array<UnknownDeclaration *> _unknownDeclarations;
-	Common::HashMap<uint32, FileDeclaration *> _fileDeclarations;
-	Common::HashMap<uint32, SubfileDeclaration *> _subfileDeclarations;
-	Common::HashMap<uint32, CursorDeclaration *> _cursorDeclarations;
-	Common::HashMap<uint32, EngineResourceDeclaration *> _engineResourceDeclarations;
+	Common::HashMap<uint32, ContextDeclaration> _contextDeclarations;
+	Common::HashMap<uint32, ScreenDeclaration> _screenDeclarations;
+	Common::HashMap<uint32, FileDeclaration> _fileDeclarations;
+	Common::HashMap<uint32, SubfileDeclaration> _subfileDeclarations;
+	Common::HashMap<uint32, CursorDeclaration> _cursorDeclarations;
+	Common::HashMap<uint32, EngineResourceDeclaration> _engineResourceDeclarations;
 
 	uint32 _entryContextId = 0;
 	bool _allowMultipleSounds = false;
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 92c29af6236..aebaa7b6355 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -260,11 +260,8 @@ void MediaStationEngine::setCursor(uint id) {
 	// that's a lookup into BOOT.STM, which gives actual name the name of the
 	// resource in the executable.
 	if (id != 0) {
-		CursorDeclaration *cursorDeclaration = _boot->_cursorDeclarations.getValOrDefault(id);
-		if (cursorDeclaration == nullptr) {
-			error("MediaStationEngine::setCursor(): Cursor %d not declared", id);
-		}
-		_cursor->setCursor(cursorDeclaration->_name);
+		const CursorDeclaration &cursorDeclaration = _boot->_cursorDeclarations.getVal(id);
+		_cursor->setCursor(cursorDeclaration._name);
 	}
 }
 
@@ -328,31 +325,23 @@ Context *MediaStationEngine::loadContext(uint32 contextId) {
 	}
 
 	// Get the file ID.
-	SubfileDeclaration *subfileDeclaration = _boot->_subfileDeclarations.getValOrDefault(contextId);
-	if (subfileDeclaration == nullptr) {
-		error("MediaStationEngine::loadContext(): Couldn't find subfile declaration with ID %d", contextId);
-		return nullptr;
-	}
+	const SubfileDeclaration &subfileDeclaration = _boot->_subfileDeclarations.getVal(contextId);
 	// There are other assets in a subfile too, so we need to make sure we're
 	// referencing the screen asset, at the start of the file.
-	if (subfileDeclaration->_startOffsetInFile != 16) {
+	if (subfileDeclaration._startOffsetInFile != 16) {
 		warning("MediaStationEngine::loadContext(): Requested ID wasn't for a context.");
 		return nullptr;
 	}
-	uint32 fileId = subfileDeclaration->_fileId;
+	uint fileId = subfileDeclaration._fileId;
 
 	// Get the filename.
-	FileDeclaration *fileDeclaration = _boot->_fileDeclarations.getValOrDefault(fileId);
-	if (fileDeclaration == nullptr) {
-		warning("MediaStationEngine::loadContext(): Couldn't find file declaration with ID 0x%x", fileId);
-		return nullptr;
-	}
-	Common::Path entryCxtFilepath(fileDeclaration->_name);
+	const FileDeclaration &fileDeclaration = _boot->_fileDeclarations.getVal(fileId);
+	Common::Path entryCxtFilepath(fileDeclaration._name);
 
 	// Load any child contexts before we actually load this one. The child
 	// contexts must be unloaded explicitly later.
-	ContextDeclaration *contextDeclaration = _boot->_contextDeclarations.getValOrDefault(contextId);
-	for (uint32 childContextId : contextDeclaration->_fileReferences) {
+	ContextDeclaration contextDeclaration = _boot->_contextDeclarations.getVal(contextId);
+	for (uint childContextId : contextDeclaration._parentContextIds) {
 		// The root context is referred to by an ID of 0, regardless of what its
 		// actual ID is. The root context is already always loaded.
 		if (childContextId != 0) {
@@ -469,8 +458,8 @@ void MediaStationEngine::releaseContext(uint32 contextId) {
 	// Make sure nothing is still using this context.
 	for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
 		uint id = it->_key;
-		ContextDeclaration *contextDeclaration = _boot->_contextDeclarations.getValOrDefault(id);
-		for (uint32 childContextId : contextDeclaration->_fileReferences) {
+		ContextDeclaration contextDeclaration = _boot->_contextDeclarations.getVal(id);
+		for (uint32 childContextId : contextDeclaration._parentContextIds) {
 			if (childContextId == contextId) {
 				return;
 			}


Commit: 1e76610489d1644179994bfd86daf3613ad21bc1
    https://github.com/scummvm/scummvm/commit/1e76610489d1644179994bfd86daf3613ad21bc1
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:25:03-04:00

Commit Message:
MEDIASTATION: Correctly implement collection methods

Changed paths:
    engines/mediastation/mediascript/collection.cpp
    engines/mediastation/mediascript/collection.h
    engines/mediastation/mediascript/scriptconstants.cpp
    engines/mediastation/mediascript/scriptconstants.h
    engines/mediastation/mediascript/scriptvalue.cpp
    engines/mediastation/mediascript/scriptvalue.h


diff --git a/engines/mediastation/mediascript/collection.cpp b/engines/mediastation/mediascript/collection.cpp
index bcb27940ac7..c78219e899b 100644
--- a/engines/mediastation/mediascript/collection.cpp
+++ b/engines/mediastation/mediascript/collection.cpp
@@ -29,105 +29,145 @@ namespace MediaStation {
 
 ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptValue> &args) {
 	ScriptValue returnValue;
-
 	switch (method) {
-	case kIsEmptyMethod: {
-		returnValue.setToBool(empty());
-		return returnValue;
-	}
-
-	case kAppendMethod: {
-		for (ScriptValue arg : args) {
-			push_back(arg);
+	case kAppendMethod:
+		for (ScriptValue value : args) {
+			push_back(value);
 		}
-		return returnValue;
-	}
+		break;
 
-	case kDeleteFirstMethod: {
-		returnValue = remove_at(0);
-		return returnValue;
-	}
+	case kApplyMethod:
+		apply(args);
+		break;
 
-	case kDeleteAtMethod: {
-		// Find the item in the collection, then remove and return it.
-		assert(args.size() == 1);
-		for (uint i = 0; i < size(); i++) {
-			if (args[0] == operator[](i)) {
-				returnValue = remove_at(i);
-				return returnValue;
-			}
-		}
+	case kCountMethod:
+		assert(args.empty());
+		returnValue.setToFloat(size());
+		break;
 
-		// The item wasn't found.
-		return returnValue;
-	}
+	case kDeleteFirstMethod:
+		assert(args.empty());
+		returnValue = remove_at(0);
+		break;
 
-	case kCountMethod: {
-		double size = static_cast<double>(this->size());
-		returnValue.setToFloat(size);
-		return returnValue;
-	}
+	case kDeleteLastMethod:
+		assert(args.empty());
+		returnValue = remove_at(size() - 1);
+		break;
+
+	case kEmptyMethod:
+		assert(args.empty());
+		clear();
+		break;
 
 	case kGetAtMethod: {
 		assert(args.size() == 1);
 		uint index = static_cast<uint>(args[0].asFloat());
 		returnValue = operator[](index);
-		return returnValue;
+		break;
 	}
 
-	case kSendMethod: {
-		// Call a method on each item in the collection.
-		BuiltInMethod methodToSend = static_cast<BuiltInMethod>(args[0].asMethodId());
-		Common::Array<ScriptValue> sendArgs;
-		for (uint i = 0; i < size(); i++) {
-			ScriptValue self = operator[](i);
-
-			uint assetId = self.asAssetId();
-			Asset *selfAsset = g_engine->getAssetById(assetId);
-			if (selfAsset != nullptr) {
-				Common::Array<ScriptValue> emptyArgs;
-				returnValue = selfAsset->callMethod(methodToSend, emptyArgs);
-			}
-		}
-		return returnValue;
-	}
+	case kIsEmptyMethod:
+		assert(args.empty());
+		returnValue.setToBool(empty());
+		break;
+
+	case kJumbleMethod:
+		assert(args.empty());
+		jumble();
+		break;
 
 	case kSeekMethod: {
-		// Find the item in the collection if it exists.
 		assert(args.size() == 1);
-		for (uint i = 0; i < size(); i++) {
-			if (args[0] == operator[](i)) {
-				return operator[](i);
-			}
-		}
+		int index = seek(args[0]);
+		returnValue.setToFloat(index);
+		break;
+	}
+
+	case kSendMethod:
+		send(args);
+		break;
 
-		// The item wasn't found.
-		returnValue.setToFloat(-1.0);
-		return returnValue;
+	case kDeleteAtMethod: {
+		assert(args.size() == 1);
+		uint index = static_cast<uint>(args[0].asFloat());
+		returnValue = remove_at(index);
+		break;
 	}
 
-	case kJumbleMethod: {
-		// Scramble the items in the collection.
-		for (uint i = size() - 1; i > 0; --i) {
-			uint j = g_engine->_randomSource.getRandomNumber(size() - 1);
-			SWAP(operator[](i), operator[](j));
-		}
-		return returnValue;
+	case kInsertAtMethod: {
+		assert(args.size() == 2);
+		uint index = static_cast<uint>(args[1].asFloat());
+		insert_at(index, args[0]);
+		break;
 	}
 
-	case kSortMethod: {
+	case kReplaceAtMethod: {
+		assert(args.size() == 2);
+		uint index = static_cast<uint>(args[1].asFloat());
+		operator[](index) = args[0];
+		break;
+	}
+
+	case kPrependListMethod:
+		insert_at(0, args);
+		break;
+
+	case kSortMethod:
 		assert(args.empty());
 		Common::sort(begin(), end());
-		return returnValue;
+		break;
+
+	default:
+		error("Attempt to call unimplemented method %s (%d)", builtInMethodToStr(method), static_cast<uint>(method));
 	}
+	return returnValue;
+}
 
-	case kEmptyMethod: {
-		clear();
-		return returnValue;
+void Collection::apply(const Common::Array<ScriptValue> &args) {
+	// Calls a function with each element of the collection as the first arg.
+	Common::Array<ScriptValue> argsToApply = args;
+	uint functionId = args[0].asFunctionId();
+	for (const ScriptValue &item : *this) {
+		argsToApply[0] = item;
+		// TODO: Need to create and call FunctionManager.
+		warning("Applying function %d not implemented", functionId);
 	}
+}
 
-	default:
-		error("Collection::callMethod(): Attempt to call unimplemented method %s (%d)", builtInMethodToStr(method), static_cast<uint>(method));
+void Collection::send(const Common::Array<ScriptValue> &args) {
+	Common::Array<ScriptValue> argsToSend(args.size() - 1);
+	if (argsToSend.size() > 0) {
+		for (uint i = 1; i < args.size(); i++) {
+			argsToSend[i - 1] = args[i];
+		}
+	}
+
+	BuiltInMethod methodToSend = static_cast<BuiltInMethod>(args[0].asMethodId());
+	Common::Array<ScriptValue> sendArgs;
+	for (const ScriptValue &item : *this) {
+		uint assetId = item.asAssetId();
+		Asset *targetAsset = g_engine->getAssetById(assetId);
+		if (targetAsset != nullptr) {
+			targetAsset->callMethod(methodToSend, argsToSend);
+		}
+	}
+}
+
+int Collection::seek(const ScriptValue &item) {
+	// Search from back to front.
+	for (int i = size() - 1; i >= 0; i--) {
+		if (item == operator[](i)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+void Collection::jumble() {
+	for (uint i = size() - 1; i > 0; --i) {
+		uint j = g_engine->_randomSource.getRandomNumber(size() - 1);
+		SWAP(operator[](i), operator[](j));
 	}
 }
 
diff --git a/engines/mediastation/mediascript/collection.h b/engines/mediastation/mediascript/collection.h
index 632597fc03a..950b7b7f662 100644
--- a/engines/mediastation/mediascript/collection.h
+++ b/engines/mediastation/mediascript/collection.h
@@ -36,6 +36,12 @@ class ScriptValue;
 class Collection : public Common::Array<ScriptValue> {
 public:
 	ScriptValue callMethod(BuiltInMethod method, Common::Array<ScriptValue> &args);
+
+private:
+	void apply(const Common::Array<ScriptValue> &values);
+	void send(const Common::Array<ScriptValue> &values);
+	int seek(const ScriptValue &itemToFind);
+	void jumble();
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index f2ff59527e7..62616d2f6cd 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -207,28 +207,38 @@ const char *builtInMethodToStr(BuiltInMethod method) {
 		return "SetText";
 	case kSetMaximumTextLengthMethod:
 		return "SetMaximumTextLength";
-	case kIsEmptyMethod:
-		return "IsEmpty";
-	case kEmptyMethod:
-		return "Empty";
 	case kAppendMethod:
 		return "Append";
-	case kGetAtMethod:
-		return "GetAt";
+	case kApplyMethod:
+		return "Apply";
 	case kCountMethod:
 		return "Count";
-	case kSendMethod:
-		return "Send";
+	case kDeleteFirstMethod:
+		return "DeleteFirst";
+	case kDeleteLastMethod:
+		return "DeleteLast";
+	case kEmptyMethod:
+		return "Empty";
+	case kGetAtMethod:
+		return "GetAt";
+	case kIsEmptyMethod:
+		return "IsEmpty";
+	case kJumbleMethod:
+		return "Jumble";
 	case kSeekMethod:
 		return "Seek";
-	case kSortMethod:
-		return "Sort";
+	case kSendMethod:
+		return "Send";
 	case kDeleteAtMethod:
 		return "DeleteAt";
-	case kJumbleMethod:
-		return "Jumble";
-	case kDeleteFirstMethod:
-		return "DeleteFirst";
+	case kInsertAtMethod:
+		return "InsertAt";
+	case kReplaceAtMethod:
+		return "ReplaceAt";
+	case kPrependListMethod:
+		return "PrependList";
+	case kSortMethod:
+		return "Sort";
 	case kOpenLensMethod:
 		return "OpenLens";
 	case kCloseLensMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 7a0b94f0d40..edd8d86ce63 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -147,22 +147,23 @@ enum BuiltInMethod {
 	kSetMaximumTextLengthMethod = 293, // PARAM: 1
 
 	// COLLECTION METHODS.
-	// These aren't assets but arrays used in Media Script.
-	kIsEmptyMethod = 254, // PARAMS: 0
-	kEmptyMethod = 252, // PARAMS: 0
+	// These are arrays used in Media Script.
 	kAppendMethod = 247, // PARAMS: 1+
-	kGetAtMethod = 253, // PARAMS: 1
+	kApplyMethod = 248, // PARAMS: 1+
 	kCountMethod = 249, // PARAMS: 0
-	// Looks like this lets you call a method on all the items in a collection.
-	// Examples look like : var_7be1_collect_shapes.send(spatialHide);
-	kSendMethod = 257, // PARAMS: 1+. Looks like the first param is the function,
-	// Seeking seems to be finding the index where a certain item is.
-	// and the next params are any arguments you want to send.
+	kDeleteFirstMethod = 250, // PARAMS: 0
+	kDeleteLastMethod = 251, // PARAMS: 0
+	kEmptyMethod = 252, // PARAMS: 0
+	kGetAtMethod = 253, // PARAMS: 1
+	kIsEmptyMethod = 254, // PARAMS: 0
+	kJumbleMethod = 255, // PARAMS: 0
 	kSeekMethod = 256, // PARAMS: 1
-	kSortMethod = 266, // PARAMS: 0
+	kSendMethod = 257, // PARAMS: 1+
 	kDeleteAtMethod = 258, // PARAMS: 1
-	kJumbleMethod = 255, // PARAMS: 0
-	kDeleteFirstMethod = 250, // PARAMS: 0
+	kInsertAtMethod = 259, // PARAMS: 2
+	kReplaceAtMethod = 260, // PARAMS: 2
+	kPrependListMethod = 261, // PARAMS: 1+
+	kSortMethod = 266, // PARAMS: 0
 
 	// PRINTER METHODS.
 	kOpenLensMethod = 346, // PARAMS: 0
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index d7ff04710b4..bbfcef64734 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -99,6 +99,14 @@ ScriptValue::ScriptValue(ParameterReadStream *stream) {
 	}
 }
 
+void ScriptValue::setToFloat(uint i) {
+	setToFloat(static_cast<double>(i));
+}
+
+void ScriptValue::setToFloat(int i) {
+	setToFloat(static_cast<double>(i));
+}
+
 void ScriptValue::setToFloat(double d) {
 	_type = kScriptValueTypeFloat;
 	_u.d = d;
diff --git a/engines/mediastation/mediascript/scriptvalue.h b/engines/mediastation/mediascript/scriptvalue.h
index 75c70f6368c..9d7b7ae1485 100644
--- a/engines/mediastation/mediascript/scriptvalue.h
+++ b/engines/mediastation/mediascript/scriptvalue.h
@@ -40,8 +40,11 @@ public:
 
 	ScriptValueType getType() const { return _type; }
 
+	void setToFloat(uint i);
+	void setToFloat(int i);
 	void setToFloat(double d);
 	double asFloat() const;
+	int asIntFromFloat() const;
 
 	void setToBool(bool b);
 	bool asBool() const;


Commit: a5c12ca646c6ee7ebbeffe336c062557e2ab664d
    https://github.com/scummvm/scummvm/commit/a5c12ca646c6ee7ebbeffe336c062557e2ab664d
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:25:03-04:00

Commit Message:
MEDIASTATION: Make the screen asset actually an asset

Changed paths:
    engines/mediastation/context.cpp
    engines/mediastation/context.h
    engines/mediastation/mediastation.cpp


diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index b21843b1202..4857e95593d 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -363,7 +363,7 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
 
 		case kAssetTypeScreen:
 			asset = new Screen(header);
-			_screenAsset = header;
+			_screenAsset = asset;
 			break;
 
 		case kAssetTypeFont:
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index 649baa8e831..a7ed7e62853 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -62,9 +62,7 @@ public:
 	uint32 _subfileCount;
 	uint32 _fileSize;
 	Graphics::Palette *_palette = nullptr;
-	// TODO: Eliminate this screenAsset because the screen that this context
-	// represents is now an asset in itself.
-	AssetHeader *_screenAsset = nullptr;
+	Asset *_screenAsset = nullptr;
 
 	Asset *getAssetById(uint assetId);
 	Asset *getAssetByChunkReference(uint chunkReference);
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index aebaa7b6355..47c5cffb6f4 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -279,7 +279,7 @@ void MediaStationEngine::refreshActiveHotspot() {
 			hotspot->runEventHandlerIfExists(kMouseEnteredEvent);
 		} else {
 			// There is no hotspot, so set the default cursor for this screen instead.
-			setCursor(_currentContext->_screenAsset->_cursorResourceId);
+			setCursor(_currentContext->_screenAsset->getHeader()->_cursorResourceId);
 		}
 	}
 
@@ -417,15 +417,8 @@ ScriptValue MediaStationEngine::callMethod(BuiltInMethod methodId, Common::Array
 
 void MediaStationEngine::doBranchToScreen() {
 	if (_currentContext != nullptr) {
-		EventHandler *exitEvent = _currentContext->_screenAsset->_eventHandlers.getValOrDefault(kExitEvent);
-		if (exitEvent != nullptr) {
-			debugC(5, kDebugScript, "Executing context exit event handler");
-			exitEvent->execute(_currentContext->_screenAsset->_id);
-		} else {
-			debugC(5, kDebugScript, "No context exit event handler");
-		}
-
-		releaseContext(_currentContext->_screenAsset->_id);
+		_currentContext->_screenAsset->runEventHandlerIfExists(kExitEvent);
+		releaseContext(_currentContext->_screenAsset->getHeader()->_id);
 	}
 
 	Context *context = loadContext(_requestedScreenBranchId);
@@ -434,15 +427,7 @@ void MediaStationEngine::doBranchToScreen() {
 	_currentHotspot = nullptr;
 
 	if (context->_screenAsset != nullptr) {
-		// TODO: Make the screen an asset just like everything else so we can
-		// run event handlers with runEventHandlerIfExists.
-		EventHandler *entryEvent = context->_screenAsset->_eventHandlers.getValOrDefault(MediaStation::kEntryEvent);
-		if (entryEvent != nullptr) {
-			debugC(5, kDebugScript, "Executing context entry event handler");
-			entryEvent->execute(context->_screenAsset->_id);
-		} else {
-			debugC(5, kDebugScript, "No context entry event handler");
-		}
+		context->_screenAsset->runEventHandlerIfExists(kEntryEvent);
 	}
 
 	_requestedScreenBranchId = 0;


Commit: 06e1446d5e76bd394ea0c6e5bd35cab10c7e06fa
    https://github.com/scummvm/scummvm/commit/06e1446d5e76bd394ea0c6e5bd35cab10c7e06fa
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-05-10T17:25:03-04:00

Commit Message:
MEDIASTATION: Use one hashmap for event handlers

Changed paths:
    engines/mediastation/asset.cpp
    engines/mediastation/asset.h
    engines/mediastation/assetheader.cpp
    engines/mediastation/assetheader.h
    engines/mediastation/assets/sprite.cpp
    engines/mediastation/assets/timer.cpp
    engines/mediastation/mediascript/eventhandler.h
    engines/mediastation/mediastation.cpp


diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index a08f0a3d96e..24cb7a0e2e4 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -71,7 +71,8 @@ void Asset::processTimeEventHandlers() {
 
 	// TODO: Replace with a queue.
 	uint currentTime = g_system->getMillis();
-	for (EventHandler *timeEvent : _header->_timeHandlers) {
+	const Common::Array<EventHandler *> &_timeHandlers = _header->_eventHandlers.getValOrDefault(kTimerEvent);
+	for (EventHandler *timeEvent : _timeHandlers) {
  		// Indeed float, not time.
 		double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
 		uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
@@ -85,24 +86,29 @@ void Asset::processTimeEventHandlers() {
 	_lastProcessedTime = currentTime - _startTime;
 }
 
-void Asset::runEventHandlerIfExists(EventType eventType) {
-	EventHandler *eventHandler = _header->_eventHandlers.getValOrDefault(eventType);
-	if (eventHandler != nullptr) {
-		debugC(5, kDebugScript, "Executing handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
-		eventHandler->execute(_header->_id);
-	} else {
-		debugC(5, kDebugScript, "No event handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
+void Asset::runEventHandlerIfExists(EventType eventType, const ScriptValue &arg) {
+	const Common::Array<EventHandler *> &_eventHandlers = _header->_eventHandlers.getValOrDefault(eventType);
+	for (EventHandler *eventHandler : _eventHandlers) {
+		const ScriptValue &argToCheck = eventHandler->_argumentValue;
+
+		if (arg.getType() != argToCheck.getType()) {
+			warning("Got event handler arg type %s, expected %s",
+				scriptValueTypeToStr(arg.getType()), scriptValueTypeToStr(argToCheck.getType()));
+			continue;
+		}
+
+		if (arg == argToCheck) {
+			debugC(5, kDebugScript, "Executing handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
+			eventHandler->execute(_header->_id);
+			return;
+		}
 	}
+	debugC(5, kDebugScript, "No event handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
 }
 
-void Asset::runKeyDownEventHandlerIfExists(Common::KeyState keyState) {
-	EventHandler *keyDownEvent = _header->_keyDownHandlers.getValOrDefault(keyState.ascii);
-	if (keyDownEvent != nullptr) {
-		debugC(5, kDebugScript, "Executing keydown event handler for ASCII code %d on asset %d", keyState.ascii, _header->_id);
-		keyDownEvent->execute(_header->_id);
-	} else {
-		debugC(5, kDebugScript, "No keydown event handler for ASCII code %d on asset %d", keyState.ascii, _header->_id);
-	}
+void Asset::runEventHandlerIfExists(EventType eventType) {
+	ScriptValue scriptValue;
+	runEventHandlerIfExists(eventType, scriptValue);
 }
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 9d84f597ecb..98e63689450 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -66,8 +66,8 @@ public:
 	void setInactive();
 	void setActive();
 	void processTimeEventHandlers();
+	void runEventHandlerIfExists(EventType eventType, const ScriptValue &arg);
 	void runEventHandlerIfExists(EventType eventType);
-	void runKeyDownEventHandlerIfExists(Common::KeyState keyState);
 
 	AssetType type() const;
 	int zIndex() const;
diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
index 6e9d8f87185..8e741d44d41 100644
--- a/engines/mediastation/assetheader.cpp
+++ b/engines/mediastation/assetheader.cpp
@@ -41,30 +41,12 @@ AssetHeader::AssetHeader(Chunk &chunk) {
 
 AssetHeader::~AssetHeader() {
 	for (auto it = _eventHandlers.begin(); it != _eventHandlers.end(); ++it) {
-		delete it->_value;
+		for (EventHandler *eventHandler : it->_value) {
+			delete eventHandler;
+		}
 	}
 	_eventHandlers.clear();
 
-	for (EventHandler *timeHandler : _timeHandlers) {
-		delete timeHandler;
-	}
-	_timeHandlers.clear();
-
-	for (auto it = _keyDownHandlers.begin(); it != _keyDownHandlers.end(); ++it) {
-		delete it->_value;
-	}
-	_keyDownHandlers.clear();
-
-	for (EventHandler *inputHandler : _inputHandlers) {
-		delete inputHandler;
-	}
-	_inputHandlers.clear();
-
-	for (EventHandler *loadCompleteHandler : _loadCompleteHandlers) {
-		delete loadCompleteHandler;
-	}
-	_loadCompleteHandlers.clear();
-
 	delete _palette;
 	_palette = nullptr;
 }
@@ -78,46 +60,16 @@ void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk)
 
 	case kAssetHeaderEventHandler: {
 		EventHandler *eventHandler = new EventHandler(chunk);
-		switch (eventHandler->_type) {
-		case kTimerEvent: {
-			_timeHandlers.push_back(eventHandler);
-			break;
-		}
+		Common::Array<EventHandler *> &eventHandlersForType = _eventHandlers.getOrCreateVal(eventHandler->_type);
 
-		case kKeyDownEvent: {
-			if (eventHandler->_argumentValue.getType() != kScriptValueTypeFloat) {
-				error("Keydown event handler doesn't have correct argument type");
+		// This is not a hashmap because we don't want to have to hash ScriptValues.
+		for (EventHandler *existingEventHandler : eventHandlersForType) {
+			if (existingEventHandler->_argumentValue == eventHandler->_argumentValue) {
+				error("AssetHeader::readSection(): Event handler for %s (%s) already exists",
+					eventTypeToStr(eventHandler->_type), eventHandler->getDebugHeader().c_str());
 			}
-			uint asciiCode = static_cast<uint>(eventHandler->_argumentValue.asFloat());
-			_keyDownHandlers.setVal(asciiCode, eventHandler);
-			break;
-		}
-
-		case kInputEvent: {
-			_inputHandlers.push_back(eventHandler);
-			break;
-		}
-
-		case kLoadCompleteEvent: {
-			_loadCompleteHandlers.push_back(eventHandler);
-			break;
-		}
-
-		default: {
-			if (eventHandler->_argumentValue.getType() != kScriptValueTypeEmpty && \
-				eventHandler->_argumentValue.getType() != kScriptValueTypeParamToken) {
-				error("AssetHeader::readSection(): Event handler of type %s has a non-null argument type %s",
-					eventTypeToStr(eventHandler->_type), scriptValueTypeToStr(eventHandler->_argumentValue.getType()));
-			}
-
-			if (_eventHandlers.contains(eventHandler->_type)) {
-				error("AssetHeader::readSection(): Event handler type %s already exists", eventTypeToStr(eventHandler->_type));
-			} else {
-				_eventHandlers.setVal(eventHandler->_type, eventHandler);
-			}
-			break;
-		}
 		}
+		eventHandlersForType.push_back(eventHandler);
 		break;
 	}
 
diff --git a/engines/mediastation/assetheader.h b/engines/mediastation/assetheader.h
index 958f4d8d27f..af077b20a30 100644
--- a/engines/mediastation/assetheader.h
+++ b/engines/mediastation/assetheader.h
@@ -203,11 +203,7 @@ public:
 	uint _totalSteps = 0;
 
 	// EVENT HANDLER FIELDS.
-	Common::HashMap<uint, EventHandler *> _eventHandlers;
-	Common::Array<EventHandler *> _timeHandlers;
-	Common::HashMap<uint, EventHandler *> _keyDownHandlers;
-	Common::Array<EventHandler *> _inputHandlers;
-	Common::Array<EventHandler *> _loadCompleteHandlers;
+	Common::HashMap<uint, Common::Array<EventHandler *>> _eventHandlers;
 
 	// TEXT FIELDS.
 	Common::String _text;
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 9d6cd5530c7..ec2b9dd652e 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -306,7 +306,10 @@ void Sprite::updateFrameState() {
 		_currentFrameIndex = 0;
 		_nextFrameTime = 0;
 
-		runEventHandlerIfExists(kSpriteMovieEndEvent);
+		ScriptValue defaultSpriteClip;
+		const uint DEFAULT_SPRITE_CLIP_ID = 1200;
+		defaultSpriteClip.setToParamToken(DEFAULT_SPRITE_CLIP_ID);
+		runEventHandlerIfExists(kSpriteMovieEndEvent, defaultSpriteClip);
 	}
 }
 
diff --git a/engines/mediastation/assets/timer.cpp b/engines/mediastation/assets/timer.cpp
index 5f04ad08e01..38a9c0debc2 100644
--- a/engines/mediastation/assets/timer.cpp
+++ b/engines/mediastation/assets/timer.cpp
@@ -68,7 +68,8 @@ void Timer::timePlay() {
 	// TODO: Is there a better way to find out what the max time is? Do we have to look
 	// through each of the timer event handlers to figure it out?
 	_duration = 0;
-	for (EventHandler *timeEvent : _header->_timeHandlers) {
+	const Common::Array<EventHandler *> &_timeHandlers = _header->_eventHandlers.getValOrDefault(kTimerEvent);
+	for (EventHandler *timeEvent : _timeHandlers) {
 		// Indeed float, not time.
 		double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
 		uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
diff --git a/engines/mediastation/mediascript/eventhandler.h b/engines/mediastation/mediascript/eventhandler.h
index 00a6c352f4c..843a008e790 100644
--- a/engines/mediastation/mediascript/eventhandler.h
+++ b/engines/mediastation/mediascript/eventhandler.h
@@ -36,12 +36,11 @@ public:
 	~EventHandler();
 
 	ScriptValue execute(uint assetId);
+	Common::String getDebugHeader();
 	EventType _type;
 	ScriptValue _argumentValue;
 
 private:
-	Common::String getDebugHeader();
-
 	CodeChunk *_code = nullptr;
 };
 
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 47c5cffb6f4..5bfe0511b8f 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -227,7 +227,9 @@ void MediaStationEngine::processEvents() {
 			Asset *hotspot = findAssetToAcceptMouseEvents();
 			if (hotspot != nullptr) {
 				debugC(1, kDebugEvents, "EVENT_KEYDOWN (%d): Sent to hotspot %d", _event.kbd.ascii, hotspot->getHeader()->_id);
-				hotspot->runKeyDownEventHandlerIfExists(_event.kbd);
+				ScriptValue keyCode;
+				keyCode.setToFloat(_event.kbd.ascii);
+				hotspot->runEventHandlerIfExists(kKeyDownEvent, keyCode);
 			}
 			break;
 		}




More information about the Scummvm-git-logs mailing list