ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

database.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the AUTHORS
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "engines/myst3/database.h"
00024 
00025 #include "common/archive.h"
00026 #include "common/debug.h"
00027 #include "common/substream.h"
00028 
00029 namespace Myst3 {
00030 
00037 class NodeTransform {
00038 public :
00039     virtual ~NodeTransform() {};
00040 
00041     virtual void read(Common::SeekableReadStream *file) = 0;
00042     virtual void apply(NodePtr &node) = 0;
00043 };
00044 
00049 class NodeTransformAddHotspots : public NodeTransform {
00050 public :
00051     NodeTransformAddHotspots();
00052 
00053     void read(Common::SeekableReadStream *file);
00054     void apply(NodePtr &node);
00055 
00056 private:
00057     int32 _zipBitIndex;
00058     Common::Array<CondScript> _scripts;
00059     Common::Array<HotSpot> _hotspots;
00060 };
00061 
00066 class NodeTransformAddSoundScripts : public NodeTransform {
00067 public :
00068     void read(Common::SeekableReadStream *file) override;
00069     void apply(NodePtr &node) override;
00070 
00071 private:
00072     Common::Array<CondScript> _scripts;
00073 };
00074 
00079 class NodeTransformAddBackgroundSoundScripts : public NodeTransform {
00080 public :
00081     void read(Common::SeekableReadStream *file) override;
00082     void apply(NodePtr &node) override;
00083 
00084 private:
00085     Common::Array<CondScript> _scripts;
00086 };
00087 
00091 class NodeWalker {
00092 public :
00093     NodeWalker(NodeTransform *transform);
00094     ~NodeWalker();
00095 
00096     void read(Common::SeekableReadStream *file, Common::Array<NodePtr> &allNodes, bool createMissingSharedNodes);
00097 
00098 private:
00099     NodeTransform *_transform;
00100 };
00101 
00105 class ScriptData {
00106 public:
00107     static Common::Array<CondScript> readCondScripts(Common::SeekableReadStream &s);
00108     static Common::Array<Opcode> readOpcodes(Common::ReadStream &s);
00109     static Common::Array<HotSpot> readHotspots(Common::ReadStream &s);
00110     static Common::Array<PolarRect> readRects(Common::ReadStream &s);
00111     static CondScript readCondScript(Common::SeekableReadStream &s);
00112     static HotSpot readHotspot(Common::ReadStream &s);
00113 
00114 private:
00115     ScriptData() {};
00116 };
00117 
00118 Common::Array<PolarRect> ScriptData::readRects(Common::ReadStream &s) {
00119     Common::Array<PolarRect> rects;
00120 
00121     bool lastRect = false;
00122     do {
00123         PolarRect rect;
00124         rect.centerPitch = s.readUint16LE();
00125         rect.centerHeading = s.readUint16LE();
00126         rect.width = s.readUint16LE();
00127         rect.height = s.readUint16LE();
00128 
00129         if (rect.width < 0) {
00130             rect.width = -rect.width;
00131         } else {
00132             lastRect = true;
00133         }
00134 
00135         rects.push_back(rect);
00136     } while (!lastRect && !s.eos());
00137 
00138     return rects;
00139 }
00140 
00141 Common::Array<Opcode> ScriptData::readOpcodes(Common::ReadStream &s) {
00142     Common::Array<Opcode> script;
00143 
00144     while (!s.eos()) {
00145         Opcode opcode;
00146         uint16 code = s.readUint16LE();
00147 
00148         opcode.op = code & 0xff;
00149         uint8 count = code >> 8;
00150         if (count == 0 && opcode.op == 0)
00151             break;
00152 
00153         for (int i = 0; i < count; i++) {
00154             int16 value = s.readSint16LE();
00155             opcode.args.push_back(value);
00156         }
00157 
00158         script.push_back(opcode);
00159     }
00160 
00161     return script;
00162 }
00163 
00164 CondScript ScriptData::readCondScript(Common::SeekableReadStream &s) {
00165     CondScript script;
00166     script.condition = s.readUint16LE();
00167     if(!script.condition)
00168         return script;
00169 
00170     // WORKAROUND: Original data bug in MATO 32765
00171     // The script data for node MATO 32765 is missing its first two bytes
00172     // of data, resulting in incorrect opcodes being read
00173 
00174     // Original disassembly:
00175     // init 0 > c[v565 != 0]
00176     //     op 115, ifVarInRange ( )
00177     //     op 45, inventoryAddBack ( )
00178     //     op 53, varSetValue ( vSunspotColor 4090 )
00179     //     op 53, varSetValue ( vSunspotRadius 40 )
00180     //     op 33, waterEffectSetWave ( 100 80 )
00181     //     op 32, waterEffectSetAttenuation ( 359 )
00182     //     op 31, waterEffectSetSpeed ( 15 )
00183 
00184     // Fixed disassembly
00185     // init 0 > c[v1 != 0]
00186     //     op 53, varSetValue ( vSunspotIntensity 45 )
00187     //     op 53, varSetValue ( vSunspotColor 4090 )
00188     //     op 53, varSetValue ( vSunspotRadius 40 )
00189     //     op 33, waterEffectSetWave ( 100 80 )
00190     //     op 32, waterEffectSetAttenuation ( 359 )
00191     //     op 31, waterEffectSetSpeed ( 15 )
00192 
00193     if (script.condition == 565) {
00194         script.condition = 1;
00195         s.seek(-2, SEEK_CUR);
00196     }
00197     // END WORKAROUND
00198 
00199     script.script = readOpcodes(s);
00200 
00201     return script;
00202 }
00203 
00204 Common::Array<CondScript> ScriptData::readCondScripts(Common::SeekableReadStream &s) {
00205     Common::Array<CondScript> scripts;
00206 
00207     while (!s.eos()) {
00208         CondScript script = readCondScript(s);
00209 
00210         if (!script.condition)
00211             break;
00212 
00213         scripts.push_back(script);
00214     }
00215 
00216     return scripts;
00217 }
00218 
00219 HotSpot ScriptData::readHotspot(Common::ReadStream &s) {
00220     HotSpot hotspot;
00221 
00222     hotspot.condition = s.readUint16LE();
00223 
00224     if (hotspot.condition == 0)
00225         return hotspot;
00226 
00227     if (hotspot.condition != -1) {
00228         hotspot.rects = readRects(s);
00229         hotspot.cursor = s.readUint16LE();
00230     }
00231 
00232     hotspot.script = readOpcodes(s);
00233 
00234     return hotspot;
00235 }
00236 
00237 Common::Array<HotSpot> ScriptData::readHotspots(Common::ReadStream &s) {
00238     Common::Array<HotSpot> scripts;
00239 
00240     while (!s.eos()) {
00241         HotSpot hotspot = readHotspot(s);
00242 
00243         if (!hotspot.condition)
00244             break;
00245 
00246         scripts.push_back(hotspot);
00247     }
00248 
00249     return scripts;
00250 }
00251 
00252 NodeTransformAddHotspots::NodeTransformAddHotspots() : _zipBitIndex(-1) {
00253 }
00254 
00255 void NodeTransformAddHotspots::read(Common::SeekableReadStream *file) {
00256     _zipBitIndex++;
00257     _scripts = ScriptData::readCondScripts(*file);
00258     _hotspots = ScriptData::readHotspots(*file);
00259 }
00260 
00261 void NodeTransformAddHotspots::apply(NodePtr &node) {
00262     node->zipBitIndex = _zipBitIndex;
00263     node->scripts.push_back(_scripts);
00264     node->hotspots.push_back(_hotspots);
00265 }
00266 
00267 void NodeTransformAddSoundScripts::read(Common::SeekableReadStream *file) {
00268     _scripts = ScriptData::readCondScripts(*file);
00269 }
00270 
00271 void NodeTransformAddSoundScripts::apply(NodePtr &node) {
00272     node->soundScripts.push_back(_scripts);
00273 }
00274 
00275 void NodeTransformAddBackgroundSoundScripts::read(Common::SeekableReadStream *file) {
00276     _scripts = ScriptData::readCondScripts(*file);
00277 }
00278 
00279 void NodeTransformAddBackgroundSoundScripts::apply(NodePtr &node) {
00280     node->backgroundSoundScripts.push_back(_scripts);
00281 }
00282 
00283 NodeWalker::NodeWalker(NodeTransform *transform) : _transform(transform) {
00284 }
00285 
00286 void NodeWalker::read(Common::SeekableReadStream *file, Common::Array<NodePtr> &allNodes, bool createMissingSharedNodes) {
00287     while (!file->eos()) {
00288         int16 id = file->readUint16LE();
00289 
00290         // End of list
00291         if (id == 0)
00292             break;
00293 
00294         if (id < -10)
00295             error("Unimplemented node list command");
00296 
00297         if (id > 0) {
00298             // Normal node, find the node if existing
00299             NodePtr node;
00300             for (uint i = 0; i < allNodes.size(); i++)
00301                 if (allNodes[i]->id == id) {
00302                     node = allNodes[i];
00303                     break;
00304                 }
00305 
00306             // Node not found, create a new one
00307             if (!node) {
00308                 node = NodePtr(new NodeData());
00309                 node->id = id;
00310                 allNodes.push_back(node);
00311             }
00312 
00313             _transform->read(file);
00314             _transform->apply(node);
00315         } else {
00316             // Several nodes sharing the same scripts
00317             // Find the node ids the script applies to
00318             Common::Array<int16> scriptNodeIds;
00319 
00320             if (id == -10)
00321                 do {
00322                     id = file->readUint16LE();
00323                     if (id < 0) {
00324                         uint16 end = file->readUint16LE();
00325                         for (int i = -id; i <= end; i++)
00326                             scriptNodeIds.push_back(i);
00327 
00328                     } else if (id > 0) {
00329                         scriptNodeIds.push_back(id);
00330                     }
00331                 } while (id);
00332             else
00333                 for (int i = 0; i < -id; i++) {
00334                     scriptNodeIds.push_back(file->readUint16LE());
00335                 }
00336 
00337             // Load the script
00338             _transform->read(file);
00339 
00340             // Add the script to each matching node
00341             for (uint i = 0; i < scriptNodeIds.size(); i++) {
00342                 NodePtr node;
00343 
00344                 // Find the current node if existing
00345                 for (uint j = 0; j < allNodes.size(); j++) {
00346                     if (allNodes[j]->id == scriptNodeIds[i]) {
00347                         node = allNodes[j];
00348                         break;
00349                     }
00350                 }
00351 
00352                 if (!node) {
00353                     if (createMissingSharedNodes) {
00354                         // Node not found, create a new one
00355                         node = NodePtr(new NodeData());
00356                         node->id = scriptNodeIds[i];
00357                         allNodes.push_back(node);
00358                     } else {
00359                         // Node not found, skip it
00360                         continue;
00361                     }
00362                 }
00363 
00364                 _transform->apply(node);
00365             }
00366         }
00367     }
00368 }
00369 
00370 NodeWalker::~NodeWalker() {
00371     delete _transform;
00372 }
00373 
00374 static const RoomData roomsXXXX[] = {
00375         { kRoomShared,        "XXXX" }
00376 };
00377 
00378 static const RoomData roomsINTR[] = {
00379         { kRoomIntro,         "INTR" }
00380 };
00381 
00382 static const RoomData roomsTOHO[] = {
00383         { kRoomTomahnaStart,  "TOHO" }
00384 };
00385 
00386 static const RoomData roomsTOHB[] = {
00387         { kRoomTomahnaReturn, "TOHB" }
00388 };
00389 
00390 static const RoomData roomsLE[] = {
00391         { kJnaninStart,       "LEIS" },
00392         { kRoomLeos,          "LEOS" },
00393         { kRoomLeet,          "LEET" },
00394         { kRoomLelt,          "LELT" },
00395         { kRoomLemt,          "LEMT" },
00396         { kRoomLeof,          "LEOF" }
00397 };
00398 
00399 static const RoomData roomsLI[] = {
00400         { kRoomEdannaStart,   "LIDR" },
00401         { kRoomLisw,          "LISW" },
00402         { kRoomLifo,          "LIFO" },
00403         { kRoomLisp,          "LISP" },
00404         { kRoomLine,          "LINE" }
00405 };
00406 
00407 static const RoomData roomsEN[] = {
00408         { kRoomVoltaicStart,  "ENSI" },
00409         { kRoomEnpp,          "ENPP" },
00410         { kRoomEnem,          "ENEM" },
00411         { kRoomEnlc,          "ENLC" },
00412         { kRoomEndd,          "ENDD" },
00413         { kRoomEnch,          "ENCH" },
00414         { kRoomEnli,          "ENLI" }
00415 };
00416 
00417 static const RoomData roomsNA[] = {
00418         { kRoomNarayan,       "NACH" }
00419 };
00420 
00421 static const RoomData roomsMENU[] = {
00422         { kRoomMenu,          "MENU" },
00423         { kRoomJournals,      "JRNL" },
00424         { kRoomDemo,          "DEMO" },
00425         { kRoomAtix,          "ATIX" }
00426 };
00427 
00428 static const RoomData roomsMA[] = {
00429         { kRoomAmateriaStart, "MACA" },
00430         { kRoomMais,          "MAIS" },
00431         { kRoomMall,          "MALL" },
00432         { kRoomMass,          "MASS" },
00433         { kRoomMaww,          "MAWW" },
00434         { kRoomMato,          "MATO" }
00435 };
00436 
00437 static const RoomData roomsLOGO[] = {
00438         { kLogo,              "LOGO" }
00439 };
00440 
00441 const AgeData Database::_ages[] = {
00442     { 1, 0, 1, roomsXXXX, 0 },
00443     { 2, 1, 1, roomsINTR, 0 },
00444     { 3, 2, 1, roomsTOHO, 0 },
00445     { 4, 4, 1, roomsTOHB, 0 },
00446     { 5, 2, 6, roomsLE, 1 },
00447     { 6, 4, 5, roomsLI, 2 },
00448     { 7, 3, 7, roomsEN, 3 },
00449     { 8, 3, 1, roomsNA, 4 },
00450     { 9, 0, 4, roomsMENU, 0 },
00451     { 10, 1, 6, roomsMA, 5 },
00452     { 11, 0, 1, roomsLOGO, 0 }
00453 };
00454 
00455 Database::Database(const Common::Platform platform, const Common::Language language, const uint32 localizationType) :
00456         _platform(platform),
00457         _language(language),
00458         _localizationType(localizationType),
00459         _soundIdMin(0),
00460         _soundIdMax(0) {
00461 
00462     _datFile = SearchMan.createReadStreamForMember("myst3.dat");
00463     if (!_datFile) {
00464         error("Unable to find 'myst3.dat'");
00465     }
00466 
00467     uint magic = _datFile->readUint32LE();
00468     if (magic != MKTAG('M', 'Y', 'S', 'T')) {
00469         error("'myst3.dat' is invalid");
00470     }
00471 
00472     uint version = _datFile->readUint32LE();
00473     if (version != kDatVersion) {
00474         error("Incorrect 'myst3.dat' version. Expected '%d', found '%d'", kDatVersion, version);
00475     }
00476 
00477     bool isWindowMacVersion = _platform == Common::kPlatformWindows || _platform == Common::kPlatformMacintosh;
00478     bool isXboxVersion = _platform == Common::kPlatformXbox;
00479 
00480     readScriptIndex(_datFile, isWindowMacVersion);                                          // Main scripts
00481     readScriptIndex(_datFile, isWindowMacVersion && _localizationType == kLocMulti6);      // Menu scripts 6 languages version
00482     readScriptIndex(_datFile, isWindowMacVersion && _localizationType == kLocMulti2);      // Menu scripts 2 languages CD version
00483     readScriptIndex(_datFile, isWindowMacVersion && _localizationType == kLocMonolingual); // Menu scripts english CD version
00484     readScriptIndex(_datFile, isXboxVersion);                                               // Main scripts Xbox version
00485     readScriptIndex(_datFile, isXboxVersion && _localizationType != kLocMonolingual);      // Menu scripts PAL Xbox version
00486     readScriptIndex(_datFile, isXboxVersion && _localizationType == kLocMonolingual);      // Menu scripts NTSC Xbox version
00487     readSoundNames(_datFile, isWindowMacVersion);                                           // Sound names
00488     readSoundNames(_datFile, isXboxVersion);                                                // Sound names Xbox
00489 
00490     _roomScriptsStartOffset = _datFile->pos();
00491 
00492     Common::SeekableReadStream *initScriptStream = getRoomScriptStream("INIT", kScriptTypeNodeInit);
00493     _nodeInitScript = ScriptData::readOpcodes(*initScriptStream);
00494     delete initScriptStream;
00495 
00496     Common::SeekableReadStream *cuesStream = getRoomScriptStream("INIT", kScriptTypeAmbientCue);
00497     loadAmbientCues(cuesStream);
00498     delete cuesStream;
00499 
00500     preloadCommonRooms();
00501     initializeZipBitIndexTable();
00502 
00503     if (isWindowMacVersion && _localizationType == kLocMulti2) {
00504         patchLanguageMenu();
00505     }
00506 }
00507 
00508 Database::~Database() {
00509     delete _datFile;
00510 }
00511 
00512 void Database::preloadCommonRooms() {
00513     for (uint i = 0; i < ARRAYSIZE(_ages); i++) {
00514         const AgeData &age = _ages[i];
00515 
00516         for (uint j = 0; j < age.roomCount; j++) {
00517             const RoomData &room = age.rooms[j];
00518 
00519             if (isCommonRoom(room.id, age.id)) {
00520                 Common::Array<NodePtr> nodes = readRoomScripts(&room);
00521                 _roomNodesCache.setVal(RoomKey(room.id, age.id), nodes);
00522             }
00523         }
00524     }
00525 }
00526 
00527 Common::Array<NodePtr> Database::getRoomNodes(uint32 roomID, uint32 ageID) const {
00528     Common::Array<NodePtr> nodes;
00529 
00530     if (_roomNodesCache.contains(RoomKey(roomID, ageID))) {
00531         nodes = _roomNodesCache.getVal(RoomKey(roomID, ageID));
00532     } else {
00533         const RoomData *data = findRoomData(roomID, ageID);
00534         nodes = readRoomScripts(data);
00535     }
00536 
00537     return nodes;
00538 }
00539 
00540 Common::Array<uint16> Database::listRoomNodes(uint32 roomID, uint32 ageID) {
00541     Common::Array<NodePtr> nodes;
00542     Common::Array<uint16> list;
00543 
00544     nodes = getRoomNodes(roomID, ageID);
00545 
00546     for (uint i = 0; i < nodes.size(); i++) {
00547         list.push_back(nodes[i]->id);
00548     }
00549 
00550     return list;
00551 }
00552 
00553 NodePtr Database::getNodeData(uint16 nodeID, uint32 roomID, uint32 ageID) {
00554     Common::Array<NodePtr> nodes = getRoomNodes(roomID, ageID);
00555 
00556     for (uint i = 0; i < nodes.size(); i++) {
00557         if (nodes[i]->id == nodeID)
00558             return nodes[i];
00559     }
00560 
00561     return NodePtr();
00562 }
00563 
00564 void Database::initializeZipBitIndexTable() {
00565     int16 zipBit = 0;
00566     for (uint i = 0; i < ARRAYSIZE(_ages); i++) {
00567         for (uint j = 0; j < _ages[i].roomCount; j++) {
00568             _roomZipBitIndex.setVal(_ages[i].rooms[j].id, zipBit);
00569 
00570             // Add the highest zip-bit index for the current room
00571             // to get the zip-bit index for the next room
00572             int16 maxZipBitForRoom = 0;
00573             Common::Array<NodePtr> nodes = readRoomScripts(&_ages[i].rooms[j]);
00574             for (uint k = 0; k < nodes.size(); k++) {
00575                 maxZipBitForRoom = MAX(maxZipBitForRoom, nodes[k]->zipBitIndex);
00576             }
00577 
00578             zipBit += maxZipBitForRoom + 1;
00579         }
00580     }
00581 }
00582 
00583 int32 Database::getNodeZipBitIndex(uint16 nodeID, uint32 roomID, uint32 ageID) {
00584     if (!_roomZipBitIndex.contains(roomID)) {
00585         error("Unable to find zip-bit index for room %d", roomID);
00586     }
00587 
00588     Common::Array<NodePtr> nodes = getRoomNodes(roomID, ageID);
00589 
00590     for (uint i = 0; i < nodes.size(); i++) {
00591         if (nodes[i]->id == nodeID) {
00592             return _roomZipBitIndex[roomID] + nodes[i]->zipBitIndex;
00593         }
00594     }
00595 
00596     error("Unable to find zip-bit index for node (%d, %d)", nodeID, roomID);
00597 }
00598 
00599 const RoomData *Database::findRoomData(uint32 roomID, uint32 ageID) const {
00600     for (uint i = 0; i < ARRAYSIZE(_ages); i++) {
00601         if (_ages[i].id == ageID) {
00602             for (uint j = 0; j < _ages[i].roomCount; j++) {
00603                 if (_ages[i].rooms[j].id == roomID) {
00604                     return &_ages[i].rooms[j];
00605                 }
00606             }
00607         }
00608     }
00609 
00610     error("No room with ID %d in age %d", roomID, ageID);
00611 }
00612 
00613 Common::Array<NodePtr> Database::readRoomScripts(const RoomData *room) const {
00614     Common::Array<NodePtr> nodes;
00615 
00616     // Load the node scripts
00617     Common::SeekableReadStream *scriptsStream = getRoomScriptStream(room->name, kScriptTypeNode);
00618     if (scriptsStream) {
00619         NodeWalker scriptWalker = NodeWalker(new NodeTransformAddHotspots());
00620         scriptWalker.read(scriptsStream, nodes, true);
00621 
00622         delete scriptsStream;
00623     }
00624 
00625     // Load the ambient sound scripts, if any
00626     Common::SeekableReadStream *ambientSoundsStream = getRoomScriptStream(room->name, kScriptTypeAmbientSound);
00627     if (ambientSoundsStream) {
00628         NodeWalker scriptWalker = NodeWalker(new NodeTransformAddSoundScripts());
00629         scriptWalker.read(ambientSoundsStream, nodes, false);
00630 
00631         delete ambientSoundsStream;
00632     }
00633 
00634     Common::SeekableReadStream *backgroundSoundsStream = getRoomScriptStream(room->name, kScriptTypeBackgroundSound);
00635     if (backgroundSoundsStream) {
00636         NodeWalker scriptWalker = NodeWalker(new NodeTransformAddBackgroundSoundScripts());
00637         scriptWalker.read(backgroundSoundsStream, nodes, false);
00638 
00639         delete backgroundSoundsStream;
00640     }
00641 
00642     patchNodeScripts(room, nodes);
00643 
00644     return nodes;
00645 }
00646 
00647 void Database::patchNodeScripts(const RoomData *room, Common::Array<NodePtr> &nodes) const {
00648     if (strcmp(room->name, "LEOF") == 0) {
00649         // The room LEOF does not have a script to set default water effect
00650         // parameters when entering a node. As a result, the pool of water
00651         // mainly visible in LEOF 23 uses the last set water effect parameters.
00652         // If the player comes from the top of the tower, the water effect is
00653         // barely visible.
00654         // As a workaround we insert default water effect settings in node
00655         // 32765 which get applied for each node in the room.
00656         // The new script disassembles as follow:
00657 
00658         // node: LEOF 32765
00659         // init 0 > c[v1 != 0] (true)
00660         // op 33, waterEffectSetWave ( 100 100 )
00661         // op 32, waterEffectSetAttenuation ( 360 )
00662         // op 31, waterEffectSetSpeed ( 12 )
00663 
00664         Opcode waterEffectSetWave;
00665         waterEffectSetWave.op = 33;
00666         waterEffectSetWave.args.push_back(100);
00667         waterEffectSetWave.args.push_back(100);
00668 
00669         Opcode waterEffectSetAttenuation;
00670         waterEffectSetAttenuation.op = 32;
00671         waterEffectSetAttenuation.args.push_back(360);
00672 
00673         Opcode waterEffectSetSpeed;
00674         waterEffectSetSpeed.op = 31;
00675         waterEffectSetSpeed.args.push_back(12);
00676 
00677         CondScript waterEffectScript;
00678         waterEffectScript.condition = 1;
00679         waterEffectScript.script.push_back(waterEffectSetWave);
00680         waterEffectScript.script.push_back(waterEffectSetAttenuation);
00681         waterEffectScript.script.push_back(waterEffectSetSpeed);
00682 
00683         NodePtr node32765 = NodePtr(new NodeData());
00684         node32765->id = 32765;
00685         node32765->scripts.push_back(waterEffectScript);
00686 
00687         nodes.push_back(node32765);
00688     }
00689 }
00690 
00691 bool Database::isCommonRoom(uint32 roomID, uint32 ageID) const {
00692     return roomID == kRoomShared || roomID == kRoomMenu || roomID == kRoomJournals;
00693 }
00694 
00695 void Database::cacheRoom(uint32 roomID, uint32 ageID) {
00696     if (_roomNodesCache.contains(RoomKey(roomID, ageID))) {
00697         return;
00698     }
00699 
00700     // Remove old rooms from cache and add the new one
00701     for (NodesCache::iterator it = _roomNodesCache.begin(); it != _roomNodesCache.end(); it++) {
00702         if (!isCommonRoom(it->_key.roomID, it->_key.ageID)) {
00703             _roomNodesCache.erase(it);
00704         }
00705     }
00706 
00707     const RoomData *currentRoomData = findRoomData(roomID, ageID);
00708 
00709     if (!currentRoomData)
00710         return;
00711 
00712     _roomNodesCache.setVal(RoomKey(roomID, ageID), readRoomScripts(currentRoomData));
00713 }
00714 
00715 Common::String Database::getRoomName(uint32 roomID, uint32 ageID) const {
00716     const RoomData *data = findRoomData(roomID, ageID);
00717     return data->name;
00718 }
00719 
00720 RoomKey Database::getRoomKey(const char *name) {
00721     for (uint i = 0; i < ARRAYSIZE(_ages); i++)
00722         for (uint j = 0; j < _ages[i].roomCount; j++) {
00723             if (scumm_stricmp(_ages[i].rooms[j].name, name) == 0) {
00724                 return RoomKey(_ages[i].rooms[j].id, _ages[i].id);
00725             }
00726         }
00727 
00728     return RoomKey(0, 0);
00729 }
00730 
00731 uint32 Database::getAgeLabelId(uint32 ageID) {
00732     for (uint i = 0; i < ARRAYSIZE(_ages); i++)
00733         if (_ages[i].id == ageID)
00734             return _ages[i].labelId;
00735 
00736     return 0;
00737 }
00738 
00739 Common::String Database::getSoundName(uint32 id) {
00740     const Common::String result = _soundNames.getVal(id, "");
00741     if (result.empty())
00742         error("Unable to find a sound with id %d", id);
00743 
00744     return result;
00745 }
00746 
00747 void Database::loadAmbientCues(Common::ReadStream *s) {
00748     _ambientCues.clear();
00749 
00750     while (!s->eos()) {
00751         uint16 id = s->readUint16LE();
00752 
00753         if (!id)
00754             break;
00755 
00756         AmbientCue cue;
00757         cue.id = id;
00758         cue.minFrames = s->readUint16LE();
00759         cue.maxFrames = s->readUint16LE();
00760 
00761         while (1) {
00762             uint16 track = s->readUint16LE();
00763 
00764             if (!track)
00765                 break;
00766 
00767             cue.tracks.push_back(track);
00768         }
00769 
00770         _ambientCues[id] = cue;
00771     }
00772 }
00773 
00774 const AmbientCue &Database::getAmbientCue(uint16 id) {
00775     if (!_ambientCues.contains(id))
00776         error("Unable to find an ambient cue with id %d", id);
00777 
00778     return _ambientCues.getVal(id);
00779 }
00780 
00781 void Database::readScriptIndex(Common::SeekableReadStream *stream, bool load) {
00782     uint count = stream->readUint32LE();
00783     for (uint i = 0; i < count; i++) {
00784 
00785         RoomScripts roomScripts;
00786 
00787         char roomName[5];
00788         stream->read(roomName, sizeof(roomName));
00789         roomName[4] = '\0';
00790 
00791         roomScripts.room = Common::String(roomName);
00792         roomScripts.type = (ScriptType) stream->readUint32LE();
00793         roomScripts.offset = stream->readUint32LE();
00794         roomScripts.size = stream->readUint32LE();
00795 
00796         if (load) {
00797             _roomScriptsIndex.push_back(roomScripts);
00798         }
00799     }
00800 }
00801 
00802 void Database::readSoundNames(Common::SeekableReadStream *stream, bool load) {
00803     uint count = stream->readUint32LE();
00804     for (uint i = 0; i < count; i++) {
00805 
00806         uint id = stream->readUint32LE();
00807 
00808         char soundName[32];
00809         stream->read(soundName, sizeof(soundName));
00810         soundName[31] = '\0';
00811 
00812         if (load) {
00813             _soundNames[id] = Common::String(soundName);
00814 
00815             if (_soundIdMin == 0 || id < _soundIdMin) {
00816                 _soundIdMin = id;
00817             }
00818 
00819             if (_soundIdMax == 0 || id > _soundIdMax) {
00820                 _soundIdMax = id;
00821             }
00822         }
00823     }
00824 }
00825 
00826 Common::SeekableReadStream *Database::getRoomScriptStream(const char *room, ScriptType scriptType) const {
00827     for (uint i = 0; i < _roomScriptsIndex.size(); i++) {
00828         if (_roomScriptsIndex[i].room.equalsIgnoreCase(room)
00829             && _roomScriptsIndex[i].type == scriptType) {
00830             uint32 startOffset = _roomScriptsStartOffset + _roomScriptsIndex[i].offset;
00831             uint32 size = _roomScriptsIndex[i].size;
00832 
00833             return new Common::SeekableSubReadStream(_datFile, startOffset, startOffset + size);
00834         }
00835     }
00836 
00837     return nullptr;
00838 }
00839 
00840 bool Database::areRoomsScriptsEqual(uint32 roomID1, uint32 ageID1, uint32 roomID2, uint32 ageID2, ScriptType scriptType) {
00841     const RoomData *data1 = findRoomData(roomID1, ageID1);
00842     const RoomData *data2 = findRoomData(roomID2, ageID2);
00843 
00844     int32 startOffset1 = -1;
00845     int32 startOffset2 = -1;
00846     for (uint i = 0; i < _roomScriptsIndex.size(); i++) {
00847         if (_roomScriptsIndex[i].room.equalsIgnoreCase(data1->name)
00848             && _roomScriptsIndex[i].type == scriptType) {
00849             startOffset1 = _roomScriptsStartOffset + _roomScriptsIndex[i].offset;
00850         }
00851 
00852         if (_roomScriptsIndex[i].room.equalsIgnoreCase(data2->name)
00853             && _roomScriptsIndex[i].type == scriptType) {
00854             startOffset2 = _roomScriptsStartOffset + _roomScriptsIndex[i].offset;
00855         }
00856     }
00857 
00858     return startOffset1 == startOffset2;
00859 }
00860 
00861 int16 Database::getGameLanguageCode() const {
00862     // The monolingual versions of the game always use 0 as the language code
00863     if (_localizationType == kLocMonolingual) {
00864         return kEnglish;
00865     }
00866 
00867     switch (_language) {
00868         case Common::FR_FRA:
00869             return kFrench;
00870         case Common::DE_DEU:
00871             return kGerman;
00872         case Common::IT_ITA:
00873             return kItalian;
00874         case Common::ES_ESP:
00875             return kSpanish;
00876         case Common::EN_ANY:
00877             return kEnglish;
00878         default:
00879             return kOther;
00880     }
00881 }
00882 
00883 void Database::patchLanguageMenu() {
00884     // The menu scripts in 'myst3.dat" for the non English CD versions come from the French version
00885     // The scripts for the other languages only differ by the value set for AudioLanguage variable
00886     // when the language selection is not English.
00887     // This function patches the language selection script to set the appropriate value based
00888     // on the detected game langage.
00889 
00890     // Script disassembly:
00891     //  hotspot 5 > c[v1 != 0] (true)
00892     //  rect > pitch: 373 heading: 114 width: 209 height: 28
00893     //  op 206, soundPlayVolume ( 795 5 )
00894     //  op 53, varSetValue ( vLanguageAudio 2 ) // <= The second argument of this opcode is patched
00895     //  op 194, runPuzzle1 ( 18 )
00896     //  op 194, runPuzzle1 ( 19 )
00897 
00898     NodePtr languageMenu = getNodeData(530, kRoomMenu, 9);
00899     languageMenu->hotspots[5].script[1].args[1] = getGameLanguageCode();
00900 }
00901 
00902 } // End of namespace Myst3


Generated on Sat Feb 16 2019 05:00:48 for ResidualVM by doxygen 1.7.1
curved edge   curved edge