diff options
-rw-r--r-- | engines/sherlock/animation.cpp | 104 | ||||
-rw-r--r-- | engines/sherlock/animation.h | 2 | ||||
-rw-r--r-- | engines/sherlock/map.cpp | 4 | ||||
-rw-r--r-- | engines/sherlock/resources.cpp | 154 | ||||
-rw-r--r-- | engines/sherlock/resources.h | 23 | ||||
-rw-r--r-- | engines/sherlock/scalpel/scalpel.cpp | 66 | ||||
-rw-r--r-- | engines/sherlock/scalpel/scalpel.h | 4 | ||||
-rw-r--r-- | engines/sherlock/scalpel/scalpel_user_interface.cpp | 6 | ||||
-rw-r--r-- | engines/sherlock/scalpel/tsage/logo.cpp | 2 | ||||
-rw-r--r-- | engines/sherlock/screen.cpp | 6 | ||||
-rw-r--r-- | engines/sherlock/surface.cpp | 40 | ||||
-rw-r--r-- | engines/sherlock/surface.h | 9 |
12 files changed, 404 insertions, 16 deletions
diff --git a/engines/sherlock/animation.cpp b/engines/sherlock/animation.cpp index 21d63633d3..bc0e337039 100644 --- a/engines/sherlock/animation.cpp +++ b/engines/sherlock/animation.cpp @@ -133,6 +133,110 @@ bool Animation::play(const Common::String &filename, int minDelay, int fade, return !skipped && !_vm->shouldQuit(); } +bool Animation::play3DO(const Common::String &filename, int minDelay, int fade, + int speed) { + Events &events = *_vm->_events; + Screen &screen = *_vm->_screen; + Sound &sound = *_vm->_sound; + //int soundNumber = 0; + + // Check for any any sound frames for the given animation + //const int *soundFrames = checkForSoundFrames(filename); + + // Add on the VDX extension + Common::String indexName = "prologue/" + filename + ".3dx"; + + // Load the animation + Common::File *indexStream = new Common::File(); + + if (!indexStream->open(indexName)) { + warning("unable to open %s\n", indexName); + return false; + } + + // Load initial image + Common::String graphicsName = "prologue/" + filename + ".3da"; + ImageFile3DO images(graphicsName, true); + + events.wait(minDelay); +// if (fade != 0 && fade != 255) +// screen.fadeToBlack(); + +// if (setPalette) { +// if (fade != 255) +// screen.setPalette(images._palette); +// } + + //int frameNumber = 0; + Common::Point pt; + bool skipped = false; + while (!_vm->shouldQuit()) { + // Get the next sprite to display + int imageFrame = indexStream->readSint16BE(); + + if (imageFrame == -2) { + // End of animation reached + break; + } else if (imageFrame != -1) { + // Read position from either animation stream or the sprite frame itself + if (imageFrame < 0) { + imageFrame += 32768; + pt.x = indexStream->readUint16BE(); + pt.y = indexStream->readUint16BE(); + } else { + pt = images[imageFrame]._offset; + } + + // Draw the sprite. Note that we explicitly use the raw frame below, rather than the ImageFrame, + // since we don't want the offsets in the image file to be used, just the explicit position we specify + screen.transBlitFromUnscaled3DO(images[imageFrame]._frame, pt); + //events.wait(1000); + } else { +#if 0 + // At this point, either the sprites for the frame has been complete, or there weren't any sprites + // at all to draw for the frame + //if (fade == 255) { + // // Gradual fade in + // if (screen.equalizePalette(images._palette) == 0) + // fade = 0; + //} + + // Check if we've reached a frame with sound + if (frameNumber++ == *soundFrames) { + ++soundNumber; + ++soundFrames; + Common::String fname = _soundLibraryFilename.empty() ? + Common::String::format("%s%01d", filename.c_str(), soundNumber) : + Common::String::format("%s%02d", filename.c_str(), soundNumber); + + if (sound._voices) + sound.playSound(fname, WAIT_RETURN_IMMEDIATELY, 100, _soundLibraryFilename.c_str()); + } +#endif + + events.wait(speed * 3); + } + + if (events.kbHit()) { + Common::KeyState keyState = events.getKey(); + if (keyState.keycode == Common::KEYCODE_ESCAPE || + keyState.keycode == Common::KEYCODE_SPACE) { + skipped = true; + break; + } + } else if (events._pressed) { + skipped = true; + break; + } + } + + events.clearEvents(); + sound.stopSound(); + delete indexStream; + + return !skipped && !_vm->shouldQuit(); +} + void Animation::setPrologueNames(const char *const *names, int count) { for (int idx = 0; idx < count; ++idx, ++names) { _prologueNames.push_back(*names); diff --git a/engines/sherlock/animation.h b/engines/sherlock/animation.h index b7811d3fa8..1755edd97b 100644 --- a/engines/sherlock/animation.h +++ b/engines/sherlock/animation.h @@ -76,6 +76,8 @@ public: * Play a full-screen animation */ bool play(const Common::String &filename, int minDelay, int fade, bool setPalette, int speed); + + bool play3DO(const Common::String &filename, int minDelay, int fade, int speed); }; } // End of namespace Sherlock diff --git a/engines/sherlock/map.cpp b/engines/sherlock/map.cpp index 96f93d42d7..4e8ac3ecc6 100644 --- a/engines/sherlock/map.cpp +++ b/engines/sherlock/map.cpp @@ -51,7 +51,7 @@ const byte *MapPaths::getPath(int srcLocation, int destLocation) { /*----------------------------------------------------------------*/ -Map::Map(SherlockEngine *vm): _vm(vm), _topLine(g_system->getWidth(), 12) { +Map::Map(SherlockEngine *vm): _vm(vm), _topLine(g_system->getWidth(), 12, vm->getPlatform()) { _active = false; _mapCursors = nullptr; _shapes = nullptr; @@ -286,7 +286,7 @@ void Map::setupSprites() { _shapes = new ImageFile("mapicon.vgs"); _iconShapes = new ImageFile("overicon.vgs"); - _iconSave.create((*_shapes)[4]._width, (*_shapes)[4]._height); + _iconSave.create((*_shapes)[4]._width, (*_shapes)[4]._height, _vm->getPlatform()); Person &p = people[AL]; p._description = " "; p._type = CHARACTER; diff --git a/engines/sherlock/resources.cpp b/engines/sherlock/resources.cpp index ecc70725d3..d98d3d8ea5 100644 --- a/engines/sherlock/resources.cpp +++ b/engines/sherlock/resources.cpp @@ -566,4 +566,158 @@ int ImageFrame::sDrawYOffset(int scaleVal) const { return result; } +// ******************************************************* + +/*----------------------------------------------------------------*/ + +SherlockEngine *ImageFile3DO::_vm; + +void ImageFile3DO::setVm(SherlockEngine *vm) { + _vm = vm; +} + +ImageFile3DO::ImageFile3DO(const Common::String &name, bool animImages) { + Common::File *dataStream = new Common::File(); + + if (!dataStream->open(name)) { + error("unable to open %s\n", name); + } + + load(*dataStream, animImages); + + delete dataStream; +} + +ImageFile3DO::ImageFile3DO(Common::SeekableReadStream &stream) { + load(stream, false); +} + +ImageFile3DO::~ImageFile3DO() { + for (uint idx = 0; idx < size(); ++idx) + (*this)[idx]._frame.free(); +} + +void ImageFile3DO::load(Common::SeekableReadStream &stream, bool animImages) { + int streamSize = stream.size(); + uint32 compressedSize = 0; + + while (stream.pos() < streamSize) { + ImageFrame frame; + + compressedSize = stream.readUint16BE(); + + frame._width = stream.readUint16BE() + 1; // 2 bytes BE width + frame._height = stream.readByte() + 1; // 1 byte BE height + frame._paletteBase = 0; + + if (animImages) { + // Animation cutscene image files use a 16-bit x offset + frame._offset.x = stream.readUint16BE(); + frame._rleEncoded = true; // always compressed + if (frame._width & 0x8000) { + frame._width &= 0x7FFF; + compressedSize += 0x10000; + } + frame._offset.y = stream.readByte(); + } else { + // Standard image files have a separate byte for the RLE flag, and an 8-bit X offset + //frame._rleEncoded = stream.readByte() == 1; + //frame._offset.x = stream.readByte(); + //frame._offset.y = stream.readByte(); + } + + frame._size = 0; + + //warning("getting frame %d from offset %d", this->size(), stream.pos()); + + // Load data for frame and decompress it + byte *data = new byte[compressedSize]; + stream.read(data, compressedSize); + decompressFrame(&stream, frame, data); + delete[] data; + + push_back(frame); + } +} + +// 3DO uses RGB555, we use RGB565 internally so that more platforms are able to run us +inline uint16 ImageFile3DO::convertPixel(uint16 pixel3DO) { + byte red = (pixel3DO >> 10) & 0x1F; + byte green = (pixel3DO >> 5) & 0x1F; + byte blue = pixel3DO & 0x1F;; + + return ((red << 11) | (green << 6) | (blue)); +} + +void ImageFile3DO::decompressFrame(Common::SeekableReadStream *stream, ImageFrame &frame, const byte *src) { + frame._frame.create(frame._width, frame._height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + uint16 *dest = (uint16 *)frame._frame.getPixels(); + Common::fill(dest, dest + frame._width * frame._height, 0); + + const byte *srcSeeker = src; + + // CEL compression + int frameHeightLeft = frame._height; + int frameWidthLeft = frame._width; + + while (frameHeightLeft > 0) { + frameWidthLeft = frame._width; + + uint16 dwordSize = READ_BE_UINT16(srcSeeker); + dwordSize += 2; + uint16 lineByteSize = dwordSize * 4; + + // debug + //warning("offset %d: decoding line, size %d, bytesize %d", srcSeeker - src, dwordSize, lineByteSize); + + const byte *srcLine = srcSeeker + 2; // start at 3rd byte + while (frameWidthLeft > 0) { + byte compressionByte = *srcLine++; + byte compressionType = compressionByte >> 6; // upper 2 bits == type + byte compressionPixels = (compressionByte & 0x3F) + 1; // lower 6 bits == length (0 = 1 pixel) + uint16 pixelCount; + uint16 pixel; + + if (!compressionType) // end of line + break; + + switch(compressionType) { + case 1: // simple copy + for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) { + pixel = READ_BE_UINT16(srcLine); srcLine += 2; + *dest++ = convertPixel(pixel); + } + break; + case 2: // transparent + for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) { + *dest++ = 0; + } + break; + case 3: // duplicate pixels + pixel = READ_BE_UINT16(srcLine); srcLine += 2; + pixel = convertPixel(pixel); + for (pixelCount = 0; pixelCount < compressionPixels; pixelCount++) { + *dest++ = pixel; + } + break; + default: + break; + } + frameWidthLeft -= compressionPixels; + } + + assert(frameWidthLeft >= 0); + + if (frameWidthLeft > 0) { + // still pixels left? skip them + dest += frameWidthLeft; + } + + frameHeightLeft--; + + // Seek to next line start + srcSeeker += lineByteSize; + } +} + } // End of namespace Sherlock diff --git a/engines/sherlock/resources.h b/engines/sherlock/resources.h index 97d2fb0616..0d820be304 100644 --- a/engines/sherlock/resources.h +++ b/engines/sherlock/resources.h @@ -222,6 +222,29 @@ public: static void setVm(SherlockEngine *vm); }; +class ImageFile3DO : public Common::Array<ImageFrame> { +private: + static SherlockEngine *_vm; + + /** + * Load the data of the sprite + */ + void load(Common::SeekableReadStream &stream, bool animImages); + + /** + * Decompress a single frame for the sprite + */ + void decompressFrame(Common::SeekableReadStream *stream, ImageFrame &frame, const byte *src); + + inline uint16 convertPixel(uint16 pixel3DO); + +public: + ImageFile3DO(const Common::String &name, bool animImages = false); + ImageFile3DO(Common::SeekableReadStream &stream); + ~ImageFile3DO(); + static void setVm(SherlockEngine *vm); +}; + } // End of namespace Sherlock #endif diff --git a/engines/sherlock/scalpel/scalpel.cpp b/engines/sherlock/scalpel/scalpel.cpp index 9d2c3b8d81..e7d8ef18c3 100644 --- a/engines/sherlock/scalpel/scalpel.cpp +++ b/engines/sherlock/scalpel/scalpel.cpp @@ -182,7 +182,16 @@ ScalpelEngine::~ScalpelEngine() { } void ScalpelEngine::initialize() { - initGraphics(320, 200, false); + // 3DO actually uses RGB555, but some platforms of ours only support RGB565, so we use that + const Graphics::PixelFormat *pixelFormatRGB565 = new Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0); + + if (getPlatform() == Common::kPlatform3DO) { + // 320x200 16-bit RGB565 for 3DO support + initGraphics(320, 200, false, pixelFormatRGB565); + } else { + // 320x200 palettized + initGraphics(320, 200, false); + } // Let the base engine intialize SherlockEngine::initialize(); @@ -231,6 +240,14 @@ void ScalpelEngine::showOpening() { // 3DO animations are in directory Prologue/ // .3DX seem to be just like .VDX except that all INT16LE are INT16BE // .3DA however seems to be completely different + if (!showCityCutscene3DO()) + return; + if (!showAlleyCutscene3DO()) + return; + if (!showStreetCutscene3DO()) + return; + if (!showOfficeCutscene3DO()) + return; return; } @@ -507,6 +524,53 @@ bool ScalpelEngine::showOfficeCutscene() { return finished; } +// 3DO variant +bool ScalpelEngine::showCityCutscene3DO() { + + bool finished = _animation->play3DO("26open1", 1, 255, 2); + + if (finished) + finished = _animation->play3DO("26open2", 1, 0, 2); + + return finished; +} + +bool ScalpelEngine::showAlleyCutscene3DO() { + bool finished = _animation->play3DO("27PRO1", 1, 3, 2); + + if (finished) + finished = _animation->play3DO("27PRO2", 1, 0, 2); + + if (finished) + finished = _animation->play3DO("27PRO3", 1, 0, 2); + + return finished; +} + +bool ScalpelEngine::showStreetCutscene3DO() { + bool finished = _animation->play3DO("14KICK", 1, 3, 2); + + if (finished) + finished = _animation->play3DO("14NOTE", 1, 0, 3); + + return finished; +} + +bool ScalpelEngine::showOfficeCutscene3DO() { + bool finished = _animation->play3DO("COFF1", 1, 3, 3); + + if (finished) + finished = _animation->play3DO("COFF2", 1, 0, 3); + + if (finished) + finished = _animation->play3DO("COFF3", 1, 0, 3); + + if (finished) + finished = _animation->play3DO("COFF4", 1, 0, 3); + + return finished; +} + void ScalpelEngine::loadInventory() { Inventory &inv = *_inventory; diff --git a/engines/sherlock/scalpel/scalpel.h b/engines/sherlock/scalpel/scalpel.h index 8743bfb7a9..7cd555a5b1 100644 --- a/engines/sherlock/scalpel/scalpel.h +++ b/engines/sherlock/scalpel/scalpel.h @@ -43,21 +43,25 @@ private: * Show the starting city cutscene which shows the game title */ bool showCityCutscene(); + bool showCityCutscene3DO(); /** * Show the back alley where the initial murder takes place */ bool showAlleyCutscene(); + bool showAlleyCutscene3DO(); /** * Show the Baker Street outside cutscene */ bool showStreetCutscene(); + bool showStreetCutscene3DO(); /** * Show Holmes and Watson at the breakfast table, lestrade's note, and then the scrolling credits */ bool showOfficeCutscene(); + bool showOfficeCutscene3DO(); /** * Show the game credits diff --git a/engines/sherlock/scalpel/scalpel_user_interface.cpp b/engines/sherlock/scalpel/scalpel_user_interface.cpp index 62083586e7..a89ebb61fc 100644 --- a/engines/sherlock/scalpel/scalpel_user_interface.cpp +++ b/engines/sherlock/scalpel/scalpel_user_interface.cpp @@ -1235,7 +1235,7 @@ void ScalpelUserInterface::doLookControl() { } else { // Looking at an inventory object // Backup the user interface - Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1); + Surface tempSurface(SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT - CONTROLS_Y1, _vm->getPlatform()); tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0), Common::Rect(0, CONTROLS_Y1, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT)); @@ -1791,7 +1791,7 @@ void ScalpelUserInterface::printObjectDesc(const Common::String &str, bool first // If it wasn't a right button click, then we need depress // the look button before we close the window. So save a copy of the // menu area, and draw the controls onto it - Surface tempSurface((*_controls)[0]._frame.w, (*_controls)[0]._frame.h); + Surface tempSurface((*_controls)[0]._frame.w, (*_controls)[0]._frame.h, _vm->getPlatform()); Common::Point pt(MENU_POINTS[0][0], MENU_POINTS[0][1]); tempSurface.blitFrom(screen._backBuffer2, Common::Point(0, 0), @@ -1982,7 +1982,7 @@ void ScalpelUserInterface::summonWindow(bool slideUp, int height) { // Extract the window that's been drawn on the back buffer Surface tempSurface(SHERLOCK_SCREEN_WIDTH, - (SHERLOCK_SCREEN_HEIGHT - height)); + (SHERLOCK_SCREEN_HEIGHT - height), _vm->getPlatform()); Common::Rect r(0, height, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT); tempSurface.blitFrom(screen._backBuffer1, Common::Point(0, 0), r); diff --git a/engines/sherlock/scalpel/tsage/logo.cpp b/engines/sherlock/scalpel/tsage/logo.cpp index 072b10f7eb..2ac6af2601 100644 --- a/engines/sherlock/scalpel/tsage/logo.cpp +++ b/engines/sherlock/scalpel/tsage/logo.cpp @@ -104,7 +104,7 @@ void Visage::surfaceFromRes(ObjectSurface &s) { int frameWidth = _stream->readUint16LE(); int frameHeight = _stream->readUint16LE(); Common::Rect r(0, 0, frameWidth, frameHeight); - s.create(r.width(), r.height()); + s.create(r.width(), r.height(), Common::kPlatformDOS); // maybe change this to a bool later? TODO s._centroid.x = _stream->readSint16LE(); s._centroid.y = _stream->readSint16LE(); diff --git a/engines/sherlock/screen.cpp b/engines/sherlock/screen.cpp index adf3129169..f0c993a708 100644 --- a/engines/sherlock/screen.cpp +++ b/engines/sherlock/screen.cpp @@ -28,9 +28,9 @@ namespace Sherlock { -Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->getHeight()), _vm(vm), - _backBuffer1(g_system->getWidth(), g_system->getHeight()), - _backBuffer2(g_system->getWidth(), g_system->getHeight()), +Screen::Screen(SherlockEngine *vm) : Surface(g_system->getWidth(), g_system->getHeight(), vm->getPlatform()), _vm(vm), + _backBuffer1(g_system->getWidth(), g_system->getHeight(), vm->getPlatform()), + _backBuffer2(g_system->getWidth(), g_system->getHeight(), vm->getPlatform()), _backBuffer(&_backBuffer1) { _transitionSeed = 1; _fadeStyle = false; diff --git a/engines/sherlock/surface.cpp b/engines/sherlock/surface.cpp index 006b31ecda..3bbaf4677e 100644 --- a/engines/sherlock/surface.cpp +++ b/engines/sherlock/surface.cpp @@ -30,8 +30,8 @@ namespace Sherlock { const int TRANSPARENCY = 0xFF; -Surface::Surface(uint16 width, uint16 height) : _freePixels(true) { - create(width, height); +Surface::Surface(uint16 width, uint16 height, Common::Platform platform) : _freePixels(true) { + create(width, height, platform); } Surface::Surface() : _freePixels(false) { @@ -42,11 +42,15 @@ Surface::~Surface() { _surface.free(); } -void Surface::create(uint16 width, uint16 height) { +void Surface::create(uint16 width, uint16 height, Common::Platform platform) { if (_freePixels) _surface.free(); - _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + if (platform == Common::kPlatform3DO) { + _surface.create(width, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0)); + } else { + _surface.create(width, height, Graphics::PixelFormat::createFormatCLUT8()); + } _freePixels = true; } @@ -185,6 +189,34 @@ void Surface::transBlitFromUnscaled(const Graphics::Surface &src, const Common:: } } +// TODO: Needs to get implemented properly +void Surface::transBlitFromUnscaled3DO(const Graphics::Surface &src, const Common::Point &pt) { + Common::Rect drawRect(0, 0, src.w, src.h); + Common::Rect destRect(pt.x, pt.y, pt.x + src.w, pt.y + src.h); + + // Clip the display area to on-screen + if (!clip(drawRect, destRect)) + // It's completely off-screen + return; + + Common::Point destPt(destRect.left, destRect.top); + addDirtyRect(Common::Rect(destPt.x, destPt.y, destPt.x + drawRect.width(), + destPt.y + drawRect.height())); + + // Draw loop + for (int yp = 0; yp < drawRect.height(); ++yp) { + const uint16 *srcP = (const uint16 *)src.getBasePtr(drawRect.left, drawRect.top + yp); + uint16 *destP = (uint16 *)getBasePtr(destPt.x, destPt.y + yp); + + for (int xp = 0; xp < drawRect.width(); ++xp, ++destP) { + if (*srcP) // 0 = transparent on 3DO + *destP = *srcP; + + srcP = srcP + 1; + } + } +} + void Surface::fillRect(int x1, int y1, int x2, int y2, byte color) { fillRect(Common::Rect(x1, y1, x2, y2), color); } diff --git a/engines/sherlock/surface.h b/engines/sherlock/surface.h index 94299b45cb..68108a6c16 100644 --- a/engines/sherlock/surface.h +++ b/engines/sherlock/surface.h @@ -24,6 +24,7 @@ #define SHERLOCK_GRAPHICS_H #include "common/rect.h" +#include "common/platform.h" #include "graphics/surface.h" namespace Sherlock { @@ -61,12 +62,16 @@ private: */ void transBlitFromUnscaled(const Graphics::Surface &src, const Common::Point &pt, bool flipped, int overrideColor); + +public: + void transBlitFromUnscaled3DO(const Graphics::Surface &src, const Common::Point &pt); + protected: Graphics::Surface _surface; virtual void addDirtyRect(const Common::Rect &r) {} public: - Surface(uint16 width, uint16 height); + Surface(uint16 width, uint16 height, Common::Platform platform); Surface(); virtual ~Surface(); @@ -74,7 +79,7 @@ public: * Sets up an internal surface with the specified dimensions that will be automatically freed * when the surface object is destroyed */ - void create(uint16 width, uint16 height); + void create(uint16 width, uint16 height, Common::Platform platform); /** * Copy a surface into this one |