scummvm/engines/mohawk/myst.cpp
David Turner 2ab3e0fb3c Mohawk : Adding resource cache class and integrating into Myst engine.
The resource caching trades increased memory usage against disk access.

This functionality can be disabled and enabled by the console "cache" command.

svn-id: r48080
2010-02-17 19:59:08 +00:00

1605 lines
54 KiB
C++

/* 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 "mohawk/graphics.h"
#include "mohawk/myst.h"
#include "mohawk/myst_scripts.h"
#include "mohawk/myst_saveload.h"
#include "mohawk/dialogs.h"
#include "mohawk/resource.h"
#include "mohawk/resource_cache.h"
#include "mohawk/video/video.h"
namespace Mohawk {
MohawkEngine_Myst::MohawkEngine_Myst(OSystem *syst, const MohawkGameDescription *gamedesc) : MohawkEngine(syst, gamedesc) {
Common::addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses");
Common::addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
Common::addDebugChannel(kDebugView, "View", "Track Card File (VIEW) Parsing");
Common::addDebugChannel(kDebugHint, "Hint", "Track Cursor Hints (HINT) Parsing");
Common::addDebugChannel(kDebugResource, "Resource", "Track Resource (RLST) Parsing");
Common::addDebugChannel(kDebugINIT, "Init", "Track Card Init Script (INIT) Parsing");
Common::addDebugChannel(kDebugEXIT, "Exit", "Track Card Exit Script (EXIT) Parsing");
Common::addDebugChannel(kDebugScript, "Script", "Track Script Execution");
Common::addDebugChannel(kDebugHelp, "Help", "Track Help File (HELP) Parsing");
Common::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;
_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)
SearchMan.addSubDirectoryMatching(_gameDataDir, "CD Data");
}
MohawkEngine_Myst::~MohawkEngine_Myst() {
delete _gfx;
delete _console;
delete _scriptParser;
delete _varStore;
delete _saveLoad;
delete _loadDialog;
delete _optionsDialog;
delete[] _view.conditionalImages;
delete[] _view.scriptResources;
delete[] _cursorHints;
_resources.clear();
}
// Uses cached data objects in preference to disk access
Common::SeekableReadStream *MohawkEngine_Myst::getRawData(uint32 tag, uint16 id) {
Common::SeekableReadStream *ret;
ret = _cache.search(tag, id);
if(ret != NULL) return ret;
for (uint32 i = 0; i < _mhk.size(); i++)
if (_mhk[i]->hasResource(tag, id)) {
ret = _mhk[i]->getRawData(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) {
Common::SeekableReadStream *tempData;
if (!_cache.enabled) return;
for (uint32 i = 0; i < _mhk.size(); i++)
if (_mhk[i]->hasResource(tag, id)) {
tempData = _mhk[i]->getRawData(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(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::StringList 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();
mhk->open("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();
}
for (uint16 i = 0; i < _resources.size(); i++)
if (_resources[i]->isEnabled())
_resources[i]->drawDataToScreen();
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();
}
_mhk[0]->open(mystFiles[_curStack]);
if (getPlatform() == Common::kPlatformMacintosh)
_gfx->loadExternalPictureFile(_curStack);
_runExitScript = false;
_cache.clear();
}
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();
_cache.clear();
_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, true, soundActionVolume, true);
} else {
error("Unknown sound action %d", soundAction);
}
// 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);
uint16 scriptCount = initStream->readUint16LE();
debugC(kDebugINIT, "\tOpcode Count: %d", scriptCount);
MystScriptEntry *scripts = new MystScriptEntry[scriptCount];
for (uint16 i = 0; i < scriptCount; i++) {
// TODO: u0 is likely variable reference for boolean to
// determine whether or not to execute opcode
uint16 u0 = initStream->readUint16LE();
scripts[i].opcode = initStream->readUint16LE();
// If variable indicates not to execute opcode, rewrite to NOP
//if (!_varStore->getVar(u0))
// scripts[i].opcode = 0xFFFF;
scripts[i].var = initStream->readUint16LE();
scripts[i].numValues = initStream->readUint16LE();
scripts[i].values = new uint16[scripts[i].numValues];
debugC(kDebugINIT, "\tu0: %d", u0);
debugC(kDebugINIT, "\tOpcode %d: %s", i, _scriptParser->getOpcodeDesc(scripts[i].opcode));
debugC(kDebugINIT, "\t\tUses Variable %d", scripts[i].var);
debugC(kDebugINIT, "\t\tHas %d Arguments:", scripts[i].numValues);
for (uint16 j = 0; j < scripts[i].numValues; j++) {
scripts[i].values[j] = initStream->readUint16LE();
debugC(kDebugINIT, "\t\tArgument %d: %d", j, scripts[i].values[j]);
}
}
delete initStream;
_scriptParser->runScript(scriptCount, scripts);
for (uint16 i = 0; i < scriptCount; i++)
delete[] scripts[i].values;
delete[] scripts;
}
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);
uint16 scriptCount = exitStream->readUint16LE();
debugC(kDebugEXIT, "\tOpcode Count: %d", scriptCount);
MystScriptEntry *scripts = new MystScriptEntry[scriptCount];
for (uint16 i = 0; i < scriptCount; i++) {
// TODO: u0 is likely variable reference for boolean to
// to determine whether or not to execute opcode (i.e. door
// close noises only when door is open).
uint16 u0 = exitStream->readUint16LE();
scripts[i].opcode = exitStream->readUint16LE();
// If variable indicates not to execute opcode, rewrite to NOP
//if (!_varStore->getVar(u0))
// scripts[i].opcode = 0xFFFF;
scripts[i].var = exitStream->readUint16LE();
scripts[i].numValues = exitStream->readUint16LE();
scripts[i].values = new uint16[scripts[i].numValues];
debugC(kDebugEXIT, "\tu0: %d", u0);
debugC(kDebugEXIT, "\tOpcode %d: %s", i, _scriptParser->getOpcodeDesc(scripts[i].opcode));
debugC(kDebugEXIT, "\t\tUses Variable %d", scripts[i].var);
debugC(kDebugEXIT, "\t\tHas %d Arguments:", scripts[i].numValues);
for (uint16 j = 0; j < scripts[i].numValues; j++) {
scripts[i].values[j] = exitStream->readUint16LE();
debugC(kDebugEXIT, "\t\tArgument %d: %d", j, scripts[i].values[j]);
}
uint16 u1 = exitStream->readUint16LE();
if (u1 != 1)
warning("Myst EXIT u1 not 1");
}
delete exitStream;
_scriptParser->runScript(scriptCount, scripts);
for (uint16 i = 0; i < scriptCount; i++)
delete[] scripts[i].values;
delete[] scripts;
}
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");
}
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() {
_resources.clear();
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);
}
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:");
_scriptCount = rlstStream->readUint16LE();
debugC(kDebugResource, "\tOpcode Count: %d", _scriptCount);
if (_scriptCount == 0)
return;
_scripts = new MystScriptEntry[_scriptCount];
for (uint16 i = 0; i < _scriptCount; i++) {
_scripts[i].opcode = rlstStream->readUint16LE();
_scripts[i].var = rlstStream->readUint16LE();
_scripts[i].numValues = rlstStream->readUint16LE();
_scripts[i].values = new uint16[_scripts[i].numValues];
debugC(kDebugResource, "\tOpcode %d: %s", i, _vm->_scriptParser->getOpcodeDesc(_scripts[i].opcode));
debugC(kDebugResource, "\t\tUses Variable %d", _scripts[i].var);
debugC(kDebugResource, "\t\tHas %d Arguments:", _scripts[i].numValues);
for (uint16 j = 0; j < _scripts[i].numValues; j++) {
_scripts[i].values[j] = rlstStream->readUint16LE();
debugC(kDebugResource, "\t\tArgument %d: %d", j, _scripts[i].values[j]);
}
}
}
void MystResourceType5::handleMouseUp() {
_vm->_scriptParser->runScript(_scriptCount, _scripts, this);
}
// In Myst/Making of Myst, the paths are hardcoded ala Windows style without extension. Convert them.
Common::String MystResourceType6::convertMystVideoName(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));
}
// 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 {
_subImages[i].rect.top = 0;
_subImages[i].rect.right = 0;
_subImages[i].rect.bottom = 0;
}
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);
}
}
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;
if (_subImages[subImageId].rect.left == -1)
_vm->_gfx->copyImageSectionToScreen(imageToDraw, _rect, _rect);
//vm->_gfx->copyImageToScreen(imageToDraw, Common::Rect(0, 0, 544, 333));
// TODO: Think this is the case when the image is full screen.. need to modify graphics to add functions for returning size of image.
// This is not right either...
//else if (_rect.width() != _subImages[draw_subimage_id].rect.width() || _rect.height() != _subImages[draw_subimage_id].rect.height())
// HACK: Hardcode cases of this until general rule can be ascertained
// These cases seem to have the source rect in the wdib with an vertical i.e. top+X, bottom+X where X is a constant, but could
// be negative, translations, when in fact both the source and dest should be equal...
//else if ((vm->getCurStack() == kSeleniticStack && vm->getCurCard() == 1155 && tmp == 1) || // X=
// (vm->getCurStack() == kSeleniticStack && vm->getCurCard() == 1225 && tmp == 1) || // X=
// (vm->getCurStack() == kMystStack && vm->getCurCard() == 4247 && tmp == 0) || // X=
// (vm->getCurStack() == kChannelwoodStack && vm->getCurCard() == 3161 && tmp == 0)) // X=
// vm->_gfx->copyImageSectionToScreen(imageToDraw, _rect, _rect);
// // TODO: Small vertical movement remains on change. Suspect off by one error from these to real
// // solution.
else
_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...
printf("Type 10 _mouseDownOpcode: %d\n", _mouseDownOpcode);
printf("Type 10 _mouseDragOpcode: %d\n", _mouseDragOpcode);
printf("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");
}
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...
printf("Type 11 _mouseDownOpcode: %d\n", _mouseDownOpcode);
printf("Type 11 _mouseDragOpcode: %d\n", _mouseDragOpcode);
printf("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");
}
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
printf("Type 12 _state0Frame: %d\n", _state0Frame);
printf("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...
printf("Type 12 _mouseDownOpcode: %d\n", _mouseDownOpcode);
printf("Type 12 _mouseDragOpcode: %d\n", _mouseDragOpcode);
printf("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;
}
void MystResourceType12::handleAnimation() {
// TODO: Probably not final version. Variable/Type 11 Controlled?
if (_doAnimation) {
_vm->_gfx->copyImageToScreen(_currentFrame++, _frameRect);
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::StringList 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