[Scummvm-git-logs] scummvm master -> 1ca640151e36825c99c4813b5558f5d9c1ac6269
npjg
noreply at scummvm.org
Wed Feb 25 02:12:08 UTC 2026
This automated email contains information about 17 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
11a48f04bf MEDIASTATION: Fix incorrect comparison when triggering timer events
f60361fd33 MEDIASTATION: Improve debug printing
f14ca14830 MEDIASTATION: Make sprite clips work with newer engine versions
f6b3fd2ab8 MEDIASTATION: Add helper function for getting an actor of a specific type
11b97406c5 MEDIASTATION: Issue warnings, not asserts, when script argument conditions are not met
90cbd70cd3 MEDIASTATION: Rename BitmapHeader to ImageInfo
a86401a695 MEDIASTATION: Add initial support for stream movie proxies
98528cadc1 MEDIASTATION: Add support for camera images
b9e38dd7e7 MEDIASTATION: Add Cursor actor
8cd32db3f4 MEDIASTATION: Make Path actor more accurate
987b07ddde MEDIASTATION: Implement more builtin script functions/methods
62184a96dd MEDIASTATION: Fix numerous inconsistencies with image data reading
64fb8b2037 MEDIASTATION: Add initial support for Canvas and Text actors
34af4483c4 MEDIASTATION: Enforce consistent switch breaks, rather than returns
9d426237f6 MEDIASTATION: Enforce correct copy semantics for script values/variables
2505aa1acb MEDIASTATION: Properly pass script return values back
1ca640151e MEDIASTATION: Make caching-related parameters consistently read
Commit: 11a48f04bfd8289d77ea701815e581871cd04020
https://github.com/scummvm/scummvm/commit/11a48f04bfd8289d77ea701815e581871cd04020
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:25:58-05:00
Commit Message:
MEDIASTATION: Fix incorrect comparison when triggering timer events
Changed paths:
engines/mediastation/actor.cpp
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index d97523ad135..2dd457a4b07 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -96,9 +96,10 @@ void Actor::processTimeEventHandlers() {
double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
bool timeEventAlreadyProcessed = timeEventInMilliseconds < _lastProcessedTime;
- bool timeEventNeedsToBeProcessed = timeEventInMilliseconds <= currentTime - _startTime;
+ bool timeEventNeedsToBeProcessed = timeEventInMilliseconds < currentTime - _startTime;
if (!timeEventAlreadyProcessed && timeEventNeedsToBeProcessed) {
- debugC(5, kDebugScript, "Actor::processTimeEventHandlers(): Running On Time handler for time %d ms", timeEventInMilliseconds);
+ debugC(5, kDebugScript, "%s: Running On Time handler for time %d ms (lastProcessedTime: %d, currentTime: %d)",
+ __func__, timeEventInMilliseconds, _lastProcessedTime, currentTime);
timeEvent->execute(_id);
}
}
Commit: f60361fd3323283856cda0baddebefbbfa826d66
https://github.com/scummvm/scummvm/commit/f60361fd3323283856cda0baddebefbbfa826d66
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Improve debug printing
Get entity names from PROFILE._ST when available.
This is only included in some titles, but when it is included it is immensely helpful.
Debug-print script variable values.
Improve debug output on mismatched script value comparison.
Adjust some debug levels and make debug formatting more consistent in places.
Changed paths:
A engines/mediastation/profile.cpp
A engines/mediastation/profile.h
engines/mediastation/actor.cpp
engines/mediastation/actor.h
engines/mediastation/actors/camera.cpp
engines/mediastation/actors/document.cpp
engines/mediastation/actors/hotspot.cpp
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/sound.cpp
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/stage.cpp
engines/mediastation/actors/timer.cpp
engines/mediastation/audio.cpp
engines/mediastation/context.cpp
engines/mediastation/datafile.cpp
engines/mediastation/graphics.cpp
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/collection.cpp
engines/mediastation/mediascript/eventhandler.cpp
engines/mediastation/mediascript/function.cpp
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptvalue.cpp
engines/mediastation/mediascript/scriptvalue.h
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
engines/mediastation/module.mk
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index 2dd457a4b07..99ccb72e361 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -30,6 +30,76 @@
namespace MediaStation {
+const char *actorTypeToStr(ActorType type) {
+ switch (type) {
+ case kActorTypeEmpty:
+ return "Empty";
+ case kActorTypeScreen:
+ return "Screen";
+ case kActorTypeStage:
+ return "Stage";
+ case kActorTypePath:
+ return "Path";
+ case kActorTypeSound:
+ return "Sound";
+ case kActorTypeTimer:
+ return "Timer";
+ case kActorTypeImage:
+ return "Image";
+ case kActorTypeHotspot:
+ return "Hotspot";
+ case kActorTypeCursor:
+ return "Cursor";
+ case kActorTypeSprite:
+ return "Sprite";
+ case kActorTypeLKZazu:
+ return "LKZazu";
+ case kActorTypeLKConstellations:
+ return "LKConstellations";
+ case kActorTypeDocument:
+ return "Document";
+ case kActorTypeImageSet:
+ return "ImageSet";
+ case kActorTypeMovie:
+ return "Movie";
+ case kActorTypePalette:
+ return "Palette";
+ case kActorTypePrinter:
+ return "Printer";
+ case kActorTypeText:
+ return "Text";
+ case kActorTypeFont:
+ return "Font";
+ case kActorTypeCamera:
+ return "Camera";
+ case kActorTypeCanvas:
+ return "Canvas";
+ case kActorTypeXsnd:
+ return "Xsnd";
+ case kActorTypeXsndMidi:
+ return "XsndMidi";
+ case kActorTypeRecorder:
+ return "Recorder";
+ case kActorTypeFunction:
+ return "Function";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+void Actor::setId(uint id) {
+ _id = id;
+ updateDebugName();
+}
+
+void Actor::updateDebugName() {
+ _debugName = g_engine->formatActorName(this);
+}
+
+const char *Actor::debugName() const {
+ return _debugName.c_str();
+}
+
Actor::~Actor() {
for (auto it = _eventHandlers.begin(); it != _eventHandlers.end(); ++it) {
Common::Array<EventHandler *> &handlersForType = it->_value;
@@ -62,8 +132,8 @@ void Actor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
// 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("%s: Event handler for %s (%s) already exists", __func__,
- eventTypeToStr(eventHandler->_type), eventHandler->_argumentValue.getDebugString().c_str());
+ error("[%s] %s: Event handler for %s (%s) already exists", debugName(), __func__,
+ eventTypeToStr(eventHandler->_type), eventHandler->_argumentValue.getDebugString().c_str());
}
}
eventHandlersForType.push_back(eventHandler);
@@ -71,19 +141,20 @@ void Actor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
}
default:
- error("Got unimplemented actor parameter 0x%x", static_cast<uint>(paramType));
+ error("[%s] %s: Got unimplemented actor parameter 0x%x", debugName(), __func__, static_cast<uint>(paramType));
}
}
void Actor::loadIsComplete() {
if (_loadIsComplete) {
- warning("%s: Called more than once for actor %d", __func__, _id);
+ warning("[%s] %s: Already loaded", debugName(), __func__);
}
_loadIsComplete = true;
}
ScriptValue Actor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
- warning("%s: Got unimplemented method call 0x%x (%s)", __func__, static_cast<uint>(methodId), builtInMethodToStr(methodId));
+ warning("[%s] %s: Got unimplemented method call 0x%x (%s)",
+ debugName(), __func__, static_cast<uint>(methodId), builtInMethodToStr(methodId));
return ScriptValue();
}
@@ -112,18 +183,19 @@ void Actor::runEventHandlerIfExists(EventType eventType, const ScriptValue &arg)
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()));
+ warning("[%s] %s: Got event handler arg type %s, expected %s", debugName(), __func__,
+ scriptValueTypeToStr(arg.getType()), scriptValueTypeToStr(argToCheck.getType()));
continue;
}
if (arg == argToCheck) {
- debugC(5, kDebugScript, "Executing handler for event type %s on actor %d", eventTypeToStr(eventType), _id);
+ debugC(5, kDebugScript, "[%s] %s: Executing handler for event type %s", debugName(), __func__, eventTypeToStr(eventType));
eventHandler->execute(_id);
return;
}
}
- debugC(5, kDebugScript, "No event handler for event type %s on actor %d", eventTypeToStr(eventType), _id);
+
+ debugC(5, kDebugScript, "[%s] %s: No event handler for event type %s", debugName(), __func__, eventTypeToStr(eventType));
}
void Actor::runEventHandlerIfExists(EventType eventType) {
@@ -335,6 +407,9 @@ void SpatialEntity::invalidateMouse() {
void SpatialEntity::moveTo(int16 x, int16 y) {
Common::Point dest(x, y);
+ debugC(3, kDebugGraphics, "[%s] %s: (%d, %d) -> (%d, %d)", debugName(), __func__,
+ _originalBoundingBox.origin().x, _originalBoundingBox.origin().y, x, y);
+
if (dest == _boundingBox.origin()) {
// We aren't actually moving anywhere.
return;
@@ -356,6 +431,7 @@ void SpatialEntity::moveTo(int16 x, int16 y) {
void SpatialEntity::moveToCentered(int16 x, int16 y) {
int16 targetX = x - (_boundingBox.width() / 2);
int16 targetY = y - (_boundingBox.height() / 2);
+ debugC(3, kDebugGraphics, "[%s] %s: (%d, %d)", debugName(), __func__, targetX, targetY);
moveTo(targetX, targetY);
}
@@ -410,12 +486,12 @@ void SpatialEntity::invalidateLocalBounds() {
_parentStage->setAdjustedBounds(kWrapNone);
_parentStage->invalidateRect(getBbox());
} else {
- error("%s: No parent stage for entity %d", __func__, _id);
+ warning("[%s] %s: No parent stage", debugName(), __func__);
}
}
void SpatialEntity::invalidateLocalZIndex() {
- warning("STUB: %s", __func__);
+ warning("[%s] %s: STUB", debugName(), __func__);
}
void SpatialEntity::setAdjustedBounds(CylindricalWrapMode alignmentMode) {
@@ -483,12 +559,13 @@ void SpatialEntity::setAdjustedBounds(CylindricalWrapMode alignmentMode) {
if (alignmentMode != kWrapNone) {
// TODO: Implement this once we have a title that actually uses it.
- warning("%s: Actor %d: Wrapping mode %d not handled yet: (%d, %d, %d, %d) -= (%d, %d)", __func__, _id, static_cast<uint>(alignmentMode), PRINT_RECT(_boundingBox), offset.x, offset.y);
+ warning("[%s] %s: Wrapping mode %d not handled yet: (%d, %d, %d, %d) -= (%d, %d)", debugName(), __func__,
+ static_cast<uint>(alignmentMode), PRINT_RECT(_boundingBox), offset.x, offset.y);
}
if (_scaleX != 0.0 || _scaleY != 0.0) {
// TODO: Implement this once we have a title that actually uses it.
- warning("%s: Scale not handled yet (scaleX: %f, scaleY: %f)", __func__, _scaleX, _scaleY);
+ warning("[%s] %s: Scale not handled yet (scaleX: %f, scaleY: %f)", debugName(), __func__, _scaleX, _scaleY);
}
}
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index 9898ae076e7..78a63c6e79e 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -64,6 +64,7 @@ enum ActorType {
kActorTypeRecorder = 0x0021,
kActorTypeFunction = 0x0069 // FUN
};
+const char *actorTypeToStr(ActorType type);
enum ActorHeaderSectionType {
kActorHeaderEmptySection = 0x0000,
@@ -186,14 +187,17 @@ public:
ActorType type() const { return _type; }
uint id() const { return _id; }
uint contextId() const { return _contextId; }
- void setId(uint id) { _id = id; }
+ void setId(uint id);
void setContextId(uint id) { _contextId = id; }
+ const char *debugName() const;
+ void updateDebugName();
protected:
ActorType _type = kActorTypeEmpty;
bool _loadIsComplete = false;
uint _id = 0;
uint _contextId = 0;
+ Common::String _debugName;
uint _startTime = 0;
uint _lastProcessedTime = 0;
diff --git a/engines/mediastation/actors/camera.cpp b/engines/mediastation/actors/camera.cpp
index ab61816ec6b..8870604a16b 100644
--- a/engines/mediastation/actors/camera.cpp
+++ b/engines/mediastation/actors/camera.cpp
@@ -336,7 +336,7 @@ void CameraActor::drawUsingCamera(DisplayContext &displayContext, const Common::
void CameraActor::drawObject(DisplayContext &sourceContext, DisplayContext &destContext, SpatialEntity *objectToDraw) {
if (_parentStage == nullptr) {
- warning("%s: No parent stage", __func__);
+ warning("[%s] %s: No parent stage", debugName(), __func__);
return;
}
@@ -347,11 +347,11 @@ void CameraActor::drawObject(DisplayContext &sourceContext, DisplayContext &dest
}
if (_parentStage->cylindricalX()) {
- warning("%s: CylindricalX not handled yet", __func__);
+ warning("[%s] %s: CylindricalX not handled yet", debugName(), __func__);
}
if (_parentStage->cylindricalY()) {
- warning("%s: CylindricalY not handled yet", __func__);
+ warning("[%s] %s: CylindricalY not handled yet", debugName(), __func__);
}
objectToDraw->setAdjustedBounds(kWrapNone);
}
@@ -359,7 +359,7 @@ void CameraActor::drawObject(DisplayContext &sourceContext, DisplayContext &dest
void CameraActor::setXYDelta(uint xDelta, uint yDelta) {
_panDelta.x = xDelta;
_panDelta.y = yDelta;
- debugC(6, kDebugCamera, "%s: (%d, %d)", __func__, _panDelta.x, _panDelta.y);
+ debugC(6, kDebugCamera, "[%s] %s: (%d, %d)", debugName(), __func__, _panDelta.x, _panDelta.y);
}
void CameraActor::setXYDelta() {
@@ -376,7 +376,7 @@ void CameraActor::setXYDelta() {
} else if (_panDest.y < _panStart.y) {
_panDelta.y = -1;
}
- debugC(6, kDebugCamera, "%s: (%d, %d)", __func__, _panDelta.x, _panDelta.y);
+ debugC(6, kDebugCamera, "[%s] %s: (%d, %d)", debugName(), __func__, _panDelta.x, _panDelta.y);
}
void CameraActor::panToByTime(int16 x, int16 y, double duration) {
@@ -387,8 +387,8 @@ void CameraActor::panToByTime(int16 x, int16 y, double duration) {
_currentPanStep = 1;
_startTime = g_system->getMillis();
_nextPanStepTime = 0;
- debugC(6, kDebugCamera, "%s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f",
- __func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration);
+ debugC(6, kDebugCamera, "[%s] %s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f",
+ debugName(), __func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration);
setXYDelta();
calcNewViewportOrigin();
}
@@ -402,8 +402,8 @@ void CameraActor::panToByStepCount(int16 x, int16 y, uint panSteps, double durat
_maxPanStep = panSteps;
_startTime = g_system->getMillis();
_nextPanStepTime = 0;
- debugC(6, kDebugCamera, "%s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f; maxPanStep: %d",
- __func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration, _maxPanStep);
+ debugC(6, kDebugCamera, "[%s] %s: panStart: (%d, %d); panDest: (%d, %d); panDuration: %f; maxPanStep: %d",
+ debugName(), __func__, _panStart.x, _panStart.y, _panDest.x, _panDest.y, _panDuration, _maxPanStep);
setXYDelta();
calcNewViewportOrigin();
}
@@ -416,7 +416,7 @@ void CameraActor::startPan(uint xOffset, uint yOffset, double duration) {
_currentPanStep = 0;
_maxPanStep = 0;
setXYDelta(xOffset, yOffset);
- debugC(6, kDebugCamera, "%s: xOffset: %u, yOffset: %u, duration: %f", __func__, xOffset, yOffset, duration);
+ debugC(6, kDebugCamera, "[%s] %s: xOffset: %u, yOffset: %u, duration: %f", debugName(), __func__, xOffset, yOffset, duration);
}
void CameraActor::stopPan() {
@@ -426,8 +426,8 @@ void CameraActor::stopPan() {
_nextPanStepTime = 0;
_currentPanStep = 0;
_maxPanStep = 0;
- debugC(6, kDebugCamera, "%s: nextViewportOrigin: (%d, %d); actualViewportOrigin: (%d, %d)",
- __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _currentViewportOrigin.x, _currentViewportOrigin.y);
+ debugC(6, kDebugCamera, "[%s] %s: nextViewportOrigin: (%d, %d); actualViewportOrigin: (%d, %d)",
+ debugName(), __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _currentViewportOrigin.x, _currentViewportOrigin.y);
}
bool CameraActor::continuePan() {
@@ -441,7 +441,7 @@ bool CameraActor::continuePan() {
panShouldContinue = false;
}
}
- debugC(6, kDebugCamera, "%s: %s", __func__, panShouldContinue ? "true": "false");
+ debugC(6, kDebugCamera, "[%s] %s: %s", debugName(), __func__, panShouldContinue ? "true": "false");
return panShouldContinue;
}
@@ -536,24 +536,24 @@ void CameraActor::processNextPanStep() {
void CameraActor::adjustCameraViewport(Common::Point &viewportToAdjust) {
if (_parentStage == nullptr) {
- warning("%s: No parent stage", __func__);
+ warning("[%s] %s: No parent stage", debugName(), __func__);
return;
}
if (_parentStage->cylindricalX()) {
- warning("%s: CylindricalX not handled yet", __func__);
+ warning("[%s] %s: CylindricalX not handled yet", debugName(), __func__);
}
if (_parentStage->cylindricalY()) {
- warning("%s: CylindricalY not handled yet", __func__);
+ warning("[%s] %s: CylindricalY not handled yet", debugName(), __func__);
}
}
void CameraActor::calcNewViewportOrigin() {
if (_panState == kCameraPanningStarted) {
_nextViewportOrigin = _currentViewportOrigin + _panDelta;
- debugC(6, kDebugCamera, "%s: (%d, %d) [panDelta: (%d, %d)]",
- __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panDelta.x, _panDelta.y);
+ debugC(6, kDebugCamera, "[%s] %s: (%d, %d) [panDelta: (%d, %d)]",
+ debugName(), __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panDelta.x, _panDelta.y);
} else {
// Interpolate from the start to the dest based on percent complete.
double progress = percentComplete();
@@ -566,8 +566,8 @@ void CameraActor::calcNewViewportOrigin() {
double endY = static_cast<double>(_panDest.y);
double interpolatedY = startY + (endY - startY) * progress + 0.5;
_nextViewportOrigin.y = static_cast<int16>(interpolatedY);
- debugC(6, kDebugCamera, "%s: (%d, %d) [panStart: (%d, %d); panDest: (%d, %d); percentComplete: %f]",
- __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panStart.x, _panStart.y, _panDest.x, _panDest.y, progress);
+ debugC(6, kDebugCamera, "[%s] %s: (%d, %d) [panStart: (%d, %d); panDest: (%d, %d); percentComplete: %f]",
+ debugName(), __func__, _nextViewportOrigin.x, _nextViewportOrigin.y, _panStart.x, _panStart.y, _panDest.x, _panDest.y, progress);
}
}
@@ -588,7 +588,7 @@ bool CameraActor::cameraWithinStage(const Common::Point &candidate) {
} else if (candidate.x < 0) {
result = false;
}
- debugC(6, kDebugCamera, "%s: %s [rightBoundary: %d, extent: %d]", __func__, result ? "true" : "false", candidateRightBoundary, _parentStage->extent().x);
+ debugC(6, kDebugCamera, "[%s] %s: %s [rightBoundary: %d, extent: %d]", debugName(), __func__, result ? "true" : "false", candidateRightBoundary, _parentStage->extent().x);
}
// We can only be out of vertical bounds if we have a requested delta and
@@ -602,7 +602,7 @@ bool CameraActor::cameraWithinStage(const Common::Point &candidate) {
} else if (candidate.y < 0) {
result = false;
}
- debugC(6, kDebugCamera, "%s: %s [bottomBoundary: %d, extent: %d]", __func__, result ? "true" : "false", candidateBottomBoundary, _parentStage->extent().y);
+ debugC(6, kDebugCamera, "[%s] %s: %s [bottomBoundary: %d, extent: %d]", debugName(), __func__, result ? "true" : "false", candidateBottomBoundary, _parentStage->extent().y);
}
return result;
}
diff --git a/engines/mediastation/actors/document.cpp b/engines/mediastation/actors/document.cpp
index 9b930d28cd6..ec58e48934f 100644
--- a/engines/mediastation/actors/document.cpp
+++ b/engines/mediastation/actors/document.cpp
@@ -49,7 +49,7 @@ ScriptValue DocumentActor::callMethod(BuiltInMethod methodId, Common::Array<Scri
case kDocumentSetMultipleSoundsMethod: {
assert(args.size() == 1);
bool value = args[0].asBool();
- warning("%s: STUB: %s: %d", __func__, builtInMethodToStr(methodId), value);
+ warning("[%s] %s: STUB: %s: %d", debugName(), __func__, builtInMethodToStr(methodId), value);
break;
}
@@ -91,7 +91,7 @@ void DocumentActor::processBranch(Common::Array<ScriptValue> &args) {
if (args.size() > 1) {
bool disableUpdates = static_cast<bool>(args[1].asParamToken());
if (disableUpdates)
- warning("%s: disableUpdates parameter not handled yet", __func__);
+ warning("[%s] %s: disableUpdates parameter not handled yet", debugName(), __func__);
}
g_engine->getDocument()->scheduleScreenBranch(contextId);
diff --git a/engines/mediastation/actors/hotspot.cpp b/engines/mediastation/actors/hotspot.cpp
index e2d960537c3..808f186ef1c 100644
--- a/engines/mediastation/actors/hotspot.cpp
+++ b/engines/mediastation/actors/hotspot.cpp
@@ -165,7 +165,7 @@ uint16 HotspotActor::findActorToAcceptMouseEvents(
result |= kMouseUpFlag;
}
} else {
- debugC(5, kDebugEvents, "%s: %d: Inactive", __func__, id());
+ debugC(6, kDebugEvents, "[%s] %s: Inactive", debugName(), __func__);
}
return result;
@@ -194,7 +194,7 @@ void HotspotActor::deactivate() {
void HotspotActor::mouseDownEvent(const Common::Event &event) {
if (!_isActive) {
- warning("%s: Called on inactive hotspot", __func__);
+ warning("[%s] %s: Inactive", debugName(), __func__);
return;
}
@@ -204,7 +204,7 @@ void HotspotActor::mouseDownEvent(const Common::Event &event) {
void HotspotActor::mouseUpEvent(const Common::Event &event) {
if (!_isActive) {
- warning("%s: Called on inactive hotspot", __func__);
+ warning("[%s] %s: Inactive", debugName(), __func__);
return;
}
@@ -214,16 +214,16 @@ void HotspotActor::mouseUpEvent(const Common::Event &event) {
void HotspotActor::mouseEnteredEvent(const Common::Event &event) {
if (!_isActive) {
- warning("%s: Called on inactive hotspot", __func__);
+ warning("[%s] %s: Inactive", debugName(), __func__);
return;
}
g_engine->setMouseInsideHotspot(this);
if (_cursorResourceId != 0) {
- debugC(5, kDebugEvents, "%s: Setting cursor %d for asset %d", __func__, _cursorResourceId, id());
+ debugC(5, kDebugEvents, "[%s] %s: Setting cursor %d", debugName(), __func__, _cursorResourceId);
g_engine->getCursorManager()->setAsTemporary(_cursorResourceId);
} else {
- debugC(5, kDebugEvents, "%s: Unsetting cursor for asset %d", __func__, id());
+ debugC(5, kDebugEvents, "[%s] %s: Unsetting cursor", debugName(), __func__);
g_engine->getCursorManager()->unsetTemporary();
}
@@ -232,7 +232,7 @@ void HotspotActor::mouseEnteredEvent(const Common::Event &event) {
void HotspotActor::mouseMovedEvent(const Common::Event &event) {
if (!_isActive) {
- warning("%s: Called on inactive hotspot", __func__);
+ warning("[%s] %s: Inactive", debugName(), __func__);
return;
}
@@ -241,7 +241,7 @@ void HotspotActor::mouseMovedEvent(const Common::Event &event) {
void HotspotActor::mouseExitedEvent(const Common::Event &event) {
if (!_isActive) {
- warning("%s: Called on inactive hotspot", __func__);
+ warning("[%s] %s: Inactive", debugName(), __func__);
return;
}
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index b29b3918cdd..3514702a552 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -262,7 +262,7 @@ void StreamMovieActor::updateFrameState() {
uint currentTime = g_system->getMillis();
movieTime = currentTime - _startTime;
}
- debugC(5, kDebugGraphics, "StreamMovieActor::updateFrameState (%d): Starting update (movie time: %d)", _id, movieTime);
+ debugC(7, kDebugGraphics, "[%s] %s: Starting update (movie time: %d)", debugName(), __func__, movieTime);
// This complexity is necessary becuase movies can have more than one frame
// showing at the same time - for instance, a movie background and an
@@ -314,14 +314,16 @@ void StreamMovieActor::updateFrameState() {
// 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, zIndex = %d", \
- movieTime, frame->index, frame->image->width(), frame->image->height(), frame->leftTop.x, frame->leftTop.y, frame->startInMilliseconds, frame->endInMilliseconds, frame->zIndex);
+ debugC(8, kDebugGraphics, "[%s] %s: (time: %d ms) Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, zIndex = %d",
+ debugName(), __func__, movieTime, frame->index, frame->image->width(), frame->image->height(), frame->leftTop.x, frame->leftTop.y, frame->startInMilliseconds, frame->endInMilliseconds, frame->zIndex);
}
}
void StreamMovieActor::draw(DisplayContext &displayContext) {
for (MovieFrame *frame : _framesOnScreen) {
Common::Rect bbox = getFrameBoundingBox(frame);
+ debugC(8, kDebugGraphics, "%s: %s: frame %d (%d, %d, %d, %d)",
+ __func__, debugName(), frame->index, PRINT_RECT(bbox));
switch (frame->blitType) {
case kUncompressedMovieBlit:
@@ -427,12 +429,11 @@ void StreamMovieActor::readChunk(Chunk &chunk) {
void StreamMovieActor::parseMovieHeader(Chunk &chunk) {
_chunkCount = chunk.readTypedUint16();
_frameRate = chunk.readTypedDouble();
- debugC(5, kDebugLoading, "%s: chunkCount = 0x%x, frameRate = %f (@0x%llx)", __func__, _chunkCount, _frameRate, static_cast<long long int>(chunk.pos()));
+ debugC(5, kDebugLoading, "[%s] %s: chunkCount: 0x%x, frameRate: %f", debugName(), __func__, _chunkCount, _frameRate);
Common::Array<uint> chunkLengths;
for (uint i = 0; i < _chunkCount; i++) {
uint chunkLength = chunk.readTypedUint32();
- debugC(5, kDebugLoading, "StreamMovieActor::readSubfile(): chunkLength = 0x%x (@0x%llx)", chunkLength, static_cast<long long int>(chunk.pos()));
chunkLengths.push_back(chunkLength);
}
}
diff --git a/engines/mediastation/actors/sound.cpp b/engines/mediastation/actors/sound.cpp
index ce7e2a426e5..5b2505c967e 100644
--- a/engines/mediastation/actors/sound.cpp
+++ b/engines/mediastation/actors/sound.cpp
@@ -41,7 +41,7 @@ void SoundActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
// as the ID we have already read.
uint32 duplicateActorId = chunk.readTypedUint16();
if (duplicateActorId != _id) {
- warning("Duplicate actor ID %d does not match original ID %d", duplicateActorId, _id);
+ warning("[%s] %s: Duplicate actor ID %s does not match", debugName(), __func__, g_engine->formatActorName(duplicateActorId).c_str());
}
break;
}
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index 19dfa5f02cd..641ea76a423 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -276,7 +276,7 @@ void SpriteMovieActor::setCurrentClip(uint clipId) {
_activeClip = _clips.getVal(clipId);
} else {
_activeClip.id = clipId;
- warning("%s: Sprite clip %d not found in sprite %d", __func__, clipId, _id);
+ warning("%s: Sprite clip %d not found in sprite %s", __func__, clipId, debugName());
}
}
@@ -304,7 +304,6 @@ void SpriteMovieActor::process() {
void SpriteMovieActor::readChunk(Chunk &chunk) {
// Reads one frame from the sprite.
- debugC(5, kDebugLoading, "Sprite::readFrame(): Reading sprite frame (@0x%llx)", static_cast<long long int>(chunk.pos()));
SpriteFrameHeader *header = new SpriteFrameHeader(chunk);
SpriteFrame *frame = new SpriteFrame(chunk, header);
_asset->frames.push_back(frame);
diff --git a/engines/mediastation/actors/stage.cpp b/engines/mediastation/actors/stage.cpp
index cf8687ca357..bb3df7c7fee 100644
--- a/engines/mediastation/actors/stage.cpp
+++ b/engines/mediastation/actors/stage.cpp
@@ -162,7 +162,8 @@ void StageActor::drawUsingStage(DisplayContext &displayContext) {
entity->setAdjustedBounds(kWrapNone);
if (entity->isVisible()) {
if (displayContext.rectIsInClip(entity->getBbox())) {
- debugC(5, kDebugGraphics, "%s: Redrawing actor %d", __func__, entity->id());
+ debugC(8, kDebugGraphics, "[%s] %s: Redrawing actor %s (%d, %d, %d, %d)", debugName(), __func__,
+ entity->debugName(), PRINT_RECT(entity->getBbox()));
entity->draw(displayContext);
}
}
@@ -181,7 +182,7 @@ void StageActor::invalidateRect(const Common::Rect &rect) {
invalidateUsingCameras(rectRelativeToParent);
}
} else {
- error("%s: Attempt to invalidate rect without a parent stage", __func__);
+ warning("[%s] %s: Attempt to invalidate rect without a parent stage", debugName(), __func__);
}
}
@@ -365,7 +366,7 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
void StageActor::addChildSpatialEntity(SpatialEntity *entity) {
if (!assertHasNoParent(entity)) {
- error("%s: Attempt to add entity that already has a parent", __func__);
+ error("[%s] %s: Attempt to add entity that already has a parent", debugName(), __func__);
}
entity->setParentStage(this);
@@ -378,7 +379,7 @@ void StageActor::addChildSpatialEntity(SpatialEntity *entity) {
void StageActor::removeChildSpatialEntity(SpatialEntity *entity) {
if (!assertIsMyChild(entity)) {
- error("%s: Attempt to remove entity that is not a child", __func__);
+ warning("[%s] %s: Attempt to remove entity that is not a child", debugName(), __func__);
}
if (isVisible()) {
@@ -401,14 +402,17 @@ uint16 StageActor::queryChildrenAboutMouseEvents(
CylindricalWrapMode wrapMode) {
uint16 result = 0;
- Common::Point adjustedPoint = point - _boundingBox.origin();
+ Common::Point mousePosRelativeToStageOrigin = point - _boundingBox.origin();
for (auto childIterator = _children.end(); childIterator != _children.begin();) {
--childIterator; // Decrement first, then dereference
SpatialEntity *child = *childIterator;
- debugC(7, kDebugEvents, " %s: Checking actor %d (z-index: %d) (eventMask: 0x%02x) (result: 0x%02x) (wrapMode: %d)", __func__, child->id(), child->zIndex(), eventMask, result, wrapMode);
+ debugC(6, kDebugEvents, " [%s] %s: Checking %s (mousePos: %d, %d -> %d, %d) (bounds: %d, %d, %d, %d) (z-index: %d) (eventMask: 0x%02x) (result: 0x%02x) (wrapMode: %d)",
+ debugName(), __func__, child->debugName(),
+ point.x, point.y, mousePosRelativeToStageOrigin.x, mousePosRelativeToStageOrigin.y,
+ PRINT_RECT(child->getBbox()), child->zIndex(), eventMask, result, wrapMode);
child->setAdjustedBounds(wrapMode);
- uint16 handledEvents = child->findActorToAcceptMouseEvents(adjustedPoint, eventMask, state, true);
+ uint16 handledEvents = child->findActorToAcceptMouseEvents(mousePosRelativeToStageOrigin, eventMask, state, true);
child->setAdjustedBounds(kWrapNone);
eventMask &= ~handledEvents;
@@ -515,11 +519,11 @@ uint16 StageActor::findActorToAcceptMouseEvents(
MouseActorState &state,
bool inBounds) {
- debugC(6, kDebugEvents, " --- %s ---", __func__);
-
- Common::Point mousePosAdjustedByStageOrigin = point;
- mousePosAdjustedByStageOrigin.x -= _boundingBox.left;
- mousePosAdjustedByStageOrigin.y -= _boundingBox.top;
+ Common::Point mousePosRelativeToStageOrigin = point;
+ mousePosRelativeToStageOrigin.x -= _boundingBox.left;
+ mousePosRelativeToStageOrigin.y -= _boundingBox.top;
+ debugC(4, kDebugEvents, "[%s] %s: mousePos: (%d, %d), relativeToStage: (%d, %d)", debugName(), __func__,
+ point.x, point.y, mousePosRelativeToStageOrigin.x, mousePosRelativeToStageOrigin.y);
uint16 result;
if (_cameras.empty()) {
@@ -528,12 +532,11 @@ uint16 StageActor::findActorToAcceptMouseEvents(
inBounds = true;
}
}
- result = queryChildrenAboutMouseEvents(mousePosAdjustedByStageOrigin, eventMask, state, kWrapNone);
+ result = queryChildrenAboutMouseEvents(point, eventMask, state, kWrapNone);
} else {
- result = findActorToAcceptMouseEventsCamera(mousePosAdjustedByStageOrigin, eventMask, state, inBounds);
+ result = findActorToAcceptMouseEventsCamera(mousePosRelativeToStageOrigin, eventMask, state, inBounds);
}
- debugC(6, kDebugEvents, " --- END %s ---", __func__);
return result;
}
@@ -601,7 +604,7 @@ void StageActor::invalidateLocalZIndex() {
void StageActor::invalidateZIndexOf(const SpatialEntity *entity) {
if (!assertIsMyChild(entity)) {
- error("%s: Attempt to invalidate local z-index of non-child", __func__);
+ error("[%s] %s: Attempt to invalidate local z-index of non-child", debugName(), __func__);
}
// Remove the entity from the sorted array and re-insert it at the correct position.
@@ -707,7 +710,7 @@ void RootStage::deleteChildrenFromContextId(uint contextId) {
void RootStage::setMousePosition(int16 x, int16 y) {
x += _boundingBox.left;
y += _boundingBox.top;
- warning("%s: STUB: (%d, %d)", __func__, x, y);
+ warning("[%s] %s: STUB: (%d, %d)", debugName(), __func__, x, y);
}
StageDirector::StageDirector() {
@@ -739,7 +742,7 @@ void StageDirector::handleKeyboardEvent(const Common::Event &event) {
MouseActorState state;
uint16 flags = _rootStage->findActorToAcceptKeyboardEvents(event.kbd.ascii, kKeyDownFlag, state);
if (flags & kKeyDownFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.keyDown->id());
+ debugC(5, kDebugEvents, "%s: Dispatching to %s from root stage", __func__, state.keyDown->debugName());
state.keyDown->keyboardEvent(event);
}
}
@@ -748,7 +751,8 @@ void StageDirector::handleMouseDownEvent(const Common::Event &event) {
MouseActorState state;
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseDownFlag, state, false);
if (flags & kMouseDownFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.mouseDown->id());
+ debugC(5, kDebugEvents, "%s: Dispatching to %s from root stage (mousePos: %d, %d) (bounds: %d, %d, %d, %d)",
+ __func__, state.mouseDown->debugName(), event.mouse.x, event.mouse.y, PRINT_RECT(state.mouseDown->getBbox()));
state.mouseDown->mouseDownEvent(event);
}
}
@@ -757,7 +761,8 @@ void StageDirector::handleMouseUpEvent(const Common::Event &event) {
MouseActorState state;
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseUpFlag, state, false);
if (flags & kMouseUpFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching to actor %d", __func__, state.mouseUp->id());
+ debugC(5, kDebugEvents, "%s: Dispatching to %s from root stage (mousePos: %d, %d) (bounds: %d, %d, %d, %d)",
+ __func__, state.mouseUp->debugName(), event.mouse.x, event.mouse.y, PRINT_RECT(state.mouseUp->getBbox()));
state.mouseUp->mouseUpEvent(event);
}
}
@@ -768,11 +773,11 @@ void StageDirector::handleMouseMovedEvent(const Common::Event &event) {
event.mouse,
kMouseEnterFlag | kMouseExitFlag | kMouseMovedFlag,
state, false);
- debugC(5, kDebugEvents, "%s: Calling sendMouseEnterExitEvent", __func__);
sendMouseEnterExitEvent(flags, state, event);
if (flags & kMouseMovedFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching mouse moved to actor %d", __func__, state.mouseMoved->id());
+ debugC(5, kDebugEvents, "%s: Dispatching to %s (mousePos: %d, %d) (bounds: %d, %d, %d, %d)",
+ __func__, state.mouseMoved->debugName(), event.mouse.x, event.mouse.y, PRINT_RECT(state.mouseMoved->getBbox()));
state.mouseMoved->mouseMovedEvent(event);
}
}
@@ -788,12 +793,12 @@ void StageDirector::handleMouseOutOfFocusEvent(const Common::Event &event) {
uint16 flags = _rootStage->findActorToAcceptMouseEvents(event.mouse, kMouseExitFlag | kMouseOutOfFocusFlag, state, false);
if (flags & kMouseExitFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching mouse enter to actor %d", __func__, state.mouseExit->id());
+ debugC(5, kDebugEvents, "%s: Dispatching mouse enter to %s", __func__, state.mouseExit->debugName());
state.mouseExit->mouseExitedEvent(event);
}
if (flags & kMouseOutOfFocusFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching mouse out of focus to actor %d", __func__, state.mouseOutOfFocus->id());
+ debugC(5, kDebugEvents, "%s: Dispatching mouse out of focus to %s", __func__, state.mouseOutOfFocus->debugName());
state.mouseOutOfFocus->mouseOutOfFocusEvent(event);
}
}
@@ -801,16 +806,16 @@ void StageDirector::handleMouseOutOfFocusEvent(const Common::Event &event) {
void StageDirector::sendMouseEnterExitEvent(uint16 flags, MouseActorState &state, const Common::Event &event) {
if (state.mouseMoved != state.mouseEnter || state.mouseMoved != state.mouseExit) {
if (flags & kMouseEnterFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching mouse enter to actor %d", __func__, state.mouseEnter->id());
+ debugC(5, kDebugEvents, "%s: Dispatching mouse enter to %s", __func__, state.mouseEnter->debugName());
state.mouseEnter->mouseEnteredEvent(event);
}
if (flags & kMouseExitFlag) {
- debugC(5, kDebugEvents, "%s: Dispatching mouse exit to actor %d", __func__, state.mouseExit->id());
+ debugC(5, kDebugEvents, "%s: Dispatching mouse exit to %s", __func__, state.mouseExit->debugName());
state.mouseExit->mouseExitedEvent(event);
}
} else {
- debugC(5, kDebugEvents, "%s: No hotspot to dispatch to", __func__);
+ debugC(5, kDebugEvents, "%s: No actor to accept event", __func__);
}
}
diff --git a/engines/mediastation/actors/timer.cpp b/engines/mediastation/actors/timer.cpp
index 44361c39592..8119a458c22 100644
--- a/engines/mediastation/actors/timer.cpp
+++ b/engines/mediastation/actors/timer.cpp
@@ -72,7 +72,7 @@ void TimerActor::timePlay() {
}
}
- debugC(5, kDebugScript, "Timer::timePlay(): Now playing for %d ms", _duration);
+ debugC(5, kDebugScript, "[%s] %s: Now playing for %d ms", debugName(), __func__, _duration);
}
void TimerActor::timeStop() {
diff --git a/engines/mediastation/audio.cpp b/engines/mediastation/audio.cpp
index ba76640e03e..97d3f662815 100644
--- a/engines/mediastation/audio.cpp
+++ b/engines/mediastation/audio.cpp
@@ -78,7 +78,6 @@ void AudioSequence::readChunk(Chunk &chunk) {
error("%s: Unknown audio encoding 0x%x", __func__, static_cast<uint>(_bitsPerSample));
}
_streams.push_back(stream);
- debugC(5, kDebugLoading, "Finished reading audio chunk (@0x%llx)", static_cast<long long int>(chunk.pos()));
}
bool AudioSequence::isActive() {
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 95c39c55d3c..b93ee4ba7cf 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -54,7 +54,7 @@ void MediaStationEngine::readControlCommands(Chunk &chunk) {
ContextSectionType sectionType = kContextEndOfSection;
do {
sectionType = static_cast<ContextSectionType>(chunk.readTypedUint16());
- debugC(5, kDebugLoading, "%s: sectionType = 0x%x (@0x%llx)", __func__, static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
+ debugC(5, kDebugLoading, "%s: command 0x%x", __func__, static_cast<uint>(sectionType));
if (sectionType != kContextEndOfSection) {
readCommandFromStream(chunk, sectionType);
}
@@ -80,14 +80,14 @@ void MediaStationEngine::readDestroyContextData(Chunk &chunk) {
void MediaStationEngine::readDestroyActorData(Chunk &chunk) {
uint actorId = chunk.readTypedUint16();
- debugC(5, kDebugLoading, "%s: Actor %d", __func__, actorId);
+ debugC(5, kDebugLoading, "[%s] %s", g_engine->formatActorName(actorId).c_str(), __func__);
destroyActor(actorId);
}
void MediaStationEngine::readActorLoadComplete(Chunk &chunk) {
uint actorId = chunk.readTypedUint16();
- debugC(5, kDebugLoading, "%s: Actor %d", __func__, actorId);
Actor *actor = g_engine->getActorById(actorId);
+ debugC(5, kDebugLoading, "[%s] %s", actor->debugName(), __func__);
actor->loadIsComplete();
}
@@ -95,7 +95,7 @@ void MediaStationEngine::readCreateActorData(Chunk &chunk) {
uint contextId = chunk.readTypedUint16();
ActorType type = static_cast<ActorType>(chunk.readTypedUint16());
uint id = chunk.readTypedUint16();
- debugC(5, kDebugLoading, "%s: Actor %d, type 0x%x", __func__, id, static_cast<uint>(type));
+ debugC(5, kDebugLoading, "[%s] %s: type 0x%x", g_engine->formatActorName(id).c_str(), __func__, static_cast<uint>(type));
Actor *actor = nullptr;
switch (type) {
@@ -156,7 +156,7 @@ void MediaStationEngine::readCreateActorData(Chunk &chunk) {
break;
default:
- error("%s: No class for actor type 0x%x (@0x%llx)", __func__, static_cast<uint>(type), static_cast<long long int>(chunk.pos()));
+ error("%s: No class for actor type 0x%x", __func__, static_cast<uint>(type));
}
actor->setId(id);
actor->setContextId(contextId);
@@ -168,7 +168,7 @@ void MediaStationEngine::readCreateVariableData(Chunk &chunk) {
uint contextId = chunk.readTypedUint16();
uint id = chunk.readTypedUint16();
if (g_engine->getVariable(id) != nullptr) {
- error("%s: Global variable %d already exists", __func__, id);
+ error("[%s] %s: Global variable already exists", g_engine->formatVariableName(id).c_str(), __func__);
}
ScriptValue *value = new ScriptValue(&chunk);
@@ -178,19 +178,21 @@ void MediaStationEngine::readCreateVariableData(Chunk &chunk) {
}
context->_variables.setVal(id, value);
- debugC(5, kDebugScript, "%s: %d (type: %s)", __func__, id, scriptValueTypeToStr(value->getType()));
+ debugC(5, kDebugLoading, "[%s] %s", g_engine->formatVariableName(id).c_str(), __func__);
}
void MediaStationEngine::readHeaderSections(Subfile &subfile, Chunk &chunk) {
do {
+ debugC(5, kDebugLoading, "[%s] %s", g_engine->formatAssetNameForChannelIdent(chunk._id).c_str(), __func__);
ChannelClient *actor = g_engine->getChannelClientByChannelIdent(chunk._id);
if (actor == nullptr) {
- error("%s: Client \"%s\" (0x%x) does not exist or has not been read yet in this title. (@0x%llx)", __func__, tag2str(chunk._id), chunk._id, static_cast<long long int>(chunk.pos()));
+ error("%s: Client %s does not exist or has not been read yet in this title",
+ __func__, g_engine->formatAssetNameForChannelIdent(chunk._id).c_str());
}
+
if (chunk.bytesRemaining() > 0) {
actor->readChunk(chunk);
}
-
if (chunk.bytesRemaining() != 0) {
warning("%s: %d bytes remaining at end of chunk", __func__, chunk.bytesRemaining());
}
diff --git a/engines/mediastation/datafile.cpp b/engines/mediastation/datafile.cpp
index 0d71ae00863..6a365d17da4 100644
--- a/engines/mediastation/datafile.cpp
+++ b/engines/mediastation/datafile.cpp
@@ -168,10 +168,10 @@ bool Chunk::seek(int64 offset, int whence) {
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()));
+ error("%s: Attempted to seek 0x%x bytes before start of chunk (@0x%llx)", __func__, 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()));
+ error("%s: Attempted to seek 0x%x bytes past end of chunk (@0x%llx)", __func__, overrun, static_cast<long long int>(pos()));
}
return true;
}
@@ -227,7 +227,7 @@ void CdRomStream::openStream(uint streamId) {
const FileInfo &fileInfo = g_engine->fileInfoForIdent(streamInfo._fileId);
if (fileInfo._id == 0) {
- error("%s: File %d for stream %d not found in current title", __func__, streamInfo._fileId, streamId);
+ error("%s: File %s for stream %d not found in current title", __func__, g_engine->formatFileName(streamInfo._fileId).c_str(), streamId);
}
bool requestedStreamAlreadyOpen = isOpen() && _fileId == streamInfo._fileId;
@@ -312,7 +312,7 @@ void StreamFeedManager::closeStreamFeed(StreamFeed *streamFeed) {
void StreamFeedManager::registerChannelClient(ChannelClient *client) {
if (_channelClients.getValOrDefault(client->channelIdent()) != nullptr) {
- error("%s: Channel ident %d already has a client", __func__, client->channelIdent());
+ error("%s: Channel %s already has a client", __func__, g_engine->formatAssetNameForChannelIdent(client->channelIdent()).c_str());
}
_channelClients.setVal(client->channelIdent(), client);
}
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index bea28202896..fb55ddb9cd7 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -228,6 +228,7 @@ bool VideoDisplayManager::attemptToReadFromStream(Chunk &chunk, uint sectionType
break;
case kVideoDisplayManagerSetTime:
+ debugC(5, kDebugGraphics, "%s", __func__);
_defaultTransitionTime = chunk.readTypedTime();
break;
@@ -253,6 +254,7 @@ void VideoDisplayManager::readAndEffectTransition(Chunk &chunk) {
}
void VideoDisplayManager::readAndRegisterPalette(Chunk &chunk) {
+ debugC(5, kDebugGraphics, "%s", __func__);
byte *buffer = new byte[Graphics::PALETTE_SIZE];
chunk.read(buffer, Graphics::PALETTE_SIZE);
if (_registeredPalette != nullptr) {
@@ -330,10 +332,12 @@ void VideoDisplayManager::doTransitionOnSync() {
}
void VideoDisplayManager::performUpdateDirty() {
+ debugC(5, kDebugGraphics, "%s", __func__);
g_engine->draw();
}
void VideoDisplayManager::performUpdateAll() {
+ debugC(5, kDebugGraphics, "%s", __func__);
g_engine->draw(false);
}
@@ -350,6 +354,7 @@ void VideoDisplayManager::fadeToBlack(Common::Array<ScriptValue> &args) {
colorCount = static_cast<uint>(args[3].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: fadeTime: %f, paletteRange: [%d, %d]", __func__, fadeTime, startIndex, startIndex + colorCount);
_fadeToColor(0, 0, 0, fadeTime, startIndex, colorCount);
}
@@ -366,6 +371,7 @@ void VideoDisplayManager::fadeToRegisteredPalette(Common::Array<ScriptValue> &ar
colorCount = static_cast<uint>(args[3].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: fadeTime: %f, paletteRange: [%d, %d]", __func__, fadeTime, startIndex, startIndex + colorCount);
_fadeToRegisteredPalette(fadeTime, startIndex, colorCount);
}
@@ -378,6 +384,7 @@ void VideoDisplayManager::setToRegisteredPalette(Common::Array<ScriptValue> &arg
colorCount = static_cast<uint>(args[2].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: colors: [%d, %d]", __func__, startIndex, startIndex + colorCount);
_setToRegisteredPalette(startIndex, colorCount);
}
@@ -390,6 +397,7 @@ void VideoDisplayManager::setToBlack(Common::Array<ScriptValue> &args) {
colorCount = static_cast<uint>(args[2].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: colors: [%d, %d]", __func__, startIndex, startIndex + colorCount);
_setToColor(0, 0, 0, startIndex, colorCount);
}
@@ -411,6 +419,8 @@ void VideoDisplayManager::fadeToColor(Common::Array<ScriptValue> &args) {
colorCount = static_cast<uint>(args[7].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: (%d, %d, %d), fadeTime: %f, paletteRange: [%d, %d]",
+ __func__, r, g, b, fadeTime, startIndex, startIndex + colorCount);
_fadeToColor(r, g, b, fadeTime, startIndex, colorCount);
}
@@ -425,6 +435,8 @@ void VideoDisplayManager::setToColor(Common::Array<ScriptValue> &args) {
uint startIndex = static_cast<uint>(args[4].asFloat());
uint colorCount = static_cast<uint>(args[5].asFloat());
+ debugC(5, kDebugGraphics, "%s: (%d, %d, %d), paletteRange: [%d, %d]",
+ __func__, r, g, b, startIndex, startIndex + colorCount);
_setToColor(r, g, b, startIndex, colorCount);
}
@@ -440,6 +452,8 @@ void VideoDisplayManager::setToPercentOfPalette(Common::Array<ScriptValue> &args
uint startIndex = static_cast<uint>(args[5].asFloat());
uint colorCount = static_cast<uint>(args[6].asFloat());
+ debugC(5, kDebugGraphics, "%s: %f of (%d, %d, %d), paletteRange: [%d, %d]",
+ __func__, percent, r, g, b, startIndex, startIndex + colorCount);
_setPercentToColor(percent, r, g, b, startIndex, colorCount);
}
@@ -463,6 +477,8 @@ void VideoDisplayManager::fadeToPaletteObject(Common::Array<ScriptValue> &args)
colorCount = static_cast<uint>(args[4].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: %d, fadeTime: %f, paletteRange: [%d, %d]",
+ __func__, paletteId, fadeTime, startIndex, startIndex + colorCount);
_fadeToPaletteObject(paletteId, fadeTime, startIndex, colorCount);
}
@@ -482,6 +498,8 @@ void VideoDisplayManager::setToPaletteObject(Common::Array<ScriptValue> &args) {
colorCount = static_cast<uint>(args[3].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: %d, paletteRange: [%d, %d]",
+ __func__, paletteId, startIndex, startIndex + colorCount);
_setToPaletteObject(paletteId, startIndex, colorCount);
}
@@ -503,6 +521,8 @@ void VideoDisplayManager::setToPercentOfPaletteObject(Common::Array<ScriptValue>
colorCount = static_cast<uint>(args[4].asFloat());
}
+ debugC(5, kDebugGraphics, "%s: %f of %d, paletteRange: [%d, %d]",
+ __func__, percent, paletteId, startIndex, startIndex + colorCount);
_setPercentToPaletteObject(percent, paletteId, startIndex, colorCount);
}
@@ -516,6 +536,8 @@ void VideoDisplayManager::colorShiftCurrentPalette(Common::Array<ScriptValue> &a
uint startIndex = static_cast<uint>(args[2].asFloat());
uint colorCount = static_cast<uint>(args[3].asFloat());
+ debugC(5, kDebugGraphics, "%s: shift: %d, paletteRange: [%d, %d]",
+ __func__, shift, startIndex, startIndex + colorCount);
_colorShiftCurrentPalette(startIndex, shift, colorCount);
}
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index f0db162afd1..b2e619c7a03 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -237,14 +237,16 @@ ScriptValue CodeChunk::evaluateValue() {
case kOperandTypeParamToken: {
uint literal = _bytecode->readTypedUint16();
- debugC(5, kDebugScript, "%d ", literal);
+ Common::String tokenName = g_engine->formatParamTokenName(literal);
+ debugC(5, kDebugScript, "%s ", tokenName.c_str());
returnValue.setToParamToken(literal);
return returnValue;
}
case kOperandTypeActorId: {
uint actorId = _bytecode->readTypedUint16();
- debugC(5, kDebugScript, "%d ", actorId);
+ Common::String actorName = g_engine->formatActorName(actorId, true);
+ debugC(5, kDebugScript, "%s ", actorName.c_str());
returnValue.setToActorId(actorId);
return returnValue;
}
@@ -263,7 +265,9 @@ ScriptValue CodeChunk::evaluateValue() {
case kOperandTypeFunctionId: {
uint functionId = _bytecode->readTypedUint16();
- debugC(5, kDebugScript, "%d ", functionId);
+ // Function IDs are included in this same listing that also includes actors.
+ Common::String functionName = g_engine->formatFunctionName(functionId);
+ debugC(5, kDebugScript, "%s ", functionName.c_str());
returnValue.setToFunctionId(functionId);
return returnValue;
}
@@ -288,27 +292,29 @@ ScriptValue CodeChunk::evaluateVariable() {
ScriptValue *CodeChunk::readAndReturnVariable() {
uint id = _bytecode->readTypedUint16();
VariableScope scope = static_cast<VariableScope>(_bytecode->readTypedUint16());
- debugC(5, kDebugScript, "%d (%s)", id, variableScopeToStr(scope));
+ Common::String name = g_engine->formatVariableName(id);
- ScriptValue returnValue;
+ ScriptValue *variable = nullptr;
switch (scope) {
case kVariableScopeGlobal: {
- ScriptValue *variable = g_engine->getVariable(id);
+ variable = g_engine->getVariable(id);
if (variable == nullptr) {
- error("%s: Global variable %d doesn't exist", __func__, id);
+ error("%s: Global variable %s doesn't exist", __func__, g_engine->formatVariableName(id).c_str());
}
- return variable;
+ break;
}
case kVariableScopeLocal: {
uint index = id - 1;
- return &_locals.operator[](index);
+ variable = &_locals.operator[](index);
+ break;
}
case kVariableScopeIndirectParameter: {
ScriptValue indexValue = evaluateExpression();
uint index = static_cast<uint>(indexValue.asFloat() + id);
- return &_args->operator[](index);
+ variable = &_args->operator[](index);
+ break;
}
case kVariableScopeParameter: {
@@ -316,12 +322,16 @@ ScriptValue *CodeChunk::readAndReturnVariable() {
if (_args == nullptr) {
error("%s: Requested a parameter in a code chunk that has no parameters", __func__);
}
- return &_args->operator[](index);
+ variable = &_args->operator[](index);
+ break;
}
default:
error("%s: Got unknown variable scope %s (%d)", __func__, variableScopeToStr(scope), static_cast<uint>(scope));
}
+
+ debugC(5, kDebugScript, "%s (%s) [value: %s]", name.c_str(), variableScopeToStr(scope), variable->getDebugString().c_str());
+ return variable;
}
void CodeChunk::evaluateIf() {
@@ -465,7 +475,8 @@ ScriptValue CodeChunk::evaluateFunctionCall(bool isIndirect) {
}
ScriptValue CodeChunk::evaluateFunctionCall(uint functionId, uint paramCount) {
- debugC(5, kDebugScript, "%d (%d params)", functionId, paramCount);
+ Common::String functionName = g_engine->formatFunctionName(functionId);
+ debugC(5, kDebugScript, "%s (%d params)", functionName.c_str(), paramCount);
Common::Array<ScriptValue> args;
for (uint i = 0; i < paramCount; i++) {
@@ -515,14 +526,14 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
if (target.asActorId() == 0) {
// It seems to be valid to call a method on a null actor ID, in
// which case nothing happens. Still issue warning for traceability.
- warning("%s: Attempt to call method on a null actor ID", __func__);
+ warning("%s: Attempt to call method %s (%d) on null actor ID", __func__, builtInMethodToStr(method), static_cast<uint>(method));
return returnValue;
} else {
// This is a regular actor that we can process directly.
uint actorId = target.asActorId();
Actor *targetActor = g_engine->getActorById(actorId);
if (targetActor == nullptr) {
- error("%s: Attempt to call method on actor ID %d, which isn't loaded", __func__, target.asActorId());
+ error("[%s] %s: Actor not loaded", g_engine->formatActorName(target.asActorId()).c_str(), __func__);
}
returnValue = targetActor->callMethod(method, args);
return returnValue;
@@ -536,7 +547,8 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
}
default:
- error("Attempt to call method on unimplemented value type %s (%d)",
+ error("%s: Attempt to call method %s (%d) on unimplemented value type %s (%d)", __func__,
+ builtInMethodToStr(method), static_cast<uint>(method),
scriptValueTypeToStr(target.getType()), static_cast<uint>(target.getType()));
}
}
@@ -544,7 +556,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
void CodeChunk::evaluateDeclareLocals() {
uint localVariableCount = _bytecode->readTypedUint16();
if (localVariableCount <= 0) {
- error("Got non-positive local variable count");
+ error("%s: Got non-positive local variable count", __func__);
}
debugC(5, kDebugScript, "%d", localVariableCount);
_locals = Common::Array<ScriptValue>(localVariableCount);
@@ -569,11 +581,11 @@ void CodeChunk::evaluateWhileLoop() {
_bytecode->seek(loopStartPosition);
ScriptValue condition = evaluateExpression();
if (condition.getType() != kScriptValueTypeBool) {
- error("Expected loop condition to be bool, not %s", scriptValueTypeToStr(condition.getType()));
+ error("%s: Expected loop condition to be bool, not %s", __func__, scriptValueTypeToStr(condition.getType()));
}
if (++iterationCount >= MAX_LOOP_ITERATION_COUNT) {
- error("Exceeded max loop iteration count");
+ error("%s: Exceeded max loop iteration count", __func__);
}
if (condition.asBool()) {
diff --git a/engines/mediastation/mediascript/collection.cpp b/engines/mediastation/mediascript/collection.cpp
index 52b64037bb7..195a84f68f5 100644
--- a/engines/mediastation/mediascript/collection.cpp
+++ b/engines/mediastation/mediascript/collection.cpp
@@ -47,12 +47,22 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
case kDeleteFirstMethod:
assert(args.empty());
- returnValue = remove_at(0);
+ if (size() > 0) {
+ returnValue = remove_at(0);
+ debugC(7, kDebugScript, "%s: %s", __func__, returnValue.getDebugString().c_str());
+ } else {
+ warning("%s: Array is empty", __func__);
+ }
break;
case kDeleteLastMethod:
assert(args.empty());
- returnValue = remove_at(size() - 1);
+ if (size() > 0) {
+ returnValue = remove_at(size() - 1);
+ debugC(7, kDebugScript, "%s: %s", __func__, returnValue.getDebugString().c_str());
+ } else {
+ warning("%s: Array is empty", __func__);
+ }
break;
case kEmptyMethod:
@@ -63,7 +73,11 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
case kGetAtMethod: {
assert(args.size() == 1);
uint index = static_cast<uint>(args[0].asFloat());
- returnValue = operator[](index);
+ if (index < size()) {
+ returnValue = operator[](index);
+ } else {
+ warning("%s: Index %d out of bounds %d", __func__, index, size());
+ }
break;
}
@@ -91,21 +105,34 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
case kDeleteAtMethod: {
assert(args.size() == 1);
uint index = static_cast<uint>(args[0].asFloat());
- returnValue = remove_at(index);
+ if (index < size()) {
+ returnValue = remove_at(index);
+ debugC(7, kDebugScript, "%s: %s", __func__, returnValue.getDebugString().c_str());
+ } else {
+ warning("%s: Index %d out of bounds %d", __func__, index, size());
+ }
break;
}
case kInsertAtMethod: {
assert(args.size() == 2);
uint index = static_cast<uint>(args[1].asFloat());
- insert_at(index, args[0]);
+ if (index <= size()) {
+ insert_at(index, args[0]);
+ } else {
+ warning("%s: Index %d out of bounds %d", __func__, index, size());
+ }
break;
}
case kReplaceAtMethod: {
assert(args.size() == 2);
uint index = static_cast<uint>(args[1].asFloat());
- operator[](index) = args[0];
+ if (index < size()) {
+ operator[](index) = args[0];
+ } else {
+ warning("%s: Index %d out of bounds %d", __func__, index, size());
+ }
break;
}
@@ -130,6 +157,7 @@ void Collection::apply(const Common::Array<ScriptValue> &args) {
uint functionId = args[0].asFunctionId();
for (const ScriptValue &item : *this) {
argsToApply[0] = item;
+ debugC(7, kDebugScript, "%s: %s: %s", __func__, g_engine->formatFunctionName(functionId).c_str(), item.getDebugString().c_str());
g_engine->getFunctionManager()->call(functionId, argsToApply);
}
}
@@ -148,15 +176,19 @@ void Collection::send(const Common::Array<ScriptValue> &args) {
uint actorId = item.asActorId();
Actor *targetActor = g_engine->getActorById(actorId);
if (targetActor != nullptr) {
+ debugC(7, kDebugScript, "%s: %s: %d", __func__, builtInMethodToStr(methodToSend), actorId);
targetActor->callMethod(methodToSend, argsToSend);
}
}
}
-int Collection::seek(const ScriptValue &item) {
+int Collection::seek(const ScriptValue &lhs) {
// Search from back to front.
for (int i = size() - 1; i >= 0; i--) {
- if (item == operator[](i)) {
+ const ScriptValue &rhs = operator[](i);
+ debugC(7, kDebugScript, "%s: %d of %d: Checking (%s) == (%s)",
+ __func__, i, size(), lhs.getDebugString().c_str(), rhs.getDebugString().c_str());
+ if (lhs == rhs) {
return i;
}
}
diff --git a/engines/mediastation/mediascript/eventhandler.cpp b/engines/mediastation/mediascript/eventhandler.cpp
index d97321475bf..ae597207d10 100644
--- a/engines/mediastation/mediascript/eventhandler.cpp
+++ b/engines/mediastation/mediascript/eventhandler.cpp
@@ -21,13 +21,13 @@
#include "mediastation/mediascript/eventhandler.h"
#include "mediastation/debugchannels.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
EventHandler::EventHandler(Chunk &chunk) {
_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()));
+ debugC(5, kDebugLoading, "%s: %s (%d)", __func__, eventTypeToStr(_type), static_cast<uint>(_type));
_argumentValue = ScriptValue(&chunk);
_code = new CodeChunk(chunk);
@@ -36,7 +36,8 @@ EventHandler::EventHandler(Chunk &chunk) {
ScriptValue EventHandler::execute(uint actorId) {
// TODO: The actorId is only passed in for debug visibility, there should be
// a better way to handle that.
- Common::String actorAndType = Common::String::format("(actor %d) (type = %s)", actorId, eventTypeToStr(_type));
+ Common::String actorName = g_engine->formatActorName(actorId, true);
+ Common::String actorAndType = Common::String::format("%s (%s)", actorName.c_str(), eventTypeToStr(_type));
Common::String argValue = Common::String::format("(%s)", _argumentValue.getDebugString().c_str());
debugC(5, kDebugScript, "\n********** EVENT HANDLER %s %s **********", actorAndType.c_str(), argValue.c_str());
diff --git a/engines/mediastation/mediascript/function.cpp b/engines/mediastation/mediascript/function.cpp
index 5075cbc4b21..490b968e978 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -26,9 +26,6 @@
namespace MediaStation {
ScriptFunction::ScriptFunction(Chunk &chunk) {
_contextId = 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 = chunk.readTypedUint16();
_code = new CodeChunk(chunk);
}
@@ -39,7 +36,8 @@ ScriptFunction::~ScriptFunction() {
}
ScriptValue ScriptFunction::execute(Common::Array<ScriptValue> &args) {
- debugC(5, kDebugScript, "\n********** SCRIPT FUNCTION %d **********", _id);
+ Common::String name = g_engine->formatFunctionName(_id);
+ debugC(5, kDebugScript, "\n********** SCRIPT FUNCTION %s **********", name.c_str());
ScriptValue returnValue = _code->execute(&args);
debugC(5, kDebugScript, "********** END SCRIPT FUNCTION **********");
return returnValue;
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 75af8bcb854..833dc63158c 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -443,7 +443,7 @@ const char *operandTypeToStr(OperandType type) {
case kOperandTypeString:
return "String";
case kOperandTypeParamToken:
- return "DollarSignVariable";
+ return "ParamToken";
case kOperandTypeActorId:
return "ActorId";
case kOperandTypeTime:
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index b6b67e0dcc5..283814e5eaf 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -21,6 +21,7 @@
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/function.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
@@ -232,7 +233,7 @@ BuiltInMethod ScriptValue::asMethodId() const {
}
}
-Common::String ScriptValue::getDebugString() {
+Common::String ScriptValue::getDebugString() const {
switch (getType()) {
case kScriptValueTypeEmpty:
return "empty";
@@ -240,14 +241,21 @@ Common::String ScriptValue::getDebugString() {
case kScriptValueTypeFloat:
return Common::String::format("float: %f", asFloat());
- case kScriptValueTypeActorId:
- return Common::String::format("actor: %d", asActorId());
+ case kScriptValueTypeActorId: {
+ Common::String actorName = g_engine->formatActorName(asActorId(), true);
+ return Common::String::format("actor: %s", actorName.c_str());
+ }
case kScriptValueTypeTime:
return Common::String::format("time: %f", asTime());
- case kScriptValueTypeParamToken:
- return Common::String::format("token: %d", asParamToken());
+ case kScriptValueTypeParamToken: {
+ Common::String tokenName = g_engine->formatParamTokenName(asParamToken());
+ return Common::String::format("token: %s", tokenName.c_str());
+ }
+
+ case kScriptValueTypeString:
+ return Common::String::format("string: \"%s\"", asString().c_str());
default:
return Common::String::format("arg type %s", scriptValueTypeToStr(getType()));
@@ -256,52 +264,56 @@ Common::String ScriptValue::getDebugString() {
bool ScriptValue::compare(Opcode op, const ScriptValue &lhs, const ScriptValue &rhs) {
if (lhs.getType() != rhs.getType()) {
- warning("%s: Attempt to compare mismatched types %s and %s", __func__, scriptValueTypeToStr(lhs.getType()), scriptValueTypeToStr(rhs.getType()));
+ warning("%s: Attempt to compare mismatched values: %s; %s",
+ __func__, lhs.getDebugString().c_str(), rhs.getDebugString().c_str());
}
+ bool result = false;
switch (lhs.getType()) {
case kScriptValueTypeEmpty:
- return compareEmptyValues(op);
+ result = compareEmptyValues(op);
+ break;
case kScriptValueTypeFloat:
- return compare(op, lhs.asFloat(), rhs.asFloat());
+ result = compare(op, lhs.asFloat(), rhs.asFloat());
break;
case kScriptValueTypeBool:
- return compare(op, lhs.asBool(), rhs.asBool());
+ result = compare(op, lhs.asBool(), rhs.asBool());
break;
case kScriptValueTypeTime:
- return compare(op, lhs.asTime(), rhs.asTime());
+ result = compare(op, lhs.asTime(), rhs.asTime());
break;
case kScriptValueTypeParamToken:
- return compare(op, lhs.asParamToken(), rhs.asParamToken());
+ result = compare(op, lhs.asParamToken(), rhs.asParamToken());
break;
case kScriptValueTypeActorId:
- return compare(op, lhs.asActorId(), rhs.asActorId());
+ result = compare(op, lhs.asActorId(), rhs.asActorId());
break;
case kScriptValueTypeString:
- return compareStrings(op, lhs.asString(), rhs.asString());
+ result = compareStrings(op, lhs.asString(), rhs.asString());
break;
case kScriptValueTypeCollection:
- return compare(op, lhs.asCollection(), rhs.asCollection());
+ result = compare(op, lhs.asCollection(), rhs.asCollection());
break;
case kScriptValueTypeFunctionId:
- return compare(op, lhs.asFunctionId(), rhs.asFunctionId());
+ result = compare(op, lhs.asFunctionId(), rhs.asFunctionId());
break;
case kScriptValueTypeMethodId:
- return compare(op, static_cast<uint>(lhs.asMethodId()), static_cast<uint>(rhs.asMethodId()));
+ result = compare(op, static_cast<uint>(lhs.asMethodId()), static_cast<uint>(rhs.asMethodId()));
break;
default:
error("%s: Got unknown script value type %d", __func__, lhs.getType());
}
+ return result;
}
bool ScriptValue::compareEmptyValues(Opcode op) {
diff --git a/engines/mediastation/mediascript/scriptvalue.h b/engines/mediastation/mediascript/scriptvalue.h
index eb738b77751..6b8fc130f24 100644
--- a/engines/mediastation/mediascript/scriptvalue.h
+++ b/engines/mediastation/mediascript/scriptvalue.h
@@ -70,7 +70,7 @@ public:
void setToMethodId(BuiltInMethod methodId);
BuiltInMethod asMethodId() const;
- Common::String getDebugString();
+ Common::String getDebugString() const;
bool operator==(const ScriptValue &other) const;
bool operator!=(const ScriptValue &other) const;
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index f187ed526d7..c99212b81fd 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -87,6 +87,9 @@ MediaStationEngine::~MediaStationEngine() {
delete _streamFeedManager;
_streamFeedManager = nullptr;
+ delete _profile;
+ _profile = nullptr;
+
_contextReferences.clear();
_streamMap.clear();
_engineResourceDeclarations.clear();
@@ -103,7 +106,7 @@ SpatialEntity *MediaStationEngine::getSpatialEntityById(uint spatialEntityId) {
Actor *actor = getActorById(spatialEntityId);
if (actor != nullptr) {
if (!actor->isSpatialActor()) {
- error("%s: Actor %d is not a spatial actor", __func__, spatialEntityId);
+ error("[%s] %s: Not a spatial actor", formatActorName(spatialEntityId).c_str(), __func__);
}
return static_cast<SpatialEntity *>(actor);
}
@@ -152,6 +155,7 @@ Common::Error MediaStationEngine::run() {
initDeviceOwner();
initStageDirector();
initStreamFeedManager();
+ initProfile();
setupInitialStreamMap();
if (ConfMan.hasKey("entry_context")) {
@@ -176,12 +180,12 @@ void MediaStationEngine::runEventLoop() {
}
_document->process();
- debugC(5, kDebugGraphics, "***** START SCREEN UPDATE ***");
+ debugC(9, kDebugGraphics, "***** START SCREEN UPDATE ***");
for (auto it = _actors.begin(); it != _actors.end(); ++it) {
it->_value->process();
}
draw();
- debugC(5, kDebugGraphics, "***** END SCREEN UPDATE ***");
+ debugC(9, kDebugGraphics, "***** END SCREEN UPDATE ***");
g_system->delayMillis(10);
}
@@ -231,6 +235,11 @@ void MediaStationEngine::initStreamFeedManager() {
registerWithStreamManager();
}
+void MediaStationEngine::initProfile() {
+ _profile = new Profile();
+ _profile->load("PROFILE._ST");
+}
+
void MediaStationEngine::setupInitialStreamMap() {
StreamInfo streamInfo;
streamInfo._actorId = 0;
@@ -299,7 +308,7 @@ void MediaStationEngine::draw(bool dirtyOnly) {
void MediaStationEngine::registerActor(Actor *actorToAdd) {
if (getActorById(actorToAdd->id())) {
- error("%s: Actor with ID 0x%d was already defined in this title", __func__, actorToAdd->id());
+ error("[%s] %s: Already defined in this title", formatActorName(actorToAdd).c_str(), __func__);
}
_actors.setVal(actorToAdd->id(), actorToAdd);
}
@@ -310,15 +319,16 @@ void MediaStationEngine::destroyActor(uint actorId) {
delete _actors[actorId];
_actors.erase(actorId);
} else {
- warning("%s: Actor %d is not currently loaded", __func__, actorId);
+ warning("[%s] %s: Not currently loaded", formatActorName(actorId).c_str(), __func__);
}
}
void MediaStationEngine::destroyContext(uint contextId, bool eraseFromLoadedContexts) {
- debugC(5, kDebugScript, "%s: Destroying context %d", __func__, contextId);
+ debugC(5, kDebugScript, "%s: Context %d", __func__, contextId);
Context *context = _loadedContexts.getValOrDefault(contextId);
if (context == nullptr) {
- error("%s: Attempted to unload context %d that is not currently loaded", __func__, contextId);
+ warning("%s: Attempted to unload context %d that is not currently loaded", __func__, contextId);
+ return;
}
getRootStage()->deleteChildrenFromContextId(contextId);
@@ -374,7 +384,7 @@ void MediaStationEngine::readUnrecognizedFromStream(Chunk &chunk, uint sectionTy
}
if (!paramHandled) {
- warning("%s: Parameter %d not handled", __func__, sectionType);
+ warning("%s: Parameter %d not handled (0x%llx)", __func__, sectionType, static_cast<long long int>(chunk.pos()));
}
}
@@ -390,7 +400,7 @@ void MediaStationEngine::readChunk(Chunk &chunk) {
break;
default:
- error("%s: Unhandled section type 0x%x", __func__, static_cast<uint>(streamType));
+ error("%s: Unhandled section type 0x%x (0x%llx)", __func__, static_cast<uint>(streamType), static_cast<long long int>(chunk.pos()));
}
}
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index d5d20411980..f57c6c27c10 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -43,6 +43,7 @@
#include "mediastation/actor.h"
#include "mediastation/cursors.h"
#include "mediastation/graphics.h"
+#include "mediastation/profile.h"
#include "mediastation/mediascript/function.h"
#include "mediastation/actors/stage.h"
@@ -107,6 +108,14 @@ public:
StreamFeedManager *getStreamFeedManager() { return _streamFeedManager; }
Document *getDocument() { return _document; }
+ Common::String formatActorName(uint actorId, bool attemptToGetType = false) { return _profile->formatActorName(actorId, attemptToGetType); }
+ Common::String formatActorName(const Actor *actor) { return _profile->formatActorName(actor); }
+ Common::String formatFunctionName(uint functionId) { return _profile->formatFunctionName(functionId); }
+ Common::String formatFileName(uint fileId) { return _profile->formatFileName(fileId); }
+ Common::String formatVariableName(uint variableId) { return _profile->formatVariableName(variableId); }
+ Common::String formatParamTokenName(uint paramToken) { return _profile->formatParamTokenName(paramToken); }
+ Common::String formatAssetNameForChannelIdent(uint channelIdent) { return _profile->formatAssetNameForChannelIdent(channelIdent); }
+
const FileInfo &fileInfoForIdent(uint fileId) { return _fileMap.getValOrDefault(fileId); }
const StreamInfo &streamInfoForIdent(uint streamId) { return _streamMap.getValOrDefault(streamId); }
const ScreenReference &screenRefWithId(uint screenActorId) { return _screenReferences.getValOrDefault(screenActorId); }
@@ -144,6 +153,7 @@ private:
DeviceOwner *_deviceOwner = nullptr;
StageDirector *_stageDirector = nullptr;
StreamFeedManager *_streamFeedManager = nullptr;
+ Profile *_profile = nullptr;
Common::HashMap<uint, Actor *> _actors;
SpatialEntity *_mouseInsideHotspot = nullptr;
@@ -169,6 +179,7 @@ private:
void initDeviceOwner();
void initStageDirector();
void initStreamFeedManager();
+ void initProfile();
void setupInitialStreamMap();
void runEventLoop();
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index 475e70fea9d..8f1d9a51e75 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -32,7 +32,8 @@ MODULE_OBJS = \
mediascript/scriptconstants.o \
mediascript/scriptvalue.o \
mediastation.o \
- metaengine.o
+ metaengine.o \
+ profile.o
# This module can be built as a plugin
ifeq ($(ENABLE_MEDIASTATION), DYNAMIC_PLUGIN)
diff --git a/engines/mediastation/profile.cpp b/engines/mediastation/profile.cpp
new file mode 100644
index 00000000000..0c9e20abad0
--- /dev/null
+++ b/engines/mediastation/profile.cpp
@@ -0,0 +1,315 @@
+/* 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 "common/tokenizer.h"
+
+#include "mediastation/actor.h"
+#include "mediastation/debugchannels.h"
+#include "mediastation/profile.h"
+#include "mediastation/mediastation.h"
+#include "mediastation/mediascript/scriptconstants.h"
+
+namespace MediaStation {
+
+void Profile::readSection(Common::File &file, void (Profile::*sectionParserMethod)(const Common::String &)) {
+ const Common::String IMAGE_SET_LINE_DELIMITER = Common::String('#');
+ const Common::String SUMMARY_LINE_DELIMITER = Common::String('*');
+ const Common::String SECTION_DELIMITER = Common::String('!');
+
+ Common::String line;
+ bool shouldKeepReading = !file.err() && !file.eos();
+ while (shouldKeepReading) {
+ line = file.readLine();
+ if (file.err() || file.eos()) {
+ break;
+ }
+
+ line.trim();
+ if (line.empty()) {
+ // There should only be a single empty line at the end of the file.
+ if (!file.eos()) {
+ warning("%s: Encountered unexpected empty line", __func__);
+ }
+
+ } else if (line == SECTION_DELIMITER) {
+ debugC(7, kDebugLoading, "%s: End of section", __func__);
+ break;
+
+ } else if (line.hasPrefix(SUMMARY_LINE_DELIMITER)) {
+ // Example: "* 3987 20432 3881 15000"
+ // Don't bother parsing the summary line, because it doesn't contain information
+ // that uniquely identifies entities for pretty printing while debugging.
+ debugC(7, kDebugLoading, "%s: summary: %s", __func__, line.c_str());
+
+ } else if (line.hasPrefix(IMAGE_SET_LINE_DELIMITER)) {
+ // Example: "# image_7d12g_Background 15000 15001 15002 15003 15004 15005"
+ // This seems to only occur once in Hercules (even though there are other titles that have
+ // image set actors). For now, just print it raw but don't process it.
+ debugC(5, kDebugLoading, "%s: image set: %s", __func__, line.c_str());
+
+ } else {
+ // Try to actually parse this line.
+ (this->*sectionParserMethod)(line);
+ }
+
+ shouldKeepReading = !file.err() && !file.eos();
+ }
+}
+
+void Profile::load(const Common::Path &filename) {
+ Common::File file;
+ if (!file.open(filename)) {
+ debugC(5, kDebugLoading, "%s: Could not open profile %s. Entity names will not be available.",
+ __func__, filename.toString().c_str());
+ return;
+ }
+ parseVersionInfo(file.readLine());
+
+ readSection(file, &Profile::parseContextInfo);
+ debugC(5, kDebugLoading, "%s: Read %d context infos", __func__, _contexts.size());
+
+ readSection(file, &Profile::parseAssetInfo);
+ debugC(5, kDebugLoading, "%s: Read %d asset infos", __func__, _assets.size());
+
+ readSection(file, &Profile::parseFileInfo);
+ debugC(5, kDebugLoading, "%s: Read %d file infos", __func__, _files.size());
+
+ readSection(file, &Profile::parseVariableInfo);
+ debugC(5, kDebugLoading, "%s: Read %d variable infos", __func__, _variables.size());
+
+ readSection(file, &Profile::parseParamTokenInfo);
+ debugC(5, kDebugLoading, "%s: Read %d param token infos", __func__, _paramTokens.size());
+
+ readSection(file, &Profile::parseScriptConstantInfo);
+ debugC(5, kDebugLoading, "%s: Read %d script constant infos", __func__, _constants.size());
+
+ file.close();
+}
+
+void Profile::parseVersionInfo(const Common::String &line) {
+ // Example: "_Version3.3_ _MAC_"
+ Common::StringTokenizer tokenizer(line);
+ _versionNumber = tokenizer.nextToken();
+ _platform = tokenizer.nextToken();
+ debugC(5, kDebugLoading, "%s: version: %s; platform: %s", __func__, _versionNumber.c_str(), _platform.c_str());
+}
+
+void Profile::parseContextInfo(const Common::String &line) {
+ // Example: "Context Root_6c00 2929877932"
+ ProfileContextInfo contextInfo;
+ Common::StringTokenizer tokenizer(line);
+ contextInfo.type = tokenizer.nextToken();
+ contextInfo.name = tokenizer.nextToken();
+ Common::String unk1Str = tokenizer.nextToken();
+ contextInfo.unk1 = atol(unk1Str.c_str());
+ _contexts.push_back(contextInfo);
+
+ debugC(5, kDebugLoading, "%s: \"%s\" -> type: %s; name: %s; unk1: %d",
+ __func__, line.c_str(), contextInfo.type.c_str(), contextInfo.name.c_str(), contextInfo.unk1);
+}
+
+void Profile::parseAssetInfo(const Common::String &line) {
+ // Examples:
+ // - No channel: "Puppy_Transition 113 0"
+ // - One channel: "snd_6c16_NoteLevel1 1436 74"
+ // - Multiple channels: "movie_6cb1_LayeredBumpers 154 111 112 113"
+ // (Currently only stream movies seem to have multiple channels.)
+ Common::StringTokenizer tokenizer(line);
+ ProfileAssetInfo assetInfo;
+ assetInfo.name = tokenizer.nextToken();
+ Common::String idAsString = tokenizer.nextToken();
+ assetInfo.id = atoi(idAsString.c_str());
+ debugCN(5, kDebugLoading, "%s: \"%s\" -> name %s; id: %d; channelIdents: [ ",
+ __func__, line.c_str(), assetInfo.name.c_str(), assetInfo.id);
+
+ // The channel IDs presented here must be transformed into the actual channel IDs
+ // (FourCCs) seen when reading chunks by converting them to hex and adding an offset.
+ // So, for example, "snd_6c16_NoteLevel1 1436 74" has actual channel ID "a04a", since
+ // 74 in hex is 0x4A, and then we add 0xA000.
+ const uint CHANNEL_ID_OFFSET_TO_OBTAIN_REAL_CHUNK_FOURCC = 0xA000;
+ while (!tokenizer.empty()) {
+ Common::String channelIdentAsString = tokenizer.nextToken();
+ uint channelIdentAsUnoffsetInt = atoi(channelIdentAsString.c_str());
+ if (channelIdentAsUnoffsetInt != 0) {
+ uint channelIdentAsInt = atoi(channelIdentAsString.c_str()) + CHANNEL_ID_OFFSET_TO_OBTAIN_REAL_CHUNK_FOURCC;
+ assetInfo.channelIdents.push_back(channelIdentAsInt);
+ _channelIdentsAsIntToAssetId.setVal(channelIdentAsInt, assetInfo.id);
+ debugCN(5, kDebugLoading, "%x ", channelIdentAsInt);
+ }
+ }
+ debugC(5, kDebugLoading, "]"); // Close out the printed channel idents list.
+ _assets.setVal(assetInfo.id, assetInfo);
+}
+
+void Profile::parseFileInfo(const Common::String &line) {
+ Common::StringTokenizer tokenizer(line);
+ ProfileFileInfo fileInfo;
+ fileInfo.name = tokenizer.nextToken();
+ Common::String idStr = tokenizer.nextToken();
+ fileInfo.id = atoi(idStr.c_str());
+ _files.setVal(fileInfo.id, fileInfo);
+
+ debugC(5, kDebugLoading, "%s: \"%s\" -> filename: %s fileId: %d",
+ __func__, line.c_str(), fileInfo.name.c_str(), fileInfo.id);
+}
+
+void Profile::parseVariableInfo(const Common::String &line) {
+ Common::StringTokenizer tokenizer(line);
+ ProfileVariableInfo variableInfo;
+ variableInfo.name = tokenizer.nextToken();
+ Common::String idStr = tokenizer.nextToken();
+ variableInfo.id = atoi(idStr.c_str());
+ _variables.setVal(variableInfo.id, variableInfo);
+
+ debugC(5, kDebugLoading, "%s: \"%s\" -> name: %s id: %d",
+ __func__, line.c_str(), variableInfo.name.c_str(), variableInfo.id);
+}
+
+void Profile::parseParamTokenInfo(const Common::String &line) {
+ Common::StringTokenizer tokenizer(line);
+ ProfileParamTokenInfo paramTokenInfo;
+ paramTokenInfo.name = tokenizer.nextToken();
+ Common::String idStr = tokenizer.nextToken();
+ paramTokenInfo.id = (uint16)atoi(idStr.c_str());
+ _paramTokens.setVal(paramTokenInfo.id, paramTokenInfo);
+
+ debugC(5, kDebugLoading, "%s: \"%s\" -> name: %s id: %d",
+ __func__, line.c_str(), paramTokenInfo.name.c_str(), paramTokenInfo.id);
+}
+
+void Profile::parseScriptConstantInfo(const Common::String &line) {
+ Common::StringTokenizer tokenizer(line);
+ ProfileScriptConstantInfo constantInfo;
+ constantInfo.name = tokenizer.nextToken();
+ if (!tokenizer.empty()) {
+ // There is no type information stored here, so we will just store the
+ // textual value of the constant as is.
+ constantInfo.value = tokenizer.nextToken();
+ }
+ _constants.push_back(constantInfo);
+
+ debugC(5, kDebugLoading, "%s: \"%s\" -> name: %s value: %s",
+ __func__, line.c_str(), constantInfo.name.c_str(), constantInfo.value.c_str());
+}
+
+Common::String Profile::formatActorName(uint actorId, bool attemptToGetType) {
+ // If requested, try to get the actor type by looking up the loaded actor
+ if (attemptToGetType) {
+ Actor *actor = g_engine->getActorById(actorId);
+ if (actor != nullptr) {
+ return formatActorName(actor);
+ }
+ }
+
+ Common::String formattedName;
+ const Common::String &actorName = _assets.getValOrDefault(actorId).name;
+ if (!actorName.empty()) {
+ formattedName = Common::String::format("%s (%d)", actorName.c_str(), actorId);
+ } else {
+ formattedName = Common::String::format("%d", actorId);
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatActorName(const Actor *actor) {
+ if (actor == nullptr) {
+ return "<null>";
+ }
+
+ Common::String formattedName;
+ const Common::String &actorName = _assets.getValOrDefault(actor->id()).name;
+ if (!actorName.empty()) {
+ formattedName = Common::String::format("%s [%s %d]", actorName.c_str(), actorTypeToStr(actor->type()), actor->id());
+ } else {
+ // Even if we don't have a name, try to give at least some visibility by including the type.
+ formattedName = Common::String::format("%s %d", actorTypeToStr(actor->type()), actor->id());
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatFunctionName(uint functionId) {
+ // Only in PROFILE._ST, 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.
+ Common::String formattedName;
+ uint offsetFunctionId = functionId + 19900;
+ const Common::String &functionName = _assets.getValOrDefault(offsetFunctionId).name;
+ if (!functionName.empty()) {
+ // Report the function ID as it appears in bytecode, so without the odd offset added.
+ formattedName = Common::String::format("%s (%d)", functionName.c_str(), functionId);
+ } else {
+ // This might be a built-in function, in which case we can try to get the built-in name.
+ formattedName = Common::String::format("%s (%d)", builtInFunctionToStr(static_cast<BuiltInFunction>(functionId)), functionId);
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatFileName(uint fileId) {
+ Common::String formattedName;
+ const Common::String &fileName = _files.getValOrDefault(fileId).name;
+ if (!fileName.empty()) {
+ formattedName = Common::String::format("%s (%d)", fileName.c_str(), fileId);
+ } else {
+ formattedName = Common::String::format("%d", fileId);
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatVariableName(uint variableId) {
+ Common::String formattedName;
+ const Common::String &variableName = _variables.getValOrDefault(variableId).name;
+ if (!variableName.empty()) {
+ formattedName = Common::String::format("%s (%d)", variableName.c_str(), variableId);
+ } else {
+ formattedName = Common::String::format("%d", variableId);
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatParamTokenName(uint paramToken) {
+ Common::String formattedName;
+ const Common::String ¶mTokenName = _paramTokens.getValOrDefault(paramToken).name;
+ if (!paramTokenName.empty()) {
+ formattedName = Common::String::format("%s (%d)", paramTokenName.c_str(), paramToken);
+ } else {
+ formattedName = Common::String::format("%d", paramToken);
+ }
+ return formattedName;
+}
+
+Common::String Profile::formatAssetNameForChannelIdent(uint channelIdentAsTag) {
+ Common::String formattedName;
+ if (channelIdentAsTag == MKTAG('i', 'g', 'o', 'd')) {
+ formattedName = "ImtGod";
+ } else {
+ Common::String channelIdentAsString = Common::tag2string(channelIdentAsTag);
+ uint channelIdentAsInt = strtol(channelIdentAsString.c_str(), nullptr, 16);
+ if (_channelIdentsAsIntToAssetId.contains(channelIdentAsInt)) {
+ uint assetId = _channelIdentsAsIntToAssetId.getVal(channelIdentAsInt);
+ formattedName = Common::String::format("%s [%s]", channelIdentAsString.c_str(), formatActorName(assetId).c_str());
+ } else {
+ formattedName = Common::String::format("%s", tag2str(channelIdentAsTag));
+ }
+ }
+ return formattedName;
+}
+
+} // End of namespace MediaStation
diff --git a/engines/mediastation/profile.h b/engines/mediastation/profile.h
new file mode 100644
index 00000000000..b1af55856e4
--- /dev/null
+++ b/engines/mediastation/profile.h
@@ -0,0 +1,112 @@
+/* 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_PROFILE_H
+#define MEDIASTATION_PROFILE_H
+
+#include "common/str.h"
+#include "common/array.h"
+#include "common/hashmap.h"
+#include "common/file.h"
+
+namespace MediaStation {
+
+class Actor;
+
+struct ProfileContextInfo {
+ // This is usually "Document", "Context", or "Screen".
+ Common::String type;
+ Common::String name;
+ uint unk1 = 0;
+};
+
+// These can be actors or functions, hence using the generic term "asset" here.
+struct ProfileAssetInfo {
+ Common::String name;
+ uint id = 0;
+ Common::Array<uint16> channelIdents;
+};
+
+struct ProfileFileInfo {
+ Common::String name;
+ uint id = 0;
+};
+
+struct ProfileVariableInfo {
+ Common::String name;
+ uint id = 0;
+};
+
+struct ProfileParamTokenInfo {
+ Common::String name;
+ uint id = 0;
+};
+
+struct ProfileScriptConstantInfo {
+ Common::String name;
+ Common::String value;
+};
+
+// Profiles (PROFILE._ST) contain mappings between names and IDs for assets,
+// functions, variables, and other entities. Some titles do not have this mapping,
+// but when it exists it is very helpful for debugging. It is not required for actually
+// running any games.
+class Profile {
+public:
+ void load(const Common::Path &filename);
+
+ Common::String formatActorName(uint actorId, bool attemptToGetType = false);
+ Common::String formatActorName(const Actor *actor);
+
+ Common::String formatFunctionName(uint assetId);
+ Common::String formatFileName(uint fileId);
+ Common::String formatVariableName(uint variableId);
+ Common::String formatParamTokenName(uint paramToken);
+ Common::String formatAssetNameForChannelIdent(uint channelIdent);
+
+ const Common::String &getFileName(uint16 fileId) const { return _files.getValOrDefault(fileId).name; }
+ const Common::String &getResourceName(uint16 resourceId) const { return _paramTokens.getValOrDefault(resourceId).name; }
+
+private:
+ Common::String _versionNumber;
+ Common::String _platform;
+ Common::Array<ProfileContextInfo> _contexts; // It isn't clear what the key would be.
+ Common::HashMap<uint, ProfileAssetInfo> _assets;
+ Common::HashMap<uint, uint> _channelIdentsAsIntToAssetId;
+ Common::HashMap<uint, ProfileFileInfo> _files;
+ Common::HashMap<uint, ProfileVariableInfo> _variables;
+ Common::HashMap<uint, ProfileParamTokenInfo> _paramTokens;
+ Common::Array<ProfileScriptConstantInfo> _constants;
+
+ void parseVersionInfo(const Common::String &line);
+ void parseContextInfo(const Common::String &line);
+ void parseAssetInfo(const Common::String &line);
+ void parseFileInfo(const Common::String &line);
+ void parseVariableInfo(const Common::String &line);
+ void parseParamTokenInfo(const Common::String &line);
+ void parseScriptConstantInfo(const Common::String &line);
+
+ void readSection(Common::File &file, void (Profile::*parser)(const Common::String &));
+};
+
+} // End of namespace MediaStation
+
+#endif
Commit: f14ca1483037f344758c7ab2cb4e3b0a734f5db2
https://github.com/scummvm/scummvm/commit/f14ca1483037f344758c7ab2cb4e3b0a734f5db2
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Make sprite clips work with newer engine versions
Changed paths:
engines/mediastation/actor.h
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/sprite.h
engines/mediastation/debugchannels.h
engines/mediastation/detection.cpp
engines/mediastation/mediastation.cpp
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index 78a63c6e79e..736ca22987b 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -134,7 +134,7 @@ enum ActorHeaderSectionType {
// SPRITE FIELDS.
kActorHeaderSpriteClip = 0x03e9,
- kActorHeaderCurrentSpriteClip = 0x03ea
+ kActorHeaderDefaultSpriteClip = 0x03ea
};
enum CylindricalWrapMode : int;
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index 641ea76a423..8b1c2fb0ebc 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -30,6 +30,10 @@ SpriteFrameHeader::SpriteFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
_offset = chunk.readTypedPoint();
}
+Common::String SpriteMovieClip::getDebugString() const {
+ return Common::String::format("%s: [%d, %d]", g_engine->formatParamTokenName(id).c_str(), firstFrameIndex, lastFrameIndex);
+}
+
SpriteFrame::SpriteFrame(Chunk &chunk, SpriteFrameHeader *header) : Bitmap(chunk, header) {
_bitmapHeader = header;
}
@@ -88,33 +92,22 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
_isVisible = static_cast<bool>(chunk.readTypedByte());
break;
- case kActorHeaderSpriteChunkCount: {
- _frameCount = chunk.readTypedUint16();
-
- // Set the default clip.
- SpriteClip clip;
- clip.id = DEFAULT_CLIP_ID;
- clip.firstFrameIndex = 0;
- clip.lastFrameIndex = _frameCount - 1;
- _clips.setVal(clip.id, clip);
- setCurrentClip(clip.id);
+ case kActorHeaderSpriteChunkCount:
+ _asset->_frameCount = chunk.readTypedUint16();
break;
- }
case kActorHeaderSpriteClip: {
- SpriteClip spriteClip;
- spriteClip.id = chunk.readTypedUint16();
- spriteClip.firstFrameIndex = chunk.readTypedUint16();
- spriteClip.lastFrameIndex = chunk.readTypedUint16();
- _clips.setVal(spriteClip.id, spriteClip);
+ SpriteMovieClip clip;
+ clip.id = chunk.readTypedUint16();
+ clip.firstFrameIndex = chunk.readTypedUint16();
+ clip.lastFrameIndex = chunk.readTypedUint16();
+ _clips.setVal(clip.id, clip);
break;
}
- case kActorHeaderCurrentSpriteClip: {
- uint clipId = chunk.readTypedUint16();
- setCurrentClip(clipId);
+ case kActorHeaderDefaultSpriteClip:
+ _defaultClipId = chunk.readTypedUint16();
break;
- }
case kActorHeaderActorReference: {
_actorReference = chunk.readTypedUint16();
@@ -135,6 +128,23 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
}
}
+void SpriteMovieActor::loadIsComplete() {
+ // This clip goes forward through all the sprite's frames.
+ SpriteMovieClip forwardClip(DEFAULT_FORWARD_CLIP_ID, 0, _asset->_frameCount - 1);
+ if (!_clips.contains(DEFAULT_FORWARD_CLIP_ID)) {
+ _clips.setVal(forwardClip.id, forwardClip);
+ }
+
+ // This clip goes backward through all the sprite's frames.
+ SpriteMovieClip backwardClip(DEFAULT_BACKWARD_CLIP_ID, _asset->_frameCount - 1, 0);
+ if (!_clips.contains(DEFAULT_BACKWARD_CLIP_ID)) {
+ _clips.setVal(backwardClip.id, backwardClip);
+ }
+
+ SpatialEntity::loadIsComplete();
+ setCurrentClip(_defaultClipId);
+}
+
ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
@@ -171,7 +181,7 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
case kSetCurrentClipMethod: {
assert(args.size() <= 1);
- uint clipId = DEFAULT_CLIP_ID;
+ uint clipId = DEFAULT_FORWARD_CLIP_ID;
if (args.size() == 1) {
clipId = args[0].asParamToken();
}
@@ -226,19 +236,53 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
}
bool SpriteMovieActor::activateNextFrame() {
- if (_currentFrameIndex < _activeClip.lastFrameIndex) {
- _currentFrameIndex++;
- dirtyIfVisible();
- return true;
+ bool clipMovesForward = _activeClip.firstFrameIndex <= _activeClip.lastFrameIndex;
+ if (clipMovesForward) {
+ debugC(3, kDebugSpriteMovie, "[%s] %s: FORWARD: currentFrameIndex: %d; activeClip.lastFrameIndex: %d",
+ debugName(), __func__, _currentFrameIndex, _activeClip.lastFrameIndex);
+
+ bool canMoveForward = _currentFrameIndex < _activeClip.lastFrameIndex;
+ if (canMoveForward) {
+ dirtyIfVisible();
+ _currentFrameIndex++;
+ dirtyIfVisible();
+ return true;
+ }
+
+ } else {
+ debugC(3, kDebugSpriteMovie, "[%s] %s: BACKWARD: currentFrameIndex: %d; activeClip.lastFrameIndex: %d",
+ debugName(), __func__, _currentFrameIndex, _activeClip.lastFrameIndex);
+
+ bool canMoveBackward = _currentFrameIndex > _activeClip.lastFrameIndex;
+ if (canMoveBackward) {
+ dirtyIfVisible();
+ _currentFrameIndex--;
+ dirtyIfVisible();
+ return true;
+ }
}
return false;
}
bool SpriteMovieActor::activatePreviousFrame() {
- if (_currentFrameIndex > _activeClip.firstFrameIndex) {
- _currentFrameIndex--;
- dirtyIfVisible();
- return true;
+ bool clipMovesBackward = _activeClip.lastFrameIndex < _activeClip.firstFrameIndex;
+ if (clipMovesBackward) {
+ bool canMoveTowardFirst = _currentFrameIndex < _activeClip.firstFrameIndex;
+ if (canMoveTowardFirst) {
+ dirtyIfVisible();
+ _currentFrameIndex++;
+ dirtyIfVisible();
+ return true;
+ }
+
+ } else {
+ bool canMoveTowardFirst = _activeClip.firstFrameIndex < _currentFrameIndex;
+ if (canMoveTowardFirst) {
+ dirtyIfVisible();
+ _currentFrameIndex--;
+ dirtyIfVisible();
+ return true;
+ }
}
return false;
}
@@ -263,35 +307,46 @@ void SpriteMovieActor::play() {
_nextFrameTime = 0;
scheduleNextFrame();
+ debugC(3, kDebugSpriteMovie, "[%s] %s", debugName(), __func__);
}
void SpriteMovieActor::stop() {
_nextFrameTime = 0;
_isPlaying = false;
+ debugC(3, kDebugSpriteMovie, "[%s] %s", debugName(), __func__);
}
void SpriteMovieActor::setCurrentClip(uint clipId) {
if (_activeClip.id != clipId) {
if (_clips.contains(clipId)) {
+ SpriteMovieClip newClip = _clips.getVal(clipId);
+ debugC(3, kDebugSpriteMovie, "[%s] %s: (frameCount: %d) activeClip: %s; newClip: %s",
+ debugName(), __func__, _asset->_frameCount, _activeClip.getDebugString().c_str(), newClip.getDebugString().c_str());
_activeClip = _clips.getVal(clipId);
} else {
_activeClip.id = clipId;
- warning("%s: Sprite clip %d not found in sprite %s", __func__, clipId, debugName());
+ warning("[%s] %s: Clip %s not found", debugName(), __func__, _activeClip.getDebugString().c_str());
}
- }
- setCurrentFrameToInitial();
+ setCurrentFrameToInitial();
+ }
}
void SpriteMovieActor::setCurrentFrameToInitial() {
+ debugC(3, kDebugSpriteMovie, "[%s] %s: currentFrameIndex: %d, activeClip.firstFrameIndex: %d",
+ debugName(), __func__, _currentFrameIndex, _activeClip.firstFrameIndex);
if (_currentFrameIndex != _activeClip.firstFrameIndex) {
+ dirtyIfVisible();
_currentFrameIndex = _activeClip.firstFrameIndex;
dirtyIfVisible();
}
}
void SpriteMovieActor::setCurrentFrameToFinal() {
+ debugC(3, kDebugSpriteMovie, "[%s] %s: currentFrameIndex: %d, activeClip.lastFrameIndex: %d",
+ debugName(), __func__, _currentFrameIndex, _activeClip.lastFrameIndex);
if (_currentFrameIndex != _activeClip.lastFrameIndex) {
+ dirtyIfVisible();
_currentFrameIndex = _activeClip.lastFrameIndex;
dirtyIfVisible();
}
@@ -320,16 +375,36 @@ void SpriteMovieActor::scheduleNextFrame() {
return;
}
- if (_currentFrameIndex < _activeClip.lastFrameIndex) {
- scheduleNextTimerEvent();
+ debugC(3, kDebugSpriteMovie, "[%s] %s: currentFrame: %d; activeClip: [%d, %d]",
+ debugName(), __func__, _currentFrameIndex,
+ _activeClip.firstFrameIndex, _activeClip.lastFrameIndex);
+ int firstFrameIndex = _activeClip.firstFrameIndex;
+ int lastFrameIndex = _activeClip.lastFrameIndex;
+
+ // For backward clips, we've "passed" the last frame when currentFrameIndex <= lastFrameIndex.
+ bool clipMovesBackward = lastFrameIndex < firstFrameIndex;
+ bool currentIsAtOrBeyondLast = lastFrameIndex <= _currentFrameIndex;
+ bool needsStopEvaluation = clipMovesBackward || currentIsAtOrBeyondLast;
+ bool backwardClipContinues = clipMovesBackward && currentIsAtOrBeyondLast;
+
+ if (needsStopEvaluation) {
+ if (backwardClipContinues) {
+ // Backward clip still has frames to show (current > last).
+ scheduleNextTimerEvent();
+ } else {
+ // We reached the end of the clip, regardless of which direction we were moving.
+ stop();
+ }
} else {
- stop();
+ // The forward clip still in progress.
+ scheduleNextTimerEvent();
}
}
void SpriteMovieActor::scheduleNextTimerEvent() {
uint frameDuration = 1000 / _frameRate;
_nextFrameTime += frameDuration;
+ debugC(3, kDebugSpriteMovie, "[%s] %s", debugName(), __func__);
}
void SpriteMovieActor::updateFrameState() {
@@ -339,7 +414,8 @@ void SpriteMovieActor::updateFrameState() {
uint currentTime = g_system->getMillis() - _startTime;
bool drawNextFrame = currentTime >= _nextFrameTime;
- debugC(kDebugGraphics, "nextFrameTime: %d; startTime: %d, currentTime: %d", _nextFrameTime, _startTime, currentTime);
+ debugC(3, kDebugSpriteMovie, "[%s] %s: nextFrameTime: %d; startTime: %d, currentTime: %d",
+ debugName(), __func__, _nextFrameTime, _startTime, currentTime);
if (drawNextFrame) {
timerEvent();
}
@@ -347,16 +423,16 @@ void SpriteMovieActor::updateFrameState() {
void SpriteMovieActor::timerEvent() {
if (!_isPlaying) {
- error("%s: Attempt to activate sprite frame when sprite is not playing", __func__);
+ warning("[%s] %s: Not playing", debugName(), __func__);
return;
}
- bool result = activateNextFrame();
- if (!result) {
- stop();
- } else {
+ bool moreFramesToShow = activateNextFrame();
+ if (moreFramesToShow) {
postMovieEndEventIfNecessary();
scheduleNextFrame();
+ } else {
+ stop();
}
}
@@ -368,6 +444,7 @@ void SpriteMovieActor::postMovieEndEventIfNecessary() {
_isPlaying = false;
_startTime = 0;
_nextFrameTime = 0;
+ debugC(3, kDebugSpriteMovie, "[%s] %s: Posting movie end", debugName(), __func__);
ScriptValue value;
value.setToParamToken(_activeClip.id);
@@ -375,10 +452,18 @@ void SpriteMovieActor::postMovieEndEventIfNecessary() {
}
void SpriteMovieActor::draw(DisplayContext &displayContext) {
+ if (static_cast<uint>(_currentFrameIndex) >= _asset->frames.size()) {
+ warning("[%s] %s: Requested frame %d, but we only have %d frames. Showing last frame",
+ debugName(), __func__, _currentFrameIndex, _asset->frames.size());
+ _currentFrameIndex = _asset->frames.size() - 1;
+ }
+
SpriteFrame *activeFrame = _asset->frames[_currentFrameIndex];
if (_isVisible) {
Common::Rect frameBbox = activeFrame->boundingBox();
frameBbox.translate(_boundingBox.left, _boundingBox.top);
+ debugC(8, kDebugSpriteMovie, "[%s] %s: frame %d",
+ debugName(), __func__, _currentFrameIndex);
g_engine->getDisplayManager()->imageBlit(frameBbox.origin(), activeFrame, _dissolveFactor, &displayContext);
}
}
diff --git a/engines/mediastation/actors/sprite.h b/engines/mediastation/actors/sprite.h
index 58441bd11b8..389365298ed 100644
--- a/engines/mediastation/actors/sprite.h
+++ b/engines/mediastation/actors/sprite.h
@@ -34,10 +34,15 @@
namespace MediaStation {
-struct SpriteClip {
+struct SpriteMovieClip {
uint id = 0;
- uint firstFrameIndex = 0;
- uint lastFrameIndex = 0;
+ int firstFrameIndex = 0;
+ int lastFrameIndex = 0;
+
+ SpriteMovieClip() = default;
+ SpriteMovieClip(uint clipId, int first, int last)
+ : id(clipId), firstFrameIndex(first), lastFrameIndex(last) {}
+ Common::String getDebugString() const;
};
class SpriteFrameHeader : public BitmapHeader {
@@ -68,6 +73,7 @@ private:
struct SpriteAsset {
~SpriteAsset();
+ uint _frameCount = 0;
Common::Array<SpriteFrame *> frames;
};
@@ -82,24 +88,25 @@ public:
virtual void draw(DisplayContext &displayContext) override;
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
+ virtual void loadIsComplete() override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
- virtual bool isVisible() const override { return _isVisible; }
-
virtual void readChunk(Chunk &chunk) override;
private:
- static const uint DEFAULT_CLIP_ID = 1200;
+ const uint DEFAULT_FORWARD_CLIP_ID = 0x4B0;
+ const uint DEFAULT_BACKWARD_CLIP_ID = 0x4B1;
+
uint _loadType = 0;
uint _frameRate = 0;
- uint _frameCount = 0;
uint _actorReference = 0;
- Common::HashMap<uint, SpriteClip> _clips;
+ Common::HashMap<uint, SpriteMovieClip> _clips;
Common::SharedPtr<SpriteAsset> _asset;
bool _isPlaying = false;
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
- SpriteClip _activeClip;
+ uint _defaultClipId = DEFAULT_FORWARD_CLIP_ID;
+ SpriteMovieClip _activeClip;
void play();
void stop();
diff --git a/engines/mediastation/debugchannels.h b/engines/mediastation/debugchannels.h
index dfbc6bc428b..bf75c59acda 100644
--- a/engines/mediastation/debugchannels.h
+++ b/engines/mediastation/debugchannels.h
@@ -38,6 +38,7 @@ enum DebugChannels {
kDebugScript,
kDebugEvents,
kDebugLoading,
+ kDebugSpriteMovie,
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/detection.cpp b/engines/mediastation/detection.cpp
index 40c3b9a1d22..ccfd49d637c 100644
--- a/engines/mediastation/detection.cpp
+++ b/engines/mediastation/detection.cpp
@@ -40,6 +40,7 @@ const DebugChannelDef MediaStationMetaEngineDetection::debugFlagList[] = {
{ MediaStation::kDebugScript, "script", "Enable debug script dump" },
{ MediaStation::kDebugEvents, "events", "Events processing" },
{ MediaStation::kDebugLoading, "loading", "File loading" },
+ { MediaStation::kDebugSpriteMovie, "spritemovie", "Sprite movie debug level" },
DEBUG_CHANNEL_END
};
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index c99212b81fd..7ec07977235 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -302,6 +302,7 @@ void MediaStationEngine::draw(bool dirtyOnly) {
} else {
_stageDirector->drawAll();
}
+ _stageDirector->clearDirtyRegion();
_displayManager->updateScreen();
_displayManager->doTransitionOnSync();
}
Commit: f6b3fd2ab80e0f7795af25a04871d1487c64f4e5
https://github.com/scummvm/scummvm/commit/f6b3fd2ab80e0f7795af25a04871d1487c64f4e5
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Add helper function for getting an actor of a specific type
Changed paths:
engines/mediastation/actor.cpp
engines/mediastation/actors/camera.cpp
engines/mediastation/actors/image.cpp
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/stage.cpp
engines/mediastation/graphics.cpp
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index 99ccb72e361..18730db7d40 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -385,13 +385,7 @@ void SpatialEntity::readParameter(Chunk &chunk, ActorHeaderSectionType paramType
void SpatialEntity::loadIsComplete() {
Actor::loadIsComplete();
if (_stageId != 0) {
- Actor *pendingParentStageActor = g_engine->getActorById(_stageId);
- if (pendingParentStageActor == nullptr) {
- error("%s: Actor %d doesn't exist", __func__, _stageId);
- } else if (pendingParentStageActor->type() != kActorTypeStage) {
- error("%s: Requested parent stage %d is not a stage", __func__, _stageId);
- }
- StageActor *pendingParentStage = static_cast<StageActor *>(pendingParentStageActor);
+ StageActor *pendingParentStage = static_cast<StageActor *>(g_engine->getActorByIdAndType(_stageId, kActorTypeStage));
pendingParentStage->addChildSpatialEntity(this);
}
}
diff --git a/engines/mediastation/actors/camera.cpp b/engines/mediastation/actors/camera.cpp
index 8870604a16b..1dac8fc3333 100644
--- a/engines/mediastation/actors/camera.cpp
+++ b/engines/mediastation/actors/camera.cpp
@@ -68,15 +68,8 @@ void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType)
case kActorHeaderCameraImageActor: {
uint actorReference = chunk.readTypedUint16();
- Actor *referencedActor = g_engine->getActorById(actorReference);
- if (referencedActor == nullptr) {
- error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, actorReference);
- }
- if (referencedActor->type() != kActorTypeCamera) {
- error("%s: Type mismatch of referenced actor %d", __func__, actorReference);
- }
- CameraActor *referencedImage = static_cast<CameraActor *>(referencedActor);
- _image = referencedImage->_image;
+ CameraActor *referencedCamera = static_cast<CameraActor *>(g_engine->getActorByIdAndType(actorReference, kActorTypeCamera));
+ _image = referencedCamera->_image;
break;
}
diff --git a/engines/mediastation/actors/image.cpp b/engines/mediastation/actors/image.cpp
index 62b11720129..637d294193c 100644
--- a/engines/mediastation/actors/image.cpp
+++ b/engines/mediastation/actors/image.cpp
@@ -60,14 +60,7 @@ void ImageActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
case kActorHeaderActorReference: {
_actorReference = chunk.readTypedUint16();
- Actor *referencedActor = g_engine->getActorById(_actorReference);
- if (referencedActor == nullptr) {
- error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, _actorReference);
- }
- if (referencedActor->type() != kActorTypeImage) {
- error("%s: Type mismatch of referenced actor %d", __func__, _actorReference);
- }
- ImageActor *referencedImage = static_cast<ImageActor *>(referencedActor);
+ ImageActor *referencedImage = static_cast<ImageActor *>(g_engine->getActorByIdAndType(_actorReference, kActorTypeImage));
_asset = referencedImage->_asset;
break;
}
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index 8b1c2fb0ebc..868e4fe2a7c 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -111,14 +111,7 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
case kActorHeaderActorReference: {
_actorReference = chunk.readTypedUint16();
- Actor *referencedActor = g_engine->getActorById(_actorReference);
- if (referencedActor == nullptr) {
- error("%s: Referenced actor %d doesn't exist or has not been read yet in this title", __func__, _actorReference);
- }
- if (referencedActor->type() != kActorTypeSprite) {
- error("%s: Type mismatch of referenced actor %d", __func__, _actorReference);
- }
- SpriteMovieActor *referencedSprite = static_cast<SpriteMovieActor *>(referencedActor);
+ SpriteMovieActor *referencedSprite = static_cast<SpriteMovieActor *>(g_engine->getActorByIdAndType(_actorReference, kActorTypeSprite));
_asset = referencedSprite->_asset;
break;
}
diff --git a/engines/mediastation/actors/stage.cpp b/engines/mediastation/actors/stage.cpp
index bb3df7c7fee..4c786877ffa 100644
--- a/engines/mediastation/actors/stage.cpp
+++ b/engines/mediastation/actors/stage.cpp
@@ -45,7 +45,7 @@ void StageActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
// In stages, this basically has the oppose meaning it has outside of stages. Here,
// it specifies an actor that is a parent of this stage.
uint parentActorId = chunk.readTypedUint16();
- _pendingParent = g_engine->getSpatialEntityById(parentActorId);
+ _pendingParent = static_cast<SpatialEntity *>(g_engine->getActorByIdAndType(parentActorId, kActorTypeStage));
break;
}
@@ -265,9 +265,6 @@ void StageActor::loadIsComplete() {
Actor::loadIsComplete();
if (_pendingParent != nullptr) {
- if (_pendingParent->type() != kActorTypeStage) {
- error("%s: Parent must be a stage", __func__);
- }
StageActor *parentStage = static_cast<StageActor *>(_pendingParent);
parentStage->addChildSpatialEntity(this);
_pendingParent = nullptr;
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index fb55ddb9cd7..06c5c43ed97 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -696,39 +696,21 @@ void VideoDisplayManager::_colorShiftCurrentPalette(uint startIndex, uint shiftA
}
void VideoDisplayManager::_fadeToPaletteObject(uint paletteId, double fadeTime, uint startIndex, uint colorCount) {
- Actor *actor = _vm->getActorById(paletteId);
- if (actor == nullptr) {
- error("%s: Got null target palette", __func__);
- } else if (actor->type() != kActorTypePalette) {
- error("%s: Actor %d is not a palette", __func__, paletteId);
- }
-
- Graphics::Palette *palette = static_cast<PaletteActor *>(actor)->_palette;
+ PaletteActor *paletteActor = static_cast<PaletteActor *>(_vm->getActorByIdAndType(paletteId, kActorTypePalette));
+ Graphics::Palette *palette = paletteActor->_palette;
_fadeToPalette(fadeTime, *palette, startIndex, colorCount);
}
void VideoDisplayManager::_setToPaletteObject(uint paletteId, uint startIndex, uint colorCount) {
- Actor *actor = _vm->getActorById(paletteId);
- if (actor == nullptr) {
- error("%s: Got null target palette", __func__);
- } else if (actor->type() != kActorTypePalette) {
- error("%s: Actor %d is not a palette", __func__, paletteId);
- }
-
- Graphics::Palette *palette = static_cast<PaletteActor *>(actor)->_palette;
+ PaletteActor *paletteActor = static_cast<PaletteActor *>(_vm->getActorByIdAndType(paletteId, kActorTypePalette));
+ Graphics::Palette *palette = paletteActor->_palette;
_setPalette(*palette, startIndex, colorCount);
}
void VideoDisplayManager::_setPercentToPaletteObject(double percent, uint paletteId, uint startIndex, uint colorCount) {
- Actor *actor = _vm->getActorById(paletteId);
- if (actor == nullptr) {
- error("%s: Got null target palette", __func__);
- } else if (actor->type() != kActorTypePalette) {
- error("%s: Actor %d is not a palette", __func__, paletteId);
- }
-
- Graphics::Palette *targetPalette = static_cast<PaletteActor *>(actor)->_palette;
- _setToPercentPalette(percent, *_registeredPalette, *targetPalette, startIndex, colorCount);
+ PaletteActor *paletteActor = static_cast<PaletteActor *>(_vm->getActorByIdAndType(paletteId, kActorTypePalette));
+ Graphics::Palette *palette = paletteActor->_palette;
+ _setToPercentPalette(percent, *_registeredPalette, *palette, startIndex, colorCount);
}
void VideoDisplayManager::imageBlit(
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 7ec07977235..5d9d60ac9f0 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -102,11 +102,21 @@ Actor *MediaStationEngine::getActorById(uint actorId) {
return _actors.getValOrDefault(actorId);
}
+Actor *MediaStationEngine::getActorByIdAndType(uint actorId, ActorType expectedType) {
+ Actor *actor = getActorById(actorId);
+ if (actor == nullptr) {
+ error("[%s] %s: Actor doesn't exist", g_engine->formatActorName(actorId).c_str(), __func__);
+ } else if (actor->type() != expectedType) {
+ error("[%s] %s: Expected type %s, got %s", actor->debugName(), __func__, actorTypeToStr(actor->type()), actorTypeToStr(expectedType));
+ }
+ return actor;
+}
+
SpatialEntity *MediaStationEngine::getSpatialEntityById(uint spatialEntityId) {
Actor *actor = getActorById(spatialEntityId);
if (actor != nullptr) {
if (!actor->isSpatialActor()) {
- error("[%s] %s: Not a spatial actor", formatActorName(spatialEntityId).c_str(), __func__);
+ error("[%s] %s: Not a spatial actor", actor->debugName(), __func__);
}
return static_cast<SpatialEntity *>(actor);
}
@@ -309,7 +319,7 @@ void MediaStationEngine::draw(bool dirtyOnly) {
void MediaStationEngine::registerActor(Actor *actorToAdd) {
if (getActorById(actorToAdd->id())) {
- error("[%s] %s: Already defined in this title", formatActorName(actorToAdd).c_str(), __func__);
+ error("[%s] %s: Already defined in this title", actorToAdd->debugName(), __func__);
}
_actors.setVal(actorToAdd->id(), actorToAdd);
}
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index f57c6c27c10..d7074215ba8 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -98,6 +98,7 @@ public:
void readHeaderSections(Subfile &subfile, Chunk &chunk);
Actor *getActorById(uint actorId);
+ Actor *getActorByIdAndType(uint actorId, ActorType expectedType);
SpatialEntity *getSpatialEntityById(uint spatialEntityId);
ChannelClient *getChannelClientByChannelIdent(uint channelIdent);
ScriptValue *getVariable(uint variableId);
Commit: 11b97406c5b6647b58f748b97fe586034c010018
https://github.com/scummvm/scummvm/commit/11b97406c5b6647b58f748b97fe586034c010018
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Issue warnings, not asserts, when script argument conditions are not met
Changed paths:
engines/mediastation/actor.cpp
engines/mediastation/actor.h
engines/mediastation/actors/camera.cpp
engines/mediastation/actors/document.cpp
engines/mediastation/actors/hotspot.cpp
engines/mediastation/actors/image.cpp
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/path.cpp
engines/mediastation/actors/sound.cpp
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/stage.cpp
engines/mediastation/actors/timer.cpp
engines/mediastation/graphics.cpp
engines/mediastation/mediascript/collection.cpp
engines/mediastation/mediascript/function.cpp
engines/mediastation/mediascript/scriptvalue.cpp
engines/mediastation/mediascript/scriptvalue.h
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index 18730db7d40..2ea0a7a6861 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -213,7 +213,7 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
ScriptValue returnValue;
switch (methodId) {
case kSpatialMoveToMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
moveTo(x, y);
@@ -221,7 +221,7 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
}
case kSpatialMoveToByOffsetMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 dx = static_cast<int16>(args[0].asFloat());
int16 dy = static_cast<int16>(args[1].asFloat());
int16 newX = _boundingBox.left + dx;
@@ -231,14 +231,14 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
}
case kSpatialZMoveToMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
int zIndex = static_cast<int>(args[0].asFloat());
setZIndex(zIndex);
break;
}
case kSpatialCenterMoveToMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
moveToCentered(x, y);
@@ -246,58 +246,58 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
}
case kGetLeftXMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.left);
break;
case kGetTopYMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.top);
break;
case kGetWidthMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.width());
break;
case kGetHeightMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.height());
break;
case kGetCenterXMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
int centerX = _boundingBox.left + (_boundingBox.width() / 2);
returnValue.setToFloat(centerX);
break;
}
case kGetCenterYMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
int centerY = _boundingBox.top + (_boundingBox.height() / 2);
returnValue.setToFloat(centerY);
break;
}
case kGetZCoordinateMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_zIndex);
break;
case kSetDissolveFactorMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
double dissolveFactor = args[0].asFloat();
setDissolveFactor(dissolveFactor);
break;
}
case kIsVisibleMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(isVisible());
break;
case kSetMousePositionMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
setMousePosition(x, y);
@@ -306,31 +306,31 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
case kGetXScaleMethod1:
case kGetXScaleMethod2:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_scaleX);
break;
case kSetScaleMethod:
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
invalidateLocalBounds();
_scaleX = _scaleY = args[0].asFloat();
invalidateLocalBounds();
break;
case kSetXScaleMethod:
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
invalidateLocalBounds();
_scaleX = args[0].asFloat();
invalidateLocalBounds();
break;
case kGetYScaleMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_scaleY);
break;
case kSetYScaleMethod:
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
invalidateLocalBounds();
_scaleY = args[0].asFloat();
invalidateLocalBounds();
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index 736ca22987b..781d068900d 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -163,6 +163,25 @@ enum MouseEventFlag {
// There is no key up event.
};
+// Argument count validation macros for built-in script methods.
+// For exact argument count.
+#define ARGCOUNTCHECK(n) \
+ if (args.size() != (n)) { \
+ warning("%s: Expected %d arguments, got %d", builtInMethodToStr(methodId), (n), args.size()); \
+ }
+
+// For a range of valid argument counts (min to max).
+#define ARGCOUNTRANGE(min, max) \
+ if (args.size() < (size_t)(min) || args.size() > (max)) { \
+ warning("%s: Expected %d to %d arguments, got %d", builtInMethodToStr(methodId), (min), (max), args.size()); \
+ }
+
+// For minimum argument count (no maximum).
+#define ARGCOUNTMIN(min) \
+ if (args.size() < (min)) { \
+ warning("%s: Expected at least %d arguments, got %d", builtInMethodToStr(methodId), (min), args.size()); \
+ }
+
class Actor {
public:
Actor(ActorType type) : _type(type) {};
diff --git a/engines/mediastation/actors/camera.cpp b/engines/mediastation/actors/camera.cpp
index 1dac8fc3333..94bd895d025 100644
--- a/engines/mediastation/actors/camera.cpp
+++ b/engines/mediastation/actors/camera.cpp
@@ -95,7 +95,7 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
break;
case kAddToStageMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
addToStage();
break;
@@ -109,12 +109,12 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kAddedToStageMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_addedToStage);
break;
case kStartPanMethod: {
- assert(args.size() == 3);
+ ARGCOUNTCHECK(3);
int16 deltaX = static_cast<uint16>(args[0].asFloat());
int16 deltaY = static_cast<int16>(args[1].asFloat());
double duration = args[2].asTime();
@@ -125,17 +125,17 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kStopPanMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
stopPan();
break;
case kIsPanningMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_panState);
break;
case kViewportMoveToMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
_nextViewportOrigin = Common::Point(x, y);
@@ -151,7 +151,7 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kAdjustCameraViewportMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 xDiff = static_cast<int16>(args[0].asFloat());
int16 yDiff = static_cast<int16>(args[1].asFloat());
Common::Point viewportDelta(xDiff, yDiff);
@@ -169,7 +169,7 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kAdjustCameraViewportSpatialCenterMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 xDiff = static_cast<int16>(args[0].asFloat());
int16 yDiff = static_cast<int16>(args[1].asFloat());
@@ -192,7 +192,7 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kSetCameraBoundsMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
int16 width = static_cast<int16>(args[0].asFloat());
int16 height = static_cast<int16>(args[1].asFloat());
Common::Rect newBounds(_originalBoundingBox.origin(), width, height);
@@ -206,17 +206,17 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
}
case kXViewportPositionMethod:
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(getViewportOrigin().x);
break;
case kYViewportPositionMethod:
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(getViewportOrigin().y);
break;
case kPanToMethod: {
- assert(args.size() >= 3);
+ ARGCOUNTRANGE(3, 4);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
@@ -224,11 +224,9 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
uint panSteps = static_cast<uint>(args[2].asFloat());
double duration = args[3].asFloat();
panToByStepCount(x, y, panSteps, duration);
- } else if (args.size() == 3) {
+ } else {
double duration = args[2].asFloat();
panToByTime(x, y, duration);
- } else {
- error("%s: Incorrect number of args for method %s", __func__, builtInMethodToStr(methodId));
}
break;
}
diff --git a/engines/mediastation/actors/document.cpp b/engines/mediastation/actors/document.cpp
index ec58e48934f..2e66ee9f409 100644
--- a/engines/mediastation/actors/document.cpp
+++ b/engines/mediastation/actors/document.cpp
@@ -30,15 +30,17 @@ ScriptValue DocumentActor::callMethod(BuiltInMethod methodId, Common::Array<Scri
ScriptValue returnValue;
switch (methodId) {
case kDocumentBranchToScreenMethod:
+ ARGCOUNTMIN(1);
processBranch(args);
break;
case kDocumentQuitMethod:
+ ARGCOUNTCHECK(0);
g_engine->quitGame();
break;
case kDocumentContextLoadInProgressMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint contextId = args[0].asActorId();
bool isLoading = g_engine->getDocument()->isContextLoadInProgress(contextId);
returnValue.setToBool(isLoading);
@@ -47,28 +49,28 @@ ScriptValue DocumentActor::callMethod(BuiltInMethod methodId, Common::Array<Scri
case kDocumentSetMultipleStreamsMethod:
case kDocumentSetMultipleSoundsMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
bool value = args[0].asBool();
warning("[%s] %s: STUB: %s: %d", debugName(), __func__, builtInMethodToStr(methodId), value);
break;
}
case kDocumentLoadContextMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint contextId = args[0].asActorId();
g_engine->getDocument()->startContextLoad(contextId);
break;
}
case kDocumentReleaseContextMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint contextId = args[0].asActorId();
g_engine->getDocument()->scheduleContextRelease(contextId);
break;
}
case kDocumentContextIsLoadedMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint contextId = args[0].asActorId();
// We are looking for the screen actor with the same ID as the context.
@@ -86,7 +88,6 @@ ScriptValue DocumentActor::callMethod(BuiltInMethod methodId, Common::Array<Scri
}
void DocumentActor::processBranch(Common::Array<ScriptValue> &args) {
- assert(args.size() >= 1);
uint contextId = args[0].asActorId();
if (args.size() > 1) {
bool disableUpdates = static_cast<bool>(args[1].asParamToken());
diff --git a/engines/mediastation/actors/hotspot.cpp b/engines/mediastation/actors/hotspot.cpp
index 808f186ef1c..cccaabe2643 100644
--- a/engines/mediastation/actors/hotspot.cpp
+++ b/engines/mediastation/actors/hotspot.cpp
@@ -96,30 +96,32 @@ ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<Scrip
switch (methodId) {
case kMouseActivateMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
activate();
return returnValue;
}
case kMouseDeactivateMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
deactivate();
return returnValue;
}
case kIsActiveMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_isActive);
return returnValue;
}
case kTriggerAbsXPositionMethod: {
+ ARGCOUNTCHECK(0);
double mouseX = static_cast<double>(g_system->getEventManager()->getMousePos().x);
returnValue.setToFloat(mouseX);
return returnValue;
}
case kTriggerAbsYPositionMethod: {
+ ARGCOUNTCHECK(0);
double mouseY = static_cast<double>(g_system->getEventManager()->getMousePos().y);
returnValue.setToFloat(mouseY);
return returnValue;
diff --git a/engines/mediastation/actors/image.cpp b/engines/mediastation/actors/image.cpp
index 637d294193c..d7dcccbcb95 100644
--- a/engines/mediastation/actors/image.cpp
+++ b/engines/mediastation/actors/image.cpp
@@ -74,13 +74,13 @@ ScriptValue ImageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
ScriptValue returnValue;
switch (methodId) {
case kSpatialShowMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
spatialShow();
return returnValue;
}
case kSpatialHideMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
spatialHide();
return returnValue;
}
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index 3514702a552..92c7967d22f 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -151,45 +151,45 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
switch (methodId) {
case kTimePlayMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
timePlay();
return returnValue;
}
case kSpatialShowMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
setVisibility(true);
updateFrameState();
return returnValue;
}
case kTimeStopMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
timeStop();
return returnValue;
}
case kSpatialHideMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
setVisibility(false);
return returnValue;
}
case kIsPlayingMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_isPlaying);
return returnValue;
}
case kGetLeftXMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
double left = static_cast<double>(_boundingBox.left);
returnValue.setToFloat(left);
return returnValue;
}
case kGetTopYMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
double top = static_cast<double>(_boundingBox.top);
returnValue.setToFloat(top);
return returnValue;
diff --git a/engines/mediastation/actors/path.cpp b/engines/mediastation/actors/path.cpp
index af8db5da1cc..49954b1550e 100644
--- a/engines/mediastation/actors/path.cpp
+++ b/engines/mediastation/actors/path.cpp
@@ -63,26 +63,26 @@ ScriptValue PathActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptVa
switch (methodId) {
case kTimePlayMethod: {
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
timePlay();
return returnValue;
}
case kSetDurationMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint durationInMilliseconds = static_cast<uint>(args[0].asTime() * 1000);
setDuration(durationInMilliseconds);
return returnValue;
}
case kPercentCompleteMethod: {
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(percentComplete());
return returnValue;
}
case kIsPlayingMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_isPlaying);
return returnValue;
}
diff --git a/engines/mediastation/actors/sound.cpp b/engines/mediastation/actors/sound.cpp
index 5b2505c967e..6b5bded9ec8 100644
--- a/engines/mediastation/actors/sound.cpp
+++ b/engines/mediastation/actors/sound.cpp
@@ -94,17 +94,17 @@ ScriptValue SoundActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
// timer_6c06_AnsweringMachine, which calls SpatialShow on a sound.
// Since the engine is currently flagging errors on unimplemented
// methods for easier debugging, a no-op is used here to avoid the error.
- assert(args.empty());
+ ARGCOUNTCHECK(0);
return returnValue;
case kTimePlayMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
timePlay();
return returnValue;
}
case kTimeStopMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
timeStop();
return returnValue;
}
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index 868e4fe2a7c..b56d378ef77 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -143,37 +143,37 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
switch (methodId) {
case kSpatialShowMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
setVisibility(true);
return returnValue;
}
case kSpatialHideMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
setVisibility(false);
return returnValue;
}
case kTimePlayMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
play();
return returnValue;
}
case kTimeStopMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
stop();
return returnValue;
}
case kMovieResetMethod: {
- assert(args.empty());
+ ARGCOUNTCHECK(0);
setCurrentFrameToInitial();
return returnValue;
}
case kSetCurrentClipMethod: {
- assert(args.size() <= 1);
+ ARGCOUNTRANGE(0, 1);
uint clipId = DEFAULT_FORWARD_CLIP_ID;
if (args.size() == 1) {
clipId = args[0].asParamToken();
@@ -183,7 +183,7 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
}
case kIncrementFrameMethod: {
- assert(args.size() <= 1);
+ ARGCOUNTRANGE(0, 1);
bool loopAround = false;
if (args.size() == 1) {
loopAround = args[0].asBool();
diff --git a/engines/mediastation/actors/stage.cpp b/engines/mediastation/actors/stage.cpp
index 4c786877ffa..ca7193cafa0 100644
--- a/engines/mediastation/actors/stage.cpp
+++ b/engines/mediastation/actors/stage.cpp
@@ -318,21 +318,21 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
ScriptValue returnValue;
switch (methodId) {
case kAddActorToStageMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
addActorToStage(actorId);
return returnValue;
}
case kRemoveActorFromStageMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
removeActorFromStage(actorId);
return returnValue;
}
case kSetBoundsMethod: {
- assert(args.size() == 4);
+ ARGCOUNTCHECK(4);
int16 x = static_cast<int16>(args[0].asFloat());
int16 y = static_cast<int16>(args[1].asFloat());
int16 width = static_cast<int16>(args[2].asFloat());
@@ -343,16 +343,18 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
}
case kStageSetSizeMethod:
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
_boundingBox.setWidth(static_cast<int16>(args[0].asFloat()));
_boundingBox.setHeight(static_cast<int16>(args[1].asFloat()));
return returnValue;
case kStageGetWidthMethod:
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.width());
return returnValue;
case kStageGetHeightMethod:
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.height());
return returnValue;
diff --git a/engines/mediastation/actors/timer.cpp b/engines/mediastation/actors/timer.cpp
index 8119a458c22..9493ab43efa 100644
--- a/engines/mediastation/actors/timer.cpp
+++ b/engines/mediastation/actors/timer.cpp
@@ -31,19 +31,19 @@ ScriptValue TimerActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
switch (methodId) {
case kTimePlayMethod: {
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
timePlay();
return returnValue;
}
case kTimeStopMethod: {
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
timeStop();
return returnValue;
}
case kIsPlayingMethod: {
- assert(args.size() == 0);
+ ARGCOUNTCHECK(0);
returnValue.setToBool(_isPlaying);
return returnValue;
}
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index 06c5c43ed97..c4eb654f00b 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -264,11 +264,6 @@ void VideoDisplayManager::readAndRegisterPalette(Chunk &chunk) {
}
void VideoDisplayManager::effectTransition(Common::Array<ScriptValue> &args) {
- if (args.empty()) {
- warning("%s: Script args cannot be empty", __func__);
- return;
- }
-
TransitionType transitionType = static_cast<TransitionType>(args[0].asParamToken());
switch (transitionType) {
case kTransitionFadeToBlack:
@@ -426,7 +421,8 @@ void VideoDisplayManager::fadeToColor(Common::Array<ScriptValue> &args) {
void VideoDisplayManager::setToColor(Common::Array<ScriptValue> &args) {
if (args.size() < 6) {
- error("%s: Too few script args", __func__);
+ warning("%s: Too few script args", __func__);
+ return;
}
byte r = static_cast<byte>(args[1].asFloat());
@@ -442,7 +438,8 @@ void VideoDisplayManager::setToColor(Common::Array<ScriptValue> &args) {
void VideoDisplayManager::setToPercentOfPalette(Common::Array<ScriptValue> &args) {
if (args.size() < 7) {
- error("%s: Too few script args", __func__);
+ warning("%s: Too few script args", __func__);
+ return;
}
double percent = args[1].asFloat();
@@ -513,7 +510,7 @@ void VideoDisplayManager::setToPercentOfPaletteObject(Common::Array<ScriptValue>
percent = args[1].asFloat();
paletteId = args[2].asActorId();
} else {
- error("%s: Too few script args", __func__);
+ warning("%s: Too few script args", __func__);
return;
}
if (args.size() >= 5) {
diff --git a/engines/mediastation/mediascript/collection.cpp b/engines/mediastation/mediascript/collection.cpp
index 195a84f68f5..4c38285ab14 100644
--- a/engines/mediastation/mediascript/collection.cpp
+++ b/engines/mediastation/mediascript/collection.cpp
@@ -27,9 +27,9 @@
namespace MediaStation {
-ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptValue> &args) {
+ScriptValue Collection::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
- switch (method) {
+ switch (methodId) {
case kAppendMethod:
for (ScriptValue value : args) {
push_back(value);
@@ -41,12 +41,12 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
break;
case kCountMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToFloat(size());
break;
case kDeleteFirstMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
if (size() > 0) {
returnValue = remove_at(0);
debugC(7, kDebugScript, "%s: %s", __func__, returnValue.getDebugString().c_str());
@@ -56,7 +56,7 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
break;
case kDeleteLastMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
if (size() > 0) {
returnValue = remove_at(size() - 1);
debugC(7, kDebugScript, "%s: %s", __func__, returnValue.getDebugString().c_str());
@@ -66,12 +66,12 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
break;
case kEmptyMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
clear();
break;
case kGetAtMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint index = static_cast<uint>(args[0].asFloat());
if (index < size()) {
returnValue = operator[](index);
@@ -82,28 +82,29 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
}
case kIsEmptyMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
returnValue.setToBool(empty());
break;
case kJumbleMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
jumble();
break;
case kSeekMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
int index = seek(args[0]);
returnValue.setToFloat(index);
break;
}
case kSendMethod:
+ ARGCOUNTMIN(1);
send(args);
break;
case kDeleteAtMethod: {
- assert(args.size() == 1);
+ ARGCOUNTCHECK(1);
uint index = static_cast<uint>(args[0].asFloat());
if (index < size()) {
returnValue = remove_at(index);
@@ -115,7 +116,7 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
}
case kInsertAtMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
uint index = static_cast<uint>(args[1].asFloat());
if (index <= size()) {
insert_at(index, args[0]);
@@ -126,7 +127,7 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
}
case kReplaceAtMethod: {
- assert(args.size() == 2);
+ ARGCOUNTCHECK(2);
uint index = static_cast<uint>(args[1].asFloat());
if (index < size()) {
operator[](index) = args[0];
@@ -137,16 +138,17 @@ ScriptValue Collection::callMethod(BuiltInMethod method, Common::Array<ScriptVal
}
case kPrependListMethod:
+ ARGCOUNTMIN(1);
insert_at(0, args);
break;
case kSortMethod:
- assert(args.empty());
+ ARGCOUNTCHECK(0);
Common::sort(begin(), end());
break;
default:
- error("%s: Attempt to call unimplemented method %s (%d)", __func__, builtInMethodToStr(method), static_cast<uint>(method));
+ error("%s: Attempt to call unimplemented method %s (%d)", __func__, builtInMethodToStr(methodId), static_cast<uint>(methodId));
}
return returnValue;
}
diff --git a/engines/mediastation/mediascript/function.cpp b/engines/mediastation/mediascript/function.cpp
index 490b968e978..76fff863a4c 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -24,6 +24,25 @@
#include "mediastation/mediastation.h"
namespace MediaStation {
+
+// For exact argument count.
+#define FUNCARGCHECK(n) \
+ if (args.size() != (n)) { \
+ warning("%s: expected %d argument%s, got %d", builtInFunctionToStr(static_cast<BuiltInFunction>(functionId)), (n), ((n) == 1 ? "" : "s"), args.size()); \
+ }
+
+// For a range of valid argument counts (min to max).
+#define FUNCARGRANGE(min, max) \
+ if (args.size() < (min) || args.size() > (max)) { \
+ warning("%s: expected %d to %d argument, got %d", builtInFunctionToStr(static_cast<BuiltInFunction>(functionId)), (min), (max), args.size()); \
+ }
+
+// For minimum argument count (no maximum).
+#define FUNCARGMIN(min) \
+ if (args.size() < (min)) { \
+ warning("%s: expected at least %d argument%s, got %d", builtInFunctionToStr(static_cast<BuiltInFunction>(functionId)), (min), ((min) == 1 ? "" : "s"), args.size()); \
+ }
+
ScriptFunction::ScriptFunction(Chunk &chunk) {
_contextId = chunk.readTypedUint16();
_id = chunk.readTypedUint16();
@@ -84,12 +103,13 @@ ScriptValue FunctionManager::call(uint functionId, Common::Array<ScriptValue> &a
switch (functionId) {
case kRandomFunction:
case kLegacy_RandomFunction:
- assert(args.size() == 2);
+ FUNCARGCHECK(2);
script_Random(args, returnValue);
break;
case kTimeOfDayFunction:
case kLegacy_TimeOfDayFunction:
+ FUNCARGCHECK(0);
script_TimeOfDay(args, returnValue);
break;
@@ -105,45 +125,49 @@ ScriptValue FunctionManager::call(uint functionId, Common::Array<ScriptValue> &a
case kPlatformFunction:
case kLegacy_PlatformFunction:
- assert(args.empty());
+ FUNCARGCHECK(0);
script_GetPlatform(args, returnValue);
break;
case kSquareRootFunction:
case kLegacy_SquareRootFunction:
- assert(args.size() == 1);
+ FUNCARGCHECK(1);
script_SquareRoot(args, returnValue);
break;
case kGetUniqueRandomFunction:
case kLegacy_GetUniqueRandomFunction:
- assert(args.size() >= 2);
+ FUNCARGMIN(2);
script_GetUniqueRandom(args, returnValue);
break;
case kCurrentRunTimeFunction:
+ FUNCARGCHECK(0);
script_CurrentRunTime(args, returnValue);
break;
case kSetGammaCorrectionFunction:
+ FUNCARGRANGE(1, 3);
script_SetGammaCorrection(args, returnValue);
break;
case kGetDefaultGammaCorrectionFunction:
+ FUNCARGCHECK(0);
script_GetDefaultGammaCorrection(args, returnValue);
break;
case kGetCurrentGammaCorrectionFunction:
+ FUNCARGCHECK(0);
script_GetCurrentGammaCorrection(args, returnValue);
break;
case kSetAudioVolumeFunction:
- assert(args.size() == 1);
+ FUNCARGCHECK(1);
script_SetAudioVolume(args, returnValue);
break;
case kGetAudioVolumeFunction:
- assert(args.empty());
+ FUNCARGCHECK(0);
script_GetAudioVolume(args, returnValue);
break;
@@ -189,6 +213,7 @@ ScriptValue FunctionManager::call(uint functionId, Common::Array<ScriptValue> &a
break;
case kLegacy_DebugPrintFunction:
+ // We don't need to check arg counts here. This just prints however many args we have.
script_DebugPrint(args, returnValue);
break;
@@ -371,11 +396,6 @@ void FunctionManager::script_CurrentRunTime(Common::Array<ScriptValue> &args, Sc
}
void FunctionManager::script_SetGammaCorrection(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
- if (args.size() != 1 && args.size() != 3) {
- warning("%s: Expected 1 or 3 arguments, got %u", __func__, args.size());
- return;
- }
-
double red = 1.0;
double green = 1.0;
double blue = 1.0;
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index 283814e5eaf..1f5010af0d5 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -23,6 +23,8 @@
#include "mediastation/mediascript/function.h"
#include "mediastation/mediastation.h"
+#define VALUETYPEMISMATCH(expectedType) warning("%s: Script value type mismatch: Expected %s, got %s", __func__, scriptValueTypeToStr(expectedType), scriptValueTypeToStr(_type))
+
namespace MediaStation {
ScriptValue::ScriptValue(ParameterReadStream *stream) {
@@ -117,7 +119,7 @@ double ScriptValue::asFloat() const {
if (_type == kScriptValueTypeFloat) {
return _u.d;
} else {
- issueValueMismatchWarning(kScriptValueTypeFloat);
+ VALUETYPEMISMATCH(kScriptValueTypeFloat);
return 0.0;
}
}
@@ -131,7 +133,7 @@ bool ScriptValue::asBool() const {
if (_type == kScriptValueTypeBool) {
return _u.b;
} else {
- issueValueMismatchWarning(kScriptValueTypeBool);
+ VALUETYPEMISMATCH(kScriptValueTypeBool);
return false;
}
}
@@ -145,7 +147,7 @@ double ScriptValue::asTime() const {
if (_type == kScriptValueTypeTime) {
return _u.d;
} else {
- issueValueMismatchWarning(kScriptValueTypeTime);
+ VALUETYPEMISMATCH(kScriptValueTypeTime);
return 0.0;
}
}
@@ -159,7 +161,7 @@ uint ScriptValue::asParamToken() const {
if (_type == kScriptValueTypeParamToken) {
return _u.paramToken;
} else {
- issueValueMismatchWarning(kScriptValueTypeParamToken);
+ VALUETYPEMISMATCH(kScriptValueTypeParamToken);
return 0;
}
}
@@ -173,7 +175,7 @@ uint ScriptValue::asActorId() const {
if (_type == kScriptValueTypeActorId) {
return _u.actorId;
} else {
- issueValueMismatchWarning(kScriptValueTypeActorId);
+ VALUETYPEMISMATCH(kScriptValueTypeActorId);
return 0;
}
}
@@ -200,7 +202,7 @@ Common::SharedPtr<Collection> ScriptValue::asCollection() const {
if (_type == kScriptValueTypeCollection) {
return _collection;
} else {
- issueValueMismatchWarning(kScriptValueTypeCollection);
+ VALUETYPEMISMATCH(kScriptValueTypeCollection);
return nullptr;
}
}
@@ -214,7 +216,7 @@ uint ScriptValue::asFunctionId() const {
if (_type == kScriptValueTypeFunctionId) {
return _u.functionId;
} else {
- issueValueMismatchWarning(kScriptValueTypeFunctionId);
+ VALUETYPEMISMATCH(kScriptValueTypeFunctionId);
return 0;
}
}
@@ -228,7 +230,7 @@ BuiltInMethod ScriptValue::asMethodId() const {
if (_type == kScriptValueTypeMethodId) {
return _u.methodId;
} else {
- issueValueMismatchWarning(kScriptValueTypeMethodId);
+ VALUETYPEMISMATCH(kScriptValueTypeMethodId);
return kInvalidMethod;
}
}
@@ -576,11 +578,4 @@ ScriptValue ScriptValue::operator-() const {
return returnValue;
}
-void ScriptValue::issueValueMismatchWarning(ScriptValueType expectedType) const {
- // The original just blithely returns 0 (or equivalent) when you call a
- // getter for the wrong type (for instance, calling asFloat() on a bool),
- // but for debugging purposes we'll issue a warning.
- warning("%s: Script value type mismatch: Expected %s, got %s", __func__, scriptValueTypeToStr(expectedType), scriptValueTypeToStr(_type));
-}
-
} // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/scriptvalue.h b/engines/mediastation/mediascript/scriptvalue.h
index 6b8fc130f24..7b4270fd88b 100644
--- a/engines/mediastation/mediascript/scriptvalue.h
+++ b/engines/mediastation/mediascript/scriptvalue.h
@@ -113,8 +113,6 @@ private:
static ScriptValue evalMathOperation(Opcode op, const ScriptValue &left, const ScriptValue &right);
static double binaryMathOperation(Opcode op, double left, double right);
-
- void issueValueMismatchWarning(ScriptValueType actualType) const;
};
} // End of namespace MediaStation
Commit: 90cbd70cd36d5d1afc95e7268e7b56262e869b70
https://github.com/scummvm/scummvm/commit/90cbd70cd36d5d1afc95e7268e7b56262e869b70
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Rename BitmapHeader to ImageInfo
And Bitmap to PixMapImage. Both of these changes make the
names more accurate to those found in debug symbols.
Changed paths:
engines/mediastation/actors/camera.cpp
engines/mediastation/actors/font.cpp
engines/mediastation/actors/font.h
engines/mediastation/actors/image.cpp
engines/mediastation/actors/image.h
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/movie.h
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/sprite.h
engines/mediastation/bitmap.cpp
engines/mediastation/bitmap.h
engines/mediastation/graphics.cpp
engines/mediastation/graphics.h
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/actors/camera.cpp b/engines/mediastation/actors/camera.cpp
index 94bd895d025..4afcbbd6f7f 100644
--- a/engines/mediastation/actors/camera.cpp
+++ b/engines/mediastation/actors/camera.cpp
@@ -79,8 +79,8 @@ void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType)
}
void CameraActor::readChunk(Chunk &chunk) {
- BitmapHeader *bitmapHeader = new BitmapHeader(chunk);
- _image->bitmap = new Bitmap(chunk, bitmapHeader);
+ ImageInfo bitmapHeader(chunk);
+ _image->bitmap = new PixMapImage(chunk, bitmapHeader);
}
ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
diff --git a/engines/mediastation/actors/font.cpp b/engines/mediastation/actors/font.cpp
index 0614a63dd89..72cd2f1e604 100644
--- a/engines/mediastation/actors/font.cpp
+++ b/engines/mediastation/actors/font.cpp
@@ -24,7 +24,7 @@
namespace MediaStation {
-FontGlyph::FontGlyph(Chunk &chunk, uint asciiCode, uint unk1, uint unk2, BitmapHeader *header) : Bitmap(chunk, header) {
+FontGlyph::FontGlyph(Chunk &chunk, uint asciiCode, int unk1, int unk2, const ImageInfo &header) : PixMapImage(chunk, header) {
_asciiCode = asciiCode;
_unk1 = unk1;
_unk2 = unk2;
@@ -55,7 +55,7 @@ void FontActor::readChunk(Chunk &chunk) {
uint asciiCode = chunk.readTypedUint16();
int unk1 = chunk.readTypedUint16();
int unk2 = chunk.readTypedUint16();
- BitmapHeader *header = new BitmapHeader(chunk);
+ ImageInfo header(chunk);
FontGlyph *glyph = new FontGlyph(chunk, asciiCode, unk1, unk2, header);
if (_glyphs.getValOrDefault(asciiCode) != nullptr) {
error("%s: Glyph for ASCII code 0x%x already exists", __func__, asciiCode);
diff --git a/engines/mediastation/actors/font.h b/engines/mediastation/actors/font.h
index b6b0eb69db5..654f269fca1 100644
--- a/engines/mediastation/actors/font.h
+++ b/engines/mediastation/actors/font.h
@@ -30,12 +30,10 @@
namespace MediaStation {
-class FontGlyph : public Bitmap {
+class FontGlyph : public PixMapImage {
public:
- FontGlyph(Chunk &chunk, uint asciiCode, uint unk1, uint unk2, BitmapHeader *header);
+ FontGlyph(Chunk &chunk, uint asciiCode, int unk1, int unk2, const ImageInfo &header);
uint _asciiCode = 0;
-
-private:
int _unk1 = 0;
int _unk2 = 0;
};
diff --git a/engines/mediastation/actors/image.cpp b/engines/mediastation/actors/image.cpp
index d7dcccbcb95..48d405fd521 100644
--- a/engines/mediastation/actors/image.cpp
+++ b/engines/mediastation/actors/image.cpp
@@ -114,8 +114,8 @@ Common::Rect ImageActor::getBbox() const {
}
void ImageActor::readChunk(Chunk &chunk) {
- BitmapHeader *bitmapHeader = new BitmapHeader(chunk);
- _asset->bitmap = new Bitmap(chunk, bitmapHeader);
+ ImageInfo bitmapHeader = ImageInfo(chunk);
+ _asset->bitmap = new PixMapImage(chunk, bitmapHeader);
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/image.h b/engines/mediastation/actors/image.h
index 7fa57a9b1e5..e005fedc9ee 100644
--- a/engines/mediastation/actors/image.h
+++ b/engines/mediastation/actors/image.h
@@ -37,7 +37,7 @@ namespace MediaStation {
struct ImageAsset {
~ImageAsset();
- Bitmap *bitmap = nullptr;
+ PixMapImage *bitmap = nullptr;
};
class ImageActor : public SpatialEntity, public ChannelClient {
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index 92c7967d22f..e417919ecb2 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -25,9 +25,9 @@
namespace MediaStation {
-MovieFrameHeader::MovieFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
+MovieFrameInfo::MovieFrameInfo(Chunk &chunk) : ImageInfo(chunk) {
_index = chunk.readTypedUint32();
- debugC(5, kDebugLoading, "MovieFrameHeader::MovieFrameHeader(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
+ debugC(5, kDebugLoading, "MovieFrameInfo::MovieFrameInfo(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
_keyframeEndInMilliseconds = chunk.readTypedUint32();
}
@@ -67,13 +67,7 @@ MovieFrame::MovieFrame(Chunk &chunk) {
}
}
-MovieFrameImage::MovieFrameImage(Chunk &chunk, MovieFrameHeader *header) : Bitmap(chunk, header) {
- _bitmapHeader = header;
-}
-
-MovieFrameImage::~MovieFrameImage() {
- // The base class destructor takes care of deleting the bitmap header, so
- // we don't need to delete that here.
+MovieFrameImage::MovieFrameImage(Chunk &chunk, const MovieFrameInfo &header) : PixMapImage(chunk, header), _frameInfo(header) {
}
StreamMovieActor::~StreamMovieActor() {
@@ -458,7 +452,7 @@ void StreamMovieActor::decompressIntoAuxImage(MovieFrame *frame) {
}
void StreamMovieActorFrames::readImageData(Chunk &chunk) {
- MovieFrameHeader *header = new MovieFrameHeader(chunk);
+ MovieFrameInfo header(chunk);
MovieFrameImage *frame = new MovieFrameImage(chunk, header);
_images.push_back(frame);
}
diff --git a/engines/mediastation/actors/movie.h b/engines/mediastation/actors/movie.h
index 01a0e0e1131..e2ce06e3f71 100644
--- a/engines/mediastation/actors/movie.h
+++ b/engines/mediastation/actors/movie.h
@@ -39,23 +39,24 @@ enum MovieBlitType {
kCompressedDeltaMovieBlit = 3,
};
-class MovieFrameHeader : public BitmapHeader {
+class MovieFrameInfo : public ImageInfo {
public:
- MovieFrameHeader(Chunk &chunk);
+ MovieFrameInfo() = default;
+ MovieFrameInfo(Chunk &chunk);
uint _index = 0;
uint _keyframeEndInMilliseconds = 0;
};
-class MovieFrameImage : public Bitmap {
+class MovieFrameImage : public PixMapImage {
public:
- MovieFrameImage(Chunk &chunk, MovieFrameHeader *header);
- virtual ~MovieFrameImage() override;
+ MovieFrameImage(Chunk &chunk, const MovieFrameInfo &header);
+ ~MovieFrameImage() = default;
- uint32 index() { return _bitmapHeader->_index; }
+ uint32 index() { return _frameInfo._index; }
private:
- MovieFrameHeader *_bitmapHeader = nullptr;
+ MovieFrameInfo _frameInfo;
};
enum MovieSectionType {
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index b56d378ef77..c6f4f66cc8d 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -25,7 +25,7 @@
namespace MediaStation {
-SpriteFrameHeader::SpriteFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
+SpriteFrameInfo::SpriteFrameInfo(Chunk &chunk) : ImageInfo(chunk) {
_index = chunk.readTypedUint16();
_offset = chunk.readTypedPoint();
}
@@ -34,20 +34,15 @@ Common::String SpriteMovieClip::getDebugString() const {
return Common::String::format("%s: [%d, %d]", g_engine->formatParamTokenName(id).c_str(), firstFrameIndex, lastFrameIndex);
}
-SpriteFrame::SpriteFrame(Chunk &chunk, SpriteFrameHeader *header) : Bitmap(chunk, header) {
- _bitmapHeader = header;
-}
-
-SpriteFrame::~SpriteFrame() {
- // The base class destructor takes care of deleting the bitmap header.
+SpriteFrame::SpriteFrame(Chunk &chunk, const SpriteFrameInfo &header) : PixMapImage(chunk, header), _frameInfo(header) {
}
uint32 SpriteFrame::left() {
- return _bitmapHeader->_offset.x;
+ return _frameInfo._offset.x;
}
uint32 SpriteFrame::top() {
- return _bitmapHeader->_offset.y;
+ return _frameInfo._offset.y;
}
Common::Point SpriteFrame::topLeft() {
@@ -59,7 +54,7 @@ Common::Rect SpriteFrame::boundingBox() {
}
uint32 SpriteFrame::index() {
- return _bitmapHeader->_index;
+ return _frameInfo._index;
}
SpriteAsset::~SpriteAsset() {
@@ -352,7 +347,7 @@ void SpriteMovieActor::process() {
void SpriteMovieActor::readChunk(Chunk &chunk) {
// Reads one frame from the sprite.
- SpriteFrameHeader *header = new SpriteFrameHeader(chunk);
+ SpriteFrameInfo header(chunk);
SpriteFrame *frame = new SpriteFrame(chunk, header);
_asset->frames.push_back(frame);
diff --git a/engines/mediastation/actors/sprite.h b/engines/mediastation/actors/sprite.h
index 389365298ed..c805ee32325 100644
--- a/engines/mediastation/actors/sprite.h
+++ b/engines/mediastation/actors/sprite.h
@@ -45,18 +45,18 @@ struct SpriteMovieClip {
Common::String getDebugString() const;
};
-class SpriteFrameHeader : public BitmapHeader {
+class SpriteFrameInfo : public ImageInfo {
public:
- SpriteFrameHeader(Chunk &chunk);
+ SpriteFrameInfo() = default;
+ SpriteFrameInfo(Chunk &chunk);
uint _index;
Common::Point _offset;
};
-class SpriteFrame : public Bitmap {
+class SpriteFrame : public PixMapImage {
public:
- SpriteFrame(Chunk &chunk, SpriteFrameHeader *header);
- virtual ~SpriteFrame() override;
+ SpriteFrame(Chunk &chunk, const SpriteFrameInfo &frameInfo);
uint32 left();
uint32 top();
@@ -65,7 +65,7 @@ public:
uint32 index();
private:
- SpriteFrameHeader *_bitmapHeader = nullptr;
+ SpriteFrameInfo _frameInfo;
};
// The original had a separate class that did reference counting,
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index 3e1019d1f8e..e3698780862 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -24,16 +24,16 @@
namespace MediaStation {
-BitmapHeader::BitmapHeader(Chunk &chunk) {
+ImageInfo::ImageInfo(Chunk &chunk) {
uint headerSizeInBytes = chunk.readTypedUint16();
_dimensions = chunk.readTypedGraphicSize();
_compressionType = static_cast<BitmapCompressionType>(chunk.readTypedUint16());
_stride = chunk.readTypedUint16();
- debugC(5, kDebugLoading, "BitmapHeader::BitmapHeader(): headerSize: %d, _compressionType = 0x%x, _stride = %d",
- headerSizeInBytes, static_cast<uint>(_compressionType), _stride);
+ debugC(5, kDebugLoading, "%s: headerSize: %d, _compressionType: 0x%x, _stride: %d",
+ __func__, headerSizeInBytes, static_cast<uint>(_compressionType), _stride);
}
-Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) : _bitmapHeader(bitmapHeader) {
+PixMapImage::PixMapImage(Chunk &chunk, const ImageInfo &imageInfo) : _imageInfo(imageInfo) {
if (stride() < width()) {
warning("%s: Got stride less than width", __func__);
}
@@ -57,15 +57,12 @@ Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) : _bitmapHeader(bitmapH
}
}
-Bitmap::~Bitmap() {
- delete _bitmapHeader;
- _bitmapHeader = nullptr;
-
+PixMapImage::~PixMapImage() {
delete _compressedStream;
_compressedStream = nullptr;
}
-bool Bitmap::isCompressed() const {
+bool PixMapImage::isCompressed() const {
return (getCompressionType() != kUncompressedBitmap) && \
(getCompressionType() != kUncompressedTransparentBitmap);
}
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index a68cf4b2db6..f3cfc7205e1 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -38,31 +38,32 @@ enum BitmapCompressionType {
kUncompressedTransparentBitmap = 7,
};
-class BitmapHeader {
+class ImageInfo {
public:
- BitmapHeader(Chunk &chunk);
+ ImageInfo() = default;
+ ImageInfo(Chunk &chunk);
Common::Point _dimensions;
BitmapCompressionType _compressionType = kUncompressedBitmap;
int16 _stride = 0;
};
-class Bitmap {
+class PixMapImage {
public:
- Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader);
- virtual ~Bitmap();
+ PixMapImage(Chunk &chunk, const ImageInfo &imageInfo);
+ virtual ~PixMapImage();
bool isCompressed() const;
- BitmapCompressionType getCompressionType() const { return _bitmapHeader->_compressionType; }
- int16 width() const { return _bitmapHeader->_dimensions.x; }
- int16 height() const { return _bitmapHeader->_dimensions.y; }
- int16 stride() const { return _bitmapHeader->_stride; }
+ BitmapCompressionType getCompressionType() const { return _imageInfo._compressionType; }
+ int16 width() const { return _imageInfo._dimensions.x; }
+ int16 height() const { return _imageInfo._dimensions.y; }
+ int16 stride() const { return _imageInfo._stride; }
Common::SeekableReadStream *_compressedStream = nullptr;
Graphics::ManagedSurface _image;
private:
- BitmapHeader *_bitmapHeader = nullptr;
+ ImageInfo _imageInfo;
uint _unk1 = 0;
};
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index c4eb654f00b..c9d4ecb5810 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -712,7 +712,7 @@ void VideoDisplayManager::_setPercentToPaletteObject(double percent, uint palett
void VideoDisplayManager::imageBlit(
Common::Point destinationPoint,
- const Bitmap *sourceImage,
+ const PixMapImage *sourceImage,
double dissolveFactor,
DisplayContext *displayContext,
Graphics::ManagedSurface *targetImage) {
@@ -832,7 +832,7 @@ void VideoDisplayManager::blitRectsClip(
void VideoDisplayManager::rleBlitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destLocation,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Array<Common::Rect> &dirtyRegion) {
Graphics::ManagedSurface surface = decompressRle8Bitmap(source);
@@ -852,7 +852,7 @@ void VideoDisplayManager::rleBlitRectsClip(
void VideoDisplayManager::dissolveBlitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destPos,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Array<Common::Rect> &dirtyRegion,
const uint integralDissolveFactor) {
@@ -878,7 +878,7 @@ void VideoDisplayManager::dissolveBlit1Rect(
Graphics::ManagedSurface *dest,
const Common::Rect &areaToRedraw,
const Common::Point &originOnScreen,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Rect &dirtyRegion,
const DissolvePattern &pattern) {
@@ -940,8 +940,8 @@ void VideoDisplayManager::dissolveBlit1Rect(
void VideoDisplayManager::imageDeltaBlit(
Common::Point deltaFramePos,
const Common::Point &keyFrameOffset,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const double dissolveFactor,
DisplayContext *displayContext) {
@@ -981,8 +981,8 @@ void VideoDisplayManager::fullDeltaRleBlitRectsClip(
Graphics::ManagedSurface *destinationImage,
const Common::Point &deltaFramePos,
const Common::Point &keyFrameOffset,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const Common::Array<Common::Rect> &dirtyRegion) {
Graphics::ManagedSurface surface = decompressRle8Bitmap(deltaFrame, &keyFrame->_image, &keyFrameOffset);
@@ -1005,8 +1005,8 @@ void VideoDisplayManager::fullDeltaRleBlitRectsClip(
void VideoDisplayManager::deltaRleBlitRectsClip(
Graphics::ManagedSurface *destinationImage,
const Common::Point &deltaFramePos,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const Common::Array<Common::Rect> &dirtyRegion) {
Common::Rect deltaFrameBounds = Common::Rect(deltaFramePos, deltaFrame->width(), deltaFrame->height());
@@ -1020,8 +1020,8 @@ void VideoDisplayManager::deltaRleBlitRectsClip(
void VideoDisplayManager::deltaRleBlit1Rect(
Graphics::ManagedSurface *destinationImage,
const Common::Point &destinationPoint,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const Common::Rect &dirtyRect) {
// This is a very complex function that attempts to decompress the keyframe
@@ -1033,7 +1033,7 @@ void VideoDisplayManager::deltaRleBlit1Rect(
}
Graphics::ManagedSurface VideoDisplayManager::decompressRle8Bitmap(
- const Bitmap *source,
+ const PixMapImage *source,
const Graphics::ManagedSurface *keyFrame,
const Common::Point *keyFrameOffset) {
diff --git a/engines/mediastation/graphics.h b/engines/mediastation/graphics.h
index cd1fc1e7bbc..49b95d85d8b 100644
--- a/engines/mediastation/graphics.h
+++ b/engines/mediastation/graphics.h
@@ -35,7 +35,7 @@ namespace MediaStation {
class MediaStationEngine;
struct DissolvePattern;
-class Bitmap;
+class PixMapImage;
enum BlitMode {
kUncompressedBlit = 0x00,
@@ -136,7 +136,7 @@ public:
void imageBlit(
Common::Point destinationPoint,
- const Bitmap *image,
+ const PixMapImage *image,
double dissolveFactor,
DisplayContext *displayContext,
Graphics::ManagedSurface *destinationImage = nullptr);
@@ -144,8 +144,8 @@ public:
void imageDeltaBlit(
Common::Point deltaFramePos,
const Common::Point &keyFrameOffset,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const double dissolveFactor,
DisplayContext *displayContext);
@@ -188,43 +188,43 @@ private:
void rleBlitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destLocation,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Array<Common::Rect> &dirtyRegion);
Graphics::ManagedSurface decompressRle8Bitmap(
- const Bitmap *source,
+ const PixMapImage *source,
const Graphics::ManagedSurface *keyFrame = nullptr,
const Common::Point *keyFrameOffset = nullptr);
void dissolveBlitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destPos,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Array<Common::Rect> &dirtyRegion,
const uint dissolveFactor);
void dissolveBlit1Rect(
Graphics::ManagedSurface *dest,
const Common::Rect &areaToRedraw,
const Common::Point &originOnScreen,
- const Bitmap *source,
+ const PixMapImage *source,
const Common::Rect &dirtyRegion,
const DissolvePattern &pattern);
void fullDeltaRleBlitRectsClip(
Graphics::ManagedSurface *destinationImage,
const Common::Point &deltaFramePos,
const Common::Point &keyFrameOffset,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const Common::Array<Common::Rect> &dirtyRegion);
void deltaRleBlitRectsClip(
Graphics::ManagedSurface *destinationImage,
const Common::Point &deltaFramePos,
- const Bitmap *deltaFrame,
- const Bitmap *keyFrame,
+ const PixMapImage *deltaFrame,
+ const PixMapImage *keyFrame,
const Common::Array<Common::Rect> &dirtyRegion);
void deltaRleBlit1Rect(
Graphics::ManagedSurface *destinationImage,
const Common::Point &destinationPoint,
- const Bitmap *sourceImage,
- const Bitmap *deltaImage,
+ const PixMapImage *sourceImage,
+ const PixMapImage *deltaImage,
const Common::Rect &dirtyRect);
// Transition methods.
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index d7074215ba8..43246c715d3 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -52,7 +52,7 @@ namespace MediaStation {
struct MediaStationGameDescription;
class HotspotActor;
class RootStage;
-class Bitmap;
+class PixMapImage;
// Most Media Station titles follow this file structure from the root directory
// of the CD-ROM:
Commit: a86401a6951744baa6e6c33cda0246ab8ddc8817
https://github.com/scummvm/scummvm/commit/a86401a6951744baa6e6c33cda0246ab8ddc8817
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Add initial support for stream movie proxies
Changed paths:
engines/mediastation/actor.cpp
engines/mediastation/actor.h
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/movie.h
engines/mediastation/datafile.cpp
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index 2ea0a7a6861..0f7f661f367 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -62,6 +62,8 @@ const char *actorTypeToStr(ActorType type) {
return "ImageSet";
case kActorTypeMovie:
return "Movie";
+ case kActorTypeStreamMovieProxy:
+ return "StreamMovieProxy";
case kActorTypePalette:
return "Palette";
case kActorTypePrinter:
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index 781d068900d..1f69b662c83 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -54,6 +54,7 @@ enum ActorType {
kActorTypePrinter = 0x0019, // PRT
kActorTypeMovie = 0x0016, // MOV
kActorTypePalette = 0x0017,
+ kActorTypeStreamMovieProxy = 0x18,
kActorTypeText = 0x001a, // TXT
kActorTypeFont = 0x001b, // FON
kActorTypeCamera = 0x001c, // CAM
@@ -97,6 +98,7 @@ enum ActorHeaderSectionType {
kActorHeaderScaleY = 0x77d,
kActorHeaderUnk0 = 0x7d0,
kActorHeaderActorName = 0x0bb8,
+ kStreamMovieProxyInfo = 0x06ac,
// PATH FIELDS.
kActorHeaderStartPoint = 0x060e,
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index e417919ecb2..d01ca3ccf7d 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -25,9 +25,38 @@
namespace MediaStation {
+StreamMovieProxy::StreamMovieProxy(Chunk &chunk, StreamMovieActor *parent) : SpatialEntity(kActorTypeStreamMovieProxy) {
+ _layerId = chunk.readTypedUint32();
+ _scriptId = chunk.readTypedUint16();
+ uint zIndex = chunk.readTypedSint16();
+ setZIndex(zIndex);
+ setBounds(parent->getBbox());
+ debugC(5, kDebugLoading, "[%s] %s: layerId: %d; scriptId: %d; zIndex: %d",
+ parent->debugName(), __func__, _layerId, _scriptId, zIndex);
+ _isVisible = true;
+ _parent = parent;
+}
+
+void StreamMovieProxy::draw(DisplayContext &displayContext) {
+ if (_parent != nullptr) {
+ _parent->drawLayer(displayContext, _layerId);
+ } else {
+ warning("[%s] %s: Stream movie proxy has no parent", debugName(), __func__);
+ }
+}
+
+bool StreamMovieProxy::isVisible() const {
+ if (_isVisible) {
+ if (_parentStage != nullptr) {
+ return _parentStage->isVisible();
+ }
+ }
+ return false;
+}
+
MovieFrameInfo::MovieFrameInfo(Chunk &chunk) : ImageInfo(chunk) {
_index = chunk.readTypedUint32();
- debugC(5, kDebugLoading, "MovieFrameInfo::MovieFrameInfo(): _index = 0x%x (@0x%llx)", _index, static_cast<long long int>(chunk.pos()));
+ debugC(5, kDebugLoading, "%s: frame 0x%x", __func__, _index);
_keyframeEndInMilliseconds = chunk.readTypedUint32();
}
@@ -59,11 +88,6 @@ MovieFrame::MovieFrame(Chunk &chunk) {
index = chunk.readTypedUint32();
keyframeIndex = chunk.readTypedUint32();
keepAfterEnd = chunk.readTypedByte();
- debugC(5, kDebugLoading, "MovieFrame::MovieFrame(): _blitType = %d, _startInMilliseconds = %d, \
- _endInMilliseconds = %d, _left = %d, _top = %d, _zIndex = %d, _diffBetweenKeyframeAndFrameX = %d, \
- _diffBetweenKeyframeAndFrameY = %d, _index = %d, _keyframeIndex = %d, _keepAfterEnd = %d (@0x%llx)",
- blitType, startInMilliseconds, endInMilliseconds, leftTop.x, leftTop.y, zIndex, diffBetweenKeyframeAndFrame.x, \
- diffBetweenKeyframeAndFrame.y, index, keyframeIndex, keepAfterEnd, static_cast<long long int>(chunk.pos()));
}
}
@@ -82,6 +106,11 @@ StreamMovieActor::~StreamMovieActor() {
delete _streamSound;
_streamSound = nullptr;
+
+ for (StreamMovieProxy *proxy : _proxies) {
+ delete proxy;
+ }
+ _proxies.clear();
}
void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
@@ -91,7 +120,7 @@ void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
// as the ID we have already read.
uint32 duplicateActorId = chunk.readTypedUint16();
if (duplicateActorId != _id) {
- warning("%s: Duplicate actor ID %d does not match original ID %d", __func__, duplicateActorId, _id);
+ warning("[%s] %s: Duplicate actor ID %d does not match original ID %d", debugName(), __func__, duplicateActorId, _id);
}
break;
}
@@ -135,6 +164,12 @@ void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
_streamSound->_audioSequence.readParameters(chunk);
break;
+ case kStreamMovieProxyInfo: {
+ StreamMovieProxy *proxy = new StreamMovieProxy(chunk, this);
+ _proxies.push_back(proxy);
+ break;
+ }
+
default:
SpatialEntity::readParameter(chunk, paramType);
}
@@ -144,16 +179,51 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
ScriptValue returnValue;
switch (methodId) {
- case kTimePlayMethod: {
- ARGCOUNTCHECK(0);
- timePlay();
+ case kSpatialShowMethod: {
+ if (args.empty()) {
+ // Set our visibility directly.
+ setVisibility(true);
+ updateFrameState();
+ } else {
+ // Set the visibility of a proxy.
+ uint scriptId = args[0].asParamToken();
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy != nullptr) {
+ if (!proxy->isVisible()) {
+ proxy->_isVisible = true;
+ }
+ } else {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ }
+ }
return returnValue;
}
- case kSpatialShowMethod: {
+ case kSpatialHideMethod: {
+ if (args.empty()) {
+ // Set our visibility directly.
+ setVisibility(false);
+ updateFrameState();
+ } else {
+ // Set the visibility of a proxy.
+ uint scriptId = args[0].asParamToken();
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy != nullptr) {
+ if (proxy->isVisible()) {
+ proxy->_isVisible = false;
+ }
+ } else {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ }
+ }
+ return returnValue;
+ }
+
+ case kTimePlayMethod: {
ARGCOUNTCHECK(0);
- setVisibility(true);
- updateFrameState();
+ timePlay();
return returnValue;
}
@@ -163,9 +233,30 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
return returnValue;
}
- case kSpatialHideMethod: {
- ARGCOUNTCHECK(0);
- setVisibility(false);
+ case kStreamMovieSetProxyZIndex: {
+ ARGCOUNTCHECK(2);
+ uint scriptId = args[0].asParamToken();
+ int zIndex = static_cast<int>(args[1].asFloat());
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy != nullptr) {
+ proxy->setZIndex(zIndex);
+ } else {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ }
+ return returnValue;
+ }
+
+ case kStreamMovieGetProxyZIndex: {
+ ARGCOUNTCHECK(2);
+ uint scriptId = args[0].asParamToken();
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy != nullptr) {
+ returnValue.setToFloat(proxy->zIndex());
+ } else {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ }
return returnValue;
}
@@ -189,6 +280,52 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
return returnValue;
}
+ case kStreamMovieMoveProxyToStageMethod: {
+ ARGCOUNTCHECK(2);
+ uint scriptId = args[0].asParamToken();
+ uint targetStageId = args[1].asActorId();
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy == nullptr) {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ return returnValue;
+ }
+ StageActor *parentStage = static_cast<StageActor *>(g_engine->getActorByIdAndType(targetStageId, kActorTypeStage));
+ if (parentStage == nullptr) {
+ warning("[%s] %s: Stream movie proxy with script ID %s has null parent stage",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ return returnValue;
+ }
+
+ proxy->getParentStage()->removeChildSpatialEntity(proxy);
+ parentStage->addChildSpatialEntity(proxy);
+ return returnValue;
+ }
+
+ case kStreamMovieMoveProxyToRootStageMethod: {
+ ARGCOUNTCHECK(2);
+ uint scriptId = args[0].asParamToken();
+ uint sourceStageId = args[1].asActorId();
+ StreamMovieProxy *proxy = proxyOfScriptId(scriptId);
+ if (proxy == nullptr) {
+ warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ return returnValue;
+ }
+
+ RootStage *rootStage = g_engine->getRootStage();
+ StageActor *sourceStage = static_cast<StageActor *>(g_engine->getActorByIdAndType(sourceStageId, kActorTypeStage));
+ if (sourceStage == nullptr) {
+ warning("[%s] %s: Stream movie proxy with script ID %s has null parent stage",
+ debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
+ return returnValue;
+ }
+
+ sourceStage->removeChildSpatialEntity(proxy);
+ rootStage->addChildSpatialEntity(proxy);
+ return returnValue;
+ }
+
default:
return SpatialEntity::callMethod(methodId, args);
}
@@ -314,10 +451,19 @@ void StreamMovieActor::updateFrameState() {
}
void StreamMovieActor::draw(DisplayContext &displayContext) {
+ const uint DEFAULT_LAYER_ID = 0;
+ drawLayer(displayContext, DEFAULT_LAYER_ID);
+}
+
+void StreamMovieActor::drawLayer(DisplayContext &displayContext, uint layerId) {
for (MovieFrame *frame : _framesOnScreen) {
+ if (frame->layerId != layerId) {
+ continue;
+ }
+
Common::Rect bbox = getFrameBoundingBox(frame);
- debugC(8, kDebugGraphics, "%s: %s: frame %d (%d, %d, %d, %d)",
- __func__, debugName(), frame->index, PRINT_RECT(bbox));
+ debugC(8, kDebugGraphics, "[%s] %s: layer %d, frame %d (%d, %d, %d, %d)",
+ debugName(), __func__, layerId, frame->index, PRINT_RECT(bbox));
switch (frame->blitType) {
case kUncompressedMovieBlit:
@@ -345,6 +491,15 @@ void StreamMovieActor::draw(DisplayContext &displayContext) {
}
}
+void StreamMovieActor::invalidateLocalBounds() {
+ for (StreamMovieProxy *proxy : _proxies) {
+ // Our bounds might have changed, so pass that on to the proxies.
+ proxy->setBounds(getBbox());
+ }
+ SpatialEntity::invalidateLocalBounds();
+}
+
+
Common::Rect StreamMovieActor::getFrameBoundingBox(MovieFrame *frame) {
// Use _boundingBox directly (which may be temporarily offset by camera rendering)
// The camera offset is already applied to _boundingBox by pushBoundingBoxOffset()
@@ -369,7 +524,7 @@ StreamMovieActorFrames::~StreamMovieActorFrames() {
void StreamMovieActorFrames::readChunk(Chunk &chunk) {
uint sectionType = chunk.readTypedUint16();
- switch ((MovieSectionType)sectionType) {
+ switch (static_cast<MovieSectionType>(sectionType)) {
case kMovieImageDataSection:
readImageData(chunk);
break;
@@ -396,6 +551,11 @@ void StreamMovieActorFrames::readChunk(Chunk &chunk) {
}
}
+void StreamMovieActor::loadIsComplete() {
+ SpatialEntity::loadIsComplete();
+ updateFrameState();
+}
+
StreamMovieActorSound::~StreamMovieActorSound() {
unregisterWithStreamManager();
}
@@ -461,6 +621,9 @@ void StreamMovieActorFrames::readFrameData(Chunk &chunk) {
uint frameDataToRead = chunk.readTypedUint16();
for (uint i = 0; i < frameDataToRead; i++) {
MovieFrame *frame = new MovieFrame(chunk);
+ if (!_parent->isLayerInSeparateZPlane(frame->layerId)) {
+ frame->layerId = 0;
+ }
// We cannot use a hashmap here because multiple frames can have the
// same index, and frames are not necessarily in index order. So we'll
@@ -485,6 +648,31 @@ void StreamMovieActorFrames::readFrameData(Chunk &chunk) {
}
}
+StreamMovieProxy *StreamMovieActor::proxyOfId(uint layerId) {
+ // TODO: Why can this not be a hashmap?
+ for (StreamMovieProxy *proxy : _proxies) {
+ if (proxy->_layerId == layerId) {
+ return proxy;
+ }
+ }
+ return nullptr;
+}
+
+StreamMovieProxy *StreamMovieActor::proxyOfScriptId(uint scriptId) {
+ // TODO: Why can this not be a hashmap?
+ for (StreamMovieProxy *proxy : _proxies) {
+ if (proxy->_scriptId == scriptId) {
+ return proxy;
+ }
+ }
+ return nullptr;
+}
+
+bool StreamMovieActor::isLayerInSeparateZPlane(uint layerId) {
+ bool proxyExistsForLayer = proxyOfId(layerId) != nullptr;
+ return proxyExistsForLayer;
+}
+
int StreamMovieActor::compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b) {
if (b->zIndex > a->zIndex) {
return 1;
diff --git a/engines/mediastation/actors/movie.h b/engines/mediastation/actors/movie.h
index e2ce06e3f71..b6c900c7612 100644
--- a/engines/mediastation/actors/movie.h
+++ b/engines/mediastation/actors/movie.h
@@ -86,6 +86,27 @@ struct MovieFrame {
class StreamMovieActor;
+// Represents an individually controllable layer of a stream movie that is its own spatial entity
+// and can be in a different stage than its parent stream movie, but which references the parent's
+// frames at all times. Only frames with a layer ID matching a proxy's layer ID will be drawn when
+// that proxy is drawn. For example, this is used in the last section of the Dalmatians hide-and-seek
+// minigame to show a "light" layer that only appears when the player shines a flashlight on an area,
+// while a "dark" layer seamlessly displays otherwise.
+class StreamMovieProxy : public SpatialEntity {
+friend class StreamMovieActor;
+
+public:
+ StreamMovieProxy(Chunk &chunk, StreamMovieActor *parent);
+ virtual void draw(DisplayContext &displayContext) override;
+ virtual bool isVisible() const override;
+
+ uint _layerId = 0;
+ uint _scriptId = 0;
+
+private:
+ StreamMovieActor *_parent = nullptr;
+};
+
// This is called `RT_stmvFrames` in the original.
class StreamMovieActorFrames : public ChannelClient {
public:
@@ -122,14 +143,15 @@ public:
virtual ~StreamMovieActor() override;
virtual void readChunk(Chunk &chunk) override;
-
+ virtual void loadIsComplete() override;
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
virtual void draw(DisplayContext &displayContext) override;
-
- virtual bool isVisible() const override { return _isVisible; }
+ void drawLayer(DisplayContext &displayContext, uint layerId);
+ virtual void invalidateLocalBounds() override;
+ bool isLayerInSeparateZPlane(uint layerId);
private:
ImtStreamFeed *_streamFeed = nullptr;
@@ -145,6 +167,7 @@ private:
StreamMovieActorSound *_streamSound = nullptr;
Common::Array<MovieFrame *> _framesNotYetShown;
+ Common::Array<StreamMovieProxy *> _proxies;
Common::SortedArray<MovieFrame *, const MovieFrame *> _framesOnScreen;
// Script method implementations.
@@ -160,6 +183,8 @@ private:
void parseMovieChunkMarker(Chunk &chunk);
Common::Rect getFrameBoundingBox(MovieFrame *frame);
+ StreamMovieProxy *proxyOfId(uint layerId);
+ StreamMovieProxy *proxyOfScriptId(uint scriptId);
static int compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b);
};
diff --git a/engines/mediastation/datafile.cpp b/engines/mediastation/datafile.cpp
index 6a365d17da4..016d1feeb7c 100644
--- a/engines/mediastation/datafile.cpp
+++ b/engines/mediastation/datafile.cpp
@@ -145,7 +145,7 @@ Chunk::Chunk(Common::SeekableReadStream *stream) : _parentStream(stream) {
_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);
+ debugC(5, kDebugLoading, "%s: Got chunk with ID \"%s\" and size 0x%x", __func__, tag2str(_id), _length);
}
uint32 Chunk::bytesRemaining() {
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 833dc63158c..2e41e0dc9f4 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -237,8 +237,22 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "GetMouseXOffset";
case kGetMouseYOffsetMethod:
return "GetMouseYOffset";
+ case kStreamMovieSetProxyZIndex:
+ return "SetProxyZIndex";
+ case kStreamMovieGetProxyZIndex:
+ return "GetProxyZIndex";
case kIsVisibleMethod:
return "IsVisible";
+ case kStartCachingMethod:
+ return "StartCaching";
+ case kIsCachingMethod:
+ return "IsCaching";
+ case kPauseMethod:
+ return "PauseWhileStarting";
+ case kResumeMethod:
+ return "ResumeStart";
+ case kIsPausedMethod:
+ return "SetMultipleSounds/IsPaused";
case kSetMousePositionMethod:
return "SetMousePosition";
case kGetXScaleMethod1:
@@ -298,6 +312,10 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "PanTo";
case kClearToPaletteMethod:
return "ClearToPalette";
+ case kStreamMovieMoveProxyToStageMethod:
+ return "MoveProxyToStage";
+ case kStreamMovieMoveProxyToRootStageMethod:
+ return "MoveProxyToRootStage";
case kDocumentLoadContextMethod:
return "LoadContext";
case kDocumentReleaseContextMethod:
@@ -308,8 +326,6 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "Quit";
case kDocumentContextLoadInProgressMethod:
return "ContextLoadInProgress";
- case kDocumentSetMultipleSoundsMethod:
- return "SetMultipleSounds";
case kDocumentContextIsLoadedMethod:
return "IsLoaded";
case kSetDurationMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index ff8c366645a..622e4385488 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -148,6 +148,17 @@ enum BuiltInMethod {
kSetXScaleMethod = 0x17F,
kGetYScaleMethod = 0x180,
kSetYScaleMethod = 0x181,
+ kStartCachingMethod = 0x113,
+ kIsCachingMethod = 0x114,
+ kPauseMethod = 0xD0,
+ kResumeMethod = 0xD1,
+ kIsPausedMethod = 0x175,
+
+ // STREAM MOVIE METHODS.
+ kStreamMovieSetProxyZIndex = 0x10B,
+ kStreamMovieGetProxyZIndex = 0x10C,
+ kStreamMovieMoveProxyToStageMethod = 0x17C,
+ kStreamMovieMoveProxyToRootStageMethod = 0x17D,
// HOTSPOT METHODS.
// NOTE: IDs 0xD2 and 0xD3 seem to be double-assigned
Commit: 98528cadc1e3917ce681571e188fd9c58c33e70a
https://github.com/scummvm/scummvm/commit/98528cadc1e3917ce681571e188fd9c58c33e70a
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Add support for camera images
This is needed to get the "flashlight" screen of the Dalmatians puppy-finding
minigame working, among other things.
Changed paths:
engines/mediastation/actors/camera.cpp
engines/mediastation/actors/camera.h
engines/mediastation/bitmap.cpp
engines/mediastation/bitmap.h
engines/mediastation/graphics.cpp
engines/mediastation/graphics.h
diff --git a/engines/mediastation/actors/camera.cpp b/engines/mediastation/actors/camera.cpp
index 4afcbbd6f7f..92b6fb5175c 100644
--- a/engines/mediastation/actors/camera.cpp
+++ b/engines/mediastation/actors/camera.cpp
@@ -34,6 +34,9 @@ CameraActor::~CameraActor() {
_parentStage->removeCamera(this);
_parentStage->removeChildSpatialEntity(this);
}
+ delete _childrenWithOverlaySurface;
+ _childrenWithOverlaySurface = nullptr;
+ _childrenWithOverlayContext._destImage = nullptr;
}
void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
@@ -41,7 +44,7 @@ void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType)
case kActorHeaderChannelIdent:
_channelIdent = chunk.readTypedChannelIdent();
registerWithStreamManager();
- _image = Common::SharedPtr<ImageAsset>(new ImageAsset);
+ _overlayImage = Common::SharedPtr<ImageAsset>(new ImageAsset);
break;
case kActorHeaderStartup:
@@ -69,7 +72,7 @@ void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType)
case kActorHeaderCameraImageActor: {
uint actorReference = chunk.readTypedUint16();
CameraActor *referencedCamera = static_cast<CameraActor *>(g_engine->getActorByIdAndType(actorReference, kActorTypeCamera));
- _image = referencedCamera->_image;
+ _overlayImage = referencedCamera->_overlayImage;
break;
}
@@ -80,7 +83,7 @@ void CameraActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType)
void CameraActor::readChunk(Chunk &chunk) {
ImageInfo bitmapHeader(chunk);
- _image->bitmap = new PixMapImage(chunk, bitmapHeader);
+ _overlayImage->bitmap = new PixMapImage(chunk, bitmapHeader);
}
ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
@@ -170,15 +173,17 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
case kAdjustCameraViewportSpatialCenterMethod: {
ARGCOUNTCHECK(2);
- int16 xDiff = static_cast<int16>(args[0].asFloat());
- int16 yDiff = static_cast<int16>(args[1].asFloat());
+ int16 x = static_cast<int16>(args[0].asFloat());
+ int16 y = static_cast<int16>(args[1].asFloat());
// Apply centering adjustment, which is indeed based on the entire camera actor's
// bounds, not just the current viewport bounds.
- int16 centeredXDiff = xDiff - (getBbox().width() / 2);
- int16 centeredYDiff = yDiff - (getBbox().height() / 2);
- Common::Point viewportDelta(centeredXDiff, centeredYDiff);
- _nextViewportOrigin = getViewportOrigin() + viewportDelta;
+ int16 centeredX = x - (getBbox().width() / 2);
+ int16 centeredY = y - (getBbox().height() / 2);
+ _nextViewportOrigin = Common::Point(centeredX, centeredY);
+ debugC(6, kDebugCamera, "%s: currentViewportOrigin: (%d, %d); nextViewportOrigin: (%d, %d)",
+ __func__, _currentViewportOrigin.x, _currentViewportOrigin.y, _nextViewportOrigin.x, _nextViewportOrigin.y);
+
adjustCameraViewport(_nextViewportOrigin);
if (!_addedToStage) {
_currentViewportOrigin = _nextViewportOrigin;
@@ -233,7 +238,6 @@ ScriptValue CameraActor::callMethod(BuiltInMethod methodId, Common::Array<Script
default:
returnValue = SpatialEntity::callMethod(methodId, args);
- break;
}
return returnValue;
}
@@ -250,8 +254,21 @@ void CameraActor::loadIsComplete() {
addToStage();
}
- if (_image != nullptr) {
- warning("%s: STUB: Camera image asset not handled yet", __func__);
+ if (_overlayImage != nullptr) {
+ // Create the intermediate surface where we'll draw the actors and the overlay.
+ ImageInfo imageInfo;
+ imageInfo._dimensions = Common::Point(getBbox().width(), getBbox().height());
+ imageInfo._stride = getBbox().width();
+ _childrenWithOverlaySurface = new PixMapImage(imageInfo);
+ _childrenWithOverlayContext._destImage = &_childrenWithOverlaySurface->_image;
+ _childrenWithOverlayContext.verifyClipSize();
+
+ // Mark this whole region dirty.
+ Region region;
+ Common::Rect cameraRect(0, 0, getBbox().width(), getBbox().height());
+ region.addRect(cameraRect);
+ _childrenWithOverlayContext.addClip();
+ _childrenWithOverlayContext.setClipTo(region);
}
}
@@ -287,42 +304,62 @@ Common::Rect CameraActor::getViewportBounds() {
return viewportBounds;
}
-void CameraActor::drawUsingCamera(DisplayContext &displayContext, const Common::Array<SpatialEntity *> &entitiesToDraw) {
- Clip *currentClip = displayContext.currentClip();
+void CameraActor::drawUsingCamera(DisplayContext &destContext, const Common::Array<SpatialEntity *> &entitiesToDraw) {
+ // Establish the initial clipping region.
+ Clip *currentClip = destContext.currentClip();
if (currentClip != nullptr) {
- Clip *previousClip = displayContext.previousClip();
+ Clip *previousClip = destContext.previousClip();
if (previousClip == nullptr) {
+ // Initialize the clip.
currentClip->addToRegion(currentClip->_bounds);
} else {
+ // Copy the previous clip to the current clip.
*currentClip = *previousClip;
}
}
- Common::Rect cameraBounds = getBbox();
- displayContext.intersectClipWith(cameraBounds);
- displayContext.pushOrigin();
-
+ destContext.intersectClipWith(getBbox());
+ destContext.pushOrigin();
Common::Point viewportOrigin = getViewportOrigin();
- Common::Point viewportOffset(
- -viewportOrigin.x + cameraBounds.left,
- -viewportOrigin.y + cameraBounds.top
- );
- displayContext._origin.x += viewportOffset.x;
- displayContext._origin.y += viewportOffset.y;
+ destContext._origin += (getBbox().origin() - viewportOrigin);
- if (_image != nullptr) {
- // TODO: Handle image asset stuff.
- warning("%s: Camera image asset not handled yet", __func__);
+ if (_overlayImage != nullptr) {
+ // Make sure we are ready to draw the overlay image.
+ _childrenWithOverlayContext.pushOrigin();
+ _childrenWithOverlayContext._origin -= _offset;
+ _childrenWithOverlayContext._origin -= viewportOrigin;
}
for (SpatialEntity *entityToDraw : entitiesToDraw) {
+ debugCN(6, kDebugGraphics, "[%s] %s: %s (viewport: %d, %d) (bounds: %d, %d, %d, %d) ", debugName(), __func__, entityToDraw->debugName(),
+ _currentViewportOrigin.x, _currentViewportOrigin.y, PRINT_RECT(entityToDraw->getBbox()));
+
if (entityToDraw->isVisible()) {
- drawObject(displayContext, displayContext, entityToDraw);
+ if (_overlayImage == nullptr) {
+ // Draw this image directly to the provided display context.
+ debugC(6, kDebugGraphics, "(no overlay)");
+ drawObject(destContext, destContext, entityToDraw);
+ } else {
+ // Draw this image to our internal display context, so we can apply the
+ // overlay to the drawn items afterward.
+ debugC(6, kDebugGraphics, "(overlay)");
+ drawObject(destContext, _childrenWithOverlayContext, entityToDraw);
+ }
}
}
- displayContext.popOrigin();
- displayContext.emptyCurrentClip();
+ if (_overlayImage != nullptr) {
+ // Now actually apply the overlay.
+ destContext._origin += _offset;
+ g_engine->getDisplayManager()->imageDeltaBlit(
+ getViewportOrigin(), Common::Point(0, 0), _overlayImage->bitmap, _childrenWithOverlaySurface, 1.0, &destContext
+ );
+
+ _childrenWithOverlayContext.popOrigin();
+ }
+
+ destContext.popOrigin();
+ destContext.emptyCurrentClip();
}
void CameraActor::drawObject(DisplayContext &sourceContext, DisplayContext &destContext, SpatialEntity *objectToDraw) {
diff --git a/engines/mediastation/actors/camera.h b/engines/mediastation/actors/camera.h
index c093c6f8f56..2606b3008f4 100644
--- a/engines/mediastation/actors/camera.h
+++ b/engines/mediastation/actors/camera.h
@@ -44,7 +44,7 @@ struct ImageAsset;
class CameraActor : public SpatialEntity, public ChannelClient {
public:
CameraActor() : SpatialEntity(kActorTypeCamera) {};
- ~CameraActor();
+ virtual ~CameraActor() override;
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
virtual void readChunk(Chunk &chunk) override;
@@ -73,8 +73,12 @@ private:
Common::Point _panStart;
Common::Point _panDest;
Common::Point _panDelta;
- Common::SharedPtr<ImageAsset> _image;
- DisplayContext _displayContext;
+
+ // A camera can have an image that overlays its contents. To do this, we need a
+ // surface on which to put the actors shown through the camera before we draw the overlay.
+ Common::SharedPtr<ImageAsset> _overlayImage;
+ PixMapImage *_childrenWithOverlaySurface = nullptr;
+ DisplayContext _childrenWithOverlayContext;
void addToStage();
void removeFromStage(bool stopPan);
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index e3698780862..4b86d6f74eb 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -57,6 +57,10 @@ PixMapImage::PixMapImage(Chunk &chunk, const ImageInfo &imageInfo) : _imageInfo(
}
}
+PixMapImage::PixMapImage(const ImageInfo &imageInfo) : _imageInfo(imageInfo) {
+ _image.create(stride(), height(), Graphics::PixelFormat::createFormatCLUT8());
+}
+
PixMapImage::~PixMapImage() {
delete _compressedStream;
_compressedStream = nullptr;
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index f3cfc7205e1..3182db65dba 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -51,6 +51,7 @@ public:
class PixMapImage {
public:
PixMapImage(Chunk &chunk, const ImageInfo &imageInfo);
+ PixMapImage(const ImageInfo &imageInfo);
virtual ~PixMapImage();
bool isCompressed() const;
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index c9d4ecb5810..dc1fb7cf274 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -161,6 +161,10 @@ void DisplayContext::verifyClipSize() {
}
}
+void DisplayContext::deleteClips() {
+ _clips.empty();
+}
+
bool DisplayContext::clipIsEmpty() {
Clip *clip = currentClip();
if (clip != nullptr) {
@@ -757,18 +761,19 @@ void VideoDisplayManager::imageBlit(
Common::Array<Common::Rect> dirtyRegion;
if (displayContext == nullptr) {
if (targetImage == nullptr) {
- warning("%s: Neither display context nor target image was provided", __func__);
+ warning("%s: Neither display context nor target image was provided. Drawing cannot continue", __func__);
+ return;
}
Common::Rect targetImageBounds(0, 0, targetImage->w, targetImage->h);
dirtyRegion.push_back(targetImageBounds);
} else {
Clip *currentClip = displayContext->currentClip();
dirtyRegion = currentClip->_region._rects;
- destinationPoint += _displayContext._origin;
- }
+ destinationPoint += displayContext->_origin;
- if (targetImage == nullptr) {
- targetImage = _screen;
+ if (targetImage == nullptr) {
+ targetImage = displayContext->_destImage;
+ }
}
// In the disasm, this whole function has complex blit flag logic
@@ -792,7 +797,7 @@ void VideoDisplayManager::imageBlit(
case kCccBlit | kClipEnabled:
case kCccTransparentBlit | kClipEnabled:
// CCC blitting is unimplemented for now because few, if any, titles actually use it.
- error("%s: CCC blitting not implemented yet", __func__);
+ warning("%s: CCC blitting not implemented yet", __func__);
break;
case kPartialDissolve | kClipEnabled:
@@ -945,36 +950,23 @@ void VideoDisplayManager::imageDeltaBlit(
const double dissolveFactor,
DisplayContext *displayContext) {
- if (deltaFrame->getCompressionType() != kRle8BitmapCompression) {
- error("%s: Unsupported delta frame compression type for delta blit: %d",
- __func__, static_cast<uint>(keyFrame->getCompressionType()));
- } else if (dissolveFactor != 1.0) {
- warning("%s: Delta blit does not support dissolving", __func__);
- }
-
Common::Array<Common::Rect> dirtyRegion;
- if (displayContext == nullptr) {
- error("%s: Display context must be provided", __func__);
- } else {
+ if (displayContext != nullptr) {
Clip *currentClip = displayContext->currentClip();
dirtyRegion = currentClip->_region._rects;
- deltaFramePos += _displayContext._origin;
+ deltaFramePos += displayContext->_origin;
+ } else {
+ warning("%s: Display context must be provided", __func__);
+ return;
}
- switch (keyFrame->getCompressionType()) {
- case kUncompressedBitmap:
- case kUncompressedTransparentBitmap:
- deltaRleBlitRectsClip(_screen, deltaFramePos, deltaFrame, keyFrame, dirtyRegion);
- break;
-
- case kRle8BitmapCompression:
- fullDeltaRleBlitRectsClip(_screen, deltaFramePos, keyFrameOffset, deltaFrame, keyFrame, dirtyRegion);
- break;
-
- default:
- error("%s: Unsupported keyframe image type for delta blit: %d",
- __func__, static_cast<uint>(deltaFrame->getCompressionType()));
+ if (dissolveFactor != 1.0) {
+ warning("%s: Delta blit does not support dissolving", __func__);
}
+
+ // This is deliberately simplified logic for now. If we are trying to use an incorrect blitting
+ // mode, we will get an error in this call, rather than checking the blitting mode here.
+ fullDeltaRleBlitRectsClip(displayContext->_destImage, deltaFramePos, keyFrameOffset, deltaFrame, keyFrame, dirtyRegion);
}
void VideoDisplayManager::fullDeltaRleBlitRectsClip(
@@ -1045,7 +1037,7 @@ Graphics::ManagedSurface VideoDisplayManager::decompressRle8Bitmap(
Common::SeekableReadStream *chunk = source->_compressedStream;
if (chunk == nullptr) {
- warning("%s: Got empty image", __func__);
+ warning("%s: No image to decompress", __func__);
return dest;
}
@@ -1077,40 +1069,43 @@ Graphics::ManagedSurface VideoDisplayManager::decompressRle8Bitmap(
} else if (operation == 0x02) {
// Copy from the keyframe region.
- assert((keyFrame != nullptr) && (keyFrameOffset != nullptr));
byte xToCopy = chunk->readByte();
byte yToCopy = chunk->readByte();
- // If we requested to copy multiple lines, do that first.
- for (int lineOffset = 0; lineOffset < yToCopy; lineOffset++) {
- Common::Point keyFramePos = sourcePos - *keyFrameOffset + Common::Point(0, lineOffset);
- Common::Point destPos = sourcePos + Common::Point(0, lineOffset);
+ if ((keyFrame == nullptr) || (keyFrameOffset == nullptr)) {
+ warning("%s: Keyframe copy (%d, %d) requested but keyframe or offset is null", __func__, xToCopy, yToCopy);
+ } else {
+ // If we requested to copy multiple lines, do that first.
+ for (int lineOffset = 0; lineOffset < yToCopy; lineOffset++) {
+ Common::Point keyFramePos = sourcePos - *keyFrameOffset + Common::Point(0, lineOffset);
+ Common::Point destPos = sourcePos + Common::Point(0, lineOffset);
+
+ bool sourceXInBounds = (keyFramePos.x >= 0) && (keyFramePos.x + xToCopy <= keyFrame->w);
+ bool sourceYInBounds = (keyFramePos.y >= 0) && (keyFramePos.y < keyFrame->h);
+ bool destInBounds = (destPos.y * dest.w) + (destPos.x + xToCopy) <= destSizeInBytes;
+ if (sourceXInBounds && sourceYInBounds && destInBounds) {
+ const byte *srcPtr = static_cast<const byte *>(keyFrame->getBasePtr(keyFramePos.x, keyFramePos.y));
+ byte *destPtr = static_cast<byte *>(dest.getBasePtr(destPos.x, destPos.y));
+ memcpy(destPtr, srcPtr, xToCopy);
+ } else {
+ warning("%s: Keyframe copy (multi-line) exceeds bounds", __func__);
+ }
+ }
+ // Then copy the pixels in the same line.
+ Common::Point keyFramePos = sourcePos - *keyFrameOffset;
bool sourceXInBounds = (keyFramePos.x >= 0) && (keyFramePos.x + xToCopy <= keyFrame->w);
bool sourceYInBounds = (keyFramePos.y >= 0) && (keyFramePos.y < keyFrame->h);
- bool destInBounds = (destPos.y * dest.w) + (destPos.x + xToCopy) <= destSizeInBytes;
+ bool destInBounds = (sourcePos.y * dest.w) + (sourcePos.x + xToCopy) <= destSizeInBytes;
if (sourceXInBounds && sourceYInBounds && destInBounds) {
const byte *srcPtr = static_cast<const byte *>(keyFrame->getBasePtr(keyFramePos.x, keyFramePos.y));
- byte *destPtr = static_cast<byte *>(dest.getBasePtr(destPos.x, destPos.y));
+ byte *destPtr = static_cast<byte *>(dest.getBasePtr(sourcePos.x, sourcePos.y));
memcpy(destPtr, srcPtr, xToCopy);
} else {
- warning("%s: Keyframe copy (multi-line) exceeds bounds", __func__);
+ warning("%s: Keyframe copy (same line) exceeds bounds", __func__);
}
}
- // Then copy the pixels in the same line.
- Common::Point keyFramePos = sourcePos - *keyFrameOffset;
- bool sourceXInBounds = (keyFramePos.x >= 0) && (keyFramePos.x + xToCopy <= keyFrame->w);
- bool sourceYInBounds = (keyFramePos.y >= 0) && (keyFramePos.y < keyFrame->h);
- bool destInBounds = (sourcePos.y * dest.w) + (sourcePos.x + xToCopy) <= destSizeInBytes;
- if (sourceXInBounds && sourceYInBounds && destInBounds) {
- const byte *srcPtr = static_cast<const byte *>(keyFrame->getBasePtr(keyFramePos.x, keyFramePos.y));
- byte *destPtr = static_cast<byte *>(dest.getBasePtr(sourcePos.x, sourcePos.y));
- memcpy(destPtr, srcPtr, xToCopy);
- } else {
- warning("%s: Keyframe copy (same line) exceeds bounds", __func__);
- }
-
sourcePos += Common::Point(xToCopy, yToCopy);
} else if (operation == 0x03) {
diff --git a/engines/mediastation/graphics.h b/engines/mediastation/graphics.h
index 49b95d85d8b..5a8c08fa1b3 100644
--- a/engines/mediastation/graphics.h
+++ b/engines/mediastation/graphics.h
@@ -104,6 +104,7 @@ public:
bool rectIsInClip(const Common::Rect &rect);
void setClipTo(Region region);
void emptyCurrentClip();
+ void deleteClips();
void addClip();
Clip *currentClip();
Commit: b9e38dd7e7af24a4f8561a97eebae7a6cd0188a6
https://github.com/scummvm/scummvm/commit/b9e38dd7e7af24a4f8561a97eebae7a6cd0188a6
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Add Cursor actor
Changed paths:
A engines/mediastation/actors/cursor.cpp
A engines/mediastation/actors/cursor.h
engines/mediastation/context.cpp
engines/mediastation/mediascript/scriptconstants.h
engines/mediastation/module.mk
diff --git a/engines/mediastation/actors/cursor.cpp b/engines/mediastation/actors/cursor.cpp
new file mode 100644
index 00000000000..59521d786ba
--- /dev/null
+++ b/engines/mediastation/actors/cursor.cpp
@@ -0,0 +1,52 @@
+/* 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/actors/cursor.h"
+#include "mediastation/mediastation.h"
+
+namespace MediaStation {
+
+void CursorActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
+ switch (paramType) {
+ case kActorHeaderCursorResourceId:
+ _cursorId = chunk.readUint32LE();
+ break;
+
+ default:
+ Actor::readParameter(chunk, paramType);
+ }
+}
+
+ScriptValue CursorActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
+ ScriptValue returnValue;
+ switch (methodId) {
+ case kCursorSetMethod:
+ g_engine->getCursorManager()->setAsPermanent(_cursorId);
+ break;
+
+ default:
+ returnValue = Actor::callMethod(methodId, args);
+ }
+
+ return returnValue;
+}
+
+} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/cursor.h b/engines/mediastation/actors/cursor.h
new file mode 100644
index 00000000000..c5a5cf752f6
--- /dev/null
+++ b/engines/mediastation/actors/cursor.h
@@ -0,0 +1,46 @@
+/* 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_ACTORS_CURSOR_H
+#define MEDIASTATION_ACTORS_CURSOR_H
+
+#include "mediastation/actor.h"
+#include "mediastation/mediascript/scriptvalue.h"
+#include "mediastation/mediascript/scriptconstants.h"
+
+namespace MediaStation {
+
+// The cursor actor's only purpose seems to be activating its pre-set cursor
+// when the appropriate method is called.
+class CursorActor : public Actor {
+public:
+ CursorActor() : Actor(kActorTypeCursor) {};
+
+ virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
+ virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+
+private:
+ uint _cursorId = 0;
+};
+
+} // End of namespace MediaStation
+
+#endif
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index b93ee4ba7cf..7cd09806d01 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -28,6 +28,7 @@
#include "mediastation/mediascript/function.h"
#include "mediastation/actors/camera.h"
#include "mediastation/actors/canvas.h"
+#include "mediastation/actors/cursor.h"
#include "mediastation/actors/palette.h"
#include "mediastation/actors/image.h"
#include "mediastation/actors/path.h"
@@ -155,6 +156,10 @@ void MediaStationEngine::readCreateActorData(Chunk &chunk) {
actor = new TextActor();
break;
+ case kActorTypeCursor:
+ actor = new CursorActor();
+ break;
+
default:
error("%s: No class for actor type 0x%x", __func__, static_cast<uint>(type));
}
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 622e4385488..d6bcd7b7602 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -112,12 +112,6 @@ const char *builtInFunctionToStr(BuiltInFunction function);
enum BuiltInMethod {
kInvalidMethod = 0,
- // TODO: What object types does CursorSet apply to?
- // Currently it's only in var_7be1_cursor_currentTool in
- // IBM/Crayola.
- kCursorSetMethod = 0xC8,
-
- // SPATIAL ENTITY METHODS.
kSpatialHideMethod = 0xCB,
kSpatialMoveToMethod = 0xCC,
kSpatialMoveToByOffsetMethod = 0xCD,
@@ -250,6 +244,9 @@ enum BuiltInMethod {
// between two camera methods and two printer methods.
kOpenLensMethod = 0x15A,
kCloseLensMethod = 0x15B,
+
+ // CURSOR METHODS.
+ kCursorSetMethod = 0xC8,
};
const char *builtInMethodToStr(BuiltInMethod method);
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index 8f1d9a51e75..c8c7779b3fc 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS = \
actor.o \
actors/camera.o \
actors/canvas.o \
+ actors/cursor.o \
actors/document.o \
actors/font.o \
actors/hotspot.o \
Commit: 8cd32db3f4ee9c6a0a14222c95ee595be04fb2f4
https://github.com/scummvm/scummvm/commit/8cd32db3f4ee9c6a0a14222c95ee595be04fb2f4
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Make Path actor more accurate
Changed paths:
engines/mediastation/actors/path.cpp
engines/mediastation/actors/path.h
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/actors/path.cpp b/engines/mediastation/actors/path.cpp
index 49954b1550e..b1028e78925 100644
--- a/engines/mediastation/actors/path.cpp
+++ b/engines/mediastation/actors/path.cpp
@@ -35,23 +35,22 @@ void PathActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
_endPoint = chunk.readTypedPoint();
break;
- case kActorHeaderStepRate: {
- double _stepRateFloat = chunk.readTypedDouble();
- // This should always be an integer anyway,
- // so we'll cast away any fractional part.
- _stepRate = static_cast<uint32>(_stepRateFloat);
+ case kActorHeaderPathTotalSteps:
+ _totalSteps = chunk.readTypedUint16();
break;
- }
- case kActorHeaderDuration:
- // These are stored in the file as fractional seconds,
- // but we want milliseconds.
- _duration = static_cast<uint32>(chunk.readTypedTime() * 1000);
+ case kActorHeaderStepRate:
+ _stepRate = chunk.readTypedDouble();
break;
- case kActorHeaderPathTotalSteps:
- _totalSteps = chunk.readTypedUint16();
+ case kActorHeaderDuration: {
+ // These are stored in the file as fractional seconds,
+ // but we want milliseconds.
+ const uint MILLISECONDS_IN_ONE_SECOND = 1000;
+ _duration = chunk.readTypedTime() * MILLISECONDS_IN_ONE_SECOND;
+ _useTimeForCompletion = true;
break;
+ }
default:
Actor::readParameter(chunk, paramType);
@@ -62,104 +61,204 @@ ScriptValue PathActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptVa
ScriptValue returnValue;
switch (methodId) {
- case kTimePlayMethod: {
+ case kTimePlayMethod:
+ ARGCOUNTCHECK(0);
+ startPath();
+ break;
+
+ case kTimeStopMethod:
ARGCOUNTCHECK(0);
- timePlay();
- return returnValue;
+ stopPath();
+ break;
+
+ case kPauseMethod:
+ ARGCOUNTCHECK(0);
+ pausePath();
+ break;
+
+ case kResumeMethod: {
+ ARGCOUNTRANGE(0, 1);
+ bool shouldRestart = false;
+ if (args.size() == 1) {
+ shouldRestart = args[0].asBool();
+ }
+ resumePath(shouldRestart);
+ break;
}
- case kSetDurationMethod: {
+ case kGetLeftXMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToFloat(_currentPoint.x);
+ break;
+
+ case kGetTopYMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToFloat(_currentPoint.y);
+ break;
+
+ case kPathSetStartPointMethod:
+ ARGCOUNTCHECK(2);
+ _startPoint.x = static_cast<int16>(args[0].asFloat());
+ _startPoint.y = static_cast<int16>(args[1].asFloat());
+ break;
+
+ case kPathSetEndPointMethod:
+ ARGCOUNTCHECK(2);
+ _endPoint.x = static_cast<int16>(args[0].asFloat());
+ _endPoint.y = static_cast<int16>(args[1].asFloat());
+ break;
+
+ case kPathSetTotalStepsMethod:
+ ARGCOUNTCHECK(1);
+ _totalSteps = static_cast<uint>(args[0].asFloat());
+ _useTimeForCompletion = false;
+ break;
+
+ case kPathSetStepRateMethod:
ARGCOUNTCHECK(1);
- uint durationInMilliseconds = static_cast<uint>(args[0].asTime() * 1000);
- setDuration(durationInMilliseconds);
- return returnValue;
+ _stepRate = static_cast<uint>(args[0].asFloat());
+ break;
+
+ case kPathSetDurationMethod: {
+ ARGCOUNTCHECK(1);
+ // Convert from seconds to milliseconds.
+ const uint MILLISECONDS_IN_ONE_SECOND = 1000;
+ _duration = args[0].asTime() * MILLISECONDS_IN_ONE_SECOND;
+ _useTimeForCompletion = true;
+ break;
}
- case kPercentCompleteMethod: {
+ case kPathGetPercentCompleteMethod:
ARGCOUNTCHECK(0);
- returnValue.setToFloat(percentComplete());
- return returnValue;
- }
+ if (_playState == kPathPlaying) {
+ returnValue.setToFloat(getPercentComplete());
+ } else {
+ returnValue.setToFloat(1.0);
+ }
+ break;
- case kIsPlayingMethod: {
+ case kIsPlayingMethod:
ARGCOUNTCHECK(0);
- returnValue.setToBool(_isPlaying);
- return returnValue;
- }
+ returnValue.setToBool(_playState == kPathPlaying || _playState == kPathPaused);
+ break;
+
+ case kIsPausedMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToBool(_playState == kPathPaused);
+ break;
default:
- return Actor::callMethod(methodId, args);
+ returnValue = Actor::callMethod(methodId, args);
}
+ return returnValue;
}
-void PathActor::timePlay() {
- if (_isPlaying) {
- return;
+void PathActor::process() {
+ if (_playState == kPathPlaying) {
+ uint currentTime = g_system->getMillis();
+ if (currentTime >= _nextPathStepTime) {
+ timerEvent();
+ }
}
+}
+
+void PathActor::startPath() {
+ _playState = kPathPlaying;
+ _startTime = g_system->getMillis();
- if (_duration == 0) {
- warning("%s: Got zero duration", __func__);
- } else if (_stepRate == 0) {
- error("%s: Got zero step rate", __func__);
+ if (_stepRate <= 0.0) {
+ error("[%s] %s: Got zero or negative step rate", debugName(), __func__);
}
- _isPlaying = true;
- _startTime = g_system->getMillis();
- _lastProcessedTime = 0;
- _percentComplete = 0;
- _nextPathStepTime = 0;
+ _currentPoint = _startPoint;
+ _stepDurationInMilliseconds = static_cast<uint>((1.0 / _stepRate) * 1000);
+ _nextPathStepTime = _startTime + _stepDurationInMilliseconds;
_currentStep = 0;
- _totalSteps = (_duration * _stepRate) / 1000;
- _stepDurationInMilliseconds = 1000 / _stepRate;
- // TODO: Run the path start event. Haven't seen one the wild yet, don't know its ID.
- debugC(5, kDebugScript, "Path::timePlay(): No PathStart event handler");
+ // There is no path start event handler.
}
-void PathActor::process() {
- if (!_isPlaying) {
- return;
+void PathActor::stopPath() {
+ if (_playState == kPathPlaying || _playState == kPathPaused) {
+ _playState = kPathStopped;
+ runEventHandlerIfExists(kPathStoppedEvent);
}
+}
- uint currentTime = g_system->getMillis();
- uint pathTime = currentTime - _startTime;
+void PathActor::pausePath() {
+ if (_playState == kPathPlaying) {
+ _playState = kPathPaused;
+ _pauseTime = g_system->getMillis();
+ }
+}
+
+void PathActor::resumePath(bool shouldRestart) {
+ if (_playState == kPathPaused) {
+ // Calculate how long we were paused, to make sure we resume at the right point.
+ uint currentTime = g_system->getMillis();
+ uint pauseDuration = currentTime - _pauseTime;
+ _startTime += pauseDuration;
+ _playState = kPathPlaying;
+ scheduleNextTimerEvent();
+ } else if (_playState != kPathPlaying && shouldRestart) {
+ startPath();
+ }
+}
- bool doNextStep = pathTime >= _nextPathStepTime;
- if (!doNextStep) {
- return;
+double PathActor::getPercentComplete() {
+ double percentComplete = 1.0;
+ if (!_useTimeForCompletion) {
+ if (_totalSteps > 0) {
+ percentComplete = static_cast<double>(_currentStep) / _totalSteps;
+ }
+ } else {
+ uint currentTime = g_system->getMillis();
+ if (currentTime > _startTime && _duration > 0) {
+ double timeElapsed = currentTime - _startTime;
+ percentComplete = timeElapsed / _duration;
+ if (percentComplete > 1.0) {
+ percentComplete = 1.0;
+ }
+ }
}
- _percentComplete = static_cast<double>(_currentStep + 1) / _totalSteps;
- debugC(2, kDebugScript, "Path::timePlay(): Step %d of %d", _currentStep, _totalSteps);
+ return percentComplete;
+}
- if (_currentStep < _totalSteps) {
- // TODO: Actually step the path. It seems they mostly just use this for
- // palette animation in the On Step event handler, so nothing is actually drawn on the screen now.
+bool PathActor::step() {
+ double percentComplete = getPercentComplete();
+ if (percentComplete < 1.0) {
+ double nextX = _startPoint.x + (_endPoint.x - _startPoint.x) * percentComplete;
+ double nextY = _startPoint.y + (_endPoint.y - _startPoint.y) * percentComplete;
+ _currentPoint = Common::Point(static_cast<int16>(nextX), static_cast<int16>(nextY));
+ debugC(4, kDebugEvents, "[%s] %s: %f%% complete (startPoint: (%d, %d)) (endPoint: (%d, %d)) (currentPoint: (%d, %d))",
+ debugName(), __func__, percentComplete,
+ _endPoint.x, _endPoint.y, _startPoint.x, _startPoint.y, _currentPoint.x, _currentPoint.y);
// We don't run a step event for the last step.
runEventHandlerIfExists(kPathStepEvent);
- _nextPathStepTime = ++_currentStep * _stepDurationInMilliseconds;
+ return false;
+ }
+ return true;
+}
+
+void PathActor::scheduleNextTimerEvent() {
+ _nextPathStepTime += _stepDurationInMilliseconds;
+}
+
+void PathActor::timerEvent() {
+ _currentStep += 1;
+ bool finishedPlaying = step();
+ if (!finishedPlaying) {
+ scheduleNextTimerEvent();
} else {
- _isPlaying = false;
+ _playState = kPathStopped;
_percentComplete = 0;
_nextPathStepTime = 0;
_currentStep = 0;
- _totalSteps = 0;
_stepDurationInMilliseconds = 0;
-
runEventHandlerIfExists(kPathEndEvent);
}
}
-void PathActor::setDuration(uint durationInMilliseconds) {
- // TODO: Do we need to save the original duration?
- debugC(5, kDebugScript, "Path::setDuration(): Setting duration to %d ms", durationInMilliseconds);
- _duration = durationInMilliseconds;
-}
-
-double PathActor::percentComplete() {
- debugC(5, kDebugScript, "Path::percentComplete(): Returning percent complete %f%%", _percentComplete * 100);
- return _percentComplete;
-}
-
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/path.h b/engines/mediastation/actors/path.h
index 1e6c42b6efb..0ef45a14a51 100644
--- a/engines/mediastation/actors/path.h
+++ b/engines/mediastation/actors/path.h
@@ -28,6 +28,12 @@
namespace MediaStation {
+enum PathPlayState {
+ kPathStopped = 1,
+ kPathPlaying = 2,
+ kPathPaused = 3,
+};
+
class PathActor : public Actor {
public:
PathActor() : Actor(kActorTypePath) {};
@@ -38,22 +44,31 @@ public:
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
private:
+ PathPlayState _playState = kPathStopped;
double _percentComplete = 0.0;
- uint _totalSteps = 0;
+ bool _useTimeForCompletion = false;
+ double _duration = 0.0;
+ double _stepRate = 0.0;
+ uint _stepDurationInMilliseconds = 0;
uint _currentStep = 0;
+ uint _startTime = 0;
+ uint _pauseTime = 0;
+ uint _totalSteps = 0;
uint _nextPathStepTime = 0;
- uint _stepDurationInMilliseconds = 0;
- bool _isPlaying = false;
Common::Point _startPoint;
Common::Point _endPoint;
- uint32 _stepRate = 0;
- uint32 _duration = 0;
+ Common::Point _currentPoint;
+
+ void startPath();
+ void stopPath();
+ void pausePath();
+ void resumePath(bool shouldRestart);
- // Method implementations.
- void timePlay();
- void setDuration(uint durationInMilliseconds);
- double percentComplete();
+ double getPercentComplete();
+ bool step();
+ void timerEvent();
+ void scheduleNextTimerEvent();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 2e41e0dc9f4..793834fc1e6 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -328,10 +328,18 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "ContextLoadInProgress";
case kDocumentContextIsLoadedMethod:
return "IsLoaded";
- case kSetDurationMethod:
+ case kPathSetDurationMethod:
return "SetDuration";
- case kPercentCompleteMethod:
+ case kPathGetPercentCompleteMethod:
return "PercentComplete";
+ case kPathSetStartPointMethod:
+ return "SetStartPoint";
+ case kPathSetEndPointMethod:
+ return "SetEndPoint";
+ case kPathSetTotalStepsMethod:
+ return "SetTotalSteps";
+ case kPathSetStepRateMethod:
+ return "SetStepRate";
case kTextMethod:
return "Text";
case kSetTextMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index d6bcd7b7602..b4d5a4aada4 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -212,8 +212,12 @@ enum BuiltInMethod {
kDocumentContextIsLoadedMethod = 0x178,
// PATH METHODS.
- kSetDurationMethod = 0x106,
- kPercentCompleteMethod = 0x107,
+ kPathSetDurationMethod = 0x106,
+ kPathGetPercentCompleteMethod = 0x107,
+ kPathSetStartPointMethod = 0xf2,
+ kPathSetEndPointMethod = 0xf3,
+ kPathSetTotalStepsMethod = 0xf4,
+ kPathSetStepRateMethod = 0xf5,
// TEXT METHODS.
kTextMethod = 0x122,
Commit: 987b07ddde448bcd46409f0a0a340eef0aed0b51
https://github.com/scummvm/scummvm/commit/987b07ddde448bcd46409f0a0a340eef0aed0b51
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Implement more builtin script functions/methods
Most of these are necessary for Pocahontas minigames.
Changed paths:
engines/mediastation/actor.cpp
engines/mediastation/actor.h
engines/mediastation/actors/hotspot.cpp
engines/mediastation/actors/sound.cpp
engines/mediastation/actors/sound.h
engines/mediastation/actors/stage.cpp
engines/mediastation/actors/stage.h
engines/mediastation/audio.cpp
engines/mediastation/audio.h
engines/mediastation/graphics.cpp
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/function.cpp
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/actor.cpp b/engines/mediastation/actor.cpp
index 0f7f661f367..3df9403c035 100644
--- a/engines/mediastation/actor.cpp
+++ b/engines/mediastation/actor.cpp
@@ -286,6 +286,16 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
returnValue.setToFloat(_zIndex);
break;
+ case kIsPointInsideMethod: {
+ ARGCOUNTCHECK(2);
+ int16 xToCheck = static_cast<int16>(args[0].asFloat());
+ int16 yToCheck = static_cast<int16>(args[1].asFloat());
+ Common::Point pointToCheck(xToCheck, yToCheck);
+ bool pointIsInside = getBbox().contains(pointToCheck);
+ returnValue.setToBool(pointIsInside);
+ break;
+ }
+
case kSetDissolveFactorMethod: {
ARGCOUNTCHECK(1);
double dissolveFactor = args[0].asFloat();
@@ -293,6 +303,22 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
break;
}
+ case kGetMouseXOffsetMethod: {
+ Common::Point mouseOffset;
+ currentMousePosition(mouseOffset);
+ mouseOffset -= _originalBoundingBox.origin();
+ returnValue.setToFloat(static_cast<double>(mouseOffset.x));
+ break;
+ }
+
+ case kGetMouseYOffsetMethod: {
+ Common::Point mouseOffset;
+ currentMousePosition(mouseOffset);
+ mouseOffset -= _originalBoundingBox.origin();
+ returnValue.setToFloat(static_cast<double>(mouseOffset.y));
+ break;
+ }
+
case kIsVisibleMethod:
ARGCOUNTCHECK(0);
returnValue.setToBool(isVisible());
@@ -392,6 +418,12 @@ void SpatialEntity::loadIsComplete() {
}
}
+void SpatialEntity::currentMousePosition(Common::Point &point) {
+ if (_parentStage != nullptr) {
+ _parentStage->currentMousePosition(point);
+ }
+}
+
void SpatialEntity::invalidateMouse() {
// TODO: Invalidate the mouse properly when we have custom events.
// For now, we simulate the mouse update event with a mouse moved event.
@@ -487,7 +519,9 @@ void SpatialEntity::invalidateLocalBounds() {
}
void SpatialEntity::invalidateLocalZIndex() {
- warning("[%s] %s: STUB", debugName(), __func__);
+ if (_parentStage != nullptr) {
+ _parentStage->invalidateZIndexOf(this);
+ }
}
void SpatialEntity::setAdjustedBounds(CylindricalWrapMode alignmentMode) {
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index 1f69b662c83..fb0f36699be 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -244,6 +244,7 @@ public:
virtual Common::Rect getBbox() const { return _boundingBox; }
int zIndex() const { return _zIndex; }
+ virtual void currentMousePosition(Common::Point &point);
virtual void invalidateMouse();
virtual bool interactsWithMouse() const { return false; }
diff --git a/engines/mediastation/actors/hotspot.cpp b/engines/mediastation/actors/hotspot.cpp
index cccaabe2643..ef859235a40 100644
--- a/engines/mediastation/actors/hotspot.cpp
+++ b/engines/mediastation/actors/hotspot.cpp
@@ -107,9 +107,14 @@ ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<Scrip
return returnValue;
}
- case kIsActiveMethod: {
- ARGCOUNTCHECK(0);
- returnValue.setToBool(_isActive);
+
+ case kIsPointInsideMethod: {
+ ARGCOUNTCHECK(2);
+ int16 xToCheck = static_cast<int16>(args[0].asFloat());
+ int16 yToCheck = static_cast<int16>(args[1].asFloat());
+ Common::Point pointToCheck(xToCheck, yToCheck);
+ bool pointIsInside = isInside(pointToCheck);
+ returnValue.setToBool(pointIsInside);
return returnValue;
}
@@ -127,6 +132,12 @@ ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<Scrip
return returnValue;
}
+ case kIsActiveMethod: {
+ ARGCOUNTCHECK(0);
+ returnValue.setToBool(_isActive);
+ return returnValue;
+ }
+
default:
return SpatialEntity::callMethod(methodId, args);
}
diff --git a/engines/mediastation/actors/sound.cpp b/engines/mediastation/actors/sound.cpp
index 6b5bded9ec8..15d1d5b699a 100644
--- a/engines/mediastation/actors/sound.cpp
+++ b/engines/mediastation/actors/sound.cpp
@@ -69,13 +69,13 @@ void SoundActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
}
void SoundActor::process() {
- if (!_isPlaying) {
+ if (_playState != kSoundPlaying) {
return;
}
processTimeEventHandlers();
if (!_sequence.isActive()) {
- _isPlaying = false;
+ _playState = kSoundStopped;
_sequence.stop();
runEventHandlerIfExists(kSoundEndEvent);
}
@@ -97,53 +97,91 @@ ScriptValue SoundActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
ARGCOUNTCHECK(0);
return returnValue;
- case kTimePlayMethod: {
+ case kTimePlayMethod:
ARGCOUNTCHECK(0);
- timePlay();
+ start();
+ return returnValue;
+
+ case kTimeStopMethod:
+ ARGCOUNTCHECK(0);
+ stop();
return returnValue;
- }
- case kTimeStopMethod: {
+ case kPauseMethod:
ARGCOUNTCHECK(0);
- timeStop();
+ pause();
+ return returnValue;
+
+ case kResumeMethod: {
+ ARGCOUNTRANGE(0, 1);
+ bool shouldRestart = false;
+ if (args.size() == 1) {
+ shouldRestart = args[0].asBool();
+ }
+ resume(shouldRestart);
return returnValue;
}
+ case kIsPlayingMethod:
+ returnValue.setToBool(_playState == kSoundPlaying || _playState == kSoundPaused);
+ return returnValue;
+
+ case kIsPausedMethod:
+ returnValue.setToBool(_playState == kSoundPaused);
+ return returnValue;
+
default:
return Actor::callMethod(methodId, args);
}
}
-void SoundActor::timePlay() {
- if (_streamFeed == nullptr && !_isLoadedFromChunk) {
- _streamFeed = g_engine->getStreamFeedManager()->openStreamFeed(_id);
- _streamFeed->readData();
- }
+void SoundActor::start() {
+ if (_loadIsComplete) {
+ if (_playState == kSoundPlaying || _playState == kSoundPaused) {
+ stop();
+ }
- if (_isPlaying) {
- return;
+ openStream();
+ _playState = kSoundPlaying;
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
+ _sequence.play();
+ runEventHandlerIfExists(kSoundBeginEvent);
+ } else {
+ warning("[%s] %s: Attempted to play sound before it was loaded", debugName(), __func__);
}
+}
- if (_sequence.isEmpty()) {
- _isPlaying = false;
- return;
+void SoundActor::stop() {
+ if (_playState == kSoundPlaying || _playState == kSoundPaused) {
+ _playState = kSoundStopped;
+ _sequence.stop();
+ runEventHandlerIfExists(kSoundStoppedEvent);
}
+}
- _isPlaying = true;
- _startTime = g_system->getMillis();
- _lastProcessedTime = 0;
- _sequence.play();
- runEventHandlerIfExists(kSoundBeginEvent);
+void SoundActor::pause() {
+ if (_playState == kSoundPlaying) {
+ _sequence.pause();
+ _playState = kSoundPaused;
+ // There don't seem to be script events to trigger in this instance.
+ }
}
-void SoundActor::timeStop() {
- if (!_isPlaying) {
- return;
+void SoundActor::resume(bool restart) {
+ if (_playState == kSoundPaused) {
+ _sequence.resume();
+ } else if (restart) {
+ start();
}
+ // There don't seem to be script events to trigger in this instance.
+}
- _isPlaying = false;
- _sequence.stop();
- runEventHandlerIfExists(kSoundStoppedEvent);
+void SoundActor::openStream() {
+ if (_streamFeed == nullptr && !_isLoadedFromChunk) {
+ _streamFeed = g_engine->getStreamFeedManager()->openStreamFeed(_id);
+ _streamFeed->readData();
+ }
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/sound.h b/engines/mediastation/actors/sound.h
index 416f2e277b3..23754a26a0c 100644
--- a/engines/mediastation/actors/sound.h
+++ b/engines/mediastation/actors/sound.h
@@ -30,6 +30,12 @@
namespace MediaStation {
+enum SoundPlayState {
+ kSoundStopped = 1,
+ kSoundPlaying = 2,
+ kSoundPaused = 3,
+};
+
class SoundActor : public Actor, public ChannelClient {
public:
SoundActor() : Actor(kActorTypeSound) {};
@@ -46,12 +52,15 @@ private:
bool _isLoadedFromChunk = false;
uint _loadType = 0;
bool _hasOwnSubfile = false;
- bool _isPlaying = false;
+ SoundPlayState _playState = kSoundStopped;
AudioSequence _sequence;
- // Script method implementations
- void timePlay();
- void timeStop();
+ void start();
+ void stop();
+ void pause();
+ void resume(bool restart);
+
+ void openStream();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/stage.cpp b/engines/mediastation/actors/stage.cpp
index ca7193cafa0..7c9e0b79072 100644
--- a/engines/mediastation/actors/stage.cpp
+++ b/engines/mediastation/actors/stage.cpp
@@ -317,14 +317,16 @@ void StageActor::setCurrentCamera(CameraActor *camera) {
ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
switch (methodId) {
- case kAddActorToStageMethod: {
+ case kAddActorToStageMethod:
+ case kAddActorToStageMethod2: {
ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
addActorToStage(actorId);
return returnValue;
}
- case kRemoveActorFromStageMethod: {
+ case kRemoveActorFromStageMethod:
+ case kRemoveActorFromStageMethod2: {
ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
removeActorFromStage(actorId);
diff --git a/engines/mediastation/actors/stage.h b/engines/mediastation/actors/stage.h
index 2cce143da20..d087745d807 100644
--- a/engines/mediastation/actors/stage.h
+++ b/engines/mediastation/actors/stage.h
@@ -91,7 +91,7 @@ public:
uint16 asciiCode,
uint16 eventMask,
MouseActorState &state) override;
- virtual void currentMousePosition(Common::Point &point);
+ virtual void currentMousePosition(Common::Point &point) override;
virtual void setMousePosition(int16 x, int16 y) override;
void invalidateZIndexOf(const SpatialEntity *entity);
diff --git a/engines/mediastation/audio.cpp b/engines/mediastation/audio.cpp
index 97d3f662815..a28819e129c 100644
--- a/engines/mediastation/audio.cpp
+++ b/engines/mediastation/audio.cpp
@@ -50,6 +50,14 @@ void AudioSequence::play() {
}
}
+void AudioSequence::pause() {
+ g_engine->_mixer->pauseHandle(_handle, true);
+}
+
+void AudioSequence::resume() {
+ g_engine->_mixer->pauseHandle(_handle, false);
+}
+
void AudioSequence::stop() {
g_engine->_mixer->stopHandle(_handle);
_handle = Audio::SoundHandle();
diff --git a/engines/mediastation/audio.h b/engines/mediastation/audio.h
index 6a1d152af78..9b064cd1e7e 100644
--- a/engines/mediastation/audio.h
+++ b/engines/mediastation/audio.h
@@ -36,6 +36,8 @@ public:
~AudioSequence();
void play();
+ void pause();
+ void resume(); // Unpause
void stop();
void readParameters(Chunk &chunk);
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index dc1fb7cf274..ce35f62c492 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -864,8 +864,8 @@ void VideoDisplayManager::dissolveBlitRectsClip(
byte dissolveIndex = DISSOLVE_PATTERN_COUNT;
if (integralDissolveFactor != 50) {
dissolveIndex = ((integralDissolveFactor + 2) / 4) - 1;
- dissolveIndex = CLIP<byte>(dissolveIndex, 0, (DISSOLVE_PATTERN_COUNT - 1));
}
+ dissolveIndex = CLIP<byte>(dissolveIndex, 0, (DISSOLVE_PATTERN_COUNT - 1));
Common::Rect destRect(Common::Rect(destPos, source->width(), source->height()));
for (const Common::Rect &dirtyRect : dirtyRegion) {
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index b2e619c7a03..dda70b3ed94 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -305,19 +305,23 @@ ScriptValue *CodeChunk::readAndReturnVariable() {
}
case kVariableScopeLocal: {
+ // The ID is actually a one-based index.
uint index = id - 1;
variable = &_locals.operator[](index);
break;
}
case kVariableScopeIndirectParameter: {
+ // The ID is actually a one-based index.
+ uint baseIndex = id - 1;
ScriptValue indexValue = evaluateExpression();
- uint index = static_cast<uint>(indexValue.asFloat() + id);
+ uint index = static_cast<uint>(indexValue.asFloat()) + baseIndex;
variable = &_args->operator[](index);
break;
}
case kVariableScopeParameter: {
+ // The ID is actually a one-based index.
uint index = id - 1;
if (_args == nullptr) {
error("%s: Requested a parameter in a code chunk that has no parameters", __func__);
diff --git a/engines/mediastation/mediascript/function.cpp b/engines/mediastation/mediascript/function.cpp
index 76fff863a4c..1f74591612d 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -142,31 +142,37 @@ ScriptValue FunctionManager::call(uint functionId, Common::Array<ScriptValue> &a
break;
case kCurrentRunTimeFunction:
+ case kLegacy_GetCurrentRunTimeFunction:
FUNCARGCHECK(0);
script_CurrentRunTime(args, returnValue);
break;
case kSetGammaCorrectionFunction:
+ case kLegacy_SetGammaCorrectionFunction:
FUNCARGRANGE(1, 3);
script_SetGammaCorrection(args, returnValue);
break;
case kGetDefaultGammaCorrectionFunction:
+ case kLegacy_GetDefaultGammaCorrectionFunction:
FUNCARGCHECK(0);
script_GetDefaultGammaCorrection(args, returnValue);
break;
case kGetCurrentGammaCorrectionFunction:
+ case kLegacy_GetCurrentGammaCorrectionFunction:
FUNCARGCHECK(0);
script_GetCurrentGammaCorrection(args, returnValue);
break;
case kSetAudioVolumeFunction:
+ case kLegacy_SetAudioVolumeFunction:
FUNCARGCHECK(1);
script_SetAudioVolume(args, returnValue);
break;
case kGetAudioVolumeFunction:
+ case kLegacy_GetAudioVolumeFunction:
FUNCARGCHECK(0);
script_GetAudioVolume(args, returnValue);
break;
@@ -321,7 +327,11 @@ void FunctionManager::script_Random(Common::Array<ScriptValue> &args, ScriptValu
}
void FunctionManager::script_TimeOfDay(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
- warning("STUB: TimeOfDay");
+ TimeDate timeDate;
+ // Calculate seconds since midnight.
+ g_system->getTimeAndDate(timeDate);
+ uint32 secondsSinceMidnight = (timeDate.tm_hour * 60 + timeDate.tm_min) * 60 + timeDate.tm_sec;
+ returnValue.setToTime(static_cast<double>(secondsSinceMidnight));
}
void FunctionManager::script_SquareRoot(Common::Array<ScriptValue> &args, ScriptValue &returnValue) {
@@ -362,10 +372,9 @@ void FunctionManager::script_GetUniqueRandom(Common::Array<ScriptValue> &args, S
SWAP(top, bottom);
}
- // Build list of unused (non-excluded) numbers in the range. For this numeric type,
- // everything is treated as an integer (even though it's stored as a double).
+ // Build list of unused (non-excluded) integers in the range.
Common::Array<double> unusedNumbers;
- for (double currentValue = bottom; currentValue < top; currentValue += 1.0) {
+ for (double currentValue = bottom; currentValue <= top; currentValue += 1.0) {
// Check if this value appears in the exclusion list (args 2 onwards).
bool isExcluded = false;
for (uint i = 2; i < args.size(); i++) {
@@ -381,7 +390,7 @@ void FunctionManager::script_GetUniqueRandom(Common::Array<ScriptValue> &args, S
}
if (unusedNumbers.size() > 0) {
- uint randomIndex = g_engine->_randomSource.getRandomNumberRng(0, unusedNumbers.size());
+ uint randomIndex = g_engine->_randomSource.getRandomNumberRng(0, unusedNumbers.size() - 1);
returnValue.setToFloat(unusedNumbers[randomIndex]);
} else {
warning("%s: No unused numbers to choose from", __func__);
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 793834fc1e6..df59b46fc70 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -174,8 +174,20 @@ const char *builtInFunctionToStr(BuiltInFunction function) {
return "Legacy SquareRoot";
case kLegacy_GetUniqueRandomFunction:
return "Legacy GetUniqueRandom";
+ case kLegacy_GetCurrentRunTimeFunction:
+ return "Legacy GetCurrentRunTime";
+ case kLegacy_SetGammaCorrectionFunction:
+ return "Legacy SetGammaCorrection";
+ case kLegacy_GetDefaultGammaCorrectionFunction:
+ return "Legacy GetDefaultGammaCorrection";
+ case kLegacy_GetCurrentGammaCorrectionFunction:
+ return "Legacy GetCurrentGammaCorrection";
case kLegacy_DebugPrintFunction:
return "DebugPrint";
+ case kLegacy_SetAudioVolumeFunction:
+ return "Legacy SetAudioVolume";
+ case kLegacy_GetAudioVolumeFunction:
+ return "Legacy GetAudioVolume";
case kLegacy_SystemLanguagePreferenceFunction:
return "Legacy SystemLanguagePreference";
default:
@@ -185,6 +197,8 @@ const char *builtInFunctionToStr(BuiltInFunction function) {
const char *builtInMethodToStr(BuiltInMethod method) {
switch (method) {
+ case kInvalidMethod:
+ return "Invalid";
case kCursorSetMethod:
return "CursorSet";
case kSpatialHideMethod:
@@ -285,8 +299,10 @@ const char *builtInMethodToStr(BuiltInMethod method) {
case kStageGetHeightMethod:
return "StageGetHeight";
case kAddToStageMethod:
+ case kAddActorToStageMethod2:
return "AddToStage\\OpenLens";
case kRemoveFromStageMethod:
+ case kRemoveActorFromStageMethod2:
return "RemoveFromStage\\CloseLens";
case kAddedToStageMethod:
return "AddedToStage";
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index b4d5a4aada4..6e356359482 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -105,7 +105,13 @@ enum BuiltInFunction {
kLegacy_PlatformFunction = 0x68,
kLegacy_SquareRootFunction = 0x69,
kLegacy_GetUniqueRandomFunction = 0x6A,
+ kLegacy_GetCurrentRunTimeFunction = 0x6B,
+ kLegacy_SetGammaCorrectionFunction = 0xAA,
+ kLegacy_GetDefaultGammaCorrectionFunction = 0xAB,
+ kLegacy_GetCurrentGammaCorrectionFunction = 0xAC,
kLegacy_DebugPrintFunction = 0xB4,
+ kLegacy_SetAudioVolumeFunction = 0xBE,
+ kLegacy_GetAudioVolumeFunction = 0xBF,
kLegacy_SystemLanguagePreferenceFunction = 0xC8,
};
const char *builtInFunctionToStr(BuiltInFunction function);
@@ -119,6 +125,8 @@ enum BuiltInMethod {
kSpatialShowMethod = 0xCA,
kTimePlayMethod = 0xCE,
kTimeStopMethod = 0xCF,
+ kTimePauseMethod = 0xD0,
+ kTimeResumeMethod = 0xD1,
kIsPlayingMethod = 0x174,
kSetDissolveFactorMethod = 0xF1,
kSpatialCenterMoveToMethod = 0xE6,
@@ -174,7 +182,9 @@ enum BuiltInMethod {
// NOTE: IDs 0xD2 and 0xD3 seem to be double-assigned
// between two hotspot methods and two stage methods.
kAddActorToStageMethod = 0xD2,
+ kAddActorToStageMethod2 = 0x170,
kRemoveActorFromStageMethod = 0xD3,
+ kRemoveActorFromStageMethod2 = 0x171,
kSetWorldSpaceExtentMethod = 0x16B,
kSetBoundsMethod = 0x11F,
kStageSetSizeMethod = 0x16B,
Commit: 62184a96ddcad35e5559781156f204156d138867
https://github.com/scummvm/scummvm/commit/62184a96ddcad35e5559781156f204156d138867
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T20:49:55-05:00
Commit Message:
MEDIASTATION: Fix numerous inconsistencies with image data reading
Changed paths:
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/movie.h
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/sprite.h
engines/mediastation/bitmap.cpp
engines/mediastation/bitmap.h
engines/mediastation/datafile.h
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index d01ca3ccf7d..2284fc82213 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -54,12 +54,6 @@ bool StreamMovieProxy::isVisible() const {
return false;
}
-MovieFrameInfo::MovieFrameInfo(Chunk &chunk) : ImageInfo(chunk) {
- _index = chunk.readTypedUint32();
- debugC(5, kDebugLoading, "%s: frame 0x%x", __func__, _index);
- _keyframeEndInMilliseconds = chunk.readTypedUint32();
-}
-
MovieFrame::MovieFrame(Chunk &chunk) {
if (g_engine->isFirstGenerationEngine()) {
blitType = static_cast<MovieBlitType>(chunk.readTypedUint16());
@@ -91,7 +85,9 @@ MovieFrame::MovieFrame(Chunk &chunk) {
}
}
-MovieFrameImage::MovieFrameImage(Chunk &chunk, const MovieFrameInfo &header) : PixMapImage(chunk, header), _frameInfo(header) {
+MovieFrameImage::MovieFrameImage(Chunk &chunk, uint index, uint keyframeEndInMilliseconds, const ImageInfo &imageInfo) :
+ PixMapImage(chunk, imageInfo), _index(index), _keyframeEndInMilliseconds(keyframeEndInMilliseconds) {
+ debugC(5, kDebugLoading, "%s: frame 0x%x", __func__, _index);
}
StreamMovieActor::~StreamMovieActor() {
@@ -612,8 +608,10 @@ void StreamMovieActor::decompressIntoAuxImage(MovieFrame *frame) {
}
void StreamMovieActorFrames::readImageData(Chunk &chunk) {
- MovieFrameInfo header(chunk);
- MovieFrameImage *frame = new MovieFrameImage(chunk, header);
+ ImageInfo imageInfo(chunk);
+ uint index = chunk.readTypedUint32();
+ uint keyframeEndInMilliseconds = chunk.readTypedUint32();
+ MovieFrameImage *frame = new MovieFrameImage(chunk, index, keyframeEndInMilliseconds, imageInfo);
_images.push_back(frame);
}
@@ -629,7 +627,7 @@ void StreamMovieActorFrames::readFrameData(Chunk &chunk) {
// same index, and frames are not necessarily in index order. So we'll
// do a linear search, which is how the original does it.
for (MovieFrameImage *image : _images) {
- if (image->index() == frame->index) {
+ if (image->_index == frame->index) {
frame->image = image;
break;
}
@@ -637,7 +635,7 @@ void StreamMovieActorFrames::readFrameData(Chunk &chunk) {
if (frame->keyframeIndex != 0) {
for (MovieFrameImage *image : _images) {
- if (image->index() == frame->keyframeIndex) {
+ if (image->_index == frame->keyframeIndex) {
frame->keyframeImage = image;
break;
}
diff --git a/engines/mediastation/actors/movie.h b/engines/mediastation/actors/movie.h
index b6c900c7612..fd017c1641b 100644
--- a/engines/mediastation/actors/movie.h
+++ b/engines/mediastation/actors/movie.h
@@ -39,26 +39,14 @@ enum MovieBlitType {
kCompressedDeltaMovieBlit = 3,
};
-class MovieFrameInfo : public ImageInfo {
+class MovieFrameImage : public PixMapImage {
public:
- MovieFrameInfo() = default;
- MovieFrameInfo(Chunk &chunk);
+ MovieFrameImage(Chunk &chunk, uint index, uint keyframeEndInMilliseconds, const ImageInfo &imageInfo);
uint _index = 0;
uint _keyframeEndInMilliseconds = 0;
};
-class MovieFrameImage : public PixMapImage {
-public:
- MovieFrameImage(Chunk &chunk, const MovieFrameInfo &header);
- ~MovieFrameImage() = default;
-
- uint32 index() { return _frameInfo._index; }
-
-private:
- MovieFrameInfo _frameInfo;
-};
-
enum MovieSectionType {
kMovieRootSection = 0x06a8,
kMovieImageDataSection = 0x06a9,
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index c6f4f66cc8d..361a058e667 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -25,36 +25,17 @@
namespace MediaStation {
-SpriteFrameInfo::SpriteFrameInfo(Chunk &chunk) : ImageInfo(chunk) {
- _index = chunk.readTypedUint16();
- _offset = chunk.readTypedPoint();
+SpriteMovieClip::SpriteMovieClip(uint clipId, int first, int last) :
+ id(clipId), firstFrameIndex(first), lastFrameIndex(last) {
}
Common::String SpriteMovieClip::getDebugString() const {
return Common::String::format("%s: [%d, %d]", g_engine->formatParamTokenName(id).c_str(), firstFrameIndex, lastFrameIndex);
}
-SpriteFrame::SpriteFrame(Chunk &chunk, const SpriteFrameInfo &header) : PixMapImage(chunk, header), _frameInfo(header) {
-}
-
-uint32 SpriteFrame::left() {
- return _frameInfo._offset.x;
-}
-
-uint32 SpriteFrame::top() {
- return _frameInfo._offset.y;
-}
-
-Common::Point SpriteFrame::topLeft() {
- return Common::Point(left(), top());
-}
-
-Common::Rect SpriteFrame::boundingBox() {
- return Common::Rect(topLeft(), width(), height());
-}
-
-uint32 SpriteFrame::index() {
- return _frameInfo._index;
+SpriteFrame::SpriteFrame(Chunk &chunk, uint index, Common::Point offset, const ImageInfo &imageInfo) :
+ PixMapImage(chunk, imageInfo), _index(index), _origin(offset) {
+ debugC(5, kDebugLoading, "%s: frame 0x%x", __func__, _index);
}
SpriteAsset::~SpriteAsset() {
@@ -88,7 +69,7 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
break;
case kActorHeaderSpriteChunkCount:
- _asset->_frameCount = chunk.readTypedUint16();
+ _asset->frameCount = chunk.readTypedUint16();
break;
case kActorHeaderSpriteClip: {
@@ -118,13 +99,13 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
void SpriteMovieActor::loadIsComplete() {
// This clip goes forward through all the sprite's frames.
- SpriteMovieClip forwardClip(DEFAULT_FORWARD_CLIP_ID, 0, _asset->_frameCount - 1);
+ SpriteMovieClip forwardClip(DEFAULT_FORWARD_CLIP_ID, 0, _asset->frameCount - 1);
if (!_clips.contains(DEFAULT_FORWARD_CLIP_ID)) {
_clips.setVal(forwardClip.id, forwardClip);
}
// This clip goes backward through all the sprite's frames.
- SpriteMovieClip backwardClip(DEFAULT_BACKWARD_CLIP_ID, _asset->_frameCount - 1, 0);
+ SpriteMovieClip backwardClip(DEFAULT_BACKWARD_CLIP_ID, _asset->frameCount - 1, 0);
if (!_clips.contains(DEFAULT_BACKWARD_CLIP_ID)) {
_clips.setVal(backwardClip.id, backwardClip);
}
@@ -309,7 +290,7 @@ void SpriteMovieActor::setCurrentClip(uint clipId) {
if (_clips.contains(clipId)) {
SpriteMovieClip newClip = _clips.getVal(clipId);
debugC(3, kDebugSpriteMovie, "[%s] %s: (frameCount: %d) activeClip: %s; newClip: %s",
- debugName(), __func__, _asset->_frameCount, _activeClip.getDebugString().c_str(), newClip.getDebugString().c_str());
+ debugName(), __func__, _asset->frameCount, _activeClip.getDebugString().c_str(), newClip.getDebugString().c_str());
_activeClip = _clips.getVal(clipId);
} else {
_activeClip.id = clipId;
@@ -346,15 +327,17 @@ void SpriteMovieActor::process() {
}
void SpriteMovieActor::readChunk(Chunk &chunk) {
- // Reads one frame from the sprite.
- SpriteFrameInfo header(chunk);
- SpriteFrame *frame = new SpriteFrame(chunk, header);
+ // Read one frame from the sprite.
+ ImageInfo imageInfo(chunk);
+ uint index = chunk.readTypedUint16();
+ Common::Point offset = chunk.readTypedPoint();
+ SpriteFrame *frame = new SpriteFrame(chunk, index, offset, imageInfo);
_asset->frames.push_back(frame);
// TODO: Are these in exactly reverse order? If we can just reverse the
// whole thing once.
Common::sort(_asset->frames.begin(), _asset->frames.end(), [](SpriteFrame *a, SpriteFrame *b) {
- return a->index() < b->index();
+ return a->_index < b->_index;
});
}
@@ -448,11 +431,10 @@ void SpriteMovieActor::draw(DisplayContext &displayContext) {
SpriteFrame *activeFrame = _asset->frames[_currentFrameIndex];
if (_isVisible) {
- Common::Rect frameBbox = activeFrame->boundingBox();
- frameBbox.translate(_boundingBox.left, _boundingBox.top);
- debugC(8, kDebugSpriteMovie, "[%s] %s: frame %d",
- debugName(), __func__, _currentFrameIndex);
- g_engine->getDisplayManager()->imageBlit(frameBbox.origin(), activeFrame, _dissolveFactor, &displayContext);
+ Common::Point originToDraw = _boundingBox.origin() + activeFrame->_origin;
+ debugC(7, kDebugSpriteMovie, "[%s] %s: frame %d (%d, %d)",
+ debugName(), __func__, activeFrame->_index, originToDraw.x, originToDraw.y);
+ g_engine->getDisplayManager()->imageBlit(originToDraw, activeFrame, _dissolveFactor, &displayContext);
}
}
diff --git a/engines/mediastation/actors/sprite.h b/engines/mediastation/actors/sprite.h
index c805ee32325..9c026931073 100644
--- a/engines/mediastation/actors/sprite.h
+++ b/engines/mediastation/actors/sprite.h
@@ -35,37 +35,21 @@
namespace MediaStation {
struct SpriteMovieClip {
- uint id = 0;
- int firstFrameIndex = 0;
- int lastFrameIndex = 0;
-
SpriteMovieClip() = default;
- SpriteMovieClip(uint clipId, int first, int last)
- : id(clipId), firstFrameIndex(first), lastFrameIndex(last) {}
+ SpriteMovieClip(uint clipId, int first, int last);
Common::String getDebugString() const;
-};
-
-class SpriteFrameInfo : public ImageInfo {
-public:
- SpriteFrameInfo() = default;
- SpriteFrameInfo(Chunk &chunk);
- uint _index;
- Common::Point _offset;
+ uint id = 0;
+ int firstFrameIndex = 0;
+ int lastFrameIndex = 0;
};
class SpriteFrame : public PixMapImage {
public:
- SpriteFrame(Chunk &chunk, const SpriteFrameInfo &frameInfo);
-
- uint32 left();
- uint32 top();
- Common::Point topLeft();
- Common::Rect boundingBox();
- uint32 index();
+ SpriteFrame(Chunk &chunk, uint index, Common::Point origin, const ImageInfo &imageInfo);
-private:
- SpriteFrameInfo _frameInfo;
+ int _index = 0;
+ Common::Point _origin;
};
// The original had a separate class that did reference counting,
@@ -73,7 +57,7 @@ private:
struct SpriteAsset {
~SpriteAsset();
- uint _frameCount = 0;
+ uint frameCount = 0;
Common::Array<SpriteFrame *> frames;
};
@@ -103,7 +87,7 @@ private:
Common::HashMap<uint, SpriteMovieClip> _clips;
Common::SharedPtr<SpriteAsset> _asset;
bool _isPlaying = false;
- uint _currentFrameIndex = 0;
+ int _currentFrameIndex = 0;
uint _nextFrameTime = 0;
uint _defaultClipId = DEFAULT_FORWARD_CLIP_ID;
SpriteMovieClip _activeClip;
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index 4b86d6f74eb..21b92b658a3 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -25,12 +25,12 @@
namespace MediaStation {
ImageInfo::ImageInfo(Chunk &chunk) {
- uint headerSizeInBytes = chunk.readTypedUint16();
+ _imageDataStartOffset = chunk.readTypedUint16();
_dimensions = chunk.readTypedGraphicSize();
_compressionType = static_cast<BitmapCompressionType>(chunk.readTypedUint16());
_stride = chunk.readTypedUint16();
- debugC(5, kDebugLoading, "%s: headerSize: %d, _compressionType: 0x%x, _stride: %d",
- __func__, headerSizeInBytes, static_cast<uint>(_compressionType), _stride);
+ debugC(5, kDebugLoading, "%s: imageDataStartOffset: 0x%x, _compressionType: 0x%x, _stride: %d",
+ __func__, _imageDataStartOffset, static_cast<uint>(_compressionType), _stride);
}
PixMapImage::PixMapImage(Chunk &chunk, const ImageInfo &imageInfo) : _imageInfo(imageInfo) {
@@ -38,7 +38,10 @@ PixMapImage::PixMapImage(Chunk &chunk, const ImageInfo &imageInfo) : _imageInfo(
warning("%s: Got stride less than width", __func__);
}
- _unk1 = chunk.readUint16LE();
+ // Make sure we are at the start of the image data.
+ uint imageDataStartPos = chunk.startPos() + _imageInfo._imageDataStartOffset;
+ chunk.seek(imageDataStartPos);
+
if (chunk.bytesRemaining() > 0) {
if (isCompressed()) {
_compressedStream = chunk.readStream(chunk.bytesRemaining());
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index 3182db65dba..d99004ac76b 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -46,6 +46,7 @@ public:
Common::Point _dimensions;
BitmapCompressionType _compressionType = kUncompressedBitmap;
int16 _stride = 0;
+ uint _imageDataStartOffset = 0;
};
class PixMapImage {
@@ -65,7 +66,6 @@ public:
private:
ImageInfo _imageInfo;
- uint _unk1 = 0;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/datafile.h b/engines/mediastation/datafile.h
index 798c6441f45..9afe2b49063 100644
--- a/engines/mediastation/datafile.h
+++ b/engines/mediastation/datafile.h
@@ -105,6 +105,7 @@ public:
Chunk(Common::SeekableReadStream *stream);
uint32 bytesRemaining();
+ uint32 startPos() const { return _dataStartOffset; }
uint32 _id = 0;
uint32 _length = 0;
Commit: 64fb8b2037cb0c2f3aa88d19a39d846fcbc2c877
https://github.com/scummvm/scummvm/commit/64fb8b2037cb0c2f3aa88d19a39d846fcbc2c877
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T21:10:49-05:00
Commit Message:
MEDIASTATION: Add initial support for Canvas and Text actors
Changed paths:
engines/mediastation/actor.h
engines/mediastation/actors/canvas.cpp
engines/mediastation/actors/canvas.h
engines/mediastation/actors/font.cpp
engines/mediastation/actors/font.h
engines/mediastation/actors/text.cpp
engines/mediastation/actors/text.h
engines/mediastation/graphics.cpp
engines/mediastation/graphics.h
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index fb0f36699be..f4d6da1edb0 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -96,7 +96,6 @@ enum ActorHeaderSectionType {
kActorHeaderScaleXAndY = 0x77a,
kActorHeaderScaleX = 0x77c,
kActorHeaderScaleY = 0x77d,
- kActorHeaderUnk0 = 0x7d0,
kActorHeaderActorName = 0x0bb8,
kStreamMovieProxyInfo = 0x06ac,
@@ -113,10 +112,8 @@ enum ActorHeaderSectionType {
kActorHeaderCameraImageActor = 0x77b,
// CANVAS FIELDS.
- kActorHeaderCanvasUnk1 = 0x491,
kActorHeaderCanvasDissolveFactor = 0x493,
- kActorHeaderCanvasUnk2 = 0x494,
- kActorHeaderCanvasUnk3 = 0x495,
+ kActorHeaderCanvasTransparency = 0x7d0,
// STAGE FIELDS.
kActorHeaderStageExtent = 0x0771,
@@ -125,14 +122,16 @@ enum ActorHeaderSectionType {
// TEXT FIELDS.
kActorHeaderEditable = 0x03eb,
- kActorHeaderFontId = 0x0258,
+ kActorHeaderFontActorId = 0x0258,
kActorHeaderInitialText = 0x0259,
kActorHeaderTextMaxLength = 0x25a,
kActorHeaderTextJustification = 0x025b,
kActorHeaderTextPosition = 0x25f,
- kActorHeaderTextUnk1 = 0x262,
- kActorHeaderTextUnk2 = 0x263,
- kActorHeaderTextCharacterClass = 0x0266,
+ kActorHeaderTextCursorIsVisible = 0x262,
+ kActorHeaderTextConstrainToWidth = 0x263,
+ kActorHeaderTextOverwriteMode = 0x264,
+ kActorHeaderTextAcceptedCharRange = 0x265,
+ kActorHeaderTextAcceptedCharRangeWithOffset = 0x0266,
// SPRITE FIELDS.
kActorHeaderSpriteClip = 0x03e9,
@@ -243,6 +242,7 @@ public:
virtual bool isVisible() const { return _isVisible; }
virtual Common::Rect getBbox() const { return _boundingBox; }
int zIndex() const { return _zIndex; }
+ void moveTo(int16 x, int16 y);
virtual void currentMousePosition(Common::Point &point);
virtual void invalidateMouse();
@@ -276,7 +276,7 @@ public:
protected:
uint _stageId = 0;
int _zIndex = 0;
- double _dissolveFactor = 0.0;
+ double _dissolveFactor = 1.0;
double _scaleX = 0.0;
double _scaleY = 0.0;
Common::Rect _boundingBox;
@@ -286,7 +286,6 @@ protected:
bool _getOffstageEvents = false;
StageActor *_parentStage = nullptr;
- void moveTo(int16 x, int16 y);
void moveToCentered(int16 x, int16 y);
void setBounds(const Common::Rect &bounds);
void setZIndex(int zIndex);
diff --git a/engines/mediastation/actors/canvas.cpp b/engines/mediastation/actors/canvas.cpp
index 57550ca4dd1..4b8a923faaa 100644
--- a/engines/mediastation/actors/canvas.cpp
+++ b/engines/mediastation/actors/canvas.cpp
@@ -20,28 +20,195 @@
*/
#include "mediastation/actors/canvas.h"
+#include "mediastation/actors/image.h"
+#include "mediastation/actors/stage.h"
+#include "mediastation/debugchannels.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
void CanvasActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
switch (paramType) {
+ case kActorHeaderChannelIdent:
+ // The original seems to read this and then throws it away!
+ _channelIdent = chunk.readTypedChannelIdent();
+ break;
+
case kActorHeaderStartup:
_isVisible = static_cast<bool>(chunk.readTypedByte());
break;
+ case kActorHeaderTransparency:
+ case kActorHeaderCanvasTransparency:
+ _hasTransparency = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kActorHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ case kActorHeaderX:
+ _offset.x = chunk.readTypedGraphicUnit();
+ break;
+
+ case kActorHeaderY:
+ _offset.y = chunk.readTypedGraphicUnit();
+ break;
+
+ case kActorHeaderLoadType:
+ // The original seems to read this and then throws it away!
+ chunk.readByte();
+ break;
+
default:
SpatialEntity::readParameter(chunk, paramType);
}
}
ScriptValue CanvasActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
+ ScriptValue returnValue;
switch (methodId) {
- case kClearToPaletteMethod: {
- error("%s: clearToPalette is not implemented yet", __func__);
+ case kSpatialShowMethod:
+ ARGCOUNTCHECK(0);
+ setVisibility(true);
+ break;
+
+ case kSpatialHideMethod:
+ ARGCOUNTCHECK(0);
+ setVisibility(false);
+ break;
+
+ case kCanvasClearToTransparencyMethod:
+ ARGCOUNTCHECK(0);
+ clearToTransparency();
+ break;
+
+ case kCanvasStampImageMethod: {
+ ARGCOUNTCHECK(3);
+ int16 x = static_cast<int16>(args[0].asFloat());
+ int16 y = static_cast<int16>(args[1].asFloat());
+ Common::Point stampPosition(x, y);
+ uint actorId = args[2].asActorId();
+ stampImage(stampPosition, actorId);
+ break;
+ }
+
+ case kCanvasCopyScreenToMethod: {
+ ARGCOUNTCHECK(2);
+ int16 x = static_cast<int16>(args[0].asFloat());
+ int16 y = static_cast<int16>(args[1].asFloat());
+ Common::Point dest(x, y);
+ copyScreenTo(dest);
+ break;
+ }
+
+ case kCanvasClearToPaletteMethod: {
+ ARGCOUNTCHECK(1);
+ uint colorIndex = static_cast<uint>(args[0].asFloat());
+ clearToPalette(colorIndex);
+ break;
}
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
+ }
+ return returnValue;
+}
+
+void CanvasActor::loadIsComplete() {
+ ImageInfo imageInfo;
+ if (_hasTransparency) {
+ imageInfo._compressionType = kUncompressedTransparentBitmap;
+ } else {
+ imageInfo._compressionType = kUncompressedBitmap;
+ }
+
+ imageInfo._dimensions = Common::Point(getBbox().width(), getBbox().height());
+ imageInfo._stride = getBbox().width();
+
+ _image = Common::SharedPtr<ImageAsset>(new ImageAsset);
+ _image->bitmap = new PixMapImage(imageInfo);
+ if (_hasTransparency) {
+ fillCanvas(0x00);
+ } else {
+ fillCanvas(0xFE);
+ }
+
+ SpatialEntity::loadIsComplete();
+}
+
+void CanvasActor::setVisibility(bool visibility) {
+ if (visibility != _isVisible) {
+ _isVisible = visibility;
+ invalidateLocalBounds();
+ }
+}
+
+void CanvasActor::fillCanvas(uint paletteIndex) {
+ if (_image != nullptr && _image->bitmap != nullptr) {
+ Graphics::ManagedSurface &surface = _image->bitmap->_image;
+ surface.fillRect(Common::Rect(0, 0, surface.w, surface.h), paletteIndex);
+ }
+}
+
+void CanvasActor::clearToTransparency() {
+ fillCanvas(0);
+ invalidateLocalBounds();
+}
+
+void CanvasActor::stampImage(const Common::Point &dest, uint actorId) {
+ // Set up the display context to draw to the canvas's image surface.
+ if (_image != nullptr) {
+ _displayContext._destImage = &_image->bitmap->_image;
+ if (_displayContext._destImage == nullptr) {
+ _displayContext.deleteClips();
+ } else {
+ _displayContext.verifyClipSize();
+ }
+ }
+
+ // Although this method is named stampImage, it can actually stamp other spatial entities too.
+ debugC(5, kDebugGraphics, "[%s] %s: %s at (%d, %d)",
+ debugName(), __func__, g_engine->formatActorName(actorId).c_str(), dest.x, dest.y);
+ SpatialEntity *imageToStamp = g_engine->getSpatialEntityById (actorId);
+ Common::Point imageToStampOriginalBoundsOrigin = imageToStamp->getBbox().origin();
+ imageToStamp->moveTo(dest.x, dest.y);
+
+ // The idea is to draw the entity in the proper place on the canvas (with a manufactured dirty region
+ // so the draw actually happens), then restore the entity to its original place.
+ Region dirtyRegion;
+ Common::Rect imageToStampBounds = imageToStamp->getBbox();
+ dirtyRegion.addRect(imageToStampBounds);
+ if (_displayContext._destImage != nullptr) {
+ _displayContext.addClip();
+ }
+ _displayContext.setClipTo(dirtyRegion);
+ imageToStamp->draw(_displayContext);
+
+ // Clean up display context if we're drawing to our own image.
+ _displayContext.emptyCurrentClip();
+ if (_image != nullptr && &_image->bitmap->_image == _displayContext._destImage) {
+ _displayContext._destImage = nullptr;
+ _displayContext.deleteClips();
+ }
+
+ imageToStamp->moveTo(imageToStampOriginalBoundsOrigin.x, imageToStampOriginalBoundsOrigin.y);
+ invalidateLocalBounds();
+}
+
+void CanvasActor::copyScreenTo(const Common::Point &dest) {
+ warning("[%s] %s: STUB", debugName(), __func__);
+}
+
+void CanvasActor::clearToPalette(uint colorIndex) {
+ fillCanvas(colorIndex);
+ invalidateLocalBounds();
+}
+
+void CanvasActor::draw(DisplayContext &displayContext) {
+ if (_image != nullptr) {
+ Common::Point drawPosition = getBbox().origin() + _offset;
+ g_engine->getDisplayManager()->imageBlit(drawPosition, _image->bitmap, _dissolveFactor, &displayContext, nullptr, true);
}
}
diff --git a/engines/mediastation/actors/canvas.h b/engines/mediastation/actors/canvas.h
index 904bc5b9e5b..c607d4d24b5 100644
--- a/engines/mediastation/actors/canvas.h
+++ b/engines/mediastation/actors/canvas.h
@@ -22,18 +22,41 @@
#ifndef MEDIASTATION_CANVAS_H
#define MEDIASTATION_CANVAS_H
+#include "common/rect.h"
+
#include "mediastation/actor.h"
+#include "mediastation/graphics.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
namespace MediaStation {
-class CanvasActor : public SpatialEntity {
+struct ImageAsset;
+
+class CanvasActor : public SpatialEntity, public ChannelClient {
public:
- CanvasActor() : SpatialEntity(kActorTypeCanvas) {};
+ CanvasActor() : SpatialEntity(kActorTypeCanvas) {
+ _dissolveFactor = 1.0;
+ _isVisible = true;
+ _hasTransparency = true;
+ }
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+ virtual void loadIsComplete() override;
+ virtual void draw(DisplayContext &displayContext) override;
+
+private:
+ Common::Point _offset;
+ Common::SharedPtr<ImageAsset> _image;
+ DisplayContext _displayContext;
+
+ void setVisibility(bool visibility);
+ void fillCanvas(uint paletteIndex);
+ void clearToTransparency();
+ void stampImage(const Common::Point &dest, uint actorId);
+ void copyScreenTo(const Common::Point &dest);
+ void clearToPalette(uint colorIndex);
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/font.cpp b/engines/mediastation/actors/font.cpp
index 72cd2f1e604..68d3b8dd4d8 100644
--- a/engines/mediastation/actors/font.cpp
+++ b/engines/mediastation/actors/font.cpp
@@ -21,21 +21,23 @@
#include "mediastation/debugchannels.h"
#include "mediastation/actors/font.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
-FontGlyph::FontGlyph(Chunk &chunk, uint asciiCode, int unk1, int unk2, const ImageInfo &header) : PixMapImage(chunk, header) {
- _asciiCode = asciiCode;
- _unk1 = unk1;
- _unk2 = unk2;
+FontCharacter::FontCharacter(Chunk &chunk, uint charCode, int horizontalSpacing, int baselineOffset, const ImageInfo &header) :
+ PixMapImage(chunk, header),
+ _charCode(charCode),
+ _horizontalSpacing(horizontalSpacing),
+ _baselineOffset(baselineOffset) {
}
FontActor::~FontActor() {
unregisterWithStreamManager();
- for (auto it = _glyphs.begin(); it != _glyphs.end(); ++it) {
+ for (auto it = _characters.begin(); it != _characters.end(); ++it) {
delete it->_value;
}
- _glyphs.clear();
+ _characters.clear();
}
void FontActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
@@ -51,16 +53,35 @@ void FontActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
}
void FontActor::readChunk(Chunk &chunk) {
- debugC(5, kDebugLoading, "FontActor::readChunk(): Reading font glyph (@0x%llx)", static_cast<long long int>(chunk.pos()));
- uint asciiCode = chunk.readTypedUint16();
- int unk1 = chunk.readTypedUint16();
- int unk2 = chunk.readTypedUint16();
+ // This is always 16-bit because there are some special char codes above 0xFF,
+ // such as the cursor and arrow keys.
+ uint charCode = chunk.readTypedUint16();
+ int16 horizontalSpacing = static_cast<int16>(chunk.readTypedUint16());
+ int16 baselineOffset = static_cast<int16>(chunk.readTypedUint16());
ImageInfo header(chunk);
- FontGlyph *glyph = new FontGlyph(chunk, asciiCode, unk1, unk2, header);
- if (_glyphs.getValOrDefault(asciiCode) != nullptr) {
- error("%s: Glyph for ASCII code 0x%x already exists", __func__, asciiCode);
+ FontCharacter *glyph = new FontCharacter(chunk, charCode, horizontalSpacing, baselineOffset, header);
+ if (_characters.getValOrDefault(charCode) != nullptr) {
+ warning("[%s] %s: Glyph for char code 0x%x already exists", debugName(), __func__, charCode);
}
- _glyphs.setVal(asciiCode, glyph);
+ _characters.setVal(charCode, glyph);
+ _totalHeightOfAllChars += glyph->height();
+ _totalWidthOfAllChars += glyph->width();
+
+ // Track the maximum ascent across all glyphs.
+ // The ascent is either the specified baseline offset, or the full glyph height if not specified.
+ _maxAscent = MAX(_maxAscent, glyph->ascent());
+
+ // Calculate descent (distance from baseline to bottom of glyph)
+ int16 charDescent = glyph->height() - glyph->ascent();
+ _maxDescent = MAX(_maxDescent, charDescent);
+}
+
+void FontActor::loadIsComplete() {
+ if (_characters.size() > 0) {
+ _averageCharWidth = _totalWidthOfAllChars / _characters.size();
+ _averageCharHeight = _totalHeightOfAllChars / _characters.size();
+ }
+ Actor::loadIsComplete();
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/font.h b/engines/mediastation/actors/font.h
index 654f269fca1..ea6638aac16 100644
--- a/engines/mediastation/actors/font.h
+++ b/engines/mediastation/actors/font.h
@@ -30,12 +30,17 @@
namespace MediaStation {
-class FontGlyph : public PixMapImage {
+class FontCharacter : public PixMapImage {
public:
- FontGlyph(Chunk &chunk, uint asciiCode, int unk1, int unk2, const ImageInfo &header);
- uint _asciiCode = 0;
- int _unk1 = 0;
- int _unk2 = 0;
+ FontCharacter(Chunk &chunk, uint charCode, int horizontalSpacing, int baselineOffset, const ImageInfo &header);
+
+ // Returns the ascent (baseline position). Falls back to full height if baseline offset is not specified.
+ int16 ascent() const { return (_baselineOffset != 0) ? _baselineOffset : height(); }
+ uint _charCode = 0;
+ int16 _horizontalSpacing = 0; // Additional horizontal spacing added after the glyph width to get total advance
+
+private:
+ int16 _baselineOffset = 0; // Baseline position within the glyph bitmap (ascent - distance from top edge to baseline)
};
class FontActor : public Actor, public ChannelClient {
@@ -45,9 +50,19 @@ public:
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
virtual void readChunk(Chunk &chunk) override;
+ virtual void loadIsComplete() override;
+
+ FontCharacter *lookupCharacter(uint charCode) { return _characters.getValOrDefault(charCode); }
+
+ int16 _totalWidthOfAllChars = 0;
+ int16 _totalHeightOfAllChars = 0;
+ int16 _averageCharWidth = 0;
+ int16 _averageCharHeight = 0;
+ int16 _maxAscent = 0; // Maximum ascent (distance from top to baseline) across all glyphs.
+ int16 _maxDescent = 0; // Maximum descent (distance from baseline to bottom) across all glyphs.
private:
- Common::HashMap<uint, FontGlyph *> _glyphs;
+ Common::HashMap<uint, FontCharacter *> _characters;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/text.cpp b/engines/mediastation/actors/text.cpp
index 2dedfa8b7da..346d474db80 100644
--- a/engines/mediastation/actors/text.cpp
+++ b/engines/mediastation/actors/text.cpp
@@ -20,6 +20,7 @@
*/
#include "mediastation/actors/text.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
@@ -29,26 +30,24 @@ void TextActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
_isVisible = static_cast<bool>(chunk.readTypedByte());
break;
- case kActorHeaderEditable:
- _editable = chunk.readTypedByte();
- break;
-
case kActorHeaderLoadType:
_loadType = chunk.readTypedByte();
break;
- case kActorHeaderFontId:
- _fontActorId = chunk.readTypedUint16();
- break;
-
- case kActorHeaderTextMaxLength:
- _maxTextLength = chunk.readTypedUint16();
+ case kActorHeaderFontActorId: {
+ uint fontActorId = chunk.readTypedUint16();
+ _fontActor = static_cast<FontActor *>(g_engine->getActorByIdAndType(fontActorId, kActorTypeFont));
break;
+ }
case kActorHeaderInitialText:
_text = chunk.readTypedString();
break;
+ case kActorHeaderTextMaxLength:
+ _maxLength = chunk.readTypedUint16();
+ break;
+
case kActorHeaderTextJustification:
_justification = static_cast<TextJustification>(chunk.readTypedUint16());
break;
@@ -57,58 +56,428 @@ void TextActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
_position = static_cast<TextPosition>(chunk.readTypedUint16());
break;
- case kActorHeaderTextCharacterClass: {
- CharacterClass characterClass;
- characterClass.firstAsciiCode = chunk.readTypedUint16();
- characterClass.lastAsciiCode = chunk.readTypedUint16();
- _acceptedInput.push_back(characterClass);
+ case kActorHeaderTextAcceptedCharRangeWithOffset: {
+ uint firstCharCode = chunk.readTypedUint16();
+ uint lastCharCode = chunk.readTypedUint16();
+ uint charCodeOffset = chunk.readTypedUint16();
+ addAcceptedChars(firstCharCode, lastCharCode, charCodeOffset);
+ break;
+ }
+
+ case kActorHeaderTextAcceptedCharRange: {
+ uint firstCharCode = chunk.readTypedUint16();
+ uint lastCharCode = chunk.readTypedUint16();
+ addAcceptedChars(firstCharCode, lastCharCode);
break;
}
+ case kActorHeaderEditable:
+ _isEditable = chunk.readTypedByte();
+ break;
+
+ case kActorHeaderTextCursorIsVisible:
+ _cursorIsVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kActorHeaderTextConstrainToWidth:
+ _constrainToWidth = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kActorHeaderTextOverwriteMode:
+ _overwriteMode = static_cast<bool>(chunk.readTypedByte());
+ break;
+
default:
SpatialEntity::readParameter(chunk, paramType);
}
}
+void TextActor::loadIsComplete() {
+ setText();
+ SpatialEntity::loadIsComplete();
+}
+
ScriptValue TextActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
switch (methodId) {
- case kTextMethod: {
- assert(args.empty());
- error("%s: Text() method not implemented yet", __func__);
+ case kSpatialShowMethod:
+ ARGCOUNTCHECK(0);
+ if (!_isVisible) {
+ _isVisible = true;
+ invalidateLocalBounds();
+ }
+ break;
+
+ case kSpatialHideMethod:
+ ARGCOUNTCHECK(0);
+ if (_isVisible) {
+ _isVisible = false;
+ invalidateLocalBounds();
+ }
+ break;
+
+ case kTextSetEditableMethod:
+ ARGCOUNTCHECK(0);
+ _isEditable = true;
+ break;
+
+ case kTextSetNonEditableMethod:
+ ARGCOUNTCHECK(0);
+ _isEditable = false;
+ break;
+
+ case kTextGetFontActorMethod: {
+ ARGCOUNTCHECK(0);
+ returnValue.setToActorId(_fontActor->id());
+ break;
+ }
+
+ case kTextSetFontActorMethod: {
+ ARGCOUNTCHECK(1);
+ uint fontActorId = args[0].asActorId();
+ _fontActor = static_cast<FontActor *>(g_engine->getActorByIdAndType(fontActorId, kActorTypeFont));
+ invalidateLocalBounds();
+ break;
+ }
+
+ case kTextGetTextMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToString(_text);
+ break;
+
+ case kTextSetTextMethod: {
+ ARGCOUNTCHECK(1);
+ _text = args[0].asString();
+ setText();
+ invalidateLocalBounds();
+ break;
+ }
+
+ case kTextGetMaxLengthMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToFloat(_maxLength);
+ break;
+
+ case kTextSetMaxLengthMethod:
+ ARGCOUNTCHECK(1);
+ _maxLength = static_cast<uint>(args[0].asFloat());
+ invalidateLocalBounds();
+ break;
+
+ case kGetLastPressedCharCodeMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToFloat(_pressedCharCode);
+ break;
+
+ case kTextGetCursorPositionMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToFloat(_cursorPosition);
+ break;
+
+ case kTextSetCursorPositionMethod: {
+ ARGCOUNTCHECK(1);
+ uint newPos = static_cast<uint>(args[0].asFloat());
+ if (newPos > _text.size()) {
+ newPos = _text.size();
+ }
+ _cursorPosition = newPos;
+ invalidateLocalBounds();
+ break;
+ }
+
+ case kTextGetJustificationMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToParamToken(static_cast<uint>(_justification));
+ break;
+
+ case kTextSetJustificationMethod:
+ ARGCOUNTCHECK(1);
+ _justification = static_cast<TextJustification>(args[0].asParamToken());
+ invalidateLocalBounds();
+ break;
+
+ case kTextGetPositionMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToParamToken(static_cast<uint>(_position));
+ break;
+
+ case kTextSetPositionMethod:
+ ARGCOUNTCHECK(1);
+ _position = static_cast<TextPosition>(args[0].asParamToken());
+ invalidateLocalBounds();
+ break;
+
+ case kTextGetConstrainToWidthMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToBool(_constrainToWidth);
+ break;
+
+ case kTextSetConstrainToWidthMethod:
+ ARGCOUNTCHECK(1);
+ _constrainToWidth = static_cast<bool>(args[0].asBool());
+ invalidateLocalBounds();
+ break;
+
+ case kTextGetCursorIsVisibleMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToBool(_cursorIsVisible);
+ break;
+
+ case kTextSetCursorIsVisibleMethod:
+ ARGCOUNTCHECK(1);
+ _cursorIsVisible = static_cast<bool>(args[0].asBool());
+ invalidateLocalBounds();
+ break;
+
+ case kTextGetOverwriteModeMethod:
+ ARGCOUNTCHECK(0);
+ returnValue.setToBool(_overwriteMode);
+ break;
+
+ case kTextSetOverwriteModeMethod:
+ ARGCOUNTCHECK(1);
+ _overwriteMode = static_cast<bool>(args[0].asBool());
+ break;
+
+ case kTextGetTranslatedCharCode: {
+ ARGCOUNTCHECK(1);
+ uint charId = args[0].asParamToken();
+ uint translatedChar = _acceptedChars.getValOrDefault(charId, 0);
+ if (translatedChar != 0) {
+ returnValue.setToFloat(translatedChar);
+ } else {
+ // Character not found, so return the input as-is.
+ returnValue = args[0];
+ }
+ break;
}
- case kSetTextMethod: {
- assert(args.size() == 1);
- error("%s: getText() method not implemented yet", __func__);
+ case kTextAddAcceptedCharsMethod: {
+ ARGCOUNTMIN(2);
+ uint startCharId = static_cast<uint>(args[0].asFloat());
+ uint endCharId = static_cast<uint>(args[1].asFloat());
+ uint category = 0;
+ if (args.size() >= 3) {
+ category = static_cast<uint>(args[2].asFloat());
+ }
+
+ addAcceptedChars(startCharId, endCharId, category);
+ break;
}
- case kSpatialShowMethod: {
- assert(args.empty());
- _isVisible = true;
- warning("%s: spatialShow method not implemented yet", __func__);
- return returnValue;
+ case kTextIsCharacterAcceptedMethod: {
+ ARGCOUNTCHECK(1);
+ uint charId = static_cast<uint>(args[0].asFloat());
+ bool isAccepted = _acceptedChars.contains(charId);
+ returnValue.setToBool(isAccepted);
+ break;
}
- case kSpatialHideMethod: {
- assert(args.empty());
- _isVisible = false;
- warning("%s: spatialHide method not implemented yet", __func__);
- return returnValue;
+ case kTextEnableDisableCharacterMethod: {
+ ARGCOUNTCHECK(2);
+ uint charId = static_cast<uint>(args[0].asFloat());
+ bool shouldEnable = static_cast<bool>(args[1].asBool());
+ if (shouldEnable) {
+ // Mark character as accepted.
+ addAcceptedChars(charId, charId, 0);
+ } else {
+ // No longer mark character as accepted.
+ _acceptedChars.erase(charId);
+ }
+ break;
}
+ case kTextIsEditableMethod:
+ returnValue.setToBool(_isEditable);
+ break;
+
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
+ }
+ return returnValue;
+}
+
+void TextActor::draw(DisplayContext &displayContext) {
+ if (_fontActor == nullptr) {
+ warning("[%s] %s: No font", debugName(), __func__);
+ return;
}
+
+ Common::Point positionOnScreen(calcStartingXPosition(), calcBaseline());
+ for (uint positionInString = 0; positionInString < _text.size(); positionInString++) {
+ FontCharacter *fontChar = nullptr;
+ char currentChar = _text[positionInString];
+
+ fontChar = _fontActor->lookupCharacter(currentChar);
+ if (fontChar != nullptr) {
+ // We have the character, so draw it.
+ drawCharacter(fontChar, positionOnScreen.x, positionOnScreen.y, displayContext);
+
+ // Also draw the cursor if necessary.
+ if (positionInString == _cursorPosition && _cursorIsVisible) {
+ drawCursor(positionOnScreen.x, positionOnScreen.y, displayContext);
+ }
+
+ positionOnScreen.x += fontChar->_horizontalSpacing + fontChar->width();
+
+ } else if (currentChar == ' ') {
+ // Handle space character, which might not be in the font.
+ if (positionInString == _cursorPosition && _cursorIsVisible) {
+ drawCursor(positionOnScreen.x, positionOnScreen.y, displayContext);
+ }
+
+ positionOnScreen.x += _fontActor->_averageCharWidth;
+
+ } else {
+ warning("[%s] %s: No font char for %d", debugName(), __func__, currentChar);
+ }
+ }
+
+ // Draw cursor at end of the text if necessary.
+ if (_cursorPosition == _text.size() && _cursorIsVisible) {
+ drawCursor(positionOnScreen.x, positionOnScreen.y, displayContext);
+ }
+}
+
+uint16 TextActor::findActorToAcceptKeyboardEvents(uint16 charCode, uint16 eventMask, MouseActorState &state) {
+ uint16 result = 0;
+ if (_loadIsComplete && (eventMask & kKeyDownFlag) && _isEditable) {
+ // If we have accepted character restrictions, check if character is valid.
+ if (!_acceptedChars.empty()) {
+ if (!_acceptedChars.contains(charCode)) {
+ return 0;
+ }
+ }
+
+ state.keyDown = this;
+ result = kNoFlag;
+ }
+
+ return result;
}
-Common::String TextActor::text() const {
- return _text;
+void TextActor::keyboardEvent(const Common::Event &event) {
+ // TODO: Implement this once we have a title that actually uses it.
+ warning("STUB: %s", __func__);
}
-void TextActor::setText(Common::String text) {
- error("%s: Setting text not implemented yet", __func__);
+bool TextActor::hasEventHandler(EventType eventType, const ScriptValue &arg) const {
+ const Common::Array<EventHandler *> &eventHandlers = _eventHandlers.getValOrDefault(eventType);
+ for (const EventHandler *eventHandler : eventHandlers) {
+ const ScriptValue &argToCheck = eventHandler->_argumentValue;
+
+ if (arg.getType() != argToCheck.getType()) {
+ continue;
+ }
+
+ if (arg == argToCheck) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void TextActor::setText() {
+ // Remove double-quotes if they're the first or last characters.
+ if (_text.firstChar() == '"') {
+ _text.deleteChar(0);
+ }
+ if (_text.lastChar() == '"') {
+ _text.deleteLastChar();
+ }
+
+ // Apply character translation if we have any.
+ for (uint positionInString = 0; positionInString < _text.size(); positionInString++) {
+ char currentChar = _text[positionInString];
+ uint translatedChar = _acceptedChars.getValOrDefault(currentChar);
+ if (translatedChar != 0) {
+ _text.setChar(translatedChar, positionInString);
+ }
+ }
+}
+
+void TextActor::addAcceptedChars(uint firstCharCode, uint lastCharCode, uint charCodeOffset) {
+ for (uint charCode = firstCharCode; charCode <= lastCharCode; charCode++) {
+ _acceptedChars.setVal(charCode, charCodeOffset);
+ if (charCodeOffset != 0) {
+ charCodeOffset++;
+ }
+ }
+}
+
+int16 TextActor::calcStartingXPosition() {
+ int16 xPos = 0;
+ Common::Rect bounds = getBbox();
+ if (_justification == kTextJustificationLeft) {
+ xPos = bounds.left;
+
+ } else if (_justification == kTextJustificationRight) {
+ int16 textPixelLength = calcPixelLength(_text);
+ xPos = bounds.right - textPixelLength;
+
+ } else if (_justification == kTextJustificationCenter) {
+ int16 textPixelLength = calcPixelLength(_text);
+ int16 boundsMidwidth = bounds.left + (bounds.width() / 2);
+ xPos = boundsMidwidth - (textPixelLength / 2);
+ }
+ return xPos;
+}
+
+int16 TextActor::calcBaseline() {
+ int16 yPos = 0;
+ Common::Rect bounds = getBbox();
+ if (_position == kTextPositionTop) {
+ yPos = bounds.top + _fontActor->_maxAscent;
+
+ } else if (_position == kTextPositionBottom) {
+ yPos = bounds.bottom - _fontActor->_maxDescent;
+
+ } else if (_position == kTextPositionMiddle) {
+ int16 boundsMidheight = bounds.top + (bounds.height() / 2);
+ yPos = boundsMidheight + (_fontActor->_maxAscent / 2);
+ }
+ return yPos;
+}
+
+void TextActor::drawCharacter(FontCharacter *glyph, int16 x, int16 y, DisplayContext &displayContext) {
+ // Draw the glyph at the baseline position (y) minus its ascent.
+ Common::Point destPoint(x, y - glyph->ascent());
+ g_engine->getDisplayManager()->imageBlit(destPoint, glyph, _dissolveFactor, &displayContext);
+}
+
+void TextActor::drawCursor(int16 x, int16 y, DisplayContext &displayContext) {
+ FontCharacter *cursorChar = _fontActor->lookupCharacter(CURSOR_CHAR_ID);
+ if (cursorChar != nullptr) {
+ drawCharacter(cursorChar, x, y, displayContext);
+ }
+}
+
+int16 TextActor::calcPixelLength(const Common::String &text) {
+ // Add the width of the main text.
+ int16 totalXOffset = 0;
+ for (uint positionInString = 0; positionInString < text.size(); positionInString++) {
+ char currentChar = text[positionInString];
+
+ FontCharacter *fontChar = _fontActor->lookupCharacter(currentChar);
+ if (fontChar != nullptr) {
+ totalXOffset += fontChar->_horizontalSpacing + fontChar->width();
+
+ } else if (currentChar == ' ') {
+ // As before, the space character might not have a defined font character.
+ totalXOffset += _fontActor->_averageCharWidth;
+ }
+ }
+
+ // Add cursor width if we are needing to show the cursor.
+ if (_cursorPosition == _text.size() && _cursorIsVisible) {
+ FontCharacter *cursorChar = _fontActor->lookupCharacter(CURSOR_CHAR_ID);
+ if (cursorChar != nullptr) {
+ totalXOffset += cursorChar->width();
+ }
+ }
+
+ return totalXOffset;
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/actors/text.h b/engines/mediastation/actors/text.h
index 595c0582e37..b53ffbdd8ba 100644
--- a/engines/mediastation/actors/text.h
+++ b/engines/mediastation/actors/text.h
@@ -25,6 +25,8 @@
#include "common/str.h"
#include "mediastation/actor.h"
+#include "mediastation/actors/font.h"
+#include "mediastation/graphics.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -39,36 +41,45 @@ enum TextJustification {
enum TextPosition {
kTextPositionMiddle = 0x25e,
kTextPositionTop = 0x260,
- kTextPositionBotom = 0x261
-};
-
-struct CharacterClass {
- uint firstAsciiCode = 0;
- uint lastAsciiCode = 0;
+ kTextPositionBottom = 0x261
};
class TextActor : public SpatialEntity {
public:
TextActor() : SpatialEntity(kActorTypeText) {};
- virtual bool isVisible() const override { return _isVisible; }
+ virtual void loadIsComplete() override;
virtual void readParameter(Chunk &chunk, ActorHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+ virtual void draw(DisplayContext &displayContext) override;
+ virtual uint16 findActorToAcceptKeyboardEvents(uint16 charCode, uint16 eventMask, MouseActorState &state) override;
+ virtual void keyboardEvent(const Common::Event &event) override;
private:
- bool _editable = false;
+ static const uint CURSOR_CHAR_ID = 0x104;
+ bool _isEditable = false;
uint _loadType = 0;
- bool _isVisible = false;
Common::String _text;
- uint _maxTextLength = 0;
- uint _fontActorId = 0;
- TextJustification _justification;
- TextPosition _position;
- Common::Array<CharacterClass> _acceptedInput;
+ uint _maxLength = 0;
+ FontActor *_fontActor = nullptr;
+ TextJustification _justification = kTextJustificationLeft;
+ TextPosition _position = kTextPositionTop;
+ Common::HashMap<uint, uint> _acceptedChars;
+ uint _cursorPosition = 0;
+ uint _pressedCharCode = 0;
+ bool _cursorIsVisible = false;
+ bool _constrainToWidth = false;
+ bool _overwriteMode = false;
+
+ void setText();
+ void addAcceptedChars(uint firstCharCode, uint lastCharCode, uint charCodeOffset = 0);
+ bool hasEventHandler(EventType eventType, const ScriptValue &arg) const;
- // Method implementations.
- Common::String text() const;
- void setText(Common::String text);
+ int16 calcStartingXPosition();
+ int16 calcBaseline();
+ void drawCharacter(FontCharacter *glyph, int16 x, int16 y, DisplayContext &displayContext);
+ void drawCursor(int16 x, int16 y, DisplayContext &displayContext);
+ int16 calcPixelLength(const Common::String &text);
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/graphics.cpp b/engines/mediastation/graphics.cpp
index ce35f62c492..75891668563 100644
--- a/engines/mediastation/graphics.cpp
+++ b/engines/mediastation/graphics.cpp
@@ -719,7 +719,8 @@ void VideoDisplayManager::imageBlit(
const PixMapImage *sourceImage,
double dissolveFactor,
DisplayContext *displayContext,
- Graphics::ManagedSurface *targetImage) {
+ Graphics::ManagedSurface *targetImage,
+ bool useTransBlit) {
byte blitFlags = kClipEnabled;
switch (sourceImage->getCompressionType()) {
@@ -787,7 +788,7 @@ void VideoDisplayManager::imageBlit(
// non-transparent blitting, but we will just use simpleBlitFrom in both
// cases. It will pick the better method if there is no transparent
// color set.
- blitRectsClip(targetImage, destinationPoint, sourceImage->_image, dirtyRegion);
+ blitRectsClip(targetImage, destinationPoint, sourceImage->_image, dirtyRegion, useTransBlit);
break;
case kRle8Blit | kClipEnabled:
@@ -819,7 +820,8 @@ void VideoDisplayManager::blitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destLocation,
const Graphics::ManagedSurface &source,
- const Common::Array<Common::Rect> &dirtyRegion) {
+ const Common::Array<Common::Rect> &dirtyRegion,
+ bool useTransBlit) {
for (const Common::Rect &dirtyRect : dirtyRegion) {
Common::Rect destRect(destLocation, source.w, source.h);
@@ -829,7 +831,11 @@ void VideoDisplayManager::blitRectsClip(
// Calculate source coordinates (relative to source image).
Common::Point originOnScreen(areaToRedraw.origin());
areaToRedraw.translate(-destLocation.x, -destLocation.y);
- dest->simpleBlitFrom(source, areaToRedraw, originOnScreen);
+ if (useTransBlit) {
+ dest->transBlitFrom(source, areaToRedraw, originOnScreen);
+ } else {
+ dest->simpleBlitFrom(source, areaToRedraw, originOnScreen);
+ }
}
}
}
diff --git a/engines/mediastation/graphics.h b/engines/mediastation/graphics.h
index 5a8c08fa1b3..1d7e8c533a6 100644
--- a/engines/mediastation/graphics.h
+++ b/engines/mediastation/graphics.h
@@ -140,7 +140,8 @@ public:
const PixMapImage *image,
double dissolveFactor,
DisplayContext *displayContext,
- Graphics::ManagedSurface *destinationImage = nullptr);
+ Graphics::ManagedSurface *destinationImage = nullptr,
+ bool useTransBlit = false);
void imageDeltaBlit(
Common::Point deltaFramePos,
@@ -185,7 +186,8 @@ private:
Graphics::ManagedSurface *dest,
const Common::Point &destLocation,
const Graphics::ManagedSurface &source,
- const Common::Array<Common::Rect> &dirtyRegion);
+ const Common::Array<Common::Rect> &dirtyRegion,
+ bool useTransBlit = false);
void rleBlitRectsClip(
Graphics::ManagedSurface *dest,
const Common::Point &destLocation,
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index df59b46fc70..65424fc87cf 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -221,10 +221,11 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "IsPlaying/SetMultipleStreams";
case kSetDissolveFactorMethod:
return "SetDissolveFactor";
+ // NOTE: IDs 0xD2 and 0xD3 are double-assigned between hotspot, stage, and text methods.
case kMouseActivateMethod:
- return "MouseActivate";
+ return "Activate";
case kMouseDeactivateMethod:
- return "MouseDeactivate";
+ return "Deactivate";
case kGetLeftXMethod:
return "GetLeftX";
case kGetTopYMethod:
@@ -233,8 +234,9 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "TriggerAbsXPosition";
case kTriggerAbsYPositionMethod:
return "TriggerAbsYPosition";
+ // NOTE: ID 0x173 is double-assigned between hotspot and text methods.
case kIsActiveMethod:
- return "IsActive";
+ return "IsActive/IsEditable";
case kGetWidthMethod:
return "GetWidth";
case kGetHeightMethod:
@@ -325,8 +327,12 @@ const char *builtInMethodToStr(BuiltInMethod method) {
case kYViewportPositionMethod:
return "YViewportPosition";
case kPanToMethod:
- return "PanTo";
- case kClearToPaletteMethod:
+ return "PanTo/CanvasClearToTransparency";
+ case kCanvasStampImageMethod:
+ return "CanvasStampImage";
+ case kCanvasCopyScreenToMethod:
+ return "CanvasCopyScreenTo";
+ case kCanvasClearToPaletteMethod:
return "ClearToPalette";
case kStreamMovieMoveProxyToStageMethod:
return "MoveProxyToStage";
@@ -356,12 +362,52 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "SetTotalSteps";
case kPathSetStepRateMethod:
return "SetStepRate";
- case kTextMethod:
+ case kTextGetFontActorMethod:
+ return "GetFontActor";
+ case kTextSetFontActorMethod:
+ return "SetFontActor";
+ case kTextGetTextMethod:
return "Text";
- case kSetTextMethod:
+ case kTextSetTextMethod:
return "SetText";
- case kSetMaximumTextLengthMethod:
- return "SetMaximumTextLength";
+ case kTextGetMaxLengthMethod:
+ return "GetMaxLength";
+ case kTextSetMaxLengthMethod:
+ return "SetMaxLength";
+ case kGetLastPressedCharCodeMethod:
+ return "GetLastPressedCharCode";
+ case kTextGetCursorPositionMethod:
+ return "GetCursorPosition";
+ case kTextSetCursorPositionMethod:
+ return "SetCursorPosition";
+ case kTextGetJustificationMethod:
+ return "GetJustification";
+ case kTextSetJustificationMethod:
+ return "SetJustification";
+ case kTextGetPositionMethod:
+ return "GetPosition";
+ case kTextSetPositionMethod:
+ return "SetPosition";
+ case kTextGetConstrainToWidthMethod:
+ return "GetConstrainToWidth";
+ case kTextSetConstrainToWidthMethod:
+ return "SetConstrainToWidth";
+ case kTextGetCursorIsVisibleMethod:
+ return "GetCursorIsVisible";
+ case kTextSetCursorIsVisibleMethod:
+ return "SetCursorIsVisible";
+ case kTextGetOverwriteModeMethod:
+ return "GetOverwriteMode";
+ case kTextSetOverwriteModeMethod:
+ return "SetOverwriteMode";
+ case kTextGetTranslatedCharCode:
+ return "GetTranslatedCharCode";
+ case kTextAddAcceptedCharsMethod:
+ return "AddAcceptedChars";
+ case kTextIsCharacterAcceptedMethod:
+ return "IsCharacterAccepted";
+ case kTextEnableDisableCharacterMethod:
+ return "EnableDisableCharacter";
case kAppendMethod:
return "Append";
case kApplyMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 6e356359482..3a23a7bbffe 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -209,7 +209,10 @@ enum BuiltInMethod {
kPanToMethod = 0x172,
// CANVAS METHODS.
- kClearToPaletteMethod = 0x17B,
+ kCanvasClearToTransparencyMethod = 0x172,
+ kCanvasStampImageMethod = 0x179,
+ kCanvasCopyScreenToMethod = 0x17A,
+ kCanvasClearToPaletteMethod = 0x17B,
// DOCUMENT METHODS.
kDocumentBranchToScreenMethod = 0xC9,
@@ -230,9 +233,32 @@ enum BuiltInMethod {
kPathSetStepRateMethod = 0xf5,
// TEXT METHODS.
- kTextMethod = 0x122,
- kSetTextMethod = 0x123,
- kSetMaximumTextLengthMethod = 0x125,
+ kTextSetEditableMethod = 0xD2,
+ kTextSetNonEditableMethod = 0xD3,
+ kTextGetFontActorMethod = 0x120,
+ kTextSetFontActorMethod = 0x121,
+ kTextGetTextMethod = 0x122,
+ kTextSetTextMethod = 0x123,
+ kTextGetMaxLengthMethod = 0x124,
+ kTextSetMaxLengthMethod = 0x125,
+ kGetLastPressedCharCodeMethod = 0x126,
+ kTextGetCursorPositionMethod = 0x127,
+ kTextSetCursorPositionMethod = 0x128,
+ kTextGetJustificationMethod = 0x14b,
+ kTextSetJustificationMethod = 0x14c,
+ kTextGetPositionMethod = 0x14d,
+ kTextSetPositionMethod = 0x14e,
+ kTextGetConstrainToWidthMethod = 0x150,
+ kTextSetConstrainToWidthMethod = 0x151,
+ kTextGetCursorIsVisibleMethod = 0x152,
+ kTextSetCursorIsVisibleMethod = 0x153,
+ kTextGetOverwriteModeMethod = 0x154,
+ kTextSetOverwriteModeMethod = 0x155,
+ kTextGetTranslatedCharCode = 0x156,
+ kTextAddAcceptedCharsMethod = 0x157,
+ kTextIsCharacterAcceptedMethod = 0x158,
+ kTextEnableDisableCharacterMethod = 0x159,
+ kTextIsEditableMethod = 0x173,
// COLLECTION METHODS.
// These are arrays used in Media Script.
Commit: 34af4483c49b4e805f32547c9b780a8cd84e7eed
https://github.com/scummvm/scummvm/commit/34af4483c49b4e805f32547c9b780a8cd84e7eed
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T21:10:58-05:00
Commit Message:
MEDIASTATION: Enforce consistent switch breaks, rather than returns
Changed paths:
engines/mediastation/actors/hotspot.cpp
engines/mediastation/actors/image.cpp
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/sound.cpp
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/stage.cpp
engines/mediastation/actors/timer.cpp
engines/mediastation/mediascript/codechunk.cpp
diff --git a/engines/mediastation/actors/hotspot.cpp b/engines/mediastation/actors/hotspot.cpp
index ef859235a40..11eb4ac07f0 100644
--- a/engines/mediastation/actors/hotspot.cpp
+++ b/engines/mediastation/actors/hotspot.cpp
@@ -98,13 +98,13 @@ ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<Scrip
case kMouseActivateMethod: {
ARGCOUNTCHECK(0);
activate();
- return returnValue;
+ break;
}
case kMouseDeactivateMethod: {
ARGCOUNTCHECK(0);
deactivate();
- return returnValue;
+ break;
}
@@ -115,32 +115,33 @@ ScriptValue HotspotActor::callMethod(BuiltInMethod methodId, Common::Array<Scrip
Common::Point pointToCheck(xToCheck, yToCheck);
bool pointIsInside = isInside(pointToCheck);
returnValue.setToBool(pointIsInside);
- return returnValue;
+ break;
}
case kTriggerAbsXPositionMethod: {
ARGCOUNTCHECK(0);
double mouseX = static_cast<double>(g_system->getEventManager()->getMousePos().x);
returnValue.setToFloat(mouseX);
- return returnValue;
+ break;
}
case kTriggerAbsYPositionMethod: {
ARGCOUNTCHECK(0);
double mouseY = static_cast<double>(g_system->getEventManager()->getMousePos().y);
returnValue.setToFloat(mouseY);
- return returnValue;
+ break;
}
case kIsActiveMethod: {
ARGCOUNTCHECK(0);
returnValue.setToBool(_isActive);
- return returnValue;
+ break;
}
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
}
+ return returnValue;
}
uint16 HotspotActor::findActorToAcceptMouseEvents(
diff --git a/engines/mediastation/actors/image.cpp b/engines/mediastation/actors/image.cpp
index 48d405fd521..593df22a2cc 100644
--- a/engines/mediastation/actors/image.cpp
+++ b/engines/mediastation/actors/image.cpp
@@ -76,18 +76,19 @@ ScriptValue ImageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
case kSpatialShowMethod: {
ARGCOUNTCHECK(0);
spatialShow();
- return returnValue;
+ break;
}
case kSpatialHideMethod: {
ARGCOUNTCHECK(0);
spatialHide();
- return returnValue;
+ break;
}
default:
return SpatialEntity::callMethod(methodId, args);
}
+ return returnValue;
}
void ImageActor::draw(DisplayContext &displayContext) {
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index 2284fc82213..1c9630177da 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -193,7 +193,7 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
}
}
- return returnValue;
+ break;
}
case kSpatialHideMethod: {
@@ -214,19 +214,19 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
}
}
- return returnValue;
+ break;
}
case kTimePlayMethod: {
ARGCOUNTCHECK(0);
timePlay();
- return returnValue;
+ break;
}
case kTimeStopMethod: {
ARGCOUNTCHECK(0);
timeStop();
- return returnValue;
+ break;
}
case kStreamMovieSetProxyZIndex: {
@@ -240,7 +240,7 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
}
- return returnValue;
+ break;
}
case kStreamMovieGetProxyZIndex: {
@@ -253,27 +253,27 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
}
- return returnValue;
+ break;
}
case kIsPlayingMethod: {
ARGCOUNTCHECK(0);
returnValue.setToBool(_isPlaying);
- return returnValue;
+ break;
}
case kGetLeftXMethod: {
ARGCOUNTCHECK(0);
double left = static_cast<double>(_boundingBox.left);
returnValue.setToFloat(left);
- return returnValue;
+ break;
}
case kGetTopYMethod: {
ARGCOUNTCHECK(0);
double top = static_cast<double>(_boundingBox.top);
returnValue.setToFloat(top);
- return returnValue;
+ break;
}
case kStreamMovieMoveProxyToStageMethod: {
@@ -284,18 +284,18 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
if (proxy == nullptr) {
warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
- return returnValue;
+ break;
}
StageActor *parentStage = static_cast<StageActor *>(g_engine->getActorByIdAndType(targetStageId, kActorTypeStage));
if (parentStage == nullptr) {
warning("[%s] %s: Stream movie proxy with script ID %s has null parent stage",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
- return returnValue;
+ break;
}
proxy->getParentStage()->removeChildSpatialEntity(proxy);
parentStage->addChildSpatialEntity(proxy);
- return returnValue;
+ break;
}
case kStreamMovieMoveProxyToRootStageMethod: {
@@ -306,7 +306,7 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
if (proxy == nullptr) {
warning("[%s] %s: Stream movie proxy with script ID %s doesn't exist",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
- return returnValue;
+ break;
}
RootStage *rootStage = g_engine->getRootStage();
@@ -314,17 +314,18 @@ ScriptValue StreamMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
if (sourceStage == nullptr) {
warning("[%s] %s: Stream movie proxy with script ID %s has null parent stage",
debugName(), __func__, g_engine->formatParamTokenName(scriptId).c_str());
- return returnValue;
+ break;
}
sourceStage->removeChildSpatialEntity(proxy);
rootStage->addChildSpatialEntity(proxy);
- return returnValue;
+ break;
}
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
}
+ return returnValue;
}
void StreamMovieActor::timePlay() {
diff --git a/engines/mediastation/actors/sound.cpp b/engines/mediastation/actors/sound.cpp
index 15d1d5b699a..70350092eab 100644
--- a/engines/mediastation/actors/sound.cpp
+++ b/engines/mediastation/actors/sound.cpp
@@ -95,22 +95,22 @@ ScriptValue SoundActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
// Since the engine is currently flagging errors on unimplemented
// methods for easier debugging, a no-op is used here to avoid the error.
ARGCOUNTCHECK(0);
- return returnValue;
+ break;
case kTimePlayMethod:
ARGCOUNTCHECK(0);
start();
- return returnValue;
+ break;
case kTimeStopMethod:
ARGCOUNTCHECK(0);
stop();
- return returnValue;
+ break;
case kPauseMethod:
ARGCOUNTCHECK(0);
pause();
- return returnValue;
+ break;
case kResumeMethod: {
ARGCOUNTRANGE(0, 1);
@@ -119,20 +119,21 @@ ScriptValue SoundActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
shouldRestart = args[0].asBool();
}
resume(shouldRestart);
- return returnValue;
+ break;
}
case kIsPlayingMethod:
returnValue.setToBool(_playState == kSoundPlaying || _playState == kSoundPaused);
- return returnValue;
+ break;
case kIsPausedMethod:
returnValue.setToBool(_playState == kSoundPaused);
- return returnValue;
+ break;
default:
- return Actor::callMethod(methodId, args);
+ returnValue = Actor::callMethod(methodId, args);
}
+ return returnValue;
}
void SoundActor::start() {
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index 361a058e667..d312e745adc 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -121,31 +121,31 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
case kSpatialShowMethod: {
ARGCOUNTCHECK(0);
setVisibility(true);
- return returnValue;
+ break;
}
case kSpatialHideMethod: {
ARGCOUNTCHECK(0);
setVisibility(false);
- return returnValue;
+ break;
}
case kTimePlayMethod: {
ARGCOUNTCHECK(0);
play();
- return returnValue;
+ break;
}
case kTimeStopMethod: {
ARGCOUNTCHECK(0);
stop();
- return returnValue;
+ break;
}
case kMovieResetMethod: {
ARGCOUNTCHECK(0);
setCurrentFrameToInitial();
- return returnValue;
+ break;
}
case kSetCurrentClipMethod: {
@@ -155,7 +155,7 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
clipId = args[0].asParamToken();
}
setCurrentClip(clipId);
- return returnValue;
+ break;
}
case kIncrementFrameMethod: {
@@ -171,7 +171,7 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
setCurrentFrameToInitial();
}
}
- return returnValue;
+ break;
}
case kDecrementFrameMethod: {
@@ -186,22 +186,23 @@ ScriptValue SpriteMovieActor::callMethod(BuiltInMethod methodId, Common::Array<S
setCurrentFrameToFinal();
}
}
- return returnValue;
+ break;
}
case kGetCurrentClipIdMethod: {
returnValue.setToParamToken(_activeClip.id);
- return returnValue;
+ break;
}
case kIsPlayingMethod: {
returnValue.setToBool(_isPlaying);
- return returnValue;
+ break;
}
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
}
+ return returnValue;
}
bool SpriteMovieActor::activateNextFrame() {
diff --git a/engines/mediastation/actors/stage.cpp b/engines/mediastation/actors/stage.cpp
index 7c9e0b79072..65cdf74c976 100644
--- a/engines/mediastation/actors/stage.cpp
+++ b/engines/mediastation/actors/stage.cpp
@@ -322,7 +322,7 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
addActorToStage(actorId);
- return returnValue;
+ break;
}
case kRemoveActorFromStageMethod:
@@ -330,7 +330,7 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
ARGCOUNTCHECK(1);
uint actorId = args[0].asActorId();
removeActorFromStage(actorId);
- return returnValue;
+ break;
}
case kSetBoundsMethod: {
@@ -341,28 +341,29 @@ ScriptValue StageActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
int16 height = static_cast<int16>(args[3].asFloat());
Common::Rect newBounds(Common::Point(x, y), width, height);
setBounds(newBounds);
- return returnValue;
+ break;
}
case kStageSetSizeMethod:
ARGCOUNTCHECK(2);
_boundingBox.setWidth(static_cast<int16>(args[0].asFloat()));
_boundingBox.setHeight(static_cast<int16>(args[1].asFloat()));
- return returnValue;
+ break;
case kStageGetWidthMethod:
ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.width());
- return returnValue;
+ break;
case kStageGetHeightMethod:
ARGCOUNTCHECK(0);
returnValue.setToFloat(_boundingBox.height());
- return returnValue;
+ break;
default:
- return SpatialEntity::callMethod(methodId, args);
+ returnValue = SpatialEntity::callMethod(methodId, args);
}
+ return returnValue;
}
void StageActor::addChildSpatialEntity(SpatialEntity *entity) {
diff --git a/engines/mediastation/actors/timer.cpp b/engines/mediastation/actors/timer.cpp
index 9493ab43efa..52adc2d9a51 100644
--- a/engines/mediastation/actors/timer.cpp
+++ b/engines/mediastation/actors/timer.cpp
@@ -33,24 +33,25 @@ ScriptValue TimerActor::callMethod(BuiltInMethod methodId, Common::Array<ScriptV
case kTimePlayMethod: {
ARGCOUNTCHECK(0);
timePlay();
- return returnValue;
+ break;
}
case kTimeStopMethod: {
ARGCOUNTCHECK(0);
timeStop();
- return returnValue;
+ break;
}
case kIsPlayingMethod: {
ARGCOUNTCHECK(0);
returnValue.setToBool(_isPlaying);
- return returnValue;
+ break;
}
default:
- return Actor::callMethod(methodId, args);
+ returnValue = Actor::callMethod(methodId, args);
}
+ return returnValue;
}
void TimerActor::timePlay() {
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index dda70b3ed94..a7d41eae002 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -208,14 +208,14 @@ ScriptValue CodeChunk::evaluateValue() {
}
debugC(5, kDebugScript, "%d ", b);
returnValue.setToBool(b == 1 ? true : false);
- return returnValue;
+ break;
}
case kOperandTypeFloat: {
double f = _bytecode->readTypedDouble();
debugC(5, kDebugScript, "%f ", f);
returnValue.setToFloat(f);
- return returnValue;
+ break;
}
case kOperandTypeInt: {
@@ -223,7 +223,7 @@ ScriptValue CodeChunk::evaluateValue() {
debugC(5, kDebugScript, "%d ", i);
// Ints are stored internally as doubles.
returnValue.setToFloat(static_cast<double>(i));
- return returnValue;
+ break;
}
case kOperandTypeString: {
@@ -232,7 +232,7 @@ ScriptValue CodeChunk::evaluateValue() {
Common::String string = _bytecode->readString('\0', size);
debugC(5, kDebugScript, "%s ", string.c_str());
returnValue.setToString(string);
- return returnValue;
+ break;
}
case kOperandTypeParamToken: {
@@ -240,7 +240,7 @@ ScriptValue CodeChunk::evaluateValue() {
Common::String tokenName = g_engine->formatParamTokenName(literal);
debugC(5, kDebugScript, "%s ", tokenName.c_str());
returnValue.setToParamToken(literal);
- return returnValue;
+ break;
}
case kOperandTypeActorId: {
@@ -248,19 +248,19 @@ ScriptValue CodeChunk::evaluateValue() {
Common::String actorName = g_engine->formatActorName(actorId, true);
debugC(5, kDebugScript, "%s ", actorName.c_str());
returnValue.setToActorId(actorId);
- return returnValue;
+ break;
}
case kOperandTypeTime: {
double d = _bytecode->readTypedTime();
debugC(5, kDebugScript, "%f ", d);
returnValue.setToTime(d);
- return returnValue;
+ break;
}
case kOperandTypeVariable: {
returnValue = ScriptValue(_bytecode);
- return returnValue;
+ break;
}
case kOperandTypeFunctionId: {
@@ -269,19 +269,20 @@ ScriptValue CodeChunk::evaluateValue() {
Common::String functionName = g_engine->formatFunctionName(functionId);
debugC(5, kDebugScript, "%s ", functionName.c_str());
returnValue.setToFunctionId(functionId);
- return returnValue;
+ break;
}
case kOperandTypeMethodId: {
BuiltInMethod methodId = static_cast<BuiltInMethod>(_bytecode->readTypedUint16());
debugC(5, kDebugScript, "%s ", builtInMethodToStr(methodId));
returnValue.setToMethodId(methodId);
- return returnValue;
+ break;
}
default:
error("%s: Got unknown ScriptValue type %s (%d)", __func__, operandTypeToStr(operandType), static_cast<uint>(operandType));
}
+ return returnValue;
}
ScriptValue CodeChunk::evaluateVariable() {
@@ -531,7 +532,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
// It seems to be valid to call a method on a null actor ID, in
// which case nothing happens. Still issue warning for traceability.
warning("%s: Attempt to call method %s (%d) on null actor ID", __func__, builtInMethodToStr(method), static_cast<uint>(method));
- return returnValue;
+ break;
} else {
// This is a regular actor that we can process directly.
uint actorId = target.asActorId();
@@ -540,14 +541,14 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
error("[%s] %s: Actor not loaded", g_engine->formatActorName(target.asActorId()).c_str(), __func__);
}
returnValue = targetActor->callMethod(method, args);
- return returnValue;
+ break;
}
}
case kScriptValueTypeCollection: {
Common::SharedPtr<Collection> collection = target.asCollection();
returnValue = collection->callMethod(method, args);
- return returnValue;
+ break;
}
default:
@@ -555,6 +556,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
builtInMethodToStr(method), static_cast<uint>(method),
scriptValueTypeToStr(target.getType()), static_cast<uint>(target.getType()));
}
+ return returnValue;
}
void CodeChunk::evaluateDeclareLocals() {
Commit: 9d426237f60a1e908110bceb1bb43885319d2ed0
https://github.com/scummvm/scummvm/commit/9d426237f60a1e908110bceb1bb43885319d2ed0
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T21:10:58-05:00
Commit Message:
MEDIASTATION: Enforce correct copy semantics for script values/variables
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/codechunk.h
engines/mediastation/mediascript/collection.cpp
engines/mediastation/mediascript/function.cpp
engines/mediastation/mediascript/scriptvalue.cpp
engines/mediastation/mediascript/scriptvalue.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index a7d41eae002..3e6fda47f52 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -91,6 +91,29 @@ ScriptValue CodeChunk::evaluateExpression() {
return returnValue;
}
+void CodeChunk::evaluateLValue(ScriptValue *&targetPtr) {
+ // Evaluate an expression as an lvalue (something that can be modified in place).
+ ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
+
+ switch (expressionType) {
+ case kExpressionTypeVariable:
+ // Make target point directly to the variable. This permits modifications to the variable
+ // (like adding to a collection) to persist in the original variable.
+ targetPtr = readAndReturnVariable();
+ break;
+
+ case kExpressionTypeValue:
+ case kExpressionTypeOperation:
+ // For values/operations, just fill in the temporary value the caller passed to us.
+ // This means modifications to the evaluated expression will not be persisted like variables.
+ *targetPtr = evaluateExpression(expressionType);
+ break;
+
+ default:
+ error("%s: Unexpected expression type %s", __func__, expressionTypeToStr(expressionType));
+ }
+}
+
ScriptValue CodeChunk::evaluateExpression(ExpressionType expressionType) {
debugCN(5, kDebugScript, "(%s) ", expressionTypeToStr(expressionType));
@@ -517,7 +540,10 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
debugC(5, kDebugScript, "%s (%d params)", builtInMethodToStr(method), paramCount);
debugCN(5, kDebugScript, " Self: ");
- ScriptValue target = evaluateExpression();
+ // Evaluate target as an lvalue to get a pointer to the actual variable if there is one.
+ ScriptValue methodCallTarget;
+ ScriptValue *methodCallTargetPtr = &methodCallTarget;
+ evaluateLValue(methodCallTargetPtr);
Common::Array<ScriptValue> args;
for (uint i = 0; i < paramCount; i++) {
debugCN(5, kDebugScript, " Param %d: ", i);
@@ -526,19 +552,19 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
}
ScriptValue returnValue;
- switch (target.getType()) {
+ switch (methodCallTargetPtr->getType()) {
case kScriptValueTypeActorId: {
- if (target.asActorId() == 0) {
+ if (methodCallTargetPtr->asActorId() == 0) {
// It seems to be valid to call a method on a null actor ID, in
// which case nothing happens. Still issue warning for traceability.
warning("%s: Attempt to call method %s (%d) on null actor ID", __func__, builtInMethodToStr(method), static_cast<uint>(method));
break;
} else {
// This is a regular actor that we can process directly.
- uint actorId = target.asActorId();
+ uint actorId = methodCallTargetPtr->asActorId();
Actor *targetActor = g_engine->getActorById(actorId);
if (targetActor == nullptr) {
- error("[%s] %s: Actor not loaded", g_engine->formatActorName(target.asActorId()).c_str(), __func__);
+ error("[%s] %s: Actor not loaded", g_engine->formatActorName(actorId).c_str(), __func__);
}
returnValue = targetActor->callMethod(method, args);
break;
@@ -546,7 +572,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
}
case kScriptValueTypeCollection: {
- Common::SharedPtr<Collection> collection = target.asCollection();
+ Collection *collection = methodCallTargetPtr->asCollection();
returnValue = collection->callMethod(method, args);
break;
}
@@ -554,7 +580,7 @@ ScriptValue CodeChunk::evaluateMethodCall(BuiltInMethod method, uint paramCount)
default:
error("%s: Attempt to call method %s (%d) on unimplemented value type %s (%d)", __func__,
builtInMethodToStr(method), static_cast<uint>(method),
- scriptValueTypeToStr(target.getType()), static_cast<uint>(target.getType()));
+ scriptValueTypeToStr(methodCallTargetPtr->getType()), static_cast<uint>(methodCallTargetPtr->getType()));
}
return returnValue;
}
diff --git a/engines/mediastation/mediascript/codechunk.h b/engines/mediastation/mediascript/codechunk.h
index cc0798d56c0..c9d497a564e 100644
--- a/engines/mediastation/mediascript/codechunk.h
+++ b/engines/mediastation/mediascript/codechunk.h
@@ -43,6 +43,7 @@ private:
ScriptValue evaluateExpression();
ScriptValue evaluateExpression(ExpressionType expressionType);
+ void evaluateLValue(ScriptValue *&targetPtr);
ScriptValue evaluateOperation();
ScriptValue evaluateValue();
ScriptValue evaluateVariable();
diff --git a/engines/mediastation/mediascript/collection.cpp b/engines/mediastation/mediascript/collection.cpp
index 4c38285ab14..77f639edcb2 100644
--- a/engines/mediastation/mediascript/collection.cpp
+++ b/engines/mediastation/mediascript/collection.cpp
@@ -28,6 +28,14 @@
namespace MediaStation {
ScriptValue Collection::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
+ // Debug print the collection contents.
+ debugC(7, kDebugScript, " COLLECTION: [");
+ for (uint i = 0; i < size(); i++) {
+ const ScriptValue &rhs = operator[](i);
+ debugC(7, kDebugScript, " %d of %d: %s", i, size(), rhs.getDebugString().c_str());
+ }
+ debugC(7, kDebugScript, " ]");
+
ScriptValue returnValue;
switch (methodId) {
case kAppendMethod:
@@ -190,7 +198,9 @@ int Collection::seek(const ScriptValue &lhs) {
const ScriptValue &rhs = operator[](i);
debugC(7, kDebugScript, "%s: %d of %d: Checking (%s) == (%s)",
__func__, i, size(), lhs.getDebugString().c_str(), rhs.getDebugString().c_str());
- if (lhs == rhs) {
+
+ // Only compare values if types match.
+ if (lhs.getType() == rhs.getType() && lhs == rhs) {
return i;
}
}
@@ -198,9 +208,11 @@ int Collection::seek(const ScriptValue &lhs) {
}
void Collection::jumble() {
- for (uint i = size() - 1; i > 0; --i) {
- uint j = g_engine->_randomSource.getRandomNumber(size() - 1);
- SWAP(operator[](i), operator[](j));
+ if (!empty()) {
+ 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/function.cpp b/engines/mediastation/mediascript/function.cpp
index 1f74591612d..1ec1719b17d 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -426,7 +426,7 @@ void FunctionManager::script_SetGammaCorrection(Common::Array<ScriptValue> &args
return;
}
- Common::SharedPtr<Collection> collection = args[0].asCollection();
+ Collection *collection = args[0].asCollection();
if (collection->size() != 3) {
warning("%s: Collection must contain exactly 3 elements, got %u", __func__, collection->size());
return;
@@ -456,7 +456,7 @@ void FunctionManager::script_GetDefaultGammaCorrection(Common::Array<ScriptValue
double red, green, blue;
g_engine->getDisplayManager()->getDefaultGammaValues(red, green, blue);
- Common::SharedPtr<Collection> collection = Common::SharedPtr<Collection>(new Collection());
+ Collection *collection = new Collection();
ScriptValue redValue;
redValue.setToFloat(red);
collection->push_back(redValue);
@@ -480,7 +480,7 @@ void FunctionManager::script_GetCurrentGammaCorrection(Common::Array<ScriptValue
double red, green, blue;
g_engine->getDisplayManager()->getGammaValues(red, green, blue);
- Common::SharedPtr<Collection> collection = Common::SharedPtr<Collection>(new Collection());
+ Collection *collection = new Collection();
ScriptValue redValue;
redValue.setToFloat(red);
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index 1f5010af0d5..c673cf2f9f5 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -76,7 +76,7 @@ ScriptValue::ScriptValue(ParameterReadStream *stream) {
case kScriptValueTypeCollection: {
uint totalItems = stream->readTypedUint16();
- Common::SharedPtr<Collection> collection(new Collection);
+ Collection *collection = new Collection;
for (uint i = 0; i < totalItems; i++) {
ScriptValue collectionValue = ScriptValue(stream);
collection->push_back(collectionValue);
@@ -102,6 +102,79 @@ ScriptValue::ScriptValue(ParameterReadStream *stream) {
}
}
+void ScriptValue::clearCollection() {
+ if (_collection) {
+ delete _collection;
+ _collection = nullptr;
+ }
+}
+
+ScriptValue::~ScriptValue() {
+ clearCollection();
+}
+
+ScriptValue::ScriptValue(const ScriptValue &other) {
+ clearCollection();
+ copyFrom(other);
+}
+
+void ScriptValue::operator=(const ScriptValue &other) {
+ clearCollection();
+ copyFrom(other);
+}
+
+void ScriptValue::copyFrom(const ScriptValue &other) {
+ _type = other._type;
+
+ switch (_type) {
+ case kScriptValueTypeEmpty:
+ // Nothing to copy for empty type.
+ break;
+
+ case kScriptValueTypeFloat:
+ _u.d = other._u.d;
+ break;
+
+ case kScriptValueTypeBool:
+ _u.b = other._u.b;
+ break;
+
+ case kScriptValueTypeTime:
+ _u.d = other._u.d;
+ break;
+
+ case kScriptValueTypeParamToken:
+ _u.paramToken = other._u.paramToken;
+ break;
+
+ case kScriptValueTypeActorId:
+ _u.actorId = other._u.actorId;
+ break;
+
+ case kScriptValueTypeString:
+ _string = other._string;
+ break;
+
+ case kScriptValueTypeCollection:
+ if (other._collection) {
+ // We always need a deep copy.
+ _collection = new Collection(*other._collection);
+ }
+ break;
+
+ case kScriptValueTypeFunctionId:
+ _u.functionId = other._u.functionId;
+ break;
+
+ case kScriptValueTypeMethodId:
+ _u.methodId = other._u.methodId;
+ break;
+
+ default:
+ error("%s: Got unknown script value type %s", __func__, scriptValueTypeToStr(_type));
+ }
+}
+
void ScriptValue::setToFloat(uint i) {
setToFloat(static_cast<double>(i));
}
@@ -193,12 +266,13 @@ Common::String ScriptValue::asString() const {
}
}
-void ScriptValue::setToCollection(Common::SharedPtr<Collection> collection) {
+void ScriptValue::setToCollection(Collection *collection) {
_type = kScriptValueTypeCollection;
+ clearCollection();
_collection = collection;
}
-Common::SharedPtr<Collection> ScriptValue::asCollection() const {
+Collection *ScriptValue::asCollection() const {
if (_type == kScriptValueTypeCollection) {
return _collection;
} else {
@@ -409,7 +483,7 @@ bool ScriptValue::compare(Opcode op, double left, double right) {
}
}
-bool ScriptValue::compare(Opcode op, Common::SharedPtr<Collection> left, Common::SharedPtr<Collection> right) {
+bool ScriptValue::compare(Opcode op, Collection *left, Collection *right) {
switch (op) {
case kOpcodeEquals:
return (left == right);
diff --git a/engines/mediastation/mediascript/scriptvalue.h b/engines/mediastation/mediascript/scriptvalue.h
index 7b4270fd88b..e247ec55f06 100644
--- a/engines/mediastation/mediascript/scriptvalue.h
+++ b/engines/mediastation/mediascript/scriptvalue.h
@@ -22,7 +22,6 @@
#ifndef MEDIASTATION_MEDIASCRIPT_SCRIPTVALUE_H
#define MEDIASTATION_MEDIASCRIPT_SCRIPTVALUE_H
-#include "common/ptr.h"
#include "common/str.h"
#include "mediastation/datafile.h"
@@ -35,8 +34,10 @@ class Actor;
class ScriptValue {
public:
- ScriptValue() : _type(kScriptValueTypeEmpty) {}
+ ScriptValue() : _type(kScriptValueTypeEmpty), _collection(nullptr) {}
ScriptValue(ParameterReadStream *stream);
+ ScriptValue(const ScriptValue &other);
+ ~ScriptValue();
ScriptValueType getType() const { return _type; }
@@ -61,8 +62,8 @@ public:
void setToString(const Common::String &string);
Common::String asString() const;
- void setToCollection(Common::SharedPtr<Collection> collection);
- Common::SharedPtr<Collection> asCollection() const;
+ void setToCollection(Collection *collection);
+ Collection *asCollection() const;
void setToFunctionId(uint functionId);
uint asFunctionId() const;
@@ -72,6 +73,7 @@ public:
Common::String getDebugString() const;
+ void operator=(const ScriptValue &other);
bool operator==(const ScriptValue &other) const;
bool operator!=(const ScriptValue &other) const;
bool operator<(const ScriptValue &other) const;
@@ -101,7 +103,9 @@ private:
BuiltInMethod methodId;
} _u;
Common::String _string;
- Common::SharedPtr<Collection> _collection;
+ Collection *_collection = nullptr;
+ void clearCollection();
+ void copyFrom(const ScriptValue &other);
static bool compare(Opcode op, const ScriptValue &left, const ScriptValue &right);
static bool compareEmptyValues(Opcode op);
@@ -109,7 +113,7 @@ private:
static bool compare(Opcode op, uint left, uint right);
static bool compare(Opcode op, bool left, bool right);
static bool compare(Opcode op, double left, double right);
- static bool compare(Opcode op, Common::SharedPtr<Collection> left, Common::SharedPtr<Collection> right);
+ static bool compare(Opcode op, Collection *left, Collection *right);
static ScriptValue evalMathOperation(Opcode op, const ScriptValue &left, const ScriptValue &right);
static double binaryMathOperation(Opcode op, double left, double right);
Commit: 2505aa1acb73464145ab7639df6ee17b1fb208af
https://github.com/scummvm/scummvm/commit/2505aa1acb73464145ab7639df6ee17b1fb208af
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T21:10:58-05:00
Commit Message:
MEDIASTATION: Properly pass script return values back
Changed paths:
engines/mediastation/mediascript/codechunk.cpp
engines/mediastation/mediascript/codechunk.h
diff --git a/engines/mediastation/mediascript/codechunk.cpp b/engines/mediastation/mediascript/codechunk.cpp
index 3e6fda47f52..e64b8841d9b 100644
--- a/engines/mediastation/mediascript/codechunk.cpp
+++ b/engines/mediastation/mediascript/codechunk.cpp
@@ -40,17 +40,15 @@ ScriptValue CodeChunk::executeNextBlock() {
debugC(7, kDebugScript, "%s: Entering new block (blockSize: %d, startingPos: %lld)",
__func__, blockSize, static_cast<long long int>(startingPos));
- ScriptValue returnValue;
ExpressionType expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
while (expressionType != kExpressionTypeEmpty && !_returnImmediately) {
- returnValue = evaluateExpression(expressionType);
+ evaluateExpression(expressionType);
expressionType = static_cast<ExpressionType>(_bytecode->readTypedUint16());
- if (expressionType == kExpressionTypeEmpty) {
- debugC(7, kDebugScript, "%s: Done executing block due to end of chunk", __func__);
- }
if (_returnImmediately) {
- debugC(7, kDebugScript, "%s: Done executing block due to script requesting immediate return", __func__);
+ debugC(7, kDebugScript, "%s: Done executing block due to script returning value (%s)", __func__, _returnValue.getDebugString().c_str());
+ } else if (expressionType == kExpressionTypeEmpty) {
+ debugC(7, kDebugScript, "%s: Done executing block due to end of chunk", __func__);
}
}
@@ -61,7 +59,8 @@ ScriptValue CodeChunk::executeNextBlock() {
error("%s: Expected to have read %d script bytes, actually read %d", __func__, blockSize, bytesRead);
}
}
- return returnValue;
+
+ return _returnValue;
}
void CodeChunk::skipNextBlock() {
@@ -193,7 +192,7 @@ ScriptValue CodeChunk::evaluateOperation() {
break;
case kOpcodeReturn:
- returnValue = evaluateReturn();
+ evaluateReturn();
break;
case kOpcodeReturnNoValue:
@@ -594,10 +593,9 @@ void CodeChunk::evaluateDeclareLocals() {
_locals = Common::Array<ScriptValue>(localVariableCount);
}
-ScriptValue CodeChunk::evaluateReturn() {
- ScriptValue returnValue = evaluateExpression();
+void CodeChunk::evaluateReturn() {
+ _returnValue = evaluateExpression();
_returnImmediately = true;
- return returnValue;
}
void CodeChunk::evaluateReturnNoValue() {
diff --git a/engines/mediastation/mediascript/codechunk.h b/engines/mediastation/mediascript/codechunk.h
index c9d497a564e..1959502227b 100644
--- a/engines/mediastation/mediascript/codechunk.h
+++ b/engines/mediastation/mediascript/codechunk.h
@@ -60,12 +60,13 @@ private:
ScriptValue evaluateMethodCall(bool isIndirect = false);
ScriptValue evaluateMethodCall(BuiltInMethod method, uint paramCount);
void evaluateDeclareLocals();
- ScriptValue evaluateReturn();
+ void evaluateReturn();
void evaluateReturnNoValue();
void evaluateWhileLoop();
static const uint MAX_LOOP_ITERATION_COUNT = 1000;
bool _returnImmediately = false;
+ ScriptValue _returnValue;
Common::Array<ScriptValue> _locals;
Common::Array<ScriptValue> *_args = nullptr;
ParameterReadStream *_bytecode = nullptr;
Commit: 1ca640151e36825c99c4813b5558f5d9c1ac6269
https://github.com/scummvm/scummvm/commit/1ca640151e36825c99c4813b5558f5d9c1ac6269
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2026-02-24T21:10:58-05:00
Commit Message:
MEDIASTATION: Make caching-related parameters consistently read
Changed paths:
engines/mediastation/actor.h
engines/mediastation/actors/image.cpp
engines/mediastation/actors/image.h
engines/mediastation/actors/movie.cpp
engines/mediastation/actors/movie.h
engines/mediastation/actors/sound.cpp
engines/mediastation/actors/sound.h
engines/mediastation/actors/sprite.cpp
engines/mediastation/actors/sprite.h
engines/mediastation/actors/text.cpp
engines/mediastation/actors/text.h
diff --git a/engines/mediastation/actor.h b/engines/mediastation/actor.h
index f4d6da1edb0..9c3b10a9084 100644
--- a/engines/mediastation/actor.h
+++ b/engines/mediastation/actor.h
@@ -81,12 +81,13 @@ enum ActorHeaderSectionType {
kActorHeaderZIndex = 0x001e,
kActorHeaderStartup = 0x001f,
kActorHeaderTransparency = 0x0020,
- kActorHeaderHasOwnSubfile = 0x0021,
+ kActorHeaderDiscardAfterUse = 0x0021,
kActorHeaderCursorResourceId = 0x0022,
kActorHeaderFrameRate = 0x0024,
kActorHeaderLoadType = 0x0032,
kActorHeaderSoundInfo = 0x0033,
- kActorHeaderMovieLoadType = 0x0037,
+ kActorHeaderCachingEnabled = 0x0034,
+ kActorHeaderInstallType = 0x0037,
kActorHeaderSpriteChunkCount = 0x03e8,
kActorHeaderPalette = 0x05aa,
kActorHeaderDissolveFactor = 0x05dc,
diff --git a/engines/mediastation/actors/image.cpp b/engines/mediastation/actors/image.cpp
index 593df22a2cc..a60ddf48314 100644
--- a/engines/mediastation/actors/image.cpp
+++ b/engines/mediastation/actors/image.cpp
@@ -47,7 +47,7 @@ void ImageActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
break;
case kActorHeaderLoadType:
- _loadType = chunk.readTypedByte();
+ _decompressImmediately = chunk.readTypedByte();
break;
case kActorHeaderX:
diff --git a/engines/mediastation/actors/image.h b/engines/mediastation/actors/image.h
index e005fedc9ee..7ff77a56d20 100644
--- a/engines/mediastation/actors/image.h
+++ b/engines/mediastation/actors/image.h
@@ -53,7 +53,7 @@ public:
private:
Common::SharedPtr<ImageAsset> _asset;
- uint _loadType = 0;
+ bool _decompressImmediately = false;
int _xOffset = 0;
int _yOffset = 0;
uint _actorReference = 0;
diff --git a/engines/mediastation/actors/movie.cpp b/engines/mediastation/actors/movie.cpp
index 1c9630177da..8050888b3c0 100644
--- a/engines/mediastation/actors/movie.cpp
+++ b/engines/mediastation/actors/movie.cpp
@@ -121,27 +121,21 @@ void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
break;
}
- case kActorHeaderMovieLoadType:
- _loadType = chunk.readTypedByte();
- break;
-
case kActorHeaderChannelIdent:
_channelIdent = chunk.readTypedChannelIdent();
registerWithStreamManager();
break;
- case kActorHeaderHasOwnSubfile: {
- bool hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
- if (!hasOwnSubfile) {
- error("%s: StreamMovieActor doesn't have a subfile", __func__);
- }
- break;
- }
case kActorHeaderStartup:
_isVisible = static_cast<bool>(chunk.readTypedByte());
break;
+ case kActorHeaderDiscardAfterUse:
+ // The original just reads this and throws it away.
+ chunk.readTypedByte();
+ break;
+
case kActorHeaderMovieAudioChannelIdent: {
ChannelIdent soundChannelIdent = chunk.readTypedChannelIdent();
_streamSound->setChannelIdent(soundChannelIdent);
@@ -149,6 +143,17 @@ void StreamMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
break;
}
+ case kActorHeaderCachingEnabled:
+ _shouldCache = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kActorHeaderInstallType:
+ // In the original, this controls behavior if the files are NOT installed. But since
+ // the "installation" is just copying from the CD-ROM, we can treat the game as always
+ // installed. So just throw away this value.
+ chunk.readTypedByte();
+ break;
+
case kActorHeaderMovieAnimationChannelIdent: {
ChannelIdent framesChannelIdent = chunk.readTypedChannelIdent();
_streamFrames->setChannelIdent(framesChannelIdent);
diff --git a/engines/mediastation/actors/movie.h b/engines/mediastation/actors/movie.h
index fd017c1641b..bfeaef3138d 100644
--- a/engines/mediastation/actors/movie.h
+++ b/engines/mediastation/actors/movie.h
@@ -147,7 +147,7 @@ private:
uint _chunkCount = 0;
double _frameRate = 0;
- uint _loadType = 0;
+ bool _shouldCache = false;
bool _isPlaying = false;
bool _hasStill = false;
diff --git a/engines/mediastation/actors/sound.cpp b/engines/mediastation/actors/sound.cpp
index 70350092eab..2e0daff3601 100644
--- a/engines/mediastation/actors/sound.cpp
+++ b/engines/mediastation/actors/sound.cpp
@@ -51,16 +51,25 @@ void SoundActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
registerWithStreamManager();
break;
- case kActorHeaderHasOwnSubfile:
- _hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
+ case kActorHeaderDiscardAfterUse:
+ _discardAfterUse = static_cast<bool>(chunk.readTypedByte());
break;
case kActorHeaderSoundInfo:
_sequence.readParameters(chunk);
break;
- case kActorHeaderMovieLoadType:
- _loadType = chunk.readTypedByte();
+ case kActorHeaderCachingEnabled:
+ // This controls some caching behavior in the original, but since that is not currently
+ // implemented here, just throw it away.
+ chunk.readTypedByte();
+ break;
+
+ case kActorHeaderInstallType:
+ // In the original, this controls behavior if the files are NOT installed. But since
+ // the "installation" is just copying from the CD-ROM, we can treat the game as always
+ // installed. So just throw away this value.
+ chunk.readTypedByte();
break;
default:
diff --git a/engines/mediastation/actors/sound.h b/engines/mediastation/actors/sound.h
index 23754a26a0c..df1bd2e1221 100644
--- a/engines/mediastation/actors/sound.h
+++ b/engines/mediastation/actors/sound.h
@@ -50,8 +50,7 @@ public:
private:
ImtStreamFeed *_streamFeed = nullptr;
bool _isLoadedFromChunk = false;
- uint _loadType = 0;
- bool _hasOwnSubfile = false;
+ bool _discardAfterUse = false;
SoundPlayState _playState = kSoundStopped;
AudioSequence _sequence;
diff --git a/engines/mediastation/actors/sprite.cpp b/engines/mediastation/actors/sprite.cpp
index d312e745adc..79701b8f20f 100644
--- a/engines/mediastation/actors/sprite.cpp
+++ b/engines/mediastation/actors/sprite.cpp
@@ -56,16 +56,16 @@ void SpriteMovieActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramT
_asset = Common::SharedPtr<SpriteAsset>(new SpriteAsset);
break;
+ case kActorHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
case kActorHeaderFrameRate:
_frameRate = static_cast<uint32>(chunk.readTypedDouble());
break;
case kActorHeaderLoadType:
- _loadType = chunk.readTypedByte();
- break;
-
- case kActorHeaderStartup:
- _isVisible = static_cast<bool>(chunk.readTypedByte());
+ _decompressImmediately = static_cast<bool>(chunk.readTypedByte());
break;
case kActorHeaderSpriteChunkCount:
diff --git a/engines/mediastation/actors/sprite.h b/engines/mediastation/actors/sprite.h
index 9c026931073..0311f82d115 100644
--- a/engines/mediastation/actors/sprite.h
+++ b/engines/mediastation/actors/sprite.h
@@ -81,7 +81,7 @@ private:
const uint DEFAULT_FORWARD_CLIP_ID = 0x4B0;
const uint DEFAULT_BACKWARD_CLIP_ID = 0x4B1;
- uint _loadType = 0;
+ bool _decompressImmediately = false;
uint _frameRate = 0;
uint _actorReference = 0;
Common::HashMap<uint, SpriteMovieClip> _clips;
diff --git a/engines/mediastation/actors/text.cpp b/engines/mediastation/actors/text.cpp
index 346d474db80..8845c0f3c6d 100644
--- a/engines/mediastation/actors/text.cpp
+++ b/engines/mediastation/actors/text.cpp
@@ -31,7 +31,8 @@ void TextActor::readParameter(Chunk &chunk, ActorHeaderSectionType paramType) {
break;
case kActorHeaderLoadType:
- _loadType = chunk.readTypedByte();
+ // The original doesn't seem to use this, so we'll throw it away.
+ chunk.readTypedByte();
break;
case kActorHeaderFontActorId: {
diff --git a/engines/mediastation/actors/text.h b/engines/mediastation/actors/text.h
index b53ffbdd8ba..e5c2a12b155 100644
--- a/engines/mediastation/actors/text.h
+++ b/engines/mediastation/actors/text.h
@@ -58,7 +58,6 @@ public:
private:
static const uint CURSOR_CHAR_ID = 0x104;
bool _isEditable = false;
- uint _loadType = 0;
Common::String _text;
uint _maxLength = 0;
FontActor *_fontActor = nullptr;
More information about the Scummvm-git-logs
mailing list