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

npjg noreply at scummvm.org
Sun Feb 23 02:20:15 UTC 2025


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

Summary:
361996787d MEDIASTATION: Refactor file/stream handling classes
3a927052fb MEDIASTATION: Add config option to start at arbitrary game screen
bde64478bf MEDIASTATION: JANITORIAL: Fix whitespace
7c0ab8b057 MEDIASTATION: Support assets that reference other assets' data
d3ecb2f91a MEDIASTATION: Fix movie persistence
f60689d434 MEDIASTATION: Fix audio memory leaks due to not freeing buffer


Commit: 361996787d2504fbd21c5b0e386735fecc1212f1
    https://github.com/scummvm/scummvm/commit/361996787d2504fbd21c5b0e386735fecc1212f1
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T20:09:19-05:00

Commit Message:
MEDIASTATION: Refactor file/stream handling classes

They are moved inside a single source unit, and the
rather messy implementation of Chunk is somewhat
cleaned up. Some hacks for chunk handling are left in
for now, but they should be addressed in the future.

Changed paths:
  R engines/mediastation/chunk.cpp
  R engines/mediastation/chunk.h
  R engines/mediastation/subfile.cpp
  R engines/mediastation/subfile.h
    engines/mediastation/asset.h
    engines/mediastation/assetheader.h
    engines/mediastation/assets/font.h
    engines/mediastation/assets/image.h
    engines/mediastation/assets/movie.h
    engines/mediastation/assets/sound.h
    engines/mediastation/assets/sprite.h
    engines/mediastation/bitmap.h
    engines/mediastation/boot.cpp
    engines/mediastation/boot.h
    engines/mediastation/context.cpp
    engines/mediastation/context.h
    engines/mediastation/contextparameters.h
    engines/mediastation/datafile.cpp
    engines/mediastation/datafile.h
    engines/mediastation/datum.h
    engines/mediastation/mediascript/codechunk.cpp
    engines/mediastation/mediascript/codechunk.h
    engines/mediastation/mediascript/eventhandler.h
    engines/mediastation/mediascript/function.h
    engines/mediastation/mediascript/variable.cpp
    engines/mediastation/mediascript/variable.h
    engines/mediastation/module.mk


diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index db318ceeba1..81380b3031a 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -25,8 +25,7 @@
 #include "common/keyboard.h"
 
 #include "mediastation/mediastation.h"
-#include "mediastation/subfile.h"
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/scriptconstants.h"
 #include "mediastation/mediascript/operand.h"
 #include "mediastation/assetheader.h"
diff --git a/engines/mediastation/assetheader.h b/engines/mediastation/assetheader.h
index f3fb3f652bd..858deba039e 100644
--- a/engines/mediastation/assetheader.h
+++ b/engines/mediastation/assetheader.h
@@ -27,7 +27,7 @@
 #include "common/hashmap.h"
 #include "graphics/palette.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/eventhandler.h"
 
 namespace MediaStation {
diff --git a/engines/mediastation/assets/font.h b/engines/mediastation/assets/font.h
index 2b084fd0bff..be258dd1ac9 100644
--- a/engines/mediastation/assets/font.h
+++ b/engines/mediastation/assets/font.h
@@ -25,7 +25,7 @@
 #include "mediastation/asset.h"
 #include "mediastation/assetheader.h"
 #include "mediastation/bitmap.h"
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/operand.h"
 #include "mediastation/mediascript/scriptconstants.h"
 
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index ddcc1966966..2f86993ebcd 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -23,7 +23,7 @@
 #define MEDIASTATION_IMAGE_H
 
 #include "mediastation/asset.h"
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/bitmap.h"
 #include "mediastation/assetheader.h"
 #include "mediastation/mediascript/operand.h"
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index a0ee439c1bb..d39cc56d6b9 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -25,8 +25,7 @@
 #include "common/array.h"
 #include "audio/audiostream.h"
 
-#include "mediastation/subfile.h"
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/assetheader.h"
 #include "mediastation/bitmap.h"
 #include "mediastation/mediascript/scriptconstants.h"
diff --git a/engines/mediastation/assets/sound.h b/engines/mediastation/assets/sound.h
index 16ec5416e02..6330128b594 100644
--- a/engines/mediastation/assets/sound.h
+++ b/engines/mediastation/assets/sound.h
@@ -25,8 +25,7 @@
 #include "audio/audiostream.h"
 
 #include "mediastation/asset.h"
-#include "mediastation/chunk.h"
-#include "mediastation/subfile.h"
+#include "mediastation/datafile.h"
 #include "mediastation/assetheader.h"
 #include "mediastation/mediascript/operand.h"
 #include "mediastation/mediascript/scriptconstants.h"
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index 8e2119c89f0..ff26a703756 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -27,7 +27,7 @@
 
 #include "mediastation/asset.h"
 #include "mediastation/assetheader.h"
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/bitmap.h"
 #include "mediastation/mediascript/operand.h"
 #include "mediastation/mediascript/scriptconstants.h"
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index dca3986e506..f6079fd0918 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -25,7 +25,7 @@
 #include "common/rect.h"
 #include "graphics/managed_surface.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/assetheader.h"
 
 namespace MediaStation {
diff --git a/engines/mediastation/boot.cpp b/engines/mediastation/boot.cpp
index 7d08bd2b84a..0f95a25389b 100644
--- a/engines/mediastation/boot.cpp
+++ b/engines/mediastation/boot.cpp
@@ -277,9 +277,8 @@ EngineResourceDeclaration::~EngineResourceDeclaration() {
 #pragma endregion
 
 #pragma region Boot
-Boot::Boot(const Common::Path &path) : Datafile(path) {
-	// OPEN THE FILE FOR READING.
-	subfile = Subfile(_stream);
+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
@@ -426,7 +425,7 @@ Boot::Boot(const Common::Path &path) : Datafile(path) {
 	}
 }
 
-BootSectionType Boot::getSectionType(Chunk& chunk) {
+BootSectionType Boot::getSectionType(Chunk &chunk) {
 	Datum datum = Datum(chunk, kDatumTypeUint16_1);
 	BootSectionType sectionType = static_cast<BootSectionType>(datum.u.i);
 	return sectionType;
diff --git a/engines/mediastation/boot.h b/engines/mediastation/boot.h
index e2b046269b8..bd9ff4f23b8 100644
--- a/engines/mediastation/boot.h
+++ b/engines/mediastation/boot.h
@@ -27,8 +27,6 @@
 #include "common/array.h"
 #include "common/hashmap.h"
 
-#include "mediastation/subfile.h"
-#include "mediastation/chunk.h"
 #include "mediastation/datafile.h"
 
 namespace MediaStation {
@@ -193,8 +191,6 @@ enum BootSectionType {
 
 class Boot : Datafile {
 private:
-	Subfile subfile;
-
 	BootSectionType getSectionType(Chunk &chunk);
 
 public:
diff --git a/engines/mediastation/chunk.cpp b/engines/mediastation/chunk.cpp
deleted file mode 100644
index d16cbc1eb48..00000000000
--- a/engines/mediastation/chunk.cpp
+++ /dev/null
@@ -1,39 +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/chunk.h"
-#include "mediastation/debugchannels.h"
-
-namespace MediaStation {
-
-Chunk::Chunk(Common::SeekableReadStream *stream) : _input(stream), _dataStartOffset(0), _dataEndOffset(0) {
-	// READ THE HEADER.
-	_id = _input->readUint32BE();
-	_length = _input->readUint32LE();
-	_dataStartOffset = pos();
-	_dataEndOffset = _dataStartOffset + _length;
-	debugC(5, kDebugLoading, "Chunk::Chunk(): Got chunk with ID \"%s\" and size 0x%x", tag2str(_id), _length);
-	if (_length == 0) {
-		error("Encountered a zero-length chunk. This usually indicates corrupted data - maybe a CD-ROM read error.");
-	}
-}
-
-} // End of namespace MediaStation
\ No newline at end of file
diff --git a/engines/mediastation/chunk.h b/engines/mediastation/chunk.h
deleted file mode 100644
index b553b049e18..00000000000
--- a/engines/mediastation/chunk.h
+++ /dev/null
@@ -1,94 +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_CHUNK_H
-#define MEDIASTATION_CHUNK_H
-
-#include "common/stream.h"
-#include "common/file.h"
-
-namespace MediaStation {
-
-class Chunk : public Common::SeekableReadStream {
-private:
-	Common::SeekableReadStream *_input = nullptr;
-	uint32 _dataStartOffset = 0;
-	uint32 _dataEndOffset = 0;
-
-public:
-	uint32 _id = 0;
-	uint32 _length = 0;
-
-	Chunk() = default;
-	Chunk(Common::SeekableReadStream *stream);
-
-	uint32 bytesRemaining() {
-		return _dataEndOffset - pos();
-	}
-
-	// ReadStream implementation
-	bool eos() const {
-		return _input->eos();
-	}
-	bool err() const {
-		return _input->err();
-	}
-	void clearErr() {
-		_input->clearErr();
-	}
-	uint32 read(void *dataPtr, uint32 dataSize) {
-		if (pos() > _dataEndOffset) {
-			uint overrun = pos() - _dataEndOffset;
-			error("Attempted to read 0x%x bytes at a location 0x%x bytes past end of chunk (@0x%llx)", dataSize, overrun, static_cast<long long int>(pos()));
-		} else {
-			return _input->read(dataPtr, dataSize);
-		}
-	}
-	int64 pos() const {
-		return _input->pos();
-	}
-	int64 size() const {
-		return _input->size();
-	}
-	bool seek(int64 offset, int whence = SEEK_SET) {
-		// TODO: This is a bad hack and should be cleaned up!
-		bool result = _input->seek(offset, whence);
-		if (result == false) {
-			return false;
-		}
-
-		if (pos() < _dataStartOffset) {
-			uint overrun = _dataStartOffset - offset;
-			error("Attempted to seek 0x%x bytes before start of chunk (@0x%llx)", overrun, static_cast<long long int>(pos()));
-		} else if (pos() > _dataEndOffset) {
-			uint overrun = offset - _dataEndOffset;
-			error("Attempted to seek 0x%x bytes past end of chunk (@0x%llx)", overrun, static_cast<long long int>(pos()));
-		}
-		return true;
-	}
-	bool skip(uint32 offset) {
-		return seek(offset, SEEK_CUR);
-	}
-};
-
-} // End of namespace MediaStation
-
-#endif
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index f03b0a20470..36c616a474d 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -40,21 +40,26 @@
 
 namespace MediaStation {
 
-Context::Context(const Common::Path &path) :
-	Datafile(path) {
-	// This stuff isn't part of any graphics palette.
-	readPreamble();
+Context::Context(const Common::Path &path) : Datafile(path) {
+	uint32 signature = _handle->readUint32BE();
+	if (signature != MKTAG('I', 'I', '\0', '\0')) {
+		error("Context::Context(): Wrong signature for file %s: 0x%08x", _name.c_str(), signature);
+	}
+
+	_unk1 = _handle->readUint32LE();
+	_subfileCount = _handle->readUint32LE();
+	_fileSize = _handle->readUint32LE();
+	debugC(5, kDebugLoading, "Context::Context(): _unk1 = 0x%x", _unk1);
 
-	// READ THE FIRST SUBFILE.
-	Subfile subfile = Subfile(_stream);
+	Subfile subfile = getNextSubfile();
 	Chunk chunk = subfile.nextChunk();
-	// First, read the header sections.
+
 	if (g_engine->isFirstGenerationEngine()) {
 		readOldStyleHeaderSections(subfile, chunk);
 	} else {
 		readNewStyleHeaderSections(subfile, chunk);
 	}
-	// Then, read any asset data.
+
 	chunk = subfile._currentChunk;
 	while (!subfile.atEnd()) {
 		readAssetInFirstSubfile(chunk);
@@ -63,9 +68,9 @@ Context::Context(const Common::Path &path) :
 		}
 	}
 
-	// Then, assets in the rest of the subfiles.
+	// Read assets in the rest of the subfiles.
 	for (uint i = 1; i < _subfileCount; i++) {
-		subfile = Subfile(_stream);
+		subfile = getNextSubfile();
 		readAssetFromLaterSubfile(subfile);
 	}
 }
@@ -111,31 +116,11 @@ void Context::registerActiveAssets() {
 	}
 }
 
-bool Context::readPreamble() {
-	uint16 signature = _stream->readUint16LE();
-	if (signature != 0x4949) { // "II"
-		warning("Datafile::openFile(): Wrong signature for file %s. Got 0x%04X", _path.toString(Common::Path::kNativeSeparator).c_str(), signature);
-		close();
-		return false;
-	}
-	_stream->skip(2); // 0x00 0x00
-
-	_unk1 = _stream->readUint32LE();
-	debugC(5, kDebugLoading, "Context::openFile(): _unk1 = 0x%x", _unk1);
-
-	_subfileCount = _stream->readUint32LE();
-	// The total size of this file, including this header.
-	// (Basically the true file size shown on the filesystem.)
-	_fileSize = _stream->readUint32LE();
-	return true;
-}
-
 void Context::readOldStyleHeaderSections(Subfile &subfile, Chunk &chunk) {
 	error("Context::readOldStyleHeaderSections(): Not implemented yet");
 }
 
 void Context::readNewStyleHeaderSections(Subfile &subfile, Chunk &chunk) {
-	// READ THE PALETTE.
 	bool moreSectionsToRead = (chunk._id == MKTAG('i', 'g', 'o', 'd'));
 	if (!moreSectionsToRead) {
 		warning("Context::readNewStyleHeaderSections(): Got no header sections (@0x%llx)", static_cast<long long int>(chunk.pos()));
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index 4738cacdb81..329d4a24366 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -22,9 +22,9 @@
 #ifndef MEDIASTATION_CONTEXT_H
 #define MEDIASTATION_CONTEXT_H
 
-#include "graphics/palette.h"
 #include "common/path.h"
 #include "common/hashmap.h"
+#include "graphics/palette.h"
 
 #include "mediastation/datafile.h"
 #include "mediastation/contextparameters.h"
@@ -45,13 +45,11 @@ enum ContextSectionType {
 	kContextFunctionSection = 0x0031
 };
 
-class Context : Datafile {
+class Context : public Datafile {
 public:
 	Context(const Common::Path &path);
 	~Context();
 
-	bool readPreamble();
-
 	uint32 _unk1;
 	uint32 _subfileCount;
 	uint32 _fileSize;
diff --git a/engines/mediastation/contextparameters.h b/engines/mediastation/contextparameters.h
index 034970b1ada..6e1c8f9606d 100644
--- a/engines/mediastation/contextparameters.h
+++ b/engines/mediastation/contextparameters.h
@@ -25,7 +25,7 @@
 #include "common/str.h"
 #include "common/hashmap.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/variable.h"
 #include "mediastation/mediascript/function.h"
 
diff --git a/engines/mediastation/datafile.cpp b/engines/mediastation/datafile.cpp
index 132e7d834bf..79c35db5fec 100644
--- a/engines/mediastation/datafile.cpp
+++ b/engines/mediastation/datafile.cpp
@@ -19,37 +19,96 @@
  *
  */
 
-#include "common/file.h"
-
-#include "mediastation/mediastation.h"
 #include "mediastation/datafile.h"
+#include "mediastation/debugchannels.h"
 
 namespace MediaStation {
 
-Datafile::Datafile(const Common::Path &path) {
-	openFile(path);
+Chunk::Chunk(Common::SeekableReadStream *stream) : _parentStream(stream) {
+	_id = _parentStream->readUint32BE();
+	_length = _parentStream->readUint32LE();
+	_dataStartOffset = pos();
+	_dataEndOffset = _dataStartOffset + _length;
+	debugC(5, kDebugLoading, "Chunk::Chunk(): Got chunk with ID \"%s\" and size 0x%x", tag2str(_id), _length);
+	if (_length == 0)
+		error("Encountered a zero-length chunk. This usually indicates corrupted data - maybe a CD-ROM read error.");
 }
 
-Datafile::~Datafile() {
-	close();
+uint32 Chunk::bytesRemaining() {
+	return _dataEndOffset - pos();
 }
 
-bool Datafile::openFile(const Common::Path &path) {
-	Common::File *file = new Common::File();
-	if (path.empty() || !file->open(path)) {
-		error("Datafile::openFile(): Error opening file %s", path.toString(Common::Path::kNativeSeparator).c_str());
-		delete file;
-		return false;
+uint32 Chunk::read(void *dataPtr, uint32 dataSize) {
+	if (pos() > _dataEndOffset) {
+		uint overrun = pos() - _dataEndOffset;
+		error("Attempted to read 0x%x bytes at a location 0x%x bytes past end of chunk (@0x%llx)", dataSize, overrun, static_cast<long long int>(pos()));
+	} else {
+		return _parentStream->read(dataPtr, dataSize);
 	}
+}
+
+bool Chunk::seek(int64 offset, int whence) {
+	bool result = _parentStream->seek(offset, whence);
+	if (result == false)
+		return false;
 
-	_path = path;
-	_stream = file;
+	if (pos() < _dataStartOffset) {
+		uint overrun = _dataStartOffset - offset;
+		error("Attempted to seek 0x%x bytes before start of chunk (@0x%llx)", overrun, static_cast<long long int>(pos()));
+	} else if (pos() > _dataEndOffset) {
+		uint overrun = offset - _dataEndOffset;
+		error("Attempted to seek 0x%x bytes past end of chunk (@0x%llx)", overrun, static_cast<long long int>(pos()));
+	}
 	return true;
 }
 
-void Datafile::close() {
-	delete _stream;
-	_stream = nullptr;
+Subfile::Subfile(Common::SeekableReadStream *stream) : _stream(stream) {
+	// VERIFY FILE SIGNATURE.
+	debugC(5, kDebugLoading, "\n*** Subfile::Subfile(): Got new subfile (@0x%llx) ***", static_cast<long long int>(_stream->pos()));
+	_rootChunk = nextChunk();
+	if (_rootChunk._id != MKTAG('R', 'I', 'F', 'F'))
+		error("Subfile::Subfile(): Expected \"RIFF\" chunk, got %s (@0x%llx)", tag2str(_rootChunk._id), static_cast<long long int>(_stream->pos()));
+	_stream->skip(4); // IMTS
+
+	// READ RATE CHUNK.
+	// This chunk should  always contain just one piece of data,
+	// the "rate" (whatever that is). Usually it is zero.
+	// TODO: Figure out what this actually is.
+	Chunk rateChunk = nextChunk();
+	if (rateChunk._id != MKTAG('r', 'a', 't', 'e'))
+		error("Subfile::Subfile(): Expected \"rate\" chunk, got %s (@0x%llx)", tag2str(_rootChunk._id), static_cast<long long int>(_stream->pos()));
+	_rate = _stream->readUint32LE();
+
+	// READ PAST LIST CHUNK.
+	nextChunk();
+
+	// QUEUE UP THE FIRST DATA CHUNK.
+	if (_stream->readUint32BE() != MKTAG('d', 'a', 't', 'a'))
+		error("Subfile::Subfile(): Expected \"data\" as first bytes of subfile, got %s @0x%llx)", tag2str(rateChunk._id), static_cast<long long int>(_stream->pos()));
+}
+
+Chunk Subfile::nextChunk() {
+	// Chunks always start on even-indexed bytes.
+	if (_stream->pos() & 1)
+		_stream->skip(1);
+
+	_currentChunk = Chunk(_stream);
+	return _currentChunk;
+}
+
+bool Subfile::atEnd() {
+	// TODO: Is this the best place to put this and approach to use?
+	return _rootChunk.bytesRemaining() == 0;
+}
+
+Datafile::Datafile(const Common::Path &path) {
+	if (!open(path)) {
+		error("Datafile::Datafile(): Failed to open %s", path.toString().c_str());
+	}
+}
+
+Subfile Datafile::getNextSubfile() {
+	return Subfile(_handle);
 }
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/datafile.h b/engines/mediastation/datafile.h
index d4e97b2df7e..98c7f6a2424 100644
--- a/engines/mediastation/datafile.h
+++ b/engines/mediastation/datafile.h
@@ -22,22 +22,67 @@
 #ifndef MEDIASTATION_DATAFILE_H
 #define MEDIASTATION_DATAFILE_H
 
-#include "common/path.h"
+#include "common/file.h"
 #include "common/stream.h"
+#include "common/path.h"
 
 namespace MediaStation {
 
-class Datafile {
+// 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
+// file. These chunks have special IDs:
+//  - igod: Indicates a chunk that contains metadata about asset(s) in metadata sections.
+//  - 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 {
 public:
-	Datafile(const Common::Path &path);
-	virtual ~Datafile();
+	Chunk() = default;
+	Chunk(Common::SeekableReadStream *stream);
+
+	uint32 bytesRemaining();
 
-	virtual bool openFile(const Common::Path &path);
-	virtual void close();
+	uint32 _id = 0;
+	uint32 _length = 0;
 
-protected:
-	Common::Path _path;
+	// ReadStream implementation
+	virtual bool eos() const { return _parentStream->eos(); };
+	virtual bool err() const {return _parentStream->err(); };
+	virtual void clearErr() { _parentStream->clearErr(); };
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+	virtual int64 pos() const { return _parentStream->pos(); };
+	virtual int64 size() const { return _parentStream->size(); };
+	virtual bool skip(uint32 offset) { return seek(offset, SEEK_CUR); };
+	virtual bool seek(int64 offset, int whence = SEEK_SET);
+
+private:
+	Common::SeekableReadStream *_parentStream = nullptr;
+	uint32 _dataStartOffset = 0;
+	uint32 _dataEndOffset = 0;
+};
+
+class Subfile {
+public:
+	Subfile() = default;
+	Subfile(Common::SeekableReadStream *stream);
+
+	Chunk nextChunk();
+	bool atEnd();
+
+	Chunk _currentChunk;
+	uint32 _rate;
+
+private:
 	Common::SeekableReadStream *_stream = nullptr;
+	Chunk _rootChunk;
+};
+
+class Datafile : public Common::File {
+public:
+	Datafile(const Common::Path &path);
+
+	Subfile getNextSubfile();
 };
 
 } // End of namespace MediaStation
diff --git a/engines/mediastation/datum.h b/engines/mediastation/datum.h
index ba7bed3f7d5..55dc6d025f1 100644
--- a/engines/mediastation/datum.h
+++ b/engines/mediastation/datum.h
@@ -27,7 +27,7 @@
 #include "common/rect.h"
 #include "common/stream.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 
 namespace MediaStation {
 
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 8247b5e05f2..2b7689a68a0 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -22,7 +22,6 @@
 #include "mediastation/mediastation.h"
 #include "mediastation/mediascript/codechunk.h"
 #include "mediastation/datum.h"
-#include "mediastation/chunk.h"
 #include "mediastation/debugchannels.h"
 
 #include "mediastation/assets/movie.h"
diff --git a/engines/mediastation/mediascript/codechunk.h b/engines/mediastation/mediascript/codechunk.h
index 6a4c4be5256..b9b8155842e 100644
--- a/engines/mediastation/mediascript/codechunk.h
+++ b/engines/mediastation/mediascript/codechunk.h
@@ -25,6 +25,7 @@
 #include "common/array.h"
 #include "common/stream.h"
 
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/variable.h"
 #include "mediastation/mediascript/operand.h"
 #include "mediastation/mediascript/scriptconstants.h"
diff --git a/engines/mediastation/mediascript/eventhandler.h b/engines/mediastation/mediascript/eventhandler.h
index 3b2f9004c70..cbabe23f3d5 100644
--- a/engines/mediastation/mediascript/eventhandler.h
+++ b/engines/mediastation/mediascript/eventhandler.h
@@ -22,7 +22,7 @@
 #ifndef MEDIASTATION_MEDIASCRIPT_EVENTHANDLER_H
 #define MEDIASTATION_MEDIASCRIPT_EVENTHANDLER_H
 
-#include "mediastation/chunk.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.h b/engines/mediastation/mediascript/function.h
index f91ddd5390b..436f6968d2f 100644
--- a/engines/mediastation/mediascript/function.h
+++ b/engines/mediastation/mediascript/function.h
@@ -24,7 +24,7 @@
 
 #include "common/array.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/mediascript/codechunk.h"
 
 namespace MediaStation {
diff --git a/engines/mediastation/mediascript/variable.cpp b/engines/mediastation/mediascript/variable.cpp
index ceeca43aeb2..d9f31503aa9 100644
--- a/engines/mediastation/mediascript/variable.cpp
+++ b/engines/mediastation/mediascript/variable.cpp
@@ -20,9 +20,7 @@
  */
 
 #include "mediastation/mediascript/variable.h"
-#include "mediastation/chunk.h"
 #include "mediastation/datum.h"
-#include "mediastation/datafile.h"
 #include "mediastation/debugchannels.h"
 #include "mediastation/mediascript/operand.h"
 
@@ -61,7 +59,7 @@ Variable::Variable(Chunk &chunk, bool readId) {
 		_id = Datum(chunk).u.i;
 	}
 	_type = static_cast<VariableType>(Datum(chunk).u.i);
-	debugC(1, kDebugLoading, "Variable::Variable(): id = %d, type %s (%d) (@0x%llx)", 
+	debugC(1, kDebugLoading, "Variable::Variable(): id = %d, type %s (%d) (@0x%llx)",
 		_id, variableTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
 	switch ((VariableType)_type) {
 	case kVariableTypeCollection: {
@@ -141,7 +139,7 @@ Variable::~Variable() {
 	}
 }
 
-Operand Variable::getValue() {	
+Operand Variable::getValue() {
 	switch (_type) {
 	case kVariableTypeEmpty: {
 		error("Variable::getValue(): Attempt to get value from an empty variable");
diff --git a/engines/mediastation/mediascript/variable.h b/engines/mediastation/mediascript/variable.h
index 5cc3bcbe373..f11e5367b33 100644
--- a/engines/mediastation/mediascript/variable.h
+++ b/engines/mediastation/mediascript/variable.h
@@ -25,7 +25,7 @@
 #include "common/str.h"
 #include "common/array.h"
 
-#include "mediastation/chunk.h"
+#include "mediastation/datafile.h"
 #include "mediastation/datum.h"
 #include "mediastation/mediascript/scriptconstants.h"
 
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index 69231ed7c1a..02f9ea68478 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -17,7 +17,6 @@ MODULE_OBJS = \
 	assets/timer.o \
 	bitmap.o \
 	boot.o \
-	chunk.o \
 	context.o \
 	contextparameters.o \
 	cursors.o \
@@ -31,7 +30,6 @@ MODULE_OBJS = \
 	mediascript/variable.o \
 	mediastation.o \
 	metaengine.o \
-	subfile.o \
 	transitions.o
 
 # This module can be built as a plugin
diff --git a/engines/mediastation/subfile.cpp b/engines/mediastation/subfile.cpp
deleted file mode 100644
index 49d562ee140..00000000000
--- a/engines/mediastation/subfile.cpp
+++ /dev/null
@@ -1,68 +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/subfile.h"
-#include "mediastation/debugchannels.h"
-
-namespace MediaStation {
-
-Subfile::Subfile() : _stream(nullptr) {}
-
-Subfile::Subfile(Common::SeekableReadStream *stream) : _stream(stream) {
-	// VERIFY FILE SIGNATURE.
-	debugC(5, kDebugLoading, "\n*** Subfile::Subfile(): Got new subfile (@0x%llx) ***", static_cast<long long int>(_stream->pos()));
-	_rootChunk = nextChunk();
-	if (_rootChunk._id != MKTAG('R', 'I', 'F', 'F'))
-		// TODO: These need to be interpreted as ASCII.
-		error("Subfile::Subfile(): Expected \"RIFF\" chunk, got %s (@0x%llx)", tag2str(_rootChunk._id), static_cast<long long int>(_stream->pos()));
-	_stream->skip(4); // IMTS
-
-	// READ RATE CHUNK.
-	// This chunk shoudl always contain just one piece of data - the "rate"
-	// (whatever that is). Usually it is zero.
-	// TODO: Figure out what this actually is.
-	Chunk rateChunk = nextChunk();
-	if (rateChunk._id != MKTAG('r', 'a', 't', 'e'))
-		error("Subfile::Subfile(): Expected \"rate\" chunk, got %s (@0x%llx)", tag2str(_rootChunk._id), static_cast<long long int>(_stream->pos()));
-	_rate = _stream->readUint32LE();
-
-	// READ PAST LIST CHUNK.
-	nextChunk();
-
-	// QUEUE UP THE FIRST DATA CHUNK.
-	if (_stream->readUint32BE() != MKTAG('d', 'a', 't', 'a'))
-		error("Subfile::Subfile(): Expected \"data\" as first bytes of subfile, got %s @0x%llx)", tag2str(rateChunk._id), static_cast<long long int>(_stream->pos()));
-}
-
-Chunk Subfile::nextChunk() {
-	// Chunks always start on even-indexed bytes.
-	if (_stream->pos() & 1)
-		_stream->skip(1);
-	_currentChunk = Chunk(_stream);
-	return _currentChunk;
-}
-
-bool Subfile::atEnd() {
-	// TODO: Is this the best place to put this and approach to use?
-	return _rootChunk.bytesRemaining() == 0;
-}
-
-} // End of namespace MediaStation
diff --git a/engines/mediastation/subfile.h b/engines/mediastation/subfile.h
deleted file mode 100644
index 5bfbbe369fd..00000000000
--- a/engines/mediastation/subfile.h
+++ /dev/null
@@ -1,51 +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_SUBFILE_H
-#define MEDIASTATION_SUBFILE_H
-
-#include "common/stream.h"
-
-#include "mediastation/chunk.h"
-
-namespace MediaStation {
-
-class Subfile {
-public:
-	Chunk _rootChunk;
-	Chunk _currentChunk;
-
-	Subfile();
-	Subfile(Common::SeekableReadStream *stream);
-
-	Chunk nextChunk();
-	bool atEnd();
-
-	uint32 _rate;
-
-private:
-	Common::SeekableReadStream *_stream;
-
-};
-
-} // End of namespace MediaStation
-
-#endif
\ No newline at end of file


Commit: 3a927052fb73f58e03a4318d7990d2b75689d5da
    https://github.com/scummvm/scummvm/commit/3a927052fb73f58e03a4318d7990d2b75689d5da
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T20:09:19-05:00

Commit Message:
MEDIASTATION: Add config option to start at arbitrary game screen

Changed paths:
    engines/mediastation/mediastation.cpp


diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index f9eff7099db..5de93447457 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -146,7 +146,15 @@ Common::Error MediaStationEngine::run() {
 	}
 	_cursor->showCursor();
 
-	_requestedScreenBranchId = _boot->_entryContextId;
+    if (ConfMan.hasKey("entry_context")) {
+		// For development purposes, we can choose to start at an arbitrary context
+		// in this title. This might not work in all cases.
+        uint entryContextId = ConfMan.get("entry_context").asUint64();
+        warning("Starting at user-requested context %d", entryContextId);
+		_requestedScreenBranchId = entryContextId;
+	} else {
+		_requestedScreenBranchId = _boot->_entryContextId;
+	}
 	doBranchToScreen();
 
 	while (true) {


Commit: bde64478bf1a63b6fa4897a91ce90757b21d5e22
    https://github.com/scummvm/scummvm/commit/bde64478bf1a63b6fa4897a91ce90757b21d5e22
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T21:05:25-05:00

Commit Message:
MEDIASTATION: JANITORIAL: Fix whitespace

Changed paths:
    engines/mediastation/assetheader.cpp
    engines/mediastation/assets/movie.cpp
    engines/mediastation/assets/screen.h
    engines/mediastation/assets/sprite.cpp
    engines/mediastation/datum.cpp
    engines/mediastation/mediascript/codechunk.cpp
    engines/mediastation/mediascript/eventhandler.cpp
    engines/mediastation/mediascript/operand.cpp
    engines/mediastation/mediascript/scriptconstants.cpp


diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
index ad739adaa6b..f653888b621 100644
--- a/engines/mediastation/assetheader.cpp
+++ b/engines/mediastation/assetheader.cpp
@@ -82,7 +82,7 @@ AssetHeader::~AssetHeader() {
 
 	delete _name;
 	_name = nullptr;
-	
+
 	delete _startPoint;
 	_startPoint = nullptr;
 
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 5e0443437d6..7faff972b49 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -64,7 +64,7 @@ MovieFrameFooter::MovieFrameFooter(Chunk &chunk) {
 		_index = Datum(chunk).u.i;
 		_keyframeIndex = Datum(chunk).u.i;
 		_unk9 = Datum(chunk).u.i;
-		debugC(5, kDebugLoading, "MovieFrameFooter::MovieFrameFooter(): _startInMilliseconds = %d, _endInMilliseconds = %d, _left = %d, _top = %d, _index = %d, _keyframeIndex = %d (@0x%llx)", 
+		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",
 			_zIndex, _diffBetweenKeyframeAndFrameX, _diffBetweenKeyframeAndFrameY, _unk4,_unk9);
@@ -345,7 +345,7 @@ void Movie::updateFrameState() {
 	if (!_isPlaying) {
 		debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _header->_id);
 		for (MovieFrame *frame : _framesOnScreen) {
-			debugC(6, kDebugGraphics, "   PERSIST: Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", 
+			debugC(6, kDebugGraphics, "   PERSIST: Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d",
 				frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
 		}
 		return;
diff --git a/engines/mediastation/assets/screen.h b/engines/mediastation/assets/screen.h
index 39d66233f0d..e51fd5c5cf3 100644
--- a/engines/mediastation/assets/screen.h
+++ b/engines/mediastation/assets/screen.h
@@ -29,7 +29,7 @@
 
 namespace MediaStation {
 
-// A Screen holds asset data and processes event handlers for a Context. 
+// A Screen holds asset data and processes event handlers for a Context.
 // The original separated them this way - there is a ContextParameters section,
 // then a Screen asset header.
 class Screen : public Asset {
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index e29c3aa2252..9e2eebf184b 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -237,7 +237,7 @@ void Sprite::updateFrameState() {
 
 	if (!_isPlaying) {
 		if (_activeFrame != nullptr) {
-			debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)", 
+			debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)",
 				_header->_id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
 		} else {
 			debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing, no persistent frame", _header->_id);
@@ -296,7 +296,7 @@ void Sprite::showFrame(SpriteFrame *frame) {
 	if (_activeFrame != nullptr) {
 		g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
 	}
-	
+
 	// Show the next frame.
 	_activeFrame = frame;
 	if (frame != nullptr) {
diff --git a/engines/mediastation/datum.cpp b/engines/mediastation/datum.cpp
index 56f6381ca87..9dd46ad28f2 100644
--- a/engines/mediastation/datum.cpp
+++ b/engines/mediastation/datum.cpp
@@ -74,15 +74,15 @@ void Datum::readWithType(Common::SeekableReadStream &chunk) {
 		u.point = new Common::Point(x, y);
 
 	} else if (kDatumTypeBoundingBox == t) {
-		Common::Point *left_top = Datum(chunk, kDatumTypePoint2).u.point;
+		Common::Point *leftTop = Datum(chunk, kDatumTypePoint2).u.point;
 		Common::Point *dimensions = Datum(chunk, kDatumTypePoint1).u.point;
-		u.bbox = new Common::Rect(*left_top, dimensions->x, dimensions->y);
-		delete left_top;
+		u.bbox = new Common::Rect(*leftTop, dimensions->x, dimensions->y);
+		delete leftTop;
 		delete dimensions;
 
 	} else if (kDatumTypePolygon == t) {
-		uint16 total_points = Datum(chunk, kDatumTypeUint16_1).u.i;
-		for (int i = 0; i < total_points; i++) {
+		uint16 totalPoints = Datum(chunk, kDatumTypeUint16_1).u.i;
+		for (int i = 0; i < totalPoints; i++) {
 			Common::Point *point = Datum(chunk, kDatumTypePoint1).u.point;
 			u.polygon->push_back(point);
 		}
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 2b7689a68a0..d00bc999220 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -106,7 +106,7 @@ Operand CodeChunk::executeNextStatement() {
 		case kOpcodeCallMethod: {
 			// In Media Station, all methods seem be built-in - there don't
 			// seem to be custom objects or methods individual titles can
-			// define. Functions, however, CAN be title-defined. 
+			// define. Functions, however, CAN be title-defined.
 			// But here, we're only looking for built-in methods.
 			BuiltInMethod methodId = static_cast<BuiltInMethod>(Datum(*_bytecode).u.i);
 			uint32 parameterCount = Datum(*_bytecode).u.i;
@@ -492,7 +492,7 @@ Operand CodeChunk::callBuiltInMethod(BuiltInMethod method, Operand self, Common:
 	}
 
 	default:
-		error("CodeChunk::callBuiltInMethod(): Attempt to call method on unsupported operand type %s (%d)", 
+		error("CodeChunk::callBuiltInMethod(): Attempt to call method on unsupported operand type %s (%d)",
 			operandTypeToStr(literalType), static_cast<uint>(literalType));
 	}
 }
diff --git a/engines/mediastation/mediascript/eventhandler.cpp b/engines/mediastation/mediascript/eventhandler.cpp
index bf12ed3949b..edde1caebab 100644
--- a/engines/mediastation/mediascript/eventhandler.cpp
+++ b/engines/mediastation/mediascript/eventhandler.cpp
@@ -26,7 +26,7 @@ namespace MediaStation {
 
 EventHandler::EventHandler(Chunk &chunk) {
 	_type = static_cast<EventType>(Datum(chunk).u.i);
-	debugC(5, kDebugLoading, "EventHandler::EventHandler(): Type %s (%d) (@0x%llx)", 
+	debugC(5, kDebugLoading, "EventHandler::EventHandler(): Type %s (%d) (@0x%llx)",
 		eventTypeToStr(_type), static_cast<uint>(_type), static_cast<long long int>(chunk.pos()));
 
 	_argumentType = static_cast<EventHandlerArgumentType>(Datum(chunk).u.i);
diff --git a/engines/mediastation/mediascript/operand.cpp b/engines/mediastation/mediascript/operand.cpp
index d6e09f12ff5..24e8eeb4573 100644
--- a/engines/mediastation/mediascript/operand.cpp
+++ b/engines/mediastation/mediascript/operand.cpp
@@ -40,7 +40,7 @@ void Operand::putInteger(int i) {
 	}
 
 	default: {
-		error("Operand::putInteger(): Attempt to put integer into operand type %s (%d)", 
+		error("Operand::putInteger(): Attempt to put integer into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -80,7 +80,7 @@ void Operand::putDouble(double d) {
 	}
 
 	default: {
-		error("Operand::putDouble(): Attempt to put double into operand type %s (%d)", 
+		error("Operand::putDouble(): Attempt to put double into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -119,7 +119,7 @@ void Operand::putString(Common::String *string) {
 	}
 
 	default: {
-		error("Operand::putString(): Attempt to put string into operand type %s (%d)", 
+		error("Operand::putString(): Attempt to put string into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -151,7 +151,7 @@ void Operand::putVariable(Variable *variable) {
 	}
 
 	default: {
-		error("Operand::putVariable(): Attempt to put variable into operand type %s (%d)", 
+		error("Operand::putVariable(): Attempt to put variable into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -178,7 +178,7 @@ void Operand::putFunction(uint functionId) {
 	}
 
 	default: {
-		error("Operand::putFunction(): Attempt to put function ID into operand type %s (%d)", 
+		error("Operand::putFunction(): Attempt to put function ID into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -211,7 +211,7 @@ void Operand::putAsset(uint32 assetId) {
 	}
 
 	default: {
-		error("Operand::putAsset(): Attempt to put asset ID into operand type %s (%d)", 
+		error("Operand::putAsset(): Attempt to put asset ID into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -271,7 +271,7 @@ void Operand::putCollection(Collection *collection) {
 	}
 
 	default: {
-		error("Operand::putCollection(): Attempt to put collection into operand type %s (%d)", 
+		error("Operand::putCollection(): Attempt to put collection into operand type %s (%d)",
 			operandTypeToStr(_type), static_cast<uint>(_type));
 	}
 	}
@@ -309,12 +309,12 @@ Operand Operand::getLiteralValue() const {
 bool Operand::operator==(const Operand &other) const {
 	Operand lhs = getLiteralValue();
 	Operand rhs = other.getLiteralValue();
-	// TODO: Maybe some better type checking here. If the types being compared 
-	// end up being incompatible, the respective get method on the rhs will 
-	// raise the error. But better might be checking both before we try getting 
+	// TODO: Maybe some better type checking here. If the types being compared
+	// end up being incompatible, the respective get method on the rhs will
+	// raise the error. But better might be checking both before we try getting
 	// values to report a more descriptive error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return lhs.getInteger() == rhs.getInteger();
 
@@ -339,7 +339,7 @@ bool Operand::operator<(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return lhs.getInteger() < rhs.getInteger();
 
@@ -358,7 +358,7 @@ bool Operand::operator>(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return lhs.getInteger() > rhs.getInteger();
 
@@ -377,7 +377,7 @@ bool Operand::operator||(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return lhs.getInteger() || rhs.getInteger();
 
@@ -391,7 +391,7 @@ bool Operand::operator!() const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (literalValue.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return !literalValue.getInteger();
 
@@ -406,7 +406,7 @@ bool Operand::operator&&(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		return lhs.getInteger() && rhs.getInteger();
 
@@ -422,7 +422,7 @@ Operand Operand::operator+(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		returnValue.putInteger(lhs.getInteger() + rhs.getInteger());
 		return returnValue;
@@ -444,7 +444,7 @@ Operand Operand::operator-(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		returnValue.putInteger(lhs.getInteger() - rhs.getInteger());
 		return returnValue;
@@ -466,7 +466,7 @@ Operand Operand::operator*(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		returnValue.putInteger(lhs.getInteger() * rhs.getInteger());
 		return returnValue;
@@ -488,7 +488,7 @@ Operand Operand::operator/(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		if (rhs.getInteger() == 0) {
 			error("Operand::operator/(): Attempted divide by zero");
@@ -517,7 +517,7 @@ Operand Operand::operator%(const Operand &other) const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (lhs.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		if (rhs.getInteger() == 0) {
 			error("Operand::operator%%(): Attempted mod by zero");
@@ -536,7 +536,7 @@ Operand Operand::operator-() const {
 	// If the types being compared end up being incompatible, the respective get
 	// method on the rhs will raise the error.
 	switch (literalValue.getType()) {
-	case kOperandTypeLiteral1: 
+	case kOperandTypeLiteral1:
 	case kOperandTypeLiteral2:
 		returnValue.putInteger(-literalValue.getInteger());
 		return returnValue;
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index b4a11d78a00..ee6e91000c9 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -213,7 +213,7 @@ const char *builtInMethodToStr(BuiltInMethod method) {
 		return "Sort";
 	case kDeleteAtMethod:
 		return "DeleteAt";
-	case kJumbleMethod: 
+	case kJumbleMethod:
 		return "Jumble";
 	case kDeleteFirstMethod:
 		return "DeleteFirst";


Commit: 7c0ab8b057485b8c55e52007d8fab14b25846413
    https://github.com/scummvm/scummvm/commit/7c0ab8b057485b8c55e52007d8fab14b25846413
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T21:05:25-05:00

Commit Message:
MEDIASTATION: Support assets that reference other assets' data

Changed paths:
    engines/mediastation/assets/image.cpp
    engines/mediastation/assets/image.h
    engines/mediastation/assets/sprite.cpp
    engines/mediastation/assets/sprite.h
    engines/mediastation/context.cpp


diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index 352fb35a768..cc92233bfc9 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -32,7 +32,11 @@ Image::Image(AssetHeader *header) : Asset(header) {
 }
 
 Image::~Image() {
-	delete _bitmap;
+	if (_header->_assetReference == 0) {
+		// If we're just referencing another asset's bitmap,
+		// don't delete that bitmap.
+		delete _bitmap;
+	}
 	_bitmap = nullptr;
 }
 
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index 2f86993ebcd..41185b4a765 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -32,6 +32,8 @@
 namespace MediaStation {
 
 class Image : public Asset {
+friend class Context;
+
 public:
 	Image(AssetHeader *header);
 	virtual ~Image() override;
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 9e2eebf184b..c423c1581c8 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -80,8 +80,12 @@ Sprite::Sprite(AssetHeader *header) : Asset(header) {
 }
 
 Sprite::~Sprite() {
-	for (SpriteFrame *frame : _frames) {
-		delete frame;
+	// If we're just referencing another asset's frames,
+	// don't delete those frames.
+	if (_header->_assetReference == 0) {
+		for (SpriteFrame *frame : _frames) {
+			delete frame;
+		}
 	}
 	_frames.clear();
 }
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index ff26a703756..e24a50d6943 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -61,6 +61,8 @@ private:
 // Sprites are somewhat like movies, but they strictly show one frame at a time
 // and don't have sound. They are intended for background/recurrent animations.
 class Sprite : public Asset {
+friend class Context;
+
 public:
 	Sprite(AssetHeader *header);
 	~Sprite();
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 36c616a474d..b8aed67d268 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -73,6 +73,41 @@ Context::Context(const Common::Path &path) : Datafile(path) {
 		subfile = getNextSubfile();
 		readAssetFromLaterSubfile(subfile);
 	}
+
+	// Some sprites and images don't have any image data themselves, they just
+	// reference the same image data in another asset. So we need to check for
+	// these and create the appropriate references.
+	for (auto it = _assets.begin(); it != _assets.end(); ++it) {
+		Asset *asset = it->_value;
+		uint referencedAssetId = asset->getHeader()->_assetReference;
+		if (referencedAssetId != 0) {
+			switch (asset->getHeader()->_type) {
+			case kAssetTypeImage: {
+				Image *image = static_cast<Image *>(asset);
+				Image *referencedImage = static_cast<Image *>(getAssetById(referencedAssetId));
+				if (referencedImage == nullptr) {
+					error("Context::Context(): Asset %d references non-existent asset %d", asset->getHeader()->_id, referencedAssetId);
+				}
+				image->_bitmap = referencedImage->_bitmap;
+				break;
+			}
+
+			case kAssetTypeSprite: {
+				Sprite *sprite = static_cast<Sprite *>(asset);
+				Sprite *referencedSprite = static_cast<Sprite *>(getAssetById(referencedAssetId));
+				if (referencedSprite == nullptr) {
+					error("Context::Context(): Asset %d references non-existent asset %d", asset->getHeader()->_id, referencedAssetId);
+				}
+				sprite->_frames = referencedSprite->_frames;
+				break;
+			}
+
+			default: {
+				error("Context::Context(): Asset type %d referenced, but reference not implemented yet", asset->getHeader()->_type);
+			}
+			}
+		}
+	}
 }
 
 Context::~Context() {


Commit: d3ecb2f91a078a2757032829bca42a3a57f38147
    https://github.com/scummvm/scummvm/commit/d3ecb2f91a078a2757032829bca42a3a57f38147
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T21:05:25-05:00

Commit Message:
MEDIASTATION: Fix movie persistence

Changed paths:
    engines/mediastation/assets/movie.cpp
    engines/mediastation/assets/movie.h


diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 7faff972b49..6c5250213e4 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -354,14 +354,7 @@ void Movie::updateFrameState() {
 	uint currentTime = g_system->getMillis();
 	uint movieTime = currentTime - _startTime;
 	debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _header->_id, movieTime);
-	if (_framesNotYetShown.empty()) {
-		_isPlaying = false;
-		setInactive();
-		_framesOnScreen.clear();
-		runEventHandlerIfExists(kMovieEndEvent);
-		return;
-	}
-	
+
 	// This complexity is necessary becuase movies can have more than one frame
 	// showing at the same time - for instance, a movie background and an
 	// animation on that background are a part of the saem movie and are on
@@ -400,12 +393,34 @@ void Movie::updateFrameState() {
 		}
 	}
 
+	// Now see if we're at the end of the movie.
+	if (_framesOnScreen.empty() && _framesNotYetShown.empty()) {
+		_isPlaying = false;
+		_framesOnScreen.clear();
+		if (_stills.empty()) {
+			setInactive();
+		} else {
+			showPersistentFrame();
+		}
+
+		runEventHandlerIfExists(kMovieEndEvent);
+		return;
+	}
+
+
 	// Show the frames that are currently active, for debugging purposes.
 	for (MovieFrame *frame : _framesOnScreen) {
 		debugC(5, kDebugGraphics, "   (time: %d ms) Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", movieTime, frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
 	}
 }
 
+void Movie::showPersistentFrame() {
+	for (MovieFrame *still : _stills) {
+		_framesOnScreen.push_back(still);
+		g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
+	}
+}
+
 void Movie::redraw(Common::Rect &rect) {
 	// Make sure the frames are ordered properly before we attempt to draw them.
 	Common::sort(_framesOnScreen.begin(), _framesOnScreen.end(), [](MovieFrame * a, MovieFrame * b) {
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index d39cc56d6b9..749d051d63d 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -123,6 +123,7 @@ private:
 	void spatialHide();
 
 	void updateFrameState();
+	void showPersistentFrame();
 
 	Common::Rect getFrameBoundingBox(MovieFrame *frame);
 };


Commit: f60689d43487558653d2edfa83072f0da3d60cf0
    https://github.com/scummvm/scummvm/commit/f60689d43487558653d2edfa83072f0da3d60cf0
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-02-22T21:05:25-05:00

Commit Message:
MEDIASTATION: Fix audio memory leaks due to not freeing buffer

Changed paths:
    engines/mediastation/assets/movie.cpp
    engines/mediastation/assets/sound.cpp


diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 6c5250213e4..ddf327ed8ee 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -536,7 +536,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
 			Audio::SeekableAudioStream *stream = nullptr;
 			switch (_header->_soundEncoding) {
 			case SoundEncoding::PCM_S16LE_MONO_22050:
-				stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO);
+				stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
 				break;
 
 			case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index 3de081af481..e2a34fecdd5 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -81,7 +81,7 @@ void Sound::readChunk(Chunk &chunk) {
 	Audio::SeekableAudioStream *stream = nullptr;
 	switch (_header->_soundEncoding) {
 	case SoundEncoding::PCM_S16LE_MONO_22050:
-		stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN, DisposeAfterUse::NO);
+		stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
 		break;
 
 	case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:




More information about the Scummvm-git-logs mailing list