diff options
Diffstat (limited to 'engines/sword1')
-rw-r--r-- | engines/sword1/animation.cpp | 303 | ||||
-rw-r--r-- | engines/sword1/animation.h | 18 | ||||
-rw-r--r-- | engines/sword1/detection.cpp | 1 | ||||
-rw-r--r-- | engines/sword1/sword1.cpp | 5 |
4 files changed, 276 insertions, 51 deletions
diff --git a/engines/sword1/animation.cpp b/engines/sword1/animation.cpp index d55a08293e..1e2964054a 100644 --- a/engines/sword1/animation.cpp +++ b/engines/sword1/animation.cpp @@ -37,6 +37,11 @@ #include "gui/message.h" +#include "video/psx_decoder.h" +#include "video/smk_decoder.h" + +#include "engines/util.h" + namespace Sword1 { static const char *const sequenceList[20] = { @@ -62,6 +67,31 @@ static const char *const sequenceList[20] = { "credits", // 19 CD2 credits, to follow "finale" sequence }; +// This is the list of the names of the PlayStation videos +// TODO: fight.str, flashy.str, +static const char *const sequenceListPSX[20] = { + "e_ferr1", + "ladder1", + "steps1", + "sewer1", + "e_intro1", + "river1", + "truck1", + "grave1", + "montfcn1", + "tapesty1", + "ireland1", + "e_fin1", + "e_hist1", + "spanish1", + "well1", + "candle1", + "geodrop1", + "vulture1", + "", // demo video not present + "" // credits are not a video +}; + /////////////////////////////////////////////////////////////////////////////// // Basic movie player /////////////////////////////////////////////////////////////////////////////// @@ -72,7 +102,7 @@ MoviePlayer::MoviePlayer(SwordEngine *vm, Text *textMan, ResMan *resMan, Audio:: _decoderType = decoderType; _decoder = decoder; - _white = 255; + _c1Color = _c2Color = _c3Color = _c4Color = 255; _black = 0; } @@ -115,7 +145,7 @@ bool MoviePlayer::load(uint32 id) { int startFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); int endFrame = strtoul(ptr, const_cast<char **>(&ptr), 10); - while (*ptr && isspace(static_cast<unsigned char>(*ptr))) + while (*ptr && Common::isSpace(*ptr)) ptr++; if (startFrame > endFrame) { @@ -127,8 +157,16 @@ bool MoviePlayer::load(uint32 id) { warning("%s:%d startFrame (%d) <= lastEnd (%d)", filename.c_str(), lineNo, startFrame, lastEnd); continue; } - - _movieTexts.push_back(MovieText(startFrame, endFrame, ptr)); + + int color = 0; + if (*ptr == '@') { + ++ptr; + color = strtoul(ptr, const_cast<char **>(&ptr), 10); + while (*ptr && Common::isSpace(*ptr)) + ptr++; + } + + _movieTexts.push_back(MovieText(startFrame, endFrame, ptr, color)); lastEnd = endFrame; } f.close(); @@ -142,6 +180,21 @@ bool MoviePlayer::load(uint32 id) { case kVideoDecoderSMK: filename = Common::String::format("%s.smk", sequenceList[id]); break; + case kVideoDecoderPSX: + filename = Common::String::format("%s.str", (_vm->_systemVars.isDemo) ? sequenceList[id] : sequenceListPSX[id]); + + // Need to switch to true color + initGraphics(g_system->getWidth(), g_system->getHeight(), true, 0); + + // Need to load here in case it fails in which case we'd need + // to go back to paletted mode + if (_decoder->loadFile(filename)) { + return true; + } else { + initGraphics(g_system->getWidth(), g_system->getHeight(), true); + return false; + } + break; } return _decoder->loadFile(filename.c_str()); @@ -179,6 +232,11 @@ void MoviePlayer::play() { } void MoviePlayer::performPostProcessing(byte *screen) { + // TODO: We don't support the PSX stream videos yet + // nor using the PSX fonts to display subtitles. + if (_vm->isPsx()) + return; + if (!_movieTexts.empty()) { if (_decoder->getCurFrame() == _movieTexts.front()._startFrame) { _textMan->makeTextSprite(2, (const uint8 *)_movieTexts.front()._text.c_str(), 600, LETTER_COL); @@ -188,6 +246,7 @@ void MoviePlayer::performPostProcessing(byte *screen) { _textHeight = _resMan->toUint16(frame->height); _textX = 320 - _textWidth / 2; _textY = 420 - _textHeight; + _textColor = _movieTexts.front()._color; } if (_decoder->getCurFrame() == _movieTexts.front()._endFrame) { _textMan->releaseText(2, false); @@ -206,10 +265,10 @@ void MoviePlayer::performPostProcessing(byte *screen) { for (x = 0; x < _textWidth; x++) { switch (src[x]) { case BORDER_COL: - dst[x] = findBlackPalIndex(); + dst[x] = getBlackColor(); break; case LETTER_COL: - dst[x] = findWhitePalIndex(); + dst[x] = findTextColor(); break; } } @@ -229,12 +288,12 @@ void MoviePlayer::performPostProcessing(byte *screen) { for (y = 0; y < _textHeight; y++) { if (_textY + y < frameY || _textY + y >= frameY + frameHeight) { - memset(dst + _textX, findBlackPalIndex(), _textWidth); + memset(dst + _textX, getBlackColor(), _textWidth); } else { if (frameX > _textX) - memset(dst + _textX, findBlackPalIndex(), frameX - _textX); + memset(dst + _textX, getBlackColor(), frameX - _textX); if (frameX + frameWidth < _textX + _textWidth) - memset(dst + frameX + frameWidth, findBlackPalIndex(), _textX + _textWidth - (frameX + frameWidth)); + memset(dst + frameX + frameWidth, getBlackColor(), _textX + _textWidth - (frameX + frameWidth)); } dst += _system->getWidth(); @@ -246,40 +305,105 @@ void MoviePlayer::performPostProcessing(byte *screen) { } bool MoviePlayer::playVideo() { + bool skipped = false; uint16 x = (g_system->getWidth() - _decoder->getWidth()) / 2; uint16 y = (g_system->getHeight() - _decoder->getHeight()) / 2; - while (!_vm->shouldQuit() && !_decoder->endOfVideo()) { + while (!_vm->shouldQuit() && !_decoder->endOfVideo() && !skipped) { if (_decoder->needsUpdate()) { const Graphics::Surface *frame = _decoder->decodeNextFrame(); - if (frame) - _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + if (frame) { + if (_decoderType == kVideoDecoderPSX) + drawFramePSX(frame); + else + _vm->_system->copyRectToScreen((byte *)frame->pixels, frame->pitch, x, y, frame->w, frame->h); + } if (_decoder->hasDirtyPalette()) { _decoder->setSystemPalette(); - uint32 maxWeight = 0; - uint32 minWeight = 0xFFFFFFFF; - uint32 weight; - byte r, g, b; - - const byte *palette = _decoder->getPalette(); - - for (int i = 0; i < 256; i++) { - r = *palette++; - g = *palette++; - b = *palette++; - - weight = 3 * r * r + 6 * g * g + 2 * b * b; - - if (weight >= maxWeight) { - maxWeight = weight; - _white = i; - } - - if (weight <= minWeight) { - minWeight = weight; - _black = i; + if (!_movieTexts.empty()) { + // Look for the best color indexes to use to display the subtitles + uint32 minWeight = 0xFFFFFFFF; + uint32 weight; + float c1Weight = 1e+30f; + float c2Weight = 1e+30f; + float c3Weight = 1e+30f; + float c4Weight = 1e+30f; + byte r, g, b; + float h, s, v, hd, hsvWeight; + + const byte *palette = _decoder->getPalette(); + + // Color comparaison for the subtitles colors is done in HSL + // C1 color is used for George and is almost white (R = 248, G = 252, B = 248) + const float h1 = 0.333333f, s1 = 0.02f, v1 = 0.99f; + + // C2 color is used for George as a narrator and is grey (R = 184, G = 188, B = 184) + const float h2 = 0.333333f, s2 = 0.02f, v2 = 0.74f; + + // C3 color is used for Nicole and is rose (R = 200, G = 120, B = 184) + const float h3 = 0.866667f, s3 = 0.4f, v3 = 0.78f; + + // C4 color is used for Maguire and is blue (R = 80, G = 152, B = 184) + const float h4 = 0.55f, s4 = 0.57f, v4 = 0.72f; + + for (int i = 0; i < 256; i++) { + r = *palette++; + g = *palette++; + b = *palette++; + + weight = 3 * r * r + 6 * g * g + 2 * b * b; + + if (weight <= minWeight) { + minWeight = weight; + _black = i; + } + + convertColor(r, g, b, h, s, v); + + // C1 color + // It is almost achromatic (very low saturation) so the hue as litle impact on the color. + // Therefore use a low weight on hue and high weight on saturation. + hd = h - h1; + hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f; + hsvWeight = 1.0f * hd * hd + 4.0f * (s - s1) * (s - s1) + 3.0f * (v - v1) * (v - v1); + if (hsvWeight <= c1Weight) { + c1Weight = hsvWeight; + _c1Color = i; + } + + // C2 color + // Also an almost achromatic color so use the same weights as for C1 color. + hd = h - h2; + hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f; + hsvWeight = 1.0f * hd * hd + 4.0f * (s - s2) * (s - s2) + 3.0f * (v - v2) * (v - v2); + if (hsvWeight <= c2Weight) { + c2Weight = hsvWeight; + _c2Color = i; + } + + // C3 color + // A light rose. Use a high weight on the hue to get a rose. + // The color is a bit gray and the saturation has not much impact so use a low weight. + hd = h - h3; + hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f; + hsvWeight = 4.0f * hd * hd + 1.0f * (s - s3) * (s - s3) + 2.0f * (v - v3) * (v - v3); + if (hsvWeight <= c3Weight) { + c3Weight = hsvWeight; + _c3Color = i; + } + + // C4 color + // Blue. Use a hight weight on the hue to get a blue. + // The color is darker and more saturated than C3 and the saturation has more impact. + hd = h - h4; + hd += hd < -0.5f ? 1.0f : hd > 0.5f ? -1.0f : 0.0f; + hsvWeight = 5.0f * hd * hd + 3.0f * (s - s4) * (s - s4) + 2.0f * (v - v4) * (v - v4); + if (hsvWeight <= c4Weight) { + c4Weight = hsvWeight; + _c4Color = i; + } } } } @@ -293,20 +417,93 @@ bool MoviePlayer::playVideo() { Common::Event event; while (_vm->_system->getEventManager()->pollEvent(event)) if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP) - return false; + skipped = true; _vm->_system->delayMillis(10); } - return !_vm->shouldQuit(); + if (_decoderType == kVideoDecoderPSX) { + // Need to jump back to paletted color + initGraphics(g_system->getWidth(), g_system->getHeight(), true); + } + + return !_vm->shouldQuit() && !skipped; +} + +uint32 MoviePlayer::getBlackColor() { + return (_decoderType == kVideoDecoderPSX) ? g_system->getScreenFormat().RGBToColor(0x00, 0x00, 0x00) : _black; +} + +uint32 MoviePlayer::findTextColor() { + if (_decoderType == kVideoDecoderPSX) { + // We're in true color mode, so return the actual colors + switch (_textColor) { + case 1: + return g_system->getScreenFormat().RGBToColor(248, 252, 248); + case 2: + return g_system->getScreenFormat().RGBToColor(184, 188, 184); + case 3: + return g_system->getScreenFormat().RGBToColor(200, 120, 184); + case 4: + return g_system->getScreenFormat().RGBToColor(80, 152, 184); + } + + return g_system->getScreenFormat().RGBToColor(0xFF, 0xFF, 0xFF); + } + + switch (_textColor) { + case 1: + return _c1Color; + case 2: + return _c2Color; + case 3: + return _c3Color; + case 4: + return _c4Color; + } + return _c1Color; } -byte MoviePlayer::findBlackPalIndex() { - return _black; +void MoviePlayer::convertColor(byte r, byte g, byte b, float &h, float &s, float &v) { + float varR = r / 255.0f; + float varG = g / 255.0f; + float varB = b / 255.0f; + + float min = MIN(varR, MIN(varG, varB)); + float max = MAX(varR, MAX(varG, varB)); + + v = max; + float d = max - min; + s = max == 0.0f ? 0.0f : d / max; + + if (min == max) { + h = 0.0f; // achromatic + } else { + if (max == varR) + h = (varG - varB) / d + (varG < varB ? 6.0f : 0.0f); + else if (max == varG) + h = (varB - varR) / d + 2.0f; + else + h = (varR - varG) / d + 4.0f; + h /= 6.0f; + } } -byte MoviePlayer::findWhitePalIndex() { - return _white; +void MoviePlayer::drawFramePSX(const Graphics::Surface *frame) { + // The PSX videos have half resolution + + Graphics::Surface scaledFrame; + scaledFrame.create(frame->w, frame->h * 2, frame->format); + + for (int y = 0; y < scaledFrame.h; y++) + memcpy(scaledFrame.getBasePtr(0, y), frame->getBasePtr(0, y / 2), scaledFrame.w * scaledFrame.format.bytesPerPixel); + + uint16 x = (g_system->getWidth() - scaledFrame.w) / 2; + uint16 y = (g_system->getHeight() - scaledFrame.h) / 2; + + _vm->_system->copyRectToScreen((byte *)scaledFrame.pixels, scaledFrame.pitch, x, y, scaledFrame.w, scaledFrame.h); + + scaledFrame.free(); } DXADecoderWithSound::DXADecoderWithSound(Audio::Mixer *mixer, Audio::SoundHandle *bgSoundHandle) @@ -328,6 +525,24 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * Common::String filename; Audio::SoundHandle *bgSoundHandle = new Audio::SoundHandle; + // For the PSX version, we'll try the PlayStation stream files + if (vm->isPsx()) { + // The demo uses the normal file names + filename = ((vm->_systemVars.isDemo) ? Common::String(sequenceList[id]) : Common::String(sequenceListPSX[id])) + ".str"; + + if (Common::File::exists(filename)) { +#ifdef USE_RGB_COLOR + // All BS1 PSX videos run the videos at 2x speed + Video::VideoDecoder *psxDecoder = new Video::PSXStreamDecoder(Video::PSXStreamDecoder::kCD2x); + return new MoviePlayer(vm, textMan, resMan, snd, system, bgSoundHandle, psxDecoder, kVideoDecoderPSX); +#else + GUI::MessageDialog dialog(Common::String::format(_("PSX stream cutscene '%s' cannot be played in paletted mode"), filename.c_str()), _("OK")); + dialog.runModal(); + return 0; +#endif + } + } + filename = Common::String::format("%s.smk", sequenceList[id]); if (Common::File::exists(filename)) { @@ -357,9 +572,11 @@ MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan * return NULL; } - Common::String buf = Common::String::format(_("Cutscene '%s' not found"), sequenceList[id]); - GUI::MessageDialog dialog(buf, _("OK")); - dialog.runModal(); + if (!vm->isPsx() || scumm_stricmp(sequenceList[id], "enddemo") != 0) { + Common::String buf = Common::String::format(_("Cutscene '%s' not found"), sequenceList[id]); + GUI::MessageDialog dialog(buf, _("OK")); + dialog.runModal(); + } return NULL; } diff --git a/engines/sword1/animation.h b/engines/sword1/animation.h index 1c03c66342..f64b03dd1b 100644 --- a/engines/sword1/animation.h +++ b/engines/sword1/animation.h @@ -24,7 +24,6 @@ #define SWORD1_ANIMATION_H #include "video/dxa_decoder.h" -#include "video/smk_decoder.h" #include "video/video_decoder.h" #include "common/list.h" @@ -38,18 +37,21 @@ namespace Sword1 { enum DecoderType { kVideoDecoderDXA = 0, - kVideoDecoderSMK = 1 + kVideoDecoderSMK = 1, + kVideoDecoderPSX = 2 }; class MovieText { public: uint16 _startFrame; uint16 _endFrame; + uint16 _color; Common::String _text; - MovieText(int startFrame, int endFrame, const Common::String &text) { + MovieText(int startFrame, int endFrame, const Common::String &text, int color) { _startFrame = startFrame; _endFrame = endFrame; _text = text; + _color = color; } }; @@ -80,7 +82,9 @@ protected: OSystem *_system; Common::List<MovieText> _movieTexts; int _textX, _textY, _textWidth, _textHeight; - byte _white, _black; + int _textColor; + uint32 _black; + uint32 _c1Color, _c2Color, _c3Color, _c4Color; DecoderType _decoderType; Video::VideoDecoder *_decoder; @@ -89,9 +93,11 @@ protected: bool playVideo(); void performPostProcessing(byte *screen); + void drawFramePSX(const Graphics::Surface *frame); - byte findBlackPalIndex(); - byte findWhitePalIndex(); + uint32 getBlackColor(); + uint32 findTextColor(); + void convertColor(byte r, byte g, byte b, float &h, float &s, float &v); }; MoviePlayer *makeMoviePlayer(uint32 id, SwordEngine *vm, Text *textMan, ResMan *resMan, Audio::Mixer *snd, OSystem *system); diff --git a/engines/sword1/detection.cpp b/engines/sword1/detection.cpp index 2214e72067..087dcd09d8 100644 --- a/engines/sword1/detection.cpp +++ b/engines/sword1/detection.cpp @@ -25,6 +25,7 @@ #include "base/plugins.h" #include "common/fs.h" +#include "common/gui_options.h" #include "common/savefile.h" #include "common/system.h" #include "graphics/thumbnail.h" diff --git a/engines/sword1/sword1.cpp b/engines/sword1/sword1.cpp index 865e025786..75e8f72d9d 100644 --- a/engines/sword1/sword1.cpp +++ b/engines/sword1/sword1.cpp @@ -62,8 +62,9 @@ SwordEngine::SwordEngine(OSystem *syst) SearchMan.addSubDirectoryMatching(gameDataDir, "speech"); SearchMan.addSubDirectoryMatching(gameDataDir, "video"); SearchMan.addSubDirectoryMatching(gameDataDir, "smackshi"); - SearchMan.addSubDirectoryMatching(gameDataDir, "english");//PSX Demo - SearchMan.addSubDirectoryMatching(gameDataDir, "italian");//PSX Demo + SearchMan.addSubDirectoryMatching(gameDataDir, "streams"); // PSX videos + SearchMan.addSubDirectoryMatching(gameDataDir, "english"); // PSX Demo + SearchMan.addSubDirectoryMatching(gameDataDir, "italian"); // PSX Demo _console = new SwordConsole(this); } |