aboutsummaryrefslogtreecommitdiff
path: root/engines/sword1/animation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/sword1/animation.cpp')
-rw-r--r--engines/sword1/animation.cpp303
1 files changed, 260 insertions, 43 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;
}