[Scummvm-git-logs] scummvm master -> 6cb99deffe1d4e09726f5e30e70c3fae8214747b
npjg
noreply at scummvm.org
Sat Jun 14 15:07:27 UTC 2025
This automated email contains information about 8 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
fdefc58d3e MEDIASTATION: Don't implicitly cast point members to unsigned
8a5282dc23 MEDIASTATION: Introduce SpatialEntity class for on-screen assets
06c02db208 MEDIASTATION: Store asset-specific fields in the assets themselves
a7aae9b0b1 MEDIASTATION: Factor audio sequences into their own class
e56caa42fe MEDIASTATION: Don't keep separate "active" assets list
cb66b27c9f MEDIASTATION: Improve Sprite rendering accuracy
65578a4510 MEDIASTATION: Improve Movie rendering accuracy
6cb99deffe MEDIASTATION: JANITORIAL: FIx whitespace & namespace comments
Commit: fdefc58d3e400237e9fba35e9e950c79b777ac24
https://github.com/scummvm/scummvm/commit/fdefc58d3e400237e9fba35e9e950c79b777ac24
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:06:58-04:00
Commit Message:
MEDIASTATION: Don't implicitly cast point members to unsigned
Changed paths:
engines/mediastation/bitmap.cpp
engines/mediastation/bitmap.h
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index 3d0f1f889ee..389c03555b4 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -44,8 +44,8 @@ bool BitmapHeader::isCompressed() {
Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) :
_bitmapHeader(bitmapHeader) {
// The header must be constructed beforehand.
- uint16 width = _bitmapHeader->_dimensions.x;
- uint16 height = _bitmapHeader->_dimensions.y;
+ int16 width = _bitmapHeader->_dimensions.x;
+ int16 height = _bitmapHeader->_dimensions.y;
_surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8());
_surface.setTransparentColor(0);
uint8 *pixels = (uint8 *)_surface.getPixels();
@@ -70,11 +70,11 @@ Bitmap::~Bitmap() {
_bitmapHeader = nullptr;
}
-uint16 Bitmap::width() {
+int16 Bitmap::width() {
return _bitmapHeader->_dimensions.x;
}
-uint16 Bitmap::height() {
+int16 Bitmap::height() {
return _bitmapHeader->_dimensions.y;
}
@@ -104,9 +104,9 @@ void Bitmap::decompress(Chunk &chunk) {
// size_t transparencyRunTopYCoordinate = 0;
// size_t transparencyRunLeftXCoordinate = 0;
bool imageFullyRead = false;
- size_t currentYCoordinate = 0;
+ int16 currentYCoordinate = 0;
while (currentYCoordinate < height()) {
- size_t currentXCoordinate = 0;
+ int16 currentXCoordinate = 0;
bool readingTransparencyRun = false;
while (true) {
byte operation = chunk.readByte();
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index a1cb1add861..8ef973a6c2a 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -55,8 +55,8 @@ public:
Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader);
virtual ~Bitmap();
- uint16 width();
- uint16 height();
+ int16 width();
+ int16 height();
Graphics::ManagedSurface _surface;
private:
Commit: 8a5282dc233cfd27ed07a6e3c653126fd51ed3ff
https://github.com/scummvm/scummvm/commit/8a5282dc233cfd27ed07a6e3c653126fd51ed3ff
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Introduce SpatialEntity class for on-screen assets
Following the pattern and name of the original.
Changed paths:
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/canvas.cpp
engines/mediastation/assets/canvas.h
engines/mediastation/assets/font.cpp
engines/mediastation/assets/font.h
engines/mediastation/assets/hotspot.cpp
engines/mediastation/assets/hotspot.h
engines/mediastation/assets/image.cpp
engines/mediastation/assets/image.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/palette.cpp
engines/mediastation/assets/palette.h
engines/mediastation/assets/path.cpp
engines/mediastation/assets/screen.cpp
engines/mediastation/assets/screen.h
engines/mediastation/assets/sound.cpp
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/assets/text.cpp
engines/mediastation/assets/text.h
engines/mediastation/assets/timer.cpp
engines/mediastation/context.cpp
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
engines/mediastation/mediastation.cpp
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index 24cb7a0e2e4..e8f26f8f285 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -30,6 +30,10 @@ Asset::~Asset() {
_header = nullptr;
}
+ScriptValue Asset::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
+ error("Got unimplemented method call %d (%s)", static_cast<uint>(methodId), builtInMethodToStr(methodId));
+}
+
void Asset::readChunk(Chunk &chunk) {
error("Asset::readChunk(): Chunk reading for asset type 0x%x is not implemented", static_cast<uint>(_header->_type));
}
@@ -38,18 +42,6 @@ void Asset::readSubfile(Subfile &subfile, Chunk &chunk) {
error("Asset::readSubfile(): Subfile reading for asset type 0x%x is not implemented", static_cast<uint>(_header->_type));
}
-AssetType Asset::type() const {
- return _header->_type;
-}
-
-int Asset::zIndex() const {
- return _header->_zIndex;
-}
-
-Common::Rect Asset::getBbox() const {
- return _header->_boundingBox;
-}
-
void Asset::setActive() {
_isActive = true;
_startTime = g_system->getMillis();
@@ -73,7 +65,7 @@ void Asset::processTimeEventHandlers() {
uint currentTime = g_system->getMillis();
const Common::Array<EventHandler *> &_timeHandlers = _header->_eventHandlers.getValOrDefault(kTimerEvent);
for (EventHandler *timeEvent : _timeHandlers) {
- // Indeed float, not time.
+ // Indeed float, not time.
double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
bool timeEventAlreadyProcessed = timeEventInMilliseconds < _lastProcessedTime;
@@ -111,4 +103,145 @@ void Asset::runEventHandlerIfExists(EventType eventType) {
runEventHandlerIfExists(eventType, scriptValue);
}
+ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
+ ScriptValue returnValue;
+ switch (methodId) {
+ case kSpatialMoveToMethod: {
+ assert(args.size() == 2);
+ int16 x = static_cast<int16>(args[0].asFloat());
+ int16 y = static_cast<int16>(args[1].asFloat());
+ moveTo(x, y);
+ break;
+ }
+
+ case kSpatialMoveToByOffsetMethod: {
+ assert(args.size() == 2);
+ int16 dx = static_cast<int16>(args[0].asFloat());
+ int16 dy = static_cast<int16>(args[1].asFloat());
+
+ Common::Point currentPos = getTopLeft();
+ int16 newX = currentPos.x + dx;
+ int16 newY = currentPos.y + dy;
+ moveTo(newX, newY);
+ break;
+ }
+
+ case kSpatialZMoveToMethod: {
+ assert(args.size() == 1);
+ int zIndex = static_cast<int>(args[0].asFloat());
+ setZIndex(zIndex);
+ break;
+ }
+
+ case kSpatialCenterMoveToMethod: {
+ assert(args.size() == 2);
+ int16 x = static_cast<int16>(args[0].asFloat());
+ int16 y = static_cast<int16>(args[1].asFloat());
+ moveToCentered(x, y);
+ break;
+ }
+
+ case kGetLeftXMethod:
+ assert(args.empty());
+ returnValue.setToFloat(_header->_boundingBox.left);
+ break;
+
+ case kGetTopYMethod:
+ assert(args.empty());
+ returnValue.setToFloat(_header->_boundingBox.top);
+ break;
+
+ case kGetWidthMethod:
+ assert(args.empty());
+ returnValue.setToFloat(_header->_boundingBox.width());
+ break;
+
+ case kGetHeightMethod:
+ assert(args.empty());
+ returnValue.setToFloat(_header->_boundingBox.height());
+ break;
+
+ case kGetCenterXMethod: {
+ int centerX = _header->_boundingBox.left + (_header->_boundingBox.width() / 2);
+ returnValue.setToFloat(centerX);
+ break;
+ }
+
+ case kGetCenterYMethod: {
+ int centerY = _header->_boundingBox.top + (_header->_boundingBox.height() / 2);
+ returnValue.setToFloat(centerY);
+ break;
+ }
+
+ case kGetZCoordinateMethod:
+ assert(args.empty());
+ returnValue.setToFloat(_header->_zIndex);
+ break;
+
+ case kIsVisibleMethod:
+ assert(args.empty());
+ returnValue.setToBool(isVisible());
+ break;
+
+ default:
+ Asset::callMethod(methodId, args);
+ }
+ return returnValue;
+}
+
+void SpatialEntity::moveTo(int16 x, int16 y) {
+ Common::Point dest(x, y);
+ if (dest == getTopLeft()) {
+ // We aren't actually moving anywhere.
+ return;
+ }
+
+ if (isVisible()) {
+ invalidateLocalBounds();
+ }
+ _header->_boundingBox.moveTo(dest);
+ if (isVisible()) {
+ invalidateLocalBounds();
+ }
+}
+
+void SpatialEntity::moveToCentered(int16 x, int16 y) {
+ int16 targetX = x - (_header->_boundingBox.width() / 2);
+ int16 targetY = y - (_header->_boundingBox.height() / 2);
+ moveTo(targetX, targetY);
+}
+
+void SpatialEntity::setBounds(const Common::Rect &bounds) {
+ if (_header->_boundingBox == bounds) {
+ // We aren't actually moving anywhere.
+ return;
+ }
+
+ if (isVisible()) {
+ invalidateLocalBounds();
+ }
+ _header->_boundingBox = bounds;
+ if (isVisible()) {
+ invalidateLocalBounds();
+ }
+}
+
+void SpatialEntity::setZIndex(int zIndex) {
+ if (_header->_zIndex == zIndex) {
+ // We aren't actually moving anywhere.
+ return;
+ }
+
+ _header->_zIndex = zIndex;
+ invalidateLocalZIndex();
+}
+
+void SpatialEntity::invalidateLocalBounds() {
+ g_engine->_dirtyRects.push_back(_header->_boundingBox);
+}
+
+void SpatialEntity::invalidateLocalZIndex() {
+ warning("STUB: Asset::invalidateLocalZIndex()");
+}
+
} // End of namespace MediaStation
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 98e63689450..390c7a23de5 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -40,23 +40,13 @@ public:
virtual ~Asset();
// Does any needed frame drawing, audio playing, event handlers, etc.
- virtual void process() {
- return;
- }
-
- // For spatial assets, actually redraws the dirty area.
- virtual void redraw(Common::Rect &rect) {
- return;
- }
+ virtual void process() { return; }
// Runs built-in bytecode methods.
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) = 0;
- // Called to have the asset do any processing, like drawing new frames,
- // handling time-based event handlers, and such. Some assets don't have any
- // processing to do.
- virtual bool isActive() const {
- return _isActive;
- }
+ virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args);
+
+ virtual bool isSpatialActor() const { return false; }
+ virtual bool isActive() const { return _isActive; }
// These are not pure virtual so if an asset doesnʻt read any chunks or
// subfiles it doesnʻt need to just implement these with an error message.
@@ -69,22 +59,40 @@ public:
void runEventHandlerIfExists(EventType eventType, const ScriptValue &arg);
void runEventHandlerIfExists(EventType eventType);
- AssetType type() const;
- int zIndex() const;
- Common::Rect getBbox() const;
- AssetHeader *getHeader() const {
- return _header;
- }
+ AssetType type() const { return _header->_type; }
+ uint id() const { return _header->_id; }
+ AssetHeader *getHeader() const { return _header; }
protected:
AssetHeader *_header = nullptr;
bool _isActive = false;
uint _startTime = 0;
uint _lastProcessedTime = 0;
- // TODO: Rename this to indicate the time is in milliseconds.
uint _duration = 0;
};
+class SpatialEntity : public Asset {
+public:
+ SpatialEntity(AssetHeader *header) : Asset(header) {};
+
+ virtual void redraw(Common::Rect &rect) { return; }
+ virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+
+ virtual bool isSpatialActor() const override { return true; }
+ virtual bool isVisible() const { return false; }
+ Common::Point getTopLeft() { return Common::Point(_header->_boundingBox.left, _header->_boundingBox.top); }
+ Common::Rect getBbox() const { return _header->_boundingBox; }
+ int zIndex() const { return _header->_zIndex; }
+
+ void moveTo(int16 x, int16 y);
+ void moveToCentered(int16 x, int16 y);
+ void setBounds(const Common::Rect &bounds);
+ void setZIndex(int zIndex);
+
+ virtual void invalidateLocalBounds();
+ virtual void invalidateLocalZIndex();
+};
+
} // End of namespace MediaStation
#endif
\ No newline at end of file
diff --git a/engines/mediastation/assets/canvas.cpp b/engines/mediastation/assets/canvas.cpp
index 6579842dbac..6bd43408244 100644
--- a/engines/mediastation/assets/canvas.cpp
+++ b/engines/mediastation/assets/canvas.cpp
@@ -30,7 +30,7 @@ ScriptValue Canvas::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
}
default:
- error("Canvas::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/assets/canvas.h b/engines/mediastation/assets/canvas.h
index ca2214ddf82..b7dcd0431e7 100644
--- a/engines/mediastation/assets/canvas.h
+++ b/engines/mediastation/assets/canvas.h
@@ -29,11 +29,15 @@
namespace MediaStation {
-class Canvas : public Asset {
+class Canvas : public SpatialEntity {
public:
- Canvas(AssetHeader *header) : Asset(header) {};
+ Canvas(AssetHeader *header) : SpatialEntity(header) {};
+ virtual bool isVisible() const override { return _isVisible; }
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+
+private:
+ bool _isVisible = false;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/font.cpp b/engines/mediastation/assets/font.cpp
index a185a375f11..8f07342e600 100644
--- a/engines/mediastation/assets/font.cpp
+++ b/engines/mediastation/assets/font.cpp
@@ -37,10 +37,6 @@ Font::~Font() {
_glyphs.clear();
}
-ScriptValue Font::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
- error("Font::callMethod(): Font does not have any callable methods");
-}
-
void Font::readChunk(Chunk &chunk) {
debugC(5, kDebugLoading, "Font::readChunk(): Reading font glyph (@0x%llx)", static_cast<long long int>(chunk.pos()));
uint asciiCode = chunk.readTypedUint16();
diff --git a/engines/mediastation/assets/font.h b/engines/mediastation/assets/font.h
index 3ecab34e8b6..5a599af6e61 100644
--- a/engines/mediastation/assets/font.h
+++ b/engines/mediastation/assets/font.h
@@ -46,15 +46,10 @@ public:
Font(AssetHeader *header) : Asset(header) {};
~Font();
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
-
virtual void readChunk(Chunk &chunk) override;
private:
Common::HashMap<uint, FontGlyph *> _glyphs;
-
- // Method implementations.
- // No methods are implemented as of now.
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/hotspot.cpp b/engines/mediastation/assets/hotspot.cpp
index 7f7a50dbfa7..989ed995be4 100644
--- a/engines/mediastation/assets/hotspot.cpp
+++ b/engines/mediastation/assets/hotspot.cpp
@@ -24,7 +24,7 @@
namespace MediaStation {
-Hotspot::Hotspot(AssetHeader *header) : Asset(header) {
+Hotspot::Hotspot(AssetHeader *header) : SpatialEntity(header) {
if (header->_startup == kAssetStartupActive) {
_isActive = true;
}
@@ -106,7 +106,7 @@ ScriptValue Hotspot::callMethod(BuiltInMethod methodId, Common::Array<ScriptValu
}
default:
- error("Hotspot::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/assets/hotspot.h b/engines/mediastation/assets/hotspot.h
index fb1aa73e2f3..dfde0dcad94 100644
--- a/engines/mediastation/assets/hotspot.h
+++ b/engines/mediastation/assets/hotspot.h
@@ -29,11 +29,12 @@
namespace MediaStation {
-class Hotspot : public Asset {
+class Hotspot : public SpatialEntity {
public:
Hotspot(AssetHeader *header);
bool isInside(const Common::Point &pointToCheck);
+ virtual bool isVisible() const override { return false; }
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
};
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index 54771709580..e2e92d9d28b 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -25,7 +25,7 @@
namespace MediaStation {
-Image::Image(AssetHeader *header) : Asset(header) {
+Image::Image(AssetHeader *header) : SpatialEntity(header) {
if (header->_startup == kAssetStartupActive) {
_isActive = true;
}
@@ -42,7 +42,6 @@ Image::~Image() {
ScriptValue Image::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
-
switch (methodId) {
case kSpatialShowMethod: {
assert(args.empty());
@@ -62,29 +61,8 @@ ScriptValue Image::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
return returnValue;
}
- case kSpatialMoveToMethod: {
- assert(args.size() == 2);
-
- // Mark the previous location dirty.
- Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
- g_engine->_dirtyRects.push_back(bbox);
-
- // Update location and mark new location dirty.
- int newXAdjust = static_cast<int>(args[0].asFloat());
- int newYAdjust = static_cast<int>(args[1].asFloat());
- if (_xAdjust != newXAdjust || _yAdjust != newYAdjust) {
- _xAdjust = newXAdjust;
- _yAdjust = newYAdjust;
-
- bbox = Common::Rect(getLeftTop(), _bitmap->width(), _bitmap->height());
- g_engine->_dirtyRects.push_back(bbox);
- }
-
- return returnValue;
- }
-
default:
- error("Image::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
@@ -106,6 +84,7 @@ void Image::redraw(Common::Rect &rect) {
void Image::spatialShow() {
_isActive = true;
+ _isVisible = true;
g_engine->addPlayingAsset(this);
Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
g_engine->_dirtyRects.push_back(bbox);
@@ -113,12 +92,13 @@ void Image::spatialShow() {
void Image::spatialHide() {
_isActive = false;
+ _isVisible = false;
Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
g_engine->_dirtyRects.push_back(bbox);
}
Common::Point Image::getLeftTop() {
- return Common::Point(_header->_x + _header->_boundingBox.left + _xAdjust, _header->_y + _header->_boundingBox.top + _yAdjust);
+ return Common::Point(_header->_x + _header->_boundingBox.left, _header->_y + _header->_boundingBox.top);
}
void Image::readChunk(Chunk &chunk) {
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index 14850c6e930..a0953058e25 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -31,7 +31,7 @@
namespace MediaStation {
-class Image : public Asset {
+class Image : public SpatialEntity {
friend class Context;
public:
@@ -39,15 +39,13 @@ public:
virtual ~Image() override;
virtual void readChunk(Chunk &chunk) override;
-
- virtual void redraw(Common::Rect &rect) override;
-
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+ virtual void redraw(Common::Rect &rect) override;
+ virtual bool isVisible() const override { return _isVisible; }
private:
Bitmap *_bitmap = nullptr;
- int _xAdjust = 0;
- int _yAdjust = 0;
+ bool _isVisible = false;
// Script method implementations.
void spatialShow();
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 65c2daf8f4a..59588f039d0 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -154,7 +154,7 @@ MovieFrame::~MovieFrame() {
_footer = nullptr;
}
-Movie::Movie(AssetHeader *header) : Asset(header) {
+Movie::Movie(AssetHeader *header) : SpatialEntity(header) {
if (header->_startup == kAssetStartupActive) {
setActive();
_showByDefault = true;
@@ -213,35 +213,13 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
return returnValue;
}
- case kIsVisibleMethod: {
- assert(args.empty());
- returnValue.setToBool(_isShowing);
- return returnValue;
- }
-
- case kSpatialCenterMoveToMethod: {
- assert(args.size() == 2);
- int x = static_cast<int>(args[0].asFloat());
- int y = static_cast<int>(args[1].asFloat());
- spatialCenterMoveTo(x, y);
- return returnValue;
- }
-
- case kSpatialMoveToMethod: {
- assert(args.size() == 2);
- int x = static_cast<int>(args[0].asFloat());
- int y = static_cast<int>(args[1].asFloat());
- spatialMoveTo(x, y);
- return returnValue;
- }
-
case kIsPlayingMethod: {
assert(args.empty());
returnValue.setToBool(_isPlaying);
return returnValue;
}
- case kXPositionMethod: {
+ case kGetLeftXMethod: {
assert(args.empty());
double left = static_cast<double>(_header->_boundingBox.left);
returnValue.setToFloat(left);
@@ -249,7 +227,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
- case kYPositionMethod: {
+ case kGetTopYMethod: {
assert(args.empty());
double top = static_cast<double>(_header->_boundingBox.top);
returnValue.setToFloat(top);
@@ -263,7 +241,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
default:
- error("Movie::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
@@ -373,48 +351,6 @@ void Movie::timeStop() {
runEventHandlerIfExists(kMovieStoppedEvent);
}
-void Movie::spatialCenterMoveTo(int x, int y) {
- // Mark the previous location dirty.
- for (MovieFrame *frame : _framesOnScreen) {
- Common::Rect bbox = getFrameBoundingBox(frame);
- g_engine->_dirtyRects.push_back(bbox);
- }
-
- // Calculate the center of the movie, and update location.
- int frameWidth = _header->_boundingBox.width();
- int frameHeight = _header->_boundingBox.height();
- int centerX = x - frameWidth / 2;
- int centerY = y - frameHeight / 2;
-
- debugC(5, kDebugScript, "Movie::callMethod(): (%d) Moving movie center to (%d, %d)", _header->_id, x, y);
- // Unlike the sprites, movie bounding boxes must be moved too.
- _header->_boundingBox.moveTo(centerX, centerY);
-
- // Mark the new location dirty.
- for (MovieFrame *frame : _framesOnScreen) {
- Common::Rect bbox = getFrameBoundingBox(frame);
- g_engine->_dirtyRects.push_back(bbox);
- }
-}
-
-void Movie::spatialMoveTo(int x, int y) {
- // Mark the previous location dirty.
- for (MovieFrame *frame : _framesOnScreen) {
- Common::Rect bbox = getFrameBoundingBox(frame);
- g_engine->_dirtyRects.push_back(bbox);
- }
-
- // Update the location and mark the new location dirty.
- debugC(5, kDebugGraphics, "Movie::callMethod(): (%d) Moving movie to (%d, %d)", _header->_id, x, y);
- // Unlike the sprites, movie bounding boxes must be moved too.
- _header->_boundingBox.moveTo(x, y);
-
- for (MovieFrame *frame : _framesOnScreen) {
- Common::Rect bbox = getFrameBoundingBox(frame);
- g_engine->_dirtyRects.push_back(bbox);
- }
-}
-
void Movie::process() {
if (_isPlaying) {
processTimeEventHandlers();
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index 514f4f57f74..2dbdc1a9977 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -89,7 +89,7 @@ enum MovieSectionType {
kMovieFooterSection = 0x06aa
};
-class Movie : public Asset {
+class Movie : public SpatialEntity {
public:
Movie(AssetHeader *header);
virtual ~Movie() override;
@@ -102,6 +102,8 @@ public:
virtual void redraw(Common::Rect &rect) override;
+ virtual bool isVisible() const override { return _isShowing; }
+
private:
bool _showByDefault = false;
bool _isShowing = false;
@@ -121,8 +123,6 @@ private:
void timeStop();
void spatialShow();
void spatialHide();
- void spatialCenterMoveTo(int x, int y);
- void spatialMoveTo(int x, int y);
void updateFrameState();
void showPersistentFrame();
diff --git a/engines/mediastation/assets/palette.cpp b/engines/mediastation/assets/palette.cpp
index d8b00ce04e8..c55e78ee6ee 100644
--- a/engines/mediastation/assets/palette.cpp
+++ b/engines/mediastation/assets/palette.cpp
@@ -25,9 +25,5 @@
namespace MediaStation {
-ScriptValue Palette::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
- error("Palette::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
-}
-
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/palette.h b/engines/mediastation/assets/palette.h
index d960792aaa1..b3dd3114c70 100644
--- a/engines/mediastation/assets/palette.h
+++ b/engines/mediastation/assets/palette.h
@@ -32,8 +32,6 @@ namespace MediaStation {
class Palette : public Asset {
public:
Palette(AssetHeader *header) : Asset(header) {};
-
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/path.cpp b/engines/mediastation/assets/path.cpp
index 6f3f6496672..10e637cf38f 100644
--- a/engines/mediastation/assets/path.cpp
+++ b/engines/mediastation/assets/path.cpp
@@ -64,7 +64,7 @@ ScriptValue Path::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
default:
- error("Path::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return Asset::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/assets/screen.cpp b/engines/mediastation/assets/screen.cpp
index 13238178b09..c9b6d131e52 100644
--- a/engines/mediastation/assets/screen.cpp
+++ b/engines/mediastation/assets/screen.cpp
@@ -24,8 +24,4 @@
namespace MediaStation {
-ScriptValue Screen::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
- error("Screen::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
-}
-
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/screen.h b/engines/mediastation/assets/screen.h
index 7a8c16f1732..c5846240361 100644
--- a/engines/mediastation/assets/screen.h
+++ b/engines/mediastation/assets/screen.h
@@ -35,8 +35,6 @@ namespace MediaStation {
class Screen : public Asset {
public:
Screen(AssetHeader *header) : Asset(header) {};
-
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index b3467d51237..100fb9a2f31 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -72,7 +72,7 @@ ScriptValue Sound::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
default:
- error("Sound::callMethod(): Got unimplemented method %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return Asset::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index ec2b9dd652e..575ee5d82f4 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -60,7 +60,7 @@ uint32 SpriteFrame::index() {
return _bitmapHeader->_index;
}
-Sprite::Sprite(AssetHeader *header) : Asset(header) {
+Sprite::Sprite(AssetHeader *header) : SpatialEntity(header) {
if (header->_startup == kAssetStartupActive) {
setActive();
_isShowing = true;
@@ -135,31 +135,8 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
return returnValue;
}
- case kSpatialMoveToMethod: {
- assert(args.size() == 2);
-
- // Mark the previous location dirty.
- if (_activeFrame != nullptr) {
- g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
- }
-
- // Update the location and mark the new location dirty.
- int newXAdjust = static_cast<int>(args[0].asFloat());
- int newYAdjust = static_cast<int>(args[1].asFloat());
- if (_xAdjust != newXAdjust || _yAdjust != newYAdjust) {
- debugC(5, kDebugGraphics, "Sprite::callMethod(): (%d) Moving sprite to (%d, %d)", _header->_id, newXAdjust, newYAdjust);
- _xAdjust = newXAdjust;
- _yAdjust = newYAdjust;
- if (_activeFrame != nullptr) {
- g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
- }
- }
-
- return returnValue;
- }
-
default:
- error("Sprite::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
@@ -322,7 +299,7 @@ void Sprite::redraw(Common::Rect &rect) {
Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
if (!areaToRedraw.isEmpty()) {
Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
- areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox.left - _xAdjust, -_activeFrame->top() - _header->_boundingBox.top - _yAdjust);
+ areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox.left, -_activeFrame->top() - _header->_boundingBox.top);
areaToRedraw.clip(Common::Rect(0, 0, _activeFrame->width(), _activeFrame->height()));
g_engine->_screen->simpleBlitFrom(_activeFrame->_surface, areaToRedraw, originOnScreen);
}
@@ -345,7 +322,7 @@ Common::Rect Sprite::getActiveFrameBoundingBox() {
// The frame dimensions are relative to those of the sprite movie.
// So we must get the absolute coordinates.
Common::Rect bbox = _activeFrame->boundingBox();
- bbox.translate(_header->_boundingBox.left + _xAdjust, _header->_boundingBox.top + _yAdjust);
+ bbox.translate(_header->_boundingBox.left, _header->_boundingBox.top);
return bbox;
}
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index d90b8506349..8994b412250 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -59,17 +59,20 @@ private:
// Sprites are somewhat like movies, but they strictly show one frame at a time
// and don't have sound. They are intended for background/recurrent animations.
-class Sprite : public Asset {
+class Sprite : public SpatialEntity {
friend class Context;
public:
Sprite(AssetHeader *header);
~Sprite();
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
virtual void redraw(Common::Rect &rect) override;
+ virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+
+ virtual bool isVisible() const override { return _isShowing; }
+
virtual void readChunk(Chunk &chunk) override;
private:
@@ -81,9 +84,6 @@ private:
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
- int _xAdjust = 0;
- int _yAdjust = 0;
-
// Method implementations.
void spatialShow();
void spatialHide();
diff --git a/engines/mediastation/assets/text.cpp b/engines/mediastation/assets/text.cpp
index 2b7ef861b01..59dead1d9c2 100644
--- a/engines/mediastation/assets/text.cpp
+++ b/engines/mediastation/assets/text.cpp
@@ -52,7 +52,7 @@ ScriptValue Text::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
default:
- error("Text::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return SpatialEntity::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/assets/text.h b/engines/mediastation/assets/text.h
index 5ba2f9be7a8..e7dd97bb690 100644
--- a/engines/mediastation/assets/text.h
+++ b/engines/mediastation/assets/text.h
@@ -31,13 +31,16 @@
namespace MediaStation {
-class Text : public Asset {
+class Text : public SpatialEntity {
public:
- Text(AssetHeader *header) : Asset(header) {};
+ Text(AssetHeader *header) : SpatialEntity(header) {};
+ virtual bool isVisible() const override { return _isVisible; }
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
private:
+ bool _isVisible = false;
+
// Method implementations.
Common::String text() const;
void setText(Common::String text);
diff --git a/engines/mediastation/assets/timer.cpp b/engines/mediastation/assets/timer.cpp
index 38a9c0debc2..284f971510e 100644
--- a/engines/mediastation/assets/timer.cpp
+++ b/engines/mediastation/assets/timer.cpp
@@ -49,7 +49,7 @@ ScriptValue Timer::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
default:
- error("Timer::callMethod(): Got unimplemented method ID %s (%d)", builtInMethodToStr(methodId), static_cast<uint>(methodId));
+ return Asset::callMethod(methodId, args);
}
}
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 4857e95593d..5fbd406c925 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -81,12 +81,12 @@ Context::Context(const Common::Path &path) : Datafile(path) {
Asset *asset = it->_value;
uint referencedAssetId = asset->getHeader()->_assetReference;
if (referencedAssetId != 0) {
- switch (asset->getHeader()->_type) {
+ switch (asset->type()) {
case kAssetTypeImage: {
Image *image = static_cast<Image *>(asset);
Image *referencedImage = static_cast<Image *>(getAssetById(referencedAssetId));
if (referencedImage == nullptr) {
- error("Context::Context(): Asset %d references non-existent asset %d", asset->getHeader()->_id, referencedAssetId);
+ error("Context::Context(): Asset %d references non-existent asset %d", asset->id(), referencedAssetId);
}
image->_bitmap = referencedImage->_bitmap;
break;
@@ -96,14 +96,14 @@ Context::Context(const Common::Path &path) : Datafile(path) {
Sprite *sprite = static_cast<Sprite *>(asset);
Sprite *referencedSprite = static_cast<Sprite *>(getAssetById(referencedAssetId));
if (referencedSprite == nullptr) {
- error("Context::Context(): Asset %d references non-existent asset %d", asset->getHeader()->_id, referencedAssetId);
+ error("Context::Context(): Asset %d references non-existent asset %d", asset->id(), referencedAssetId);
}
sprite->_frames = referencedSprite->_frames;
break;
}
default:
- error("Context::Context(): Asset type %d referenced, but reference not implemented yet", asset->getHeader()->_type);
+ error("Context::Context(): Asset type %d referenced, but reference not implemented yet", asset->type());
}
}
}
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 62616d2f6cd..966e1e33e49 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -153,20 +153,20 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "MouseActivate";
case kMouseDeactivateMethod:
return "MouseDeactivate";
- case kXPositionMethod:
- return "XPosition";
- case kYPositionMethod:
- return "YPosition";
+ case kGetLeftXMethod:
+ return "GetLeftX";
+ case kGetTopYMethod:
+ return "GetTopY";
case kTriggerAbsXPositionMethod:
return "TriggerAbsXPosition";
case kTriggerAbsYPositionMethod:
return "TriggerAbsYPosition";
case kIsActiveMethod:
return "IsActive";
- case kWidthMethod:
- return "Width";
- case kHeightMethod:
- return "Height";
+ case kGetWidthMethod:
+ return "GetWidth";
+ case kGetHeightMethod:
+ return "GetHeight";
case kIsVisibleMethod:
return "IsVisible";
case kMovieResetMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index edd8d86ce63..ae2f70a3f9c 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -89,8 +89,11 @@ enum BuiltInMethod {
// Currently it's only in var_7be1_cursor_currentTool in
// IBM/Crayola.
kCursorSetMethod = 200, // PARAMS: 0
+
+ // SPATIAL ENTITY METHODS.
kSpatialHideMethod = 203, // PARAMS: 1
kSpatialMoveToMethod = 204, // PARAMS: 2
+ kSpatialMoveToByOffsetMethod = 205, // PARAMS: 2
kSpatialZMoveToMethod = 216, // PARAMS: 1
kSpatialShowMethod = 202, // PARAMS: 1
kTimePlayMethod = 206, // PARAMS: 1
@@ -98,21 +101,25 @@ enum BuiltInMethod {
kIsPlayingMethod = 372, // PARAMS: 0
kSetDissolveFactorMethod = 241, // PARAMS: 1
kSpatialCenterMoveToMethod = 230,
+ kGetLeftXMethod = 233,
+ kGetTopYMethod = 234,
+ kGetWidthMethod = 235, // PARAMS: 0
+ kGetHeightMethod = 236, // PARAMS: 0
+ kGetCenterXMethod = 237,
+ kGetCenterYMethod = 238,
+ kGetZCoordinateMethod = 239,
+ kIsPointInsideMethod = 246,
+ kGetMouseXOffsetMethod = 264,
+ kGetMouseYOffsetMethod = 265,
+ kIsVisibleMethod = 269,
// HOTSPOT METHODS.
kMouseActivateMethod = 210, // PARAMS: 1
kMouseDeactivateMethod = 211, // PARAMS: 0
- kXPositionMethod = 233, // PARAMS: 0
- kYPositionMethod = 234, // PARAMS: 0
kTriggerAbsXPositionMethod = 321, // PARAMS: 0
kTriggerAbsYPositionMethod = 322, // PARAMS: 0
kIsActiveMethod = 371, // PARAMS: 0
- // IMAGE METHODS.
- kWidthMethod = 235, // PARAMS: 0
- kHeightMethod = 236, // PARAMS: 0
- kIsVisibleMethod = 269,
-
// SPRITE METHODS.
kMovieResetMethod = 219, // PARAMS: 0
kSetSpriteFrameByIdMethod = 220, // PARAMS: 1
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 5bfe0511b8f..7001e7edf8e 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -226,7 +226,7 @@ void MediaStationEngine::processEvents() {
// Even though this is a keydown event, we need to look at the mouse position.
Asset *hotspot = findAssetToAcceptMouseEvents();
if (hotspot != nullptr) {
- debugC(1, kDebugEvents, "EVENT_KEYDOWN (%d): Sent to hotspot %d", _event.kbd.ascii, hotspot->getHeader()->_id);
+ debugC(1, kDebugEvents, "EVENT_KEYDOWN (%d): Sent to hotspot %d", _event.kbd.ascii, hotspot->id());
ScriptValue keyCode;
keyCode.setToFloat(_event.kbd.ascii);
hotspot->runEventHandlerIfExists(kKeyDownEvent, keyCode);
@@ -237,7 +237,7 @@ void MediaStationEngine::processEvents() {
case Common::EVENT_LBUTTONDOWN: {
Asset *hotspot = findAssetToAcceptMouseEvents();
if (hotspot != nullptr) {
- debugC(1, kDebugEvents, "EVENT_LBUTTONDOWN (%d, %d): Sent to hotspot %d", _mousePos.x, _mousePos.y, hotspot->getHeader()->_id);
+ debugC(1, kDebugEvents, "EVENT_LBUTTONDOWN (%d, %d): Sent to hotspot %d", _mousePos.x, _mousePos.y, hotspot->id());
hotspot->runEventHandlerIfExists(kMouseDownEvent);
}
break;
@@ -272,11 +272,11 @@ void MediaStationEngine::refreshActiveHotspot() {
if (hotspot != _currentHotspot) {
if (_currentHotspot != nullptr) {
_currentHotspot->runEventHandlerIfExists(kMouseExitedEvent);
- debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Exited hotspot %d", _mousePos.x, _mousePos.y, _currentHotspot->getHeader()->_id);
+ debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Exited hotspot %d", _mousePos.x, _mousePos.y, _currentHotspot->id());
}
_currentHotspot = hotspot;
if (hotspot != nullptr) {
- debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Entered hotspot %d", _mousePos.x, _mousePos.y, hotspot->getHeader()->_id);
+ debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Entered hotspot %d", _mousePos.x, _mousePos.y, hotspot->id());
setCursor(hotspot->getHeader()->_cursorResourceId);
hotspot->runEventHandlerIfExists(kMouseEnteredEvent);
} else {
@@ -286,7 +286,7 @@ void MediaStationEngine::refreshActiveHotspot() {
}
if (hotspot != nullptr) {
- debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Sent to hotspot %d", _mousePos.x, _mousePos.y, hotspot->getHeader()->_id);
+ debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Sent to hotspot %d", _mousePos.x, _mousePos.y, hotspot->id());
hotspot->runEventHandlerIfExists(kMouseMovedEvent);
}
}
@@ -297,15 +297,23 @@ void MediaStationEngine::redraw() {
}
Common::sort(_assetsPlaying.begin(), _assetsPlaying.end(), [](Asset * a, Asset * b) {
- return a->zIndex() > b->zIndex();
+ if (!a->isSpatialActor() || !b->isSpatialActor()) {
+ return false;
+ }
+ return static_cast<SpatialEntity *>(a)->zIndex() > static_cast<SpatialEntity *>(b)->zIndex();
});
for (Common::Rect dirtyRect : _dirtyRects) {
for (Asset *asset : _assetsPlaying) {
- Common::Rect bbox = asset->getBbox();
+ if (!asset->isSpatialActor()) {
+ continue;
+ }
+
+ SpatialEntity *entity = static_cast<SpatialEntity *>(asset);
+ Common::Rect bbox = entity->getBbox();
if (!bbox.isEmpty()) {
if (dirtyRect.intersects(bbox)) {
- asset->redraw(dirtyRect);
+ entity->redraw(dirtyRect);
}
}
}
@@ -420,7 +428,7 @@ ScriptValue MediaStationEngine::callMethod(BuiltInMethod methodId, Common::Array
void MediaStationEngine::doBranchToScreen() {
if (_currentContext != nullptr) {
_currentContext->_screenAsset->runEventHandlerIfExists(kExitEvent);
- releaseContext(_currentContext->_screenAsset->getHeader()->_id);
+ releaseContext(_currentContext->_screenAsset->id());
}
Context *context = loadContext(_requestedScreenBranchId);
@@ -456,7 +464,7 @@ void MediaStationEngine::releaseContext(uint32 contextId) {
// Unload any assets currently playing from this context. They should have
// already been stopped by scripts, but this is a last check.
for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
- uint assetId = (*it)->getHeader()->_id;
+ uint assetId = (*it)->id();
Asset *asset = context->getAssetById(assetId);
if (asset != nullptr) {
it = _assetsPlaying.erase(it);
@@ -477,10 +485,11 @@ Asset *MediaStationEngine::findAssetToAcceptMouseEvents() {
for (Asset *asset : _assetsPlaying) {
if (asset->type() == kAssetTypeHotspot) {
- debugC(5, kDebugGraphics, "findAssetToAcceptMouseEvents(): Hotspot %d (z-index %d)", asset->getHeader()->_id, asset->zIndex());
- if (asset->isActive() && static_cast<Hotspot *>(asset)->isInside(_mousePos)) {
- if (asset->zIndex() < lowestZIndex) {
- lowestZIndex = asset->zIndex();
+ Hotspot *hotspot = static_cast<Hotspot *>(asset);
+ debugC(5, kDebugGraphics, "findAssetToAcceptMouseEvents(): Hotspot %d (z-index %d)", hotspot->id(), hotspot->zIndex());
+ if (hotspot->isActive() && hotspot->isInside(_mousePos)) {
+ if (hotspot->zIndex() < lowestZIndex) {
+ lowestZIndex = hotspot->zIndex();
intersectingAsset = asset;
}
}
Commit: 06c02db20893622b21c83a2b13dfa2cc520ef7cd
https://github.com/scummvm/scummvm/commit/06c02db20893622b21c83a2b13dfa2cc520ef7cd
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Store asset-specific fields in the assets themselves
Rather than putting all these fields in a generic asset header type.
Changed paths:
R engines/mediastation/assetheader.cpp
R engines/mediastation/assetheader.h
R engines/mediastation/assets/stage.h
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/canvas.cpp
engines/mediastation/assets/canvas.h
engines/mediastation/assets/font.cpp
engines/mediastation/assets/font.h
engines/mediastation/assets/hotspot.cpp
engines/mediastation/assets/hotspot.h
engines/mediastation/assets/image.cpp
engines/mediastation/assets/image.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/palette.cpp
engines/mediastation/assets/palette.h
engines/mediastation/assets/path.cpp
engines/mediastation/assets/path.h
engines/mediastation/assets/screen.cpp
engines/mediastation/assets/screen.h
engines/mediastation/assets/sound.cpp
engines/mediastation/assets/sound.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/assets/text.cpp
engines/mediastation/assets/text.h
engines/mediastation/assets/timer.cpp
engines/mediastation/assets/timer.h
engines/mediastation/bitmap.h
engines/mediastation/context.cpp
engines/mediastation/context.h
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
engines/mediastation/module.mk
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index e8f26f8f285..028241aa347 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -19,15 +19,56 @@
*
*/
-#include "mediastation/debugchannels.h"
#include "mediastation/asset.h"
+#include "mediastation/debugchannels.h"
#include "mediastation/mediascript/scriptconstants.h"
+#include "mediastation/mediastation.h"
namespace MediaStation {
Asset::~Asset() {
- delete _header;
- _header = nullptr;
+ for (auto it = _eventHandlers.begin(); it != _eventHandlers.end(); ++it) {
+ Common::Array<EventHandler *> &handlersForType = it->_value;
+ for (EventHandler *handler : handlersForType) {
+ delete handler;
+ }
+ handlersForType.clear();
+ }
+ _eventHandlers.clear();
+}
+
+void Asset::initFromParameterStream(Chunk &chunk) {
+ AssetHeaderSectionType paramType = kAssetHeaderEmptySection;
+ while (true) {
+ paramType = static_cast<AssetHeaderSectionType>(chunk.readTypedUint16());
+ if (paramType == 0) {
+ break;
+ } else {
+ readParameter(chunk, paramType);
+ }
+ }
+}
+
+void Asset::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderEventHandler: {
+ EventHandler *eventHandler = new EventHandler(chunk);
+ Common::Array<EventHandler *> &eventHandlersForType = _eventHandlers.getOrCreateVal(eventHandler->_type);
+
+ // This is not a hashmap because we don't want to have to hash ScriptValues.
+ for (EventHandler *existingEventHandler : eventHandlersForType) {
+ if (existingEventHandler->_argumentValue == eventHandler->_argumentValue) {
+ error("AssetHeader::readSection(): Event handler for %s (%s) already exists",
+ eventTypeToStr(eventHandler->_type), eventHandler->getDebugHeader().c_str());
+ }
+ }
+ eventHandlersForType.push_back(eventHandler);
+ break;
+ }
+
+ default:
+ error("Got unimplemented asset parameter 0x%x", static_cast<uint>(paramType));
+ }
}
ScriptValue Asset::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
@@ -35,11 +76,11 @@ ScriptValue Asset::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
void Asset::readChunk(Chunk &chunk) {
- error("Asset::readChunk(): Chunk reading for asset type 0x%x is not implemented", static_cast<uint>(_header->_type));
+ error("Asset::readChunk(): Chunk reading for asset type 0x%x is not implemented", static_cast<uint>(_type));
}
void Asset::readSubfile(Subfile &subfile, Chunk &chunk) {
- error("Asset::readSubfile(): Subfile reading for asset type 0x%x is not implemented", static_cast<uint>(_header->_type));
+ error("Asset::readSubfile(): Subfile reading for asset type 0x%x is not implemented", static_cast<uint>(_type));
}
void Asset::setActive() {
@@ -57,13 +98,13 @@ void Asset::setInactive() {
void Asset::processTimeEventHandlers() {
if (!_isActive) {
- warning("Asset::processTimeEventHandlers(): Attempted to process time event handlers while asset %d is not playing", _header->_id);
+ warning("Asset::processTimeEventHandlers(): Attempted to process time event handlers while asset %d is not playing", _id);
return;
}
// TODO: Replace with a queue.
uint currentTime = g_system->getMillis();
- const Common::Array<EventHandler *> &_timeHandlers = _header->_eventHandlers.getValOrDefault(kTimerEvent);
+ const Common::Array<EventHandler *> &_timeHandlers = _eventHandlers.getValOrDefault(kTimerEvent);
for (EventHandler *timeEvent : _timeHandlers) {
// Indeed float, not time.
double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
@@ -72,30 +113,30 @@ void Asset::processTimeEventHandlers() {
bool timeEventNeedsToBeProcessed = timeEventInMilliseconds <= currentTime - _startTime;
if (!timeEventAlreadyProcessed && timeEventNeedsToBeProcessed) {
debugC(5, kDebugScript, "Asset::processTimeEventHandlers(): Running On Time handler for time %d ms", timeEventInMilliseconds);
- timeEvent->execute(_header->_id);
+ timeEvent->execute(_id);
}
}
_lastProcessedTime = currentTime - _startTime;
}
void Asset::runEventHandlerIfExists(EventType eventType, const ScriptValue &arg) {
- const Common::Array<EventHandler *> &_eventHandlers = _header->_eventHandlers.getValOrDefault(eventType);
- for (EventHandler *eventHandler : _eventHandlers) {
+ const Common::Array<EventHandler *> &eventHandlers = _eventHandlers.getValOrDefault(eventType);
+ for (EventHandler *eventHandler : eventHandlers) {
const ScriptValue &argToCheck = eventHandler->_argumentValue;
if (arg.getType() != argToCheck.getType()) {
warning("Got event handler arg type %s, expected %s",
- scriptValueTypeToStr(arg.getType()), scriptValueTypeToStr(argToCheck.getType()));
+ scriptValueTypeToStr(arg.getType()), scriptValueTypeToStr(argToCheck.getType()));
continue;
}
if (arg == argToCheck) {
- debugC(5, kDebugScript, "Executing handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
- eventHandler->execute(_header->_id);
+ debugC(5, kDebugScript, "Executing handler for event type %s on asset %d", eventTypeToStr(eventType), _id);
+ eventHandler->execute(_id);
return;
}
}
- debugC(5, kDebugScript, "No event handler for event type %s on asset %d", eventTypeToStr(eventType), _header->_id);
+ debugC(5, kDebugScript, "No event handler for event type %s on asset %d", eventTypeToStr(eventType), _id);
}
void Asset::runEventHandlerIfExists(EventType eventType) {
@@ -118,10 +159,8 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
assert(args.size() == 2);
int16 dx = static_cast<int16>(args[0].asFloat());
int16 dy = static_cast<int16>(args[1].asFloat());
-
- Common::Point currentPos = getTopLeft();
- int16 newX = currentPos.x + dx;
- int16 newY = currentPos.y + dy;
+ int16 newX = _boundingBox.left + dx;
+ int16 newY = _boundingBox.top + dy;
moveTo(newX, newY);
break;
}
@@ -143,39 +182,39 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
case kGetLeftXMethod:
assert(args.empty());
- returnValue.setToFloat(_header->_boundingBox.left);
+ returnValue.setToFloat(_boundingBox.left);
break;
case kGetTopYMethod:
assert(args.empty());
- returnValue.setToFloat(_header->_boundingBox.top);
+ returnValue.setToFloat(_boundingBox.top);
break;
case kGetWidthMethod:
assert(args.empty());
- returnValue.setToFloat(_header->_boundingBox.width());
+ returnValue.setToFloat(_boundingBox.width());
break;
case kGetHeightMethod:
assert(args.empty());
- returnValue.setToFloat(_header->_boundingBox.height());
+ returnValue.setToFloat(_boundingBox.height());
break;
case kGetCenterXMethod: {
- int centerX = _header->_boundingBox.left + (_header->_boundingBox.width() / 2);
+ int centerX = _boundingBox.left + (_boundingBox.width() / 2);
returnValue.setToFloat(centerX);
break;
}
case kGetCenterYMethod: {
- int centerY = _header->_boundingBox.top + (_header->_boundingBox.height() / 2);
+ int centerY = _boundingBox.top + (_boundingBox.height() / 2);
returnValue.setToFloat(centerY);
break;
}
case kGetZCoordinateMethod:
assert(args.empty());
- returnValue.setToFloat(_header->_zIndex);
+ returnValue.setToFloat(_zIndex);
break;
case kIsVisibleMethod:
@@ -189,9 +228,43 @@ ScriptValue SpatialEntity::callMethod(BuiltInMethod methodId, Common::Array<Scri
return returnValue;
}
+void SpatialEntity::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderBoundingBox:
+ _boundingBox = chunk.readTypedRect();
+ break;
+
+ case kAssetHeaderZIndex:
+ _zIndex = chunk.readTypedGraphicUnit();
+ break;
+
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ if (_isVisible) {
+ setActive();
+ }
+ break;
+
+ case kAssetHeaderTransparency:
+ _hasTransparency = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kAssetHeaderStageId:
+ _stageId = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderAssetReference:
+ _assetReference = chunk.readTypedUint16();
+ break;
+
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
+}
+
void SpatialEntity::moveTo(int16 x, int16 y) {
Common::Point dest(x, y);
- if (dest == getTopLeft()) {
+ if (dest == _boundingBox.origin()) {
// We aren't actually moving anywhere.
return;
}
@@ -199,20 +272,20 @@ void SpatialEntity::moveTo(int16 x, int16 y) {
if (isVisible()) {
invalidateLocalBounds();
}
- _header->_boundingBox.moveTo(dest);
+ _boundingBox.moveTo(dest);
if (isVisible()) {
invalidateLocalBounds();
}
}
void SpatialEntity::moveToCentered(int16 x, int16 y) {
- int16 targetX = x - (_header->_boundingBox.width() / 2);
- int16 targetY = y - (_header->_boundingBox.height() / 2);
+ int16 targetX = x - (_boundingBox.width() / 2);
+ int16 targetY = y - (_boundingBox.height() / 2);
moveTo(targetX, targetY);
}
void SpatialEntity::setBounds(const Common::Rect &bounds) {
- if (_header->_boundingBox == bounds) {
+ if (_boundingBox == bounds) {
// We aren't actually moving anywhere.
return;
}
@@ -220,24 +293,24 @@ void SpatialEntity::setBounds(const Common::Rect &bounds) {
if (isVisible()) {
invalidateLocalBounds();
}
- _header->_boundingBox = bounds;
+ _boundingBox = bounds;
if (isVisible()) {
invalidateLocalBounds();
}
}
void SpatialEntity::setZIndex(int zIndex) {
- if (_header->_zIndex == zIndex) {
+ if (_zIndex == zIndex) {
// We aren't actually moving anywhere.
return;
}
- _header->_zIndex = zIndex;
+ _zIndex = zIndex;
invalidateLocalZIndex();
}
void SpatialEntity::invalidateLocalBounds() {
- g_engine->_dirtyRects.push_back(_header->_boundingBox);
+ g_engine->_dirtyRects.push_back(_boundingBox);
}
void SpatialEntity::invalidateLocalZIndex() {
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 390c7a23de5..f4f27c09445 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -24,19 +24,108 @@
#include "common/keyboard.h"
-#include "mediastation/mediastation.h"
#include "mediastation/datafile.h"
+#include "mediastation/mediascript/eventhandler.h"
#include "mediastation/mediascript/scriptconstants.h"
#include "mediastation/mediascript/scriptvalue.h"
-#include "mediastation/assetheader.h"
namespace MediaStation {
-class AssetHeader;
+enum AssetType {
+ kAssetTypeEmpty = 0x0000,
+ kAssetTypeScreen = 0x0001, // SCR
+ kAssetTypeStage = 0x0002, // STG
+ kAssetTypePath = 0x0004, // PTH
+ kAssetTypeSound = 0x0005, // SND
+ kAssetTypeTimer = 0x0006, // TMR
+ kAssetTypeImage = 0x0007, // IMG
+ kAssetTypeHotspot = 0x000b, // HSP
+ kAssetTypeSprite = 0x000e, // SPR
+ kAssetTypeLKZazu = 0x000f,
+ kAssetTypeLKConstellations = 0x0010,
+ kAssetTypeImageSet = 0x001d,
+ kAssetTypeCursor = 0x000c, // CSR
+ kAssetTypePrinter = 0x0019, // PRT
+ kAssetTypeMovie = 0x0016, // MOV
+ kAssetTypePalette = 0x0017,
+ kAssetTypeText = 0x001a, // TXT
+ kAssetTypeFont = 0x001b, // FON
+ kAssetTypeCamera = 0x001c, // CAM
+ kAssetTypeCanvas = 0x001e, // CVS
+ kAssetTypeXsnd = 0x001f,
+ kAssetTypeXsndMidi = 0x0020,
+ kAssetTypeRecorder = 0x0021,
+ kAssetTypeFunction = 0x0069 // FUN
+};
+
+enum AssetHeaderSectionType {
+ kAssetHeaderEmptySection = 0x0000,
+ kAssetHeaderSoundEncoding1 = 0x0001,
+ kAssetHeaderSoundEncoding2 = 0x0002,
+ kAssetHeaderEventHandler = 0x0017,
+ kAssetHeaderStageId = 0x0019,
+ kAssetHeaderAssetId = 0x001a,
+ kAssetHeaderChunkReference = 0x001b,
+ kAssetHeaderMovieAnimationChunkReference = 0x06a4,
+ kAssetHeaderMovieAudioChunkReference = 0x06a5,
+ kAssetHeaderAssetReference = 0x077b,
+ kAssetHeaderBoundingBox = 0x001c,
+ kAssetHeaderMouseActiveArea = 0x001d,
+ kAssetHeaderZIndex = 0x001e,
+ kAssetHeaderStartup = 0x001f,
+ kAssetHeaderTransparency = 0x0020,
+ kAssetHeaderHasOwnSubfile = 0x0021,
+ kAssetHeaderCursorResourceId = 0x0022,
+ kAssetHeaderFrameRate = 0x0024,
+ kAssetHeaderLoadType = 0x0032,
+ kAssetHeaderSoundInfo = 0x0033,
+ kAssetHeaderMovieLoadType = 0x0037,
+ kAssetHeaderSpriteChunkCount = 0x03e8,
+ kAssetHeaderPalette = 0x05aa,
+ kAssetHeaderDissolveFactor = 0x05dc,
+ kAssetHeaderGetOffstageEvents = 0x05dd,
+ kAssetHeaderX = 0x05de,
+ kAssetHeaderY = 0x05df,
+
+ // PATH FIELDS.
+ kAssetHeaderStartPoint = 0x060e,
+ kAssetHeaderEndPoint = 0x060f,
+ kAssetHeaderPathTotalSteps = 0x0610,
+ kAssetHeaderStepRate = 0x0611,
+ kAssetHeaderDuration = 0x0612,
+
+ // CAMERA FIELDS.
+ kAssetHeaderViewportOrigin = 0x076f,
+ kAssetHeaderLensOpen = 0x0770,
+
+ // STAGE FIELDS.
+ kAssetHeaderStageUnk1 = 0x0771,
+ kAssetHeaderCylindricalX = 0x0772,
+ kAssetHeaderCylindricalY = 0x0773,
+ kAssetHeaderAssetName = 0x0bb8,
+
+ // TEXT FIELDS.
+ kAssetHeaderEditable = 0x03eb,
+ kAssetHeaderFontId = 0x0258,
+ kAssetHeaderInitialText = 0x0259,
+ kAssetHeaderTextMaxLength = 0x25a,
+ kAssetHeaderTextJustification = 0x025b,
+ kAssetHeaderTextPosition = 0x25f,
+ kAssetHeaderTextUnk1 = 0x262,
+ kAssetHeaderTextUnk2 = 0x263,
+ kAssetHeaderTextCharacterClass = 0x0266,
+
+ // SPRITE FIELDS.
+ kAssetHeaderSpriteFrameMapping = 0x03e9
+};
+enum SoundEncoding {
+ PCM_S16LE_MONO_22050 = 0x0010, // Uncompressed linear PCM
+ IMA_ADPCM_S16LE_MONO_22050 = 0x0004 // IMA ADPCM encoding, must be decoded
+};
class Asset {
public:
- Asset(AssetHeader *header) : _header(header) {};
+ Asset(AssetType type) : _type(type) {};
virtual ~Asset();
// Does any needed frame drawing, audio playing, event handlers, etc.
@@ -48,6 +137,9 @@ public:
virtual bool isSpatialActor() const { return false; }
virtual bool isActive() const { return _isActive; }
+ virtual void initFromParameterStream(Chunk &chunk);
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType);
+
// These are not pure virtual so if an asset doesnʻt read any chunks or
// subfiles it doesnʻt need to just implement these with an error message.
virtual void readChunk(Chunk &chunk);
@@ -59,30 +151,49 @@ public:
void runEventHandlerIfExists(EventType eventType, const ScriptValue &arg);
void runEventHandlerIfExists(EventType eventType);
- AssetType type() const { return _header->_type; }
- uint id() const { return _header->_id; }
- AssetHeader *getHeader() const { return _header; }
+ AssetType type() const { return _type; }
+ uint id() const { return _id; }
+ uint contextId() const { return _contextId; }
+ void setId(uint id) { _id = id; }
+ void setContextId(uint id) { _contextId = id; }
+
+ uint32 _chunkReference = 0;
+ uint _assetReference = 0;
protected:
- AssetHeader *_header = nullptr;
+ AssetType _type = kAssetTypeEmpty;
+ uint _id = 0;
+ uint _contextId = 0;
+
bool _isActive = false;
uint _startTime = 0;
uint _lastProcessedTime = 0;
uint _duration = 0;
+ Common::HashMap<uint, Common::Array<EventHandler *> > _eventHandlers;
};
class SpatialEntity : public Asset {
public:
- SpatialEntity(AssetHeader *header) : Asset(header) {};
+ SpatialEntity(AssetType type) : Asset(type) {};
virtual void redraw(Common::Rect &rect) { return; }
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual bool isSpatialActor() const override { return true; }
- virtual bool isVisible() const { return false; }
- Common::Point getTopLeft() { return Common::Point(_header->_boundingBox.left, _header->_boundingBox.top); }
- Common::Rect getBbox() const { return _header->_boundingBox; }
- int zIndex() const { return _header->_zIndex; }
+ virtual bool isVisible() const { return _isVisible; }
+
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
+
+ Common::Rect getBbox() const { return _boundingBox; }
+ int zIndex() const { return _zIndex; }
+
+protected:
+ uint _stageId = 0;
+ int _zIndex = 0;
+ Common::Rect _boundingBox;
+ bool _isVisible = false;
+ bool _hasTransparency = false;
+ bool _getOffstageEvents = false;
void moveTo(int16 x, int16 y);
void moveToCentered(int16 x, int16 y);
diff --git a/engines/mediastation/assetheader.cpp b/engines/mediastation/assetheader.cpp
deleted file mode 100644
index 8e741d44d41..00000000000
--- a/engines/mediastation/assetheader.cpp
+++ /dev/null
@@ -1,303 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include "mediastation/assetheader.h"
-#include "mediastation/debugchannels.h"
-
-namespace MediaStation {
-
-AssetHeader::AssetHeader(Chunk &chunk) {
- _fileNumber = chunk.readTypedUint16();
- _type = static_cast<AssetType>(chunk.readTypedUint16());
- _id = chunk.readTypedUint16();
- debugC(4, kDebugLoading, "AssetHeader::AssetHeader(): _type = 0x%x, _id = 0x%x (@0x%llx)", static_cast<uint>(_type), _id, static_cast<long long int>(chunk.pos()));
-
- AssetHeaderSectionType sectionType = static_cast<AssetHeaderSectionType>(chunk.readTypedUint16());
- bool moreSectionsToRead = (kAssetHeaderEmptySection != sectionType);
- while (moreSectionsToRead) {
- readSection(sectionType, chunk);
- sectionType = static_cast<AssetHeaderSectionType>(chunk.readTypedUint16());
- moreSectionsToRead = (kAssetHeaderEmptySection != sectionType);
- }
-}
-
-AssetHeader::~AssetHeader() {
- for (auto it = _eventHandlers.begin(); it != _eventHandlers.end(); ++it) {
- for (EventHandler *eventHandler : it->_value) {
- delete eventHandler;
- }
- }
- _eventHandlers.clear();
-
- delete _palette;
- _palette = nullptr;
-}
-
-void AssetHeader::readSection(AssetHeaderSectionType sectionType, Chunk& chunk) {
- debugC(5, kDebugLoading, "AssetHeader::AssetHeader(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
- switch (sectionType) {
- case kAssetHeaderEmptySection: {
- break;
- }
-
- case kAssetHeaderEventHandler: {
- EventHandler *eventHandler = new EventHandler(chunk);
- Common::Array<EventHandler *> &eventHandlersForType = _eventHandlers.getOrCreateVal(eventHandler->_type);
-
- // This is not a hashmap because we don't want to have to hash ScriptValues.
- for (EventHandler *existingEventHandler : eventHandlersForType) {
- if (existingEventHandler->_argumentValue == eventHandler->_argumentValue) {
- error("AssetHeader::readSection(): Event handler for %s (%s) already exists",
- eventTypeToStr(eventHandler->_type), eventHandler->getDebugHeader().c_str());
- }
- }
- eventHandlersForType.push_back(eventHandler);
- break;
- }
-
- case kAssetHeaderStageId: {
- _stageId = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderAssetId: {
- // We already have this asset's ID, so we will just verify it is the same
- // as the ID we have already read.
- uint32 duplicateAssetId = chunk.readTypedUint16();
- if (duplicateAssetId != _id) {
- warning("AssetHeader::readSection(): AssetHeader ID %d does not match original asset ID %d", duplicateAssetId, _id);
- }
- break;
- }
-
- case kAssetHeaderChunkReference: {
- // These are references to the chunk(s) that hold the data for this asset.
- // The references and the chunks have the following format "a501".
- // There is no guarantee where these chunk(s) might actually be located:
- // - They might be in the same RIFF subfile as this header,
- // - They might be in a different RIFF subfile in the same CXT file,
- // - They might be in a different CXT file entirely.
- _chunkReference = chunk.readTypedChunkReference();
- break;
- }
-
- case kAssetHeaderMovieAudioChunkReference: {
- _audioChunkReference = chunk.readTypedChunkReference();
- break;
- }
-
- case kAssetHeaderMovieAnimationChunkReference: {
- _animationChunkReference = chunk.readTypedChunkReference();
- break;
- }
-
- case kAssetHeaderBoundingBox: {
- _boundingBox = chunk.readTypedRect();
- break;
- }
-
- case kAssetHeaderMouseActiveArea: {
- uint16 total_points = chunk.readTypedUint16();
- for (int i = 0; i < total_points; i++) {
- Common::Point point = chunk.readTypedPoint();
- _mouseActiveArea.push_back(point);
- }
- break;
- }
-
- case kAssetHeaderZIndex: {
- _zIndex = chunk.readTypedGraphicUnit();
- break;
- }
-
- case kAssetHeaderAssetReference: {
- _assetReference = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderStartup: {
- _startup = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderTransparency: {
- _transparency = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderHasOwnSubfile: {
- _hasOwnSubfile = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderCursorResourceId: {
- _cursorResourceId = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderFrameRate: {
- _frameRate = static_cast<uint32>(chunk.readTypedDouble());
- break;
- }
-
- case kAssetHeaderLoadType: {
- _loadType = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderSoundInfo: {
- _chunkCount = chunk.readTypedUint16();
- _rate = chunk.readTypedUint32();
- break;
- }
-
- case kAssetHeaderMovieLoadType: {
- _loadType = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderGetOffstageEvents: {
- _getOffstageEvents = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderPalette: {
- // TODO: Avoid the copying here!
- const uint PALETTE_ENTRIES = 256;
- const uint PALETTE_BYTES = PALETTE_ENTRIES * 3;
- byte* buffer = new byte[PALETTE_BYTES];
- chunk.read(buffer, PALETTE_BYTES);
- _palette = new Graphics::Palette(buffer, PALETTE_ENTRIES);
- delete[] buffer;
- break;
- }
-
- case kAssetHeaderDissolveFactor: {
- _dissolveFactor = chunk.readTypedDouble();
- break;
- }
-
- case kAssetHeaderSoundEncoding1:
- case kAssetHeaderSoundEncoding2: {
- _soundEncoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
- break;
- }
-
- case kAssetHeaderSpriteChunkCount: {
- _chunkCount = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderStartPoint: {
- _startPoint = chunk.readTypedPoint();
- break;
- }
-
- case kAssetHeaderEndPoint: {
- _endPoint = chunk.readTypedPoint();
- break;
- }
-
- case kAssetHeaderStepRate: {
- double _stepRateFloat = chunk.readTypedDouble();
- // This should always be an integer anyway,
- // so we'll cast away any fractional part.
- _stepRate = static_cast<uint32>(_stepRateFloat);
- break;
- }
-
- case kAssetHeaderDuration: {
- // These are stored in the file as fractional seconds,
- // but we want milliseconds.
- _duration = (uint32)(chunk.readTypedTime() * 1000);
- break;
- }
-
- case kAssetHeaderX: {
- _x = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderY: {
- _y = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderEditable: {
- _editable = chunk.readTypedByte();
- break;
- }
-
- case kAssetHeaderFontId: {
- _fontAssetId = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderTextMaxLength: {
- _maxTextLength = chunk.readTypedUint16();
- break;
- }
-
- case kAssetHeaderInitialText: {
- _text = chunk.readTypedString();
- break;
- }
-
- case kAssetHeaderTextJustification: {
- _justification = static_cast<TextJustification>(chunk.readTypedUint16());
- break;
- }
-
- case kAssetHeaderTextPosition: {
- _position = static_cast<TextPosition>(chunk.readTypedUint16());
- break;
- }
-
- case kAssetHeaderTextCharacterClass: {
- CharacterClass characterClass;
- characterClass.firstAsciiCode = chunk.readTypedUint16();
- characterClass.lastAsciiCode = chunk.readTypedUint16();
- _acceptedInput.push_back(characterClass);
- break;
- }
-
- case kAssetHeaderSpriteFrameMapping: {
- uint32 externalFrameId = chunk.readTypedUint16();
- uint32 internalFrameId = chunk.readTypedUint16();
- uint32 unk1 = chunk.readTypedUint16();
- if (unk1 != internalFrameId) {
- warning("AssetHeader::readSection(): Repeated internalFrameId doesn't match");
- }
- _spriteFrameMapping.setVal(externalFrameId, internalFrameId);
- break;
- }
-
- case kAssetHeaderPathTotalSteps: {
- _totalSteps = chunk.readTypedUint16();
- break;
- }
-
- default:
- error("AssetHeader::readSection(): Unknown section type 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
- }
-}
-
-} // end of namespace MediaStation
diff --git a/engines/mediastation/assetheader.h b/engines/mediastation/assetheader.h
deleted file mode 100644
index af077b20a30..00000000000
--- a/engines/mediastation/assetheader.h
+++ /dev/null
@@ -1,222 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef MEDIASTATION_ASSET_HEADER_H
-#define MEDIASTATION_ASSET_HEADER_H
-
-#include "common/str.h"
-#include "common/rect.h"
-#include "common/hashmap.h"
-#include "graphics/palette.h"
-
-#include "mediastation/datafile.h"
-#include "mediastation/mediascript/eventhandler.h"
-
-namespace MediaStation {
-
-struct MovieChunkReference {
- uint32 headerChunkId;
- uint32 audioChunkId;
- uint32 videoChunkId;
-};
-
-typedef uint32 ChunkReference;
-
-typedef uint32 AssetId;
-
-enum AssetType {
- kAssetTypeScreen = 0x0001, // SCR
- kAssetTypeStage = 0x0002, // STG
- kAssetTypePath = 0x0004, // PTH
- kAssetTypeSound = 0x0005, // SND
- kAssetTypeTimer = 0x0006, // TMR
- kAssetTypeImage = 0x0007, // IMG
- kAssetTypeHotspot = 0x000b, // HSP
- kAssetTypeSprite = 0x000e, // SPR
- kAssetTypeLKZazu = 0x000f,
- kAssetTypeLKConstellations = 0x0010,
- kAssetTypeImageSet = 0x001d,
- kAssetTypeCursor = 0x000c, // CSR
- kAssetTypePrinter = 0x0019, // PRT
- kAssetTypeMovie = 0x0016, // MOV
- kAssetTypePalette = 0x0017,
- kAssetTypeText = 0x001a, // TXT
- kAssetTypeFont = 0x001b, // FON
- kAssetTypeCamera = 0x001c, // CAM
- kAssetTypeCanvas = 0x001e, // CVS
- // TODO: Discover how the XSND differs from regular sounds.
- // Only appears in Ariel.
- kAssetTypeXsnd = 0x001f,
- kAssetTypeXsndMidi = 0x0020,
- // TODO: Figure out what this is. Only appears in Ariel.
- kAssetTypeRecorder = 0x0021,
- kAssetTypeFunction = 0x0069 // FUN
-};
-
-enum AssetStartupType {
- kAssetStartupInactive = 0,
- kAssetStartupActive = 1
-};
-
-enum AssetHeaderSectionType {
- kAssetHeaderEmptySection = 0x0000,
- kAssetHeaderSoundEncoding1 = 0x0001,
- kAssetHeaderSoundEncoding2 = 0x0002,
- kAssetHeaderEventHandler = 0x0017,
- kAssetHeaderStageId = 0x0019,
- kAssetHeaderAssetId = 0x001a,
- kAssetHeaderChunkReference = 0x001b,
- kAssetHeaderMovieAnimationChunkReference = 0x06a4,
- kAssetHeaderMovieAudioChunkReference = 0x06a5,
- kAssetHeaderAssetReference = 0x077b,
- kAssetHeaderBoundingBox = 0x001c,
- kAssetHeaderMouseActiveArea = 0x001d,
- kAssetHeaderZIndex = 0x001e,
- kAssetHeaderStartup = 0x001f,
- kAssetHeaderTransparency = 0x0020,
- kAssetHeaderHasOwnSubfile = 0x0021,
- kAssetHeaderCursorResourceId = 0x0022,
- kAssetHeaderFrameRate = 0x0024,
- kAssetHeaderLoadType = 0x0032,
- kAssetHeaderSoundInfo = 0x0033,
- kAssetHeaderMovieLoadType = 0x0037,
- kAssetHeaderSpriteChunkCount = 0x03e8,
- kAssetHeaderPalette = 0x05aa,
- kAssetHeaderDissolveFactor = 0x05dc,
- kAssetHeaderGetOffstageEvents = 0x05dd,
- kAssetHeaderX = 0x05de,
- kAssetHeaderY = 0x05df,
-
- // PATH FIELDS.
- kAssetHeaderStartPoint = 0x060e,
- kAssetHeaderEndPoint = 0x060f,
- kAssetHeaderPathTotalSteps = 0x0610,
- kAssetHeaderStepRate = 0x0611,
- kAssetHeaderDuration = 0x0612,
-
- // CAMERA FIELDS.
- kAssetHeaderViewportOrigin = 0x076f,
- kAssetHeaderLensOpen = 0x0770,
-
- // STAGE FIELDS.
- kAssetHeaderStageUnk1 = 0x0771,
- kAssetHeaderCylindricalX = 0x0772,
- kAssetHeaderCylindricalY = 0x0773,
- kAssetHeaderAssetName = 0x0bb8,
-
- // TEXT FIELDS.
- kAssetHeaderEditable = 0x03eb,
- kAssetHeaderFontId = 0x0258,
- kAssetHeaderInitialText = 0x0259,
- kAssetHeaderTextMaxLength = 0x25a,
- kAssetHeaderTextJustification = 0x025b,
- kAssetHeaderTextPosition = 0x25f,
- kAssetHeaderTextUnk1 = 0x262,
- kAssetHeaderTextUnk2 = 0x263,
- kAssetHeaderTextCharacterClass = 0x0266,
-
- // SPRITE FIELDS.
- kAssetHeaderSpriteFrameMapping = 0x03e9
-};
-
-enum TextJustification {
- kTextJustificationLeft = 0x25c,
- kTextJustificationRight = 0x25d,
- kTextJustificationCenter = 0x25e
-};
-
-enum TextPosition {
- kTextPositionMiddle = 0x25e,
- kTextPositionTop = 0x260,
- kTextPositionBotom = 0x261
-};
-
-struct CharacterClass {
- uint firstAsciiCode = 0;
- uint lastAsciiCode = 0;
-};
-
-enum SoundEncoding {
- PCM_S16LE_MONO_22050 = 0x0010, // Uncompressed linear PCM
- IMA_ADPCM_S16LE_MONO_22050 = 0x0004 // IMA ADPCM encoding, must be decoded
-};
-
-class AssetHeader {
-public:
- AssetHeader(Chunk &chunk);
- ~AssetHeader();
-
- uint32 _fileNumber = 0;
- AssetType _type;
- AssetId _id;
-
- ChunkReference _chunkReference = 0;
- // These two are only used in movies.
- ChunkReference _audioChunkReference = 0;
- ChunkReference _animationChunkReference = 0;
- Common::Rect _boundingBox;
- Common::Array<Common::Point> _mouseActiveArea;
- int _zIndex = 0;
- uint32 _assetReference = 0;
- uint32 _startup = 0;
- bool _transparency = false;
- bool _hasOwnSubfile = false;
- uint32 _cursorResourceId = 0;
- uint32 _frameRate = 0;
- uint32 _loadType = 0;
- uint32 _rate = 0;
- bool _editable = 0;
- Graphics::Palette *_palette = nullptr;
- bool _getOffstageEvents = 0;
- uint32 _x = 0; // Image only.
- uint32 _y = 0; // Image only.
- Common::String _name;
- uint32 _stageId = 0;
- SoundEncoding _soundEncoding;
- uint32 _chunkCount = 0;
- Common::HashMap<uint32, uint32> _spriteFrameMapping;
-
- // PATH FIELDS.
- uint32 _dissolveFactor = 0;
- Common::Point _startPoint;
- Common::Point _endPoint;
- uint32 _stepRate = 0;
- uint32 _duration = 0;
- uint _totalSteps = 0;
-
- // EVENT HANDLER FIELDS.
- Common::HashMap<uint, Common::Array<EventHandler *>> _eventHandlers;
-
- // TEXT FIELDS.
- Common::String _text;
- uint _maxTextLength = 0;
- uint _fontAssetId = 0;
- TextJustification _justification;
- TextPosition _position;
- Common::Array<CharacterClass> _acceptedInput;
-
-private:
- void readSection(AssetHeaderSectionType sectionType, Chunk &chunk);
-};
-
-} // End of namespace MediaStation
-
-#endif
\ No newline at end of file
diff --git a/engines/mediastation/assets/canvas.cpp b/engines/mediastation/assets/canvas.cpp
index 6bd43408244..cb9ead43def 100644
--- a/engines/mediastation/assets/canvas.cpp
+++ b/engines/mediastation/assets/canvas.cpp
@@ -23,6 +23,21 @@
namespace MediaStation {
+void Canvas::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kAssetHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
+ }
+}
+
ScriptValue Canvas::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
switch (methodId) {
case kClearToPaletteMethod: {
diff --git a/engines/mediastation/assets/canvas.h b/engines/mediastation/assets/canvas.h
index b7dcd0431e7..76e121d092a 100644
--- a/engines/mediastation/assets/canvas.h
+++ b/engines/mediastation/assets/canvas.h
@@ -23,7 +23,6 @@
#define MEDIASTATION_CANVAS_H
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -31,13 +30,13 @@ namespace MediaStation {
class Canvas : public SpatialEntity {
public:
- Canvas(AssetHeader *header) : SpatialEntity(header) {};
+ Canvas() : SpatialEntity(kAssetTypeCanvas) {};
- virtual bool isVisible() const override { return _isVisible; }
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
private:
- bool _isVisible = false;
+ double _dissolveFactor = 0.0;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/font.cpp b/engines/mediastation/assets/font.cpp
index 8f07342e600..bfc94798cdc 100644
--- a/engines/mediastation/assets/font.cpp
+++ b/engines/mediastation/assets/font.cpp
@@ -37,6 +37,17 @@ Font::~Font() {
_glyphs.clear();
}
+void Font::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderChunkReference:
+ _chunkReference = chunk.readTypedChunkReference();
+ break;
+
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
+}
+
void Font::readChunk(Chunk &chunk) {
debugC(5, kDebugLoading, "Font::readChunk(): Reading font glyph (@0x%llx)", static_cast<long long int>(chunk.pos()));
uint asciiCode = chunk.readTypedUint16();
diff --git a/engines/mediastation/assets/font.h b/engines/mediastation/assets/font.h
index 5a599af6e61..c53f2436b0f 100644
--- a/engines/mediastation/assets/font.h
+++ b/engines/mediastation/assets/font.h
@@ -23,7 +23,6 @@
#define MEDIASTATION_FONT_H
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/bitmap.h"
#include "mediastation/datafile.h"
#include "mediastation/mediascript/scriptvalue.h"
@@ -43,9 +42,10 @@ private:
class Font : public Asset {
public:
- Font(AssetHeader *header) : Asset(header) {};
+ Font() : Asset(kAssetTypeFont) {};
~Font();
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual void readChunk(Chunk &chunk) override;
private:
diff --git a/engines/mediastation/assets/hotspot.cpp b/engines/mediastation/assets/hotspot.cpp
index 989ed995be4..5081b8fb176 100644
--- a/engines/mediastation/assets/hotspot.cpp
+++ b/engines/mediastation/assets/hotspot.cpp
@@ -24,30 +24,48 @@
namespace MediaStation {
-Hotspot::Hotspot(AssetHeader *header) : SpatialEntity(header) {
- if (header->_startup == kAssetStartupActive) {
- _isActive = true;
+void Hotspot::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderMouseActiveArea: {
+ uint16 total_points = chunk.readTypedUint16();
+ for (int i = 0; i < total_points; i++) {
+ Common::Point point = chunk.readTypedPoint();
+ _mouseActiveArea.push_back(point);
+ }
+ break;
+ }
+
+ case kAssetHeaderCursorResourceId:
+ _cursorResourceId = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderGetOffstageEvents:
+ _getOffstageEvents = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
}
}
bool Hotspot::isInside(const Common::Point &pointToCheck) {
// No sense checking the polygon if we're not even in the bbox.
- if (!_header->_boundingBox.contains(pointToCheck)) {
+ if (!_boundingBox.contains(pointToCheck)) {
return false;
}
// We're in the bbox, but there might not be a polygon to check.
- if (_header->_mouseActiveArea.empty()) {
+ if (_mouseActiveArea.empty()) {
return true;
}
// Polygon intersection code adapted from HADESCH engine, might need more
// refinement once more testing is possible.
- Common::Point point = pointToCheck - Common::Point(_header->_boundingBox.left, _header->_boundingBox.top);
+ Common::Point point = pointToCheck - Common::Point(_boundingBox.left, _boundingBox.top);
int rcross = 0; // Number of right-side overlaps
// Each edge is checked whether it cuts the outgoing stream from the point
- Common::Array<Common::Point> _polygon = _header->_mouseActiveArea;
+ Common::Array<Common::Point> _polygon = _mouseActiveArea;
for (unsigned i = 0; i < _polygon.size(); i++) {
const Common::Point &edgeStart = _polygon[i];
const Common::Point &edgeEnd = _polygon[(i + 1) % _polygon.size()];
diff --git a/engines/mediastation/assets/hotspot.h b/engines/mediastation/assets/hotspot.h
index dfde0dcad94..6994a716afc 100644
--- a/engines/mediastation/assets/hotspot.h
+++ b/engines/mediastation/assets/hotspot.h
@@ -23,7 +23,6 @@
#define MEDIASTATION_HOTSPOT_H
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -31,12 +30,17 @@ namespace MediaStation {
class Hotspot : public SpatialEntity {
public:
- Hotspot(AssetHeader *header);
+ Hotspot() : SpatialEntity(kAssetTypeHotspot) {};
+ virtual ~Hotspot() { _mouseActiveArea.clear(); }
bool isInside(const Common::Point &pointToCheck);
virtual bool isVisible() const override { return false; }
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
+
+ uint _cursorResourceId = 0;
+ Common::Array<Common::Point> _mouseActiveArea;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index e2e92d9d28b..9e91db1f263 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -25,14 +25,8 @@
namespace MediaStation {
-Image::Image(AssetHeader *header) : SpatialEntity(header) {
- if (header->_startup == kAssetStartupActive) {
- _isActive = true;
- }
-}
-
Image::~Image() {
- if (_header->_assetReference == 0) {
+ if (_assetReference == 0) {
// If we're just referencing another asset's bitmap,
// don't delete that bitmap.
delete _bitmap;
@@ -40,6 +34,33 @@ Image::~Image() {
_bitmap = nullptr;
}
+void Image::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderChunkReference:
+ _chunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderLoadType:
+ _loadType = chunk.readTypedByte();
+ break;
+
+ case kAssetHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ case kAssetHeaderX:
+ _xOffset = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderY:
+ _yOffset = chunk.readTypedUint16();
+ break;
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
+ }
+}
+
ScriptValue Image::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
switch (methodId) {
@@ -56,8 +77,9 @@ ScriptValue Image::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
case kSetDissolveFactorMethod: {
+ warning("STUB: setDissolveFactor");
assert(args.size() == 1);
- warning("Image::callMethod(): setDissolveFactor not implemented yet");
+ _dissolveFactor = args[0].asFloat();
return returnValue;
}
@@ -98,7 +120,7 @@ void Image::spatialHide() {
}
Common::Point Image::getLeftTop() {
- return Common::Point(_header->_x + _header->_boundingBox.left, _header->_y + _header->_boundingBox.top);
+ return Common::Point(_xOffset + _boundingBox.left, _yOffset + _boundingBox.top);
}
void Image::readChunk(Chunk &chunk) {
diff --git a/engines/mediastation/assets/image.h b/engines/mediastation/assets/image.h
index a0953058e25..a729504c0be 100644
--- a/engines/mediastation/assets/image.h
+++ b/engines/mediastation/assets/image.h
@@ -25,7 +25,6 @@
#include "mediastation/asset.h"
#include "mediastation/datafile.h"
#include "mediastation/bitmap.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -35,17 +34,20 @@ class Image : public SpatialEntity {
friend class Context;
public:
- Image(AssetHeader *header);
+ Image() : SpatialEntity(kAssetTypeImage) {};
virtual ~Image() override;
virtual void readChunk(Chunk &chunk) override;
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void redraw(Common::Rect &rect) override;
- virtual bool isVisible() const override { return _isVisible; }
private:
Bitmap *_bitmap = nullptr;
- bool _isVisible = false;
+ uint _loadType = 0;
+ double _dissolveFactor = 0.0;
+ int _xOffset = 0;
+ int _yOffset = 0;
// Script method implementations.
void spatialShow();
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 59588f039d0..3887330e49f 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -154,13 +154,6 @@ MovieFrame::~MovieFrame() {
_footer = nullptr;
}
-Movie::Movie(AssetHeader *header) : SpatialEntity(header) {
- if (header->_startup == kAssetStartupActive) {
- setActive();
- _showByDefault = true;
- }
-}
-
Movie::~Movie() {
g_engine->_mixer->stopHandle(_soundHandle);
@@ -185,6 +178,61 @@ Movie::~Movie() {
_footers.clear();
}
+void Movie::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderAssetId: {
+ // We already have this asset's ID, so we will just verify it is the same
+ // as the ID we have already read.
+ uint32 duplicateAssetId = chunk.readTypedUint16();
+ if (duplicateAssetId != _id) {
+ warning("Duplicate asset ID %d does not match original ID %d", duplicateAssetId, _id);
+ }
+ break;
+ }
+
+ case kAssetHeaderMovieLoadType:
+ _loadType = chunk.readTypedByte();
+ break;
+
+ case kAssetHeaderChunkReference:
+ _chunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderHasOwnSubfile: {
+ bool hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
+ if (!hasOwnSubfile) {
+ error("Movie doesn't have a subfile");
+ }
+ break;
+ }
+
+ case kAssetHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ case kAssetHeaderMovieAudioChunkReference:
+ _audioChunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderMovieAnimationChunkReference:
+ _animationChunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderSoundInfo:
+ _chunkCount = chunk.readTypedUint16();
+ _rate = chunk.readTypedUint32();
+ break;
+
+ case kAssetHeaderSoundEncoding1:
+ case kAssetHeaderSoundEncoding2:
+ _soundEncoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
+ break;
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
+ }
+}
+
ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
@@ -221,7 +269,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kGetLeftXMethod: {
assert(args.empty());
- double left = static_cast<double>(_header->_boundingBox.left);
+ double left = static_cast<double>(_boundingBox.left);
returnValue.setToFloat(left);
return returnValue;
@@ -229,14 +277,15 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kGetTopYMethod: {
assert(args.empty());
- double top = static_cast<double>(_header->_boundingBox.top);
+ double top = static_cast<double>(_boundingBox.top);
returnValue.setToFloat(top);
return returnValue;
}
case kSetDissolveFactorMethod: {
+ warning("STUB: setDissolveFactor");
assert(args.size() == 1);
- warning("Movie::callMethod(): setDissolveFactor not implemented yet");
+ _dissolveFactor = args[0].asFloat();
return returnValue;
}
@@ -247,13 +296,10 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
void Movie::spatialShow() {
if (_isPlaying) {
- warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already showing", _header->_id);
- return;
- } else if (_isShowing) {
- warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already showing", _header->_id);
+ warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already playing", _id);
return;
} else if (_stills.empty()) {
- warning("Movie::spatialShow(): (%d) No still frame to show", _header->_id);
+ warning("Movie::spatialShow(): (%d) No still frame to show", _id);
return;
}
@@ -267,16 +313,16 @@ void Movie::spatialShow() {
}
setActive();
- _isShowing = true;
+ _isVisible = true;
_isPlaying = false;
}
void Movie::spatialHide() {
if (_isPlaying) {
- warning("Movie::spatialShow(): (%d) Attempted to spatialHide movie that is playing", _header->_id);
+ warning("Movie::spatialShow(): (%d) Attempted to spatialHide movie that is playing", _id);
return;
- } else if (!_isShowing) {
- warning("Movie::spatialHide(): (%d) Attempted to spatialHide movie that is not showing", _header->_id);
+ } else if (!_isVisible) {
+ warning("Movie::spatialHide(): (%d) Attempted to spatialHide movie that is not showing", _id);
return;
}
@@ -286,7 +332,7 @@ void Movie::spatialHide() {
_framesOnScreen.clear();
_framesNotYetShown.clear();
- _isShowing = false;
+ _isVisible = false;
_isPlaying = false;
setInactive();
}
@@ -295,7 +341,7 @@ void Movie::timePlay() {
// TODO: Play movies one chunk at a time, which more directly approximates
// the original's reading from the CD one chunk at a time.
if (_isPlaying) {
- warning("Movie::timePlay(): (%d) Attempted to play a movie that is already playing", _header->_id);
+ warning("Movie::timePlay(): (%d) Attempted to play a movie that is already playing", _id);
return;
}
@@ -313,18 +359,18 @@ void Movie::timePlay() {
}
_framesNotYetShown = _frames;
- _isShowing = true;
+ _isVisible = true;
_isPlaying = true;
setActive();
runEventHandlerIfExists(kMovieBeginEvent);
}
void Movie::timeStop() {
- if (!_isShowing) {
- warning("Movie::timeStop(): (%d) Attempted to stop a movie that isn't showing", _header->_id);
+ if (!_isVisible) {
+ warning("Movie::timeStop(): (%d) Attempted to stop a movie that isn't showing", _id);
return;
} else if (!_isPlaying) {
- warning("Movie::timePlay(): (%d) Attempted to stop a movie that isn't playing", _header->_id);
+ warning("Movie::timePlay(): (%d) Attempted to stop a movie that isn't playing", _id);
return;
}
@@ -359,13 +405,13 @@ void Movie::process() {
}
void Movie::updateFrameState() {
- if (_showByDefault) {
+ if (_isVisible && _atFirstFrame) {
spatialShow();
- _showByDefault = false;
+ _atFirstFrame = false;
}
if (!_isPlaying) {
- debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _header->_id);
+ debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _id);
for (MovieFrame *frame : _framesOnScreen) {
debugC(6, kDebugGraphics, " PERSIST: Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d",
frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
@@ -375,7 +421,7 @@ void Movie::updateFrameState() {
uint currentTime = g_system->getMillis();
uint movieTime = currentTime - _startTime;
- debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _header->_id, movieTime);
+ debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _id, 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
@@ -453,7 +499,7 @@ void Movie::redraw(Common::Rect &rect) {
Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
if (!areaToRedraw.isEmpty()) {
Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
- areaToRedraw.translate(-frame->left() - _header->_boundingBox.left, -frame->top() - _header->_boundingBox.top);
+ areaToRedraw.translate(-frame->left() - _boundingBox.left, -frame->top() - _boundingBox.top);
areaToRedraw.clip(Common::Rect(0, 0, frame->width(), frame->height()));
g_engine->_screen->simpleBlitFrom(frame->_surface, areaToRedraw, originOnScreen);
}
@@ -462,7 +508,7 @@ void Movie::redraw(Common::Rect &rect) {
Common::Rect Movie::getFrameBoundingBox(MovieFrame *frame) {
Common::Rect bbox = frame->boundingBox();
- bbox.translate(_header->_boundingBox.left, _header->_boundingBox.top);
+ bbox.translate(_boundingBox.left, _boundingBox.top);
return bbox;
}
@@ -515,7 +561,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
// READ ALL THE FRAMES IN THIS CHUNK.
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading animation chunks... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
- bool isAnimationChunk = (chunk._id == _header->_animationChunkReference);
+ bool isAnimationChunk = (chunk._id == _animationChunkReference);
if (!isAnimationChunk) {
warning("Movie::readSubfile(): (Frameset %d of %d) No animation chunks found (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
}
@@ -542,17 +588,17 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
// READ THE NEXT CHUNK.
chunk = subfile.nextChunk();
- isAnimationChunk = (chunk._id == _header->_animationChunkReference);
+ isAnimationChunk = (chunk._id == _animationChunkReference);
}
// READ THE AUDIO.
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading audio chunk... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
- bool isAudioChunk = (chunk._id == _header->_audioChunkReference);
+ bool isAudioChunk = (chunk._id == _audioChunkReference);
if (isAudioChunk) {
byte *buffer = (byte *)malloc(chunk._length);
chunk.read((void *)buffer, chunk._length);
Audio::SeekableAudioStream *stream = nullptr;
- switch (_header->_soundEncoding) {
+ switch (_soundEncoding) {
case SoundEncoding::PCM_S16LE_MONO_22050:
stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
break;
@@ -562,7 +608,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
break;
default:
- error("Movie::readSubfile(): Unknown audio encoding 0x%x", static_cast<uint>(_header->_soundEncoding));
+ error("Movie::readSubfile(): Unknown audio encoding 0x%x", static_cast<uint>(_soundEncoding));
}
_audioStreams.push_back(stream);
chunk = subfile.nextChunk();
@@ -572,7 +618,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
// READ THE FOOTER FOR THIS SUBFILE.
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading header chunk... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
- bool isHeaderChunk = (chunk._id == _header->_chunkReference);
+ bool isHeaderChunk = (chunk._id == _chunkReference);
if (isHeaderChunk) {
if (chunk._length != 0x04) {
error("Movie::readSubfile(): Expected movie header chunk of size 0x04, got 0x%x (@0x%llx)", chunk._length, static_cast<long long int>(chunk.pos()));
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index 2dbdc1a9977..62c50bd0bf2 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -25,8 +25,8 @@
#include "common/array.h"
#include "audio/audiostream.h"
+#include "mediastation/asset.h"
#include "mediastation/datafile.h"
-#include "mediastation/assetheader.h"
#include "mediastation/bitmap.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -91,23 +91,32 @@ enum MovieSectionType {
class Movie : public SpatialEntity {
public:
- Movie(AssetHeader *header);
+ Movie() : SpatialEntity(kAssetTypeMovie) {};
virtual ~Movie() override;
virtual void readChunk(Chunk &chunk) override;
virtual void readSubfile(Subfile &subfile, Chunk &chunk) override;
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
virtual void redraw(Common::Rect &rect) override;
- virtual bool isVisible() const override { return _isShowing; }
+ virtual bool isVisible() const override { return _isVisible; }
+
+ uint32 _audioChunkReference = 0;
+ uint32 _animationChunkReference = 0;
private:
- bool _showByDefault = false;
- bool _isShowing = false;
+ SoundEncoding _soundEncoding;
+ uint _chunkCount = 0;
+ uint _rate = 0;
+
+ uint _loadType = 0;
+ double _dissolveFactor = 0.0;
bool _isPlaying = false;
+ bool _atFirstFrame = true;
Common::Array<MovieFrame *> _frames;
Common::Array<MovieFrame *> _stills;
diff --git a/engines/mediastation/assets/palette.cpp b/engines/mediastation/assets/palette.cpp
index c55e78ee6ee..a82d4536e5b 100644
--- a/engines/mediastation/assets/palette.cpp
+++ b/engines/mediastation/assets/palette.cpp
@@ -25,5 +25,25 @@
namespace MediaStation {
-} // End of namespace MediaStation
+Palette::~Palette() {
+ delete _palette;
+ _palette = nullptr;
+}
+
+void Palette::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderPalette: {
+ const uint PALETTE_ENTRIES = 256;
+ const uint PALETTE_BYTES = PALETTE_ENTRIES * 3;
+ byte *buffer = new byte[PALETTE_BYTES];
+ chunk.read(buffer, PALETTE_BYTES);
+ _palette = new Graphics::Palette(buffer, PALETTE_ENTRIES, DisposeAfterUse::YES);
+ break;
+ }
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
+}
+
+} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/palette.h b/engines/mediastation/assets/palette.h
index b3dd3114c70..b54dc21fe8b 100644
--- a/engines/mediastation/assets/palette.h
+++ b/engines/mediastation/assets/palette.h
@@ -22,7 +22,6 @@
#ifndef MEDIASTATION_PALETTE_H
#define MEDIASTATION_PALETTE_H
-#include "mediastation/assetheader.h"
#include "mediastation/asset.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -31,7 +30,12 @@ namespace MediaStation {
class Palette : public Asset {
public:
- Palette(AssetHeader *header) : Asset(header) {};
+ Palette() : Asset(kAssetTypePalette) {};
+ virtual ~Palette() override;
+
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
+
+ Graphics::Palette *_palette = nullptr;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/path.cpp b/engines/mediastation/assets/path.cpp
index 10e637cf38f..5c5b9cb9f02 100644
--- a/engines/mediastation/assets/path.cpp
+++ b/engines/mediastation/assets/path.cpp
@@ -20,12 +20,42 @@
*/
#include "mediastation/assets/path.h"
+#include "mediastation/mediastation.h"
#include "mediastation/debugchannels.h"
namespace MediaStation {
-Path::~Path() {
- _percentComplete = 0;
+void Path::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderStartPoint:
+ _startPoint = chunk.readTypedPoint();
+ break;
+
+ case kAssetHeaderEndPoint:
+ _endPoint = chunk.readTypedPoint();
+ break;
+
+ case kAssetHeaderStepRate: {
+ double _stepRateFloat = chunk.readTypedDouble();
+ // This should always be an integer anyway,
+ // so we'll cast away any fractional part.
+ _stepRate = static_cast<uint32>(_stepRateFloat);
+ break;
+ }
+
+ case kAssetHeaderDuration:
+ // These are stored in the file as fractional seconds,
+ // but we want milliseconds.
+ _duration = static_cast<uint32>(chunk.readTypedTime() * 1000);
+ break;
+
+ case kAssetHeaderPathTotalSteps:
+ _totalSteps = chunk.readTypedUint16();
+ break;
+
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
}
ScriptValue Path::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
@@ -51,12 +81,6 @@ ScriptValue Path::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
return returnValue;
}
- case kSetDissolveFactorMethod: {
- assert(args.size() == 1);
- warning("Path::callMethod(): setDissolveFactor not implemented yet");
- return returnValue;
- }
-
case kIsPlayingMethod: {
assert(args.empty());
returnValue.setToBool(_isActive);
@@ -74,9 +98,9 @@ void Path::timePlay() {
return;
}
- if (_header->_duration == 0) {
+ if (_duration == 0) {
warning("Path::timePlay(): Got zero duration");
- } else if (_header->_stepRate == 0) {
+ } else if (_stepRate == 0) {
error("Path::timePlay(): Got zero step rate");
}
@@ -84,8 +108,8 @@ void Path::timePlay() {
_percentComplete = 0;
_nextPathStepTime = 0;
_currentStep = 0;
- _totalSteps = (_header->_duration * _header->_stepRate) / 1000;
- _stepDurationInMilliseconds = 1000 / _header->_stepRate;
+ _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");
@@ -125,14 +149,12 @@ void Path::process() {
void Path::setDuration(uint durationInMilliseconds) {
// TODO: Do we need to save the original duration?
debugC(5, kDebugScript, "Path::setDuration(): Setting duration to %d ms", durationInMilliseconds);
- _header->_duration = durationInMilliseconds;
+ _duration = durationInMilliseconds;
}
-
double Path::percentComplete() {
debugC(5, kDebugScript, "Path::percentComplete(): Returning percent complete %f%%", _percentComplete * 100);
return _percentComplete;
}
} // End of namespace MediaStation
-
diff --git a/engines/mediastation/assets/path.h b/engines/mediastation/assets/path.h
index 60239bc7ef5..fe204387efe 100644
--- a/engines/mediastation/assets/path.h
+++ b/engines/mediastation/assets/path.h
@@ -22,7 +22,6 @@
#ifndef MEDIASTATION_PATH_H
#define MEDIASTATION_PATH_H
-#include "mediastation/assetheader.h"
#include "mediastation/asset.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -31,11 +30,11 @@ namespace MediaStation {
class Path : public Asset {
public:
- Path(AssetHeader *header) : Asset(header) {};
- virtual ~Path() override;
+ Path() : Asset(kAssetTypePath) {};
virtual void process() override;
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
private:
@@ -45,6 +44,11 @@ private:
uint _nextPathStepTime = 0;
uint _stepDurationInMilliseconds = 0;
+ Common::Point _startPoint;
+ Common::Point _endPoint;
+ uint32 _stepRate = 0;
+ uint32 _duration = 0;
+
// Method implementations.
void timePlay();
void setDuration(uint durationInMilliseconds);
diff --git a/engines/mediastation/assets/screen.cpp b/engines/mediastation/assets/screen.cpp
index c9b6d131e52..2d6cb41a807 100644
--- a/engines/mediastation/assets/screen.cpp
+++ b/engines/mediastation/assets/screen.cpp
@@ -24,4 +24,15 @@
namespace MediaStation {
+void Screen::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderCursorResourceId:
+ _cursorResourceId = chunk.readTypedUint16();
+ break;
+
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
+}
+
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/screen.h b/engines/mediastation/assets/screen.h
index c5846240361..78ef10b597a 100644
--- a/engines/mediastation/assets/screen.h
+++ b/engines/mediastation/assets/screen.h
@@ -22,7 +22,6 @@
#ifndef MEDIASTATION_SCREEN_H
#define MEDIASTATION_SCREEN_H
-#include "mediastation/assetheader.h"
#include "mediastation/asset.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -34,7 +33,11 @@ namespace MediaStation {
// then a Screen asset header.
class Screen : public Asset {
public:
- Screen(AssetHeader *header) : Asset(header) {};
+ Screen() : Asset(kAssetTypeScreen) {};
+
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
+
+ uint _cursorResourceId = 0;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index 100fb9a2f31..a7aa6d33d68 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -28,12 +28,6 @@
namespace MediaStation {
-Sound::Sound(AssetHeader *header) : Asset(header) {
- if (_header != nullptr) {
- _encoding = _header->_soundEncoding;
- }
-}
-
Sound::~Sound() {
g_engine->_mixer->stopHandle(_handle);
@@ -43,6 +37,45 @@ Sound::~Sound() {
_streams.clear();
}
+void Sound::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderAssetId: {
+ // We already have this asset's ID, so we will just verify it is the same
+ // as the ID we have already read.
+ uint32 duplicateAssetId = chunk.readTypedUint16();
+ if (duplicateAssetId != _id) {
+ warning("Duplicate asset ID %d does not match original ID %d", duplicateAssetId, _id);
+ }
+ break;
+ }
+
+ case kAssetHeaderChunkReference:
+ _chunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderHasOwnSubfile:
+ _hasOwnSubfile = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kAssetHeaderSoundInfo:
+ _chunkCount = chunk.readTypedUint16();
+ _rate = chunk.readTypedUint32();
+ break;
+
+ case kAssetHeaderSoundEncoding1:
+ case kAssetHeaderSoundEncoding2:
+ _encoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
+ break;
+
+ case kAssetHeaderMovieLoadType:
+ _loadType = chunk.readTypedByte();
+ break;
+
+ default:
+ Asset::readParameter(chunk, paramType);
+ }
+}
+
void Sound::process() {
processTimeEventHandlers();
@@ -80,7 +113,7 @@ void Sound::readChunk(Chunk &chunk) {
byte *buffer = (byte *)malloc(chunk._length);
chunk.read((void *)buffer, chunk._length);
Audio::SeekableAudioStream *stream = nullptr;
- switch (_header->_soundEncoding) {
+ switch (_encoding) {
case SoundEncoding::PCM_S16LE_MONO_22050:
stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
break;
@@ -96,20 +129,19 @@ void Sound::readChunk(Chunk &chunk) {
break;
default:
- error("Sound::readChunk(): Unknown audio encoding 0x%x", static_cast<uint>(_header->_soundEncoding));
+ error("Sound::readChunk(): Unknown audio encoding 0x%x", static_cast<uint>(_encoding));
}
_streams.push_back(stream);
debugC(5, kDebugLoading, "Sound::readChunk(): Finished reading audio chunk (@0x%llx)", static_cast<long long int>(chunk.pos()));
}
void Sound::readSubfile(Subfile &subfile, Chunk &chunk) {
- uint32 totalChunks = _header->_chunkCount;
uint32 expectedChunkId = chunk._id;
- debugC(5, kDebugLoading, "Sound::readSubfile(): Reading %d chunks", totalChunks);
+ debugC(5, kDebugLoading, "Sound::readSubfile(): Reading %d chunks", _chunkCount);
readChunk(chunk);
- for (uint i = 1; i < totalChunks; i++) {
- debugC(5, kDebugLoading, "Sound::readSubfile(): Reading chunk %d of %d", i, totalChunks);
+ for (uint i = 1; i < _chunkCount; i++) {
+ debugC(5, kDebugLoading, "Sound::readSubfile(): Reading chunk %d of %d", i, _chunkCount);
chunk = subfile.nextChunk();
if (chunk._id != expectedChunkId) {
error("Sound::readSubfile(): Expected chunk %s, got %s", tag2str(expectedChunkId), tag2str(chunk._id));
diff --git a/engines/mediastation/assets/sound.h b/engines/mediastation/assets/sound.h
index 41435f730e4..d30124533ab 100644
--- a/engines/mediastation/assets/sound.h
+++ b/engines/mediastation/assets/sound.h
@@ -22,11 +22,11 @@
#ifndef MEDIASTATION_ASSETS_SOUND_H
#define MEDIASTATION_ASSETS_SOUND_H
+#include "audio/mixer.h"
#include "audio/audiostream.h"
#include "mediastation/asset.h"
#include "mediastation/datafile.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -34,9 +34,10 @@ namespace MediaStation {
class Sound : public Asset {
public:
- Sound(AssetHeader *header);
+ Sound() : Asset(kAssetTypeSound) {};
~Sound();
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
@@ -44,6 +45,10 @@ public:
virtual void readSubfile(Subfile &subFile, Chunk &chunk) override;
private:
+ uint _loadType = 0;
+ uint _chunkCount = 0;
+ uint _rate = 0;
+ bool _hasOwnSubfile = false;
SoundEncoding _encoding;
Audio::SoundHandle _handle;
Common::Array<Audio::SeekableAudioStream *> _streams;
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 575ee5d82f4..8fdd5094310 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -60,18 +60,11 @@ uint32 SpriteFrame::index() {
return _bitmapHeader->_index;
}
-Sprite::Sprite(AssetHeader *header) : SpatialEntity(header) {
- if (header->_startup == kAssetStartupActive) {
- setActive();
- _isShowing = true;
- _showFirstFrame = true;
- }
-}
Sprite::~Sprite() {
// If we're just referencing another asset's frames,
// don't delete those frames.
- if (_header->_assetReference == 0) {
+ if (_assetReference == 0) {
for (SpriteFrame *frame : _frames) {
delete frame;
}
@@ -79,6 +72,44 @@ Sprite::~Sprite() {
_frames.clear();
}
+void Sprite::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderChunkReference:
+ _chunkReference = chunk.readTypedChunkReference();
+ break;
+
+ case kAssetHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ case kAssetHeaderFrameRate:
+ _frameRate = static_cast<uint32>(chunk.readTypedDouble());
+ break;
+
+ case kAssetHeaderLoadType:
+ _loadType = chunk.readTypedByte();
+ break;
+
+ case kAssetHeaderSpriteChunkCount:
+ _frameCount = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderSpriteFrameMapping: {
+ uint32 externalFrameId = chunk.readTypedUint16();
+ uint32 internalFrameId = chunk.readTypedUint16();
+ uint32 unk1 = chunk.readTypedUint16();
+ if (unk1 != internalFrameId) {
+ warning("AssetHeader::readSection(): Repeated internalFrameId doesn't match");
+ }
+ _spriteFrameMapping.setVal(externalFrameId, internalFrameId);
+ break;
+ }
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
+ }
+}
+
ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
@@ -95,6 +126,13 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
return returnValue;
}
+ case kSetDissolveFactorMethod: {
+ warning("STUB: setDissolveFactor");
+ assert(args.size() == 1);
+ _dissolveFactor = args[0].asFloat();
+ return returnValue;
+ }
+
case kTimePlayMethod: {
assert(args.empty());
timePlay();
@@ -116,7 +154,7 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
case kSetCurrentClipMethod: {
assert(args.size() <= 1);
if (args.size() == 1 && args[0].asParamToken() != 0) {
- error("Sprite::callMethod(): (%d) setClip() called with unhandled arg: %d", _header->_id, args[0].asParamToken());
+ error("Sprite::callMethod(): (%d) setClip() called with unhandled arg: %d", _id, args[0].asParamToken());
}
setCurrentClip();
return returnValue;
@@ -125,7 +163,7 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
case kSetSpriteFrameByIdMethod: {
assert(args.size() == 1);
uint32 externalFrameId = args[0].asParamToken();
- uint32 internalFrameId = _header->_spriteFrameMapping.getVal(externalFrameId);
+ uint32 internalFrameId = _spriteFrameMapping.getVal(externalFrameId);
showFrame(_frames[internalFrameId]);
return returnValue;
}
@@ -141,35 +179,35 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
}
void Sprite::spatialShow() {
- if (_isShowing) {
- warning("Sprite::spatialShow(): (%d) Attempted to spatialShow when already showing", _header->_id);
+ if (_isVisible) {
+ warning("Sprite::spatialShow(): (%d) Attempted to spatialShow when already showing", _id);
return;
}
showFrame(_frames[0]);
setActive();
- _isShowing = true;
+ _isVisible = true;
_isPlaying = false;
}
void Sprite::spatialHide() {
- if (!_isShowing) {
- warning("Sprite::spatialHide(): (%d) Attempted to spatialHide when not showing", _header->_id);
+ if (!_isVisible) {
+ warning("Sprite::spatialHide(): (%d) Attempted to spatialHide when not showing", _id);
return;
}
showFrame(nullptr);
setInactive();
- _isShowing = false;
+ _isVisible = false;
_isPlaying = false;
}
void Sprite::timePlay() {
- if (!_isShowing) {
- warning("Sprite::timePlay(): (%d) Attempted to timePlay when not showing", _header->_id);
+ if (!_isVisible) {
+ warning("Sprite::timePlay(): (%d) Attempted to timePlay when not showing", _id);
return;
} else if (_isPlaying) {
- warning("Sprite::timePlay(): (%d) Attempted to timePlay when already playing", _header->_id);
+ warning("Sprite::timePlay(): (%d) Attempted to timePlay when already playing", _id);
return;
}
@@ -181,11 +219,11 @@ void Sprite::timePlay() {
}
void Sprite::timeStop() {
- if (!_isShowing) {
- warning("Sprite::timeStop(): (%d) Attempted to timeStop when not showing", _header->_id);
+ if (!_isVisible) {
+ warning("Sprite::timeStop(): (%d) Attempted to timeStop when not showing", _id);
return;
} else if (!_isPlaying) {
- warning("Sprite::timeStop(): (%d) Attempted to timeStop when not playing", _header->_id);
+ warning("Sprite::timeStop(): (%d) Attempted to timeStop when not playing", _id);
return;
}
@@ -195,7 +233,7 @@ void Sprite::timeStop() {
void Sprite::movieReset() {
setActive();
- if (_isShowing) {
+ if (_isVisible) {
showFrame(_frames[0]);
} else {
showFrame(nullptr);
@@ -211,7 +249,7 @@ void Sprite::setCurrentClip() {
if (_currentFrameIndex < _frames.size()) {
showFrame(_frames[_currentFrameIndex++]);
} else {
- warning("Sprite::setCurrentClip(): (%d) Attempted to increment past number of frames", _header->_id);
+ warning("Sprite::setCurrentClip(): (%d) Attempted to increment past number of frames", _id);
}
}
@@ -229,15 +267,15 @@ void Sprite::readChunk(Chunk &chunk) {
// TODO: Are these in exactly reverse order? If we can just reverse the
// whole thing once.
- Common::sort(_frames.begin(), _frames.end(), [](SpriteFrame * a, SpriteFrame * b) {
+ Common::sort(_frames.begin(), _frames.end(), [](SpriteFrame *a, SpriteFrame *b) {
return a->index() < b->index();
});
}
void Sprite::updateFrameState() {
- if (_showFirstFrame) {
+ if (_isVisible && _atFirstFrame) {
showFrame(_frames[0]);
- _showFirstFrame = false;
+ _atFirstFrame = false;
return;
}
@@ -248,15 +286,15 @@ void Sprite::updateFrameState() {
if (!_isPlaying) {
if (_activeFrame != nullptr) {
debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)",
- _header->_id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
+ _id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
} else {
- debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing, no persistent frame", _header->_id);
+ debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing, no persistent frame", _id);
}
return;
}
debugC(5, kDebugGraphics, "Sprite::updateFrameState(): (%d) Frame %d (%d x %d) @ (%d, %d)",
- _header->_id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
+ _id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
uint currentTime = g_system->getMillis() - _startTime;
bool drawNextFrame = currentTime >= _nextFrameTime;
@@ -266,7 +304,7 @@ void Sprite::updateFrameState() {
showFrame(_frames[_currentFrameIndex]);
- uint frameDuration = 1000 / _header->_frameRate;
+ uint frameDuration = 1000 / _frameRate;
_nextFrameTime = ++_currentFrameIndex * frameDuration;
bool spriteFinishedPlaying = (_currentFrameIndex == _frames.size());
@@ -291,7 +329,7 @@ void Sprite::updateFrameState() {
}
void Sprite::redraw(Common::Rect &rect) {
- if (_activeFrame == nullptr || !_isShowing) {
+ if (_activeFrame == nullptr || !_isVisible) {
return;
}
@@ -299,7 +337,7 @@ void Sprite::redraw(Common::Rect &rect) {
Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
if (!areaToRedraw.isEmpty()) {
Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
- areaToRedraw.translate(-_activeFrame->left() - _header->_boundingBox.left, -_activeFrame->top() - _header->_boundingBox.top);
+ areaToRedraw.translate(-_activeFrame->left() - _boundingBox.left, -_activeFrame->top() - _boundingBox.top);
areaToRedraw.clip(Common::Rect(0, 0, _activeFrame->width(), _activeFrame->height()));
g_engine->_screen->simpleBlitFrom(_activeFrame->_surface, areaToRedraw, originOnScreen);
}
@@ -322,7 +360,7 @@ Common::Rect Sprite::getActiveFrameBoundingBox() {
// The frame dimensions are relative to those of the sprite movie.
// So we must get the absolute coordinates.
Common::Rect bbox = _activeFrame->boundingBox();
- bbox.translate(_header->_boundingBox.left, _header->_boundingBox.top);
+ bbox.translate(_boundingBox.left, _boundingBox.top);
return bbox;
}
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index 8994b412250..ebbebb98604 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -26,7 +26,6 @@
#include "common/array.h"
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/datafile.h"
#include "mediastation/bitmap.h"
#include "mediastation/mediascript/scriptvalue.h"
@@ -63,24 +62,29 @@ class Sprite : public SpatialEntity {
friend class Context;
public:
- Sprite(AssetHeader *header);
+ Sprite() : SpatialEntity(kAssetTypeSprite) {};
~Sprite();
virtual void process() override;
virtual void redraw(Common::Rect &rect) override;
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
- virtual bool isVisible() const override { return _isShowing; }
+ virtual bool isVisible() const override { return _isVisible; }
virtual void readChunk(Chunk &chunk) override;
private:
+ double _dissolveFactor = 0.0;
+ uint _loadType = 0;
+ uint _frameRate = 0;
+ uint _frameCount = 0;
+ Common::HashMap<uint32, uint32> _spriteFrameMapping;
Common::Array<SpriteFrame *> _frames;
SpriteFrame *_activeFrame = nullptr;
- bool _showFirstFrame = true;
- bool _isShowing = false;
bool _isPlaying = false;
+ bool _atFirstFrame = true;
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
diff --git a/engines/mediastation/assets/stage.h b/engines/mediastation/assets/stage.h
deleted file mode 100644
index e6768a07719..00000000000
--- a/engines/mediastation/assets/stage.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/* ScummVM - Graphic Adventure Engine
- *
- * ScummVM is the legal property of its developers, whose names
- * are too numerous to list here. Please refer to the COPYRIGHT
- * file distributed with this source distribution.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#ifndef MEDIASTATION_STAGE_H
-#define MEDIASTATION_STAGE_H
-
-#include "mediastation/asset.h"
-
-namespace MediaStation {
-
-class Camera : public Asset {
-public:
- Camera(AssetHeader *header) : Asset(header) {};
-
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override { error("CallMethod not implemented"); };
- virtual void process() override {};
-};
-
-class Stage : public Asset {
-public:
- Stage(AssetHeader *header) : Asset(header) {};
-
- virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override { error("CallMethod not implemented"); };
- virtual void process() override {};
-
-private:
- Common::Array<Asset *> _children;
-};
-
-}
-
-#endif
\ No newline at end of file
diff --git a/engines/mediastation/assets/text.cpp b/engines/mediastation/assets/text.cpp
index 59dead1d9c2..1006a517039 100644
--- a/engines/mediastation/assets/text.cpp
+++ b/engines/mediastation/assets/text.cpp
@@ -23,6 +23,57 @@
namespace MediaStation {
+void Text::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
+ switch (paramType) {
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
+ case kAssetHeaderEditable:
+ _editable = chunk.readTypedByte();
+ break;
+
+ case kAssetHeaderLoadType:
+ _loadType = chunk.readTypedByte();
+ break;
+
+ case kAssetHeaderFontId:
+ _fontAssetId = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderTextMaxLength:
+ _maxTextLength = chunk.readTypedUint16();
+ break;
+
+ case kAssetHeaderInitialText:
+ _text = chunk.readTypedString();
+ break;
+
+ case kAssetHeaderTextJustification:
+ _justification = static_cast<TextJustification>(chunk.readTypedUint16());
+ break;
+
+ case kAssetHeaderTextPosition:
+ _position = static_cast<TextPosition>(chunk.readTypedUint16());
+ break;
+
+ case kAssetHeaderTextCharacterClass: {
+ CharacterClass characterClass;
+ characterClass.firstAsciiCode = chunk.readTypedUint16();
+ characterClass.lastAsciiCode = chunk.readTypedUint16();
+ _acceptedInput.push_back(characterClass);
+ break;
+ }
+
+ case kAssetHeaderDissolveFactor:
+ _dissolveFactor = chunk.readTypedDouble();
+ break;
+
+ default:
+ SpatialEntity::readParameter(chunk, paramType);
+ }
+}
+
ScriptValue Text::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
ScriptValue returnValue;
@@ -57,7 +108,7 @@ ScriptValue Text::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
Common::String Text::text() const {
- return _header->_text;
+ return _text;
}
void Text::setText(Common::String text) {
diff --git a/engines/mediastation/assets/text.h b/engines/mediastation/assets/text.h
index e7dd97bb690..0e186f82220 100644
--- a/engines/mediastation/assets/text.h
+++ b/engines/mediastation/assets/text.h
@@ -25,21 +25,47 @@
#include "common/str.h"
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
namespace MediaStation {
+enum TextJustification {
+ kTextJustificationLeft = 0x25c,
+ kTextJustificationRight = 0x25d,
+ kTextJustificationCenter = 0x25e
+};
+
+enum TextPosition {
+ kTextPositionMiddle = 0x25e,
+ kTextPositionTop = 0x260,
+ kTextPositionBotom = 0x261
+};
+
+struct CharacterClass {
+ uint firstAsciiCode = 0;
+ uint lastAsciiCode = 0;
+};
+
class Text : public SpatialEntity {
public:
- Text(AssetHeader *header) : SpatialEntity(header) {};
+ Text() : SpatialEntity(kAssetTypeText) {};
virtual bool isVisible() const override { return _isVisible; }
+ virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
private:
+ bool _editable = false;
+ uint _loadType = 0;
+ double _dissolveFactor = 0.0;
bool _isVisible = false;
+ Common::String _text;
+ uint _maxTextLength = 0;
+ uint _fontAssetId = 0;
+ TextJustification _justification;
+ TextPosition _position;
+ Common::Array<CharacterClass> _acceptedInput;
// Method implementations.
Common::String text() const;
diff --git a/engines/mediastation/assets/timer.cpp b/engines/mediastation/assets/timer.cpp
index 284f971510e..323cf409858 100644
--- a/engines/mediastation/assets/timer.cpp
+++ b/engines/mediastation/assets/timer.cpp
@@ -68,8 +68,8 @@ void Timer::timePlay() {
// TODO: Is there a better way to find out what the max time is? Do we have to look
// through each of the timer event handlers to figure it out?
_duration = 0;
- const Common::Array<EventHandler *> &_timeHandlers = _header->_eventHandlers.getValOrDefault(kTimerEvent);
- for (EventHandler *timeEvent : _timeHandlers) {
+ const Common::Array<EventHandler *> &timeHandlers = _eventHandlers.getValOrDefault(kTimerEvent);
+ for (EventHandler *timeEvent : timeHandlers) {
// Indeed float, not time.
double timeEventInFractionalSeconds = timeEvent->_argumentValue.asFloat();
uint timeEventInMilliseconds = timeEventInFractionalSeconds * 1000;
diff --git a/engines/mediastation/assets/timer.h b/engines/mediastation/assets/timer.h
index ecf037becf8..2775b78001e 100644
--- a/engines/mediastation/assets/timer.h
+++ b/engines/mediastation/assets/timer.h
@@ -23,7 +23,6 @@
#define MEDIASTATION_TIMER_H
#include "mediastation/asset.h"
-#include "mediastation/assetheader.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -31,7 +30,7 @@ namespace MediaStation {
class Timer : public Asset {
public:
- Timer(AssetHeader *header) : Asset(header) {};
+ Timer() : Asset(kAssetTypeTimer) {};
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index 8ef973a6c2a..441396a33fe 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -26,7 +26,7 @@
#include "graphics/managed_surface.h"
#include "mediastation/datafile.h"
-#include "mediastation/assetheader.h"
+#include "mediastation/asset.h"
namespace MediaStation {
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 5fbd406c925..23a868975c3 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -79,7 +79,7 @@ Context::Context(const Common::Path &path) : Datafile(path) {
// these and create the appropriate references.
for (auto it = _assets.begin(); it != _assets.end(); ++it) {
Asset *asset = it->_value;
- uint referencedAssetId = asset->getHeader()->_assetReference;
+ uint referencedAssetId = asset->_assetReference;
if (referencedAssetId != 0) {
switch (asset->type()) {
case kAssetTypeImage: {
@@ -155,7 +155,7 @@ void Context::registerActiveAssets() {
}
}
-void Context::readParametersSection(Chunk &chunk) {
+void Context::readCreateContextData(Chunk &chunk) {
_fileNumber = chunk.readTypedUint16();
ContextParametersSectionType sectionType = static_cast<ContextParametersSectionType>(chunk.readTypedUint16());
@@ -182,7 +182,7 @@ void Context::readParametersSection(Chunk &chunk) {
}
case kContextParametersVariable: {
- readVariable(chunk);
+ readCreateVariableData(chunk);
break;
}
@@ -200,20 +200,86 @@ void Context::readParametersSection(Chunk &chunk) {
}
}
-void Context::readVariable(Chunk &chunk) {
+Asset *Context::readCreateAssetData(Chunk &chunk) {
+ uint contextId = chunk.readTypedUint16();
+ AssetType type = static_cast<AssetType>(chunk.readTypedUint16());
+ uint id = chunk.readTypedUint16();
+ debugC(4, kDebugLoading, "_type = 0x%x, _id = 0x%x", static_cast<uint>(type), id);
+
+ Asset *asset = nullptr;
+ switch (type) {
+ case kAssetTypeImage:
+ asset = new Image();
+ break;
+
+ case kAssetTypeMovie:
+ asset = new Movie();
+ break;
+
+ case kAssetTypeSound:
+ asset = new Sound();
+ break;
+
+ case kAssetTypePalette:
+ asset = new Palette();
+ break;
+
+ case kAssetTypePath:
+ asset = new Path();
+ break;
+
+ case kAssetTypeTimer:
+ asset = new Timer();
+ break;
+
+ case kAssetTypeHotspot:
+ asset = new Hotspot();
+ break;
+
+ case kAssetTypeSprite:
+ asset = new Sprite();
+ break;
+
+ case kAssetTypeCanvas:
+ asset = new Canvas();
+ break;
+
+ case kAssetTypeScreen:
+ asset = new Screen();
+ _screenAsset = static_cast<Screen *>(asset);
+ break;
+
+ case kAssetTypeFont:
+ asset = new Font();
+ break;
+
+ case kAssetTypeText:
+ asset = new Text();
+ break;
+
+ default:
+ error("No class for asset type 0x%x (@0x%llx)", static_cast<uint>(type), static_cast<long long int>(chunk.pos()));
+ }
+ asset->setId(id);
+ asset->setContextId(contextId);
+ asset->initFromParameterStream(chunk);
+ return asset;
+}
+
+void Context::readCreateVariableData(Chunk &chunk) {
uint repeatedFileNumber = chunk.readTypedUint16();
if (repeatedFileNumber != _fileNumber) {
- warning("Context::readVariable(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
+ warning("Context::readCreateVariableData(): Repeated file number didn't match: %d != %d", repeatedFileNumber, _fileNumber);
}
uint id = chunk.readTypedUint16();
if (g_engine->getVariable(id) != nullptr) {
- error("Context::readVariable(): Global variable %d already exists", id);
+ error("Global variable %d already exists", id);
}
ScriptValue *value = new ScriptValue(&chunk);
_variables.setVal(id, value);
- debugC(5, kDebugScript, "Context::readVariable(): Created global variable %d (type: %s)",
+ debugC(5, kDebugScript, "Created global variable %d (type: %s)",
id, scriptValueTypeToStr(value->getType()));
}
@@ -238,7 +304,7 @@ void Context::readNewStyleHeaderSections(Subfile &subfile, Chunk &chunk) {
}
// Read this header section.
- moreSectionsToRead = readHeaderSection(subfile, chunk);
+ moreSectionsToRead = readHeaderSection(chunk);
if (subfile.atEnd()) {
break;
} else {
@@ -286,12 +352,12 @@ void Context::readAssetFromLaterSubfile(Subfile &subfile) {
asset->readSubfile(subfile, chunk);
}
-bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
+bool Context::readHeaderSection(Chunk &chunk) {
uint16 sectionType = chunk.readTypedUint16();
debugC(5, kDebugLoading, "Context::readHeaderSection(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
switch (sectionType) {
case kContextParametersSection: {
- readParametersSection(chunk);
+ readCreateContextData(chunk);
break;
}
@@ -322,76 +388,24 @@ bool Context::readHeaderSection(Subfile &subfile, Chunk &chunk) {
}
case kContextAssetHeaderSection: {
- Asset *asset = nullptr;
- AssetHeader *header = new AssetHeader(chunk);
- switch (header->_type) {
- case kAssetTypeImage:
- asset = new Image(header);
- break;
-
- case kAssetTypeMovie:
- asset = new Movie(header);
- break;
-
- case kAssetTypeSound:
- asset = new Sound(header);
- break;
-
- case kAssetTypePalette:
- asset = new Palette(header);
- break;
-
- case kAssetTypePath:
- asset = new Path(header);
- break;
-
- case kAssetTypeTimer:
- asset = new Timer(header);
- break;
-
- case kAssetTypeHotspot:
- asset = new Hotspot(header);
- break;
-
- case kAssetTypeSprite:
- asset = new Sprite(header);
- break;
-
- case kAssetTypeCanvas:
- asset = new Canvas(header);
- break;
-
- case kAssetTypeScreen:
- asset = new Screen(header);
- _screenAsset = asset;
- break;
-
- case kAssetTypeFont:
- asset = new Font(header);
- break;
-
- case kAssetTypeText:
- asset = new Text(header);
- break;
-
- default:
- error("Context::readHeaderSection(): No class for asset type 0x%x (@0x%llx)", static_cast<uint>(header->_type), static_cast<long long int>(chunk.pos()));
+ Asset *asset = readCreateAssetData(chunk);
+ if (g_engine->getAssetById(asset->id())) {
+ error("Context::readHeaderSection(): Asset with ID 0x%d was already defined in this title", asset->id());
}
- if (g_engine->getAssetById(header->_id)) {
- error("Context::readHeaderSection(): Asset with ID 0x%d was already defined in this title", header->_id);
- }
- _assets.setVal(header->_id, asset);
- if (header->_chunkReference != 0) {
- debugC(5, kDebugLoading, "Context::readHeaderSection(): Storing asset with chunk ID \"%s\" (0x%x)", tag2str(header->_chunkReference), header->_chunkReference);
- _assetsByChunkReference.setVal(header->_chunkReference, asset);
+ _assets.setVal(asset->id(), asset);
+ if (asset->_chunkReference != 0) {
+ debugC(5, kDebugLoading, "Context::readHeaderSection(): Storing asset with chunk ID \"%s\" (0x%x)", tag2str(asset->_chunkReference), asset->_chunkReference);
+ _assetsByChunkReference.setVal(asset->_chunkReference, asset);
}
- // TODO: Store the movie chunk references better.
- if (header->_audioChunkReference != 0) {
- _assetsByChunkReference.setVal(header->_audioChunkReference, asset);
- }
- if (header->_animationChunkReference != 0) {
- _assetsByChunkReference.setVal(header->_animationChunkReference, asset);
+ if (asset->type() == kAssetTypeMovie) {
+ Movie *movie = static_cast<Movie *>(asset);
+ if (movie->_audioChunkReference != 0) {
+ _assetsByChunkReference.setVal(movie->_audioChunkReference, asset);
+ }
+ if (movie->_animationChunkReference != 0) {
+ _assetsByChunkReference.setVal(movie->_animationChunkReference, asset);
+ }
}
// TODO: This datum only appears sometimes.
uint unk2 = chunk.readTypedUint16();
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index a7ed7e62853..986d64a9f1e 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -28,7 +28,7 @@
#include "graphics/palette.h"
#include "mediastation/datafile.h"
-#include "mediastation/assetheader.h"
+#include "mediastation/asset.h"
#include "mediastation/mediascript/function.h"
namespace MediaStation {
@@ -53,6 +53,8 @@ enum ContextSectionType {
kContextFunctionSection = 0x0031
};
+class Screen;
+
class Context : public Datafile {
public:
Context(const Common::Path &path);
@@ -62,7 +64,7 @@ public:
uint32 _subfileCount;
uint32 _fileSize;
Graphics::Palette *_palette = nullptr;
- Asset *_screenAsset = nullptr;
+ Screen *_screenAsset = nullptr;
Asset *getAssetById(uint assetId);
Asset *getAssetByChunkReference(uint chunkReference);
@@ -82,11 +84,13 @@ private:
Common::HashMap<uint, Asset *> _assetsByChunkReference;
Common::HashMap<uint, ScriptValue *> _variables;
- void readParametersSection(Chunk &chunk);
- void readVariable(Chunk &chunk);
void readOldStyleHeaderSections(Subfile &subfile, Chunk &chunk);
void readNewStyleHeaderSections(Subfile &subfile, Chunk &chunk);
- bool readHeaderSection(Subfile &subfile, Chunk &chunk);
+
+ bool readHeaderSection(Chunk &chunk);
+ void readCreateContextData(Chunk &chunk);
+ Asset *readCreateAssetData(Chunk &chunk);
+ void readCreateVariableData(Chunk &chunk);
void readAssetInFirstSubfile(Chunk &chunk);
void readAssetFromLaterSubfile(Subfile &subfile);
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 7001e7edf8e..69f417f4527 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -28,8 +28,10 @@
#include "mediastation/boot.h"
#include "mediastation/context.h"
#include "mediastation/asset.h"
-#include "mediastation/assets/hotspot.h"
#include "mediastation/assets/movie.h"
+#include "mediastation/assets/screen.h"
+#include "mediastation/assets/palette.h"
+#include "mediastation/assets/hotspot.h"
#include "mediastation/mediascript/scriptconstants.h"
namespace MediaStation {
@@ -268,7 +270,11 @@ void MediaStationEngine::setCursor(uint id) {
}
void MediaStationEngine::refreshActiveHotspot() {
- Asset *hotspot = findAssetToAcceptMouseEvents();
+ Asset *asset = findAssetToAcceptMouseEvents();
+ if (asset != nullptr && asset->type() != kAssetTypeHotspot) {
+ return;
+ }
+ Hotspot *hotspot = static_cast<Hotspot *>(asset);
if (hotspot != _currentHotspot) {
if (_currentHotspot != nullptr) {
_currentHotspot->runEventHandlerIfExists(kMouseExitedEvent);
@@ -277,11 +283,11 @@ void MediaStationEngine::refreshActiveHotspot() {
_currentHotspot = hotspot;
if (hotspot != nullptr) {
debugC(5, kDebugEvents, "refreshActiveHotspot(): (%d, %d): Entered hotspot %d", _mousePos.x, _mousePos.y, hotspot->id());
- setCursor(hotspot->getHeader()->_cursorResourceId);
+ setCursor(hotspot->_cursorResourceId);
hotspot->runEventHandlerIfExists(kMouseEnteredEvent);
} else {
// There is no hotspot, so set the default cursor for this screen instead.
- setCursor(_currentContext->_screenAsset->getHeader()->_cursorResourceId);
+ setCursor(_currentContext->_screenAsset->_cursorResourceId);
}
}
@@ -373,17 +379,14 @@ Context *MediaStationEngine::loadContext(uint32 contextId) {
return context;
}
-void MediaStationEngine::setPalette(Asset *palette) {
- assert(palette != nullptr);
- setPaletteFromHeader(palette->getHeader());
-}
-
-void MediaStationEngine::setPaletteFromHeader(AssetHeader *header) {
- assert(header != nullptr);
- if (header->_palette != nullptr) {
- _screen->setPalette(*header->_palette);
+void MediaStationEngine::setPalette(Asset *asset) {
+ if (asset == nullptr) {
+ error("Requested palette not found");
+ } else if (asset->type() != kAssetTypePalette) {
+ error("Requested palette %d is not a palette", asset->id());
} else {
- warning("MediaStationEngine::setPaletteFromHeader(): Asset %d does not have a palette. Current palette will be unchanged.", header->_id);
+ Palette *paletteAsset = static_cast<Palette *>(asset);
+ _screen->setPalette(*paletteAsset->_palette);
}
}
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index b0e07b0064e..10e3f1a0f9f 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -46,6 +46,7 @@
namespace MediaStation {
struct MediaStationGameDescription;
+class Hotspot;
// Most Media Station titles follow this file structure from the root directory
// of the CD-ROM:
@@ -118,14 +119,13 @@ private:
Boot *_boot = nullptr;
Common::List<Asset *> _assetsPlaying;
Common::HashMap<uint, Context *> _loadedContexts;
- Asset *_currentHotspot = nullptr;
+ Hotspot *_currentHotspot = nullptr;
uint _requestedScreenBranchId = 0;
Common::Array<uint> _requestedContextReleaseId;
void doBranchToScreen();
Context *loadContext(uint32 contextId);
- void setPaletteFromHeader(AssetHeader *header);
void releaseContext(uint32 contextId);
Asset *findAssetToAcceptMouseEvents();
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index 8c43dddd48c..b0a38fea5f8 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -2,7 +2,6 @@ MODULE := engines/mediastation
MODULE_OBJS = \
asset.o \
- assetheader.o \
assets/canvas.o \
assets/font.o \
assets/hotspot.o \
Commit: a7aae9b0b1ab7797eae817535b650a8b30010894
https://github.com/scummvm/scummvm/commit/a7aae9b0b1ab7797eae817535b650a8b30010894
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Factor audio sequences into their own class
Changed paths:
A engines/mediastation/audio.cpp
A engines/mediastation/audio.h
engines/mediastation/asset.h
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
engines/mediastation/assets/sound.cpp
engines/mediastation/assets/sound.h
engines/mediastation/module.mk
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index f4f27c09445..0e6e759e0ec 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -60,8 +60,6 @@ enum AssetType {
enum AssetHeaderSectionType {
kAssetHeaderEmptySection = 0x0000,
- kAssetHeaderSoundEncoding1 = 0x0001,
- kAssetHeaderSoundEncoding2 = 0x0002,
kAssetHeaderEventHandler = 0x0017,
kAssetHeaderStageId = 0x0019,
kAssetHeaderAssetId = 0x001a,
@@ -119,10 +117,6 @@ enum AssetHeaderSectionType {
kAssetHeaderSpriteFrameMapping = 0x03e9
};
-enum SoundEncoding {
- PCM_S16LE_MONO_22050 = 0x0010, // Uncompressed linear PCM
- IMA_ADPCM_S16LE_MONO_22050 = 0x0004 // IMA ADPCM encoding, must be decoded
-};
class Asset {
public:
Asset(AssetType type) : _type(type) {};
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index 3887330e49f..e1bfe3f12d7 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -19,9 +19,6 @@
*
*/
-#include "audio/decoders/raw.h"
-#include "audio/decoders/adpcm.h"
-
#include "mediastation/mediastation.h"
#include "mediastation/assets/movie.h"
#include "mediastation/debugchannels.h"
@@ -155,8 +152,6 @@ MovieFrame::~MovieFrame() {
}
Movie::~Movie() {
- g_engine->_mixer->stopHandle(_soundHandle);
-
for (MovieFrame *frame : _frames) {
delete frame;
}
@@ -167,11 +162,6 @@ Movie::~Movie() {
}
_stills.clear();
- for (Audio::SeekableAudioStream *stream : _audioStreams) {
- delete stream;
- }
- _audioStreams.clear();
-
for (MovieFrameFooter *footer : _footers) {
delete footer;
}
@@ -219,13 +209,8 @@ void Movie::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
break;
case kAssetHeaderSoundInfo:
- _chunkCount = chunk.readTypedUint16();
- _rate = chunk.readTypedUint32();
- break;
-
- case kAssetHeaderSoundEncoding1:
- case kAssetHeaderSoundEncoding2:
- _soundEncoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
+ _audioChunkCount = chunk.readTypedUint16();
+ _audioSequence.readParameters(chunk);
break;
default:
@@ -345,19 +330,8 @@ void Movie::timePlay() {
return;
}
- // START PLAYING SOUND.
// TODO: This won't work when we have some chunks that don't have audio.
- if (!_audioStreams.empty()) {
- Audio::QueuingAudioStream *audio = Audio::makeQueuingAudioStream(22050, false);
- for (Audio::SeekableAudioStream *stream : _audioStreams) {
- stream->rewind();
- audio->queueAudioStream(stream, DisposeAfterUse::NO);
- }
- // Then play the audio!
- g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, audio, -1, Audio::Mixer::kMaxChannelVolume);
- audio->finish();
- }
-
+ _audioSequence.play();
_framesNotYetShown = _frames;
_isVisible = true;
_isPlaying = true;
@@ -379,9 +353,7 @@ void Movie::timeStop() {
}
_framesOnScreen.clear();
_framesNotYetShown.clear();
-
- g_engine->_mixer->stopHandle(_soundHandle);
- _soundHandle = Audio::SoundHandle();
+ _audioSequence.stop();
// Show the persistent frames.
_isPlaying = false;
@@ -595,22 +567,7 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading audio chunk... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
bool isAudioChunk = (chunk._id == _audioChunkReference);
if (isAudioChunk) {
- byte *buffer = (byte *)malloc(chunk._length);
- chunk.read((void *)buffer, chunk._length);
- Audio::SeekableAudioStream *stream = nullptr;
- switch (_soundEncoding) {
- case SoundEncoding::PCM_S16LE_MONO_22050:
- stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
- break;
-
- case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:
- error("Movie::readSubfile(): ADPCM decoding not supported for movies");
- break;
-
- default:
- error("Movie::readSubfile(): Unknown audio encoding 0x%x", static_cast<uint>(_soundEncoding));
- }
- _audioStreams.push_back(stream);
+ _audioSequence.readChunk(chunk);
chunk = subfile.nextChunk();
} else {
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) No audio chunk to read. (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index 62c50bd0bf2..aca2623f284 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -23,9 +23,9 @@
#define MEDIASTATION_MOVIE_H
#include "common/array.h"
-#include "audio/audiostream.h"
#include "mediastation/asset.h"
+#include "mediastation/audio.h"
#include "mediastation/datafile.h"
#include "mediastation/bitmap.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -109,9 +109,8 @@ public:
uint32 _animationChunkReference = 0;
private:
- SoundEncoding _soundEncoding;
- uint _chunkCount = 0;
- uint _rate = 0;
+ AudioSequence _audioSequence;
+ uint _audioChunkCount = 0;
uint _loadType = 0;
double _dissolveFactor = 0.0;
@@ -121,8 +120,6 @@ private:
Common::Array<MovieFrame *> _frames;
Common::Array<MovieFrame *> _stills;
Common::Array<MovieFrameFooter *> _footers;
- Common::Array<Audio::SeekableAudioStream *> _audioStreams;
- Audio::SoundHandle _soundHandle;
Common::Array<MovieFrame *> _framesNotYetShown;
Common::Array<MovieFrame *> _framesOnScreen;
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index a7aa6d33d68..b7670596189 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -19,24 +19,12 @@
*
*/
-#include "audio/decoders/raw.h"
-#include "audio/decoders/adpcm.h"
-
+#include "mediastation/audio.h"
#include "mediastation/debugchannels.h"
#include "mediastation/assets/sound.h"
-#include "mediastation/mediastation.h"
namespace MediaStation {
-Sound::~Sound() {
- g_engine->_mixer->stopHandle(_handle);
-
- for (Audio::SeekableAudioStream *stream : _streams) {
- delete stream;
- }
- _streams.clear();
-}
-
void Sound::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
switch (paramType) {
case kAssetHeaderAssetId: {
@@ -59,12 +47,7 @@ void Sound::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
case kAssetHeaderSoundInfo:
_chunkCount = chunk.readTypedUint16();
- _rate = chunk.readTypedUint32();
- break;
-
- case kAssetHeaderSoundEncoding1:
- case kAssetHeaderSoundEncoding2:
- _encoding = static_cast<SoundEncoding>(chunk.readTypedUint16());
+ _sequence.readParameters(chunk);
break;
case kAssetHeaderMovieLoadType:
@@ -79,10 +62,10 @@ void Sound::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
void Sound::process() {
processTimeEventHandlers();
- if (_isActive && !g_engine->_mixer->isSoundHandleActive(_handle)) {
+ if (_isActive && !_sequence.isActive()) {
_isPlaying = false;
setInactive();
- _handle = Audio::SoundHandle();
+ _sequence.stop();
runEventHandlerIfExists(kSoundEndEvent);
}
@@ -92,6 +75,14 @@ ScriptValue Sound::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
ScriptValue returnValue;
switch (methodId) {
+ case kSpatialShowMethod:
+ // WORKAROUND: No-op to avoid triggering error on Dalmatians
+ // 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());
+ return returnValue;
+
case kTimePlayMethod: {
assert(args.empty());
timePlay();
@@ -109,32 +100,6 @@ ScriptValue Sound::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
}
-void Sound::readChunk(Chunk &chunk) {
- byte *buffer = (byte *)malloc(chunk._length);
- chunk.read((void *)buffer, chunk._length);
- Audio::SeekableAudioStream *stream = nullptr;
- switch (_encoding) {
- case SoundEncoding::PCM_S16LE_MONO_22050:
- stream = Audio::makeRawStream(buffer, chunk._length, 22050, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
- break;
-
- case SoundEncoding::IMA_ADPCM_S16LE_MONO_22050:
- // TODO: The interface here is different. We can't pass in the
- // buffers directly. We have to make a stream first.
- // stream = Audio::makeADPCMStream(buffer, chunk.length,
- // DisposeAfterUse::NO, Audio::ADPCMType::kADPCMMSIma, 22050, 1,
- // 4);
- warning("Sound::readSubfile(): ADPCM decoding not implemented yet");
- chunk.skip(chunk.bytesRemaining());
- break;
-
- default:
- error("Sound::readChunk(): Unknown audio encoding 0x%x", static_cast<uint>(_encoding));
- }
- _streams.push_back(stream);
- debugC(5, kDebugLoading, "Sound::readChunk(): Finished reading audio chunk (@0x%llx)", static_cast<long long int>(chunk.pos()));
-}
-
void Sound::readSubfile(Subfile &subfile, Chunk &chunk) {
uint32 expectedChunkId = chunk._id;
@@ -156,25 +121,15 @@ void Sound::timePlay() {
return;
}
- if (_streams.empty()) {
+ if (_sequence.isEmpty()) {
warning("Sound::timePlay(): Sound has no contents, probably because the sound is in INSTALL.CXT and isn't loaded yet");
return;
}
+
_isPlaying = true;
setActive();
-
+ _sequence.play();
runEventHandlerIfExists(kSoundBeginEvent);
-
- _handle = Audio::SoundHandle();
- if (!_streams.empty()) {
- Audio::QueuingAudioStream *audio = Audio::makeQueuingAudioStream(22050, false);
- for (Audio::SeekableAudioStream *stream : _streams) {
- stream->rewind();
- audio->queueAudioStream(stream, DisposeAfterUse::NO);
- }
- g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audio, -1, Audio::Mixer::kMaxChannelVolume, DisposeAfterUse::YES);
- audio->finish();
- }
}
void Sound::timeStop() {
@@ -184,10 +139,7 @@ void Sound::timeStop() {
}
setInactive();
-
- g_engine->_mixer->stopHandle(_handle);
- _handle = Audio::SoundHandle();
-
+ _sequence.stop();
runEventHandlerIfExists(kSoundStoppedEvent);
}
diff --git a/engines/mediastation/assets/sound.h b/engines/mediastation/assets/sound.h
index d30124533ab..bdb7ac7a91e 100644
--- a/engines/mediastation/assets/sound.h
+++ b/engines/mediastation/assets/sound.h
@@ -22,10 +22,8 @@
#ifndef MEDIASTATION_ASSETS_SOUND_H
#define MEDIASTATION_ASSETS_SOUND_H
-#include "audio/mixer.h"
-#include "audio/audiostream.h"
-
#include "mediastation/asset.h"
+#include "mediastation/audio.h"
#include "mediastation/datafile.h"
#include "mediastation/mediascript/scriptvalue.h"
#include "mediastation/mediascript/scriptconstants.h"
@@ -35,24 +33,20 @@ namespace MediaStation {
class Sound : public Asset {
public:
Sound() : Asset(kAssetTypeSound) {};
- ~Sound();
virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
- virtual void readChunk(Chunk& chunk) override;
+ virtual void readChunk(Chunk& chunk) override { _sequence.readChunk(chunk); }
virtual void readSubfile(Subfile &subFile, Chunk &chunk) override;
private:
uint _loadType = 0;
- uint _chunkCount = 0;
- uint _rate = 0;
bool _hasOwnSubfile = false;
- SoundEncoding _encoding;
- Audio::SoundHandle _handle;
- Common::Array<Audio::SeekableAudioStream *> _streams;
bool _isPlaying = true;
+ uint _chunkCount = 0;
+ AudioSequence _sequence;
// Script method implementations
void timePlay();
diff --git a/engines/mediastation/audio.cpp b/engines/mediastation/audio.cpp
new file mode 100644
index 00000000000..78322f42e49
--- /dev/null
+++ b/engines/mediastation/audio.cpp
@@ -0,0 +1,91 @@
+/* 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 "audio/decoders/adpcm.h"
+#include "audio/decoders/raw.h"
+
+#include "mediastation/audio.h"
+#include "mediastation/debugchannels.h"
+#include "mediastation/mediastation.h"
+
+namespace MediaStation {
+
+AudioSequence::~AudioSequence() {
+ g_engine->_mixer->stopHandle(_handle);
+
+ for (Audio::SeekableAudioStream *stream : _streams) {
+ delete stream;
+ }
+ _streams.clear();
+}
+
+void AudioSequence::play() {
+ _handle = Audio::SoundHandle();
+ if (!_streams.empty()) {
+ Audio::QueuingAudioStream *audio = Audio::makeQueuingAudioStream(22050, false);
+ for (Audio::SeekableAudioStream *stream : _streams) {
+ stream->rewind();
+ audio->queueAudioStream(stream, DisposeAfterUse::NO);
+ }
+ g_engine->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_handle, audio, -1, Audio::Mixer::kMaxChannelVolume, DisposeAfterUse::YES);
+ audio->finish();
+ }
+}
+
+void AudioSequence::stop() {
+ g_engine->_mixer->stopHandle(_handle);
+ _handle = Audio::SoundHandle();
+}
+
+void AudioSequence::readParameters(Chunk &chunk) {
+ _rate = chunk.readTypedUint32();
+ _channelCount = chunk.readTypedUint16();
+ _bitsPerSample = chunk.readTypedUint16();
+}
+
+void AudioSequence::readChunk(Chunk &chunk) {
+ byte *buffer = (byte *)malloc(chunk._length);
+ chunk.read((void *)buffer, chunk._length);
+ Audio::SeekableAudioStream *stream = nullptr;
+ switch (_bitsPerSample) {
+ case 16:
+ stream = Audio::makeRawStream(buffer, chunk._length, _rate, Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+ break;
+
+ case 4: // IMA ADPCM-encoded
+ // TODO: The interface here is different. We can't pass in the
+ // buffers directly. We have to make a stream first.
+ warning("ADPCM decoding not implemented yet");
+ chunk.skip(chunk.bytesRemaining());
+ break;
+
+ default:
+ error("Unknown audio encoding 0x%x", 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() {
+ return g_engine->_mixer->isSoundHandleActive(_handle);
+}
+
+} // End of namespace MediaStation
diff --git a/engines/mediastation/audio.h b/engines/mediastation/audio.h
new file mode 100644
index 00000000000..bf62956cc20
--- /dev/null
+++ b/engines/mediastation/audio.h
@@ -0,0 +1,57 @@
+/* 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_AUDIO_H
+#define MEDIASTATION_AUDIO_H
+
+#include "common/array.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+#include "mediastation/datafile.h"
+
+namespace MediaStation {
+
+class AudioSequence {
+public:
+ AudioSequence() {};
+ ~AudioSequence();
+
+ void play();
+ void stop();
+
+ void readParameters(Chunk &chunk);
+ void readChunk(Chunk &chunk);
+ bool isActive();
+ bool isEmpty() { return _streams.empty(); }
+
+ uint _rate = 0;
+ uint _channelCount = 0;
+ uint _bitsPerSample = 0;
+
+private:
+ Common::Array<Audio::SeekableAudioStream *> _streams;
+ Audio::SoundHandle _handle;
+};
+
+} // End of namespace MediaStation
+
+#endif
diff --git a/engines/mediastation/module.mk b/engines/mediastation/module.mk
index b0a38fea5f8..abd4880133c 100644
--- a/engines/mediastation/module.mk
+++ b/engines/mediastation/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS = \
assets/sprite.o \
assets/text.o \
assets/timer.o \
+ audio.o \
bitmap.o \
boot.o \
context.o \
Commit: e56caa42fe808af800f6ab4e7e797ae5e3772bd5
https://github.com/scummvm/scummvm/commit/e56caa42fe808af800f6ab4e7e797ae5e3772bd5
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Don't keep separate "active" assets list
Because it's not necessary and not how the original does it.
Changed paths:
engines/mediastation/asset.cpp
engines/mediastation/asset.h
engines/mediastation/assets/hotspot.cpp
engines/mediastation/assets/hotspot.h
engines/mediastation/assets/image.cpp
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/path.cpp
engines/mediastation/assets/path.h
engines/mediastation/assets/sound.cpp
engines/mediastation/assets/sound.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/text.cpp
engines/mediastation/assets/timer.cpp
engines/mediastation/assets/timer.h
engines/mediastation/context.cpp
engines/mediastation/context.h
engines/mediastation/mediastation.cpp
engines/mediastation/mediastation.h
diff --git a/engines/mediastation/asset.cpp b/engines/mediastation/asset.cpp
index 028241aa347..d2be6c294fb 100644
--- a/engines/mediastation/asset.cpp
+++ b/engines/mediastation/asset.cpp
@@ -83,25 +83,7 @@ void Asset::readSubfile(Subfile &subfile, Chunk &chunk) {
error("Asset::readSubfile(): Subfile reading for asset type 0x%x is not implemented", static_cast<uint>(_type));
}
-void Asset::setActive() {
- _isActive = true;
- _startTime = g_system->getMillis();
- _lastProcessedTime = 0;
- g_engine->addPlayingAsset(this);
-}
-
-void Asset::setInactive() {
- _isActive = false;
- _startTime = 0;
- _lastProcessedTime = 0;
-}
-
void Asset::processTimeEventHandlers() {
- if (!_isActive) {
- warning("Asset::processTimeEventHandlers(): Attempted to process time event handlers while asset %d is not playing", _id);
- return;
- }
-
// TODO: Replace with a queue.
uint currentTime = g_system->getMillis();
const Common::Array<EventHandler *> &_timeHandlers = _eventHandlers.getValOrDefault(kTimerEvent);
@@ -238,13 +220,6 @@ void SpatialEntity::readParameter(Chunk &chunk, AssetHeaderSectionType paramType
_zIndex = chunk.readTypedGraphicUnit();
break;
- case kAssetHeaderStartup:
- _isVisible = static_cast<bool>(chunk.readTypedByte());
- if (_isVisible) {
- setActive();
- }
- break;
-
case kAssetHeaderTransparency:
_hasTransparency = static_cast<bool>(chunk.readTypedByte());
break;
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 0e6e759e0ec..68138c74e4a 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -129,7 +129,6 @@ public:
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args);
virtual bool isSpatialActor() const { return false; }
- virtual bool isActive() const { return _isActive; }
virtual void initFromParameterStream(Chunk &chunk);
virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType);
@@ -139,8 +138,6 @@ public:
virtual void readChunk(Chunk &chunk);
virtual void readSubfile(Subfile &subfile, Chunk &chunk);
- void setInactive();
- void setActive();
void processTimeEventHandlers();
void runEventHandlerIfExists(EventType eventType, const ScriptValue &arg);
void runEventHandlerIfExists(EventType eventType);
@@ -159,7 +156,6 @@ protected:
uint _id = 0;
uint _contextId = 0;
- bool _isActive = false;
uint _startTime = 0;
uint _lastProcessedTime = 0;
uint _duration = 0;
diff --git a/engines/mediastation/assets/hotspot.cpp b/engines/mediastation/assets/hotspot.cpp
index 5081b8fb176..888babbc503 100644
--- a/engines/mediastation/assets/hotspot.cpp
+++ b/engines/mediastation/assets/hotspot.cpp
@@ -35,6 +35,10 @@ void Hotspot::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
break;
}
+ case kAssetHeaderStartup:
+ _isActive = static_cast<bool>(chunk.readTypedByte());
+ break;
+
case kAssetHeaderCursorResourceId:
_cursorResourceId = chunk.readTypedUint16();
break;
@@ -93,7 +97,6 @@ ScriptValue Hotspot::callMethod(BuiltInMethod methodId, Common::Array<ScriptValu
case kMouseActivateMethod: {
assert(args.empty());
_isActive = true;
- g_engine->addPlayingAsset(this);
g_engine->_needsHotspotRefresh = true;
return returnValue;
}
diff --git a/engines/mediastation/assets/hotspot.h b/engines/mediastation/assets/hotspot.h
index 6994a716afc..7dab8844e1f 100644
--- a/engines/mediastation/assets/hotspot.h
+++ b/engines/mediastation/assets/hotspot.h
@@ -35,12 +35,16 @@ public:
bool isInside(const Common::Point &pointToCheck);
virtual bool isVisible() const override { return false; }
+ bool isActive() const { return _isActive; }
virtual void readParameter(Chunk &chunk, AssetHeaderSectionType paramType) override;
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
uint _cursorResourceId = 0;
Common::Array<Common::Point> _mouseActiveArea;
+
+private:
+ bool _isActive = false;
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/image.cpp b/engines/mediastation/assets/image.cpp
index 9e91db1f263..a3b1fb91e7a 100644
--- a/engines/mediastation/assets/image.cpp
+++ b/engines/mediastation/assets/image.cpp
@@ -40,6 +40,10 @@ void Image::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
_chunkReference = chunk.readTypedChunkReference();
break;
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
case kAssetHeaderLoadType:
_loadType = chunk.readTypedByte();
break;
@@ -89,7 +93,7 @@ ScriptValue Image::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
void Image::redraw(Common::Rect &rect) {
- if (!_isActive) {
+ if (!_isVisible) {
return;
}
@@ -105,15 +109,12 @@ void Image::redraw(Common::Rect &rect) {
}
void Image::spatialShow() {
- _isActive = true;
_isVisible = true;
- g_engine->addPlayingAsset(this);
Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
g_engine->_dirtyRects.push_back(bbox);
}
void Image::spatialHide() {
- _isActive = false;
_isVisible = false;
Common::Rect bbox(getLeftTop(), _bitmap->width(), _bitmap->height());
g_engine->_dirtyRects.push_back(bbox);
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index e1bfe3f12d7..bdc6ff29a2d 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -196,6 +196,10 @@ void Movie::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
break;
}
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
case kAssetHeaderDissolveFactor:
_dissolveFactor = chunk.readTypedDouble();
break;
@@ -297,7 +301,6 @@ void Movie::spatialShow() {
g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
}
- setActive();
_isVisible = true;
_isPlaying = false;
}
@@ -319,14 +322,12 @@ void Movie::spatialHide() {
_isVisible = false;
_isPlaying = false;
- setInactive();
}
void Movie::timePlay() {
// TODO: Play movies one chunk at a time, which more directly approximates
// the original's reading from the CD one chunk at a time.
if (_isPlaying) {
- warning("Movie::timePlay(): (%d) Attempted to play a movie that is already playing", _id);
return;
}
@@ -335,8 +336,10 @@ void Movie::timePlay() {
_framesNotYetShown = _frames;
_isVisible = true;
_isPlaying = true;
- setActive();
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
runEventHandlerIfExists(kMovieBeginEvent);
+ process();
}
void Movie::timeStop() {
@@ -362,26 +365,24 @@ void Movie::timeStop() {
_framesOnScreen.push_back(still);
g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
}
- } else {
- setInactive();
}
runEventHandlerIfExists(kMovieStoppedEvent);
}
void Movie::process() {
+ if (_isVisible && _atFirstFrame) {
+ spatialShow();
+ _atFirstFrame = false;
+ }
+
if (_isPlaying) {
processTimeEventHandlers();
+ updateFrameState();
}
- updateFrameState();
}
void Movie::updateFrameState() {
- if (_isVisible && _atFirstFrame) {
- spatialShow();
- _atFirstFrame = false;
- }
-
if (!_isPlaying) {
debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _id);
for (MovieFrame *frame : _framesOnScreen) {
@@ -437,9 +438,7 @@ void Movie::updateFrameState() {
if (_framesOnScreen.empty() && _framesNotYetShown.empty()) {
_isPlaying = false;
_framesOnScreen.clear();
- if (_stills.empty()) {
- setInactive();
- } else {
+ if (!_stills.empty()) {
showPersistentFrame();
}
diff --git a/engines/mediastation/assets/path.cpp b/engines/mediastation/assets/path.cpp
index 5c5b9cb9f02..e487eb6b0ed 100644
--- a/engines/mediastation/assets/path.cpp
+++ b/engines/mediastation/assets/path.cpp
@@ -83,7 +83,7 @@ ScriptValue Path::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kIsPlayingMethod: {
assert(args.empty());
- returnValue.setToBool(_isActive);
+ returnValue.setToBool(_isPlaying);
return returnValue;
}
@@ -93,8 +93,7 @@ ScriptValue Path::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
void Path::timePlay() {
- if (_isActive) {
- warning("Path::timePlay(): Attempted to play a path that is already playing");
+ if (_isPlaying) {
return;
}
@@ -104,7 +103,9 @@ void Path::timePlay() {
error("Path::timePlay(): Got zero step rate");
}
- setActive();
+ _isPlaying = true;
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
_percentComplete = 0;
_nextPathStepTime = 0;
_currentStep = 0;
@@ -116,6 +117,10 @@ void Path::timePlay() {
}
void Path::process() {
+ if (!_isPlaying) {
+ return;
+ }
+
uint currentTime = g_system->getMillis();
uint pathTime = currentTime - _startTime;
@@ -135,7 +140,7 @@ void Path::process() {
runEventHandlerIfExists(kStepEvent);
_nextPathStepTime = ++_currentStep * _stepDurationInMilliseconds;
} else {
- setInactive();
+ _isPlaying = false;
_percentComplete = 0;
_nextPathStepTime = 0;
_currentStep = 0;
diff --git a/engines/mediastation/assets/path.h b/engines/mediastation/assets/path.h
index fe204387efe..dc2f280bf8d 100644
--- a/engines/mediastation/assets/path.h
+++ b/engines/mediastation/assets/path.h
@@ -43,6 +43,7 @@ private:
uint _currentStep = 0;
uint _nextPathStepTime = 0;
uint _stepDurationInMilliseconds = 0;
+ bool _isPlaying = false;
Common::Point _startPoint;
Common::Point _endPoint;
diff --git a/engines/mediastation/assets/sound.cpp b/engines/mediastation/assets/sound.cpp
index b7670596189..c972f2595bd 100644
--- a/engines/mediastation/assets/sound.cpp
+++ b/engines/mediastation/assets/sound.cpp
@@ -60,13 +60,14 @@ void Sound::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
}
void Sound::process() {
- processTimeEventHandlers();
+ if (!_isPlaying) {
+ return;
+ }
- if (_isActive && !_sequence.isActive()) {
+ processTimeEventHandlers();
+ if (!_sequence.isActive()) {
_isPlaying = false;
- setInactive();
_sequence.stop();
-
runEventHandlerIfExists(kSoundEndEvent);
}
}
@@ -116,29 +117,29 @@ void Sound::readSubfile(Subfile &subfile, Chunk &chunk) {
}
void Sound::timePlay() {
- if (_isActive) {
- warning("Sound::timePlay(): Attempt to play a sound that is already playing");
+ if (_isPlaying) {
return;
}
if (_sequence.isEmpty()) {
warning("Sound::timePlay(): Sound has no contents, probably because the sound is in INSTALL.CXT and isn't loaded yet");
+ _isPlaying = false;
return;
}
_isPlaying = true;
- setActive();
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
_sequence.play();
runEventHandlerIfExists(kSoundBeginEvent);
}
void Sound::timeStop() {
- if (!_isActive) {
- warning("Sound::timeStop(): Attempt to stop a sound that isn't playing");
+ if (!_isPlaying) {
return;
}
- setInactive();
+ _isPlaying = false;
_sequence.stop();
runEventHandlerIfExists(kSoundStoppedEvent);
}
diff --git a/engines/mediastation/assets/sound.h b/engines/mediastation/assets/sound.h
index bdb7ac7a91e..d1c94ccf6f8 100644
--- a/engines/mediastation/assets/sound.h
+++ b/engines/mediastation/assets/sound.h
@@ -38,13 +38,13 @@ public:
virtual ScriptValue callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) override;
virtual void process() override;
- virtual void readChunk(Chunk& chunk) override { _sequence.readChunk(chunk); }
+ virtual void readChunk(Chunk &chunk) override { _sequence.readChunk(chunk); }
virtual void readSubfile(Subfile &subFile, Chunk &chunk) override;
private:
uint _loadType = 0;
bool _hasOwnSubfile = false;
- bool _isPlaying = true;
+ bool _isPlaying = false;
uint _chunkCount = 0;
AudioSequence _sequence;
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index 8fdd5094310..c9b3721d00b 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -90,6 +90,10 @@ void Sprite::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
_loadType = chunk.readTypedByte();
break;
+ case kAssetHeaderStartup:
+ _isVisible = static_cast<bool>(chunk.readTypedByte());
+ break;
+
case kAssetHeaderSpriteChunkCount:
_frameCount = chunk.readTypedUint16();
break;
@@ -180,50 +184,37 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
void Sprite::spatialShow() {
if (_isVisible) {
- warning("Sprite::spatialShow(): (%d) Attempted to spatialShow when already showing", _id);
return;
}
showFrame(_frames[0]);
- setActive();
_isVisible = true;
- _isPlaying = false;
}
void Sprite::spatialHide() {
if (!_isVisible) {
- warning("Sprite::spatialHide(): (%d) Attempted to spatialHide when not showing", _id);
return;
}
showFrame(nullptr);
- setInactive();
_isVisible = false;
- _isPlaying = false;
}
void Sprite::timePlay() {
- if (!_isVisible) {
- warning("Sprite::timePlay(): (%d) Attempted to timePlay when not showing", _id);
- return;
- } else if (_isPlaying) {
- warning("Sprite::timePlay(): (%d) Attempted to timePlay when already playing", _id);
+ if (_isPlaying) {
return;
}
- setActive();
_isPlaying = true;
+ _startTime = g_system->getMillis();
+ _lastProcessedTime = 0;
_nextFrameTime = 0;
runEventHandlerIfExists(kMovieBeginEvent);
}
void Sprite::timeStop() {
- if (!_isVisible) {
- warning("Sprite::timeStop(): (%d) Attempted to timeStop when not showing", _id);
- return;
- } else if (!_isPlaying) {
- warning("Sprite::timeStop(): (%d) Attempted to timeStop when not playing", _id);
+ if (!_isPlaying) {
return;
}
@@ -232,11 +223,8 @@ void Sprite::timeStop() {
}
void Sprite::movieReset() {
- setActive();
if (_isVisible) {
showFrame(_frames[0]);
- } else {
- showFrame(nullptr);
}
_isPlaying = false;
_startTime = 0;
@@ -279,10 +267,6 @@ void Sprite::updateFrameState() {
return;
}
- if (!_isActive) {
- return;
- }
-
if (!_isPlaying) {
if (_activeFrame != nullptr) {
debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)",
@@ -315,7 +299,6 @@ void Sprite::updateFrameState() {
_isPlaying = false;
// But otherwise, the sprite's params should be reset.
- _isActive = true;
_startTime = 0;
_lastProcessedTime = 0;
_currentFrameIndex = 0;
diff --git a/engines/mediastation/assets/text.cpp b/engines/mediastation/assets/text.cpp
index 1006a517039..154c3b78205 100644
--- a/engines/mediastation/assets/text.cpp
+++ b/engines/mediastation/assets/text.cpp
@@ -90,14 +90,14 @@ ScriptValue Text::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kSpatialShowMethod: {
assert(args.empty());
- _isActive = true;
+ _isVisible = true;
warning("Text::callMethod(): spatialShow method not implemented yet");
return returnValue;
}
case kSpatialHideMethod: {
assert(args.empty());
- _isActive = false;
+ _isVisible = false;
warning("Text::callMethod(): spatialHide method not implemented yet");
return returnValue;
}
diff --git a/engines/mediastation/assets/timer.cpp b/engines/mediastation/assets/timer.cpp
index 323cf409858..10a149a3cfd 100644
--- a/engines/mediastation/assets/timer.cpp
+++ b/engines/mediastation/assets/timer.cpp
@@ -44,7 +44,7 @@ ScriptValue Timer::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kIsPlayingMethod: {
assert(args.size() == 0);
- returnValue.setToBool(_isActive);
+ returnValue.setToBool(_isPlaying);
return returnValue;
}
@@ -54,15 +54,9 @@ ScriptValue Timer::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
void Timer::timePlay() {
- if (_isActive) {
- warning("Timer::timePlay(): Attempted to play a timer that is already playing");
- //return;
- }
-
- _isActive = true;
+ _isPlaying = true;
_startTime = g_system->getMillis();
_lastProcessedTime = 0;
- g_engine->addPlayingAsset(this);
// Get the duration of the timer.
// TODO: Is there a better way to find out what the max time is? Do we have to look
@@ -82,18 +76,19 @@ void Timer::timePlay() {
}
void Timer::timeStop() {
- if (!_isActive) {
- warning("Timer::stop(): Attempted to stop a timer that is not playing");
+ if (!_isPlaying) {
return;
}
- _isActive = false;
+ _isPlaying = false;
_startTime = 0;
_lastProcessedTime = 0;
}
void Timer::process() {
- processTimeEventHandlers();
+ if (_isPlaying) {
+ processTimeEventHandlers();
+ }
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/timer.h b/engines/mediastation/assets/timer.h
index 2775b78001e..c1a3bc272d0 100644
--- a/engines/mediastation/assets/timer.h
+++ b/engines/mediastation/assets/timer.h
@@ -36,7 +36,8 @@ public:
virtual void process() override;
private:
- // Method implementations.
+ bool _isPlaying = false;
+
void timePlay();
void timeStop();
};
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 23a868975c3..09b4d1a5959 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -147,14 +147,6 @@ ScriptValue *Context::getVariable(uint variableId) {
return _variables.getValOrDefault(variableId);
}
-void Context::registerActiveAssets() {
- for (auto it = _assets.begin(); it != _assets.end(); ++it) {
- if (it->_value->isActive()) {
- g_engine->addPlayingAsset(it->_value);
- }
- }
-}
-
void Context::readCreateContextData(Chunk &chunk) {
_fileNumber = chunk.readTypedUint16();
@@ -389,15 +381,13 @@ bool Context::readHeaderSection(Chunk &chunk) {
case kContextAssetHeaderSection: {
Asset *asset = readCreateAssetData(chunk);
- if (g_engine->getAssetById(asset->id())) {
- error("Context::readHeaderSection(): Asset with ID 0x%d was already defined in this title", asset->id());
- }
-
_assets.setVal(asset->id(), asset);
+ g_engine->registerAsset(asset);
if (asset->_chunkReference != 0) {
debugC(5, kDebugLoading, "Context::readHeaderSection(): Storing asset with chunk ID \"%s\" (0x%x)", tag2str(asset->_chunkReference), asset->_chunkReference);
_assetsByChunkReference.setVal(asset->_chunkReference, asset);
}
+
if (asset->type() == kAssetTypeMovie) {
Movie *movie = static_cast<Movie *>(asset);
if (movie->_audioChunkReference != 0) {
diff --git a/engines/mediastation/context.h b/engines/mediastation/context.h
index 986d64a9f1e..846fcea05ef 100644
--- a/engines/mediastation/context.h
+++ b/engines/mediastation/context.h
@@ -70,7 +70,6 @@ public:
Asset *getAssetByChunkReference(uint chunkReference);
Function *getFunctionById(uint functionId);
ScriptValue *getVariable(uint variableId);
- void registerActiveAssets();
private:
// This is not an internal file ID, but the number of the file
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 69f417f4527..161da5f2b39 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -40,7 +40,8 @@ MediaStationEngine *g_engine;
MediaStationEngine::MediaStationEngine(OSystem *syst, const ADGameDescription *gameDesc) : Engine(syst),
_gameDescription(gameDesc),
- _randomSource("MediaStation") {
+ _randomSource("MediaStation"),
+ _spatialEntities(MediaStationEngine::compareAssetByZIndex) {
g_engine = this;
_gameDataDir = Common::FSNode(ConfMan.getPath("path"));
@@ -68,9 +69,8 @@ MediaStationEngine::~MediaStationEngine() {
}
Asset *MediaStationEngine::getAssetById(uint assetId) {
- for (auto it = _loadedContexts.begin(); it != _loadedContexts.end(); ++it) {
- Asset *asset = it->_value->getAssetById(assetId);
- if (asset != nullptr) {
+ for (auto asset : _assets) {
+ if (asset->id() == assetId) {
return asset;
}
}
@@ -174,11 +174,9 @@ Common::Error MediaStationEngine::run() {
}
debugC(5, kDebugGraphics, "***** START SCREEN UPDATE ***");
- for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
- (*it)->process();
+ for (Asset *asset : _assets) {
+ asset->process();
- // If we're changing screens, exit out now so we don't try to access
- // any assets that no longer exist.
if (_requestedScreenBranchId != 0) {
doBranchToScreen();
_requestedScreenBranchId = 0;
@@ -189,12 +187,6 @@ Common::Error MediaStationEngine::run() {
refreshActiveHotspot();
_needsHotspotRefresh = false;
}
-
- if (!(*it)->isActive()) {
- it = _assetsPlaying.erase(it);
- } else {
- ++it;
- }
}
redraw();
debugC(5, kDebugGraphics, "***** END SCREEN UPDATE ***");
@@ -302,20 +294,17 @@ void MediaStationEngine::redraw() {
return;
}
- Common::sort(_assetsPlaying.begin(), _assetsPlaying.end(), [](Asset * a, Asset * b) {
- if (!a->isSpatialActor() || !b->isSpatialActor()) {
- return false;
- }
- return static_cast<SpatialEntity *>(a)->zIndex() > static_cast<SpatialEntity *>(b)->zIndex();
- });
-
for (Common::Rect dirtyRect : _dirtyRects) {
- for (Asset *asset : _assetsPlaying) {
+ for (Asset *asset : _spatialEntities) {
if (!asset->isSpatialActor()) {
continue;
}
SpatialEntity *entity = static_cast<SpatialEntity *>(asset);
+ if (!entity->isVisible()) {
+ continue;
+ }
+
Common::Rect bbox = entity->getBbox();
if (!bbox.isEmpty()) {
if (dirtyRect.intersects(bbox)) {
@@ -374,7 +363,6 @@ Context *MediaStationEngine::loadContext(uint32 contextId) {
_screen->setPalette(*context->_palette);
}
- context->registerActiveAssets();
_loadedContexts.setVal(contextId, context);
return context;
}
@@ -390,15 +378,15 @@ void MediaStationEngine::setPalette(Asset *asset) {
}
}
-void MediaStationEngine::addPlayingAsset(Asset *assetToAdd) {
- // If we're already marking the asset as played, we don't need to mark it
- // played again.
- for (Asset *asset : g_engine->_assetsPlaying) {
- if (asset == assetToAdd) {
- return;
- }
+void MediaStationEngine::registerAsset(Asset *assetToAdd) {
+ if (getAssetById(assetToAdd->id())) {
+ error("Asset with ID 0x%d was already defined in this title", assetToAdd->id());
+ }
+
+ _assets.push_back(assetToAdd);
+ if (assetToAdd->isSpatialActor()) {
+ _spatialEntities.insert(static_cast<SpatialEntity *>(assetToAdd));
}
- g_engine->_assetsPlaying.push_back(assetToAdd);
}
ScriptValue MediaStationEngine::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue> &args) {
@@ -464,13 +452,19 @@ void MediaStationEngine::releaseContext(uint32 contextId) {
}
}
- // Unload any assets currently playing from this context. They should have
- // already been stopped by scripts, but this is a last check.
- for (auto it = _assetsPlaying.begin(); it != _assetsPlaying.end();) {
- uint assetId = (*it)->id();
- Asset *asset = context->getAssetById(assetId);
- if (asset != nullptr) {
- it = _assetsPlaying.erase(it);
+ for (auto it = _assets.begin(); it != _assets.end();) {
+ uint assetContextId = (*it)->contextId();
+ if (assetContextId == contextId) {
+ it = _assets.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ for (auto it = _spatialEntities.begin(); it != _spatialEntities.end();) {
+ uint assetContextId = (*it)->contextId();
+ if (assetContextId == contextId) {
+ it = _spatialEntities.erase(it);
} else {
++it;
}
@@ -486,7 +480,7 @@ Asset *MediaStationEngine::findAssetToAcceptMouseEvents() {
// actually the lowest asset.
int lowestZIndex = INT_MAX;
- for (Asset *asset : _assetsPlaying) {
+ for (Asset *asset : _assets) {
if (asset->type() == kAssetTypeHotspot) {
Hotspot *hotspot = static_cast<Hotspot *>(asset);
debugC(5, kDebugGraphics, "findAssetToAcceptMouseEvents(): Hotspot %d (z-index %d)", hotspot->id(), hotspot->zIndex());
@@ -530,4 +524,16 @@ ScriptValue MediaStationEngine::callBuiltInFunction(BuiltInFunction function, Co
}
}
+int MediaStationEngine::compareAssetByZIndex(const SpatialEntity *a, const SpatialEntity *b) {
+ int diff = b->zIndex() - a->zIndex();
+ if (diff < 0)
+ return -1; // a should come before b
+ else if (diff > 0)
+ return 1; // b should come before a
+ else {
+ // If z-indices are equal, compare pointers for stable sort
+ return (a < b) ? -1 : 1;
+ }
+}
+
} // End of namespace MediaStation
diff --git a/engines/mediastation/mediastation.h b/engines/mediastation/mediastation.h
index 10e3f1a0f9f..85e83ba43cc 100644
--- a/engines/mediastation/mediastation.h
+++ b/engines/mediastation/mediastation.h
@@ -81,7 +81,7 @@ public:
void redraw();
void setPalette(Asset *palette);
- void addPlayingAsset(Asset *assetToAdd);
+ void registerAsset(Asset *assetToAdd);
Asset *getAssetById(uint assetId);
Asset *getAssetByChunkReference(uint chunkReference);
@@ -117,9 +117,10 @@ private:
void setCursor(uint id);
Boot *_boot = nullptr;
- Common::List<Asset *> _assetsPlaying;
+ Common::Array<Asset *> _assets;
+ Common::SortedArray<SpatialEntity *, const SpatialEntity *> _spatialEntities;
Common::HashMap<uint, Context *> _loadedContexts;
- Hotspot *_currentHotspot = nullptr;
+ Asset *_currentHotspot = nullptr;
uint _requestedScreenBranchId = 0;
Common::Array<uint> _requestedContextReleaseId;
@@ -130,6 +131,8 @@ private:
Asset *findAssetToAcceptMouseEvents();
void effectTransition(Common::Array<ScriptValue> &args);
+
+ static int compareAssetByZIndex(const SpatialEntity *a, const SpatialEntity *b);
};
extern MediaStationEngine *g_engine;
Commit: cb66b27c9fd7cf88a281b7c214a85477b40f788d
https://github.com/scummvm/scummvm/commit/cb66b27c9fd7cf88a281b7c214a85477b40f788d
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Improve Sprite rendering accuracy
Changed paths:
engines/mediastation/asset.h
engines/mediastation/assets/sprite.cpp
engines/mediastation/assets/sprite.h
engines/mediastation/context.cpp
engines/mediastation/mediascript/scriptconstants.cpp
engines/mediastation/mediascript/scriptconstants.h
diff --git a/engines/mediastation/asset.h b/engines/mediastation/asset.h
index 68138c74e4a..d322aa2519e 100644
--- a/engines/mediastation/asset.h
+++ b/engines/mediastation/asset.h
@@ -114,7 +114,8 @@ enum AssetHeaderSectionType {
kAssetHeaderTextCharacterClass = 0x0266,
// SPRITE FIELDS.
- kAssetHeaderSpriteFrameMapping = 0x03e9
+ kAssetHeaderSpriteClip = 0x03e9,
+ kAssetHeaderCurrentSpriteClip = 0x03ea
};
class Asset {
diff --git a/engines/mediastation/assets/sprite.cpp b/engines/mediastation/assets/sprite.cpp
index c9b3721d00b..45cf88de556 100644
--- a/engines/mediastation/assets/sprite.cpp
+++ b/engines/mediastation/assets/sprite.cpp
@@ -60,7 +60,6 @@ uint32 SpriteFrame::index() {
return _bitmapHeader->_index;
}
-
Sprite::~Sprite() {
// If we're just referencing another asset's frames,
// don't delete those frames.
@@ -94,18 +93,31 @@ void Sprite::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
_isVisible = static_cast<bool>(chunk.readTypedByte());
break;
- case kAssetHeaderSpriteChunkCount:
+ case kAssetHeaderSpriteChunkCount: {
_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);
break;
+ }
- case kAssetHeaderSpriteFrameMapping: {
- uint32 externalFrameId = chunk.readTypedUint16();
- uint32 internalFrameId = chunk.readTypedUint16();
- uint32 unk1 = chunk.readTypedUint16();
- if (unk1 != internalFrameId) {
- warning("AssetHeader::readSection(): Repeated internalFrameId doesn't match");
- }
- _spriteFrameMapping.setVal(externalFrameId, internalFrameId);
+ case kAssetHeaderSpriteClip: {
+ SpriteClip spriteClip;
+ spriteClip.id = chunk.readTypedUint16();
+ spriteClip.firstFrameIndex = chunk.readTypedUint16();
+ spriteClip.lastFrameIndex = chunk.readTypedUint16();
+ _clips.setVal(spriteClip.id, spriteClip);
+ break;
+ }
+
+ case kAssetHeaderCurrentSpriteClip: {
+ uint clipId = chunk.readTypedUint16();
+ setCurrentClip(clipId);
break;
}
@@ -120,13 +132,13 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
switch (methodId) {
case kSpatialShowMethod: {
assert(args.empty());
- spatialShow();
+ setVisibility(true);
return returnValue;
}
case kSpatialHideMethod: {
assert(args.empty());
- spatialHide();
+ setVisibility(false);
return returnValue;
}
@@ -139,36 +151,65 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
case kTimePlayMethod: {
assert(args.empty());
- timePlay();
+ play();
return returnValue;
}
case kTimeStopMethod: {
assert(args.empty());
- timeStop();
+ stop();
return returnValue;
}
case kMovieResetMethod: {
assert(args.empty());
- movieReset();
+ setCurrentFrameToInitial();
return returnValue;
}
case kSetCurrentClipMethod: {
assert(args.size() <= 1);
- if (args.size() == 1 && args[0].asParamToken() != 0) {
- error("Sprite::callMethod(): (%d) setClip() called with unhandled arg: %d", _id, args[0].asParamToken());
+ uint clipId = DEFAULT_CLIP_ID;
+ if (args.size() == 1) {
+ clipId = args[0].asParamToken();
}
- setCurrentClip();
+ setCurrentClip(clipId);
return returnValue;
}
- case kSetSpriteFrameByIdMethod: {
- assert(args.size() == 1);
- uint32 externalFrameId = args[0].asParamToken();
- uint32 internalFrameId = _spriteFrameMapping.getVal(externalFrameId);
- showFrame(_frames[internalFrameId]);
+ case kIncrementFrameMethod: {
+ assert(args.size() <= 1);
+ bool loopAround = false;
+ if (args.size() == 1) {
+ loopAround = args[0].asBool();
+ }
+
+ bool moreFrames = activateNextFrame();
+ if (!moreFrames) {
+ if (loopAround) {
+ setCurrentFrameToInitial();
+ }
+ }
+ return returnValue;
+ }
+
+ case kDecrementFrameMethod: {
+ bool shouldSetCurrentFrameToFinal = false;
+ if (args.size() == 1) {
+ shouldSetCurrentFrameToFinal = args[0].asBool();
+ }
+
+ bool moreFrames = activatePreviousFrame();
+ if (!moreFrames) {
+ if (shouldSetCurrentFrameToFinal) {
+ setCurrentFrameToFinal();
+ }
+ }
+ return returnValue;
+ }
+
+ case kGetCurrentClipIdMethod: {
+ returnValue.setToParamToken(_activeClip.id);
return returnValue;
}
@@ -182,62 +223,75 @@ ScriptValue Sprite::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue
}
}
-void Sprite::spatialShow() {
- if (_isVisible) {
- return;
+bool Sprite::activateNextFrame() {
+ if (_currentFrameIndex < _activeClip.lastFrameIndex) {
+ _currentFrameIndex++;
+ dirtyIfVisible();
+ return true;
}
- showFrame(_frames[0]);
-
- _isVisible = true;
+ return false;
}
-void Sprite::spatialHide() {
- if (!_isVisible) {
- return;
+bool Sprite::activatePreviousFrame() {
+ if (_currentFrameIndex > _activeClip.firstFrameIndex) {
+ _currentFrameIndex--;
+ dirtyIfVisible();
+ return true;
}
- showFrame(nullptr);
+ return false;
+}
- _isVisible = false;
+void Sprite::dirtyIfVisible() {
+ if (_isVisible) {
+ invalidateLocalBounds();
+ }
}
-void Sprite::timePlay() {
- if (_isPlaying) {
- return;
+void Sprite::setVisibility(bool visibility) {
+ if (_isVisible != visibility) {
+ _isVisible = visibility;
+ invalidateLocalBounds();
}
+}
+void Sprite::play() {
_isPlaying = true;
_startTime = g_system->getMillis();
_lastProcessedTime = 0;
_nextFrameTime = 0;
- runEventHandlerIfExists(kMovieBeginEvent);
+ scheduleNextFrame();
}
-void Sprite::timeStop() {
- if (!_isPlaying) {
- return;
+void Sprite::stop() {
+ _nextFrameTime = 0;
+ _isPlaying = false;
+}
+
+void Sprite::setCurrentClip(uint clipId) {
+ if (_activeClip.id != clipId) {
+ if (_clips.contains(clipId)) {
+ _activeClip = _clips.getVal(clipId);
+ } else {
+ _activeClip.id = clipId;
+ warning("Sprite clip %d not found in sprite %d", clipId, _id);
+ }
}
- _isPlaying = false;
- // TODO: Find the right event handler to run here.
+ setCurrentFrameToInitial();
}
-void Sprite::movieReset() {
- if (_isVisible) {
- showFrame(_frames[0]);
+void Sprite::setCurrentFrameToInitial() {
+ if (_currentFrameIndex != _activeClip.firstFrameIndex) {
+ _currentFrameIndex = _activeClip.firstFrameIndex;
+ dirtyIfVisible();
}
- _isPlaying = false;
- _startTime = 0;
- _currentFrameIndex = 0;
- _nextFrameTime = 0;
- _lastProcessedTime = 0;
}
-void Sprite::setCurrentClip() {
- if (_currentFrameIndex < _frames.size()) {
- showFrame(_frames[_currentFrameIndex++]);
- } else {
- warning("Sprite::setCurrentClip(): (%d) Attempted to increment past number of frames", _id);
+void Sprite::setCurrentFrameToFinal() {
+ if (_currentFrameIndex != _activeClip.lastFrameIndex) {
+ _currentFrameIndex = _activeClip.lastFrameIndex;
+ dirtyIfVisible();
}
}
@@ -260,91 +314,80 @@ void Sprite::readChunk(Chunk &chunk) {
});
}
-void Sprite::updateFrameState() {
- if (_isVisible && _atFirstFrame) {
- showFrame(_frames[0]);
- _atFirstFrame = false;
+void Sprite::scheduleNextFrame() {
+ if (!_isPlaying) {
return;
}
+ if (_currentFrameIndex < _activeClip.lastFrameIndex) {
+ scheduleNextTimerEvent();
+ } else {
+ stop();
+ }
+}
+
+void Sprite::scheduleNextTimerEvent() {
+ uint frameDuration = 1000 / _frameRate;
+ _nextFrameTime += frameDuration;
+}
+
+void Sprite::updateFrameState() {
if (!_isPlaying) {
- if (_activeFrame != nullptr) {
- debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing. Persistent frame %d (%d x %d) @ (%d, %d)",
- _id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
- } else {
- debugC(6, kDebugGraphics, "Sprite::updateFrameState(): (%d): Not playing, no persistent frame", _id);
- }
return;
}
- debugC(5, kDebugGraphics, "Sprite::updateFrameState(): (%d) Frame %d (%d x %d) @ (%d, %d)",
- _id, _activeFrame->index(), _activeFrame->width(), _activeFrame->height(), _activeFrame->left(), _activeFrame->top());
-
uint currentTime = g_system->getMillis() - _startTime;
bool drawNextFrame = currentTime >= _nextFrameTime;
- if (!drawNextFrame) {
+ debugC(kDebugGraphics, "nextFrameTime: %d; startTime: %d, currentTime: %d", _nextFrameTime, _startTime, currentTime);
+ if (drawNextFrame) {
+ timerEvent();
+ }
+}
+
+void Sprite::timerEvent() {
+ if (!_isPlaying) {
+ error("Attempt to activate sprite frame when sprite is not playing");
return;
}
- showFrame(_frames[_currentFrameIndex]);
+ bool result = activateNextFrame();
+ if (!result) {
+ stop();
+ } else {
+ postMovieEndEventIfNecessary();
+ scheduleNextFrame();
+ }
+}
- uint frameDuration = 1000 / _frameRate;
- _nextFrameTime = ++_currentFrameIndex * frameDuration;
-
- bool spriteFinishedPlaying = (_currentFrameIndex == _frames.size());
- if (spriteFinishedPlaying) {
- // Sprites always keep their last frame showing until they are hidden
- // with spatialHide.
- showFrame(_frames[_currentFrameIndex - 1]);
- _isPlaying = false;
-
- // But otherwise, the sprite's params should be reset.
- _startTime = 0;
- _lastProcessedTime = 0;
- _currentFrameIndex = 0;
- _nextFrameTime = 0;
-
- ScriptValue defaultSpriteClip;
- const uint DEFAULT_SPRITE_CLIP_ID = 1200;
- defaultSpriteClip.setToParamToken(DEFAULT_SPRITE_CLIP_ID);
- runEventHandlerIfExists(kSpriteMovieEndEvent, defaultSpriteClip);
+void Sprite::postMovieEndEventIfNecessary() {
+ if (_currentFrameIndex != _activeClip.lastFrameIndex) {
+ return;
}
+
+ _isPlaying = false;
+ _startTime = 0;
+ _nextFrameTime = 0;
+
+ ScriptValue value;
+ value.setToParamToken(_activeClip.id);
+ runEventHandlerIfExists(kSpriteMovieEndEvent, value);
}
void Sprite::redraw(Common::Rect &rect) {
- if (_activeFrame == nullptr || !_isVisible) {
+ SpriteFrame *activeFrame = _frames[_currentFrameIndex];
+ if (activeFrame == nullptr || !_isVisible) {
return;
}
- Common::Rect bbox = getActiveFrameBoundingBox();
+ Common::Rect bbox = activeFrame->boundingBox();
+ bbox.translate(_boundingBox.left, _boundingBox.top);
Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
if (!areaToRedraw.isEmpty()) {
Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
- areaToRedraw.translate(-_activeFrame->left() - _boundingBox.left, -_activeFrame->top() - _boundingBox.top);
- areaToRedraw.clip(Common::Rect(0, 0, _activeFrame->width(), _activeFrame->height()));
- g_engine->_screen->simpleBlitFrom(_activeFrame->_surface, areaToRedraw, originOnScreen);
- }
-}
-
-void Sprite::showFrame(SpriteFrame *frame) {
- // Erase the previous frame.
- if (_activeFrame != nullptr) {
- g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
+ areaToRedraw.translate(-activeFrame->left() - _boundingBox.left, -activeFrame->top() - _boundingBox.top);
+ areaToRedraw.clip(Common::Rect(0, 0, activeFrame->width(), activeFrame->height()));
+ g_engine->_screen->simpleBlitFrom(activeFrame->_surface, areaToRedraw, originOnScreen);
}
-
- // Show the next frame.
- _activeFrame = frame;
- if (frame != nullptr) {
- g_engine->_dirtyRects.push_back(getActiveFrameBoundingBox());
- }
-}
-
-Common::Rect Sprite::getActiveFrameBoundingBox() {
- // The frame dimensions are relative to those of the sprite movie.
- // So we must get the absolute coordinates.
- Common::Rect bbox = _activeFrame->boundingBox();
- bbox.translate(_boundingBox.left, _boundingBox.top);
- return bbox;
}
} // End of namespace MediaStation
diff --git a/engines/mediastation/assets/sprite.h b/engines/mediastation/assets/sprite.h
index ebbebb98604..15d55001c8c 100644
--- a/engines/mediastation/assets/sprite.h
+++ b/engines/mediastation/assets/sprite.h
@@ -33,6 +33,12 @@
namespace MediaStation {
+struct SpriteClip {
+ uint id = 0;
+ uint firstFrameIndex = 0;
+ uint lastFrameIndex = 0;
+};
+
class SpriteFrameHeader : public BitmapHeader {
public:
SpriteFrameHeader(Chunk &chunk);
@@ -76,29 +82,36 @@ public:
virtual void readChunk(Chunk &chunk) override;
private:
+ static const uint DEFAULT_CLIP_ID = 1200;
double _dissolveFactor = 0.0;
uint _loadType = 0;
uint _frameRate = 0;
uint _frameCount = 0;
- Common::HashMap<uint32, uint32> _spriteFrameMapping;
+ Common::HashMap<uint, SpriteClip> _clips;
Common::Array<SpriteFrame *> _frames;
- SpriteFrame *_activeFrame = nullptr;
bool _isPlaying = false;
- bool _atFirstFrame = true;
uint _currentFrameIndex = 0;
uint _nextFrameTime = 0;
+ SpriteClip _activeClip;
+
+ void play();
+ void stop();
+ void setCurrentClip(uint clipId);
+
+ bool activateNextFrame();
+ bool activatePreviousFrame();
+
+ void dirtyIfVisible();
+ void setCurrentFrameToInitial();
+ void setCurrentFrameToFinal();
- // Method implementations.
- void spatialShow();
- void spatialHide();
- void timePlay();
- void timeStop();
- void movieReset();
- void setCurrentClip();
+ void scheduleNextFrame();
+ void scheduleNextTimerEvent();
+ void postMovieEndEventIfNecessary();
+ void setVisibility(bool visibility);
void updateFrameState();
- void showFrame(SpriteFrame *frame);
- Common::Rect getActiveFrameBoundingBox();
+ void timerEvent();
};
} // End of namespace MediaStation
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 09b4d1a5959..2363b41c5f2 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -99,6 +99,7 @@ Context::Context(const Common::Path &path) : Datafile(path) {
error("Context::Context(): Asset %d references non-existent asset %d", asset->id(), referencedAssetId);
}
sprite->_frames = referencedSprite->_frames;
+ sprite->_clips = referencedSprite->_clips;
break;
}
diff --git a/engines/mediastation/mediascript/scriptconstants.cpp b/engines/mediastation/mediascript/scriptconstants.cpp
index 966e1e33e49..d8946caf7f1 100644
--- a/engines/mediastation/mediascript/scriptconstants.cpp
+++ b/engines/mediastation/mediascript/scriptconstants.cpp
@@ -171,10 +171,14 @@ const char *builtInMethodToStr(BuiltInMethod method) {
return "IsVisible";
case kMovieResetMethod:
return "MovieReset";
- case kSetSpriteFrameByIdMethod:
- return "SetSpriteFrameById";
case kSetCurrentClipMethod:
return "SetCurrentClip";
+ case kIncrementFrameMethod:
+ return "IncrementFrame";
+ case kDecrementFrameMethod:
+ return "DecrementFrame";
+ case kGetCurrentClipIdMethod:
+ return "GetCurrentClipId";
case kSetWorldSpaceExtentMethod:
return "SetWorldSpaceExtent";
case kSetBoundsMethod:
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index ae2f70a3f9c..9658dc49af1 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -122,8 +122,10 @@ enum BuiltInMethod {
// SPRITE METHODS.
kMovieResetMethod = 219, // PARAMS: 0
- kSetSpriteFrameByIdMethod = 220, // PARAMS: 1
- kSetCurrentClipMethod = 221, // PARAMS: 0-1
+ kSetCurrentClipMethod = 220, // PARAMS: 1
+ kIncrementFrameMethod = 221, // PARAMS: 0-1
+ kDecrementFrameMethod = 222, // PARAMS: 0-1
+ kGetCurrentClipIdMethod = 240, // PARAMS: 0
// STAGE METHODS.
kSetWorldSpaceExtentMethod = 363, // PARAMS: 2
Commit: 65578a45109d50620b812411b5d4a232224d4d0b
https://github.com/scummvm/scummvm/commit/65578a45109d50620b812411b5d4a232224d4d0b
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: Improve Movie rendering accuracy
Changed paths:
engines/mediastation/assets/movie.cpp
engines/mediastation/assets/movie.h
diff --git a/engines/mediastation/assets/movie.cpp b/engines/mediastation/assets/movie.cpp
index bdc6ff29a2d..d71928ba0db 100644
--- a/engines/mediastation/assets/movie.cpp
+++ b/engines/mediastation/assets/movie.cpp
@@ -31,124 +31,48 @@ MovieFrameHeader::MovieFrameHeader(Chunk &chunk) : BitmapHeader(chunk) {
_keyframeEndInMilliseconds = chunk.readTypedUint32();
}
-MovieFrameFooter::MovieFrameFooter(Chunk &chunk) {
- _unk1 = chunk.readTypedUint16();
- _unk2 = chunk.readTypedUint32();
+MovieFrame::MovieFrame(Chunk &chunk) {
+ layerId = chunk.readTypedUint32();
if (g_engine->isFirstGenerationEngine()) {
- _startInMilliseconds = chunk.readTypedUint32();
- _endInMilliseconds = chunk.readTypedUint32();
- _left = chunk.readTypedUint16();
- _top = chunk.readTypedUint16();
- _unk3 = chunk.readTypedUint16();
- _unk4 = chunk.readTypedUint16();
- _index = chunk.readTypedUint16();
+ startInMilliseconds = chunk.readTypedUint32();
+ endInMilliseconds = chunk.readTypedUint32();
+ // These are unsigned in the data files but ScummVM expects signed.
+ leftTop.x = static_cast<int16>(chunk.readTypedUint16());
+ leftTop.y = static_cast<int16>(chunk.readTypedUint16());
+ unk3 = chunk.readTypedUint16();
+ unk4 = chunk.readTypedUint16();
+ index = chunk.readTypedUint16();
} else {
- _unk4 = chunk.readTypedUint16();
- _startInMilliseconds = chunk.readTypedUint32();
- _endInMilliseconds = chunk.readTypedUint32();
- _left = chunk.readTypedUint16();
- _top = chunk.readTypedUint16();
- _zIndex = chunk.readTypedSint16();
- // This represents the difference between the left coordinate of the
+ blitType = chunk.readTypedUint16();
+ startInMilliseconds = chunk.readTypedUint32();
+ endInMilliseconds = chunk.readTypedUint32();
+ // These are unsigned in the data files but ScummVM expects signed.
+ leftTop.x = static_cast<int16>(chunk.readTypedUint16());
+ leftTop.y = static_cast<int16>(chunk.readTypedUint16());
+ zIndex = chunk.readTypedSint16();
+ // This represents the difference between the left-top coordinate of the
// keyframe (if applicable) and the left coordinate of this frame. Zero
// if there is no keyframe.
- _diffBetweenKeyframeAndFrameX = chunk.readTypedSint16();
- // This represents the difference between the top coordinate of the
- // keyframe (if applicable) and the top coordinate of this frame. Zero
- // if there is no keyframe.
- _diffBetweenKeyframeAndFrameY = chunk.readTypedSint16();
- _index = chunk.readTypedUint32();
- _keyframeIndex = chunk.readTypedUint32();
- _unk9 = chunk.readTypedByte();
- debugC(5, kDebugLoading, "MovieFrameFooter::MovieFrameFooter(): _startInMilliseconds = %d, _endInMilliseconds = %d, _left = %d, _top = %d, _index = %d, _keyframeIndex = %d (@0x%llx)",
- _startInMilliseconds, _endInMilliseconds, _left, _top, _index, _keyframeIndex, static_cast<long long int>(chunk.pos()));
- debugC(5, kDebugLoading, "MovieFrameFooter::MovieFrameFooter(): _zIndex = %d, _diffBetweenKeyframeAndFrameX = %d, _diffBetweenKeyframeAndFrameY = %d, _unk4 = %d, _unk9 = %d",
- _zIndex, _diffBetweenKeyframeAndFrameX, _diffBetweenKeyframeAndFrameY, _unk4,_unk9);
+ diffBetweenKeyframeAndFrame.x = chunk.readTypedSint16();
+ diffBetweenKeyframeAndFrame.y = chunk.readTypedSint16();
+ 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()));
}
}
-MovieFrame::MovieFrame(Chunk &chunk, MovieFrameHeader *header) :
- Bitmap(chunk, header),
- _footer(nullptr) {
+MovieFrameImage::MovieFrameImage(Chunk &chunk, MovieFrameHeader *header) : Bitmap(chunk, header) {
_bitmapHeader = header;
}
-void MovieFrame::setFooter(MovieFrameFooter *footer) {
- if (footer != nullptr) {
- assert(footer->_index == _bitmapHeader->_index);
- }
- _footer = footer;
-}
-
-uint32 MovieFrame::left() {
- if (_footer != nullptr) {
- return _footer->_left;
- } else {
- error("MovieFrame::left(): Cannot get the left coordinate of a keyframe");
- }
-}
-
-uint32 MovieFrame::top() {
- if (_footer != nullptr) {
- return _footer->_top;
- } else {
- error("MovieFrame::left(): Cannot get the top coordinate of a keyframe");
- }
-}
-
-Common::Point MovieFrame::topLeft() {
- if (_footer != nullptr) {
- return Common::Point(_footer->_left, _footer->_top);
- } else {
- error("MovieFrame::topLeft(): Cannot get the top-left coordinate of a keyframe");
- }
-}
-
-Common::Rect MovieFrame::boundingBox() {
- if (_footer != nullptr) {
- return Common::Rect(Common::Point(_footer->_left, _footer->_top), width(), height());
- } else {
- error("MovieFrame::boundingBox(): Cannot get the bounding box of a keyframe");
- }
-}
-
-uint32 MovieFrame::index() {
- return _bitmapHeader->_index;
-}
-
-uint32 MovieFrame::startInMilliseconds() {
- if (_footer != nullptr) {
- return _footer->_startInMilliseconds;
- } else {
- error("MovieFrame::startInMilliseconds(): Cannot get the start time of a keyframe");
- }
-}
-
-uint32 MovieFrame::endInMilliseconds() {
- if (_footer != nullptr) {
- return _footer->_endInMilliseconds;
- } else {
- error("MovieFrame::endInMilliseconds(): Cannot get the end time of a keyframe");
- }
-}
-
-uint32 MovieFrame::zCoordinate() {
- if (_footer != nullptr) {
- return _footer->_zIndex;
- } else {
- error("MovieFrame::zCoordinate(): Cannot get the z-coordinate of a keyframe");
- }
-}
-
-uint32 MovieFrame::keyframeEndInMilliseconds() {
- return _bitmapHeader->_keyframeEndInMilliseconds;
-}
-
-MovieFrame::~MovieFrame() {
+MovieFrameImage::~MovieFrameImage() {
// The base class destructor takes care of deleting the bitmap header, so
// we don't need to delete that here.
- // The movie will delete the footer.
- _footer = nullptr;
}
Movie::~Movie() {
@@ -157,15 +81,10 @@ Movie::~Movie() {
}
_frames.clear();
- for (MovieFrame *still : _stills) {
- delete still;
- }
- _stills.clear();
-
- for (MovieFrameFooter *footer : _footers) {
- delete footer;
+ for (MovieFrameImage *image : _images) {
+ delete image;
}
- _footers.clear();
+ _images.clear();
}
void Movie::readParameter(Chunk &chunk, AssetHeaderSectionType paramType) {
@@ -234,7 +153,8 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kSpatialShowMethod: {
assert(args.empty());
- spatialShow();
+ setVisibility(true);
+ updateFrameState();
return returnValue;
}
@@ -246,7 +166,7 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
case kSpatialHideMethod: {
assert(args.empty());
- spatialHide();
+ setVisibility(false);
return returnValue;
}
@@ -261,7 +181,6 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
double left = static_cast<double>(_boundingBox.left);
returnValue.setToFloat(left);
return returnValue;
-
}
case kGetTopYMethod: {
@@ -283,47 +202,6 @@ ScriptValue Movie::callMethod(BuiltInMethod methodId, Common::Array<ScriptValue>
}
}
-void Movie::spatialShow() {
- if (_isPlaying) {
- warning("Movie::spatialShow(): (%d) Attempted to spatialShow movie that is already playing", _id);
- return;
- } else if (_stills.empty()) {
- warning("Movie::spatialShow(): (%d) No still frame to show", _id);
- return;
- }
-
- // TODO: For movies with keyframes, there is more than one still and they
- // must be composited. All other movies should have just one still.)
- _framesNotYetShown.clear();
- _framesOnScreen.clear();
- for (MovieFrame *still : _stills) {
- _framesOnScreen.push_back(still);
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
- }
-
- _isVisible = true;
- _isPlaying = false;
-}
-
-void Movie::spatialHide() {
- if (_isPlaying) {
- warning("Movie::spatialShow(): (%d) Attempted to spatialHide movie that is playing", _id);
- return;
- } else if (!_isVisible) {
- warning("Movie::spatialHide(): (%d) Attempted to spatialHide movie that is not showing", _id);
- return;
- }
-
- for (MovieFrame *frame : _framesOnScreen) {
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
- }
- _framesOnScreen.clear();
- _framesNotYetShown.clear();
-
- _isVisible = false;
- _isPlaying = false;
-}
-
void Movie::timePlay() {
// TODO: Play movies one chunk at a time, which more directly approximates
// the original's reading from the CD one chunk at a time.
@@ -331,10 +209,9 @@ void Movie::timePlay() {
return;
}
- // TODO: This won't work when we have some chunks that don't have audio.
_audioSequence.play();
_framesNotYetShown = _frames;
- _isVisible = true;
+ _framesOnScreen.clear();
_isPlaying = true;
_startTime = g_system->getMillis();
_lastProcessedTime = 0;
@@ -343,57 +220,47 @@ void Movie::timePlay() {
}
void Movie::timeStop() {
- if (!_isVisible) {
- warning("Movie::timeStop(): (%d) Attempted to stop a movie that isn't showing", _id);
- return;
- } else if (!_isPlaying) {
- warning("Movie::timePlay(): (%d) Attempted to stop a movie that isn't playing", _id);
+ if (!_isPlaying) {
return;
}
for (MovieFrame *frame : _framesOnScreen) {
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ invalidateRect(getFrameBoundingBox(frame));
}
- _framesOnScreen.clear();
- _framesNotYetShown.clear();
_audioSequence.stop();
-
- // Show the persistent frames.
- _isPlaying = false;
- if (!_stills.empty()) {
- for (MovieFrame *still : _stills) {
- _framesOnScreen.push_back(still);
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
- }
+ _framesNotYetShown.empty();
+ if (_hasStill) {
+ _framesNotYetShown = _frames;
}
-
+ _framesOnScreen.clear();
+ _startTime = 0;
+ _lastProcessedTime = 0;
+ _isPlaying = false;
runEventHandlerIfExists(kMovieStoppedEvent);
}
void Movie::process() {
- if (_isVisible && _atFirstFrame) {
- spatialShow();
- _atFirstFrame = false;
+ if (_isVisible) {
+ if (_isPlaying) {
+ processTimeEventHandlers();
+ }
+ updateFrameState();
}
+}
- if (_isPlaying) {
- processTimeEventHandlers();
- updateFrameState();
+void Movie::setVisibility(bool visibility) {
+ if (visibility != _isVisible) {
+ _isVisible = visibility;
+ invalidateLocalBounds();
}
}
void Movie::updateFrameState() {
- if (!_isPlaying) {
- debugC(6, kDebugGraphics, "Movie::updateFrameState (%d): Not playing", _id);
- for (MovieFrame *frame : _framesOnScreen) {
- debugC(6, kDebugGraphics, " PERSIST: Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d",
- frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
- }
- return;
+ uint movieTime = 0;
+ if (_isPlaying) {
+ uint currentTime = g_system->getMillis();
+ movieTime = currentTime - _startTime;
}
-
- uint currentTime = g_system->getMillis();
- uint movieTime = currentTime - _startTime;
debugC(5, kDebugGraphics, "Movie::updateFrameState (%d): Starting update (movie time: %d)", _id, movieTime);
// This complexity is necessary becuase movies can have more than one frame
@@ -406,10 +273,10 @@ void Movie::updateFrameState() {
// see if there are any new frames to show.
for (auto it = _framesNotYetShown.begin(); it != _framesNotYetShown.end();) {
MovieFrame *frame = *it;
- bool isAfterStart = movieTime >= frame->startInMilliseconds();
+ bool isAfterStart = movieTime >= frame->startInMilliseconds;
if (isAfterStart) {
- _framesOnScreen.push_back(frame);
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ _framesOnScreen.insert(frame);
+ invalidateRect(getFrameBoundingBox(frame));
// We don't need ++it because we will either have another frame
// that needs to be drawn, or we have reached the end of the new
@@ -425,61 +292,48 @@ void Movie::updateFrameState() {
// Now see if there are any old frames that no longer need to be shown.
for (auto it = _framesOnScreen.begin(); it != _framesOnScreen.end();) {
MovieFrame *frame = *it;
- bool isAfterEnd = movieTime >= frame->endInMilliseconds();
+ bool isAfterEnd = movieTime >= frame->endInMilliseconds;
if (isAfterEnd) {
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(frame));
+ invalidateRect(getFrameBoundingBox(frame));
it = _framesOnScreen.erase(it);
+
+ if (_framesOnScreen.empty() && movieTime >= _fullTime) {
+ _isPlaying = false;
+ if (_hasStill) {
+ _framesNotYetShown = _frames;
+ updateFrameState();
+ }
+ runEventHandlerIfExists(kMovieEndEvent);
+ break;
+ }
} else {
++it;
}
}
- // Now see if we're at the end of the movie.
- if (_framesOnScreen.empty() && _framesNotYetShown.empty()) {
- _isPlaying = false;
- _framesOnScreen.clear();
- if (!_stills.empty()) {
- showPersistentFrame();
- }
-
- runEventHandlerIfExists(kMovieEndEvent);
- return;
- }
-
// Show the frames that are currently active, for debugging purposes.
for (MovieFrame *frame : _framesOnScreen) {
- debugC(5, kDebugGraphics, " (time: %d ms) Frame %d (%d x %d) @ (%d, %d); start: %d ms, end: %d ms, keyframeEnd: %d ms, zIndex = %d", movieTime, frame->index(), frame->width(), frame->height(), frame->left(), frame->top(), frame->startInMilliseconds(), frame->endInMilliseconds(), frame->keyframeEndInMilliseconds(), frame->zCoordinate());
- }
-}
-
-void Movie::showPersistentFrame() {
- for (MovieFrame *still : _stills) {
- _framesOnScreen.push_back(still);
- g_engine->_dirtyRects.push_back(getFrameBoundingBox(still));
+ 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);
}
}
void Movie::redraw(Common::Rect &rect) {
- // Make sure the frames are ordered properly before we attempt to draw them.
- Common::sort(_framesOnScreen.begin(), _framesOnScreen.end(), [](MovieFrame * a, MovieFrame * b) {
- return a->zCoordinate() > b->zCoordinate();
- });
-
for (MovieFrame *frame : _framesOnScreen) {
Common::Rect bbox = getFrameBoundingBox(frame);
Common::Rect areaToRedraw = bbox.findIntersectingRect(rect);
if (!areaToRedraw.isEmpty()) {
Common::Point originOnScreen(areaToRedraw.left, areaToRedraw.top);
- areaToRedraw.translate(-frame->left() - _boundingBox.left, -frame->top() - _boundingBox.top);
- areaToRedraw.clip(Common::Rect(0, 0, frame->width(), frame->height()));
- g_engine->_screen->simpleBlitFrom(frame->_surface, areaToRedraw, originOnScreen);
+ areaToRedraw.translate(-frame->leftTop.x - _boundingBox.left, -frame->leftTop.y - _boundingBox.top);
+ areaToRedraw.clip(Common::Rect(0, 0, frame->image->width(), frame->image->height()));
+ g_engine->_screen->simpleBlitFrom(frame->image->_surface, areaToRedraw, originOnScreen);
}
}
}
Common::Rect Movie::getFrameBoundingBox(MovieFrame *frame) {
- Common::Rect bbox = frame->boundingBox();
- bbox.translate(_boundingBox.left, _boundingBox.top);
+ Common::Point origin = _boundingBox.origin() + frame->leftTop;
+ Common::Rect bbox = Common::Rect(origin, frame->image->width(), frame->image->height());
return bbox;
}
@@ -487,28 +341,21 @@ void Movie::readChunk(Chunk &chunk) {
// Individual chunks are "stills" and are stored in the first subfile.
uint sectionType = chunk.readTypedUint16();
switch ((MovieSectionType)sectionType) {
- case kMovieFrameSection: {
- debugC(5, kDebugLoading, "Movie::readStill(): Reading frame");
- MovieFrameHeader *header = new MovieFrameHeader(chunk);
- MovieFrame *frame = new MovieFrame(chunk, header);
- _stills.push_back(frame);
+ case kMovieImageDataSection:
+ readImageData(chunk);
break;
- }
- case kMovieFooterSection: {
- debugC(5, kDebugLoading, "Movie::readStill(): Reading footer");
- MovieFrameFooter *footer = new MovieFrameFooter(chunk);
- _footers.push_back(footer);
+ case kMovieFrameDataSection:
+ readFrameData(chunk);
break;
- }
default:
error("Unknown movie still section type");
}
+ _hasStill = true;
}
void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
- // READ THE METADATA FOR THE WHOLE MOVIE.
uint expectedRootSectionType = chunk.readTypedUint16();
debugC(5, kDebugLoading, "Movie::readSubfile(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(expectedRootSectionType), static_cast<long long int>(chunk.pos()));
if (kMovieRootSection != (MovieSectionType)expectedRootSectionType) {
@@ -540,24 +387,18 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
uint sectionType = chunk.readTypedUint16();
debugC(5, kDebugLoading, "Movie::readSubfile(): sectionType = 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
switch (MovieSectionType(sectionType)) {
- case kMovieFrameSection: {
- MovieFrameHeader *header = new MovieFrameHeader(chunk);
- MovieFrame *frame = new MovieFrame(chunk, header);
- _frames.push_back(frame);
+ case kMovieImageDataSection:
+ readImageData(chunk);
break;
- }
- case kMovieFooterSection: {
- MovieFrameFooter *footer = new MovieFrameFooter(chunk);
- _footers.push_back(footer);
+ case kMovieFrameDataSection:
+ readFrameData(chunk);
break;
- }
default:
error("Movie::readSubfile(): Unknown movie animation section type 0x%x (@0x%llx)", static_cast<uint>(sectionType), static_cast<long long int>(chunk.pos()));
}
- // READ THE NEXT CHUNK.
chunk = subfile.nextChunk();
isAnimationChunk = (chunk._id == _animationChunkReference);
}
@@ -572,7 +413,6 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) No audio chunk to read. (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
}
- // READ THE FOOTER FOR THIS SUBFILE.
debugC(5, kDebugLoading, "Movie::readSubfile(): (Frameset %d of %d) Reading header chunk... (@0x%llx)", i, chunkCount, static_cast<long long int>(chunk.pos()));
bool isHeaderChunk = (chunk._id == _chunkReference);
if (isHeaderChunk) {
@@ -585,21 +425,62 @@ void Movie::readSubfile(Subfile &subfile, Chunk &chunk) {
}
}
- // SET THE MOVIE FRAME FOOTERS.
- for (MovieFrame *frame : _stills) {
- for (MovieFrameFooter *footer : _footers) {
- if (frame->index() == footer->_index) {
- frame->setFooter(footer);
- }
+ for (MovieFrame *frame : _frames) {
+ if (frame->endInMilliseconds > _fullTime) {
+ _fullTime = frame->endInMilliseconds;
}
}
- for (MovieFrame *frame : _frames) {
- for (MovieFrameFooter *footer : _footers) {
- if (frame->index() == footer->_index) {
- frame->setFooter(footer);
+ if (_hasStill) {
+ _framesNotYetShown = _frames;
+ }
+}
+
+void Movie::invalidateRect(const Common::Rect &rect) {
+ g_engine->_dirtyRects.push_back(rect);
+}
+
+void Movie::readImageData(Chunk &chunk) {
+ MovieFrameHeader *header = new MovieFrameHeader(chunk);
+ MovieFrameImage *frame = new MovieFrameImage(chunk, header);
+ _images.push_back(frame);
+}
+
+void Movie::readFrameData(Chunk &chunk) {
+ uint frameDataToRead = chunk.readTypedUint16();
+ for (uint i = 0; i < frameDataToRead; i++) {
+ MovieFrame *frame = new MovieFrame(chunk);
+
+ // 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
+ // do a linear search, which is how the original does it.
+ for (MovieFrameImage *image : _images) {
+ if (image->index() == frame->index) {
+ frame->image = image;
+ break;
+ }
+ }
+
+ if (frame->keyframeIndex != 0) {
+ for (MovieFrameImage *image : _images) {
+ if (image->index() == frame->keyframeIndex) {
+ frame->keyframeImage = image;
+ break;
+ }
}
}
+
+ _frames.push_back(frame);
+ }
+}
+
+int Movie::compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b) {
+ if (b->zIndex > a->zIndex) {
+ return 1;
+ } else if (a->zIndex > b->zIndex) {
+ return -1;
+ } else {
+ return 0;
}
}
diff --git a/engines/mediastation/assets/movie.h b/engines/mediastation/assets/movie.h
index aca2623f284..896a0311836 100644
--- a/engines/mediastation/assets/movie.h
+++ b/engines/mediastation/assets/movie.h
@@ -40,58 +40,44 @@ public:
uint _keyframeEndInMilliseconds = 0;
};
-class MovieFrameFooter {
+class MovieFrameImage : public Bitmap {
public:
- MovieFrameFooter(Chunk &chunk);
-
- uint _unk1 = 0;
- uint _unk2 = 0;
- uint _startInMilliseconds = 0;
- uint _endInMilliseconds = 0;
- uint _left = 0;
- uint _top = 0;
- uint _unk3 = 0;
- uint _unk4 = 0;
- uint _zIndex = 0; // TODO: This is still unconfirmed but seems likely.
- uint _diffBetweenKeyframeAndFrameX = 0;
- uint _diffBetweenKeyframeAndFrameY = 0;
- uint _keyframeIndex = 0;
- uint _unk9 = 0;
- uint _index = 0;
-};
+ MovieFrameImage(Chunk &chunk, MovieFrameHeader *header);
+ virtual ~MovieFrameImage() override;
-class MovieFrame : public Bitmap {
-public:
- MovieFrame(Chunk &chunk, MovieFrameHeader *header);
- virtual ~MovieFrame() override;
-
- void setFooter(MovieFrameFooter *footer);
- uint32 left();
- uint32 top();
- Common::Point topLeft();
- Common::Rect boundingBox();
- uint32 index();
- uint32 startInMilliseconds();
- uint32 endInMilliseconds();
- uint32 keyframeEndInMilliseconds();
- // This is called zCoordinate because zIndex is too close to "index" and
- // that could be confusing.
- uint32 zCoordinate();
+ uint32 index() { return _bitmapHeader->_index; }
private:
MovieFrameHeader *_bitmapHeader = nullptr;
- MovieFrameFooter *_footer = nullptr;
};
enum MovieSectionType {
kMovieRootSection = 0x06a8,
- kMovieFrameSection = 0x06a9,
- kMovieFooterSection = 0x06aa
+ kMovieImageDataSection = 0x06a9,
+ kMovieFrameDataSection = 0x06aa
+};
+
+struct MovieFrame {
+ MovieFrame(Chunk &chunk);
+ uint unk3 = 0;
+ uint unk4 = 0;
+ uint layerId = 0;
+ uint startInMilliseconds = 0;
+ uint endInMilliseconds = 0;
+ Common::Point leftTop;
+ Common::Point diffBetweenKeyframeAndFrame;
+ uint blitType = 0;
+ int16 zIndex = 0;
+ uint keyframeIndex = 0;
+ bool keepAfterEnd = false;
+ uint index = 0;
+ MovieFrameImage *image = nullptr;
+ MovieFrameImage *keyframeImage = nullptr;
};
class Movie : public SpatialEntity {
public:
- Movie() : SpatialEntity(kAssetTypeMovie) {};
+ Movie() : _framesOnScreen(Movie::compareFramesByZIndex), SpatialEntity(kAssetTypeMovie) {}
virtual ~Movie() override;
virtual void readChunk(Chunk &chunk) override;
@@ -111,29 +97,32 @@ public:
private:
AudioSequence _audioSequence;
uint _audioChunkCount = 0;
+ uint _fullTime = 0;
uint _loadType = 0;
double _dissolveFactor = 0.0;
bool _isPlaying = false;
- bool _atFirstFrame = true;
+ bool _hasStill = false;
Common::Array<MovieFrame *> _frames;
- Common::Array<MovieFrame *> _stills;
- Common::Array<MovieFrameFooter *> _footers;
+ Common::Array<MovieFrameImage *> _images;
Common::Array<MovieFrame *> _framesNotYetShown;
- Common::Array<MovieFrame *> _framesOnScreen;
+ Common::SortedArray<MovieFrame *, const MovieFrame *> _framesOnScreen;
// Script method implementations.
void timePlay();
void timeStop();
- void spatialShow();
- void spatialHide();
+ void setVisibility(bool visibility);
void updateFrameState();
- void showPersistentFrame();
+ void invalidateRect(const Common::Rect &rect);
+
+ void readImageData(Chunk &chunk);
+ void readFrameData(Chunk &chunk);
Common::Rect getFrameBoundingBox(MovieFrame *frame);
+ static int compareFramesByZIndex(const MovieFrame *a, const MovieFrame *b);
};
} // End of namespace MediaStation
Commit: 6cb99deffe1d4e09726f5e30e70c3fae8214747b
https://github.com/scummvm/scummvm/commit/6cb99deffe1d4e09726f5e30e70c3fae8214747b
Author: Nathanael Gentry (nathanael.gentrydb8 at gmail.com)
Date: 2025-06-14T11:07:14-04:00
Commit Message:
MEDIASTATION: JANITORIAL: FIx whitespace & namespace comments
Changed paths:
engines/mediastation/bitmap.cpp
engines/mediastation/bitmap.h
engines/mediastation/boot.cpp
engines/mediastation/boot.h
engines/mediastation/context.cpp
engines/mediastation/datafile.h
engines/mediastation/mediascript/function.cpp
engines/mediastation/mediascript/scriptconstants.h
engines/mediastation/mediascript/scriptvalue.cpp
engines/mediastation/mediastation.cpp
diff --git a/engines/mediastation/bitmap.cpp b/engines/mediastation/bitmap.cpp
index 389c03555b4..c0251d3a5a8 100644
--- a/engines/mediastation/bitmap.cpp
+++ b/engines/mediastation/bitmap.cpp
@@ -41,8 +41,7 @@ bool BitmapHeader::isCompressed() {
return (_compressionType != kUncompressedBitmap1) && (_compressionType != kUncompressedBitmap2);
}
-Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) :
- _bitmapHeader(bitmapHeader) {
+Bitmap::Bitmap(Chunk &chunk, BitmapHeader *bitmapHeader) : _bitmapHeader(bitmapHeader) {
// The header must be constructed beforehand.
int16 width = _bitmapHeader->_dimensions.x;
int16 height = _bitmapHeader->_dimensions.y;
@@ -212,4 +211,4 @@ void Bitmap::decompress(Chunk &chunk) {
}
}
-}
+} // End of namespace MediaStation
diff --git a/engines/mediastation/bitmap.h b/engines/mediastation/bitmap.h
index 441396a33fe..2c92728fcc9 100644
--- a/engines/mediastation/bitmap.h
+++ b/engines/mediastation/bitmap.h
@@ -63,6 +63,6 @@ private:
void decompress(Chunk &chunk);
};
-}
+} // End of namespace MediaStation
#endif
\ No newline at end of file
diff --git a/engines/mediastation/boot.cpp b/engines/mediastation/boot.cpp
index fb767591453..3aabdda634a 100644
--- a/engines/mediastation/boot.cpp
+++ b/engines/mediastation/boot.cpp
@@ -144,7 +144,7 @@ SubfileDeclarationSectionType SubfileDeclaration::getSectionType(Chunk &chunk) {
#pragma endregion
#pragma region CursorDeclaration
-CursorDeclaration::CursorDeclaration(Chunk& chunk) {
+CursorDeclaration::CursorDeclaration(Chunk &chunk) {
uint16 unk1 = chunk.readTypedUint16(); // Always 0x0001
_id = chunk.readTypedUint16();
_unk = chunk.readTypedUint16();
@@ -154,7 +154,7 @@ CursorDeclaration::CursorDeclaration(Chunk& chunk) {
#pragma endregion
#pragma region Boot
-Boot::Boot(const Common::Path &path) : Datafile(path){
+Boot::Boot(const Common::Path &path) : Datafile(path) {
Subfile subfile = getNextSubfile();
Chunk chunk = subfile.nextChunk();
diff --git a/engines/mediastation/boot.h b/engines/mediastation/boot.h
index f77332bbdb9..664f22135be 100644
--- a/engines/mediastation/boot.h
+++ b/engines/mediastation/boot.h
@@ -69,7 +69,7 @@ public:
uint _screenId = 0;
private:
- ScreenDeclarationSectionType getSectionType(Chunk& chunk);
+ ScreenDeclarationSectionType getSectionType(Chunk &chunk);
};
enum FileDeclarationSectionType {
diff --git a/engines/mediastation/context.cpp b/engines/mediastation/context.cpp
index 2363b41c5f2..02756b834ee 100644
--- a/engines/mediastation/context.cpp
+++ b/engines/mediastation/context.cpp
@@ -367,7 +367,7 @@ bool Context::readHeaderSection(Chunk &chunk) {
// TODO: Avoid the copying here!
const uint PALETTE_ENTRIES = 256;
const uint PALETTE_BYTES = PALETTE_ENTRIES * 3;
- byte* buffer = new byte[PALETTE_BYTES];
+ byte *buffer = new byte[PALETTE_BYTES];
chunk.read(buffer, PALETTE_BYTES);
_palette = new Graphics::Palette(buffer, PALETTE_ENTRIES);
delete[] buffer;
diff --git a/engines/mediastation/datafile.h b/engines/mediastation/datafile.h
index 33d3c4c272a..5a1b47e9519 100644
--- a/engines/mediastation/datafile.h
+++ b/engines/mediastation/datafile.h
@@ -115,7 +115,7 @@ public:
// ReadStream implementation
virtual bool eos() const { return _parentStream->eos(); };
- virtual bool err() const {return _parentStream->err(); };
+ virtual bool err() const { return _parentStream->err(); };
virtual void clearErr() { _parentStream->clearErr(); };
virtual uint32 read(void *dataPtr, uint32 dataSize);
virtual int64 pos() const { return _parentStream->pos(); };
diff --git a/engines/mediastation/mediascript/function.cpp b/engines/mediastation/mediascript/function.cpp
index 89570482602..8f8de848757 100644
--- a/engines/mediastation/mediascript/function.cpp
+++ b/engines/mediastation/mediascript/function.cpp
@@ -46,4 +46,3 @@ ScriptValue Function::execute(Common::Array<ScriptValue> &args) {
}
} // End of namespace MediaStation
-
diff --git a/engines/mediastation/mediascript/scriptconstants.h b/engines/mediastation/mediascript/scriptconstants.h
index 9658dc49af1..232294a46f5 100644
--- a/engines/mediastation/mediascript/scriptconstants.h
+++ b/engines/mediastation/mediascript/scriptconstants.h
@@ -206,7 +206,7 @@ enum EventType {
kMovieStoppedEvent = 31,
kMovieBeginEvent = 32,
- //SPRITE EVENTS.
+ // SPRITE EVENTS.
// Just "MovieEnd" in source.
kSpriteMovieEndEvent = 23,
diff --git a/engines/mediastation/mediascript/scriptvalue.cpp b/engines/mediastation/mediascript/scriptvalue.cpp
index bbfcef64734..75697963fa2 100644
--- a/engines/mediastation/mediascript/scriptvalue.cpp
+++ b/engines/mediastation/mediascript/scriptvalue.cpp
@@ -234,8 +234,7 @@ BuiltInMethod ScriptValue::asMethodId() const {
bool ScriptValue::compare(Opcode op, const ScriptValue &lhs, const ScriptValue &rhs) {
if (lhs.getType() != rhs.getType()) {
- error("Attempt to compare mismatched types %s and %s",
- scriptValueTypeToStr(lhs.getType()), scriptValueTypeToStr(rhs.getType()));
+ error("Attempt to compare mismatched types %s and %s", scriptValueTypeToStr(lhs.getType()), scriptValueTypeToStr(rhs.getType()));
}
switch (lhs.getType()) {
diff --git a/engines/mediastation/mediastation.cpp b/engines/mediastation/mediastation.cpp
index 161da5f2b39..649733a3e20 100644
--- a/engines/mediastation/mediastation.cpp
+++ b/engines/mediastation/mediastation.cpp
@@ -149,11 +149,11 @@ Common::Error MediaStationEngine::run() {
}
_cursor->showCursor();
- if (ConfMan.hasKey("entry_context")) {
+ if (ConfMan.hasKey("entry_context")) {
// For development purposes, we can choose to start at an arbitrary context
// in this title. This might not work in all cases.
- uint entryContextId = ConfMan.get("entry_context").asUint64();
- warning("Starting at user-requested context %d", entryContextId);
+ uint entryContextId = ConfMan.get("entry_context").asUint64();
+ warning("Starting at user-requested context %d", entryContextId);
_requestedScreenBranchId = entryContextId;
} else {
_requestedScreenBranchId = _boot->_entryContextId;
More information about the Scummvm-git-logs
mailing list