aboutsummaryrefslogtreecommitdiff
path: root/sword2
diff options
context:
space:
mode:
authorJames Brown2004-01-12 11:11:19 +0000
committerJames Brown2004-01-12 11:11:19 +0000
commita685422a138c99af7f95f8deaf787425af356f9b (patch)
tree21b0edaab5647e9f5889648d982099e995f06455 /sword2
parentbbf32c0076ec53a3df9416419b901c9ef3659884 (diff)
downloadscummvm-rg350-a685422a138c99af7f95f8deaf787425af356f9b.tar.gz
scummvm-rg350-a685422a138c99af7f95f8deaf787425af356f9b.tar.bz2
scummvm-rg350-a685422a138c99af7f95f8deaf787425af356f9b.zip
Initial libmpeg2 cutscene support based on patch #874510.
Pre-converted cutscenes w/ palette files available - ask LeChuck about Cutscenes :) svn-id: r12338
Diffstat (limited to 'sword2')
-rw-r--r--sword2/driver/animation.cpp199
-rw-r--r--sword2/driver/animation.h58
-rw-r--r--sword2/driver/d_draw.cpp128
-rw-r--r--sword2/driver/d_draw.h12
-rw-r--r--sword2/driver/render.cpp27
-rw-r--r--sword2/module.mk3
6 files changed, 418 insertions, 9 deletions
diff --git a/sword2/driver/animation.cpp b/sword2/driver/animation.cpp
new file mode 100644
index 0000000000..6426624218
--- /dev/null
+++ b/sword2/driver/animation.cpp
@@ -0,0 +1,199 @@
+#include "common/stdafx.h"
+#include "sword2/sword2.h"
+#include "sword2/driver/menu.h"
+#include "sword2/driver/render.h"
+
+#include "common/file.h"
+
+namespace Sword2 {
+
+// Build 'Best-Match' RGB lookup table
+void MoviePlayer::buildlookup(AnimationState * st, int p, int lines) {
+ int y, cb;
+ int r, g, b, ii;
+
+
+ if (p != st->curpal) {
+ st->curpal = p;
+ st->cr = 0;
+ st->pos = 0;
+ }
+
+ if (st->cr >= BITDEPTH)
+ return;
+
+ for (ii = 0; ii < lines; ii++) {
+ r = (-16*256 + (int)(256*1.596) * ((st->cr<<SHIFT)-128)) / 256;
+ for (cb = 0; cb < BITDEPTH; cb++) {
+ g = (-16*256 - (int)(0.813*256) * ((st->cr<<SHIFT)-128) - (int)(0.391*256) * ((cb<<SHIFT)-128)) / 256;
+ b = (-16*256 + (int)(2.018*256) * ((cb<<SHIFT)-128)) / 256;
+
+ for (y = 0; y < BITDEPTH; y++) {
+ int idx, bst = 0;
+ int dis = 2*SQR(r-st->palettes[p].pal[0])+4*SQR(g-st->palettes[p].pal[1])+SQR(b-st->palettes[p].pal[2]);
+
+ for (idx = 1; idx < 256; idx++) {
+ long d2 = 2*SQR(r-st->palettes[p].pal[4*idx])+4*SQR(g-st->palettes[p].pal[4*idx+1])+SQR(b-st->palettes[p].pal[4*idx+2]);
+ if (d2 < dis) {
+ bst = idx;
+ dis = d2;
+ }
+ }
+ st->lut2[st->pos++] = bst;
+
+ r += (1 << SHIFT);
+ g += (1 << SHIFT);
+ b += (1 << SHIFT);
+ }
+ r -= 256;
+ }
+ st->cr++;
+ if (st->cr >= BITDEPTH)
+ return;
+ }
+}
+
+void MoviePlayer::checkPaletteSwitch(AnimationState * st) {
+ // if we have reached the last image with this palette, switch to new one
+ if (st->framenum == st->palettes[st->palnum].end) {
+ unsigned char *l = st->lut2;
+ st->palnum++;
+ _vm->_graphics->setPalette(0, 256, st->palettes[st->palnum].pal, RDPAL_INSTANT);
+ st->lutcalcnum = (BITDEPTH + st->palettes[st->palnum].end - (st->framenum + 1) + 2) / (st->palettes[st->palnum].end - (st->framenum + 1) + 2);
+ st->lut2 = st->lut;
+ st->lut = l;
+ }
+}
+
+#ifndef USE_MPEG2
+bool MoviePlayer::pic(AnimationState * st) {
+ // Dummy for MPEG2-less builds
+ return false;
+}
+#else
+bool MoviePlayer::pic(AnimationState * st) {
+ mpeg2_state_t state;
+ const mpeg2_sequence_t *sequence_i;
+ size_t size = (size_t)-1;
+
+ do {
+ state = mpeg2_parse (st->decoder);
+ sequence_i = st->info->sequence;
+
+ switch (state) {
+ case STATE_BUFFER:
+ size = st->mpgfile->read(st->buffer, BUFFER_SIZE);
+ mpeg2_buffer (st->decoder, st->buffer, st->buffer + size);
+ break;
+
+ case STATE_SLICE:
+ case STATE_END:
+ if (st->info->display_fbuf) {
+ checkPaletteSwitch(st);
+ _vm->_graphics->plotYUV(st->lut, sequence_i->width, sequence_i->height, st->info->display_fbuf->buf);
+ st->framenum++;
+ buildlookup(st, st->palnum+1, st->lutcalcnum);
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ } while (size);
+
+ return false;
+}
+#endif
+
+#ifndef USE_MPEG2
+AnimationState *MoviePlayer::initanimation(char *name) {
+ return 0;
+}
+#else
+AnimationState *MoviePlayer::initanimation(char *name) {
+ char basename[512], tempFile[512];
+ AnimationState *st = new AnimationState;
+ int i, p;
+
+ strcpy(basename, name);
+ basename[strlen(basename)-4] = 0;
+
+ // Load lookup palettes
+ // TODO: Binary format so we can use File class
+ sprintf(tempFile, "%s/%s.pal", _vm->getGameDataPath(), basename);
+ FILE *f = fopen(tempFile, "r");
+
+ if (!f) {
+ warning("Cutscene: %s.pal palette missing", basename);
+ return 0;
+ }
+
+ p = 0;
+ while (!feof(f)) {
+ fscanf(f, "%i %i", &st->palettes[p].end, &st->palettes[p].cnt);
+ for (i = 0; i < st->palettes[p].cnt; i++) {
+ fscanf(f, "%i", &st->palettes[p].pal[4*i]);
+ fscanf(f, "%i", &st->palettes[p].pal[4*i+1]);
+ fscanf(f, "%i", &st->palettes[p].pal[4*i+2]);
+ }
+ p++;
+ }
+ fclose(f);
+
+ st->palnum = 0;
+ _vm->_graphics->setPalette(0, 256, st->palettes[st->palnum].pal, RDPAL_INSTANT);
+ st->lut = st->lut2 = st->lookup[0];
+ st->curpal = -1;
+ st->cr = 0;
+ buildlookup(st, st->palnum, 256);
+ st->lut2 = st->lookup[1];
+
+ // Open MPEG2 stream
+ st->mpgfile = new File();
+ sprintf(tempFile, "%s.mp2", basename);
+ if (!st->mpgfile->open(tempFile)) {
+ warning("Cutscene: Could not open %s", tempFile);
+ delete st;
+ return 0;
+ }
+
+ // Load and configure decoder
+ st->decoder = mpeg2_init ();
+ if (st->decoder == NULL) {
+ warning("Cutscene: Could not allocate an MPEG2 decoder");
+ delete st;
+ return 0;
+ }
+
+ st->info = mpeg2_info(st->decoder);
+ st->framenum = 0;
+
+ // Load in palette data
+ st->lutcalcnum = (BITDEPTH + st->palettes[st->palnum].end + 2) / (st->palettes[st->palnum].end + 2);
+
+
+ /* Play audio - TODO: Sync with video?*/
+ File *sndFile = new File;
+ sprintf(tempFile, "%s.ogg", basename);
+ if (sndFile->open(tempFile))
+ _vm->_mixer->playVorbis(&st->bgSound, sndFile, 100000000);
+
+ return st;
+}
+#endif
+
+#ifndef USE_MPEG2
+void MoviePlayer::doneanimation(AnimationState *st) {
+}
+#else
+void MoviePlayer::doneanimation(AnimationState *st) {
+ _vm->_mixer->stopHandle(st->bgSound);
+
+ mpeg2_close (st->decoder);
+ st->mpgfile->close();
+ delete st->mpgfile;
+ delete st;
+}
+#endif
+} // End of namespace Sword2
diff --git a/sword2/driver/animation.h b/sword2/driver/animation.h
new file mode 100644
index 0000000000..889a68d9fd
--- /dev/null
+++ b/sword2/driver/animation.h
@@ -0,0 +1,58 @@
+#ifndef ANIMATION_H
+#define ANIMATION_H
+
+#include <inttypes.h>
+#ifdef USE_MPEG2
+extern "C" {
+ #include <mpeg2dec/mpeg2.h>
+}
+#endif
+
+namespace Sword2 {
+
+#define SQR(x) ((x)*(x))
+
+#define SHIFT 3
+#define BITDEPTH (1<<(8-SHIFT))
+#define ROUNDADD (1<<(SHIFT-1))
+
+#define BUFFER_SIZE 4096
+
+
+typedef struct {
+
+
+ int palnum;
+
+ unsigned char lookup[2][BITDEPTH*BITDEPTH*BITDEPTH];
+ unsigned char * lut;
+ unsigned char * lut2;
+ int lutcalcnum;
+
+ int framenum;
+
+ #ifdef USE_MPEG2
+ mpeg2dec_t * decoder;
+ const mpeg2_info_t * info;
+ #endif
+ File * mpgfile;
+
+ int curpal;
+ int cr;
+ int pos;
+
+ struct {
+ int cnt;
+ int end;
+ unsigned char pal[4*256];
+ } palettes[50];
+
+ unsigned char buffer[BUFFER_SIZE];
+
+ PlayingSoundHandle bgSound;
+
+} AnimationState;
+
+} // End of namespace Sword2
+
+#endif
diff --git a/sword2/driver/d_draw.cpp b/sword2/driver/d_draw.cpp
index 52226f794e..f7f0f51add 100644
--- a/sword2/driver/d_draw.cpp
+++ b/sword2/driver/d_draw.cpp
@@ -141,11 +141,125 @@ void MoviePlayer::drawTextObject(MovieTextObject *obj) {
*/
int32 MoviePlayer::play(char *filename, MovieTextObject *text[], uint8 *musicOut) {
- warning("semi-stub PlaySmacker %s", filename);
+#ifdef USE_MPEG2
+ int frameCounter = 0, textCounter = 0;
+ PlayingSoundHandle handle;
+ bool skipCutscene = false, textVisible = false;
+ uint32 flags = SoundMixer::FLAG_16BITS, ticks = _vm->_system->get_msecs() + 83;
+
+ uint8 oldPal[1024];
+ memcpy(oldPal, _vm->_graphics->_palCopy, 1024);
+
+ AnimationState * anim = initanimation(filename);
+ if (!anim) {
+ // Missing Files? Use the old 'Narration Only' hack
+ playDummy(filename, text, musicOut);
+ return RD_OK;
+ }
+
+ _vm->_graphics->clearScene();
+ memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
+
+
+#ifndef SCUMM_BIG_ENDIAN
+ flags |= SoundMixer::FLAG_LITTLE_ENDIAN;
+#endif
+
+ while (1) {
+ if (!pic(anim)) break;
+ _vm->_graphics->setNeedFullRedraw();
+
+ if (text && text[textCounter]) {
+ if (frameCounter == text[textCounter]->startFrame) {
+ openTextObject(text[textCounter]);
+ textVisible = true;
+ if (text[textCounter]->speech) {
+ _vm->_mixer->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
+ }
+ }
+
+ if (frameCounter == text[textCounter]->endFrame) {
+ closeTextObject(text[textCounter]);
+ textCounter++;
+ textVisible = false;
+ }
+ if (textVisible)
+ drawTextObject(text[textCounter]);
+ }
+
+ frameCounter++;
+
+ _vm->_graphics->updateDisplay(true);
+
+ KeyboardEvent ke;
+
+ if ((_vm->_input->readKey(&ke) == RD_OK && ke.keycode == 27) || _vm->_quit) {
+ _vm->_mixer->stopHandle(handle);
+ skipCutscene = true;
+ break;
+ }
- // WORKAROUND: For now, we just do the voice-over parts of the
- // movies, since they're separate from the actual smacker files.
+ // Simulate ~12 frames per second. I don't know what
+ // frame rate the original movies had, or even if it
+ // was constant, but this seems to work reasonably.
+ while (_vm->_system->get_msecs() < ticks);
+ ticks += 82;
+
+ }
+
+ // Wait for the voice to stop playing. This is to make sure
+ // that we don't cut off the speech in mid-sentence, and - even
+ // more importantly - that we don't free the sound buffer while
+ // it's in use.
+
+ while (handle.isActive()) {
+ _vm->_graphics->updateDisplay(false);
+ _vm->_system->delay_msecs(100);
+ }
+
+ if (text)
+ closeTextObject(text[textCounter]);
+
+ _vm->_graphics->clearScene();
+ _vm->_graphics->setNeedFullRedraw();
+
+ // HACK: Remove the instructions created above
+ Common::Rect r;
+
+ memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
+ r.left = r.top = 0;
+ r.right = _vm->_graphics->_screenWide;
+ r.bottom = MENUDEEP;
+ _vm->_graphics->updateRect(&r);
+
+ // FIXME: For now, only play the lead-out music for cutscenes
+ // that have subtitles.
+ if (!skipCutscene)
+ _vm->_sound->playLeadOut(musicOut);
+
+ _vm->_graphics->setPalette(0, 256, oldPal, RDPAL_INSTANT);
+
+ doneanimation(anim);
+
+ // Lead-in and lead-out music are, as far as I can tell, only used for
+ // the animated cut-scenes, so this seems like a good place to close
+ // both of them.
+
+ _vm->_sound->closeFx(-1);
+ _vm->_sound->closeFx(-2);
+
+ return RD_OK;
+#else
+ // No MPEG2? Use the old 'Narration Only' hack
+ playDummy(filename, text, musicOut);
+ return RD_OK;
+#endif
+}
+
+// This just plays the cutscene with voiceovers / subtitles, in case the files are missing
+int32 MoviePlayer::playDummy(char *filename, MovieTextObject *text[], uint8 *musicOut) {
+ int frameCounter = 0, textCounter = 0;
if (text) {
uint8 oldPal[1024];
uint8 tmpPal[1024];
@@ -159,7 +273,7 @@ int32 MoviePlayer::play(char *filename, MovieTextObject *text[], uint8 *musicOut
memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
- uint8 msg[] = "Cutscene - Press ESC to exit";
+ uint8 msg[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos";
Memory *data = _vm->_fontRenderer->makeTextSprite(msg, 640, 255, _vm->_speechFontId);
FrameHeader *frame = (FrameHeader *) data->ad;
SpriteInfo msgSprite;
@@ -180,8 +294,7 @@ int32 MoviePlayer::play(char *filename, MovieTextObject *text[], uint8 *musicOut
// In case the cutscene has a long lead-in, start just before
// the first line of text.
- int frameCounter = text[0]->startFrame - 12;
- int textCounter = 0;
+ frameCounter = text[0]->startFrame - 12;
// Fake a palette that will hopefully make the text visible.
// In the opening cutscene it seems to use colours 1 (black?)
@@ -197,7 +310,7 @@ int32 MoviePlayer::play(char *filename, MovieTextObject *text[], uint8 *musicOut
tmpPal[255 * 4 + 2] = 255;
_vm->_graphics->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
- PlayingSoundHandle handle;
+PlayingSoundHandle handle;
bool skipCutscene = false;
@@ -219,7 +332,6 @@ int32 MoviePlayer::play(char *filename, MovieTextObject *text[], uint8 *musicOut
_vm->_mixer->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
}
}
-
if (frameCounter == text[textCounter]->endFrame) {
closeTextObject(text[textCounter]);
_vm->_graphics->clearScene();
diff --git a/sword2/driver/d_draw.h b/sword2/driver/d_draw.h
index 8b107164a3..3b29ccf02b 100644
--- a/sword2/driver/d_draw.h
+++ b/sword2/driver/d_draw.h
@@ -22,6 +22,8 @@
#include "common/rect.h"
+#include "animation.h"
+
namespace Sword2 {
// This is the maximum mouse cursor size in the SDL backend
@@ -72,9 +74,17 @@ private:
void closeTextObject(MovieTextObject *obj);
void drawTextObject(MovieTextObject *obj);
+ void buildlookup(AnimationState * st, int p, int lines);
+ void checkPaletteSwitch(AnimationState * st);
+
+ AnimationState * initanimation(char *name);
+ void doneanimation(AnimationState * st);
+ bool pic(AnimationState * st);
+
public:
MoviePlayer(Sword2Engine *vm) : _vm(vm), _textSurface(NULL) {}
int32 play(char *filename, MovieTextObject *text[], uint8 *musicOut);
+ int32 playDummy(char *filename, MovieTextObject *text[], uint8 *musicOut);
};
struct BlockSurface {
@@ -185,6 +195,7 @@ private:
void unwindRaw16(uint8 *dest, uint8 *source, uint8 blockSize, uint8 *colTable);
int32 decompressRLE16(uint8 *dest, uint8 *source, int32 decompSize, uint8 *colTable);
+
public:
Graphics(Sword2Engine *vm, int16 width, int16 height);
~Graphics();
@@ -238,6 +249,7 @@ public:
void plotPoint(uint16 x, uint16 y, uint8 colour);
void drawLine(int16 x1, int16 y1, int16 x2, int16 y2, uint8 colour);
+ void plotYUV(unsigned char * lut, int width, int height, uint8_t * const * buf);
int32 createSurface(SpriteInfo *s, uint8 **surface);
void drawSurface(SpriteInfo *s, uint8 *surface, Common::Rect *clipRect = NULL);
diff --git a/sword2/driver/render.cpp b/sword2/driver/render.cpp
index 5e7d2961f0..7e05fa9919 100644
--- a/sword2/driver/render.cpp
+++ b/sword2/driver/render.cpp
@@ -820,4 +820,31 @@ void Graphics::closeBackgroundLayer(void) {
_layer = 0;
}
+
+void Graphics::plotYUV(unsigned char * lut, int width, int height, uint8_t * const * dat)
+{
+ uint8 *buf = _buffer + (40+(400-height)/2) * RENDERWIDE + (640-width)/2;
+
+ int x, y;
+
+ int ypos = 0;
+ int cpos = 0;
+ int linepos = 0;
+
+ for (y = 0; y < height; y+=2) {
+ for (x = 0; x < width; x+=2) {
+ int i = ((((dat[2][cpos]+ROUNDADD)>>SHIFT) * BITDEPTH) + ((dat[1][cpos]+ROUNDADD)>>SHIFT)) * BITDEPTH;
+ cpos++;
+
+ buf[linepos ] = lut[i+((dat[0][ ypos ]+ROUNDADD)>>SHIFT)];
+ buf[RENDERWIDE + linepos++] = lut[i+((dat[0][width+ypos++]+ROUNDADD)>>SHIFT)];
+ buf[linepos ] = lut[i+((dat[0][ ypos ]+ROUNDADD)>>SHIFT)];
+ buf[RENDERWIDE + linepos++] = lut[i+((dat[0][width+ypos++]+ROUNDADD)>>SHIFT)];
+ }
+ linepos += (2*RENDERWIDE-width);
+ ypos += width;
+ }
+}
+
+
} // End of namespace Sword2
diff --git a/sword2/module.mk b/sword2/module.mk
index 2b4d805f7f..e552165c23 100644
--- a/sword2/module.mk
+++ b/sword2/module.mk
@@ -35,7 +35,8 @@ MODULE_OBJS := \
sword2/driver/palette.o \
sword2/driver/rdwin.o \
sword2/driver/render.o \
- sword2/driver/sprite.o
+ sword2/driver/sprite.o \
+ sword2/driver/animation.o
MODULE_DIRS += \
sword2 \