/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $URL$ * $Id$ * */ #include "common/config-manager.h" #include "common/debug-channels.h" #include "common/translation.h" #include "mohawk/graphics.h" #include "mohawk/myst.h" #include "mohawk/myst_saveload.h" #include "mohawk/dialogs.h" #include "mohawk/resource.h" #include "mohawk/resource_cache.h" #include "mohawk/video.h" namespace Mohawk { MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) { DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses"); DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function"); DebugMan.addDebugChannel(kDebugView, "View", "Track Card File (VIEW) Parsing"); DebugMan.addDebugChannel(kDebugHint, "Hint", "Track Cursor Hints (HINT) Parsing"); DebugMan.addDebugChannel(kDebugResource, "Resource", "Track Resource (RLST) Parsing"); DebugMan.addDebugChannel(kDebugINIT, "Init", "Track Card Init Script (INIT) Parsing"); DebugMan.addDebugChannel(kDebugEXIT, "Exit", "Track Card Exit Script (EXIT) Parsing"); DebugMan.addDebugChannel(kDebugScript, "Script", "Track Script Execution"); DebugMan.addDebugChannel(kDebugHelp, "Help", "Track Help File (HELP) Parsing"); DebugMan.addDebugChannel(kDebugCache, "Cache", "Track Resource Cache Accesses"); _zipMode = false; _transitionsEnabled = false; // Engine tweaks // Disabling this makes engine behaviour as per // original, including bugs, missing bits etc. :) _tweaksEnabled = true; _currentCursor = _mainCursor = kDefaultMystCursor; _showResourceRects = false; _curCard = 0; _needsUpdate = false; _curResource = -1; _gfx = NULL; _console = NULL; _scriptParser = NULL; _varStore = NULL; _saveLoad = NULL; _loadDialog = NULL; _optionsDialog = NULL; _cursorHintCount = 0; _cursorHints = NULL; _view.conditionalImageCount = 0; _view.conditionalImages = NULL; _view.soundList = NULL; _view.soundListVolume = NULL; _view.scriptResCount = 0; _view.scriptResources = NULL; _scriptParser->disableInitOpcodes(); if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh) { const Common::FSNode gameDataDir(ConfMan.get("path")); SearchMan.addSubDirectoryMatching(gameDataDir, "CD Data"); } } MohawkEngine_Myst::~MohawkEngine_Myst() { DebugMan.clearAllDebugChannels(); delete _gfx; delete _console; delete _scriptParser; delete _varStore; delete _saveLoad; delete _loadDialog; delete _optionsDialog; delete[] _cursorHints; delete[] _view.conditionalImages; delete[] _view.scriptResources; while(!_resources.empty()) { MystResource *temp = _resources.back(); _resources.pop_back(); delete temp; } } // Uses cached data objects in preference to disk access Common::SeekableReadStream *MohawkEngine_Myst::getRawData(uint32 tag, uint16 id) { Common::SeekableReadStream *ret = _cache.search(tag, id); if (ret) return ret; for (uint32 i = 0; i < _mhk.size(); i++) if (_mhk[i]->hasResource(tag, id)) { ret = _mhk[i]->getResource(tag, id); _cache.add(tag, id, ret); return ret; } error("Could not find a \'%s\' resource with ID %04x", tag2str(tag), id); return NULL; } void MohawkEngine_Myst::cachePreload(uint32 tag, uint16 id) { if (!_cache.enabled) return; for (uint32 i = 0; i < _mhk.size(); i++) { // Check for MJMP in Myst ME if ((getFeatures() & GF_ME) && tag == ID_MSND && _mhk[i]->hasResource(ID_MJMP, id)) { Common::SeekableReadStream *tempData = _mhk[i]->getResource(ID_MJMP, id); uint16 msndId = tempData->readUint16LE(); delete tempData; // We've found where the real MSND data is, so go get that tempData = _mhk[i]->getResource(tag, msndId); _cache.add(tag, id, tempData); delete tempData; return; } if (_mhk[i]->hasResource(tag, id)) { Common::SeekableReadStream *tempData = _mhk[i]->getResource(tag, id); _cache.add(tag, id, tempData); delete tempData; return; } } warning("cachePreload: Could not find a \'%s\' resource with ID %04x", tag2str(tag), id); } static const char *mystFiles[] = { "channel.dat", "credits.dat", "demo.dat", "dunny.dat", "intro.dat", "making.dat", "mechan.dat", "myst.dat", "selen.dat", "slides.dat", "sneak.dat", "stone.dat" }; // Myst Hardcoded Movie Paths // Mechanical Stack Movie "sstairs" referenced in executable, but not used? // NOTE: cl1wg1.mov etc. found in the root directory in versions of Myst // Original are duplicates of those in /qtw/myst directory and thus not necessary. // The following movies are not referenced in RLST or hardcoded into the executables. // It is likely they are unused: // qtw/mech/lwrgear2.mov + lwrgears.mov: I have no idea what these are; perhaps replaced by an animated image in-game? // qtw/myst/gar4wbf1.mov: gar4wbf2.mov has two butterflies instead of one // qtw/myst/libelev.mov: libup.mov is basically the same with sound Common::String MohawkEngine_Myst::wrapMovieFilename(const Common::String &movieName, uint16 stack) { // The Macintosh release of Myst ME stores its videos in a different folder if ((getFeatures() & GF_ME) && getPlatform() == Common::kPlatformMacintosh) return Common::String("CD Data/m/") + movieName + ".mov"; const char* prefix; switch (stack) { case kIntroStack: prefix = "intro/"; break; case kChannelwoodStack: // The Windmill videos like to hide in a different folder if (movieName.contains("wmill")) prefix = "channel2/"; else prefix = "channel/"; break; case kDniStack: prefix = "dunny/"; break; case kMechanicalStack: prefix = "mech/"; break; case kMystStack: prefix = "myst/"; break; case kSeleniticStack: prefix = "selen/"; break; case kStoneshipStack: prefix = "stone/"; break; default: prefix = ""; // Masterpiece Edition Only Movies break; } return Common::String("qtw/") + prefix + movieName + ".mov"; } Common::Error MohawkEngine_Myst::run() { MohawkEngine::run(); _gfx = new MystGraphics(this); _console = new MystConsole(this); _varStore = new MystVar(this); _saveLoad = new MystSaveLoad(this, _saveFileMan); _scriptParser = new MystScriptParser(this); _loadDialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load")); _loadDialog->setSaveMode(false); _optionsDialog = new MystOptionsDialog(this); // Start us on the first stack. if (getGameType() == GType_MAKINGOF) changeToStack(kMakingOfStack); else if (getFeatures() & GF_DEMO) changeToStack(kDemoStack); else changeToStack(kIntroStack); if (getFeatures() & GF_DEMO) changeToCard(2000); else changeToCard(1); // Load game from launcher/command line if requested if (ConfMan.hasKey("save_slot") && !(getFeatures() & GF_DEMO)) { uint32 gameToLoad = ConfMan.getInt("save_slot"); Common::StringArray savedGamesList = _saveLoad->generateSaveGameList(); if (gameToLoad > savedGamesList.size()) error ("Could not find saved game"); _saveLoad->loadGame(savedGamesList[gameToLoad]); } // Load Help System (Masterpiece Edition Only) if (getFeatures() & GF_ME) { MohawkArchive *mhk = new MohawkArchive(); if (!mhk->open("help.dat")) error("Could not load help.dat"); _mhk.push_back(mhk); } // Test Load Function... loadHelp(10000); // Set the cursor _gfx->changeCursor(_currentCursor); _gfx->showCursor(); Common::Event event; while (!shouldQuit()) { // Update any background videos _needsUpdate = _video->updateBackgroundMovies(); _scriptParser->runPersistentOpcodes(); // Run animations... for (uint16 i = 0; i < _resources.size(); i++) _resources[i]->handleAnimation(); while (_eventMan->pollEvent(event)) { switch (event.type) { case Common::EVENT_MOUSEMOVE: _mousePos = event.mouse; _needsUpdate = true; checkCurrentResource(); break; case Common::EVENT_LBUTTONUP: if (_curResource >= 0) { debug(2, "Sending mouse up event to resource %d\n", _curResource); _resources[_curResource]->handleMouseUp(); } drawResourceImages(); break; case Common::EVENT_LBUTTONDOWN: if (_curResource >= 0) { debug(2, "Sending mouse up event to resource %d\n", _curResource); _resources[_curResource]->handleMouseDown(); } break; case Common::EVENT_KEYDOWN: switch (event.kbd.keycode) { case Common::KEYCODE_d: if (event.kbd.flags & Common::KBD_CTRL) { _console->attach(); _console->onFrame(); } break; case Common::KEYCODE_SPACE: pauseGame(); break; case Common::KEYCODE_F4: _showResourceRects = !_showResourceRects; if (_showResourceRects) drawResourceRects(); break; case Common::KEYCODE_F5: runDialog(*_optionsDialog); break; default: break; } break; default: break; } } if (_needsUpdate) { _system->updateScreen(); _needsUpdate = false; } // Cut down on CPU usage _system->delayMillis(10); } return Common::kNoError; } void MohawkEngine_Myst::changeToStack(uint16 stack) { debug(2, "changeToStack(%d)", stack); _curStack = stack; // If the array is empty, add a new one. Otherwise, delete the first // entry which is the stack file (the second, if there, is the help file). if (_mhk.empty()) _mhk.push_back(new MohawkArchive()); else { delete _mhk[0]; _mhk[0] = new MohawkArchive(); } if (!_mhk[0]->open(mystFiles[_curStack])) error("Could not open %s", mystFiles[_curStack]); if (getPlatform() == Common::kPlatformMacintosh) _gfx->loadExternalPictureFile(_curStack); _runExitScript = false; // Clear the resource cache and the image cache _cache.clear(); _gfx->clearCache(); } void MohawkEngine_Myst::changeToCard(uint16 card) { debug(2, "changeToCard(%d)", card); _scriptParser->disableInitOpcodes(); _video->stopVideos(); // Run exit script from last card (if present) if (_runExitScript) runExitScript(); _runExitScript = true; unloadCard(); // Clear the resource cache and image cache _cache.clear(); _gfx->clearCache(); _curCard = card; // Load a bunch of stuff loadCard(); loadResources(); loadCursorHints(); // Handle images if (_view.conditionalImageCount != 0) { for (uint16 i = 0; i < _view.conditionalImageCount; i++) { if (_varStore->getVar(_view.conditionalImages[i].var) < _view.conditionalImages[i].numStates) _gfx->copyImageToScreen(_view.conditionalImages[i].values[_varStore->getVar(_view.conditionalImages[i].var)], Common::Rect(0, 0, 544, 333)); else warning("Conditional image %d variable %d: %d exceeds maximum state of %d", i, _view.conditionalImages[i].var, _varStore->getVar(_view.conditionalImages[i].var), _view.conditionalImages[i].numStates-1); } } else if (_view.mainImage != 0) _gfx->copyImageToScreen(_view.mainImage, Common::Rect(0, 0, 544, 333)); // Handle sound int16 soundAction = 0; uint16 soundActionVolume = 0; if (_view.sound == kMystSoundActionConditional) { uint16 soundVarValue = _varStore->getVar(_view.soundVar); if (soundVarValue >= _view.soundCount) warning("Conditional sound variable outside range"); else { soundAction = _view.soundList[soundVarValue]; soundActionVolume = _view.soundListVolume[soundVarValue]; } } else { soundAction = _view.sound; soundActionVolume = _view.soundVolume; } // NOTE: Mixer only has 8-bit channel volume granularity, // Myst uses 16-bit? Or is part of this balance? soundActionVolume = (byte)(soundActionVolume / 255); if (soundAction == kMystSoundActionContinue) debug(2, "Continuing with current sound"); else if (soundAction == kMystSoundActionChangeVolume) { debug(2, "Continuing with current sound, changing volume"); // TODO: Implement Volume Control.. } else if (soundAction == kMystSoundActionStop) { debug(2, "Stopping sound"); _sound->stopSound(); } else if (soundAction > 0) { debug(2, "Playing new sound %d", soundAction); _sound->stopSound(); // TODO: Need to keep sound handle and add function to change volume of // looped running sound for kMystSoundActionChangeVolume type // NOTE: All sounds are looped when played via the sound section of the // VIEW resources. _sound->playSound(soundAction, soundActionVolume, true); } else { error("Unknown sound action %d", soundAction); } // Update the images of each area too drawResourceImages(); // TODO: Handle Script Resources // Run the entrance script (if present) runInitScript(); // Make sure we have the right cursor showing _curResource = -1; checkCurrentResource(); // Debug: Show resource rects if (_showResourceRects) drawResourceRects(); } void MohawkEngine_Myst::drawResourceRects() { for (uint16 i = 0; i < _resources.size(); i++) { _resources[i]->getRect().debugPrint(0); if (_resources[i]->getRect().isValidRect()) _gfx->drawRect(_resources[i]->getRect(), _resources[i]->isEnabled()); } _system->updateScreen(); } void MohawkEngine_Myst::checkCurrentResource() { // See what resource we're over bool foundResource = false; for (uint16 i = 0; i < _resources.size(); i++) if (_resources[i]->isEnabled() && _resources[i]->contains(_system->getEventManager()->getMousePos())) { if (_curResource != i) { if (_curResource != -1) _resources[_curResource]->handleMouseLeave(); _resources[i]->handleMouseEnter(); } _curResource = i; foundResource = true; break; } // Set the resource to none if we're not over any if (!foundResource) _curResource = -1; checkCursorHints(); } void MohawkEngine_Myst::loadCard() { debugC(kDebugView, "Loading Card View:"); Common::SeekableReadStream *viewStream = getRawData(ID_VIEW, _curCard); // Card Flags _view.flags = viewStream->readUint16LE(); debugC(kDebugView, "Flags: 0x%04X", _view.flags); // The Image Block (Reminiscent of Riven PLST resources) _view.conditionalImageCount = viewStream->readUint16LE(); debugC(kDebugView, "Conditional Image Count: %d", _view.conditionalImageCount); if (_view.conditionalImageCount != 0) { _view.conditionalImages = new MystCondition[_view.conditionalImageCount]; for (uint16 i = 0; i < _view.conditionalImageCount; i++) { debugC(kDebugView, "\tImage %d:", i); _view.conditionalImages[i].var = viewStream->readUint16LE(); debugC(kDebugView, "\t\tVar: %d", _view.conditionalImages[i].var); _view.conditionalImages[i].numStates = viewStream->readUint16LE(); debugC(kDebugView, "\t\tNumber of States: %d", _view.conditionalImages[i].numStates); _view.conditionalImages[i].values = new uint16[_view.conditionalImages[i].numStates]; for (uint16 j = 0; j < _view.conditionalImages[i].numStates; j++) { _view.conditionalImages[i].values[j] = viewStream->readUint16LE(); debugC(kDebugView, "\t\tState %d -> Value %d", j, _view.conditionalImages[i].values[j]); } } _view.mainImage = 0; } else { _view.mainImage = viewStream->readUint16LE(); debugC(kDebugView, "Main Image: %d", _view.mainImage); } // The Sound Block (Reminiscent of Riven SLST resources) _view.sound = viewStream->readSint16LE(); debugCN(kDebugView, "Sound Control: %d = ", _view.sound); if (_view.sound > 0) { debugC(kDebugView, "Play new Sound, change volume"); debugC(kDebugView, "\tSound: %d", _view.sound); _view.soundVolume = viewStream->readUint16LE(); debugC(kDebugView, "\tVolume: %d", _view.soundVolume); } else if (_view.sound == kMystSoundActionContinue) debugC(kDebugView, "Continue current sound"); else if (_view.sound == kMystSoundActionChangeVolume) { debugC(kDebugView, "Continue current sound, change volume"); _view.soundVolume = viewStream->readUint16LE(); debugC(kDebugView, "\tVolume: %d", _view.soundVolume); } else if (_view.sound == kMystSoundActionStop) { debugC(kDebugView, "Stop sound"); } else if (_view.sound == kMystSoundActionConditional) { debugC(kDebugView, "Conditional sound list"); _view.soundVar = viewStream->readUint16LE(); debugC(kDebugView, "\tVar: %d", _view.soundVar); _view.soundCount = viewStream->readUint16LE(); debugC(kDebugView, "\tCount: %d", _view.soundCount); _view.soundList = new int16[_view.soundCount]; _view.soundListVolume = new uint16[_view.soundCount]; for (uint16 i = 0; i < _view.soundCount; i++) { _view.soundList[i] = viewStream->readSint16LE(); debugC(kDebugView, "\t\tCondition %d: Action %d", i, _view.soundList[i]); if (_view.soundList[i] == kMystSoundActionChangeVolume || _view.soundList[i] >= 0) { _view.soundListVolume[i] = viewStream->readUint16LE(); debugC(kDebugView, "\t\tCondition %d: Volume %d", i, _view.soundListVolume[i]); } } } else { debugC(kDebugView, "Unknown"); warning("Unknown sound control value in card"); } // Resources that scripts can call upon _view.scriptResCount = viewStream->readUint16LE(); debugC(kDebugView, "Script Resource Count: %d", _view.scriptResCount); if (_view.scriptResCount != 0) { _view.scriptResources = new MystView::ScriptResource[_view.scriptResCount]; for (uint16 i = 0; i < _view.scriptResCount; i++) { debugC(kDebugView, "\tResource %d:", i); _view.scriptResources[i].type = viewStream->readUint16LE(); debugC(kDebugView, "\t\t Type: %d", _view.scriptResources[i].type); switch (_view.scriptResources[i].type) { case 1: debugC(kDebugView, "\t\t\t\t= Image"); break; case 2: debugC(kDebugView, "\t\t\t\t= Sound"); break; case 3: debugC(kDebugView, "\t\t\t\t= Resource List"); break; default: debugC(kDebugView, "\t\t\t\t= Unknown"); break; } if (_view.scriptResources[i].type == 3) { _view.scriptResources[i].var = viewStream->readUint16LE(); debugC(kDebugView, "\t\t Var: %d", _view.scriptResources[i].var); _view.scriptResources[i].count = viewStream->readUint16LE(); debugC(kDebugView, "\t\t Resource List Count: %d", _view.scriptResources[i].count); _view.scriptResources[i].u0 = viewStream->readUint16LE(); debugC(kDebugView, "\t\t u0: %d", _view.scriptResources[i].u0); _view.scriptResources[i].resource_list = new int16[_view.scriptResources[i].count]; for (uint16 j = 0; j < _view.scriptResources[i].count; j++) { _view.scriptResources[i].resource_list[j] = viewStream->readSint16LE(); debugC(kDebugView, "\t\t Resource List %d: %d", j, _view.scriptResources[i].resource_list[j]); } } else { _view.scriptResources[i].resource_list = NULL; _view.scriptResources[i].id = viewStream->readUint16LE(); debugC(kDebugView, "\t\t Id: %d", _view.scriptResources[i].id); } } } // Identifiers for other resources. 0 if non existent. There is always an RLST. _view.rlst = viewStream->readUint16LE(); if (!_view.rlst) error("RLST Index missing"); _view.hint = viewStream->readUint16LE(); _view.init = viewStream->readUint16LE(); _view.exit = viewStream->readUint16LE(); delete viewStream; // Precache Card Resources // TODO: Deal with Mac ME External Picture File uint32 cacheImageType; if (getFeatures() & GF_ME) cacheImageType = ID_PICT; else cacheImageType = ID_WDIB; // Precache Image Block data if (_view.conditionalImageCount != 0) { for (uint16 i = 0; i < _view.conditionalImageCount; i++) for (uint16 j = 0; j < _view.conditionalImages[i].numStates; j++) cachePreload(cacheImageType, _view.conditionalImages[i].values[j]); } else cachePreload(cacheImageType, _view.mainImage); // Precache Sound Block data if (_view.sound > 0) cachePreload(ID_MSND, _view.sound); else if (_view.sound == kMystSoundActionConditional) { for (uint16 i = 0; i < _view.soundCount; i++) { if (_view.soundList[i] > 0) cachePreload(ID_MSND, _view.soundList[i]); } } // Precache Script Resources if (_view.scriptResCount != 0) { for (uint16 i = 0; i < _view.scriptResCount; i++) { switch (_view.scriptResources[i].type) { case 1: cachePreload(cacheImageType, _view.scriptResources[i].id); break; case 2: cachePreload(ID_MSND, _view.scriptResources[i].id); break; case 3: warning("TODO: Precaching of Script Resource List not supported"); break; default: warning("Unknown Resource in Script Resource List Precaching"); break; } } } } void MohawkEngine_Myst::unloadCard() { for (uint16 i = 0; i < _view.conditionalImageCount; i++) delete[] _view.conditionalImages[i].values; delete[] _view.conditionalImages; _view.conditionalImageCount = 0; _view.conditionalImages = NULL; delete[] _view.soundList; _view.soundList = NULL; delete[] _view.soundListVolume; _view.soundListVolume = NULL; for (uint16 i = 0; i < _view.scriptResCount; i++) delete[] _view.scriptResources[i].resource_list; delete[] _view.scriptResources; _view.scriptResources = NULL; _view.scriptResCount = 0; } void MohawkEngine_Myst::runInitScript() { if (!_view.init) { debugC(kDebugINIT, "No INIT Present"); return; } debugC(kDebugINIT, "Running INIT script"); Common::SeekableReadStream *initStream = getRawData(ID_INIT, _view.init); MystScript script = _scriptParser->readScript(initStream, kMystScriptInit); delete initStream; _scriptParser->runScript(script); _gfx->updateScreen(); } void MohawkEngine_Myst::runExitScript() { if (!_view.exit) { debugC(kDebugEXIT, "No EXIT Present"); return; } debugC(kDebugEXIT, "Running EXIT script"); Common::SeekableReadStream *exitStream = getRawData(ID_EXIT, _view.exit); MystScript script = _scriptParser->readScript(exitStream, kMystScriptExit); delete exitStream; _scriptParser->runScript(script); _gfx->updateScreen(); } void MohawkEngine_Myst::loadHelp(uint16 id) { // The original version did not have the help system if (!(getFeatures() & GF_ME)) return; // TODO: Help File contains 5 cards i.e. VIEW, RLST, etc. // in addition to HELP resources. // These are Ids 9930 to 9934 // Need to deal with loading and displaying these.. // Current engine structure only supports display of // card from primary stack MHK debugC(kDebugHelp, "Loading Help System Data"); Common::SeekableReadStream *helpStream = getRawData(ID_HELP, id); uint16 count = helpStream->readUint16LE(); uint16 *u0 = new uint16[count]; Common::String helpText; debugC(kDebugHelp, "\tcount: %d", count); for (uint16 i = 0; i < count; i++) { u0[i] = helpStream->readUint16LE(); debugC(kDebugHelp, "\tu0[%d]: %d", i, u0[i]); } // TODO: Previous values i.e. u0[0] to u0[count - 2] // appear to be resource ids in the help.dat file.. if (u0[count - 1] != count) warning("loadHelp(): last u0 value is not equal to count"); do { helpText += helpStream->readByte(); } while (helpText.lastChar() != 0); helpText.deleteLastChar(); debugC(kDebugHelp, "\thelpText: \"%s\"", helpText.c_str()); delete[] u0; } void MohawkEngine_Myst::loadCursorHints() { for (uint16 i = 0; i < _cursorHintCount; i++) delete[] _cursorHints[i].variableHint.values; _cursorHintCount = 0; delete[] _cursorHints; _cursorHints = NULL; if (!_view.hint) { debugC(kDebugHint, "No HINT Present"); return; } debugC(kDebugHint, "Loading Cursor Hints:"); Common::SeekableReadStream *hintStream = getRawData(ID_HINT, _curCard); _cursorHintCount = hintStream->readUint16LE(); debugC(kDebugHint, "Cursor Hint Count: %d", _cursorHintCount); _cursorHints = new MystCursorHint[_cursorHintCount]; for (uint16 i = 0; i < _cursorHintCount; i++) { debugC(kDebugHint, "Cursor Hint %d:", i); _cursorHints[i].id = hintStream->readUint16LE(); debugC(kDebugHint, "\tId: %d", _cursorHints[i].id); _cursorHints[i].cursor = hintStream->readSint16LE(); debugC(kDebugHint, "\tCursor: %d", _cursorHints[i].cursor); if (_cursorHints[i].cursor == -1) { debugC(kDebugHint, "\tConditional Cursor Hints:"); _cursorHints[i].variableHint.var = hintStream->readUint16LE(); debugC(kDebugHint, "\tVar: %d", _cursorHints[i].variableHint.var); _cursorHints[i].variableHint.numStates = hintStream->readUint16LE(); debugC(kDebugHint, "\tNumber of States: %d", _cursorHints[i].variableHint.numStates); _cursorHints[i].variableHint.values = new uint16[_cursorHints[i].variableHint.numStates]; for (uint16 j = 0; j < _cursorHints[i].variableHint.numStates; j++) { _cursorHints[i].variableHint.values[j] = hintStream->readUint16LE(); debugC(kDebugHint, "\t\t State %d: Cursor %d", j, _cursorHints[i].variableHint.values[j]); } } else { _cursorHints[i].variableHint.var = 0; _cursorHints[i].variableHint.numStates = 0; _cursorHints[i].variableHint.values = NULL; } } delete hintStream; } void MohawkEngine_Myst::setMainCursor(uint16 cursor) { _currentCursor = _mainCursor = cursor; _gfx->changeCursor(_currentCursor); } void MohawkEngine_Myst::checkCursorHints() { if (!_view.hint) return; // Check all the cursor hints to see if we're in a hotspot that contains a hint. for (uint16 i = 0; i < _cursorHintCount; i++) if (_cursorHints[i].id == _curResource && _resources[_cursorHints[i].id]->isEnabled()) { if (_cursorHints[i].cursor == -1) { uint16 var_value = _varStore->getVar(_cursorHints[i].variableHint.var); if (var_value >= _cursorHints[i].variableHint.numStates) warning("Variable %d Out of Range in variable HINT Resource %d", _cursorHints[i].variableHint.var, i); else { _currentCursor = _cursorHints[i].variableHint.values[var_value]; if (_currentCursor == 0) _currentCursor = _mainCursor; _gfx->changeCursor(_currentCursor); } } else if (_currentCursor != _cursorHints[i].cursor) { if (_cursorHints[i].cursor == 0) _currentCursor = _mainCursor; else _currentCursor = _cursorHints[i].cursor; _gfx->changeCursor(_currentCursor); } return; } if (_currentCursor != _mainCursor) { _currentCursor = _mainCursor; _gfx->changeCursor(_currentCursor); } } void MohawkEngine_Myst::setResourceEnabled(uint16 resourceId, bool enable) { if (resourceId < _resources.size()) { _resources[resourceId]->setEnabled(enable); } else warning("Attempt to change unknown resource enable state"); } void MohawkEngine_Myst::drawResourceImages() { for (uint16 i = 0; i < _resources.size(); i++) if (_resources[i]->isEnabled()) _resources[i]->drawDataToScreen(); // Make sure the screen is updated _gfx->updateScreen(); } static MystResource *loadResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) { uint16 type = rlstStream->readUint16LE(); debugC(kDebugResource, "\tType: %d", type); debugC(kDebugResource, "\tSub_Record: %d", (parent == NULL) ? 0 : 1); switch (type) { case kMystForwardResource: case kMystLeftResource: case kMystRightResource: case kMystDownResource: case kMystUpResource: case 14: // TODO: kMystBackwardResource? return new MystResource(vm, rlstStream, parent); case kMystActionResource: return new MystResourceType5(vm, rlstStream, parent); case kMystVideoResource: return new MystResourceType6(vm, rlstStream, parent); case kMystSwitchResource: return new MystResourceType7(vm, rlstStream, parent); case 8: return new MystResourceType8(vm, rlstStream, parent); case 10: return new MystResourceType10(vm, rlstStream, parent); case 11: return new MystResourceType11(vm, rlstStream, parent); case 12: return new MystResourceType12(vm, rlstStream, parent); case 13: return new MystResourceType13(vm, rlstStream, parent); default: error ("Unknown/Unhandled MystResource type %d", type); } } void MohawkEngine_Myst::loadResources() { while(!_resources.empty()) { MystResource *temp = _resources.back(); _resources.pop_back(); delete temp; } if (!_view.rlst) { debugC(kDebugResource, "No RLST present"); return; } Common::SeekableReadStream *rlstStream = getRawData(ID_RLST, _view.rlst); uint16 resourceCount = rlstStream->readUint16LE(); debugC(kDebugResource, "RLST Resource Count: %d", resourceCount); for (uint16 i = 0; i < resourceCount; i++) { debugC(kDebugResource, "Resource #%d:", i); _resources.push_back(loadResource(this, rlstStream, NULL)); } delete rlstStream; } MystResource::MystResource(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) { _vm = vm; _parent = parent; if (parent == NULL) { _flags = rlstStream->readUint16LE(); _rect.left = rlstStream->readSint16LE(); _rect.top = rlstStream->readSint16LE(); if (_rect.top == -1) { warning("Invalid _rect.top of -1 found - clamping to 0"); _rect.top = 0; } _rect.right = rlstStream->readSint16LE(); _rect.bottom = rlstStream->readSint16LE(); _dest = rlstStream->readUint16LE(); } else { _flags = parent->_flags; _rect.left = parent->_rect.left; _rect.top = parent->_rect.top; _rect.right = parent->_rect.right; _rect.bottom = parent->_rect.bottom; _dest = parent->_dest; } debugC(kDebugResource, "\tflags: 0x%04X", _flags); debugC(kDebugResource, "\tleft: %d", _rect.left); debugC(kDebugResource, "\ttop: %d", _rect.top); debugC(kDebugResource, "\tright: %d", _rect.right); debugC(kDebugResource, "\tbottom: %d", _rect.bottom); debugC(kDebugResource, "\tdest: %d", _dest); // Default Enable based on flags... if (_vm->_zipMode) _enabled = (_flags & kMystZipModeEnableFlag) != 0 || (_flags & kMystHotspotEnableFlag) != 0 || (_flags & kMystSubimageEnableFlag) != 0; else _enabled = (_flags & kMystZipModeEnableFlag) == 0 && ((_flags & kMystHotspotEnableFlag) != 0 || (_flags & kMystSubimageEnableFlag) != 0); } MystResource::~MystResource() { } void MystResource::handleMouseUp() { if (_dest != 0) _vm->changeToCard(_dest); else warning("Movement type resource with null destination at position (%d, %d), (%d, %d)", _rect.left, _rect.top, _rect.right, _rect.bottom); } MystResourceType5::MystResourceType5(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) { debugC(kDebugResource, "\tResource Type 5 Script:"); _script = vm->_scriptParser->readScript(rlstStream, kMystScriptNormal); } void MystResourceType5::handleMouseUp() { _vm->_scriptParser->runScript(_script, this); } // In Myst/Making of Myst, the paths are hardcoded ala Windows style without extension. Convert them. Common::String MystResourceType6::convertMystVideoName(const Common::String &name) { Common::String temp; for (uint32 i = 1; i < name.size(); i++) { if (name[i] == '\\') temp += '/'; else temp += name[i]; } return temp + ".mov"; } MystResourceType6::MystResourceType6(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType5(vm, rlstStream, parent) { char c = 0; do { c = rlstStream->readByte(); _videoFile += c; } while (c); rlstStream->skip(_videoFile.size() & 1); // Trim method does not remove extra trailing nulls while (_videoFile.size() != 0 && _videoFile.lastChar() == 0) _videoFile.deleteLastChar(); _videoFile = convertMystVideoName(_videoFile); // Position values require modulus 10000 to keep in sane range. _left = rlstStream->readUint16LE() % 10000; _top = rlstStream->readUint16LE() % 10000; _loop = rlstStream->readUint16LE(); _u0 = rlstStream->readUint16LE(); _playBlocking = rlstStream->readUint16LE(); _playOnCardChange = rlstStream->readUint16LE(); _u3 = rlstStream->readUint16LE(); if (_u0 != 1) warning("Type 6 _u0 != 1"); if (_u3 != 0) warning("Type 6 _u3 != 0"); debugC(kDebugResource, "\tvideoFile: \"%s\"", _videoFile.c_str()); debugC(kDebugResource, "\tleft: %d", _left); debugC(kDebugResource, "\ttop: %d", _top); debugC(kDebugResource, "\tloop: %d", _loop); debugC(kDebugResource, "\tu0: %d", _u0); debugC(kDebugResource, "\tplayBlocking: %d", _playBlocking); debugC(kDebugResource, "\tplayOnCardChange: %d", _playOnCardChange); debugC(kDebugResource, "\tu3: %d", _u3); _videoRunning = false; } void MystResourceType6::handleAnimation() { // TODO: Implement Code to allow _playOnCardChange when set // and trigger by Opcode 9 when clear if (!_videoRunning) { // NOTE: The left and top coordinates are often incorrect and do not make sense. // We use the rect coordinates here instead. if (_playBlocking) _vm->_video->playMovie(_videoFile, _rect.left, _rect.top); else _vm->_video->playBackgroundMovie(_videoFile, _rect.left, _rect.top, _loop); _videoRunning = true; } } MystResourceType7::MystResourceType7(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) { _var7 = rlstStream->readUint16LE(); _numSubResources = rlstStream->readUint16LE(); debugC(kDebugResource, "\tvar7: %d", _var7); debugC(kDebugResource, "\tnumSubResources: %d", _numSubResources); for (uint16 i = 0; i < _numSubResources; i++) _subResources.push_back(loadResource(vm, rlstStream, this)); } MystResourceType7::~MystResourceType7() { while(!_subResources.empty()) { MystResource *temp = _subResources.back(); _subResources.pop_back(); delete temp; } } // TODO: All these functions to switch subresource are very similar. // Find way to share code (function pointer pass?) void MystResourceType7::drawDataToScreen() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->drawDataToScreen(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->drawDataToScreen(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->drawDataToScreen(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } void MystResourceType7::handleAnimation() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->handleAnimation(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->handleAnimation(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->handleAnimation(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } void MystResourceType7::handleMouseUp() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->handleMouseUp(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->handleMouseUp(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->handleMouseUp(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } void MystResourceType7::handleMouseDown() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->handleMouseDown(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->handleMouseDown(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->handleMouseDown(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } void MystResourceType7::handleMouseEnter() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->handleMouseEnter(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->handleMouseEnter(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->handleMouseEnter(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } void MystResourceType7::handleMouseLeave() { if (_var7 == 0xFFFF) { if (_numSubResources == 1) _subResources[0]->handleMouseLeave(); else if (_numSubResources != 0) warning("Type 7 Resource with _numSubResources of %d, but no control variable", _numSubResources); } else { uint16 varValue = _vm->_varStore->getVar(_var7); if (_numSubResources == 1 && varValue != 0) _subResources[0]->handleMouseLeave(); else if (_numSubResources != 0) { if (varValue < _numSubResources) _subResources[varValue]->handleMouseLeave(); else warning("Type 7 Resource Var %d: %d exceeds number of sub resources %d", _var7, varValue, _numSubResources); } } } MystResourceType8::MystResourceType8(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType7(vm, rlstStream, parent) { _var8 = rlstStream->readUint16LE(); _numSubImages = rlstStream->readUint16LE(); debugC(kDebugResource, "\tvar8: %d", _var8); debugC(kDebugResource, "\tnumSubImages: %d", _numSubImages); _subImages = new MystResourceType8::SubImage[_numSubImages]; for (uint16 i = 0; i < _numSubImages; i++) { debugC(kDebugResource, "\tSubimage %d:", i); _subImages[i].wdib = rlstStream->readUint16LE(); _subImages[i].rect.left = rlstStream->readSint16LE(); if (_subImages[i].rect.left != -1) { _subImages[i].rect.top = rlstStream->readSint16LE(); _subImages[i].rect.right = rlstStream->readSint16LE(); _subImages[i].rect.bottom = rlstStream->readSint16LE(); } else { // Use the hotspot rect as the source rect since the subimage is fullscreen // Convert to bitmap coordinates (upside down) _subImages[i].rect.left = _rect.left; _subImages[i].rect.top = 333 - _rect.bottom; _subImages[i].rect.right = _rect.right; _subImages[i].rect.bottom = 333 - _rect.top; } debugC(kDebugResource, "\twdib: %d", _subImages[i].wdib); debugC(kDebugResource, "\tleft: %d", _subImages[i].rect.left); debugC(kDebugResource, "\ttop: %d", _subImages[i].rect.top); debugC(kDebugResource, "\tright: %d", _subImages[i].rect.right); debugC(kDebugResource, "\tbottom: %d", _subImages[i].rect.bottom); } } MystResourceType8::~MystResourceType8() { delete[] _subImages; } void MystResourceType8::drawDataToScreen() { // Need to call overidden Type 7 function to ensure // switch section is processed correctly. MystResourceType7::drawDataToScreen(); bool drawSubImage = false; int16 subImageId = 0; if (_var8 == 0xFFFF) { if (_numSubImages == 1) { subImageId = 0; drawSubImage = true; } else if (_numSubImages != 0) warning("Type 8 Resource with _numSubImages of %d, but no control variable", _numSubImages); } else { uint16 varValue = _vm->_varStore->getVar(_var8); if (_numSubImages == 1 && varValue != 0) { subImageId = 0; drawSubImage = true; } else if (_numSubImages != 0) { if (varValue < _numSubImages) { subImageId = varValue; drawSubImage = true; } else warning("Type 8 Image Var %d: %d exceeds number of subImages %d", _var8, varValue, _numSubImages); } } if (drawSubImage) { uint16 imageToDraw = 0; if (_subImages[subImageId].wdib == 0xFFFF) { // TODO: Think the reason for problematic screen updates in some rects is that they // are these -1 cases. // They need to be redrawn i.e. if the Myst marker switches are changed, but I don't think // the rects are valid. This does not matter in the original engine as the screen update redraws // the VIEW images, followed by the RLST resource images, and -1 for the WDIB is interpreted as // "Do Not Draw Image" i.e so the VIEW image is shown through.. We need to fix screen update // to do this same behaviour. if (_vm->_view.conditionalImageCount == 0) imageToDraw = _vm->_view.mainImage; else { for (uint16 i = 0; i < _vm->_view.conditionalImageCount; i++) if (_vm->_varStore->getVar(_vm->_view.conditionalImages[i].var) < _vm->_view.conditionalImages[i].numStates) imageToDraw = _vm->_view.conditionalImages[i].values[_vm->_varStore->getVar(_vm->_view.conditionalImages[i].var)]; } } else imageToDraw = _subImages[subImageId].wdib; _vm->_gfx->copyImageSectionToScreen(imageToDraw, _subImages[subImageId].rect, _rect); } } uint16 MystResourceType8::getType8Var() { return _var8; } // No MystResourceType9! MystResourceType10::MystResourceType10(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType8(vm, rlstStream, parent) { _kind = rlstStream->readUint16LE(); // NOTE: l,r,t,b differs from standard l,t,r,b order _rect10.left = rlstStream->readUint16LE(); _rect10.right = rlstStream->readUint16LE(); _rect10.top = rlstStream->readUint16LE(); _rect10.bottom = rlstStream->readUint16LE(); _u0 = rlstStream->readUint16LE(); _u1 = rlstStream->readUint16LE(); _mouseDownOpcode = rlstStream->readUint16LE(); _mouseDragOpcode = rlstStream->readUint16LE(); _mouseUpOpcode = rlstStream->readUint16LE(); // TODO: Need to work out meaning of kind... debugC(kDebugResource, "\tkind: %d", _kind); debugC(kDebugResource, "\trect10.left: %d", _rect10.left); debugC(kDebugResource, "\trect10.right: %d", _rect10.right); debugC(kDebugResource, "\trect10.top: %d", _rect10.top); debugC(kDebugResource, "\trect10.bottom: %d", _rect10.bottom); debugC(kDebugResource, "\tu0: %d", _u0); debugC(kDebugResource, "\tu1: %d", _u1); debugC(kDebugResource, "\t_mouseDownOpcode: %d", _mouseDownOpcode); debugC(kDebugResource, "\t_mouseDragOpcode: %d", _mouseDragOpcode); debugC(kDebugResource, "\t_mouseUpOpcode: %d", _mouseUpOpcode); // TODO: Think that u0 and u1 are unused in Type 10 if (_u0) warning("Type 10 u0 non-zero"); if (_u1) warning("Type 10 u1 non-zero"); // TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up // Or whether this is slightly different... debugCN(kDebugResource, "Type 10 _mouseDownOpcode: %d\n", _mouseDownOpcode); debugCN(kDebugResource, "Type 10 _mouseDragOpcode: %d\n", _mouseDragOpcode); debugCN(kDebugResource, "Type 10 _mouseUpOpcode: %d\n", _mouseUpOpcode); for (byte i = 0; i < 4; i++) { debugC(kDebugResource, "\tList %d:", i); _lists[i].listCount = rlstStream->readUint16LE(); debugC(kDebugResource, "\t%d values", _lists[i].listCount); _lists[i].list = new uint16[_lists[i].listCount]; for (uint16 j = 0; j < _lists[i].listCount; j++) { _lists[i].list[j] = rlstStream->readUint16LE(); debugC(kDebugResource, "\tValue %d: %d", j, _lists[i].list[j]); } } warning("TODO: Card contains Type 10 Resource - Function not yet implemented"); } MystResourceType10::~MystResourceType10() { for (byte i = 0; i < 4; i++) delete[] _lists[i].list; } void MystResourceType10::handleMouseUp() { // TODO } MystResourceType11::MystResourceType11(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType8(vm, rlstStream, parent) { _kind = rlstStream->readUint16LE(); // NOTE: l,r,t,b differs from standard l,t,r,b order _rect11.left = rlstStream->readUint16LE(); _rect11.right = rlstStream->readUint16LE(); _rect11.top = rlstStream->readUint16LE(); _rect11.bottom = rlstStream->readUint16LE(); _u0 = rlstStream->readUint16LE(); _u1 = rlstStream->readUint16LE(); _mouseDownOpcode = rlstStream->readUint16LE(); _mouseDragOpcode = rlstStream->readUint16LE(); _mouseUpOpcode = rlstStream->readUint16LE(); debugC(kDebugResource, "\tkind: %d", _kind); debugC(kDebugResource, "\trect11.left: %d", _rect11.left); debugC(kDebugResource, "\trect11.right: %d", _rect11.right); debugC(kDebugResource, "\trect11.top: %d", _rect11.top); debugC(kDebugResource, "\trect11.bottom: %d", _rect11.bottom); debugC(kDebugResource, "\tu0: %d", _u0); debugC(kDebugResource, "\tu1: %d", _u1); debugC(kDebugResource, "\t_mouseDownOpcode: %d", _mouseDownOpcode); debugC(kDebugResource, "\t_mouseDragOpcode: %d", _mouseDragOpcode); debugC(kDebugResource, "\t_mouseUpOpcode: %d", _mouseUpOpcode); // TODO: Think that u0 and u1 are unused in Type 11 if (_u0) warning("Type 11 u0 non-zero"); if (_u1) warning("Type 11 u1 non-zero"); // TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up // Or whether this is slightly different... debugCN(kDebugResource, "Type 11 _mouseDownOpcode: %d\n", _mouseDownOpcode); debugCN(kDebugResource, "Type 11 _mouseDragOpcode: %d\n", _mouseDragOpcode); debugCN(kDebugResource, "Type 11 _mouseUpOpcode: %d\n", _mouseUpOpcode); for (byte i = 0; i < 3; i++) { debugC(kDebugResource, "\tList %d:", i); _lists[i].listCount = rlstStream->readUint16LE(); debugC(kDebugResource, "\t%d values", _lists[i].listCount); _lists[i].list = new uint16[_lists[i].listCount]; for (uint16 j = 0; j < _lists[i].listCount; j++) { _lists[i].list[j] = rlstStream->readUint16LE(); debugC(kDebugResource, "\tValue %d: %d", j, _lists[i].list[j]); } } warning("TODO: Card contains Type 11 Resource - Function not yet implemented"); } MystResourceType11::~MystResourceType11() { for (byte i = 0; i < 3; i++) delete[] _lists[i].list; } void MystResourceType11::handleMouseUp() { // TODO // HACK: Myst Card 4059 (Fireplace Code Book) to usuable state if (_mouseDownOpcode == 191) { uint16 tmp = _vm->_varStore->getVar(0); if (tmp > 0) _vm->_varStore->setVar(0, tmp - 1); } else if (_mouseDownOpcode == 190) { _vm->_varStore->setVar(0, _vm->_varStore->getVar(0) + 1); } } MystResourceType12::MystResourceType12(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResourceType8(vm, rlstStream, parent) { _kind = rlstStream->readUint16LE(); // NOTE: l,r,t,b differs from standard l,t,r,b order _rect11.left = rlstStream->readUint16LE(); _rect11.right = rlstStream->readUint16LE(); _rect11.top = rlstStream->readUint16LE(); _rect11.bottom = rlstStream->readUint16LE(); _state0Frame = rlstStream->readUint16LE(); _state1Frame = rlstStream->readUint16LE(); _mouseDownOpcode = rlstStream->readUint16LE(); _mouseDragOpcode = rlstStream->readUint16LE(); _mouseUpOpcode = rlstStream->readUint16LE(); debugC(kDebugResource, "\tkind: %d", _kind); debugC(kDebugResource, "\trect11.left: %d", _rect11.left); debugC(kDebugResource, "\trect11.right: %d", _rect11.right); debugC(kDebugResource, "\trect11.top: %d", _rect11.top); debugC(kDebugResource, "\trect11.bottom: %d", _rect11.bottom); debugC(kDebugResource, "\t_state0Frame: %d", _state0Frame); debugC(kDebugResource, "\t_state1Frame: %d", _state1Frame); debugC(kDebugResource, "\t_mouseDownOpcode: %d", _mouseDownOpcode); debugC(kDebugResource, "\t_mouseDragOpcode: %d", _mouseDragOpcode); debugC(kDebugResource, "\t_mouseUpOpcode: %d", _mouseUpOpcode); // TODO: Think that u0 and u1 are animation frames to be // drawn for var == 0 and var == 1 debugCN(kDebugResource, "Type 12 _state0Frame: %d\n", _state0Frame); debugCN(kDebugResource, "Type 12 _state1Frame: %d\n", _state1Frame); // TODO: Not sure about order of Mouse Down, Mouse Drag and Mouse Up // Or whether this is slightly different... debugCN(kDebugResource, "Type 12 _mouseDownOpcode: %d\n", _mouseDownOpcode); debugCN(kDebugResource, "Type 12 _mouseDragOpcode: %d\n", _mouseDragOpcode); debugCN(kDebugResource, "Type 12 _mouseUpOpcode: %d\n", _mouseUpOpcode); for (byte i = 0; i < 3; i++) { debugC(kDebugResource, "\tList %d:", i); _lists[i].listCount = rlstStream->readUint16LE(); debugC(kDebugResource, "\t%d values", _lists[i].listCount); _lists[i].list = new uint16[_lists[i].listCount]; for (uint16 j = 0; j < _lists[i].listCount; j++) { _lists[i].list[j] = rlstStream->readUint16LE(); debugC(kDebugResource, "\tValue %d: %d", j, _lists[i].list[j]); } } warning("TODO: Card contains Type 12, Type 11 section Resource - Function not yet implemented"); _numFrames = rlstStream->readUint16LE(); _firstFrame = rlstStream->readUint16LE(); uint16 frameWidth = rlstStream->readUint16LE(); uint16 frameHeight = rlstStream->readUint16LE(); _frameRect.left = rlstStream->readUint16LE(); _frameRect.top = rlstStream->readUint16LE(); _frameRect.right = _frameRect.left + frameWidth; _frameRect.bottom = _frameRect.top + frameHeight; debugC(kDebugResource, "\t_numFrames: %d", _numFrames); debugC(kDebugResource, "\t_firstFrame: %d", _firstFrame); debugC(kDebugResource, "\tframeWidth: %d", frameWidth); debugC(kDebugResource, "\tframeHeight: %d", frameHeight); debugC(kDebugResource, "\t_frameRect.left: %d", _frameRect.left); debugC(kDebugResource, "\t_frameRect.top: %d", _frameRect.top); debugC(kDebugResource, "\t_frameRect.right: %d", _frameRect.right); debugC(kDebugResource, "\t_frameRect.bottom: %d", _frameRect.bottom); _doAnimation = false; } MystResourceType12::~MystResourceType12() { for (byte i = 0; i < 3; i++) delete[] _lists[i].list; } void MystResourceType12::handleAnimation() { // TODO: Probably not final version. Variable/Type 11 Controlled? if (_doAnimation) { _vm->_gfx->copyImageToScreen(_currentFrame++, _frameRect); _vm->_gfx->updateScreen(); if ((_currentFrame - _firstFrame) >= _numFrames) _doAnimation = false; } } void MystResourceType12::handleMouseUp() { // HACK/TODO: Trigger Animation on Mouse Click. Probably not final version. Variable/Type 11 Controlled? _currentFrame = _firstFrame; _doAnimation = true; } MystResourceType13::MystResourceType13(MohawkEngine_Myst *vm, Common::SeekableReadStream *rlstStream, MystResource *parent) : MystResource(vm, rlstStream, parent) { _enterOpcode = rlstStream->readUint16LE(); _leaveOpcode = rlstStream->readUint16LE(); debugC(kDebugResource, "\t_enterOpcode: %d", _enterOpcode); debugC(kDebugResource, "\t_leaveOpcode: %d", _leaveOpcode); } void MystResourceType13::handleMouseEnter() { // Pass along the enter opcode (with no parameters) to the script parser _vm->_scriptParser->runOpcode(_enterOpcode); } void MystResourceType13::handleMouseLeave() { // Pass along the leave opcode (with no parameters) to the script parser _vm->_scriptParser->runOpcode(_leaveOpcode); } void MystResourceType13::handleMouseUp() { // Type 13 Resources do nothing on Mouse Clicks. // This is required to override the inherited default // i.e. MystResource::handleMouseUp } void MohawkEngine_Myst::runLoadDialog() { runDialog(*_loadDialog); } Common::Error MohawkEngine_Myst::loadGameState(int slot) { if (_saveLoad->loadGame(_saveLoad->generateSaveGameList()[slot])) { changeToStack(kIntroStack); changeToCard(5); return Common::kNoError; } else return Common::kUnknownError; } Common::Error MohawkEngine_Myst::saveGameState(int slot, const char *desc) { Common::StringArray saveList = _saveLoad->generateSaveGameList(); if ((uint)slot < saveList.size()) _saveLoad->deleteSave(saveList[slot]); return _saveLoad->saveGame(Common::String(desc)) ? Common::kNoError : Common::kUnknownError; } } // End of namespace Mohawk