aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorMatthew Hoops2011-02-20 02:31:34 -0500
committerMatthew Hoops2011-02-21 18:44:24 -0500
commit950676bf4c8576f63db8dc21a0c89e06981378d5 (patch)
treeee2cb731c5c5e247ccb9d9dd0a2f2b6ec0b1b31a /engines
parent0ec91de76df9fbe452bc62775d337114179b2fe0 (diff)
downloadscummvm-rg350-950676bf4c8576f63db8dc21a0c89e06981378d5.tar.gz
scummvm-rg350-950676bf4c8576f63db8dc21a0c89e06981378d5.tar.bz2
scummvm-rg350-950676bf4c8576f63db8dc21a0c89e06981378d5.zip
PEGASUS: Add my very WIP Pegasus Prime engine
Diffstat (limited to 'engines')
-rw-r--r--engines/engines.mk5
-rw-r--r--engines/pegasus/detection.cpp143
-rw-r--r--engines/pegasus/graphics.cpp255
-rw-r--r--engines/pegasus/graphics.h86
-rw-r--r--engines/pegasus/menu.cpp192
-rw-r--r--engines/pegasus/module.mk18
-rw-r--r--engines/pegasus/pegasus.cpp317
-rw-r--r--engines/pegasus/pegasus.h250
-rw-r--r--engines/pegasus/sound.cpp88
-rw-r--r--engines/pegasus/sound.h72
-rw-r--r--engines/pegasus/video.cpp240
-rw-r--r--engines/pegasus/video.h92
12 files changed, 1758 insertions, 0 deletions
diff --git a/engines/engines.mk b/engines/engines.mk
index eea4ffc0b9..5d62525a56 100644
--- a/engines/engines.mk
+++ b/engines/engines.mk
@@ -104,6 +104,11 @@ DEFINES += -DENABLE_PARALLACTION=$(ENABLE_PARALLACTION)
MODULES += engines/parallaction
endif
+ifdef ENABLE_PEGASUS
+DEFINES += -DENABLE_PEGASUS=$(ENABLE_PEGASUS)
+MODULES += engines/pegasus
+endif
+
ifdef ENABLE_QUEEN
DEFINES += -DENABLE_QUEEN=$(ENABLE_QUEEN)
MODULES += engines/queen
diff --git a/engines/pegasus/detection.cpp b/engines/pegasus/detection.cpp
new file mode 100644
index 0000000000..17dd7c3486
--- /dev/null
+++ b/engines/pegasus/detection.cpp
@@ -0,0 +1,143 @@
+/* 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 "base/plugins.h"
+
+#include "engines/advancedDetector.h"
+#include "common/config-manager.h"
+#include "common/file.h"
+
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+struct PegasusGameDescription {
+ ADGameDescription desc;
+};
+
+bool PegasusEngine::hasFeature(EngineFeature f) const {
+ return
+ (f == kSupportsRTL);
+}
+
+bool PegasusEngine::isDemo() const {
+ return (_gameDescription->desc.flags & ADGF_DEMO) != 0;
+}
+
+} // End of namespace Pegasus
+
+static const PlainGameDescriptor pegasusGames[] = {
+ {"pegasus", "The Journeyman Project: Pegasus Prime"},
+ {0, 0}
+};
+
+
+namespace Pegasus {
+
+static const PegasusGameDescription gameDescriptions[] = {
+ {
+ {
+ "pegasus",
+ "",
+ AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 2009943),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK,
+ Common::GUIO_NONE
+ },
+ },
+
+ {
+ {
+ "pegasus",
+ "",
+ AD_ENTRY1s("JMP PP Resources", "d13a602d2498010d720a6534f097f88b", 360129),
+ Common::EN_ANY,
+ Common::kPlatformMacintosh,
+ ADGF_MACRESFORK|ADGF_DEMO,
+ Common::GUIO_NONE
+ },
+ },
+
+ { AD_TABLE_END_MARKER }
+};
+
+} // End of namespace Pegasus
+
+static const ADParams detectionParams = {
+ // Pointer to ADGameDescription or its superset structure
+ (const byte *)Pegasus::gameDescriptions,
+ // Size of that superset structure
+ sizeof(Pegasus::PegasusGameDescription),
+ // Number of bytes to compute MD5 sum for
+ 5000,
+ // List of all engine targets
+ pegasusGames,
+ // Structure for autoupgrading obsolete targets
+ 0,
+ // Name of single gameid (optional)
+ "pegasus",
+ // List of files for file-based fallback detection (optional)
+ 0,
+ // Flags
+ 0,
+ // Additional GUI options (for every game)
+ Common::GUIO_NONE,
+ // Maximum directory depth
+ 1,
+ // List of directory globs
+ 0
+};
+
+class PegasusMetaEngine : public AdvancedMetaEngine {
+public:
+ PegasusMetaEngine() : AdvancedMetaEngine(detectionParams) {}
+
+ virtual const char *getName() const {
+ return "Pegasus Prime Engine";
+ }
+
+ virtual const char *getOriginalCopyright() const {
+ return "The Journeyman Project: Pegasus Prime (C) Presto Studios";
+ }
+
+ virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
+};
+
+bool PegasusMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
+ const Pegasus::PegasusGameDescription *gd = (const Pegasus::PegasusGameDescription *)desc;
+
+ if (gd)
+ *engine = new Pegasus::PegasusEngine(syst, gd);
+
+ return (gd != 0);
+}
+
+#if PLUGIN_ENABLED_DYNAMIC(PEGASUS)
+ REGISTER_PLUGIN_DYNAMIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine);
+#else
+ REGISTER_PLUGIN_STATIC(PEGASUS, PLUGIN_TYPE_ENGINE, PegasusMetaEngine);
+#endif
+
diff --git a/engines/pegasus/graphics.cpp b/engines/pegasus/graphics.cpp
new file mode 100644
index 0000000000..a05e316ee5
--- /dev/null
+++ b/engines/pegasus/graphics.cpp
@@ -0,0 +1,255 @@
+/* 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 "pegasus/graphics.h"
+
+#include "common/endian.h"
+#include "common/file.h"
+#include "engines/util.h"
+#include "graphics/cursorman.h"
+
+namespace Pegasus {
+
+GraphicsManager::GraphicsManager(PegasusEngine *vm) : _vm(vm) {
+ initGraphics(640, 480, true, NULL);
+
+ _pictDecoder = new Graphics::PictDecoder(_vm->_system->getScreenFormat());
+
+ for (int i = 0; i < kImageCacheSize; i++)
+ _cache[i].surface = 0;
+}
+
+GraphicsManager::~GraphicsManager() {
+ delete _pictDecoder;
+
+ for (int i = 0; i < kImageCacheSize; i++) {
+ if (_cache[i].surface) {
+ _cache[i].surface->free();
+ delete _cache[i].surface;
+ }
+ }
+}
+
+Graphics::Surface *GraphicsManager::decodeImage(const Common::String &filename) {
+ int imageSlot = getImageSlot(filename);
+
+ if (_cache[imageSlot].surface)
+ return _cache[imageSlot].surface;
+
+ Common::File file;
+ if (!file.open(filename))
+ error("Could not open \'%s\'", filename.c_str());
+
+ byte palette[256 * 3];
+ Graphics::Surface *image = _pictDecoder->decodeImage(&file, palette);
+
+ // For <= 8bpp, we need to convert
+ if (image->bytesPerPixel == 1) {
+ Graphics::PixelFormat format = _vm->_system->getScreenFormat();
+ Graphics::Surface *output = new Graphics::Surface();
+ output->create(image->w, image->h, format.bytesPerPixel);
+
+ for (uint16 y = 0; y < image->h; y++) {
+ for (uint16 x = 0; x < image->w; x++) {
+ byte c = *((byte *)image->getBasePtr(x, y));
+ byte r = palette[c * 3];
+ byte g = palette[c * 3 + 1];
+ byte b = palette[c * 3 + 2];
+
+ if (format.bytesPerPixel == 2) {
+ uint16 color = format.RGBToColor(r, g, b);
+ memcpy(output->getBasePtr(x, y), &color, 2);
+ } else if (format.bytesPerPixel == 4) {
+ uint32 color = format.RGBToColor(r, g, b);
+ memcpy(output->getBasePtr(x, y), &color, 4);
+ }
+ }
+ }
+
+ image->free();
+ delete image;
+ image = output;
+ }
+
+ _cache[imageSlot].surface = image;
+ return image;
+}
+
+void GraphicsManager::drawPict(Common::String filename, int x, int y, bool updateScreen) {
+ Graphics::Surface *surface = decodeImage(filename);
+
+ _vm->_system->copyRectToScreen((byte *)surface->pixels, surface->pitch, x, y, surface->w, surface->h);
+
+ if (updateScreen)
+ _vm->_system->updateScreen();
+}
+
+void GraphicsManager::drawPictTransparent(Common::String filename, int x, int y, uint32 transparency, bool updateScreen) {
+ if (_vm->_system->getScreenFormat().bytesPerPixel == 2)
+ transparency &= 0xffff;
+
+ Graphics::Surface *surface = decodeImage(filename);
+ Graphics::Surface *screen = _vm->_system->lockScreen();
+
+ for (uint16 i = 0; i < surface->h; i++) {
+ for (uint16 j = 0; j < surface->w; j++) {
+ if (_vm->_system->getScreenFormat().bytesPerPixel == 2) {
+ uint16 color = *((uint16 *)surface->getBasePtr(j, i));
+ if (color != transparency)
+ memcpy(screen->getBasePtr(j + x, i + y), &color, 2);
+ } else if (_vm->_system->getScreenFormat().bytesPerPixel == 4) {
+ uint32 color = *((uint32 *)surface->getBasePtr(j, i));
+ if (color != transparency)
+ memcpy(screen->getBasePtr(j + x, i + y), &color, 4);
+ }
+ }
+ }
+
+ _vm->_system->unlockScreen();
+
+ if (updateScreen)
+ _vm->_system->updateScreen();
+}
+
+uint32 GraphicsManager::getColor(byte r, byte g, byte b) {
+ return _vm->_system->getScreenFormat().RGBToColor(r, g, b);
+}
+
+void GraphicsManager::setCursor(uint16 cursor) {
+ Common::SeekableReadStream *cicnStream = _vm->_resFork->getResource(MKID_BE('cicn'), cursor);
+
+ // PixMap section
+ Graphics::PictDecoder::PixMap pixMap = _pictDecoder->readPixMap(cicnStream);
+
+ // Mask section
+ cicnStream->readUint32BE(); // mask baseAddr
+ uint16 maskRowBytes = cicnStream->readUint16BE(); // mask rowBytes
+ cicnStream->skip(3 * 2); // mask rect
+ /* uint16 maskHeight = */ cicnStream->readUint16BE();
+
+ // Bitmap section
+ cicnStream->readUint32BE(); // baseAddr
+ uint16 rowBytes = cicnStream->readUint16BE();
+ cicnStream->readUint16BE(); // top
+ cicnStream->readUint16BE(); // left
+ uint16 height = cicnStream->readUint16BE(); // bottom
+ cicnStream->readUint16BE(); // right
+
+ // Data section
+ cicnStream->readUint32BE(); // icon handle
+ cicnStream->skip(maskRowBytes * height); // FIXME: maskHeight doesn't work here, though the specs say it should
+ cicnStream->skip(rowBytes * height);
+
+ // Palette section
+ cicnStream->readUint32BE(); // always 0
+ cicnStream->readUint16BE(); // always 0
+ uint16 colorCount = cicnStream->readUint16BE() + 1;
+
+ byte *colors = (byte *)malloc(256 * 4);
+ for (uint16 i = 0; i < colorCount; i++) {
+ cicnStream->readUint16BE();
+ colors[i * 4] = cicnStream->readByte();
+ cicnStream->readByte();
+ colors[i * 4 + 1] = cicnStream->readByte();
+ cicnStream->readByte();
+ colors[i * 4 + 2] = cicnStream->readByte();
+ cicnStream->readByte();
+ }
+
+ // PixMap data
+ byte *data = (byte *)malloc(pixMap.rowBytes * pixMap.bounds.height());
+ cicnStream->read(data, pixMap.rowBytes * pixMap.bounds.height());
+ delete cicnStream;
+
+ // Now to go get the hotspots
+ Common::SeekableReadStream *cursStream = NULL;
+
+ if (cursor >= kMainCursor && cursor <= kGrabbingHand)
+ cursStream = _vm->_resFork->getResource(MKID_BE('Curs'), kMainCursor);
+ else // if (cursor == kTargetingReticle1 || cursor == kTargetingReticle2)
+ cursStream = _vm->_resFork->getResource(MKID_BE('Curs'), kTargetingReticle1);
+
+ // Go through the stream until we find the right cursor hotspot
+ uint16 x = 0, y = 0;
+ uint16 numHotspots = cursStream->readUint16BE();
+
+ for (uint16 i = 0; i < numHotspots; i++) {
+ uint16 res = cursStream->readUint16BE();
+ uint16 tempX = cursStream->readUint16BE();
+ uint16 tempY = cursStream->readUint16BE();
+
+ if (res == cursor) {
+ x = tempX;
+ y = tempY;
+ break;
+ }
+ }
+
+ // We have the bitmap and the hotspot, let's do this!
+ CursorMan.replaceCursorPalette(colors, 0, colorCount);
+ CursorMan.replaceCursor(data, pixMap.rowBytes, pixMap.bounds.height(), x, y, 0);
+ CursorMan.showMouse(true);
+ _vm->_system->updateScreen();
+
+ free(colors);
+ free(data);
+}
+
+int GraphicsManager::getImageSlot(const Common::String &filename) {
+ // Let's find a match, an open slot, or the oldest image slot
+ uint32 oldestAge = 0xffffffff;
+ int slot = 0;
+
+ for (int i = 0; i < kImageCacheSize; i++) {
+ if (_cache[i].filename.equalsIgnoreCase(filename)) {
+ //warning("Found image %s at slot %d", filename.c_str(), i);
+ _cache[i].lastUsed = _vm->_system->getMillis();
+ return i;
+ }
+
+ if (!_cache[i].surface) {
+ //warning("Putting image %s in empty slot %d", filename.c_str(), i);
+ _cache[i].filename = filename;
+ _cache[i].lastUsed = _vm->_system->getMillis();
+ return i;
+ }
+
+ if (_cache[i].lastUsed < oldestAge) {
+ oldestAge = _cache[i].lastUsed;
+ slot = i;
+ }
+ }
+
+ // Let's make sure that's cleaned out
+ //warning("Replacing old image %s with %s in slot %d", _cache[slot].filename.c_str(), filename.c_str(), slot);
+ _cache[slot].filename = filename;
+ _cache[slot].surface->free();
+ delete _cache[slot].surface;
+ _cache[slot].surface = 0;
+ _cache[slot].lastUsed = _vm->_system->getMillis();
+ return slot;
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/graphics.h b/engines/pegasus/graphics.h
new file mode 100644
index 0000000000..c184172394
--- /dev/null
+++ b/engines/pegasus/graphics.h
@@ -0,0 +1,86 @@
+/* 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$
+ *
+ */
+
+#ifndef PEGASUS_GRAPHICS_H
+#define PEGASUS_GRAPHICS_H
+
+#include "common/rect.h"
+#include "common/str.h"
+#include "common/system.h"
+#include "graphics/pict.h"
+#include "graphics/surface.h"
+
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+enum {
+ // The main cursors
+ kMainCursor = 128,
+ kZoomInCursor = 129,
+ kZoomOutCursor = 130,
+ kPointingCursor = 131,
+ kInteractHand = 132,
+ kGrabbingHand = 133,
+
+ // Reticles when using the Mars shuttle
+ kTargetingReticle1 = 900,
+ kTargetingReticle2 = 901
+};
+
+enum {
+ kImageCacheSize = 10
+};
+
+struct ImageCache {
+ Common::String filename;
+ Graphics::Surface *surface;
+ uint32 lastUsed;
+};
+
+class PegasusEngine;
+
+class GraphicsManager {
+public:
+ GraphicsManager(PegasusEngine *vm);
+ ~GraphicsManager();
+
+ void drawPict(Common::String filename, int x, int y, bool updateScreen = true);
+ void drawPictTransparent(Common::String filename, int x, int y, uint32 transparency, bool updateScreen = true);
+ void setCursor(uint16 cursor);
+ uint32 getColor(byte r, byte g, byte b);
+
+private:
+ PegasusEngine *_vm;
+ Graphics::PictDecoder *_pictDecoder;
+
+ Graphics::Surface *decodeImage(const Common::String &filename);
+ ImageCache _cache[kImageCacheSize];
+ int getImageSlot(const Common::String &filename);
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/menu.cpp b/engines/pegasus/menu.cpp
new file mode 100644
index 0000000000..a498a65755
--- /dev/null
+++ b/engines/pegasus/menu.cpp
@@ -0,0 +1,192 @@
+/* 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/events.h"
+
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+enum {
+ kInterfaceOverviewButton = 0,
+ kStartButton = 1,
+ kRestoreButton = 2,
+ kDifficultyButton = 3,
+ kCreditsButton = 4,
+ kQuitButton = 5
+};
+
+enum {
+ kDemoStartButton = 0,
+ kDemoCreditsButton = 1,
+ kDemoQuitButton = 2
+};
+
+void PegasusEngine::runMainMenu() {
+ _sound->playSound("Sounds/Main Menu.aiff", true);
+
+ // Note down how long since the last click
+ uint32 lastClickTime = _system->getMillis();
+
+ int buttonSelected = 0;
+ drawMenu(buttonSelected);
+
+ while (!shouldQuit() && _system->getMillis() - lastClickTime < 60 * 1000) {
+ Common::Event event;
+
+ // Ignore events for now
+ while (_eventMan->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_UP:
+ if (buttonSelected > 0) {
+ buttonSelected--;
+ drawMenu(buttonSelected);
+ }
+ break;
+ case Common::KEYCODE_DOWN:
+ if ((isDemo() && buttonSelected < 2) || (!isDemo() && buttonSelected < 5)) {
+ buttonSelected++;
+ drawMenu(buttonSelected);
+ }
+ break;
+ case Common::KEYCODE_LEFT:
+ case Common::KEYCODE_RIGHT:
+ if (buttonSelected == kDifficultyButton) {
+ _adventureMode = !_adventureMode;
+ drawMenu(buttonSelected);
+ }
+ break;
+ case Common::KEYCODE_RETURN:
+ if (buttonSelected != kDifficultyButton) {
+ drawMenuButtonSelected(buttonSelected);
+ setGameMode(buttonSelected);
+ _sound->stopSound();
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // Update our last press time too
+ lastClickTime = _system->getMillis();
+ break;
+ default:
+ break;
+ }
+ }
+
+ //_system->updateScreen();
+ _system->delayMillis(10);
+ }
+
+ if (shouldQuit())
+ return;
+
+ // Too slow! Go back and show the intro again.
+ _sound->stopSound();
+ _video->playMovie("Images/Opening_Closing/LilMovie.movie");
+}
+
+void PegasusEngine::drawMenu(int buttonSelected) {
+ if (isDemo()) {
+ _gfx->drawPict("Images/Demo/DemoMenu.pict", 0, 0, false);
+ } else {
+ _gfx->drawPict("Images/Main Menu/MainMenu.mac", 0, 0, false);
+ if (!_adventureMode)
+ _gfx->drawPict("Images/Main Menu/BtnWlk.pict", 320, 340, false);
+ }
+
+ drawMenuButtonHighlighted(buttonSelected);
+}
+
+// FIXME: Most of these coordinates can use tweaking
+
+static const int kMainMenuButtonX = 152;
+static const char s_mainMenuButtonSuffix[] = { 'L', 'S', 'S', 'L', 'S', 'S' };
+static const int s_mainMenuButtonY[] = { 202, 252, 292, 337, 382, 422 };
+static const char s_demoMainMenuButtonSuffix[] = { 'S', 'S', 'L' }; // SSL!
+static const int s_demoMainMenuButtonX[] = { 38, 38, 28 };
+static const int s_demoMainMenuButtonY[] = { 332, 366, 408 };
+
+void PegasusEngine::drawMenuButtonHighlighted(int buttonSelected) {
+ if (isDemo())
+ _gfx->drawPictTransparent(Common::String("Images/Demo/Select") + s_demoMainMenuButtonSuffix[buttonSelected] + ".pict", s_demoMainMenuButtonX[buttonSelected], s_demoMainMenuButtonY[buttonSelected], _gfx->getColor(0xff, 0xff, 0xff), true);
+ else
+ _gfx->drawPictTransparent(Common::String("Images/Main Menu/Select") + s_mainMenuButtonSuffix[buttonSelected] + ".pict", kMainMenuButtonX, s_mainMenuButtonY[buttonSelected], _gfx->getColor(0xf8, 0xf8, 0xf8), true);
+}
+
+static const char *s_mainMenuButtonSelSuffix[] = { "Overvi", "Start", "Restor", "", "Credit", "Quit" };
+static const char *s_demoMainMenuButtonSel[] = { "Start", "Credits", "Quit" };
+static const int s_mainMenuSelButtonX[] = { 198, 210, 210, 0, 210, 210 };
+static const int s_demoMainMenuSelButtonX[] = { 43, 43, 34 };
+static const int s_demoMainMenuSelButtonY[] = { 338, 373, 410 };
+
+void PegasusEngine::drawMenuButtonSelected(int buttonSelected) {
+ if (isDemo())
+ _gfx->drawPict(Common::String("Images/Demo/") + s_demoMainMenuButtonSel[buttonSelected] + ".pict", s_demoMainMenuSelButtonX[buttonSelected], s_demoMainMenuSelButtonY[buttonSelected], false);
+ else
+ _gfx->drawPict(Common::String("Images/Main Menu/pb") + s_mainMenuButtonSelSuffix[buttonSelected] + ".pict", s_mainMenuSelButtonX[buttonSelected], s_mainMenuButtonY[buttonSelected] + 5, false);
+
+ drawMenuButtonHighlighted(buttonSelected);
+}
+
+void PegasusEngine::setGameMode(int buttonSelected) {
+ if (isDemo()) {
+ switch (buttonSelected) {
+ case kDemoStartButton:
+ _gameMode = kMainGameMode;
+ break;
+ case kDemoCreditsButton:
+ _gameMode = kCreditsMode;
+ break;
+ case kDemoQuitButton:
+ _gameMode = kQuitMode;
+ break;
+ }
+ } else {
+ switch (buttonSelected) {
+ case kInterfaceOverviewButton:
+ _gameMode = kInterfaceOverviewMode;
+ break;
+ case kStartButton:
+ _gameMode = kMainGameMode;
+ break;
+ case kRestoreButton:
+ _gameMode = kRestoreMode;
+ break;
+ case kCreditsButton:
+ _gameMode = kCreditsMode;
+ break;
+ case kQuitButton:
+ _gameMode = kQuitMode;
+ break;
+ }
+ }
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/module.mk b/engines/pegasus/module.mk
new file mode 100644
index 0000000000..28a92aa800
--- /dev/null
+++ b/engines/pegasus/module.mk
@@ -0,0 +1,18 @@
+MODULE := engines/pegasus
+
+MODULE_OBJS = \
+ detection.o \
+ graphics.o \
+ menu.o \
+ pegasus.o \
+ sound.o \
+ video.o
+
+
+# This module can be built as a plugin
+ifeq ($(ENABLE_PEGASUS), DYNAMIC_PLUGIN)
+PLUGIN := 1
+endif
+
+# Include common rules
+include $(srcdir)/rules.mk
diff --git a/engines/pegasus/pegasus.cpp b/engines/pegasus/pegasus.cpp
new file mode 100644
index 0000000000..062ec10ef9
--- /dev/null
+++ b/engines/pegasus/pegasus.cpp
@@ -0,0 +1,317 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "common/config-manager.h"
+#include "common/events.h"
+#include "base/plugins.h"
+#include "base/version.h"
+
+#include "pegasus/pegasus.h"
+
+#include "common/file.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;
+}
+
+Common::Error PegasusEngine::run() {
+ _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 = kMainMenuMode;
+ _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 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 kMainMenuMode:
+ if (!isDemo())
+ runIntro();
+
+ 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;
+}
+
+void PegasusEngine::loadItemLocationData() {
+ Common::SeekableReadStream *res = _resFork->getResource(MKID_BE('NItm'), 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() {
+ // The Opening/Closing folder will need to be renamed to something else. Windows
+ // and other OS's/FS's do not support a '/' in the filename. I arbitrarily chose
+ // to rename my folder with the underscore.
+ _video->playMovieCentered("Images/Opening_Closing/BandaiLogo.movie");
+ VideoHandle handle = _video->playBackgroundMovie("Images/Opening_Closing/Big Movie.movie");
+ _video->seekToTime(handle, 10 * 600);
+ _video->waitUntilMovieEnds(handle);
+}
+
+static const int kViewScreenOffset = 64;
+
+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::drawInterfaceOverview() {
+ _gfx->drawPict("Images/Interface/OVTop.mac", 0, 0, false);
+ _gfx->drawPict("Images/Interface/OVLeft.mac", 0, kViewScreenOffset, false);
+ _gfx->drawPict("Images/Interface/OVRight.mac", 640 - kViewScreenOffset, kViewScreenOffset, false);
+ _gfx->drawPict("Images/Interface/OVBottom.mac", 0, kViewScreenOffset + 256, false);
+ _system->updateScreen();
+}
+
+void PegasusEngine::mainGameLoop() {
+ // TODO: Yeah...
+ _system->fillScreen(0);
+ _video->playMovieCentered("Images/Caldoria/Pullback.movie");
+ drawInterface();
+ if (isDemo())
+ _video->playMovie("Images/Prehistoric/Prehistoric.movie", kViewScreenOffset, kViewScreenOffset);
+ else
+ _video->playMovie("Images/Caldoria/Caldoria.movie", 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(MKID_BE('View'), 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(MKID_BE('Door'), 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(MKID_BE('Spot'), 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(MKID_BE('Zoom'), 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(MKID_BE('Xtra'), 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;
+}
+
+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];
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/pegasus.h b/engines/pegasus/pegasus.h
new file mode 100644
index 0000000000..b4f4d8f98e
--- /dev/null
+++ b/engines/pegasus/pegasus.h
@@ -0,0 +1,250 @@
+/* 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$
+ *
+ */
+
+#ifndef PEGASUS_H
+#define PEGASUS_H
+
+#include "common/macresman.h"
+#include "common/scummsys.h"
+#include "common/system.h"
+#include "common/rect.h"
+#include "common/util.h"
+
+#include "engines/engine.h"
+
+#include "pegasus/sound.h"
+#include "pegasus/graphics.h"
+#include "pegasus/video.h"
+
+namespace Pegasus {
+
+struct PegasusGameDescription;
+class SoundManager;
+class VideoManager;
+class GraphicsManager;
+
+enum ItemLocation {
+ kItemLocationCaldoria = 0,
+ kItemLocationTSA = 1,
+ kItemLocationNorad = 4, // ???
+ kItemLocationMars = 5,
+ kItemLocationWSC = 6,
+ kItemLocationPrehistoric = 7,
+ kItemLocationBuiltIn = 0xffff
+};
+
+struct ItemLocationData {
+ uint16 id;
+ ItemLocation location;
+ uint16 u0;
+ byte u1;
+};
+
+struct View {
+ uint16 u0;
+ byte u1;
+ byte u2;
+ uint32 frameTime;
+};
+
+struct Door {
+ uint16 u0;
+ uint16 u1;
+ uint32 startTime;
+ uint32 endTime;
+ uint16 u2;
+};
+
+struct SoundSpot {
+ uint16 u0;
+ uint16 u1;
+ uint16 u2;
+ uint32 startTime;
+ uint32 endTime;
+ uint16 u3;
+};
+
+struct Zoom {
+ uint16 u0;
+ uint32 startTime;
+ uint32 endTime;
+ uint16 u1;
+ uint16 u2;
+};
+
+struct Extra {
+ uint32 u0;
+ uint32 startTime;
+ uint32 endTime;
+};
+
+struct InventoryPanelEntry {
+ uint32 startTime;
+ uint32 endTime;
+};
+
+struct InventoryItemData {
+ uint32 leftFrameTime;
+ uint32 rightStartTime;
+ uint32 rightEndTime;
+ uint16 pict; // Cannot use item at this spot
+ uint16 usablePict; // Can use item at this spot
+};
+
+struct InventoryExtra {
+ uint32 id;
+ uint16 movie;
+ uint32 startTime;
+ uint32 endTime;
+};
+
+struct LeftAreaData {
+ uint16 frame;
+ uint32 time;
+};
+
+struct MiddleAreaData {
+ uint16 id;
+ uint32 time;
+};
+
+struct RightAreaData {
+ uint16 frame;
+ uint32 time;
+};
+
+enum TimeZone {
+ kLocPrehistoric = 0,
+ kLocMars = 1,
+ kLocWSC = 2,
+ kLocTinyTSA = 3,
+ kLocFullTSA = 4,
+ kLocNoradAlpha = 5,
+ kLocCaldoria = 6,
+ kLocNoradDelta = 7
+};
+
+// Taken from JMP PP Resources
+enum Item {
+ kAIBiochip = 128,
+ kInterfaceBiochip = 129, // NOT USED!
+ kMapBiochip = 130,
+ kOpticalBiochip = 131,
+ kPegasusBiochip = 132,
+ kRetinalScanBiochip = 133,
+ kShieldBiochip = 134,
+ kAirMask = 135,
+ kAntidote = 136,
+ kArgonCanister = 137,
+ kCardBomb = 138,
+ kCrowbar = 139,
+ kGasCanister = 140,
+ kHistoricalLog = 141,
+ kJourneymanKey = 142,
+ kKeyCard = 143,
+ kMachineGun = 144, // What the hell is this?
+ kMarsCard = 145,
+ kNitrogenCanister = 146,
+ kOrangeJuiceGlassFull = 147,
+ kOrangeJuiceGlassEmpty = 148,
+ kPoisonDart = 149,
+ kSinclairKey = 150,
+ kStunGun = 151,
+ kArgonPickup = 152 // ???
+};
+
+enum GameMode {
+ kMainMenuMode,
+ kMainGameMode,
+ kCreditsMode,
+ kInterfaceOverviewMode,
+ kRestoreMode,
+ kQuitMode
+};
+
+class PegasusEngine : public ::Engine {
+protected:
+ Common::Error run();
+
+public:
+ PegasusEngine(OSystem *syst, const PegasusGameDescription *gamedesc);
+ virtual ~PegasusEngine();
+
+ const PegasusGameDescription *_gameDescription;
+ bool hasFeature(EngineFeature f) const;
+
+ VideoManager *_video;
+ SoundManager *_sound;
+ GraphicsManager *_gfx;
+ Common::MacResManager *_resFork, *_inventoryLid, *_biochipLid;
+
+ bool isDemo() const;
+
+private:
+ // Intro
+ void runIntro();
+ void runMainMenu();
+ void drawMenu(int buttonSelected);
+ void drawMenuButtonHighlighted(int buttonSelected);
+ void drawMenuButtonSelected(int buttonSelected);
+ //void runInterfaceOverview();
+ void setGameMode(int buttonSelected);
+
+ // Interface
+ void drawInterface();
+ //void drawCompass();
+ //void runPauseMenu();
+
+ // Interface Overview
+ void drawInterfaceOverview();
+
+ // Main Game Functions
+ void mainGameLoop();
+ void loadItemLocationData();
+ void changeLocation(TimeZone timeZone);
+ void loadViews(TimeZone timeZone);
+ void loadDoors(TimeZone timeZone);
+ void loadSoundSpots(TimeZone timeZone);
+ void loadZooms(TimeZone timeZone);
+ void loadExtras(TimeZone timeZone);
+
+ // Misc Functions
+ static Common::String getTimeZoneDesc(TimeZone timeZone);
+
+ // Game Variables
+ bool _adventureMode;
+ GameMode _gameMode;
+ TimeZone _timeZone;
+ Common::Array<ItemLocationData> _itemLocationData;
+ Common::Array<View> _currentViews;
+ Common::Array<Door> _currentDoors;
+ Common::Array<SoundSpot> _currentSoundSpots;
+ Common::Array<Zoom> _currentZooms;
+ Common::Array<Extra> _currentExtras;
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/sound.cpp b/engines/pegasus/sound.cpp
new file mode 100644
index 0000000000..c03e4be2b5
--- /dev/null
+++ b/engines/pegasus/sound.cpp
@@ -0,0 +1,88 @@
+/* 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 "pegasus/sound.h"
+
+#include "common/file.h"
+#include "audio/decoders/aiff.h"
+
+namespace Pegasus {
+
+SoundManager::SoundManager(PegasusEngine *vm) : _vm(vm) {
+}
+
+void SoundManager::playSound(Common::String filename, bool loop) {
+ SndHandle *handle = getHandle();
+ handle->type = kUsedHandle;
+
+ Common::File *file = new Common::File();
+ if (!file->open(filename.c_str()))
+ error("Could not open file \'%s\'", filename.c_str());
+
+ Audio::AudioStream* audStream = Audio::makeAIFFStream(file, DisposeAfterUse::YES);
+
+ if (loop)
+ audStream = Audio::makeLoopingAudioStream((Audio::RewindableAudioStream*)audStream, 0);
+
+ if (audStream)
+ _vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &handle->handle, audStream);
+}
+
+SndHandle *SoundManager::getHandle() {
+ for (int i = 0; i < SOUND_HANDLES; i++) {
+ if (_handles[i].type == kFreeHandle)
+ return &_handles[i];
+
+ if (!_vm->_mixer->isSoundHandleActive(_handles[i].handle)) {
+ _handles[i].type = kFreeHandle;
+ return &_handles[i];
+ }
+ }
+
+ error("SoundManager::getHandle(): Too many sound handles");
+ return NULL;
+}
+
+bool SoundManager::isPlaying() {
+ for (int i = 0; i < SOUND_HANDLES; i++)
+ if (_handles[i].type == kUsedHandle)
+ if (_vm->_mixer->isSoundHandleActive(_handles[i].handle))
+ return true;
+ return false;
+}
+
+void SoundManager::stopSound() {
+ _vm->_mixer->stopAll();
+}
+
+void SoundManager::pauseSound() {
+ _vm->_mixer->pauseAll(true);
+}
+
+void SoundManager::resumeSound() {
+ _vm->_mixer->pauseAll(false);
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/sound.h b/engines/pegasus/sound.h
new file mode 100644
index 0000000000..adfb502c6c
--- /dev/null
+++ b/engines/pegasus/sound.h
@@ -0,0 +1,72 @@
+/* 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$
+ *
+ */
+
+#ifndef PEGASUS_SOUND_H
+#define PEGASUS_SOUND_H
+
+#include "common/scummsys.h"
+#include "common/str.h"
+
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+#include "pegasus/pegasus.h"
+
+namespace Pegasus {
+
+#define SOUND_HANDLES 10
+
+enum sndHandleType {
+ kFreeHandle,
+ kUsedHandle
+};
+
+struct SndHandle {
+ Audio::SoundHandle handle;
+ sndHandleType type;
+};
+
+class PegasusEngine;
+
+class SoundManager {
+public:
+ SoundManager(PegasusEngine *vm);
+
+ void playSound(Common::String filename, bool loop = false);
+ void stopSound();
+ void pauseSound();
+ void resumeSound();
+ bool isPlaying();
+
+private:
+ PegasusEngine *_vm;
+
+ SndHandle _handles[SOUND_HANDLES];
+ SndHandle *getHandle();
+};
+
+} // End of namespace Pegasus
+
+#endif
diff --git a/engines/pegasus/video.cpp b/engines/pegasus/video.cpp
new file mode 100644
index 0000000000..925c6d77a9
--- /dev/null
+++ b/engines/pegasus/video.cpp
@@ -0,0 +1,240 @@
+/* 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 "pegasus/pegasus.h"
+#include "pegasus/video.h"
+
+#include "common/events.h"
+#include "graphics/scaler.h"
+#include "video/qt_decoder.h"
+
+namespace Pegasus {
+
+VideoManager::VideoManager(PegasusEngine *vm) : _vm(vm) {
+ _timeZoneVideo = new Video::QuickTimeDecoder();
+}
+
+VideoManager::~VideoManager() {
+ stopVideos();
+ delete _timeZoneVideo;
+}
+
+void VideoManager::setTimeZoneVideo(const Common::String &filename) {
+ if (!_timeZoneVideo->loadFile(filename))
+ error("Could not load time zone video '%s'", filename.c_str());
+
+ // Set it on pause
+ _timeZoneVideo->pauseVideo(true);
+}
+
+void VideoManager::drawTimeZoneVideoFrame(uint32 time) {
+ assert(_timeZoneVideo->isVideoLoaded());
+
+ if (!_timeZoneVideo->isPaused())
+ _timeZoneVideo->pauseVideo(true);
+
+ _timeZoneVideo->seekToTime(Audio::Timestamp(0, time, 600));
+
+ const Graphics::Surface *frame = _timeZoneVideo->decodeNextFrame();
+
+ if (!frame)
+ error("Could not find frame at time %d", time);
+
+ // TODO
+}
+
+void VideoManager::playTimeZoneVideoSegment(uint32 startTime, uint32 endTime) {
+ assert(_timeZoneVideo->isVideoLoaded());
+
+ if (_timeZoneVideo->isPaused())
+ _timeZoneVideo->pauseVideo(false);
+
+ _timeZoneVideo->seekToTime(Audio::Timestamp(0, startTime, 600));
+
+ // TODO
+}
+
+void VideoManager::pauseVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ _videoStreams[i]->pauseVideo(true);
+}
+
+void VideoManager::resumeVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++)
+ _videoStreams[i]->pauseVideo(false);
+}
+
+void VideoManager::stopVideos() {
+ for (uint16 i = 0; i < _videoStreams.size(); i++) {
+ delete _videoStreams[i].video;
+ _videoStreams[i].video = 0;
+ }
+}
+
+void VideoManager::playMovie(Common::String filename, uint16 x, uint16 y) {
+ VideoHandle videoHandle = playBackgroundMovie(filename, x, y, false);
+
+ if (videoHandle != NULL_VID_HANDLE)
+ waitUntilMovieEnds(videoHandle);
+}
+
+void VideoManager::playMovieCentered(Common::String filename) {
+ VideoHandle videoHandle = playBackgroundMovie(filename, 0, 0, false);
+
+ if (videoHandle == NULL_VID_HANDLE)
+ return;
+
+ _videoStreams[videoHandle].x = (_vm->_system->getWidth() - _videoStreams[videoHandle]->getWidth()) / 2;
+ _videoStreams[videoHandle].y = (_vm->_system->getHeight() - _videoStreams[videoHandle]->getHeight()) / 2;
+
+ waitUntilMovieEnds(videoHandle);
+}
+
+void VideoManager::waitUntilMovieEnds(VideoHandle videoHandle) {
+ bool continuePlaying = true;
+
+ while (!_videoStreams[videoHandle]->endOfVideo() && !_vm->shouldQuit() && continuePlaying) {
+ if (updateBackgroundMovies())
+ _vm->_system->updateScreen();
+
+ Common::Event event;
+ while (_vm->_system->getEventManager()->pollEvent(event)) {
+ switch (event.type) {
+ case Common::EVENT_RTL:
+ case Common::EVENT_QUIT:
+ continuePlaying = false;
+ break;
+ case Common::EVENT_KEYDOWN:
+ switch (event.kbd.keycode) {
+ case Common::KEYCODE_ESCAPE:
+ continuePlaying = false;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ // Cut down on CPU usage
+ _vm->_system->delayMillis(10);
+ }
+
+ delete _videoStreams[videoHandle].video;
+ _videoStreams.clear();
+}
+
+bool VideoManager::updateBackgroundMovies() {
+ bool updateScreen = false;
+
+ for (uint32 i = 0; i < _videoStreams.size() && !_vm->shouldQuit(); i++) {
+ // Skip deleted videos
+ if (!_videoStreams[i].video)
+ continue;
+
+ // Remove any videos that are over
+ if (_videoStreams[i]->endOfVideo()) {
+ if (_videoStreams[i].loop) {
+ _videoStreams[i]->rewind();
+ } else {
+ delete _videoStreams[i].video;
+ memset(&_videoStreams[i], 0, sizeof(VideoEntry));
+ _videoStreams[i].video = NULL;
+ continue;
+ }
+ }
+
+ // Check if we need to draw a frame
+ if (_videoStreams[i]->needsUpdate()) {
+ const Graphics::Surface *frame = _videoStreams[i]->decodeNextFrame();
+
+ if (frame) {
+ if (frame->bytesPerPixel == 1)
+ error("Unhandled 8bpp frames"); // Cut out because Pegasus Prime shouldn't need this
+
+ // Clip the width/height to make sure we stay on the screen
+ uint16 width = MIN<int32>(_videoStreams[i]->getWidth(), _vm->_system->getWidth() - _videoStreams[i].x);
+ uint16 height = MIN<int32>(_videoStreams[i]->getHeight(), _vm->_system->getHeight() - _videoStreams[i].y);
+
+ if (width == 320 && height == 240) {
+ // TODO: Is this right? At least "Big Movie" and the "Sub Chase Movie" need to be scaled...
+ // FIXME: Normal2x is only compiled in when USE_SCALERS is defined
+ _videoStreams[i].x = 0;
+ _videoStreams[i].y = 0;
+ Graphics::Surface scaledSurf;
+ scaledSurf.create(frame->w * 2, frame->h * 2, frame->bytesPerPixel);
+ Normal2x((byte *)frame->pixels, frame->pitch, (byte *)scaledSurf.pixels, scaledSurf.pitch, frame->w, frame->h);
+ _vm->_system->copyRectToScreen((byte*)scaledSurf.pixels, scaledSurf.pitch, _videoStreams[i].x, _videoStreams[i].y, width * 2, height * 2);
+ scaledSurf.free();
+ } else
+ _vm->_system->copyRectToScreen((byte*)frame->pixels, frame->pitch, _videoStreams[i].x, _videoStreams[i].y, width, height);
+
+
+ // We've drawn something to the screen, make sure we update it
+ updateScreen = true;
+ }
+ }
+ }
+
+ // Return true if we need to update the screen
+ return updateScreen;
+}
+
+VideoHandle VideoManager::playBackgroundMovie(Common::String filename, int x, int y, bool loop) {
+ // First, check to see if that video is already playing
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (_videoStreams[i].filename == filename)
+ return i;
+
+ // Otherwise, create a new entry
+ VideoEntry entry;
+ entry.video = new Video::QuickTimeDecoder();
+ entry.x = x;
+ entry.y = y;
+ entry.filename = filename;
+ entry.loop = loop;
+
+ if (!entry->loadFile(filename))
+ return NULL_VID_HANDLE;
+
+ // Search for any deleted videos so we can take a formerly used slot
+ for (uint32 i = 0; i < _videoStreams.size(); i++)
+ if (!_videoStreams[i].video) {
+ _videoStreams[i] = entry;
+ return i;
+ }
+
+ // Otherwise, just add it to the list
+ _videoStreams.push_back(entry);
+ return _videoStreams.size() - 1;
+}
+
+void VideoManager::seekToTime(VideoHandle handle, uint32 time) {
+ if (handle != NULL_VID_HANDLE)
+ _videoStreams[handle]->seekToTime(Audio::Timestamp(0, time, 600));
+}
+
+} // End of namespace Pegasus
diff --git a/engines/pegasus/video.h b/engines/pegasus/video.h
new file mode 100644
index 0000000000..03f0a51bf2
--- /dev/null
+++ b/engines/pegasus/video.h
@@ -0,0 +1,92 @@
+/* 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$
+ *
+ */
+
+#ifndef PEGASUS_VIDEO_H
+#define PEGASUS_VIDEO_H
+
+#include "common/array.h"
+
+namespace Common {
+ class String;
+}
+
+namespace Video {
+ class QuickTimeDecoder;
+}
+
+namespace Pegasus {
+
+class PegasusEngine;
+
+struct VideoEntry {
+ Video::QuickTimeDecoder *video;
+ uint16 x;
+ uint16 y;
+ bool loop;
+ Common::String filename;
+
+ Video::QuickTimeDecoder *operator->() const { assert(video); return video; }
+};
+
+typedef int32 VideoHandle;
+
+enum {
+ NULL_VID_HANDLE = -1
+};
+
+class VideoManager {
+public:
+ VideoManager(PegasusEngine *vm);
+ ~VideoManager();
+
+ void setTimeZoneVideo(const Common::String &filename);
+ void drawTimeZoneVideoFrame(uint32 time);
+ void playTimeZoneVideoSegment(uint32 startTime, uint32 endTime);
+
+ // Generic movie functions
+ void playMovie(Common::String filename, uint16 x = 0, uint16 y = 0);
+ void playMovieCentered(Common::String filename);
+ VideoHandle playBackgroundMovie(Common::String filename, int x = 0, int y = 0, bool loop = false);
+ bool updateBackgroundMovies();
+ void pauseVideos();
+ void resumeVideos();
+ void stopVideos();
+ void waitUntilMovieEnds(VideoHandle videoHandle);
+
+ void seekToTime(VideoHandle handle, uint32 time);
+
+private:
+ PegasusEngine *_vm;
+
+ Video::QuickTimeDecoder *_timeZoneVideo;
+
+ // Keep tabs on any videos playing
+ Common::Array<VideoEntry> _videoStreams;
+ uint32 _pauseStart;
+};
+
+} // End of namespace Pegasus
+
+#endif