aboutsummaryrefslogtreecommitdiff
path: root/engines/pegasus/pegasus.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/pegasus/pegasus.cpp')
-rw-r--r--engines/pegasus/pegasus.cpp388
1 files changed, 388 insertions, 0 deletions
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
new file mode 100644
index 0000000000..cf79900bf8
--- /dev/null
+++ b/engines/pegasus/pegasus.cpp
@@ -0,0 +1,388 @@
+/* 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.
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/error.h"
+#include "common/events.h"
+#include "common/file.h"
+#include "common/fs.h"
+#include "common/textconsole.h"
+#include "common/translation.h"
+#include "base/plugins.h"
+#include "base/version.h"
+#include "gui/saveload.h"
+
+#include "pegasus/console.h"
+#include "pegasus/pegasus.h"
+
+//#define RUN_SUB_MOVIE // :D :D :D :D :D :D
+//#define RUN_INTERFACE_TEST
+
+namespace Pegasus {
+
+PegasusEngine::PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc) : Engine(syst), _gameDescription(gamedesc) {
+}
+
+PegasusEngine::~PegasusEngine() {
+ delete _video;
+ delete _sound;
+ delete _gfx;
+ delete _resFork;
+ delete _inventoryLid;
+ delete _biochipLid;
+ delete _console;
+}
+
+Common::Error PegasusEngine::run() {
+ _console = new PegasusConsole(this);
+ _gfx = new GraphicsManager(this);
+ _video = new VideoManager(this);
+ _sound = new SoundManager(this);
+ _resFork = new Common::MacResManager();
+ _inventoryLid = new Common::MacResManager();
+ _biochipLid = new Common::MacResManager();
+ _gameMode = kIntroMode;
+ _adventureMode = true;
+
+ if (!_resFork->open("JMP PP Resources") || !_resFork->hasResFork())
+ error("Could not load JMP PP Resources");
+
+ if (!_inventoryLid->open("Images/Lids/Inventory Lid Sequence") || !_inventoryLid->hasResFork())
+ error("Could not open Inventory Lid Sequence");
+
+ if (!_biochipLid->open("Images/Lids/Biochip Lid Sequence") || !_biochipLid->hasResFork())
+ error("Could not open Biochip Lid Sequence");
+
+ loadItemLocationData();
+
+ if (!isDemo() && !detectOpeningClosingDirectory()) {
+ Common::String message = "Missing intro directory. ";
+
+ // Give Mac OS X a more specific message because we can
+#ifdef MACOSX
+ message += "Make sure \"Opening/Closing\" is present.";
+#else
+ message += "Be sure to rename \"Opening/Closing\" to \"Opening_Closing\".";
+#endif
+
+ GUIErrorMessage(message);
+ warning("%s", message.c_str());
+ return Common::kNoGameDataFoundError;
+ }
+
+#if 0
+ Common::MacResIDArray pictIds = _biochipLid->getResIDArray(MKID_BE('PICT'));
+ for (uint32 i = 0; i < pictIds.size(); i++) {
+ Common::String filename = Common::String::printf("PICT_%d.pict", pictIds[i]);
+ Common::DumpFile file;
+ assert(file.open(filename));
+ Common::SeekableReadStream *res = _biochipLid->getResource(MKID_BE('PICT'), pictIds[i]);
+ byte *data = new byte[res->size()];
+ res->read(data, res->size());
+ for (int j = 0; j < 512; j++)
+ file.writeByte(0);
+ file.write(data, res->size());
+ file.close();
+ delete res;
+ delete[] data;
+ }
+#endif
+
+#if defined(RUN_SUB_MOVIE)
+ _video->playMovie("Images/Norad Alpha/Sub Chase Movie");
+#elif defined(RUN_INTERFACE_TEST)
+ drawInterface();
+ _gfx->setCursor(kMainCursor);
+ _sound->playSound("Sounds/Caldoria/Apartment Music.aiff", true);
+
+ while (!shouldQuit()) {
+ Common::Event event;
+ // Ignore events for now
+ while (_eventMan->pollEvent(event)) {
+ if (event.type == Common::EVENT_MOUSEMOVE)
+ _system->updateScreen();
+ }
+
+ _system->delayMillis(10);
+ }
+#else
+ while (!shouldQuit()) {
+ switch (_gameMode) {
+ case kIntroMode:
+ if (!isDemo())
+ runIntro();
+ _gameMode = kMainMenuMode;
+ break;
+ case kMainMenuMode:
+ runMainMenu();
+ break;
+ case kMainGameMode:
+ if (isDemo())
+ changeLocation(kLocPrehistoric);
+ else
+ changeLocation(kLocCaldoria);
+ mainGameLoop();
+ break;
+ case kQuitMode:
+ return Common::kNoError;
+ default:
+ _gameMode = kMainMenuMode;
+ break;
+ }
+ }
+#endif
+
+ return Common::kNoError;
+}
+
+bool PegasusEngine::detectOpeningClosingDirectory() {
+ // We need to detect what our Opening/Closing directory is listed as
+ // On the original disc, it was 'Opening/Closing' but only HFS(+) supports the slash
+ // Mac OS X will display this as 'Opening:Closing' and we can use that directly
+ // On other systems, users will need to rename to "Opening_Closing"
+
+ Common::FSNode gameDataDir(ConfMan.get("path"));
+ gameDataDir = gameDataDir.getChild("Images");
+
+ if (!gameDataDir.exists())
+ return false;
+
+ Common::FSList fsList;
+ if (!gameDataDir.getChildren(fsList, Common::FSNode::kListDirectoriesOnly, true))
+ return false;
+
+ for (uint i = 0; i < fsList.size() && _introDirectory.empty(); i++) {
+ Common::String name = fsList[i].getName();
+
+ if (name.equalsIgnoreCase("Opening:Closing"))
+ _introDirectory = name;
+ else if (name.equalsIgnoreCase("Opening_Closing"))
+ _introDirectory = name;
+ }
+
+ if (_introDirectory.empty())
+ return false;
+
+ debug(0, "Detected intro location as '%s'", _introDirectory.c_str());
+ _introDirectory = Common::String("Images/") + _introDirectory;
+ return true;
+}
+
+void PegasusEngine::loadItemLocationData() {
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('N', 'I', 't', 'm'), 0x80);
+
+ uint16 entryCount = res->readUint16BE();
+
+ for (uint16 i = 0; i < entryCount; i++) {
+ ItemLocationData loc;
+ loc.id = res->readUint16BE(); // Which is always == i, anyway
+ loc.location = (ItemLocation)res->readUint16BE();
+ loc.u0 = res->readUint16BE();
+ loc.u1 = res->readByte();
+ debug(1, "Item[%d]: ID = %d, location = %x, u0 = %d, u1 = %d", i, loc.id, loc.location, loc.u0, loc.u1);
+ res->readByte();
+ _itemLocationData.push_back(loc);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::runIntro() {
+ _video->playMovieCentered(_introDirectory + "/BandaiLogo.movie");
+ VideoHandle handle = _video->playBackgroundMovie(_introDirectory + "/Big Movie.movie");
+ _video->seekToTime(handle, 10 * 600);
+ _video->waitUntilMovieEnds(handle);
+}
+
+void PegasusEngine::drawInterface() {
+ _gfx->drawPict("Images/Interface/3DInterface Top", 0, 0, false);
+ _gfx->drawPict("Images/Interface/3DInterface Left", 0, kViewScreenOffset, false);
+ _gfx->drawPict("Images/Interface/3DInterface Right", 640 - kViewScreenOffset, kViewScreenOffset, false);
+ _gfx->drawPict("Images/Interface/3DInterface Bottom", 0, kViewScreenOffset + 256, false);
+ //drawCompass();
+ _system->updateScreen();
+}
+
+void PegasusEngine::mainGameLoop() {
+ // TODO: Yeah...
+ _system->fillScreen(0);
+ _video->playMovieCentered("Images/Caldoria/Pullback.movie");
+ drawInterface();
+
+ Common::String navMovie = Common::String::format("Images/%s/%s.movie", getTimeZoneFolder(_timeZone).c_str(), getTimeZoneDesc(_timeZone).c_str());
+ _video->playMovie(navMovie, kViewScreenOffset, kViewScreenOffset);
+
+ _gameMode = kQuitMode;
+}
+
+void PegasusEngine::changeLocation(TimeZone timeZone) {
+ _timeZone = timeZone;
+ loadViews(_timeZone);
+ //loadExits(_timeZone);
+ loadDoors(_timeZone);
+ //loadHSLs(_timeZone);
+ //loadHSIn(_timeZone);
+ loadSoundSpots(_timeZone);
+ //loadTurns(_timeZone);
+ loadZooms(_timeZone);
+ loadExtras(_timeZone);
+}
+
+void PegasusEngine::loadViews(TimeZone timeZone) {
+ _currentViews.clear();
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('V', 'i', 'e', 'w'), getTimeZoneDesc(timeZone));
+
+ uint32 entryCount = res->readUint32BE();
+
+ for (uint32 i = 0; i < entryCount; i++) {
+ View view;
+ view.u0 = res->readUint16BE(); // Compass reading?
+ view.u1 = res->readByte(); // Always 0-3, direction?
+ view.u2 = res->readByte(); // Usually 0, rarely 3
+ view.frameTime = res->readUint32BE();
+ debug(1, "View[%d]: u0 = %d, u1 = %d, u2 = %d, time = %d", i, view.u0, view.u1, view.u2, view.frameTime);
+ _currentViews.push_back(view);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::loadDoors(TimeZone timeZone) {
+ _currentDoors.clear();
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('D', 'o', 'o', 'r'), getTimeZoneDesc(timeZone));
+
+ uint32 entryCount = res->readUint32BE();
+
+ for (uint32 i = 0; i < entryCount; i++) {
+ Door door;
+ door.u0 = res->readUint16BE();
+ door.u1 = res->readUint16BE(); // Always divisible by 256?
+ door.startTime = res->readUint32BE();
+ door.endTime = res->readUint32BE();
+ door.u2 = res->readUint16BE();
+ debug(1, "Door[%d]: u0 = %d, u1 = %d, startTime = %d, endTime = %d, u2 = %d", i, door.u0, door.u1, door.startTime, door.endTime, door.u2);
+ _currentDoors.push_back(door);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::loadSoundSpots(TimeZone timeZone) {
+ _currentSoundSpots.clear();
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('S', 'p', 'o', 't'), getTimeZoneDesc(timeZone));
+
+ uint32 entryCount = res->readUint32BE();
+
+ for (uint32 i = 0; i < entryCount; i++) {
+ SoundSpot spot;
+ spot.u0 = res->readUint16BE();
+ spot.u1 = res->readUint16BE();
+ spot.u2 = res->readUint16BE(); // 0/1 or 768/769
+ spot.startTime = res->readUint32BE();
+ spot.endTime = res->readUint32BE();
+ spot.u3 = res->readUint16BE();
+ debug(1, "Sound Spot[%d]: u0 = %d, u1 = %d, u2 = %d, startTime = %d, endTime = %d, u3 = %d", i, spot.u0, spot.u1, spot.u2, spot.startTime, spot.endTime, spot.u3);
+ _currentSoundSpots.push_back(spot);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::loadZooms(TimeZone timeZone) {
+ _currentZooms.clear();
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('Z', 'o', 'o', 'm'), getTimeZoneDesc(timeZone));
+
+ uint32 entryCount = res->readUint32BE();
+
+ for (uint32 i = 0; i < entryCount; i++) {
+ Zoom zoom;
+ zoom.u0 = res->readUint16BE();
+ zoom.u1 = res->readUint16BE();
+ zoom.startTime = res->readUint32BE();
+ zoom.endTime = res->readUint32BE();
+ zoom.u2 = res->readUint16BE();
+ debug(1, "Zoom[%d]: u0 = %d, u1 = %d, startTime = %d, endTime = %d, u2 = %d", i, zoom.u0, zoom.u1, zoom.startTime, zoom.endTime, zoom.u2);
+ _currentZooms.push_back(zoom);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::loadExtras(TimeZone timeZone) {
+ _currentExtras.clear();
+
+ Common::SeekableReadStream *res = _resFork->getResource(MKTAG('X', 't', 'r', 'a'), getTimeZoneDesc(timeZone));
+
+ uint32 entryCount = res->readUint32BE();
+
+ for (uint32 i = 0; i < entryCount; i++) {
+ Extra extra;
+ extra.u0 = res->readUint32BE();
+ extra.startTime = res->readUint32BE();
+ extra.endTime = res->readUint32BE();
+ debug(1, "Extra[%d]: u0 = %d, startTime = %d, endTime = %d", i, extra.u0, extra.startTime, extra.endTime);
+ _currentExtras.push_back(extra);
+ }
+
+ delete res;
+}
+
+void PegasusEngine::showLoadDialog() {
+ GUI::SaveLoadChooser slc(_("Load game:"), _("Load"));
+ slc.setSaveMode(false);
+
+ Common::String gameId = ConfMan.get("gameid");
+
+ const EnginePlugin *plugin = 0;
+ EngineMan.findGame(gameId, &plugin);
+
+ int slot = slc.runModalWithPluginAndTarget(plugin, ConfMan.getActiveDomainName());
+
+ if (slot >= 0) {
+ warning("TODO: Load game");
+ _gameMode = kMainGameMode;
+ }
+
+ slc.close();
+}
+
+Common::String PegasusEngine::getTimeZoneDesc(TimeZone timeZone) {
+ static const char *names[] = { "Prehistoric", "Mars", "WSC", "Tiny TSA", "Full TSA", "Norad Alpha", "Caldoria", "Norad Delta" };
+ return names[timeZone];
+}
+
+Common::String PegasusEngine::getTimeZoneFolder(TimeZone timeZone) {
+ if (timeZone == kLocFullTSA || timeZone == kLocTinyTSA)
+ return "TSA";
+
+ return getTimeZoneDesc(timeZone);
+}
+
+GUI::Debugger *PegasusEngine::getDebugger() {
+ return _console;
+}
+
+} // End of namespace Pegasus