mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
This is an updated and slightly cleaned up version of patch #1657061 ("SWORD1:
Cutscene subtitles"). It still has the deficiencies listen in the patch tracker and should therefore be considered work-in-progress, but sev said I should go ahead and commit it anyway. I have no further plans for it right now, so feel free to improve on it. svn-id: r26644
This commit is contained in:
+156
-30
@@ -25,6 +25,7 @@
|
||||
#include "sword1/sword1.h"
|
||||
#include "sword1/animation.h"
|
||||
#include "sword1/credits.h"
|
||||
#include "sword1/text.h"
|
||||
#include "sound/vorbis.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
@@ -62,11 +63,13 @@ static const char *sequenceList[20] = {
|
||||
// Basic movie player
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayer::MoviePlayer(Screen *scr, Audio::Mixer *snd, OSystem *sys)
|
||||
: _scr(scr), _snd(snd), _system(sys) {
|
||||
MoviePlayer::MoviePlayer(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system)
|
||||
: _screen(screen), _textMan(textMan), _snd(snd), _system(system) {
|
||||
_bgSoundStream = NULL;
|
||||
_ticks = 0;
|
||||
_frameBuffer = NULL;
|
||||
_textSpriteBuf = NULL;
|
||||
_black = 1;
|
||||
_white = 255;
|
||||
_currentFrame = 0;
|
||||
_forceFrame = false;
|
||||
_framesSkipped = 0;
|
||||
@@ -78,15 +81,36 @@ MoviePlayer::~MoviePlayer(void) {
|
||||
void MoviePlayer::updatePalette(byte *pal, bool packed) {
|
||||
byte palette[4 * 256];
|
||||
byte *p = palette;
|
||||
|
||||
uint32 maxWeight = 0;
|
||||
uint32 minWeight = 0xFFFFFFFF;
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
*p++ = *pal++;
|
||||
*p++ = *pal++;
|
||||
*p++ = *pal++;
|
||||
int r = *pal++;
|
||||
int g = *pal++;
|
||||
int b = *pal++;
|
||||
|
||||
if (!packed)
|
||||
*p++ = *pal++;
|
||||
else
|
||||
*p++ = 0;
|
||||
pal++;
|
||||
|
||||
uint32 weight = 3 * r * r + 6 * g * g + 2 * b * b;
|
||||
|
||||
if (weight >= maxWeight) {
|
||||
_white = i;
|
||||
maxWeight = weight;
|
||||
}
|
||||
|
||||
if (weight <= minWeight) {
|
||||
_black = i;
|
||||
minWeight = i;
|
||||
}
|
||||
|
||||
*p++ = r;
|
||||
*p++ = g;
|
||||
*p++ = b;
|
||||
*p++ = 0;
|
||||
}
|
||||
|
||||
_system->setPalette(palette, 0, 256);
|
||||
_forceFrame = true;
|
||||
}
|
||||
@@ -148,16 +172,58 @@ bool MoviePlayer::syncFrame(void) {
|
||||
* @param id the id of the file
|
||||
*/
|
||||
bool MoviePlayer::load(uint32 id) {
|
||||
Common::File f;
|
||||
char fileName[20];
|
||||
|
||||
_id = id;
|
||||
_bgSoundStream = NULL;
|
||||
|
||||
if (SwordEngine::_systemVars.showText) {
|
||||
sprintf(fileName, "%s.txt", sequenceList[id]);
|
||||
if (f.open(fileName)) {
|
||||
char line[120];
|
||||
int lineNo = 0;
|
||||
int lastEnd = -1;
|
||||
|
||||
_movieTexts.clear();
|
||||
while (f.readLine(line, sizeof(line))) {
|
||||
lineNo++;
|
||||
if (line[0] == '#' || line[0] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *ptr = line;
|
||||
|
||||
// TODO: Better error handling
|
||||
int startFrame = strtoul(ptr, &ptr, 10);
|
||||
int endFrame = strtoul(ptr, &ptr, 10);
|
||||
|
||||
while (*ptr && isspace(*ptr))
|
||||
ptr++;
|
||||
|
||||
if (startFrame > endFrame) {
|
||||
warning("%s:%d: startFrame (%d) > endFrame (%d)", fileName, lineNo, startFrame, endFrame);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startFrame <= lastEnd) {
|
||||
warning("%s:%d startFrame (%d) <= lastEnd (%d)", fileName, lineNo, startFrame, lastEnd);
|
||||
continue;
|
||||
}
|
||||
|
||||
_movieTexts.push_back(new MovieText(startFrame, endFrame, ptr));
|
||||
lastEnd = endFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SwordEngine::_systemVars.cutscenePackVersion == 1) {
|
||||
if ((id == SEQ_INTRO) || (id == SEQ_FINALE) || (id == SEQ_HISTORY) || (id == SEQ_FERRARI)) {
|
||||
#ifdef USE_VORBIS
|
||||
// these sequences are language specific
|
||||
char sndName[20];
|
||||
sprintf(sndName, "%s.snd", sequenceList[id]);
|
||||
sprintf(fileName, "%s.snd", sequenceList[id]);
|
||||
Common::File *oggSource = new Common::File();
|
||||
if (oggSource->open(sndName)) {
|
||||
if (oggSource->open(fileName)) {
|
||||
SplittedAudioStream *sStream = new SplittedAudioStream();
|
||||
uint32 numSegs = oggSource->readUint32LE(); // number of audio segments, either 1 or 2.
|
||||
// for each segment and each of the 7 languages, we've got fileoffset and size
|
||||
@@ -170,13 +236,13 @@ bool MoviePlayer::load(uint32 id) {
|
||||
Common::MemoryReadStream *stream = oggSource->readStream(segSize);
|
||||
Audio::AudioStream *apStream = Audio::makeVorbisStream(stream, true);
|
||||
if (!apStream)
|
||||
error("Can't create Vorbis Stream from file %s", sndName);
|
||||
error("Can't create Vorbis Stream from file %s", fileName);
|
||||
sStream->appendStream(apStream);
|
||||
}
|
||||
free(header);
|
||||
_bgSoundStream = sStream;
|
||||
} else
|
||||
warning("Sound file \"%s\" not found", sndName);
|
||||
warning("Sound file \"%s\" not found", fileName);
|
||||
delete oggSource;
|
||||
#endif
|
||||
initOverlays(id);
|
||||
@@ -186,7 +252,7 @@ bool MoviePlayer::load(uint32 id) {
|
||||
}
|
||||
|
||||
void MoviePlayer::play(void) {
|
||||
_scr->clearScreen();
|
||||
_screen->clearScreen();
|
||||
_framesSkipped = 0;
|
||||
_ticks = _system->getMillis();
|
||||
_bgSoundStream = Audio::AudioStream::openStreamFile(sequenceList[_id]);
|
||||
@@ -197,6 +263,24 @@ void MoviePlayer::play(void) {
|
||||
bool terminated = false;
|
||||
Common::EventManager *eventMan = _system->getEventManager();
|
||||
while (!terminated && decodeFrame()) {
|
||||
if (!_movieTexts.empty()) {
|
||||
if (_currentFrame == _movieTexts[0]->_startFrame) {
|
||||
_textMan->makeTextSprite(2, (uint8 *)_movieTexts[0]->_text, 600, LETTER_COL);
|
||||
|
||||
FrameHeader *frame = _textMan->giveSpriteData(2);
|
||||
_textWidth = frame->width;
|
||||
_textHeight = frame->height;
|
||||
_textX = 320 - _textWidth / 2;
|
||||
_textY = 420 - _textHeight;
|
||||
_textSpriteBuf = (byte *)calloc(_textHeight, _textWidth);
|
||||
}
|
||||
if (_currentFrame == _movieTexts[0]->_endFrame) {
|
||||
_textMan->releaseText(2);
|
||||
free(_textSpriteBuf);
|
||||
_textSpriteBuf = NULL;
|
||||
delete _movieTexts.remove_at(0);
|
||||
}
|
||||
}
|
||||
processFrame();
|
||||
if (syncFrame())
|
||||
updateScreen();
|
||||
@@ -221,6 +305,11 @@ void MoviePlayer::play(void) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!_movieTexts.empty()) {
|
||||
delete _movieTexts.remove_at(_movieTexts.size() - 1);
|
||||
}
|
||||
|
||||
while (_snd->isSoundHandleActive(_bgSoundHandle))
|
||||
_system->delayMillis(100);
|
||||
|
||||
@@ -301,14 +390,13 @@ int SplittedAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
// Movie player for the new DXA movies
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayerDXA::MoviePlayerDXA(Screen *src, Audio::Mixer *snd, OSystem *sys)
|
||||
: MoviePlayer(src, snd, sys) {
|
||||
MoviePlayerDXA::MoviePlayerDXA(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system)
|
||||
: MoviePlayer(screen, textMan, snd, system) {
|
||||
debug(0, "Creating DXA cutscene player");
|
||||
}
|
||||
|
||||
MoviePlayerDXA::~MoviePlayerDXA(void) {
|
||||
closeFile();
|
||||
// free(_frameBuffer);
|
||||
}
|
||||
|
||||
bool MoviePlayerDXA::load(uint32 id) {
|
||||
@@ -348,13 +436,51 @@ bool MoviePlayerDXA::decodeFrame(void) {
|
||||
}
|
||||
|
||||
void MoviePlayerDXA::processFrame(void) {
|
||||
// TODO
|
||||
// TODO: Handle the advanced cutscene packs. Do they really exist?
|
||||
|
||||
// We cannot draw the text to _drawBuffer, sinzce ethat's one of the
|
||||
// decoder's internal buffers. Instead, we copy part of _drawBuffer
|
||||
// to the text sprite.
|
||||
|
||||
if (_textSpriteBuf) {
|
||||
memset(_textSpriteBuf, 0, _textWidth * _textHeight);
|
||||
|
||||
// FIXME: This is inefficient
|
||||
int x, y;
|
||||
|
||||
for (y = _textY; y < _textY + _textHeight; y++) {
|
||||
for (x = _textX; x < _textX + _textWidth; x++) {
|
||||
if (x >= _frameX && x <= _frameX + _frameWidth && y >= _frameY && y <= _frameY + _frameWidth) {
|
||||
_textSpriteBuf[(y - _textY) * _textWidth + x - _textX] = _drawBuffer[(y - _frameY) * _frameWidth + x - _frameX];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte *src = (byte *)_textMan->giveSpriteData(2) + sizeof(FrameHeader);
|
||||
byte *dst = _textSpriteBuf;
|
||||
|
||||
for (y = 0; y < _textHeight; y++) {
|
||||
for (x = 0; x < _textWidth; x++) {
|
||||
switch (src[x]) {
|
||||
case BORDER_COL:
|
||||
dst[x] = _black;
|
||||
break;
|
||||
case LETTER_COL:
|
||||
dst[x] = _white;
|
||||
break;
|
||||
}
|
||||
}
|
||||
src += _textWidth;
|
||||
dst += _textWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayerDXA::updateScreen(void) {
|
||||
// Using _drawBuffer directly should work, as long as we don't do any
|
||||
// post-processing of the frame.
|
||||
_system->copyRectToScreen(_drawBuffer, _frameWidth, _frameX, _frameY, _frameWidth, _frameHeight);
|
||||
if (_textSpriteBuf) {
|
||||
_system->copyRectToScreen(_textSpriteBuf, _textWidth, _textX, _textY, _textWidth, _textHeight);
|
||||
}
|
||||
_system->updateScreen();
|
||||
}
|
||||
|
||||
@@ -366,8 +492,8 @@ void MoviePlayerDXA::updateScreen(void) {
|
||||
// Movie player for the old MPEG movies
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayerMPEG::MoviePlayerMPEG(Screen *src, Audio::Mixer *snd, OSystem *sys)
|
||||
: MoviePlayer(src, snd, sys) {
|
||||
MoviePlayerMPEG::MoviePlayerMPEG(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system)
|
||||
: MoviePlayer(screen, textMan, snd, system) {
|
||||
#ifdef BACKEND_8BIT
|
||||
debug(0, "Creating MPEG cutscene player (8-bit)");
|
||||
#else
|
||||
@@ -399,7 +525,7 @@ void MoviePlayerMPEG::insertOverlay(OverlayColor *buf, uint8 *ovl, OverlayColor
|
||||
|
||||
bool MoviePlayerMPEG::load(uint32 id) {
|
||||
if (MoviePlayer::load(id)) {
|
||||
_anim = new AnimationState(this, _scr, _system);
|
||||
_anim = new AnimationState(this, _screen, _system);
|
||||
return _anim->init(sequenceList[id]);
|
||||
}
|
||||
return false;
|
||||
@@ -454,8 +580,8 @@ void MoviePlayerMPEG::processFrame(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
AnimationState::AnimationState(MoviePlayer *player, Screen *scr, OSystem *sys)
|
||||
: BaseAnimationState(sys, 640, 400), _player(player), _scr(scr) {
|
||||
AnimationState::AnimationState(MoviePlayer *player, Screen *screen, OSystem *system)
|
||||
: BaseAnimationState(system, 640, 400), _player(player), _screen(screen) {
|
||||
}
|
||||
|
||||
AnimationState::~AnimationState(void) {
|
||||
@@ -472,7 +598,7 @@ void AnimationState::drawYUV(int width, int height, byte *const *dat) {
|
||||
_frameHeight = height;
|
||||
|
||||
#ifdef BACKEND_8BIT
|
||||
_scr->plotYUV(_lut, width, height, dat);
|
||||
_screen->plotYUV(_lut, width, height, dat);
|
||||
#else
|
||||
plotYUV(width, height, dat);
|
||||
#endif
|
||||
@@ -499,7 +625,7 @@ Audio::AudioStream *AnimationState::createAudioStream(const char *name, void *ar
|
||||
// Factory function for creating the appropriate cutscene player
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayer *makeMoviePlayer(uint32 id, Screen *scr, Audio::Mixer *snd, OSystem *sys) {
|
||||
MoviePlayer *makeMoviePlayer(uint32 id, Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system) {
|
||||
#if defined(USE_ZLIB) || defined(USE_MPEG2)
|
||||
char filename[20];
|
||||
#endif
|
||||
@@ -508,7 +634,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, Screen *scr, Audio::Mixer *snd, OSystem
|
||||
snprintf(filename, sizeof(filename), "%s.dxa", sequenceList[id]);
|
||||
|
||||
if (Common::File::exists(filename)) {
|
||||
return new MoviePlayerDXA(scr, snd, sys);
|
||||
return new MoviePlayerDXA(screen, textMan, snd, system);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -516,7 +642,7 @@ MoviePlayer *makeMoviePlayer(uint32 id, Screen *scr, Audio::Mixer *snd, OSystem
|
||||
snprintf(filename, sizeof(filename), "%s.mp2", sequenceList[id]);
|
||||
|
||||
if (Common::File::exists(filename)) {
|
||||
return new MoviePlayerMPEG(scr, snd, sys);
|
||||
return new MoviePlayerMPEG(screen, textMan, snd, system);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -58,9 +58,24 @@ enum {
|
||||
#define INTRO_LOGO_OVLS 12
|
||||
#define INTRO_TEXT_OVLS 8
|
||||
|
||||
class MovieText {
|
||||
public:
|
||||
uint16 _startFrame;
|
||||
uint16 _endFrame;
|
||||
char *_text;
|
||||
MovieText(int startFrame, int endFrame, char *text) {
|
||||
_startFrame = startFrame;
|
||||
_endFrame = endFrame;
|
||||
_text = strdup(text);
|
||||
}
|
||||
~MovieText() {
|
||||
free(_text);
|
||||
}
|
||||
};
|
||||
|
||||
class MoviePlayer {
|
||||
public:
|
||||
MoviePlayer(Screen *scr, Audio::Mixer *snd, OSystem *sys);
|
||||
MoviePlayer(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system);
|
||||
virtual ~MoviePlayer(void);
|
||||
virtual bool load(uint32 id);
|
||||
void play(void);
|
||||
@@ -68,13 +83,17 @@ public:
|
||||
private:
|
||||
bool checkSkipFrame(void);
|
||||
protected:
|
||||
Screen *_scr;
|
||||
Screen *_screen;
|
||||
Text *_textMan;
|
||||
Audio::Mixer *_snd;
|
||||
OSystem *_system;
|
||||
Common::Array<MovieText *> _movieTexts;
|
||||
byte *_textSpriteBuf;
|
||||
int _textX, _textY, _textWidth, _textHeight;
|
||||
byte _black, _white;
|
||||
|
||||
uint32 _id;
|
||||
|
||||
byte *_frameBuffer;
|
||||
uint _currentFrame;
|
||||
int _framesSkipped;
|
||||
bool _forceFrame;
|
||||
@@ -100,7 +119,7 @@ class MoviePlayerDXA : public MoviePlayer, ::Graphics::DXAPlayer {
|
||||
protected:
|
||||
virtual void setPalette(byte *pal);
|
||||
public:
|
||||
MoviePlayerDXA(Screen *scr, Audio::Mixer *snd, OSystem *sys);
|
||||
MoviePlayerDXA(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system);
|
||||
virtual ~MoviePlayerDXA(void);
|
||||
bool load(uint32 id);
|
||||
protected:
|
||||
@@ -117,10 +136,10 @@ protected:
|
||||
class AnimationState : public Graphics::BaseAnimationState {
|
||||
private:
|
||||
MoviePlayer *_player;
|
||||
Screen *_scr;
|
||||
Screen *_screen;
|
||||
|
||||
public:
|
||||
AnimationState(MoviePlayer *player, Screen *scr, OSystem *sys);
|
||||
AnimationState(MoviePlayer *player, Screen *screen, OSystem *system);
|
||||
~AnimationState(void);
|
||||
OverlayColor *giveRgbBuffer(void);
|
||||
|
||||
@@ -137,7 +156,7 @@ protected:
|
||||
|
||||
class MoviePlayerMPEG : public MoviePlayer {
|
||||
public:
|
||||
MoviePlayerMPEG(Screen *scr, Audio::Mixer *snd, OSystem *sys);
|
||||
MoviePlayerMPEG(Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system);
|
||||
virtual ~MoviePlayerMPEG(void);
|
||||
bool load(uint32 id);
|
||||
protected:
|
||||
@@ -173,7 +192,7 @@ private:
|
||||
FileQueue *_queue;
|
||||
};
|
||||
|
||||
MoviePlayer *makeMoviePlayer(uint32 id, Screen *scr, Audio::Mixer *snd, OSystem *sys);
|
||||
MoviePlayer *makeMoviePlayer(uint32 id, Screen *screen, Text *textMan, Audio::Mixer *snd, OSystem *system);
|
||||
|
||||
} // End of namespace Sword1
|
||||
|
||||
|
||||
@@ -957,7 +957,7 @@ int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int3
|
||||
CreditsPlayer player(_system, _mixer);
|
||||
player.play();
|
||||
} else {
|
||||
MoviePlayer *player = makeMoviePlayer(sequenceId, _screen, _mixer, _system);
|
||||
MoviePlayer *player = makeMoviePlayer(sequenceId, _screen, _textMan, _mixer, _system);
|
||||
if (player) {
|
||||
if (player->load(sequenceId))
|
||||
player->play();
|
||||
|
||||
+6
-10
@@ -34,9 +34,6 @@ namespace Sword1 {
|
||||
|
||||
#define OVERLAP 3
|
||||
#define SPACE ' '
|
||||
#define BORDER_COL 200
|
||||
#define LETTER_COL 193
|
||||
#define NO_COL 0 // sprite background - 0 for transparency
|
||||
#define MAX_LINES 30
|
||||
|
||||
|
||||
@@ -49,14 +46,13 @@ Text::Text(ObjectMan *pObjMan, ResMan *pResMan, bool czechVersion) {
|
||||
|
||||
_joinWidth = charWidth( SPACE ) - 2 * OVERLAP;
|
||||
_charHeight = _resMan->getUint16(_resMan->fetchFrame(_font, 0)->height); // all chars have the same height
|
||||
_textBlocks[0] = _textBlocks[1] = NULL;
|
||||
for (int i = 0; i < MAX_TEXT_OBS; i++)
|
||||
_textBlocks[i] = NULL;
|
||||
}
|
||||
|
||||
Text::~Text(void) {
|
||||
if (_textBlocks[0])
|
||||
free(_textBlocks[0]);
|
||||
if (_textBlocks[1])
|
||||
free(_textBlocks[1]);
|
||||
for (int i = 0; i < MAX_TEXT_OBS; i++)
|
||||
free(_textBlocks[i]);
|
||||
//_resMan->resClose(_fontId); => wiped automatically by _resMan->flush();
|
||||
}
|
||||
|
||||
@@ -175,14 +171,14 @@ FrameHeader *Text::giveSpriteData(uint32 textTarget) {
|
||||
// textTarget is the resource ID of the Compact linking the textdata.
|
||||
// that's 0x950000 for slot 0 and 0x950001 for slot 1. easy, huh? :)
|
||||
textTarget &= ITM_ID;
|
||||
assert(textTarget <= 1);
|
||||
assert(textTarget < MAX_TEXT_OBS);
|
||||
|
||||
return _textBlocks[textTarget];
|
||||
}
|
||||
|
||||
void Text::releaseText(uint32 id) {
|
||||
id &= ITM_ID;
|
||||
assert(id <= 1);
|
||||
assert(id < MAX_TEXT_OBS);
|
||||
if (_textBlocks[id]) {
|
||||
free(_textBlocks[id]);
|
||||
_textBlocks[id] = NULL;
|
||||
|
||||
@@ -28,7 +28,11 @@
|
||||
|
||||
namespace Sword1 {
|
||||
|
||||
#define MAX_TEXT_OBS 2
|
||||
#define MAX_TEXT_OBS 3
|
||||
|
||||
#define BORDER_COL 200
|
||||
#define LETTER_COL 193
|
||||
#define NO_COL 0 // sprite background - 0 for transparency
|
||||
|
||||
class ObjectMan;
|
||||
class ResMan;
|
||||
@@ -44,10 +48,10 @@ public:
|
||||
~Text(void);
|
||||
FrameHeader *giveSpriteData(uint32 textTarget);
|
||||
uint32 lowTextManager(uint8 *text, int32 width, uint8 pen);
|
||||
void makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen);
|
||||
void releaseText(uint32 id);
|
||||
|
||||
private:
|
||||
void makeTextSprite(uint8 slot, uint8 *text, uint16 maxWidth, uint8 pen);
|
||||
uint16 analyzeSentence(uint8 *text, uint16 maxWidth, LineInfo *info);
|
||||
uint16 charWidth(uint8 ch);
|
||||
uint16 copyChar(uint8 ch, uint8 *sprPtr, uint16 sprWidth, uint8 pen);
|
||||
|
||||
Reference in New Issue
Block a user