diff options
Diffstat (limited to 'engines/mohawk')
-rw-r--r-- | engines/mohawk/configure.engine | 2 | ||||
-rw-r--r-- | engines/mohawk/console.cpp | 31 | ||||
-rw-r--r-- | engines/mohawk/cursors.cpp | 6 | ||||
-rw-r--r-- | engines/mohawk/detection_tables.h | 16 | ||||
-rw-r--r-- | engines/mohawk/livingbooks.cpp | 87 | ||||
-rw-r--r-- | engines/mohawk/livingbooks_code.cpp | 55 | ||||
-rw-r--r-- | engines/mohawk/livingbooks_code.h | 5 | ||||
-rw-r--r-- | engines/mohawk/myst.cpp | 12 | ||||
-rw-r--r-- | engines/mohawk/myst_areas.cpp | 24 | ||||
-rw-r--r-- | engines/mohawk/myst_graphics.cpp | 165 | ||||
-rw-r--r-- | engines/mohawk/myst_graphics.h | 27 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/channelwood.cpp | 34 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/dni.cpp | 56 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/intro.cpp | 23 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/mechanical.cpp | 64 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/myst.cpp | 258 | ||||
-rw-r--r-- | engines/mohawk/myst_stacks/stoneship.cpp | 62 | ||||
-rw-r--r-- | engines/mohawk/riven.cpp | 22 | ||||
-rw-r--r-- | engines/mohawk/riven_external.cpp | 85 | ||||
-rw-r--r-- | engines/mohawk/riven_scripts.cpp | 8 | ||||
-rw-r--r-- | engines/mohawk/video.cpp | 569 | ||||
-rw-r--r-- | engines/mohawk/video.h | 315 |
22 files changed, 1321 insertions, 605 deletions
diff --git a/engines/mohawk/configure.engine b/engines/mohawk/configure.engine index fa9d15cffc..47402c4560 100644 --- a/engines/mohawk/configure.engine +++ b/engines/mohawk/configure.engine @@ -3,4 +3,4 @@ add_engine mohawk "Mohawk" yes "cstime myst riven" "Living Books" add_engine cstime "Where in Time is Carmen Sandiego?" no add_engine riven "Riven: The Sequel to Myst" no "" "" "16bit" -add_engine myst "Myst" no "" "" "16bit" +add_engine myst "Myst" no diff --git a/engines/mohawk/console.cpp b/engines/mohawk/console.cpp index d49f3e8637..9b5bae78be 100644 --- a/engines/mohawk/console.cpp +++ b/engines/mohawk/console.cpp @@ -248,9 +248,9 @@ bool MystConsole::Cmd_PlayMovie(int argc, const char **argv) { return true; } - int8 stackNum = 0; - + Common::String fileName; if (argc == 3 || argc > 4) { + int8 stackNum = 0; for (byte i = 1; i <= ARRAYSIZE(mystStackNames); i++) if (!scumm_stricmp(argv[2], mystStackNames[i - 1])) { stackNum = i; @@ -261,16 +261,27 @@ bool MystConsole::Cmd_PlayMovie(int argc, const char **argv) { debugPrintf("\'%s\' is not a stack name!\n", argv[2]); return true; } + + fileName = _vm->wrapMovieFilename(argv[1], stackNum - 1); + } else { + fileName = argv[1]; } - if (argc == 2) - _vm->_video->playMovie(argv[1], 0, 0); - else if (argc == 3) - _vm->_video->playMovie(_vm->wrapMovieFilename(argv[1], stackNum - 1), 0, 0); - else if (argc == 4) - _vm->_video->playMovie(argv[1], atoi(argv[2]), atoi(argv[3])); - else - _vm->_video->playMovie(_vm->wrapMovieFilename(argv[1], stackNum - 1), atoi(argv[3]), atoi(argv[4])); + VideoHandle handle = _vm->_video->playMovie(fileName); + if (!handle) { + debugPrintf("Failed to open movie '%s'\n", fileName.c_str()); + return true; + } + + if (argc == 4) { + handle->setX(atoi(argv[2])); + handle->setY(atoi(argv[3])); + } else if (argc > 4) { + handle->setX(atoi(argv[3])); + handle->setY(atoi(argv[4])); + } else { + handle->center(); + } return false; } diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index f1baac02e2..4b66829e6a 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -122,7 +122,11 @@ void MystCursorManager::setCursor(uint16 id) { // Myst ME stores some cursors as 24bpp images instead of 8bpp if (surface->format.bytesPerPixel == 1) { CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, 0); - CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256); + + // We're using the screen palette for the original game, but we need + // to use this for any 8bpp cursor in ME. + if (_vm->getFeatures() & GF_ME) + CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256); } else { Graphics::PixelFormat pixelFormat = g_system->getScreenFormat(); CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false, &pixelFormat); diff --git a/engines/mohawk/detection_tables.h b/engines/mohawk/detection_tables.h index 7632cde294..6bb836b5b8 100644 --- a/engines/mohawk/detection_tables.h +++ b/engines/mohawk/detection_tables.h @@ -1379,6 +1379,22 @@ static const MohawkGameDescription gameDescriptions[] = { "GRANDMA.EXE" }, + // Just Grandma and Me 1.0, Macintosh + { + { + "grandma", + "v1.0", + AD_ENTRY1("BookOutline", "9162483da06179e76f4a082412245efa"), + Common::EN_ANY, + Common::kPlatformMacintosh, + ADGF_NO_FLAGS, + GUIO1(GUIO_NOASPECT) + }, + GType_LIVINGBOOKSV1, + GF_LB_10, + 0 + }, + // Just Grandma and Me 1.1 Mac // From eisnerguy1 in bug#3610725 { diff --git a/engines/mohawk/livingbooks.cpp b/engines/mohawk/livingbooks.cpp index 998ef048f6..5af8fac901 100644 --- a/engines/mohawk/livingbooks.cpp +++ b/engines/mohawk/livingbooks.cpp @@ -363,6 +363,44 @@ void MohawkEngine_LivingBooks::destroyPage() { _focus = NULL; } +// Replace any colons (originally a slash) with another character +static Common::String replaceColons(const Common::String &in, char replace) { + Common::String out; + + for (uint32 i = 0; i < in.size(); i++) { + if (in[i] == ':') + out += replace; + else + out += in[i]; + } + + return out; +} + +// Helper function to assist in opening pages +static bool tryOpenPage(Archive *archive, const Common::String &fileName) { + // Try the plain file name first + if (archive->openFile(fileName)) + return true; + + // No colons, then bail out + if (!fileName.contains(':')) + return false; + + // Try replacing colons with underscores (in case the original was + // a Mac version and had slashes not as a separator). + if (archive->openFile(replaceColons(fileName, '_'))) + return true; + + // Try replacing colons with slashes (in case the original was a Mac + // version and had slashes as a separator). + if (archive->openFile(replaceColons(fileName, '/'))) + return true; + + // Failed to open the archive + return false; +} + bool MohawkEngine_LivingBooks::loadPage(LBMode mode, uint page, uint subpage) { destroyPage(); @@ -410,7 +448,7 @@ bool MohawkEngine_LivingBooks::loadPage(LBMode mode, uint page, uint subpage) { } Archive *pageArchive = createArchive(); - if (!filename.empty() && pageArchive->openFile(filename)) { + if (!filename.empty() && tryOpenPage(pageArchive, filename)) { _page = new LBPage(this); _page->open(pageArchive, 1000); } else { @@ -824,18 +862,18 @@ int MohawkEngine_LivingBooks::getIntFromConfig(const Common::String §ion, co Common::String MohawkEngine_LivingBooks::getFileNameFromConfig(const Common::String §ion, const Common::String &key, Common::String &leftover) { Common::String string = getStringFromConfig(section, key, leftover); - Common::String x; - uint32 i = 0; if (string.hasPrefix("//")) { // skip "//CD-ROM Title/" prefixes which we don't care about - i = 3; + uint i = 3; while (i < string.size() && string[i - 1] != '/') i++; + + // Already uses slashes, no need to convert further + return string.c_str() + i; } - x = string.c_str() + i; - return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(x) : convertWinFileName(x); + return (getPlatform() == Common::kPlatformMacintosh) ? convertMacFileName(string) : convertWinFileName(string); } Common::String MohawkEngine_LivingBooks::removeQuotesFromString(const Common::String &string, Common::String &leftover) { @@ -866,8 +904,10 @@ Common::String MohawkEngine_LivingBooks::convertMacFileName(const Common::String for (uint32 i = 0; i < string.size(); i++) { if (i == 0 && string[i] == ':') // First character should be ignored (another colon) continue; - if (string[i] == ':') + if (string[i] == ':') // Directory separator filename += '/'; + else if (string[i] == '/') // Literal slash + filename += ':'; // Replace by colon, as used by Mac OS X for slash else filename += string[i]; } @@ -3772,7 +3812,7 @@ LBMovieItem::~LBMovieItem() { void LBMovieItem::update() { if (_playing) { VideoHandle videoHandle = _vm->_video->findVideoHandle(_resourceId); - if (videoHandle == NULL_VID_HANDLE || _vm->_video->endOfVideo(videoHandle)) + if (!videoHandle || videoHandle->endOfVideo()) done(true); } @@ -3783,8 +3823,11 @@ bool LBMovieItem::togglePlaying(bool playing, bool restart) { if (playing) { if ((_loaded && _enabled && _globalEnabled) || _phase == kLBPhaseNone) { debug("toggled video for phase %d", _phase); - _vm->_video->playMovie(_resourceId, _rect.left, _rect.top); + VideoHandle handle = _vm->_video->playMovie(_resourceId); + if (!handle) + error("Failed to open tMOV %d", _resourceId); + handle->moveTo(_rect.left, _rect.top); return true; } } @@ -3808,9 +3851,9 @@ bool LBMiniGameItem::togglePlaying(bool playing, bool restart) { // just skip to the most logical page. For optional minigames, this // will return the player to the previous page. For mandatory minigames, // this will send the player to the next page. - // TODO: Document mini games from Arthur's Reading Race - uint16 destPage; + uint16 destPage = 0; + bool returnToMenu = false; // Figure out what minigame we have and bring us back to a page where // the player can continue @@ -3820,13 +3863,31 @@ bool LBMiniGameItem::togglePlaying(bool playing, bool restart) { destPage = 5; else if (_desc == "Fall") // Green Eggs and Ham: Fall minigame destPage = 13; + else if (_desc == "MagicWrite3") // Arthur's Reading Race: "Let Me Write" minigame (Page 3) + destPage = 3; + else if (_desc == "MagicWrite4") // Arthur's Reading Race: "Let Me Write" minigame (Page 4) + destPage = 4; + else if (_desc == "MagicSpy5") // Arthur's Reading Race: "I Spy" minigame (Page 5) + destPage = 5; + else if (_desc == "MagicSpy6") // Arthur's Reading Race: "I Spy" minigame (Page 6) + destPage = 6; + else if (_desc == "MagicWrite7") // Arthur's Reading Race: "Let Me Write" minigame (Page 7) + destPage = 7; + else if (_desc == "MagicSpy8") // Arthur's Reading Race: "I Spy" minigame (Page 8) + destPage = 8; + else if (_desc == "MagicRace") // Arthur's Reading Race: Race minigame + returnToMenu = true; else error("Unknown minigame '%s'", _desc.c_str()); GUI::MessageDialog dialog(Common::String::format("The '%s' minigame is not supported yet.", _desc.c_str())); dialog.runModal(); - _vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, destPage)); + // Go back to the menu if requested, otherwise go to the requested page + if (returnToMenu) + _vm->addNotifyEvent(NotifyEvent(kLBNotifyGoToControls, 1)); + else + _vm->addNotifyEvent(NotifyEvent(kLBNotifyChangePage, destPage)); return false; } @@ -3863,7 +3924,7 @@ void LBProxyItem::load() { debug(1, "LBProxyItem loading archive '%s' with id %d", filename.c_str(), baseId); Archive *pageArchive = _vm->createArchive(); - if (!pageArchive->openFile(filename)) + if (!tryOpenPage(pageArchive, filename)) error("failed to open archive '%s' (for proxy '%s')", filename.c_str(), _desc.c_str()); _page = new LBPage(_vm); _page->open(pageArchive, baseId); diff --git a/engines/mohawk/livingbooks_code.cpp b/engines/mohawk/livingbooks_code.cpp index 6dcd8c3ce7..b5ea547414 100644 --- a/engines/mohawk/livingbooks_code.cpp +++ b/engines/mohawk/livingbooks_code.cpp @@ -842,8 +842,8 @@ CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = { { "bottom", &LBCode::cmdBottom }, // 0x10 { "right", &LBCode::cmdRight }, - { "xpos", 0 }, - { "ypos", 0 }, + { "xpos", &LBCode::cmdXPos }, + { "ypos", &LBCode::cmdYPos }, { "playFrom", 0 }, { "move", &LBCode::cmdMove }, { 0, 0 }, @@ -852,13 +852,13 @@ CodeCommandInfo generalCommandInfo[NUM_GENERAL_COMMANDS] = { { "resetDragParams", 0 }, { "enableRollover", &LBCode::cmdUnimplemented /* FIXME */ }, { "setCursor", 0 }, - { "width", 0 }, - { "height", 0 }, + { "width", &LBCode::cmdWidth }, + { "height", &LBCode::cmdHeight }, { "getFrameBounds", 0 }, // also "getFrameRect" { "traceRect", 0 }, { "sqrt", 0 }, // 0x20 - { "deleteVar", 0 }, + { "deleteVar", &LBCode::cmdDeleteVar }, { "saveVars", 0 }, { "scriptLink", 0 }, { "setViewOrigin", &LBCode::cmdUnimplemented }, @@ -1131,6 +1131,38 @@ void LBCode::cmdRight(const Common::Array<LBValue> ¶ms) { _stack.push(rect.right); } +void LBCode::cmdXPos(const Common::Array<LBValue> ¶ms) { + if (params.size() != 1) + error("too many parameters (%d) to xpos", params.size()); + + Common::Point point = params[0].toPoint(); + _stack.push(point.x); +} + +void LBCode::cmdYPos(const Common::Array<LBValue> ¶ms) { + if (params.size() != 1) + error("too many parameters (%d) to ypos", params.size()); + + Common::Point point = params[0].toPoint(); + _stack.push(point.y); +} + +void LBCode::cmdWidth(const Common::Array<LBValue> ¶ms) { + if (params.size() > 1) + error("too many parameters (%d) to width", params.size()); + + Common::Rect rect = getRectFromParams(params); + _stack.push(rect.width()); +} + +void LBCode::cmdHeight(const Common::Array<LBValue> ¶ms) { + if (params.size() > 1) + error("too many parameters (%d) to height", params.size()); + + Common::Rect rect = getRectFromParams(params); + _stack.push(rect.height()); +} + void LBCode::cmdMove(const Common::Array<LBValue> ¶ms) { if (params.size() != 1 && params.size() != 2) error("incorrect number of parameters (%d) to move", params.size()); @@ -1263,6 +1295,14 @@ void LBCode::cmdGetProperty(const Common::Array<LBValue> ¶ms) { _stack.push(target->_variables[name]); } +void LBCode::cmdDeleteVar(const Common::Array<LBValue> ¶ms) { + if (params.size() != 1) + error("incorrect number of parameters (%d) to deleteVar", params.size()); + + const Common::String &string = params[0].toString(); + _vm->_variables.erase(string); +} + void LBCode::cmdExec(const Common::Array<LBValue> ¶ms) { if (params.size() != 1) error("incorrect number of parameters (%d) to exec", params.size()); @@ -1706,6 +1746,10 @@ uint LBCode::parseCode(const Common::String &source) { if (token != ' ' && token != '(' && wasFunction) error("while parsing script '%s', encountered incomplete function call", source.c_str()); + // Skip C++-style comments + if (token == '/' && lookahead == '/') + break; + // First, we check for simple operators. for (uint i = 0; i < NUM_LB_OPERATORS; i++) { if (token != operators[i].token) @@ -1736,6 +1780,7 @@ uint LBCode::parseCode(const Common::String &source) { switch (token) { // whitespace case ' ': + case '\t': // ignore break; // literal string diff --git a/engines/mohawk/livingbooks_code.h b/engines/mohawk/livingbooks_code.h index 080377ce99..6f6297d592 100644 --- a/engines/mohawk/livingbooks_code.h +++ b/engines/mohawk/livingbooks_code.h @@ -263,6 +263,10 @@ public: void cmdLeft(const Common::Array<LBValue> ¶ms); void cmdBottom(const Common::Array<LBValue> ¶ms); void cmdRight(const Common::Array<LBValue> ¶ms); + void cmdXPos(const Common::Array<LBValue> ¶ms); + void cmdYPos(const Common::Array<LBValue> ¶ms); + void cmdWidth(const Common::Array<LBValue> ¶ms); + void cmdHeight(const Common::Array<LBValue> ¶ms); void cmdMove(const Common::Array<LBValue> ¶ms); void cmdSetDragParams(const Common::Array<LBValue> ¶ms); void cmdNewList(const Common::Array<LBValue> ¶ms); @@ -273,6 +277,7 @@ public: void cmdDeleteAt(const Common::Array<LBValue> ¶ms); void cmdSetProperty(const Common::Array<LBValue> ¶ms); void cmdGetProperty(const Common::Array<LBValue> ¶ms); + void cmdDeleteVar(const Common::Array<LBValue> ¶ms); void cmdExec(const Common::Array<LBValue> ¶ms); void cmdReturn(const Common::Array<LBValue> ¶ms); void cmdSetPlayParams(const Common::Array<LBValue> ¶ms); diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 7634e8d88a..b6a6c27329 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -413,7 +413,12 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS // Fill screen with black and empty cursor _cursor->setCursor(0); - _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0)); + + if (getFeatures() & GF_ME) + _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0)); + else + _gfx->clearScreenPalette(); + _system->updateScreen(); _sound->stopSound(); @@ -495,9 +500,10 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _cache.clear(); _gfx->clearCache(); - // Play Flyby Entry Movie on Masterpiece Edition. - const char *flyby = 0; if (getFeatures() & GF_ME) { + // Play Flyby Entry Movie on Masterpiece Edition. + const char *flyby = 0; + switch (_curStack) { case kSeleniticStack: flyby = "selenitic flyby"; diff --git a/engines/mohawk/myst_areas.cpp b/engines/mohawk/myst_areas.cpp index 4a3001774a..7a9596d8e0 100644 --- a/engines/mohawk/myst_areas.cpp +++ b/engines/mohawk/myst_areas.cpp @@ -223,20 +223,26 @@ VideoHandle MystResourceType6::playMovie() { VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); // If the video is not running, play it - if (handle == NULL_VID_HANDLE || _vm->_video->endOfVideo(handle)) { - handle = _vm->_video->playMovie(_videoFile, _left, _top, _loop); + if (!handle || handle->endOfVideo()) { + handle = _vm->_video->playMovie(_videoFile); + if (!handle) + error("Failed to open '%s'", _videoFile.c_str()); + + handle->moveTo(_left, _top); + handle->setLooping(_loop != 0); + if (_direction == -1) { - _vm->_video->seekToTime(handle, _vm->_video->getDuration(handle)); - _vm->_video->setVideoRate(handle, -1); + handle->seek(handle->getDuration()); + handle->setRate(-1); } } else { // Resume the video - _vm->_video->pauseMovie(handle, false); + handle->pause(false); } if (_playBlocking) { _vm->_video->waitUntilMovieEnds(handle); - handle = NULL_VID_HANDLE; + return VideoHandle(); } return handle; @@ -249,13 +255,13 @@ void MystResourceType6::handleCardChange() { bool MystResourceType6::isPlaying() { VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); - return handle != NULL_VID_HANDLE && !_vm->_video->endOfVideo(handle); + return handle && !handle->endOfVideo(); } void MystResourceType6::pauseMovie(bool pause) { VideoHandle handle = _vm->_video->findVideoHandle(_videoFile); - if (handle != NULL_VID_HANDLE && !_vm->_video->endOfVideo(handle)) - _vm->_video->pauseMovie(handle, pause); + if (handle && !handle->endOfVideo()) + handle->pause(pause); } MystResourceType7::MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) { diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index 9ea9f15444..49f97cca63 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/util.h" +#include "graphics/palette.h" #include "image/pict.h" namespace Mohawk { @@ -37,16 +38,20 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { _viewport = Common::Rect(544, 332); - // The original version of Myst could run in 8bpp color too. - // However, it dithered videos to 8bpp and they looked considerably - // worse (than they already did :P). So we're not even going to - // support 8bpp mode in Myst (Myst ME required >8bpp anyway). - initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size! + if (_vm->getFeatures() & GF_ME) { + // High color + initGraphics(_viewport.width(), _viewport.height(), true, NULL); - _pixelFormat = _vm->_system->getScreenFormat(); + if (_vm->_system->getScreenFormat().bytesPerPixel == 1) + error("Myst ME requires greater than 256 colors to run"); + } else { + // Paletted + initGraphics(_viewport.width(), _viewport.height(), true); + setBasePalette(); + setPaletteToScreen(); + } - if (_pixelFormat.bytesPerPixel == 1) - error("Myst requires greater than 256 colors to run"); + _pixelFormat = _vm->_system->getScreenFormat(); // Initialize our buffer _backBuffer = new Graphics::Surface(); @@ -101,7 +106,9 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) { mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); } else { mhkSurface = _bmpDecoder->decodeImage(dataStream); - mhkSurface->convertToTrueColor(); + + if (_vm->getFeatures() & GF_ME) + mhkSurface->convertToTrueColor(); } assert(mhkSurface); @@ -151,7 +158,8 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm } void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) { - Graphics::Surface *surface = findImage(image)->getSurface(); + MohawkSurface *mhkSurface = findImage(image); + Graphics::Surface *surface = mhkSurface->getSurface(); // Make sure the image is bottom aligned in the dest rect dest.top = dest.bottom - MIN<int>(surface->h, dest.height()); @@ -190,6 +198,13 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, for (uint16 i = 0; i < height; i++) memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel); + + if (!(_vm->getFeatures() & GF_ME)) { + // Make sure the palette is set + assert(mhkSurface->getPalette()); + memcpy(_palette + 10 * 3, mhkSurface->getPalette() + 10 * 3, (256 - 10 * 2) * 3); + setPaletteToScreen(); + } } void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) { @@ -419,12 +434,16 @@ void MystGraphics::transitionDissolve(Common::Rect rect, uint step) { for (uint16 x = rect.left; x < rect.right; x++) { if (linePattern[x % 4]) { - if (_pixelFormat.bytesPerPixel == 2) { - uint16 *dst = (uint16 *)screen->getBasePtr(x, y); - *dst = *(const uint16 *)_backBuffer->getBasePtr(x, y); - } else { - uint32 *dst = (uint32 *)screen->getBasePtr(x, y); - *dst = *(const uint32 *)_backBuffer->getBasePtr(x, y); + switch (_pixelFormat.bytesPerPixel) { + case 1: + *((byte *)screen->getBasePtr(x, y)) = *((const byte *)_backBuffer->getBasePtr(x, y)); + break; + case 2: + *((uint16 *)screen->getBasePtr(x, y)) = *((const uint16 *)_backBuffer->getBasePtr(x, y)); + break; + case 4: + *((uint32 *)screen->getBasePtr(x, y)) = *((const uint32 *)_backBuffer->getBasePtr(x, y)); + break; } } } @@ -588,11 +607,11 @@ void MystGraphics::drawRect(Common::Rect rect, RectState state) { Graphics::Surface *screen = _vm->_system->lockScreen(); if (state == kRectEnabled) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 255, 0) : 250); else if (state == kRectUnreachable) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 0, 255) : 252); else - screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(255, 0, 0) : 249); _vm->_system->unlockScreen(); } @@ -629,50 +648,94 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; } -void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { - Graphics::Surface *screen = _vm->_system->lockScreen(); +void MystGraphics::fadeToBlack() { + // This is only for the demo + assert(!(_vm->getFeatures() & GF_ME)); - for (uint16 y = 0; y < _viewport.height(); y++) - for (uint16 x = 0; x < _viewport.width(); x++) { - uint32 color; - uint8 r, g, b; + // Linear fade in 64 steps + for (int i = 63; i >= 0; i--) { + byte palette[256 * 3]; + byte *src = _palette; + byte *dst = palette; - if (_pixelFormat.bytesPerPixel == 2) - color = *(const uint16 *)_backBuffer->getBasePtr(x, y); - else - color = *(const uint32 *)_backBuffer->getBasePtr(x, y); + for (uint j = 0; j < sizeof(palette); j++) + *dst++ = *src++ * i / 64; - _pixelFormat.colorToRGB(color, r, g, b); + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); + _vm->_system->updateScreen(); + } +} - r = CLIP<int16>((int16)r - saturation, 0, 255); - g = CLIP<int16>((int16)g - saturation, 0, 255); - b = CLIP<int16>((int16)b - saturation, 0, 255); +void MystGraphics::fadeFromBlack() { + // This is only for the demo + assert(!(_vm->getFeatures() & GF_ME)); - color = _pixelFormat.RGBToColor(r, g, b); + copyBackBufferToScreen(_viewport); - if (_pixelFormat.bytesPerPixel == 2) { - uint16 *dst = (uint16 *)screen->getBasePtr(x, y); - *dst = color; - } else { - uint32 *dst = (uint32 *)screen->getBasePtr(x, y); - *dst = color; - } - } + // Linear fade in 64 steps + for (int i = 0; i < 64; i++) { + byte palette[256 * 3]; + byte *src = _palette; + byte *dst = palette; - _vm->_system->unlockScreen(); + for (uint j = 0; j < sizeof(palette); j++) + *dst++ = *src++ * i / 64; + + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); + _vm->_system->updateScreen(); + } + + // Set the full palette + _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256); _vm->_system->updateScreen(); } -void MystGraphics::fadeToBlack() { - for (int16 i = 0; i < 256; i += 32) { - copyBackBufferToScreenWithSaturation(i); - } +void MystGraphics::clearScreenPalette() { + // Set the palette to all black + byte palette[256 * 3]; + memset(palette, 0, sizeof(palette)); + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); } -void MystGraphics::fadeFromBlack() { - for (int16 i = 256; i >= 0; i -= 32) { - copyBackBufferToScreenWithSaturation(i); - } +void MystGraphics::setBasePalette() { + // Entries [0, 9] of the palette + static const byte lowPalette[] = { + 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, + 0x80, 0x80, 0x00, + 0x00, 0x00, 0x80, + 0x80, 0x00, 0x80, + 0x00, 0x80, 0x80, + 0xC0, 0xC0, 0xC0, + 0xC0, 0xDC, 0xC0, + 0xA6, 0xCA, 0xF0 + }; + + // Entries [246, 255] of the palette + static const byte highPalette[] = { + 0xFF, 0xFB, 0xF0, + 0xA0, 0xA0, 0xA4, + 0x80, 0x80, 0x80, + 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x00 + }; + + // Note that 0 and 255 are different from normal Windows. + // Myst seems to hack that to white, resp. black (probably for Mac compat). + + memcpy(_palette, lowPalette, sizeof(lowPalette)); + memset(_palette + sizeof(lowPalette), 0, sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette)); + memcpy(_palette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette)); +} + +void MystGraphics::setPaletteToScreen() { + _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256); } } // End of namespace Mohawk diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h index 1f70320bf6..6281c94cc8 100644 --- a/engines/mohawk/myst_graphics.h +++ b/engines/mohawk/myst_graphics.h @@ -40,7 +40,7 @@ enum RectState { class MystGraphics : public GraphicsManager { public: - MystGraphics(MohawkEngine_Myst*); + MystGraphics(MohawkEngine_Myst *vm); ~MystGraphics(); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); @@ -55,18 +55,15 @@ public: void fadeToBlack(); void fadeFromBlack(); + void clearScreenPalette(); + void setBasePalette(); + void setPaletteToScreen(); + const byte *getPalette() const { return _palette; } + protected: MohawkSurface *decodeImage(uint16 id); MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - void simulatePreviousDrawDelay(const Common::Rect &dest); - void copyBackBufferToScreenWithSaturation(int16 saturation); - void transitionDissolve(Common::Rect rect, uint step); - void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay); - void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay); - void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay); - void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay); - void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps); - void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps); + private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; @@ -74,11 +71,21 @@ private: Graphics::Surface *_backBuffer; Graphics::PixelFormat _pixelFormat; Common::Rect _viewport; + byte _palette[256 * 3]; int _enableDrawingTimeSimulation; uint32 _nextAllowedDrawTime; static const uint _constantDrawDelay = 10; // ms static const uint _proportionalDrawDelay = 500; // pixels per ms + + void simulatePreviousDrawDelay(const Common::Rect &dest); + void transitionDissolve(Common::Rect rect, uint step); + void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay); + void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps); + void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps); }; } // End of namespace Mohawk diff --git a/engines/mohawk/myst_stacks/channelwood.cpp b/engines/mohawk/myst_stacks/channelwood.cpp index 2dd5745550..dfa15a9b6c 100644 --- a/engines/mohawk/myst_stacks/channelwood.cpp +++ b/engines/mohawk/myst_stacks/channelwood.cpp @@ -299,13 +299,17 @@ bool Channelwood::pipeChangeValve(bool open, uint16 mask) { void Channelwood::o_bridgeToggle(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Bridge rise / skink video", op); - VideoHandle bridge = _vm->_video->playMovie(_vm->wrapMovieFilename("bridge", kChannelwoodStack), 292, 203); + VideoHandle bridge = _vm->_video->playMovie(_vm->wrapMovieFilename("bridge", kChannelwoodStack)); + if (!bridge) + error("Failed to open 'bridge' movie"); + + bridge->moveTo(292, 203); // Toggle bridge state if (_state.waterPumpBridgeState) - _vm->_video->setVideoBounds(bridge, Audio::Timestamp(0, 3050, 600), Audio::Timestamp(0, 6100, 600)); + bridge->setBounds(Audio::Timestamp(0, 3050, 600), Audio::Timestamp(0, 6100, 600)); else - _vm->_video->setVideoBounds(bridge, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 3050, 600)); + bridge->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 3050, 600)); _vm->_video->waitUntilMovieEnds(bridge); } @@ -317,13 +321,17 @@ void Channelwood::o_pipeExtend(uint16 op, uint16 var, uint16 argc, uint16 *argv) debugC(kDebugScript, "\tsoundId: %d", soundId); _vm->_sound->replaceSoundMyst(soundId); - VideoHandle pipe = _vm->_video->playMovie(_vm->wrapMovieFilename("pipebrid", kChannelwoodStack), 267, 170); + VideoHandle pipe = _vm->_video->playMovie(_vm->wrapMovieFilename("pipebrid", kChannelwoodStack)); + if (!pipe) + error("Failed to open 'pipebrid' movie"); + + pipe->moveTo(267, 170); // Toggle pipe state if (_state.pipeState) - _vm->_video->setVideoBounds(pipe, Audio::Timestamp(0, 3040, 600), Audio::Timestamp(0, 6080, 600)); + pipe->setBounds(Audio::Timestamp(0, 3040, 600), Audio::Timestamp(0, 6080, 600)); else - _vm->_video->setVideoBounds(pipe, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 3040, 600)); + pipe->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 3040, 600)); _vm->_video->waitUntilMovieEnds(pipe); _vm->_sound->resumeBackgroundMyst(); @@ -605,23 +613,29 @@ void Channelwood::o_hologramMonitor(uint16 op, uint16 var, uint16 argc, uint16 * _vm->_video->stopVideos(); + VideoHandle handle; + switch (button) { case 0: - _vm->_video->playMovie(_vm->wrapMovieFilename("monalgh", kChannelwoodStack), 227, 70); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monalgh", kChannelwoodStack)); break; case 1: - _vm->_video->playMovie(_vm->wrapMovieFilename("monamth", kChannelwoodStack), 227, 70); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monamth", kChannelwoodStack)); break; case 2: - _vm->_video->playMovie(_vm->wrapMovieFilename("monasirs", kChannelwoodStack), 227, 70); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monasirs", kChannelwoodStack)); break; case 3: - _vm->_video->playMovie(_vm->wrapMovieFilename("monsmsg", kChannelwoodStack), 227, 70); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("monsmsg", kChannelwoodStack)); break; default: warning("Opcode %d Control Variable Out of Range", op); break; } + + // Move the video to the right location + if (handle) + handle->moveTo(227, 70); } } diff --git a/engines/mohawk/myst_stacks/dni.cpp b/engines/mohawk/myst_stacks/dni.cpp index 3eb3c40cbb..6ba0b63423 100644 --- a/engines/mohawk/myst_stacks/dni.cpp +++ b/engines/mohawk/myst_stacks/dni.cpp @@ -103,14 +103,14 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { VideoHandle atrus = _vm->_video->findVideoHandle(_video); // Good ending and Atrus asked to give page - if (_globals.ending == 1 && _vm->_video->getTime(atrus) > (uint)Audio::Timestamp(0, 6801, 600).msecs()) { + if (_globals.ending == 1 && atrus && atrus->getTime() > (uint)Audio::Timestamp(0, 6801, 600).msecs()) { _globals.ending = 2; _globals.heldPage = 0; _vm->setMainCursor(kDefaultMystCursor); // Play movie end (atrus leaving) - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 14813, 600), _vm->_video->getDuration(atrus)); - _vm->_video->setVideoLooping(atrus, false); + atrus->setBounds(Audio::Timestamp(0, 14813, 600), atrus->getDuration()); + atrus->setLooping(false); _atrusLeft = true; _waitForLoop = false; @@ -121,8 +121,12 @@ void Dni::o_handPage(uint16 op, uint16 var, uint16 argc, uint16 *argv) { void Dni::atrusLeft_run() { if (_vm->_system->getMillis() > _atrusLeftTime + 63333) { _video = _vm->wrapMovieFilename("atrus2", kDniStack); - VideoHandle atrus = _vm->_video->playMovie(_video, 215, 77); - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 98000, 600)); + VideoHandle atrus = _vm->_video->playMovie(_video); + if (!atrus) + error("Failed to open '%s'", _video.c_str()); + + atrus->moveTo(215, 77); + atrus->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 98000, 600)); _waitForLoop = true; _loopStart = 73095; @@ -139,9 +143,13 @@ void Dni::atrusLeft_run() { void Dni::loopVideo_run() { if (!_vm->_video->isVideoPlaying()) { - VideoHandle atrus = _vm->_video->playMovie(_video, 215, 77); - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, _loopStart, 600), Audio::Timestamp(0, _loopEnd, 600)); - _vm->_video->setVideoLooping(atrus, true); + VideoHandle atrus = _vm->_video->playMovie(_video); + if (!atrus) + error("Failed to open '%s'", _video.c_str()); + + atrus->moveTo(215, 77); + atrus->setBounds(Audio::Timestamp(0, _loopStart, 600), Audio::Timestamp(0, _loopEnd, 600)); + atrus->setLooping(true); _waitForLoop = false; } @@ -155,14 +163,23 @@ void Dni::atrus_run() { // Atrus asking for page if (!_vm->_video->isVideoPlaying()) { _video = _vm->wrapMovieFilename("atr1page", kDniStack); - VideoHandle atrus = _vm->_video->playMovie(_video, 215, 77, true); - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 7388, 600), Audio::Timestamp(0, 14700, 600)); + VideoHandle atrus = _vm->_video->playMovie(_video); + if (!atrus) + error("Failed to open '%s'", _video.c_str()); + + atrus->moveTo(215, 77); + atrus->setLooping(true); + atrus->setBounds(Audio::Timestamp(0, 7388, 600), Audio::Timestamp(0, 14700, 600)); } } else if (_globals.ending != 3 && _globals.ending != 4) { if (_globals.heldPage == 13) { _video = _vm->wrapMovieFilename("atr1page", kDniStack); - VideoHandle atrus = _vm->_video->playMovie(_video, 215, 77); - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 14700, 600)); + VideoHandle atrus = _vm->_video->playMovie(_video); + if (!atrus) + error("Failed to open '%s'", _video.c_str()); + + atrus->moveTo(215, 77); + atrus->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 14700, 600)); _waitForLoop = true; _loopStart = 7388; @@ -173,8 +190,12 @@ void Dni::atrus_run() { } else { _video = _vm->wrapMovieFilename("atr1nopg", kDniStack); - VideoHandle atrus = _vm->_video->playMovie(_video, 215, 77); - _vm->_video->setVideoBounds(atrus, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 46175, 600)); + VideoHandle atrus = _vm->_video->playMovie(_video); + if (!atrus) + error("Failed to open '%s'", _video.c_str()); + + atrus->moveTo(215, 77); + atrus->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 46175, 600)); _waitForLoop = true; _loopStart = 30656; @@ -184,7 +205,12 @@ void Dni::atrus_run() { _globals.ending = 3; } } else if (!_vm->_video->isVideoPlaying()) { - _vm->_video->playMovie(_vm->wrapMovieFilename("atrwrite", kDniStack), 215, 77, true); + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename("atrwrite", kDniStack)); + if (!handle) + error("Failed to open atrwrite movie"); + + handle->moveTo(215, 77); + handle->setLooping(true); } } diff --git a/engines/mohawk/myst_stacks/intro.cpp b/engines/mohawk/myst_stacks/intro.cpp index 2a33379198..dc66984398 100644 --- a/engines/mohawk/myst_stacks/intro.cpp +++ b/engines/mohawk/myst_stacks/intro.cpp @@ -98,10 +98,16 @@ void Intro::introMovies_run() { // Play Intro Movies // This is all quite messy... + VideoHandle handle; + switch (_introStep) { case 0: _introStep = 1; - _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("broder", kIntroStack)); + if (!handle) + error("Failed to open broder movie"); + + handle->center(); break; case 1: if (!_vm->_video->isVideoPlaying()) @@ -109,7 +115,11 @@ void Intro::introMovies_run() { break; case 2: _introStep = 3; - _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("cyanlogo", kIntroStack)); + if (!handle) + error("Failed to open cyanlogo movie"); + + handle->center(); break; case 3: if (!_vm->_video->isVideoPlaying()) @@ -118,8 +128,13 @@ void Intro::introMovies_run() { case 4: _introStep = 5; - if (!(_vm->getFeatures() & GF_DEMO)) // The demo doesn't have the intro video - _vm->_video->playMovie(_vm->wrapMovieFilename("intro", kIntroStack)); + if (!(_vm->getFeatures() & GF_DEMO)) { // The demo doesn't have the intro video + handle = _vm->_video->playMovie(_vm->wrapMovieFilename("intro", kIntroStack)); + if (!handle) + error("Failed to open intro movie"); + + handle->center(); + } break; case 5: if (!_vm->_video->isVideoPlaying()) diff --git a/engines/mohawk/myst_stacks/mechanical.cpp b/engines/mohawk/myst_stacks/mechanical.cpp index b5d1285435..ffcaa226c6 100644 --- a/engines/mohawk/myst_stacks/mechanical.cpp +++ b/engines/mohawk/myst_stacks/mechanical.cpp @@ -316,12 +316,16 @@ void Mechanical::o_snakeBoxTrigger(uint16 op, uint16 var, uint16 argc, uint16 *a void Mechanical::o_fortressStaircaseMovie(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Play Stairs Movement Movie", op); - VideoHandle staircase = _vm->_video->playMovie(_vm->wrapMovieFilename("hhstairs", kMechanicalStack), 174, 222); + VideoHandle staircase = _vm->_video->playMovie(_vm->wrapMovieFilename("hhstairs", kMechanicalStack)); + if (!staircase) + error("Failed to open hhstairs movie"); + + staircase->moveTo(174, 222); if (_state.staircaseState) { - _vm->_video->setVideoBounds(staircase, Audio::Timestamp(0, 840, 600), Audio::Timestamp(0, 1680, 600)); + staircase->setBounds(Audio::Timestamp(0, 840, 600), Audio::Timestamp(0, 1680, 600)); } else { - _vm->_video->setVideoBounds(staircase, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 840, 600)); + staircase->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 840, 600)); } _vm->_video->waitUntilMovieEnds(staircase); @@ -571,8 +575,12 @@ void Mechanical::o_elevatorWindowMovie(uint16 op, uint16 var, uint16 argc, uint1 debugC(kDebugScript, "Opcode %d Movie Time Index %d to %d", op, startTime, endTime); - VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("ewindow", kMechanicalStack), 253, 0); - _vm->_video->setVideoBounds(window, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); + VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("ewindow", kMechanicalStack)); + if (!window) + error("Failed to open ewindow movie"); + + window->moveTo(253, 0); + window->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); _vm->_video->waitUntilMovieEnds(window); } @@ -644,8 +652,12 @@ void Mechanical::o_elevatorTopMovie(uint16 op, uint16 var, uint16 argc, uint16 * debugC(kDebugScript, "Opcode %d Movie Time Index %d to %d", op, startTime, endTime); - VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("hcelev", kMechanicalStack), 206, 38); - _vm->_video->setVideoBounds(window, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); + VideoHandle window = _vm->_video->playMovie(_vm->wrapMovieFilename("hcelev", kMechanicalStack)); + if (!window) + error("Failed to open hcelev movie"); + + window->moveTo(206, 38); + window->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); _vm->_video->waitUntilMovieEnds(window); } @@ -653,7 +665,7 @@ void Mechanical::o_fortressRotationSetPosition(uint16 op, uint16 var, uint16 arg debugC(kDebugScript, "Opcode %d: Set fortress position", op); VideoHandle gears = _fortressRotationGears->playMovie(); - uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(gears), 600).totalNumberOfFrames(); + uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); // Myst ME short movie workaround, explained in o_fortressRotation_init if (_fortressRotationShortMovieWorkaround) { @@ -788,9 +800,8 @@ void Mechanical::o_elevatorRotation_init(uint16 op, uint16 var, uint16 argc, uin void Mechanical::fortressRotation_run() { VideoHandle gears = _fortressRotationGears->playMovie(); - double oldRate = _vm->_video->getVideoRate(gears).toDouble(); - - uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(gears), 600).totalNumberOfFrames(); + double oldRate = gears->getRate().toDouble(); + uint32 moviePosition = Audio::Timestamp(gears->getTime(), 600).totalNumberOfFrames(); // Myst ME short movie workaround, explained in o_fortressRotation_init if (_fortressRotationShortMovieWorkaround) { @@ -837,19 +848,19 @@ void Mechanical::fortressRotation_run() { newRate = CLIP<double>(newRate, -2.5, 2.5); - _vm->_video->setVideoRate(gears, Common::Rational((int)(newRate * 1000.0), 1000)); + gears->setRate(Common::Rational((int)(newRate * 1000.0), 1000)); _gearsWereRunning = true; } else if (_gearsWereRunning) { // The fortress has stopped. Set its new position _fortressPosition = (moviePosition + 900) / 1800 % 4; - _vm->_video->setVideoRate(gears, 0); + gears->setRate(0); if (!_fortressRotationShortMovieWorkaround) { - _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * _fortressPosition, 600)); + gears->seek(Audio::Timestamp(0, 1800 * _fortressPosition, 600)); } else { - _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * (_fortressPosition % 2), 600)); + gears->seek(Audio::Timestamp(0, 1800 * (_fortressPosition % 2), 600)); } _vm->_sound->playSoundBlocking(_fortressRotationSounds[_fortressPosition]); @@ -864,9 +875,9 @@ void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uin _fortressRotationGears = static_cast<MystResourceType6 *>(_invokingResource); VideoHandle gears = _fortressRotationGears->playMovie(); - _vm->_video->setVideoLooping(gears, true); - _vm->_video->seekToTime(gears, Audio::Timestamp(0, 1800 * _fortressPosition, 600)); - _vm->_video->setVideoRate(gears, 0); + gears->setLooping(true); + gears->seek(Audio::Timestamp(0, 1800 * _fortressPosition, 600)); + gears->setRate(0); _fortressRotationSounds[0] = argv[0]; _fortressRotationSounds[1] = argv[1]; @@ -884,7 +895,7 @@ void Mechanical::o_fortressRotation_init(uint16 op, uint16 var, uint16 argc, uin // ScummVM simulates a longer movie by counting the number of times the movie // looped and adding that time to the current movie position. // Hence allowing the fortress position to be properly computed. - uint32 movieDuration = _vm->_video->getDuration(gears).convertToFramerate(600).totalNumberOfFrames(); + uint32 movieDuration = gears->getDuration().convertToFramerate(600).totalNumberOfFrames(); if (movieDuration == 3680) { _fortressRotationShortMovieWorkaround = true; _fortressRotationShortMovieCount = 0; @@ -924,8 +935,8 @@ void Mechanical::fortressSimulation_run() { _fortressSimulationStartup->pauseMovie(true); VideoHandle holo = _fortressSimulationHolo->playMovie(); - _vm->_video->setVideoLooping(holo, true); - _vm->_video->setVideoRate(holo, 0); + holo->setLooping(true); + holo->setRate(0); _vm->_cursor->showCursor(); @@ -933,9 +944,8 @@ void Mechanical::fortressSimulation_run() { } else { VideoHandle holo = _fortressSimulationHolo->playMovie(); - double oldRate = _vm->_video->getVideoRate(holo).toDouble(); - - uint32 moviePosition = Audio::Timestamp(_vm->_video->getTime(holo), 600).totalNumberOfFrames(); + double oldRate = holo->getRate().toDouble(); + uint32 moviePosition = Audio::Timestamp(holo->getTime(), 600).totalNumberOfFrames(); int32 positionInQuarter = 900 - (moviePosition + 900) % 1800; @@ -968,15 +978,15 @@ void Mechanical::fortressSimulation_run() { newRate = CLIP<double>(newRate, -2.5, 2.5); - _vm->_video->setVideoRate(holo, Common::Rational((int)(newRate * 1000.0), 1000)); + holo->setRate(Common::Rational((int)(newRate * 1000.0), 1000)); _gearsWereRunning = true; } else if (_gearsWereRunning) { // The fortress has stopped. Set its new position uint16 simulationPosition = (moviePosition + 900) / 1800 % 4; - _vm->_video->setVideoRate(holo, 0); - _vm->_video->seekToTime(holo, Audio::Timestamp(0, 1800 * simulationPosition, 600)); + holo->setRate(0); + holo->seek(Audio::Timestamp(0, 1800 * simulationPosition, 600)); _vm->_sound->playSoundBlocking( _fortressRotationSounds[simulationPosition]); _gearsWereRunning = false; diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index c500df5ad3..98f0aa5349 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -51,8 +51,6 @@ Myst::Myst(MohawkEngine_Myst *vm) : _dockVaultState = 0; _cabinDoorOpened = 0; _cabinMatchState = 2; - _cabinGaugeMovie = NULL_VID_HANDLE; - _cabinFireMovie = NULL_VID_HANDLE; _matchBurning = false; _tree = 0; _treeAlcove = 0; @@ -1135,10 +1133,13 @@ void Myst::o_clockWheelsExecute(uint16 op, uint16 var, uint16 argc, uint16 *argv _vm->_system->delayMillis(500); // Gears rise up - VideoHandle gears = _vm->_video->playMovie(_vm->wrapMovieFilename("gears", kMystStack), 305, 33); - _vm->_video->setVideoBounds(gears, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 650, 600)); - _vm->_video->waitUntilMovieEnds(gears); + VideoHandle gears = _vm->_video->playMovie(_vm->wrapMovieFilename("gears", kMystStack)); + if (!gears) + error("Failed to open gears movie"); + gears->moveTo(305, 33); + gears->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 650, 600)); + _vm->_video->waitUntilMovieEnds(gears); _state.clockTowerBridgeOpen = 1; _vm->redrawArea(12); @@ -1147,8 +1148,12 @@ void Myst::o_clockWheelsExecute(uint16 op, uint16 var, uint16 argc, uint16 *argv _vm->_system->delayMillis(500); // Gears sink down - VideoHandle gears = _vm->_video->playMovie(_vm->wrapMovieFilename("gears", kMystStack), 305, 33); - _vm->_video->setVideoBounds(gears, Audio::Timestamp(0, 700, 600), Audio::Timestamp(0, 1300, 600)); + VideoHandle gears = _vm->_video->playMovie(_vm->wrapMovieFilename("gears", kMystStack)); + if (!gears) + error("Failed to open gears movie"); + + gears->moveTo(305, 33); + gears->setBounds(Audio::Timestamp(0, 700, 600), Audio::Timestamp(0, 1300, 600)); _vm->_video->waitUntilMovieEnds(gears); _state.clockTowerBridgeOpen = 0; @@ -1191,15 +1196,23 @@ void Myst::o_imagerPlayButton(uint16 op, uint16 var, uint16 argc, uint16 *argv) if (_state.imagerActive) { // Mountains disappearing Common::String file = _vm->wrapMovieFilename("vltmntn", kMystStack); - VideoHandle mountain = _vm->_video->playMovie(file, 159, 96, false); - _vm->_video->setVideoBounds(mountain, Audio::Timestamp(0, 11180, 600), Audio::Timestamp(0, 16800, 600)); + VideoHandle mountain = _vm->_video->playMovie(file); + if (!mountain) + error("Failed to open '%s'", file.c_str()); + + mountain->moveTo(159, 96); + mountain->setBounds(Audio::Timestamp(0, 11180, 600), Audio::Timestamp(0, 16800, 600)); _state.imagerActive = 0; } else { // Mountains appearing Common::String file = _vm->wrapMovieFilename("vltmntn", kMystStack); - VideoHandle mountain = _vm->_video->playMovie(file, 159, 96, false); - _vm->_video->setVideoBounds(mountain, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 11180, 600)); + VideoHandle mountain = _vm->_video->playMovie(file); + if (!mountain) + error("Failed to open '%s'", file.c_str()); + + mountain->moveTo(159, 96); + mountain->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 11180, 600)); _state.imagerActive = 1; } @@ -1212,20 +1225,20 @@ void Myst::o_imagerPlayButton(uint16 op, uint16 var, uint16 argc, uint16 *argv) // Water disappearing VideoHandle water = _imagerMovie->playMovie(); - _vm->_video->setVideoBounds(water, Audio::Timestamp(0, 4204, 600), Audio::Timestamp(0, 6040, 600)); - _vm->_video->setVideoLooping(water, false); + water->setBounds(Audio::Timestamp(0, 4204, 600), Audio::Timestamp(0, 6040, 600)); + water->setLooping(false); _state.imagerActive = 0; } else { // Water appearing VideoHandle water = _imagerMovie->playMovie(); - _vm->_video->setVideoBounds(water, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 1814, 600)); + water->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 1814, 600)); _vm->_video->waitUntilMovieEnds(water); // Water looping water = _imagerMovie->playMovie(); - _vm->_video->setVideoBounds(water, Audio::Timestamp(0, 1814, 600), Audio::Timestamp(0, 4204, 600)); - _vm->_video->setVideoLooping(water, true); + water->setBounds(Audio::Timestamp(0, 1814, 600), Audio::Timestamp(0, 4204, 600)); + water->setLooping(true); _state.imagerActive = 1; } @@ -1902,11 +1915,19 @@ Common::Rational Myst::boilerComputeGaugeRate(uint16 pressure, uint32 delay) { } void Myst::boilerResetGauge(const Common::Rational &rate) { - if (_vm->_video->endOfVideo(_cabinGaugeMovie)) { + if (!_cabinGaugeMovie || _cabinGaugeMovie->endOfVideo()) { if (_vm->getCurCard() == 4098) { - _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack), 243, 96); + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack)); + if (!_cabinGaugeMovie) + error("Failed to open cabingau movie"); + + _cabinGaugeMovie->moveTo(243, 96); } else { - _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack), 254, 136); + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack)); + if (!_cabinGaugeMovie) + error("Failed to open cabingau movie"); + + _cabinGaugeMovie->moveTo(254, 136); } } @@ -1914,10 +1935,10 @@ void Myst::boilerResetGauge(const Common::Rational &rate) { if (rate > 0) goTo = Audio::Timestamp(0, 0, 600); else - goTo = _vm->_video->getDuration(_cabinGaugeMovie); + goTo = _cabinGaugeMovie->getDuration(); - _vm->_video->seekToTime(_cabinGaugeMovie, goTo); - _vm->_video->setVideoRate(_cabinGaugeMovie, rate); + _cabinGaugeMovie->seek(goTo); + _cabinGaugeMovie->setRate(rate); } void Myst::o_boilerIncreasePressureStop(uint16 op, uint16 var, uint16 argc, uint16 *argv) { @@ -1931,10 +1952,10 @@ void Myst::o_boilerIncreasePressureStop(uint16 op, uint16 var, uint16 argc, uint if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(8098, 49152); - if (!_vm->_video->endOfVideo(_cabinGaugeMovie)) { + if (_cabinGaugeMovie && !_cabinGaugeMovie->endOfVideo()) { uint16 delay = treeNextMoveDelay(_state.cabinValvePosition); Common::Rational rate = boilerComputeGaugeRate(_state.cabinValvePosition, delay); - _vm->_video->setVideoRate(_cabinGaugeMovie, rate); + _cabinGaugeMovie->setRate(rate); } } else if (_state.cabinValvePosition > 0) @@ -2006,10 +2027,10 @@ void Myst::o_boilerDecreasePressureStop(uint16 op, uint16 var, uint16 argc, uint if (_state.cabinValvePosition > 0) _vm->_sound->replaceBackgroundMyst(8098, 49152); - if (!_vm->_video->endOfVideo(_cabinGaugeMovie)) { + if (_cabinGaugeMovie && !_cabinGaugeMovie->endOfVideo()) { uint16 delay = treeNextMoveDelay(_state.cabinValvePosition); Common::Rational rate = boilerComputeGaugeRate(_state.cabinValvePosition, delay); - _vm->_video->setVideoRate(_cabinGaugeMovie, rate); + _cabinGaugeMovie->setRate(rate); } } else { @@ -2117,7 +2138,7 @@ void Myst::tree_run() { // Check if alcove is accessible treeSetAlcoveAccessible(); - if (_cabinGaugeMovie != NULL_VID_HANDLE) { + if (_cabinGaugeMovie) { Common::Rational rate = boilerComputeGaugeRate(pressure, delay); boilerResetGauge(rate); } @@ -2246,13 +2267,22 @@ void Myst::rocketCheckSolution() { // Book appearing Common::String movieFile = _vm->wrapMovieFilename("selenbok", kMystStack); - _rocketLinkBook = _vm->_video->playMovie(movieFile, 224, 41); - _vm->_video->setVideoBounds(_rocketLinkBook, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 660, 600)); + _rocketLinkBook = _vm->_video->playMovie(movieFile); + if (!_rocketLinkBook) + error("Failed to open '%s'", movieFile.c_str()); + + _rocketLinkBook->moveTo(224, 41); + _rocketLinkBook->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 660, 600)); _vm->_video->waitUntilMovieEnds(_rocketLinkBook); // Book looping closed - _rocketLinkBook = _vm->_video->playMovie(movieFile, 224, 41, true); - _vm->_video->setVideoBounds(_rocketLinkBook, Audio::Timestamp(0, 660, 600), Audio::Timestamp(0, 3500, 600)); + _rocketLinkBook = _vm->_video->playMovie(movieFile); + if (!_rocketLinkBook) + error("Failed to open '%s'", movieFile.c_str()); + + _rocketLinkBook->moveTo(224, 41); + _rocketLinkBook->setLooping(true); + _rocketLinkBook->setBounds(Audio::Timestamp(0, 660, 600), Audio::Timestamp(0, 3500, 600)); _tempVar = 1; } @@ -2367,7 +2397,7 @@ void Myst::o_rocketOpenBook(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Rocket open link book", op); // Flyby movie - _vm->_video->setVideoBounds(_rocketLinkBook, Audio::Timestamp(0, 3500, 600), Audio::Timestamp(0, 13100, 600)); + _rocketLinkBook->setBounds(Audio::Timestamp(0, 3500, 600), Audio::Timestamp(0, 13100, 600)); // Set linkable _tempVar = 2; @@ -2889,8 +2919,12 @@ void Myst::clockGearForwardOneStep(uint16 gear) { // Set video bounds uint16 gearPosition = _clockGearsPositions[gear] - 1; - _clockGearsVideos[gear] = _vm->_video->playMovie(_vm->wrapMovieFilename(videos[gear], kMystStack), x[gear], y[gear]); - _vm->_video->setVideoBounds(_clockGearsVideos[gear], + _clockGearsVideos[gear] = _vm->_video->playMovie(_vm->wrapMovieFilename(videos[gear], kMystStack)); + if (!_clockGearsVideos[gear]) + error("Failed to open %s movie", videos[gear]); + + _clockGearsVideos[gear]->moveTo(x[gear], y[gear]); + _clockGearsVideos[gear]->setBounds( Audio::Timestamp(0, startTime[gearPosition], 600), Audio::Timestamp(0, endTime[gearPosition], 600)); } @@ -2902,8 +2936,12 @@ void Myst::clockWeightDownOneStep() { // Set video bounds if (updateVideo) { - _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); - _vm->_video->setVideoBounds(_clockWeightVideo, + _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack)); + if (!_clockWeightVideo) + error("Failed to open cl1wlfch movie"); + + _clockWeightVideo->moveTo(124, 0); + _clockWeightVideo->setBounds( Audio::Timestamp(0, _clockWeightPosition, 600), Audio::Timestamp(0, _clockWeightPosition + 246, 600)); } @@ -2931,7 +2969,7 @@ void Myst::o_clockLeverEndMove(uint16 op, uint16 var, uint16 argc, uint16 *argv) // Let movies stop playing for (uint i = 0; i < ARRAYSIZE(videos); i++) { VideoHandle handle = _vm->_video->findVideoHandle(_vm->wrapMovieFilename(videos[i], kMystStack)); - if (handle != NULL_VID_HANDLE) + if (handle) _vm->_video->delayUntilMovieEnds(handle); } @@ -2956,8 +2994,12 @@ void Myst::clockGearsCheckSolution() { // Make weight go down _vm->_sound->replaceSoundMyst(9113); - _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); - _vm->_video->setVideoBounds(_clockWeightVideo, + _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack)); + if (!_clockWeightVideo) + error("Failed to open cl1wlfch movie"); + + _clockWeightVideo->moveTo(124, 0); + _clockWeightVideo->setBounds( Audio::Timestamp(0, _clockWeightPosition, 600), Audio::Timestamp(0, 2214, 600)); _vm->_video->waitUntilMovieEnds(_clockWeightVideo); @@ -2968,7 +3010,7 @@ void Myst::clockGearsCheckSolution() { _vm->_sound->replaceSoundMyst(7113); // Gear opening video - _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cl1wggat", kMystStack) , 195, 225); + _vm->_video->playMovieBlocking(_vm->wrapMovieFilename("cl1wggat", kMystStack), 195, 225); _state.gearsOpen = 1; _vm->redrawArea(40); @@ -3011,7 +3053,7 @@ void Myst::clockReset() { // Let movies stop playing for (uint i = 0; i < ARRAYSIZE(videos); i++) { VideoHandle handle = _vm->_video->findVideoHandle(_vm->wrapMovieFilename(videos[i], kMystStack)); - if (handle != NULL_VID_HANDLE) + if (handle) _vm->_video->delayUntilMovieEnds(handle); } @@ -3024,9 +3066,13 @@ void Myst::clockReset() { _vm->_sound->replaceSoundMyst(7113); // Gear closing movie - VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wggat", kMystStack) , 195, 225); - _vm->_video->seekToTime(handle, _vm->_video->getDuration(handle)); - _vm->_video->setVideoRate(handle, -1); + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wggat", kMystStack)); + if (!handle) + error("Failed to open cl1wggat movie"); + + handle->moveTo(195, 225); + handle->seek(handle->getDuration()); + handle->setRate(-1); _vm->_video->waitUntilMovieEnds(handle); // Redraw gear @@ -3038,11 +3084,15 @@ void Myst::clockReset() { } void Myst::clockResetWeight() { - _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack) , 124, 0); + _clockWeightVideo = _vm->_video->playMovie(_vm->wrapMovieFilename("cl1wlfch", kMystStack)); + if (!_clockWeightVideo) + error("Failed to open cl1wlfch movie"); + + _clockWeightVideo->moveTo(124, 0); // Play the movie backwards, weight going up - _vm->_video->seekToTime(_clockWeightVideo, Audio::Timestamp(0, _clockWeightPosition, 600)); - _vm->_video->setVideoRate(_clockWeightVideo, -1); + _clockWeightVideo->seek(Audio::Timestamp(0, _clockWeightPosition, 600)); + _clockWeightVideo->setRate(-1); // Reset position _clockWeightPosition = 0; @@ -3057,8 +3107,12 @@ void Myst::clockResetGear(uint16 gear) { // Set video bounds, gears going to 3 uint16 gearPosition = _clockGearsPositions[gear] - 1; if (gearPosition != 2) { - _clockGearsVideos[gear] = _vm->_video->playMovie(_vm->wrapMovieFilename(videos[gear], kMystStack), x[gear], y[gear]); - _vm->_video->setVideoBounds(_clockGearsVideos[gear], + _clockGearsVideos[gear] = _vm->_video->playMovie(_vm->wrapMovieFilename(videos[gear], kMystStack)); + if (!_clockGearsVideos[gear]) + error("Failed to open gears movie"); + + _clockGearsVideos[gear]->moveTo(x[gear], y[gear]); + _clockGearsVideos[gear]->setBounds( Audio::Timestamp(0, time[gearPosition], 600), Audio::Timestamp(0, time[2], 600)); } @@ -3201,13 +3255,21 @@ Common::Point Myst::towerRotationMapComputeCoords(const Common::Point ¢er, u } void Myst::towerRotationMapDrawLine(const Common::Point ¢er, const Common::Point &end) { - Graphics::PixelFormat pf = _vm->_system->getScreenFormat(); - uint32 color = 0; + uint32 color; - if (!_towerRotationOverSpot) - color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White - else - color = pf.RGBToColor(0xFF, 0, 0); // Red + if (_vm->getFeatures() & GF_ME) { + Graphics::PixelFormat pf = _vm->_system->getScreenFormat(); + + if (!_towerRotationOverSpot) + color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White + else + color = pf.RGBToColor(0xFF, 0, 0); // Red + } else { + if (!_towerRotationOverSpot) + color = 0x00; // White + else + color = 0xF9; // Red + } const Common::Rect rect = Common::Rect(106, 42, 459, 273); @@ -3281,8 +3343,8 @@ void Myst::imager_run() { if (_state.imagerActive && _state.imagerSelection == 67) { VideoHandle water = _imagerMovie->playMovie(); - _vm->_video->setVideoBounds(water, Audio::Timestamp(0, 1814, 600), Audio::Timestamp(0, 4204, 600)); - _vm->_video->setVideoLooping(water, true); + water->setBounds(Audio::Timestamp(0, 1814, 600), Audio::Timestamp(0, 4204, 600)); + water->setLooping(true); } } @@ -3394,8 +3456,11 @@ void Myst::gullsFly1_run() { else x = _vm->_rnd->getRandomNumber(160) + 260; - _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack), x, 0); + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack)); + if (!handle) + error("Failed to open gulls movie"); + handle->moveTo(x, 0); _gullsNextTime = time + _vm->_rnd->getRandomNumber(16667) + 13334; } } @@ -3540,8 +3605,11 @@ void Myst::gullsFly2_run() { if (time > _gullsNextTime) { uint16 video = _vm->_rnd->getRandomNumber(3); if (video != 3) { - _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack), 424, 0); - + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack)); + if (!handle) + error("Failed to open gulls movie"); + + handle->moveTo(424, 0); _gullsNextTime = time + _vm->_rnd->getRandomNumber(16667) + 13334; } } @@ -3572,31 +3640,41 @@ void Myst::o_boilerMovies_init(uint16 op, uint16 var, uint16 argc, uint16 *argv) void Myst::boilerFireInit() { if (_vm->getCurCard() == 4098) { - _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfire", kMystStack), 240, 279, true); - _vm->_video->pauseMovie(_cabinFireMovie, true); + _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfire", kMystStack)); + if (!_cabinFireMovie) + error("Failed to open cabfire movie"); + + _cabinFireMovie->moveTo(240, 279); + _cabinFireMovie->setLooping(true); + _cabinFireMovie->pause(true); _vm->redrawArea(305); boilerFireUpdate(true); } else { if (_state.cabinPilotLightLit == 1 && _state.cabinValvePosition >= 1) { - _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfirfr", kMystStack), 254, 244, true); + _cabinFireMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabfirfr", kMystStack)); + if (!_cabinFireMovie) + error("Failed to open cabfirfr movie"); + + _cabinFireMovie->moveTo(254, 244); + _cabinFireMovie->setLooping(true); } } } void Myst::boilerFireUpdate(bool init) { - uint position = _vm->_video->getTime(_cabinFireMovie); + uint position = _cabinFireMovie->getTime(); if (_state.cabinPilotLightLit == 1) { if (_state.cabinValvePosition == 0) { if (position > (uint)Audio::Timestamp(0, 200, 600).msecs() || init) { - _vm->_video->setVideoBounds(_cabinFireMovie, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 100, 600)); - _vm->_video->pauseMovie(_cabinFireMovie, false); + _cabinFireMovie->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 100, 600)); + _cabinFireMovie->pause(false); } } else { if (position < (uint)Audio::Timestamp(0, 200, 600).msecs() || init) { - _vm->_video->setVideoBounds(_cabinFireMovie, Audio::Timestamp(0, 201, 600), Audio::Timestamp(0, 1900, 600)); - _vm->_video->pauseMovie(_cabinFireMovie, false); + _cabinFireMovie->setBounds(Audio::Timestamp(0, 201, 600), Audio::Timestamp(0, 1900, 600)); + _cabinFireMovie->pause(false); } } } @@ -3604,15 +3682,23 @@ void Myst::boilerFireUpdate(bool init) { void Myst::boilerGaugeInit() { if (_vm->getCurCard() == 4098) { - _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack), 243, 96); + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabingau", kMystStack)); + if (!_cabinFireMovie) + error("Failed to open cabingau movie"); + + _cabinFireMovie->moveTo(243, 96); } else { - _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack), 254, 136); + _cabinGaugeMovie = _vm->_video->playMovie(_vm->wrapMovieFilename("cabcgfar", kMystStack)); + if (!_cabinFireMovie) + error("Failed to open cabcgfar movie"); + + _cabinFireMovie->moveTo(254, 136); } Audio::Timestamp frame; if (_state.cabinPilotLightLit == 1 && _state.cabinValvePosition > 12) - frame = _vm->_video->getDuration(_cabinGaugeMovie); + frame = _cabinGaugeMovie->getDuration(); else frame = Audio::Timestamp(0, 0, 600); @@ -3672,18 +3758,27 @@ void Myst::greenBook_run() { _vm->_sound->stopSound(); _vm->_sound->pauseBackgroundMyst(); + VideoHandle book = _vm->_video->playMovie(file); + if (!book) + error("Failed to open '%s'", file.c_str()); + + book->moveTo(314, 76); + if (_globals.ending != 4) { _tempVar = 2; - _vm->_video->playMovie(file, 314, 76); } else { - VideoHandle book = _vm->_video->playMovie(file, 314, 76, true); - _vm->_video->setVideoBounds(book, Audio::Timestamp(0, loopStart, 600), Audio::Timestamp(0, loopEnd, 600)); + book->setBounds(Audio::Timestamp(0, loopStart, 600), Audio::Timestamp(0, loopEnd, 600)); + book->setLooping(true); _tempVar = 0; } } else if (_tempVar == 2 && !_vm->_video->isVideoPlaying()) { - VideoHandle book = _vm->_video->playMovie(file, 314, 76); - _vm->_video->setVideoBounds(book, Audio::Timestamp(0, loopStart, 600), Audio::Timestamp(0, loopEnd, 600)); - _vm->_video->setVideoLooping(book, true); + VideoHandle book = _vm->_video->playMovie(file); + if (!book) + error("Failed to open '%s'", file.c_str()); + + book->moveTo(314, 76); + book->setBounds(Audio::Timestamp(0, loopStart, 600), Audio::Timestamp(0, loopEnd, 600)); + book->setLooping(true); _tempVar = 0; } } @@ -3706,8 +3801,11 @@ void Myst::gullsFly3_run() { if (video != 3) { uint16 x = _vm->_rnd->getRandomNumber(280) + 135; - _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack), x, 0); + VideoHandle handle = _vm->_video->playMovie(_vm->wrapMovieFilename(gulls[video], kMystStack)); + if (!handle) + error("Failed to open gulls movie"); + handle->moveTo(x, 0); _gullsNextTime = time + _vm->_rnd->getRandomNumber(16667) + 13334; } } @@ -3742,8 +3840,8 @@ void Myst::o_treeEntry_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { void Myst::o_boiler_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { debugC(kDebugScript, "Opcode %d: Exit boiler card", op); - _cabinGaugeMovie = NULL_VID_HANDLE; - _cabinFireMovie = NULL_VID_HANDLE; + _cabinGaugeMovie = VideoHandle(); + _cabinFireMovie = VideoHandle(); } void Myst::o_generatorControlRoom_exit(uint16 op, uint16 var, uint16 argc, uint16 *argv) { diff --git a/engines/mohawk/myst_stacks/stoneship.cpp b/engines/mohawk/myst_stacks/stoneship.cpp index d8dbeef641..1113ceeac9 100644 --- a/engines/mohawk/myst_stacks/stoneship.cpp +++ b/engines/mohawk/myst_stacks/stoneship.cpp @@ -425,8 +425,12 @@ void Stoneship::o_cabinBookMovie(uint16 op, uint16 var, uint16 argc, uint16 *arg uint16 startTime = argv[0]; uint16 endTime = argv[1]; - VideoHandle book = _vm->_video->playMovie(_vm->wrapMovieFilename("bkroom", kStoneshipStack), 159, 99); - _vm->_video->setVideoBounds(book, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); + VideoHandle book = _vm->_video->playMovie(_vm->wrapMovieFilename("bkroom", kStoneshipStack)); + if (!book) + error("Failed to open bkroom movie"); + + book->moveTo(159, 99); + book->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, endTime, 600)); _vm->_video->waitUntilMovieEnds(book); } @@ -597,9 +601,9 @@ void Stoneship::o_hologramPlayback(uint16 op, uint16 var, uint16 argc, uint16 *a if (_hologramTurnedOn) { if (_hologramDisplayPos) endPoint = _hologramDisplayPos; - _vm->_video->setVideoBounds(displayMovie, Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600)); + displayMovie->setBounds(Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600)); } else { - _vm->_video->setVideoBounds(displayMovie, Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600)); + displayMovie->setBounds(Audio::Timestamp(0, startPoint, 600), Audio::Timestamp(0, endPoint, 600)); } _vm->_video->delayUntilMovieEnds(displayMovie); @@ -673,29 +677,45 @@ void Stoneship::o_chestValveVideos(uint16 op, uint16 var, uint16 argc, uint16 *a if (_state.chestValveState) { // Valve closing - VideoHandle valve = _vm->_video->playMovie(movie, 97, 267); - _vm->_video->setVideoBounds(valve, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 350, 600)); + VideoHandle valve = _vm->_video->playMovie(movie); + if (!valve) + error("Failed to open '%s'", movie.c_str()); + + valve->moveTo(97, 267); + valve->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 350, 600)); _vm->_video->waitUntilMovieEnds(valve); } else if (_state.chestWaterState) { // Valve opening, spilling water - VideoHandle valve = _vm->_video->playMovie(movie, 97, 267); - _vm->_video->setVideoBounds(valve, Audio::Timestamp(0, 350, 600), Audio::Timestamp(0, 650, 600)); + VideoHandle valve = _vm->_video->playMovie(movie); + if (!valve) + error("Failed to open '%s'", movie.c_str()); + + valve->moveTo(97, 267); + valve->setBounds(Audio::Timestamp(0, 350, 600), Audio::Timestamp(0, 650, 600)); _vm->_video->waitUntilMovieEnds(valve); _vm->_sound->playSound(3132); for (uint i = 0; i < 25; i++) { - valve = _vm->_video->playMovie(movie, 97, 267); - _vm->_video->setVideoBounds(valve, Audio::Timestamp(0, 650, 600), Audio::Timestamp(0, 750, 600)); + valve = _vm->_video->playMovie(movie); + if (!valve) + error("Failed to open '%s'", movie.c_str()); + + valve->moveTo(97, 267); + valve->setBounds(Audio::Timestamp(0, 650, 600), Audio::Timestamp(0, 750, 600)); _vm->_video->waitUntilMovieEnds(valve); } _vm->_sound->resumeBackgroundMyst(); } else { // Valve opening - VideoHandle valve = _vm->_video->playMovie(movie, 97, 267); - _vm->_video->seekToTime(valve, Audio::Timestamp(0, 350, 600)); - _vm->_video->setVideoRate(valve, -1); + VideoHandle valve = _vm->_video->playMovie(movie); + if (!valve) + error("Failed to open '%s'", movie.c_str()); + + valve->moveTo(97, 267); + valve->seek(Audio::Timestamp(0, 350, 600)); + valve->setRate(-1); _vm->_video->waitUntilMovieEnds(valve); } } @@ -716,14 +736,22 @@ void Stoneship::o_trapLockOpen(uint16 op, uint16 var, uint16 argc, uint16 *argv) Common::String movie = _vm->wrapMovieFilename("openloc", kStoneshipStack); - VideoHandle lock = _vm->_video->playMovie(movie, 187, 71); - _vm->_video->setVideoBounds(lock, Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 750, 600)); + VideoHandle lock = _vm->_video->playMovie(movie); + if (!lock) + error("Failed to open '%s'", movie.c_str()); + + lock->moveTo(187, 71); + lock->setBounds(Audio::Timestamp(0, 0, 600), Audio::Timestamp(0, 750, 600)); _vm->_video->waitUntilMovieEnds(lock); _vm->_sound->playSound(2143); - lock = _vm->_video->playMovie(movie, 187, 71); - _vm->_video->setVideoBounds(lock, Audio::Timestamp(0, 750, 600), Audio::Timestamp(0, 10000, 600)); + lock = _vm->_video->playMovie(movie); + if (!lock) + error("Failed to open '%s'", movie.c_str()); + + lock->moveTo(187, 71); + lock->setBounds(Audio::Timestamp(0, 750, 600), Audio::Timestamp(0, 10000, 600)); _vm->_video->waitUntilMovieEnds(lock); if (_state.pumpState != 4) diff --git a/engines/mohawk/riven.cpp b/engines/mohawk/riven.cpp index a7fe12b9e1..898f68c581 100644 --- a/engines/mohawk/riven.cpp +++ b/engines/mohawk/riven.cpp @@ -828,7 +828,7 @@ static void sunnersTopStairsTimer(MohawkEngine_Riven *vm) { VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1); uint32 timerTime = 500; - if (oldHandle == NULL_VID_HANDLE || vm->_video->endOfVideo(oldHandle)) { + if (!oldHandle || oldHandle->endOfVideo()) { uint32 &sunnerTime = vm->_vars["jsunnertime"]; if (sunnerTime == 0) { @@ -836,7 +836,7 @@ static void sunnersTopStairsTimer(MohawkEngine_Riven *vm) { } else if (sunnerTime < vm->getTotalPlayTime()) { VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(1, 3)); - timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000; + timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(2, 15) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -858,7 +858,7 @@ static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) { VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1); uint32 timerTime = 500; - if (oldHandle == NULL_VID_HANDLE || vm->_video->endOfVideo(oldHandle)) { + if (!oldHandle || oldHandle->endOfVideo()) { uint32 &sunnerTime = vm->_vars["jsunnertime"]; if (sunnerTime == 0) { @@ -874,7 +874,7 @@ static void sunnersMidStairsTimer(MohawkEngine_Riven *vm) { VideoHandle handle = vm->_video->playMovieRiven(movie); - timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000; + timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 10) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -896,7 +896,7 @@ static void sunnersLowerStairsTimer(MohawkEngine_Riven *vm) { VideoHandle oldHandle = vm->_video->findVideoHandleRiven(1); uint32 timerTime = 500; - if (oldHandle == NULL_VID_HANDLE || vm->_video->endOfVideo(oldHandle)) { + if (!oldHandle || oldHandle->endOfVideo()) { uint32 &sunnerTime = vm->_vars["jsunnertime"]; if (sunnerTime == 0) { @@ -904,7 +904,7 @@ static void sunnersLowerStairsTimer(MohawkEngine_Riven *vm) { } else if (sunnerTime < vm->getTotalPlayTime()) { VideoHandle handle = vm->_video->playMovieRiven(vm->_rnd->getRandomNumberRng(3, 5)); - timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; + timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -926,7 +926,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) { VideoHandle oldHandle = vm->_video->findVideoHandleRiven(3); uint32 timerTime = 500; - if (oldHandle == NULL_VID_HANDLE || vm->_video->endOfVideo(oldHandle)) { + if (!oldHandle || oldHandle->endOfVideo()) { uint32 &sunnerTime = vm->_vars["jsunnertime"]; if (sunnerTime == 0) { @@ -938,7 +938,7 @@ static void sunnersBeachTimer(MohawkEngine_Riven *vm) { vm->_video->activateMLST(mlstID, vm->getCurCard()); VideoHandle handle = vm->_video->playMovieRiven(mlstID); - timerTime = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; + timerTime = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(1, 30) * 1000; } sunnerTime = timerTime + vm->getTotalPlayTime(); @@ -969,7 +969,7 @@ void MohawkEngine_Riven::installCardTimer() { } void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) { - assert(handle != NULL_VID_HANDLE); + assert(handle); uint16 id = _scriptMan->getStoredMovieOpcodeID(); @@ -977,7 +977,7 @@ void MohawkEngine_Riven::doVideoTimer(VideoHandle handle, bool force) { return; // Run the opcode if we can at this point - if (force || _video->getTime(handle) >= _scriptMan->getStoredMovieOpcodeTime()) + if (force || handle->getTime() >= _scriptMan->getStoredMovieOpcodeTime()) _scriptMan->runStoredMovieOpcode(); } @@ -1003,7 +1003,7 @@ void MohawkEngine_Riven::checkSunnerAlertClick() { // If the alert video is no longer playing, we have nothing left to do VideoHandle handle = _video->findVideoHandleRiven(1); - if (handle == NULL_VID_HANDLE || _video->endOfVideo(handle)) + if (!handle || handle->endOfVideo()) return; sunners = 1; diff --git a/engines/mohawk/riven_external.cpp b/engines/mohawk/riven_external.cpp index cda0683028..7ca41e253c 100644 --- a/engines/mohawk/riven_external.cpp +++ b/engines/mohawk/riven_external.cpp @@ -229,7 +229,7 @@ void RivenExternal::runCredits(uint16 video, uint32 delay) { VideoHandle videoHandle = _vm->_video->findVideoHandleRiven(video); while (!_vm->shouldQuit() && _vm->_gfx->getCurCreditsImage() <= 320) { - if (_vm->_video->getCurFrame(videoHandle) >= (int32)_vm->_video->getFrameCount(videoHandle) - 1) { + if (videoHandle->getCurFrame() >= (int32)videoHandle->getFrameCount() - 1) { if (nextCreditsFrameStart == 0) { // Set us up to start after delay ms nextCreditsFrameStart = _vm->_system->getMillis() + delay; @@ -265,10 +265,10 @@ void RivenExternal::runDomeCheck() { // Check if we clicked while the golden frame was showing VideoHandle video = _vm->_video->findVideoHandleRiven(1); - assert(video != NULL_VID_HANDLE); + assert(video); - int32 curFrame = _vm->_video->getCurFrame(video); - int32 frameCount = _vm->_video->getFrameCount(video); + int32 curFrame = video->getCurFrame(); + int32 frameCount = video->getFrameCount(); // The final frame of the video is the 'golden' frame (double meaning: the // frame that is the magic one is the one with the golden symbol) but we @@ -857,8 +857,12 @@ void RivenExternal::xbupdateboiler(uint16 argc, uint16 *argv) { _vm->_video->playMovieRiven(7); } } else { - _vm->_video->disableMovieRiven(7); - _vm->_video->disableMovieRiven(8); + VideoHandle handle = _vm->_video->findVideoHandleRiven(7); + if (handle) + handle->setEnabled(false); + handle = _vm->_video->findVideoHandleRiven(8); + if (handle) + handle->setEnabled(false); } } @@ -1149,8 +1153,8 @@ void RivenExternal::lowerPins() { // Play the video of the pins going down VideoHandle handle = _vm->_video->playMovieRiven(upMovie); - assert(handle != NULL_VID_HANDLE); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600)); + assert(handle); + handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600)); _vm->_video->waitUntilMovieEnds(handle); upMovie = 0; @@ -1181,8 +1185,8 @@ void RivenExternal::xgrotatepins(uint16 argc, uint16 *argv) { // Play the video of the pins rotating VideoHandle handle = _vm->_video->playMovieRiven(_vm->_vars["gupmoov"]); - assert(handle != NULL_VID_HANDLE); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600)); + assert(handle); + handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 1215, 600)); _vm->_video->waitUntilMovieEnds(handle); } @@ -1265,9 +1269,9 @@ void RivenExternal::xgpincontrols(uint16 argc, uint16 *argv) { // Actually play the movie VideoHandle handle = _vm->_video->playMovieRiven(pinMovieCodes[imagePos - 1]); - assert(handle != NULL_VID_HANDLE); + assert(handle); uint32 startTime = 9630 - pinPos * 600; - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600)); + handle->setBounds(Audio::Timestamp(0, startTime, 600), Audio::Timestamp(0, startTime + 550, 600)); _vm->_video->waitUntilMovieEnds(handle); // Update the relevant variables @@ -1343,8 +1347,8 @@ void RivenExternal::xgrviewer(uint16 argc, uint16 *argv) { // Now play the movie VideoHandle handle = _vm->_video->playMovieRiven(1); - assert(handle != NULL_VID_HANDLE); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600)); + assert(handle); + handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600)); _vm->_video->waitUntilMovieEnds(handle); // Set the new position and let the card's scripts take over again @@ -1412,8 +1416,8 @@ void RivenExternal::xglviewer(uint16 argc, uint16 *argv) { // Now play the movie VideoHandle handle = _vm->_video->playMovieRiven(1); - assert(handle != NULL_VID_HANDLE); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600)); + assert(handle); + handle->setBounds(Audio::Timestamp(0, s_viewerTimeIntervals[curPos], 600), Audio::Timestamp(0, s_viewerTimeIntervals[newPos], 600)); _vm->_video->waitUntilMovieEnds(handle); // Set the new position to the variable @@ -1467,7 +1471,7 @@ static void catherineViewerIdleTimer(MohawkEngine_Riven *vm) { VideoHandle videoHandle = vm->_video->playMovieRiven(30); // Reset the timer - vm->installTimer(&catherineViewerIdleTimer, vm->_video->getDuration(videoHandle).msecs() + vm->_rnd->getRandomNumber(60) * 1000); + vm->installTimer(&catherineViewerIdleTimer, videoHandle->getDuration().msecs() + vm->_rnd->getRandomNumber(60) * 1000); } void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) { @@ -1507,7 +1511,7 @@ void RivenExternal::xglview_prisonon(uint16 argc, uint16 *argv) { _vm->_video->activateMLST(cathMovie, _vm->getCurCard()); VideoHandle videoHandle = _vm->_video->playMovieRiven(30); - timeUntilNextMovie = _vm->_video->getDuration(videoHandle).msecs() + _vm->_rnd->getRandomNumber(60) * 1000; + timeUntilNextMovie = videoHandle->getDuration().msecs() + _vm->_rnd->getRandomNumber(60) * 1000; } else { // Otherwise, just redraw the imager timeUntilNextMovie = _vm->_rnd->getRandomNumberRng(10, 20) * 1000; @@ -1986,7 +1990,7 @@ void RivenExternal::xschool280_playwhark(uint16 argc, uint16 *argv) { Audio::Timestamp startTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600); *posVar += number; // Adjust to the end Audio::Timestamp endTime = Audio::Timestamp(0, (11560 / 19) * (*posVar), 600); - _vm->_video->setVideoBounds(handle, startTime, endTime); + handle->setBounds(startTime, endTime); _vm->_video->waitUntilMovieEnds(handle); if (*posVar > 19) { @@ -2059,7 +2063,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { debug(0, "\tHotspot = %d -> %d", argv[3], hotspotMap[argv[3] - 1]); // Just let the video play while we wait until Gehn opens the trap book for us - while (_vm->_video->getTime(video) < startTime && !_vm->shouldQuit()) { + while (video->getTime() < startTime && !_vm->shouldQuit()) { if (_vm->_video->updateMovies()) _vm->_system->updateScreen(); @@ -2084,7 +2088,7 @@ void RivenExternal::xbookclick(uint16 argc, uint16 *argv) { // OK, Gehn has opened the trap book and has asked us to go in. Let's watch // and see what the player will do... - while (_vm->_video->getTime(video) < endTime && !_vm->shouldQuit()) { + while (video->getTime() < endTime && !_vm->shouldQuit()) { bool updateScreen = _vm->_video->updateMovies(); Common::Event event; @@ -2335,7 +2339,7 @@ static void rebelPrisonWindowTimer(MohawkEngine_Riven *vm) { VideoHandle handle = vm->_video->playMovieRiven(movie); // Ensure the next video starts after this one ends - uint32 timeUntilNextVideo = vm->_video->getDuration(handle).msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000; + uint32 timeUntilNextVideo = handle->getDuration().msecs() + vm->_rnd->getRandomNumberRng(38, 58) * 1000; // Save the time in case we leave the card and return vm->_vars["rvillagetime"] = timeUntilNextVideo + vm->getTotalPlayTime(); @@ -2434,7 +2438,7 @@ void RivenExternal::xtexterior300_telescopedown(uint16 argc, uint16 *argv) { static const uint32 timeIntervals[] = { 4320, 3440, 2560, 1760, 880, 0 }; uint16 movieCode = telescopeCover ? 1 : 2; VideoHandle handle = _vm->_video->playMovieRiven(movieCode); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600)); + handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos], 600), Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600)); _vm->_sound->playSound(14); // Play the moving sound _vm->_video->waitUntilMovieEnds(handle); @@ -2467,7 +2471,7 @@ void RivenExternal::xtexterior300_telescopeup(uint16 argc, uint16 *argv) { static const uint32 timeIntervals[] = { 0, 800, 1680, 2560, 3440, 4320 }; uint16 movieCode = _vm->_vars["ttelecover"] ? 4 : 5; VideoHandle handle = _vm->_video->playMovieRiven(movieCode); - _vm->_video->setVideoBounds(handle, Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600), Audio::Timestamp(0, timeIntervals[telescopePos], 600)); + handle->setBounds(Audio::Timestamp(0, timeIntervals[telescopePos - 1], 600), Audio::Timestamp(0, timeIntervals[telescopePos], 600)); _vm->_sound->playSound(14); // Play the moving sound _vm->_video->waitUntilMovieEnds(handle); @@ -2569,26 +2573,47 @@ void RivenExternal::xt7500_checkmarbles(uint16 argc, uint16 *argv) { void RivenExternal::xt7600_setupmarbles(uint16 argc, uint16 *argv) { // Draw the small marbles when we're a step away from the waffle - uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred"); + + // Convert from marble X coordinate to screen X coordinate + static const uint16 xPosOffsets[] = { + 246, 245, 244, 243, 243, 241, 240, 240, 239, 238, 237, 237, 236, 235, 234, 233, 232, 231, 230, 229, 228, 227, 226, 226, 225 + }; + + // Convert from marble Y coordinate to screen Y coordinate + static const uint16 yPosOffsets[] = { + 261, 263, 265, 267, 268, 270, 272, 274, 276, 278, 281, 284, 285, 288, 290, 293, 295, 298, 300, 303, 306, 309, 311, 314, 316 + }; + + // Handle spacing for y coordinates due to the angle + static const double yAdjusts[] = { + 4.56, 4.68, 4.76, 4.84, 4.84, 4.96, 5.04, 5.04, 5.12, 5.2, 5.28, 5.28, 5.36, 5.44, 5.4, 5.6, 5.72, 5.8, 5.88, 5.96, 6.04, 6.12, 6.2, 6.2, 6.28 + }; + + // Waffle state of 0 is up, 1 down bool waffleDown = _vm->_vars["twaffle"] != 0; // Note that each of the small marble images is exactly 4x2 + // The original seems to scale the marble images from extras.mhk, but + // we're using the pre-scaled images in the stack. + uint16 baseBitmapId = _vm->findResourceID(ID_TBMP, "*tsmallred"); for (uint16 i = 0; i < kMarbleCount; i++) { - uint32 &var = _vm->_vars[s_marbleNames[i]]; + uint32 var = _vm->_vars[s_marbleNames[i]]; if (var == 0) { // The marble is still in its initial place // (Note that this is still drawn even if the waffle is down) - int marbleX = 376 + i * 2; - int marbleY = 253 + i * 4; - _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight); + static const uint16 defaultX[] = { 375, 377, 379, 381, 383, 385 }; + static const uint16 defaultY[] = { 253, 257, 261, 265, 268, 273 }; + _vm->_gfx->copyImageToScreen(baseBitmapId + i, defaultX[i], defaultY[i], defaultX[i] + kSmallMarbleWidth, defaultY[i] + kSmallMarbleHeight); } else if (waffleDown) { // The marble is on the grid and the waffle is down // (Nothing to draw here) } else { // The marble is on the grid and the waffle is up - // TODO: Draw them onto the grid + int marbleX = (int)round(getMarbleX(var) * yAdjusts[getMarbleY(var)] + xPosOffsets[getMarbleY(var)]); + int marbleY = yPosOffsets[getMarbleY(var)]; + _vm->_gfx->copyImageToScreen(baseBitmapId + i, marbleX, marbleY, marbleX + kSmallMarbleWidth, marbleY + kSmallMarbleHeight); } } } diff --git a/engines/mohawk/riven_scripts.cpp b/engines/mohawk/riven_scripts.cpp index 29ee5cd50b..caa235ec8b 100644 --- a/engines/mohawk/riven_scripts.cpp +++ b/engines/mohawk/riven_scripts.cpp @@ -493,7 +493,9 @@ void RivenScript::changeStack(uint16 op, uint16 argc, uint16 *argv) { // Command 28: disable a movie void RivenScript::disableMovie(uint16 op, uint16 argc, uint16 *argv) { - _vm->_video->disableMovieRiven(argv[0]); + VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]); + if (handle) + handle->setEnabled(false); } // Command 29: disable all movies @@ -503,7 +505,9 @@ void RivenScript::disableAllMovies(uint16 op, uint16 argc, uint16 *argv) { // Command 31: enable a movie void RivenScript::enableMovie(uint16 op, uint16 argc, uint16 *argv) { - _vm->_video->enableMovieRiven(argv[0]); + VideoHandle handle = _vm->_video->findVideoHandleRiven(argv[0]); + if (handle) + handle->setEnabled(true); } // Command 32: play foreground movie - blocking (movie_id) diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index cebb72e24f..ff4a69cd28 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -24,6 +24,7 @@ #include "mohawk/resource.h" #include "mohawk/video.h" +#include "common/algorithm.h" #include "common/debug.h" #include "common/events.h" #include "common/textconsole.h" @@ -37,22 +38,115 @@ namespace Mohawk { -void VideoEntry::clear() { - video = 0; - x = 0; - y = 0; - loop = false; - enabled = false; - start = Audio::Timestamp(0, 1); - filename.clear(); - id = -1; +VideoEntry::VideoEntry() : _video(0), _id(-1), _x(0), _y(0), _loop(false), _enabled(true) { } -bool VideoEntry::endOfVideo() { - return !video || video->endOfVideo(); +VideoEntry::VideoEntry(Video::VideoDecoder *video, const Common::String &fileName) : _video(video), _fileName(fileName), _id(-1), _x(0), _y(0), _loop(false), _enabled(true) { +} + +VideoEntry::VideoEntry(Video::VideoDecoder *video, int id) : _video(video), _id(id), _x(0), _y(0), _loop(false), _enabled(true) { +} + +VideoEntry::~VideoEntry() { + close(); +} + +void VideoEntry::close() { + delete _video; + _video = 0; +} + +bool VideoEntry::endOfVideo() const { + return !isOpen() || _video->endOfVideo(); +} + +int VideoEntry::getCurFrame() const { + assert(_video); + return _video->getCurFrame(); +} + +uint32 VideoEntry::getFrameCount() const { + assert(_video); + return _video->getFrameCount(); +} + +uint32 VideoEntry::getTime() const { + assert(_video); + return _video->getTime(); +} + +Audio::Timestamp VideoEntry::getDuration() const { + assert(_video); + return _video->getDuration(); +} + +Common::Rational VideoEntry::getRate() const { + assert(_video); + return _video->getRate(); +} + +void VideoEntry::center() { + assert(_video); + _x = (g_system->getWidth() - _video->getWidth()) / 2; + _y = (g_system->getHeight() - _video->getHeight()) / 2; +} + +void VideoEntry::setBounds(const Audio::Timestamp &startTime, const Audio::Timestamp &endTime) { + assert(_video); + _start = startTime; + _video->setEndTime(endTime); + _video->seek(startTime); +} + +void VideoEntry::seek(const Audio::Timestamp &time) { + assert(_video); + _video->seek(time); +} + +void VideoEntry::setRate(const Common::Rational &rate) { + assert(_video); + _video->setRate(rate); +} + +void VideoEntry::pause(bool isPaused) { + assert(_video); + _video->pauseVideo(isPaused); +} + +void VideoEntry::start() { + assert(_video); + _video->start(); +} + +void VideoEntry::stop() { + assert(_video); + _video->stop(); +} + +bool VideoEntry::isPlaying() const { + assert(_video); + return _video->isPlaying(); +} + +int VideoEntry::getVolume() const { + assert(_video); + return _video->getVolume(); +} + +void VideoEntry::setVolume(int volume) { + assert(_video); + _video->setVolume(CLIP(volume, 0, 255)); +} + +VideoHandle::VideoHandle(VideoEntryPtr ptr) : _ptr(ptr) { +} + +VideoHandle::VideoHandle(const VideoHandle &handle) : _ptr(handle._ptr) { } VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { + // Set dithering enabled, if required + _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME); } VideoManager::~VideoManager() { @@ -60,41 +154,42 @@ VideoManager::~VideoManager() { } void VideoManager::pauseVideos() { - for (uint16 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video) - _videoStreams[i]->pauseVideo(true); + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + (*it)->pause(true); } void VideoManager::resumeVideos() { - for (uint16 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video) - _videoStreams[i]->pauseVideo(false); + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + (*it)->pause(false); } void VideoManager::stopVideos() { - for (uint16 i = 0; i < _videoStreams.size(); i++) - delete _videoStreams[i].video; + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + (*it)->close(); - _videoStreams.clear(); + _videos.clear(); } -void VideoManager::playMovieBlocking(const Common::String &filename, uint16 x, uint16 y, bool clearScreen) { - VideoHandle videoHandle = createVideoHandle(filename, x, y, false); - if (videoHandle == NULL_VID_HANDLE) +void VideoManager::playMovieBlocking(const Common::String &fileName, uint16 x, uint16 y, bool clearScreen) { + VideoEntryPtr ptr = open(fileName); + if (!ptr) return; + ptr->moveTo(x, y); + // Clear screen if requested if (clearScreen) { _vm->_system->fillScreen(_vm->_system->getScreenFormat().RGBToColor(0, 0, 0)); _vm->_system->updateScreen(); } - waitUntilMovieEnds(videoHandle); + ptr->start(); + waitUntilMovieEnds(ptr); } -void VideoManager::playMovieBlockingCentered(const Common::String &filename, bool clearScreen) { - VideoHandle videoHandle = createVideoHandle(filename, 0, 0, false); - if (videoHandle == NULL_VID_HANDLE) +void VideoManager::playMovieBlockingCentered(const Common::String &fileName, bool clearScreen) { + VideoEntryPtr ptr = open(fileName); + if (!ptr) return; // Clear screen if requested @@ -103,19 +198,22 @@ void VideoManager::playMovieBlockingCentered(const Common::String &filename, boo _vm->_system->updateScreen(); } - _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2; - _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2; - - waitUntilMovieEnds(videoHandle); + ptr->center(); + ptr->start(); + waitUntilMovieEnds(ptr); } void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { - if (videoHandle == NULL_VID_HANDLE) + if (!videoHandle) return; + // Sanity check + if (videoHandle._ptr->isLooping()) + error("Called waitUntilMovieEnds() on a looping video"); + bool continuePlaying = true; - while (!_videoStreams[videoHandle].endOfVideo() && !_vm->shouldQuit() && continuePlaying) { + while (!videoHandle->endOfVideo() && !_vm->shouldQuit() && continuePlaying) { if (updateMovies()) _vm->_system->updateScreen(); @@ -147,12 +245,22 @@ void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) { _vm->_system->delayMillis(10); } - delete _videoStreams[videoHandle].video; - _videoStreams[videoHandle].clear(); + // Ensure it's removed + removeEntry(videoHandle._ptr); } void VideoManager::delayUntilMovieEnds(VideoHandle videoHandle) { - while (!_videoStreams[videoHandle].endOfVideo() && !_vm->shouldQuit()) { + // FIXME: Why is this separate from waitUntilMovieEnds? + // It seems to only cut out the event loop (which is bad). + + if (!videoHandle) + return; + + // Sanity check + if (videoHandle._ptr->isLooping()) + error("Called delayUntilMovieEnds() on a looping video"); + + while (!videoHandle->endOfVideo() && !_vm->shouldQuit()) { if (updateMovies()) _vm->_system->updateScreen(); @@ -160,92 +268,85 @@ void VideoManager::delayUntilMovieEnds(VideoHandle videoHandle) { _vm->_system->delayMillis(10); } - delete _videoStreams[videoHandle].video; - _videoStreams[videoHandle].clear(); + // Ensure it's removed + removeEntry(videoHandle._ptr); } -VideoHandle VideoManager::playMovie(const Common::String &filename, int16 x, int16 y, bool loop) { - VideoHandle videoHandle = createVideoHandle(filename, x, y, loop); - if (videoHandle == NULL_VID_HANDLE) - return NULL_VID_HANDLE; - - // Center x if requested - if (x < 0) - _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2; +VideoHandle VideoManager::playMovie(const Common::String &fileName) { + VideoEntryPtr ptr = open(fileName); + if (!ptr) + return VideoHandle(); - // Center y if requested - if (y < 0) - _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2; - - return videoHandle; + ptr->start(); + return ptr; } -VideoHandle VideoManager::playMovie(uint16 id, int16 x, int16 y, bool loop) { - VideoHandle videoHandle = createVideoHandle(id, x, y, loop); - if (videoHandle == NULL_VID_HANDLE) - return NULL_VID_HANDLE; - - // Center x if requested - if (x < 0) - _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2; +VideoHandle VideoManager::playMovie(uint16 id) { + VideoEntryPtr ptr = open(id); + if (!ptr) + return VideoHandle(); - // Center y if requested - if (y < 0) - _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2; - - return videoHandle; + ptr->start(); + return ptr; } bool VideoManager::updateMovies() { bool updateScreen = false; - for (uint32 i = 0; i < _videoStreams.size() && !_vm->shouldQuit(); i++) { - // Skip deleted videos - if (!_videoStreams[i].video) - continue; - - // Remove any videos that are over - if (_videoStreams[i].endOfVideo()) { - if (_videoStreams[i].loop) { - _videoStreams[i]->seek(_videoStreams[i].start); + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); ) { + // Check of the video has reached the end + if ((*it)->endOfVideo()) { + if ((*it)->isLooping()) { + // Seek back if looping + (*it)->seek((*it)->getStart()); } else { - // Check the video time one last time before deleting it - _vm->doVideoTimer(i, true); - delete _videoStreams[i].video; - _videoStreams[i].clear(); + // Done; close and continue on + (*it)->close(); + it = _videos.erase(it); continue; } } - // Nothing more to do if we're paused - if (_videoStreams[i]->isPaused()) + Video::VideoDecoder *video = (*it)->_video; + + // Ignore paused videos + if (video->isPaused()) { + it++; continue; + } // Check if we need to draw a frame - if (_videoStreams[i]->needsUpdate()) { - const Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame(); + if (video->needsUpdate()) { + const Graphics::Surface *frame = video->decodeNextFrame(); Graphics::Surface *convertedFrame = 0; - if (frame && _videoStreams[i].enabled) { + if (frame && (*it)->isEnabled()) { Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); if (frame->format != pixelFormat) { - // We don't support downconverting to 8bpp - if (pixelFormat.bytesPerPixel == 1) - error("Cannot convert high color video frame to 8bpp"); + // We don't support downconverting to 8bpp without having + // support in the codec. Set _enableDither if shows up. + if (pixelFormat.bytesPerPixel == 1) { + warning("Cannot convert high color video frame to 8bpp"); + (*it)->close(); + it = _videos.erase(it); + continue; + } // Convert to the current screen format - convertedFrame = frame->convertTo(pixelFormat, _videoStreams[i]->getPalette()); + convertedFrame = frame->convertTo(pixelFormat, video->getPalette()); frame = convertedFrame; - } else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) { + } else if (pixelFormat.bytesPerPixel == 1 && video->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only - _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); + // Don't do this for Myst, which has its own per-stack handling + if (_vm->getGameType() != GType_MYST) + _vm->_system->getPaletteManager()->setPalette(video->getPalette(), 0, 256); } // Clip the width/height to make sure we stay on the screen (Myst does this a few times) - uint16 width = MIN<int32>(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x); - uint16 height = MIN<int32>(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y); - _vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height); + uint16 width = MIN<int32>(video->getWidth(), _vm->_system->getWidth() - (*it)->getX()); + uint16 height = MIN<int32>(video->getHeight(), _vm->_system->getHeight() - (*it)->getY()); + _vm->_system->copyRectToScreen(frame->getPixels(), frame->pitch, (*it)->getX(), (*it)->getY(), width, height); // We've drawn something to the screen, make sure we update it updateScreen = true; @@ -259,7 +360,10 @@ bool VideoManager::updateMovies() { } // Check the video time - _vm->doVideoTimer(i, false); + _vm->doVideoTimer(*it, false); + + // Remember to increase the iterator + it++; } // Return true if we need to update the screen @@ -314,241 +418,180 @@ void VideoManager::clearMLST() { } VideoHandle VideoManager::playMovieRiven(uint16 id) { - for (uint16 i = 0; i < _mlstRecords.size(); i++) + for (uint16 i = 0; i < _mlstRecords.size(); i++) { if (_mlstRecords[i].code == id) { debug(1, "Play tMOV %d (non-blocking) at (%d, %d) %s, Volume = %d", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0 ? "looping" : "non-looping", _mlstRecords[i].volume); - return createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].loop != 0, _mlstRecords[i].volume); + + VideoEntryPtr ptr = open(_mlstRecords[i].movieID); + if (ptr) { + ptr->moveTo(_mlstRecords[i].left, _mlstRecords[i].top); + ptr->setLooping(_mlstRecords[i].loop != 0); + ptr->setVolume(_mlstRecords[i].volume); + ptr->start(); + } + + return ptr; } + } - return NULL_VID_HANDLE; + return VideoHandle(); } void VideoManager::playMovieBlockingRiven(uint16 id) { - for (uint16 i = 0; i < _mlstRecords.size(); i++) + for (uint16 i = 0; i < _mlstRecords.size(); i++) { if (_mlstRecords[i].code == id) { debug(1, "Play tMOV %d (blocking) at (%d, %d), Volume = %d", _mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, _mlstRecords[i].volume); - VideoHandle videoHandle = createVideoHandle(_mlstRecords[i].movieID, _mlstRecords[i].left, _mlstRecords[i].top, false); - waitUntilMovieEnds(videoHandle); + VideoEntryPtr ptr = open(_mlstRecords[i].movieID); + ptr->moveTo(_mlstRecords[i].left, _mlstRecords[i].top); + ptr->setVolume(_mlstRecords[i].volume); + ptr->start(); + waitUntilMovieEnds(ptr); return; } + } } void VideoManager::stopMovieRiven(uint16 id) { debug(2, "Stopping movie %d", id); - for (uint16 i = 0; i < _mlstRecords.size(); i++) - if (_mlstRecords[i].code == id) - for (uint16 j = 0; j < _videoStreams.size(); j++) - if (_mlstRecords[i].movieID == _videoStreams[j].id) { - delete _videoStreams[j].video; - _videoStreams[j].clear(); - return; - } -} - -void VideoManager::enableMovieRiven(uint16 id) { - debug(2, "Enabling movie %d", id); - for (uint16 i = 0; i < _mlstRecords.size(); i++) - if (_mlstRecords[i].code == id) - for (uint16 j = 0; j < _videoStreams.size(); j++) - if (_mlstRecords[i].movieID == _videoStreams[j].id) { - _videoStreams[j].enabled = true; - return; - } -} - -void VideoManager::disableMovieRiven(uint16 id) { - debug(2, "Disabling movie %d", id); - for (uint16 i = 0; i < _mlstRecords.size(); i++) - if (_mlstRecords[i].code == id) - for (uint16 j = 0; j < _videoStreams.size(); j++) - if (_mlstRecords[i].movieID == _videoStreams[j].id) { - _videoStreams[j].enabled = false; - return; - } + VideoHandle handle = findVideoHandleRiven(id); + if (handle) + removeEntry(handle._ptr); } void VideoManager::disableAllMovies() { debug(2, "Disabling all movies"); - for (uint16 i = 0; i < _videoStreams.size(); i++) - _videoStreams[i].enabled = false; + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + (*it)->setEnabled(false); } -VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop, uint16 volume) { - // First, check to see if that video is already playing - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].id == id) - return i; +VideoEntryPtr VideoManager::open(uint16 id) { + // If this video is already playing, return that handle + VideoHandle oldHandle = findVideoHandle(id); + if (oldHandle._ptr) + return oldHandle._ptr; // Otherwise, create a new entry - Video::QuickTimeDecoder *decoder = new Video::QuickTimeDecoder(); - decoder->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id)); - decoder->loadStream(_vm->getResource(ID_TMOV, id)); - decoder->setVolume((volume >= 256) ? 255 : volume); - - VideoEntry entry; - entry.clear(); - entry.video = decoder; - entry.x = x; - entry.y = y; - entry.id = id; - entry.loop = loop; - entry.enabled = true; - - entry->start(); - - // Search for any deleted videos so we can take a formerly used slot - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (!_videoStreams[i].video) { - _videoStreams[i] = entry; - return i; - } + Video::QuickTimeDecoder *video = new Video::QuickTimeDecoder(); + video->setChunkBeginOffset(_vm->getResourceOffset(ID_TMOV, id)); + video->loadStream(_vm->getResource(ID_TMOV, id)); + + // Create the entry + VideoEntryPtr entry(new VideoEntry(video, id)); + + // Enable dither if necessary + checkEnableDither(entry); + + // Add it to the video list + _videos.push_back(entry); - // Otherwise, just add it to the list - _videoStreams.push_back(entry); - return _videoStreams.size() - 1; + return entry; } -VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume) { - // First, check to see if that video is already playing - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].filename == filename) - return i; +VideoEntryPtr VideoManager::open(const Common::String &fileName) { + // If this video is already playing, return that entry + VideoHandle oldHandle = findVideoHandle(fileName); + if (oldHandle._ptr) + return oldHandle._ptr; // Otherwise, create a new entry - VideoEntry entry; - entry.clear(); - entry.video = new Video::QuickTimeDecoder(); - entry.x = x; - entry.y = y; - entry.filename = filename; - entry.loop = loop; - entry.enabled = true; - - Common::File *file = new Common::File(); - if (!file->open(filename)) { - delete file; - return NULL_VID_HANDLE; + Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(fileName); + if (!stream) + return VideoEntryPtr(); + + Video::VideoDecoder *video = new Video::QuickTimeDecoder(); + if (!video->loadStream(stream)) { + // FIXME: Better error handling + delete video; + return VideoEntryPtr(); } - entry->loadStream(file); - entry->setVolume(volume); - entry->start(); + // Create the entry + VideoEntryPtr entry(new VideoEntry(video, fileName)); - // Search for any deleted videos so we can take a formerly used slot - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (!_videoStreams[i].video) { - _videoStreams[i] = entry; - return i; - } + // Enable dither if necessary + checkEnableDither(entry); + + // Add it to the video list + _videos.push_back(entry); - // Otherwise, just add it to the list - _videoStreams.push_back(entry); - return _videoStreams.size() - 1; + return entry; } VideoHandle VideoManager::findVideoHandleRiven(uint16 id) { for (uint16 i = 0; i < _mlstRecords.size(); i++) if (_mlstRecords[i].code == id) - for (uint32 j = 0; j < _videoStreams.size(); j++) - if (_videoStreams[j].video && _mlstRecords[i].movieID == _videoStreams[j].id) - return j; + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + if ((*it)->getID() == _mlstRecords[i].movieID) + return *it; - return NULL_VID_HANDLE; + return VideoHandle(); } VideoHandle VideoManager::findVideoHandle(uint16 id) { - if (!id) - return NULL_VID_HANDLE; + if (id == 0) + return VideoHandle(); - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video && _videoStreams[i].id == id) - return i; + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + if ((*it)->getID() == id) + return *it; - return NULL_VID_HANDLE; + return VideoHandle(); } -VideoHandle VideoManager::findVideoHandle(const Common::String &filename) { - if (filename.empty()) - return NULL_VID_HANDLE; - - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (_videoStreams[i].video && _videoStreams[i].filename.equalsIgnoreCase(filename)) - return i; +VideoHandle VideoManager::findVideoHandle(const Common::String &fileName) { + if (fileName.empty()) + return VideoHandle(); - return NULL_VID_HANDLE; -} - -int VideoManager::getCurFrame(VideoHandle handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getCurFrame(); -} + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + if ((*it)->getFileName().equalsIgnoreCase(fileName)) + return *it; -uint32 VideoManager::getFrameCount(VideoHandle handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getFrameCount(); -} - -uint32 VideoManager::getTime(VideoHandle handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getTime(); -} - -Audio::Timestamp VideoManager::getDuration(VideoHandle handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getDuration(); -} - -bool VideoManager::endOfVideo(VideoHandle handle) { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle].endOfVideo(); + return VideoHandle(); } bool VideoManager::isVideoPlaying() { - for (uint32 i = 0; i < _videoStreams.size(); i++) - if (!_videoStreams[i].endOfVideo()) + for (VideoList::iterator it = _videos.begin(); it != _videos.end(); it++) + if (!(*it)->endOfVideo()) return true; return false; } -void VideoManager::setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle].start = start; - _videoStreams[handle]->setEndTime(end); - _videoStreams[handle]->seek(start); -} - -void VideoManager::drawVideoFrame(VideoHandle handle, Audio::Timestamp time) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->seek(time); +void VideoManager::drawVideoFrame(VideoHandle handle, const Audio::Timestamp &time) { + // FIXME: This should be done separately from the "playing" + // videos eventually. + assert(handle); + handle->seek(time); updateMovies(); - delete _videoStreams[handle].video; - _videoStreams[handle].clear(); + handle->close(); } -void VideoManager::seekToTime(VideoHandle handle, Audio::Timestamp time) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->seek(time); +VideoManager::VideoList::iterator VideoManager::findEntry(VideoEntryPtr ptr) { + return Common::find(_videos.begin(), _videos.end(), ptr); } -void VideoManager::setVideoLooping(VideoHandle handle, bool loop) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle].loop = loop; +void VideoManager::removeEntry(VideoEntryPtr ptr) { + VideoManager::VideoList::iterator it = findEntry(ptr); + if (it != _videos.end()) + _videos.erase(it); } -Common::Rational VideoManager::getVideoRate(VideoHandle handle) const { - assert(handle != NULL_VID_HANDLE); - return _videoStreams[handle]->getRate(); -} +void VideoManager::checkEnableDither(VideoEntryPtr &entry) { + // If we're not dithering, bail out + if (!_enableDither) + return; -void VideoManager::setVideoRate(VideoHandle handle, const Common::Rational &rate) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->setRate(rate); -} + // Set the palette + byte palette[256 * 3]; + g_system->getPaletteManager()->grabPalette(palette, 0, 256); + entry->_video->setDitheringPalette(palette); -void VideoManager::pauseMovie(VideoHandle handle, bool pause) { - assert(handle != NULL_VID_HANDLE); - _videoStreams[handle]->pauseVideo(pause); + if (entry->_video->getPixelFormat().bytesPerPixel != 1) { + if (entry->getFileName().empty()) + error("Failed to set dither for video tMOV %d", entry->getID()); + else + error("Failed to set dither for video %s", entry->getFileName().c_str()); + } } } // End of namespace Mohawk diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 43181e3e6c..106a32f8e2 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -23,9 +23,17 @@ #ifndef MOHAWK_VIDEO_H #define MOHAWK_VIDEO_H +#include "audio/timestamp.h" #include "common/array.h" +#include "common/list.h" +#include "common/noncopyable.h" +#include "common/ptr.h" +#include "common/rational.h" #include "graphics/pixelformat.h" -#include "video/video_decoder.h" + +namespace Video { +class VideoDecoder; +} namespace Mohawk { @@ -43,29 +51,254 @@ struct MLSTRecord { uint16 u1; }; -struct VideoEntry { +/** + * A video monitored by the VideoManager + */ +class VideoEntry : private Common::NonCopyable { + // The private members should be able to be manipulated by VideoManager + friend class VideoManager; + +private: + // Hide the destructor/constructor + // Only VideoManager should be allowed + VideoEntry(); + VideoEntry(Video::VideoDecoder *video, const Common::String &fileName); + VideoEntry(Video::VideoDecoder *video, int id); + +public: + ~VideoEntry(); + + /** + * Convenience implicit cast to bool + */ + operator bool() const { return isOpen(); } + + /** + * Is the video open? + */ + bool isOpen() const { return _video != 0; } + + /** + * Close the video + */ + void close(); + + /** + * Has the video reached its end? + */ + bool endOfVideo() const; + + /** + * Get the X position of where the video is displayed + */ + uint16 getX() const { return _x; } + + /** + * Get the Y position of where the video is displayed + */ + uint16 getY() const { return _y; } + + /** + * Is the video looping? + */ + bool isLooping() const { return _loop; } + + /** + * Is the video enabled? (Drawing to the screen) + */ + bool isEnabled() const { return _enabled; } + + /** + * Get the start time of the video bounds + */ + const Audio::Timestamp &getStart() const { return _start; } + + /** + * Get the file name of the video, or empty if by ID + */ + const Common::String &getFileName() const { return _fileName; } + + /** + * Get the ID of the video, or -1 if by file name + */ + int getID() const { return _id; } + + /** + * Get the current frame of the video + */ + int getCurFrame() const; + + /** + * Get the frame count of the video + */ + uint32 getFrameCount() const; + + /** + * Get the current time position of the video + */ + uint32 getTime() const; + + /** + * Get the duration of the video + */ + Audio::Timestamp getDuration() const; + + /** + * Get the current playback rate of the videos + */ + Common::Rational getRate() const; + + /** + * Move the x position of the video + */ + void setX(uint16 x) { _x = x; } + + /** + * Move the y position of the video + */ + void setY(uint16 y) { _y = y; } + + /** + * Move the video to the specified coordinates + */ + void moveTo(uint16 x, uint16 y) { setX(x); setY(y); } + + /** + * Center the video on the screen + */ + void center(); + + /** + * Set the start time when using video bounds + */ + void setStart(const Audio::Timestamp &time) { _start = time; } + + /** + * Set the video to loop (true) or not (false) + */ + void setLooping(bool loop) { _loop = loop; } + + /** + * Set the video's enabled status + */ + void setEnabled(bool enabled) { _enabled = enabled; } + + /** + * Set the bounds of the video + * + * This automatically seeks to the start time + */ + void setBounds(const Audio::Timestamp &startTime, const Audio::Timestamp &endTime); + + /** + * Seek to the given time + */ + void seek(const Audio::Timestamp &time); + + /** + * Set the playback rate + */ + void setRate(const Common::Rational &rate); + + /** + * Pause the video + */ + void pause(bool isPaused); + + /** + * Start playing the video + */ + void start(); + + /** + * Stop playing the video + */ + void stop(); + + /** + * Is the video playing? + */ + bool isPlaying() const; + + /** + * Get the volume of the video + */ + int getVolume() const; + + /** + * Set the volume of the video + */ + void setVolume(int volume); + +private: + // Non-changing variables + Video::VideoDecoder *_video; + Common::String _fileName; // External video files + int _id; // Internal Mohawk files + // Playback variables - Video::VideoDecoder *video; - uint16 x; - uint16 y; - bool loop; - bool enabled; - Audio::Timestamp start; - - // Identification - Common::String filename; // External video files - int id; // Internal Mohawk files - - // Helper functions - Video::VideoDecoder *operator->() const { assert(video); return video; } // TODO: Remove this eventually - void clear(); - bool endOfVideo(); + uint16 _x; + uint16 _y; + bool _loop; + bool _enabled; + Audio::Timestamp _start; }; -typedef int32 VideoHandle; +typedef Common::SharedPtr<VideoEntry> VideoEntryPtr; + +/** + * A handle for manipulating a video + */ +class VideoHandle { + // The private members should be able to be manipulated by VideoManager + friend class VideoManager; + +public: + /** + * Default constructor + */ + VideoHandle() {} + + /** + * Copy constructor + */ + VideoHandle(const VideoHandle &handle); + + /** + * Is this handle pointing to a valid video entry? + */ + bool isValid() const { return _ptr && _ptr->isOpen(); } + + /** + * Convenience implicit cast to bool + */ + operator bool() const { return isValid(); } + + /** + * Simple equality operator + */ + bool operator==(const VideoHandle &other) const { return _ptr.get() == other._ptr.get(); } -enum { - NULL_VID_HANDLE = -1 + /** + * Simple inequality operator + */ + bool operator!=(const VideoHandle &other) const { return !(*this == other); } + + /** + * Convenience operator-> override to give direct access to the VideoEntry + */ + VideoEntryPtr operator->() const { return _ptr; } + +private: + /** + * Constructor for internal VideoManager use + */ + VideoHandle(VideoEntryPtr ptr); + + /** + * The video entry this is associated with + */ + VideoEntryPtr _ptr; }; class VideoManager { @@ -76,8 +309,8 @@ public: // Generic movie functions void playMovieBlocking(const Common::String &filename, uint16 x = 0, uint16 y = 0, bool clearScreen = false); void playMovieBlockingCentered(const Common::String &filename, bool clearScreen = true); - VideoHandle playMovie(const Common::String &filename, int16 x = -1, int16 y = -1, bool loop = false); - VideoHandle playMovie(uint16 id, int16 x = -1, int16 y = -1, bool loop = false); + VideoHandle playMovie(const Common::String &filename); + VideoHandle playMovie(uint16 id); bool updateMovies(); void pauseVideos(); void resumeVideos(); @@ -87,31 +320,18 @@ public: // Riven-related functions void activateMLST(uint16 mlstId, uint16 card); void clearMLST(); - void enableMovieRiven(uint16 id); - void disableMovieRiven(uint16 id); void disableAllMovies(); VideoHandle playMovieRiven(uint16 id); - void stopMovieRiven(uint16 id); void playMovieBlockingRiven(uint16 id); VideoHandle findVideoHandleRiven(uint16 id); + void stopMovieRiven(uint16 id); // Handle functions VideoHandle findVideoHandle(uint16 id); - VideoHandle findVideoHandle(const Common::String &filename); - int getCurFrame(VideoHandle handle); - uint32 getFrameCount(VideoHandle handle); - uint32 getTime(VideoHandle handle); - Audio::Timestamp getDuration(VideoHandle videoHandle); - bool endOfVideo(VideoHandle handle); - void setVideoBounds(VideoHandle handle, Audio::Timestamp start, Audio::Timestamp end); - void drawVideoFrame(VideoHandle handle, Audio::Timestamp time); - void seekToTime(VideoHandle handle, Audio::Timestamp time); - void setVideoLooping(VideoHandle handle, bool loop); - Common::Rational getVideoRate(VideoHandle handle) const; - void setVideoRate(VideoHandle handle, const Common::Rational &rate); - void waitUntilMovieEnds(VideoHandle videoHandle); - void delayUntilMovieEnds(VideoHandle videoHandle); - void pauseMovie(VideoHandle videoHandle, bool pause); + VideoHandle findVideoHandle(const Common::String &fileName); + void waitUntilMovieEnds(VideoHandle handle); + void delayUntilMovieEnds(VideoHandle handle); + void drawVideoFrame(VideoHandle handle, const Audio::Timestamp &time); private: MohawkEngine *_vm; @@ -120,10 +340,19 @@ private: Common::Array<MLSTRecord> _mlstRecords; // Keep tabs on any videos playing - Common::Array<VideoEntry> _videoStreams; + typedef Common::List<VideoEntryPtr> VideoList; + VideoList _videos; + + // Utility functions for managing entries + VideoEntryPtr open(uint16 id); + VideoEntryPtr open(const Common::String &fileName); + + VideoList::iterator findEntry(VideoEntryPtr ptr); + void removeEntry(VideoEntryPtr ptr); - VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop, uint16 volume = 0xff); - VideoHandle createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume = 0xff); + // Dithering control + bool _enableDither; + void checkEnableDither(VideoEntryPtr &entry); }; } // End of namespace Mohawk |