mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-18 07:53:12 +00:00
Instead of pre-rendering all subtitles and pre-loading all sounds for a movie
cutscene, render the text and play the speech when needed. It probably won't play as nicely from CD now, but using less memory seems more important to me. svn-id: r25428
This commit is contained in:
parent
65dfc335ef
commit
98a8e88f61
@ -30,6 +30,7 @@
|
||||
#include "sword2/sword2.h"
|
||||
#include "sword2/defs.h"
|
||||
#include "sword2/header.h"
|
||||
#include "sword2/logic.h"
|
||||
#include "sword2/maketext.h"
|
||||
#include "sword2/mouse.h"
|
||||
#include "sword2/resman.h"
|
||||
@ -65,11 +66,11 @@ const MovieInfo MoviePlayer::_movies[19] = {
|
||||
{ "enddemo", 110, false }
|
||||
};
|
||||
|
||||
MoviePlayer::MoviePlayer(Sword2Engine *vm) {
|
||||
MoviePlayer::MoviePlayer(Sword2Engine *vm, const char *name) {
|
||||
_vm = vm;
|
||||
_name = strdup(name);
|
||||
_mixer = _vm->_mixer;
|
||||
_system = _vm->_system;
|
||||
_name = NULL;
|
||||
_textSurface = NULL;
|
||||
_bgSoundStream = NULL;
|
||||
_ticks = 0;
|
||||
@ -86,7 +87,6 @@ MoviePlayer::MoviePlayer(Sword2Engine *vm) {
|
||||
_seamless = false;
|
||||
_framesSkipped = 0;
|
||||
_forceFrame = false;
|
||||
_textList = NULL;
|
||||
_currentText = 0;
|
||||
}
|
||||
|
||||
@ -202,84 +202,121 @@ void MoviePlayer::drawFrame() {
|
||||
_system->copyRectToScreen(_frameBuffer + _frameY * screenWidth + _frameX, screenWidth, _frameX, _frameY, _frameWidth, _frameHeight);
|
||||
}
|
||||
|
||||
void MoviePlayer::openTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite) {
|
||||
_vm->_screen->createSurface(t->textSprite, &_textSurface);
|
||||
void MoviePlayer::openTextObject(SequenceTextInfo *t) {
|
||||
// Pull out the text line to get the official text number (for WAV id)
|
||||
|
||||
uint32 res = t->textNumber / SIZE;
|
||||
uint32 localText = t->textNumber & 0xffff;
|
||||
|
||||
// Open text resource and get the line
|
||||
|
||||
byte *text = _vm->fetchTextLine(_vm->_resman->openResource(res), localText);
|
||||
|
||||
_textObject.speechId = READ_LE_UINT16(text);
|
||||
|
||||
// Is it speech or subtitles, or both?
|
||||
|
||||
// If we want subtitles, or there was no sound
|
||||
|
||||
if (_vm->getSubtitles() || !_textObject.speechId) {
|
||||
_textObject.textMem = _vm->_fontRenderer->makeTextSprite(text + 2, 600, 255, _vm->_speechFontId, 1);
|
||||
}
|
||||
|
||||
_vm->_resman->closeResource(res);
|
||||
|
||||
if (_textObject.textMem) {
|
||||
FrameHeader frame;
|
||||
|
||||
frame.read(_textObject.textMem);
|
||||
|
||||
_textObject.textSprite.x = 320 - frame.width / 2;
|
||||
_textObject.textSprite.y = 440 - frame.height;
|
||||
_textObject.textSprite.w = frame.width;
|
||||
_textObject.textSprite.h = frame.height;
|
||||
_textObject.textSprite.type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION;
|
||||
_textObject.textSprite.data = _textObject.textMem + FrameHeader::size();
|
||||
_vm->_screen->createSurface(&_textObject.textSprite, &_textSurface);
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayer::closeTextObject(MovieTextObject *t) {
|
||||
void MoviePlayer::closeTextObject() {
|
||||
free(_textObject.textMem);
|
||||
_textObject.textMem = NULL;
|
||||
|
||||
_textObject.speechId = 0;
|
||||
|
||||
if (_textSurface) {
|
||||
_vm->_screen->deleteSurface(_textSurface);
|
||||
_textSurface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayer::calcTextPosition(MovieTextObject *t, int &xPos, int &yPos) {
|
||||
xPos = 320 - t->textSprite->w / 2;
|
||||
yPos = 420 - t->textSprite->h;
|
||||
void MoviePlayer::calcTextPosition(int &xPos, int &yPos) {
|
||||
xPos = 320 - _textObject.textSprite.w / 2;
|
||||
yPos = 420 - _textObject.textSprite.h;
|
||||
}
|
||||
|
||||
void MoviePlayer::drawTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite && _textSurface) {
|
||||
void MoviePlayer::drawTextObject() {
|
||||
if (_textObject.textMem && _textSurface) {
|
||||
int screenWidth = _vm->_screen->getScreenWide();
|
||||
byte *src = t->textSprite->data;
|
||||
byte *src = _textObject.textSprite.data;
|
||||
uint16 width = _textObject.textSprite.w;
|
||||
uint16 height = _textObject.textSprite.h;
|
||||
int xPos, yPos;
|
||||
|
||||
calcTextPosition(t, xPos, yPos);
|
||||
calcTextPosition(xPos, yPos);
|
||||
|
||||
byte *dst = _frameBuffer + yPos * screenWidth + xPos;
|
||||
|
||||
for (int y = 0; y < t->textSprite->h; y++) {
|
||||
for (int x = 0; x < t->textSprite->w; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
if (src[x] == 1)
|
||||
dst[x] = _black;
|
||||
else if (src[x] == 255)
|
||||
dst[x] = _white;
|
||||
}
|
||||
src += t->textSprite->w;
|
||||
src += width;
|
||||
dst += screenWidth;
|
||||
}
|
||||
|
||||
if (yPos + t->textSprite->h > _frameY + _frameHeight || t->textSprite->w > _frameWidth) {
|
||||
_system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, t->textSprite->w, t->textSprite->h);
|
||||
if (yPos + height > _frameY + _frameHeight || width > _frameWidth) {
|
||||
_system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayer::undrawTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite) {
|
||||
void MoviePlayer::undrawTextObject() {
|
||||
if (_textObject.textMem) {
|
||||
int xPos, yPos;
|
||||
|
||||
calcTextPosition(t, xPos, yPos);
|
||||
calcTextPosition(xPos, yPos);
|
||||
uint16 width = _textObject.textSprite.w;
|
||||
uint16 height = _textObject.textSprite.h;
|
||||
|
||||
// We only need to undraw the text if it's outside the frame.
|
||||
// Otherwise the next frame will cover the old text anyway.
|
||||
|
||||
if (yPos + t->textSprite->h > _frameY + _frameHeight || t->textSprite->w > _frameWidth) {
|
||||
if (yPos + height > _frameY + _frameHeight || width > _frameWidth) {
|
||||
int screenWidth = _vm->_screen->getScreenWide();
|
||||
byte *dst = _frameBuffer + yPos * screenWidth + xPos;
|
||||
|
||||
for (int y = 0; y < t->textSprite->h; y++) {
|
||||
memset(dst, 0, t->textSprite->w);
|
||||
for (int y = 0; y < height; y++) {
|
||||
memset(dst, 0, width);
|
||||
dst += screenWidth;
|
||||
}
|
||||
|
||||
_system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, t->textSprite->w, t->textSprite->h);
|
||||
_system->copyRectToScreen(_frameBuffer + yPos * screenWidth + xPos, screenWidth, xPos, yPos, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MoviePlayer::load(const char *name, MovieTextObject *text[]) {
|
||||
bool MoviePlayer::load() {
|
||||
_bgSoundStream = NULL;
|
||||
_textList = text;
|
||||
_currentText = 0;
|
||||
_currentFrame = 0;
|
||||
|
||||
_name = strdup(name);
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(_movies); i++) {
|
||||
if (scumm_stricmp(name, _movies[i].name) == 0) {
|
||||
if (scumm_stricmp(_name, _movies[i].name) == 0) {
|
||||
_seamless = _movies[i].seamless;
|
||||
_numFrames = _movies[i].frames;
|
||||
if (_numFrames > 60)
|
||||
@ -304,26 +341,24 @@ bool MoviePlayer::load(const char *name, MovieTextObject *text[]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void MoviePlayer::play(int32 leadIn, int32 leadOut) {
|
||||
void MoviePlayer::play(SequenceTextInfo *textList, uint32 numLines, int32 leadIn, int32 leadOut) {
|
||||
bool terminate = false;
|
||||
bool textVisible = false;
|
||||
bool startNextText = false;
|
||||
uint32 flags = Audio::Mixer::FLAG_16BITS;
|
||||
|
||||
// This happens if the user quits during the "eye" cutscene.
|
||||
if (_vm->_quit)
|
||||
return;
|
||||
|
||||
_numSpeechLines = numLines;
|
||||
_firstSpeechFrame = (numLines > 0) ? textList[0].startFrame : 0;
|
||||
|
||||
if (leadIn) {
|
||||
_vm->_sound->playMovieSound(leadIn, kLeadInSound);
|
||||
}
|
||||
|
||||
savePalette();
|
||||
|
||||
#ifndef SCUMM_BIG_ENDIAN
|
||||
flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
|
||||
_framesSkipped = 0;
|
||||
_ticks = _system->getMillis();
|
||||
_bgSoundStream = Audio::AudioStream::openStreamFile(_name);
|
||||
@ -338,32 +373,32 @@ void MoviePlayer::play(int32 leadIn, int32 leadOut) {
|
||||
// The frame has been decoded. Now draw the subtitles, if any,
|
||||
// before drawing it to the screen.
|
||||
|
||||
if (_textList && _textList[_currentText]) {
|
||||
MovieTextObject *t = _textList[_currentText];
|
||||
if (_currentText < numLines) {
|
||||
SequenceTextInfo *t = &textList[_currentText];
|
||||
|
||||
if (_currentFrame == t->startFrame) {
|
||||
openTextObject(t);
|
||||
textVisible = true;
|
||||
|
||||
if (t->speech) {
|
||||
if (_textObject.speechId) {
|
||||
startNextText = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (startNextText && !_mixer->isSoundHandleActive(_speechHandle)) {
|
||||
_mixer->playRaw(Audio::Mixer::kSpeechSoundType, &_speechHandle, t->speech, t->speechBufferSize, 22050, flags);
|
||||
if (startNextText && _vm->_sound->amISpeaking() == RDSE_QUIET) {
|
||||
_vm->_sound->playCompSpeech(_textObject.speechId, 16, 0);
|
||||
startNextText = false;
|
||||
}
|
||||
|
||||
if (_currentFrame == t->endFrame) {
|
||||
undrawTextObject(t);
|
||||
closeTextObject(t);
|
||||
undrawTextObject();
|
||||
closeTextObject();
|
||||
_currentText++;
|
||||
textVisible = false;
|
||||
}
|
||||
|
||||
if (textVisible)
|
||||
drawTextObject(t);
|
||||
drawTextObject();
|
||||
}
|
||||
|
||||
if (leadOut && _currentFrame == _leadOutFrame) {
|
||||
@ -404,8 +439,8 @@ void MoviePlayer::play(int32 leadIn, int32 leadOut) {
|
||||
// If the sound is still playing, draw the subtitles one final
|
||||
// time. This happens in the "carib" cutscene.
|
||||
|
||||
if (textVisible && _mixer->isSoundHandleActive(_speechHandle)) {
|
||||
drawTextObject(_textList[_currentText]);
|
||||
if (textVisible && _vm->_sound->amISpeaking() == RDSE_SPEAKING) {
|
||||
drawTextObject();
|
||||
}
|
||||
|
||||
drawFrame();
|
||||
@ -418,19 +453,17 @@ void MoviePlayer::play(int32 leadIn, int32 leadOut) {
|
||||
// mid-sentence, and - even more importantly - that we don't
|
||||
// free the sound buffer while it's still in use.
|
||||
|
||||
while (_mixer->isSoundHandleActive(_speechHandle) || _mixer->isSoundHandleActive(_bgSoundHandle)) {
|
||||
while (_vm->_sound->amISpeaking() == RDSE_SPEAKING || _mixer->isSoundHandleActive(_bgSoundHandle)) {
|
||||
_system->delayMillis(100);
|
||||
}
|
||||
} else {
|
||||
_mixer->stopHandle(_speechHandle);
|
||||
_vm->_sound->stopSpeech();
|
||||
_mixer->stopHandle(_bgSoundHandle);
|
||||
}
|
||||
|
||||
// The current text object may still be open
|
||||
if (_textList && _textList[_currentText]) {
|
||||
undrawTextObject(_textList[_currentText]);
|
||||
closeTextObject(_textList[_currentText]);
|
||||
}
|
||||
undrawTextObject();
|
||||
closeTextObject();
|
||||
|
||||
if (!_seamless) {
|
||||
clearFrame();
|
||||
@ -448,7 +481,8 @@ void MoviePlayer::play(int32 leadIn, int32 leadOut) {
|
||||
// Movie player for the new DXA movies
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayerDXA::MoviePlayerDXA(Sword2Engine *vm) : MoviePlayer(vm) {
|
||||
MoviePlayerDXA::MoviePlayerDXA(Sword2Engine *vm, const char *name)
|
||||
: MoviePlayer(vm, name) {
|
||||
debug(0, "Creating DXA cutscene player");
|
||||
}
|
||||
|
||||
@ -466,13 +500,13 @@ bool MoviePlayerDXA::decodeFrame() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoviePlayerDXA::load(const char *name, MovieTextObject *text[]) {
|
||||
if (!MoviePlayer::load(name, text))
|
||||
bool MoviePlayerDXA::load() {
|
||||
if (!MoviePlayer::load())
|
||||
return false;
|
||||
|
||||
char filename[20];
|
||||
|
||||
snprintf(filename, sizeof(filename), "%s.dxa", name);
|
||||
snprintf(filename, sizeof(filename), "%s.dxa", _name);
|
||||
|
||||
if (loadFile(filename)) {
|
||||
// The Broken Sword games always use external audio tracks.
|
||||
@ -501,7 +535,8 @@ bool MoviePlayerDXA::load(const char *name, MovieTextObject *text[]) {
|
||||
// Movie player for the old MPEG movies
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayerMPEG::MoviePlayerMPEG(Sword2Engine *vm) : MoviePlayer(vm) {
|
||||
MoviePlayerMPEG::MoviePlayerMPEG(Sword2Engine *vm, const char *name)
|
||||
: MoviePlayer(vm, name) {
|
||||
#ifdef BACKEND_8BIT
|
||||
debug(0, "Creating MPEG cutscene player (8-bit)");
|
||||
#else
|
||||
@ -514,13 +549,13 @@ MoviePlayerMPEG::~MoviePlayerMPEG() {
|
||||
_anim = NULL;
|
||||
}
|
||||
|
||||
bool MoviePlayerMPEG::load(const char *name, MovieTextObject *text[]) {
|
||||
if (!MoviePlayer::load(name, text))
|
||||
bool MoviePlayerMPEG::load() {
|
||||
if (!MoviePlayer::load())
|
||||
return false;
|
||||
|
||||
_anim = new AnimationState(_vm, this);
|
||||
|
||||
if (!_anim->init(name)) {
|
||||
if (!_anim->init(_name)) {
|
||||
delete _anim;
|
||||
_anim = NULL;
|
||||
return false;
|
||||
@ -579,13 +614,13 @@ void MoviePlayerMPEG::updateScreen() {
|
||||
_anim->updateScreen();
|
||||
}
|
||||
|
||||
void MoviePlayerMPEG::drawTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite && _textSurface) {
|
||||
_anim->drawTextObject(t->textSprite, _textSurface);
|
||||
void MoviePlayerMPEG::drawTextObject() {
|
||||
if (_textObject.textMem && _textSurface) {
|
||||
_anim->drawTextObject(&_textObject.textSprite, _textSurface);
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayerMPEG::undrawTextObject(MovieTextObject *t) {
|
||||
void MoviePlayerMPEG::undrawTextObject() {
|
||||
// As long as we only have subtitles for full-sized cutscenes, we don't
|
||||
// really need to implement this function.
|
||||
}
|
||||
@ -689,15 +724,16 @@ void AnimationState::drawYUV(int width, int height, byte *const *dat) {
|
||||
// Dummy player for subtitled speech only
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayerDummy::MoviePlayerDummy(Sword2Engine *vm) : MoviePlayer(vm) {
|
||||
MoviePlayerDummy::MoviePlayerDummy(Sword2Engine *vm, const char *name)
|
||||
: MoviePlayer(vm, name) {
|
||||
debug(0, "Creating Dummy cutscene player");
|
||||
}
|
||||
|
||||
MoviePlayerDummy::~MoviePlayerDummy() {
|
||||
}
|
||||
|
||||
bool MoviePlayerDummy::load(const char *name, MovieTextObject *text[]) {
|
||||
if (!MoviePlayer::load(name, text))
|
||||
bool MoviePlayerDummy::load() {
|
||||
if (!MoviePlayer::load())
|
||||
return false;
|
||||
|
||||
_frameBuffer = _vm->_screen->getScreen();
|
||||
@ -711,7 +747,7 @@ bool MoviePlayerDummy::load(const char *name, MovieTextObject *text[]) {
|
||||
}
|
||||
|
||||
bool MoviePlayerDummy::decodeFrame() {
|
||||
if (_currentFrame == 0 && _textList) {
|
||||
if (_currentFrame == 0 && _numSpeechLines > 0) {
|
||||
byte dummyPalette[] = {
|
||||
0, 0, 0, 0,
|
||||
255, 255, 255, 0,
|
||||
@ -767,7 +803,7 @@ bool MoviePlayerDummy::decodeFrame() {
|
||||
|
||||
// If we have played the final voice-over, skip ahead to the lead out
|
||||
|
||||
if (_textList && !_textList[_currentText] && !_mixer->isSoundHandleActive(_speechHandle) && _leadOutFrame != (uint)-1 && _currentFrame < _leadOutFrame) {
|
||||
if (_currentText >= _numSpeechLines && _vm->_sound->amISpeaking() == RDSE_QUIET && _leadOutFrame != (uint)-1 && _currentFrame < _leadOutFrame) {
|
||||
_currentFrame = _leadOutFrame - 1;
|
||||
}
|
||||
|
||||
@ -775,7 +811,7 @@ bool MoviePlayerDummy::decodeFrame() {
|
||||
}
|
||||
|
||||
void MoviePlayerDummy::syncFrame() {
|
||||
if (!_textList || _currentFrame < _textList[0]->startFrame) {
|
||||
if (_numSpeechLines == 0 || _currentFrame < _firstSpeechFrame) {
|
||||
_ticks = _system->getMillis();
|
||||
return;
|
||||
}
|
||||
@ -786,16 +822,16 @@ void MoviePlayerDummy::syncFrame() {
|
||||
void MoviePlayerDummy::drawFrame() {
|
||||
}
|
||||
|
||||
void MoviePlayerDummy::drawTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite && _textSurface) {
|
||||
_vm->_screen->drawSurface(t->textSprite, _textSurface);
|
||||
void MoviePlayerDummy::drawTextObject() {
|
||||
if (_textObject.textMem && _textSurface) {
|
||||
_vm->_screen->drawSurface(&_textObject.textSprite, _textSurface);
|
||||
}
|
||||
}
|
||||
|
||||
void MoviePlayerDummy::undrawTextObject(MovieTextObject *t) {
|
||||
if (t->textSprite && _textSurface) {
|
||||
memset(_textSurface, 1, t->textSprite->w * t->textSprite->h);
|
||||
drawTextObject(t);
|
||||
void MoviePlayerDummy::undrawTextObject() {
|
||||
if (_textObject.textMem && _textSurface) {
|
||||
memset(_textSurface, 1, _textObject.textSprite.w * _textObject.textSprite.h);
|
||||
drawTextObject();
|
||||
}
|
||||
}
|
||||
|
||||
@ -804,13 +840,13 @@ void MoviePlayerDummy::undrawTextObject(MovieTextObject *t) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MoviePlayer *makeMoviePlayer(Sword2Engine *vm, const char *name) {
|
||||
char filename[20];
|
||||
static char filename[20];
|
||||
|
||||
#ifdef USE_ZLIB
|
||||
snprintf(filename, sizeof(filename), "%s.dxa", name);
|
||||
|
||||
if (Common::File::exists(filename)) {
|
||||
return new MoviePlayerDXA(vm);
|
||||
return new MoviePlayerDXA(vm, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -818,11 +854,11 @@ MoviePlayer *makeMoviePlayer(Sword2Engine *vm, const char *name) {
|
||||
snprintf(filename, sizeof(filename), "%s.mp2", name);
|
||||
|
||||
if (Common::File::exists(filename)) {
|
||||
return new MoviePlayerMPEG(vm);
|
||||
return new MoviePlayerMPEG(vm, name);
|
||||
}
|
||||
#endif
|
||||
|
||||
return new MoviePlayerDummy(vm);
|
||||
return new MoviePlayerDummy(vm, name);
|
||||
}
|
||||
|
||||
} // End of namespace Sword2
|
||||
|
@ -26,20 +26,25 @@
|
||||
#include "graphics/mpeg_player.h"
|
||||
#include "sound/mixer.h"
|
||||
|
||||
#include "sword2/screen.h"
|
||||
|
||||
namespace Sword2 {
|
||||
|
||||
struct SpriteInfo;
|
||||
|
||||
// This is the structure which is passed to the sequence player. It includes
|
||||
// the smack to play, and any text lines which are to be displayed over the top
|
||||
// of the sequence.
|
||||
|
||||
struct MovieTextObject {
|
||||
struct SequenceTextInfo {
|
||||
uint32 textNumber;
|
||||
uint16 startFrame;
|
||||
uint16 endFrame;
|
||||
SpriteInfo *textSprite;
|
||||
uint32 speechBufferSize;
|
||||
uint16 *speech;
|
||||
};
|
||||
|
||||
struct MovieTextObject {
|
||||
byte *textMem;
|
||||
SpriteInfo textSprite;
|
||||
uint16 speechId;
|
||||
|
||||
MovieTextObject() {
|
||||
textMem = NULL;
|
||||
speechId = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct MovieInfo {
|
||||
@ -61,9 +66,11 @@ protected:
|
||||
|
||||
byte _originalPalette[4 * 256];
|
||||
|
||||
uint32 _numSpeechLines;
|
||||
uint32 _firstSpeechFrame;
|
||||
MovieTextObject _textObject;
|
||||
byte *_textSurface;
|
||||
|
||||
Audio::SoundHandle _speechHandle;
|
||||
Audio::SoundHandle _bgSoundHandle;
|
||||
Audio::AudioStream *_bgSoundStream;
|
||||
|
||||
@ -85,15 +92,14 @@ protected:
|
||||
|
||||
static const MovieInfo _movies[];
|
||||
|
||||
MovieTextObject **_textList;
|
||||
int _currentText;
|
||||
uint32 _currentText;
|
||||
|
||||
void savePalette();
|
||||
void restorePalette();
|
||||
|
||||
void openTextObject(MovieTextObject *t);
|
||||
void closeTextObject(MovieTextObject *t);
|
||||
void calcTextPosition(MovieTextObject *t, int &xPos, int &yPos);
|
||||
void openTextObject(SequenceTextInfo *t);
|
||||
void closeTextObject();
|
||||
void calcTextPosition(int &xPos, int &yPos);
|
||||
|
||||
virtual void handleScreenChanged() {}
|
||||
|
||||
@ -102,16 +108,16 @@ protected:
|
||||
virtual bool decodeFrame() = 0;
|
||||
virtual void syncFrame();
|
||||
virtual void drawFrame();
|
||||
virtual void drawTextObject(MovieTextObject *t);
|
||||
virtual void undrawTextObject(MovieTextObject *t);
|
||||
virtual void drawTextObject();
|
||||
virtual void undrawTextObject();
|
||||
|
||||
public:
|
||||
MoviePlayer(Sword2Engine *vm);
|
||||
MoviePlayer(Sword2Engine *vm, const char *name);
|
||||
virtual ~MoviePlayer();
|
||||
|
||||
void updatePalette(byte *pal, bool packed = true);
|
||||
virtual bool load(const char *name, MovieTextObject *text[]);
|
||||
void play(int32 leadIn, int32 leadOut);
|
||||
virtual bool load();
|
||||
void play(SequenceTextInfo *textList, uint32 numLines, int32 leadIn, int32 leadOut);
|
||||
};
|
||||
|
||||
class MoviePlayerDummy : public MoviePlayer {
|
||||
@ -119,14 +125,14 @@ protected:
|
||||
bool decodeFrame();
|
||||
void syncFrame();
|
||||
void drawFrame();
|
||||
void drawTextObject(MovieTextObject *t);
|
||||
void undrawTextObject(MovieTextObject *t);
|
||||
void drawTextObject();
|
||||
void undrawTextObject();
|
||||
|
||||
public:
|
||||
MoviePlayerDummy(Sword2Engine *vm);
|
||||
MoviePlayerDummy(Sword2Engine *vm, const char *name);
|
||||
virtual ~MoviePlayerDummy();
|
||||
|
||||
bool load(const char *name, MovieTextObject *text[]);
|
||||
bool load();
|
||||
};
|
||||
|
||||
#ifdef USE_MPEG2
|
||||
@ -164,15 +170,15 @@ protected:
|
||||
void clearFrame();
|
||||
void drawFrame();
|
||||
void updateScreen();
|
||||
void drawTextObject(MovieTextObject *t);
|
||||
void undrawTextObject(MovieTextObject *t);
|
||||
void drawTextObject();
|
||||
void undrawTextObject();
|
||||
#endif
|
||||
|
||||
public:
|
||||
MoviePlayerMPEG(Sword2Engine *vm);
|
||||
MoviePlayerMPEG(Sword2Engine *vm, const char *name);
|
||||
~MoviePlayerMPEG();
|
||||
|
||||
bool load(const char *name, MovieTextObject *text[]);
|
||||
bool load();
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -183,10 +189,10 @@ protected:
|
||||
bool decodeFrame();
|
||||
|
||||
public:
|
||||
MoviePlayerDXA(Sword2Engine *vm);
|
||||
MoviePlayerDXA(Sword2Engine *vm, const char *name);
|
||||
~MoviePlayerDXA();
|
||||
|
||||
bool load(const char *name, MovieTextObject *text[]);
|
||||
bool load();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -179,131 +179,4 @@ void Router::setSpriteShading(byte *ob_graph, uint32 type) {
|
||||
obGraph.setType((obGraph.getType() & 0x0000ffff) | type);
|
||||
}
|
||||
|
||||
void Logic::createSequenceSpeech(MovieTextObject *sequenceText[]) {
|
||||
uint32 line;
|
||||
uint32 local_text;
|
||||
uint32 text_res;
|
||||
byte *text;
|
||||
uint32 wavId; // ie. offical text number (actor text number)
|
||||
bool speechRunning;
|
||||
|
||||
// for each sequence text line that's been logged
|
||||
for (line = 0; line < _sequenceTextLines; line++) {
|
||||
// allocate this structure
|
||||
sequenceText[line] = new MovieTextObject;
|
||||
|
||||
sequenceText[line]->startFrame = _sequenceTextList[line].startFrame;
|
||||
sequenceText[line]->endFrame = _sequenceTextList[line].endFrame;
|
||||
|
||||
// pull out the text line to get the official text number
|
||||
// (for wav id)
|
||||
|
||||
text_res = _sequenceTextList[line].textNumber / SIZE;
|
||||
local_text = _sequenceTextList[line].textNumber & 0xffff;
|
||||
|
||||
// open text resource & get the line
|
||||
text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
|
||||
wavId = (int32)READ_LE_UINT16(text);
|
||||
|
||||
// now ok to close the text file
|
||||
_vm->_resman->closeResource(text_res);
|
||||
|
||||
// 1st word of text line is the official line number
|
||||
debug(5,"(%d) SEQUENCE TEXT: %s", READ_LE_UINT16(text), text + 2);
|
||||
|
||||
// is it to be speech or subtitles or both?
|
||||
// assume speech is not running until know otherwise
|
||||
|
||||
speechRunning = false;
|
||||
_sequenceTextList[line].speech_mem = NULL;
|
||||
sequenceText[line]->speech = NULL;
|
||||
|
||||
if (!_vm->_sound->isSpeechMute()) {
|
||||
_sequenceTextList[line].speechBufferSize = _vm->_sound->preFetchCompSpeech(wavId, &_sequenceTextList[line].speech_mem);
|
||||
if (_sequenceTextList[line].speechBufferSize) {
|
||||
// ok, we've got speech!
|
||||
speechRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if we want subtitles, or speech failed to load
|
||||
|
||||
if (_vm->getSubtitles() || !speechRunning) {
|
||||
// open text resource & get the line
|
||||
text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
|
||||
// make the sprite
|
||||
// 'text+2' to skip the first 2 bytes which form the
|
||||
// line reference number
|
||||
|
||||
// NB. The mem block containing the text sprite is
|
||||
// currently FLOATING!
|
||||
|
||||
// When rendering text over a sequence we need a
|
||||
// different colour for the border.
|
||||
|
||||
_sequenceTextList[line].text_mem = _vm->_fontRenderer->makeTextSprite(text + 2, 600, 255, _vm->_speechFontId, 1);
|
||||
|
||||
// ok to close the text resource now
|
||||
_vm->_resman->closeResource(text_res);
|
||||
} else {
|
||||
_sequenceTextList[line].text_mem = NULL;
|
||||
sequenceText[line]->textSprite = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// for drivers: NULL-terminate the array of pointers to
|
||||
// MovieTextObject's
|
||||
sequenceText[_sequenceTextLines] = NULL;
|
||||
|
||||
for (line = 0; line < _sequenceTextLines; line++) {
|
||||
// if we've made a text sprite for this line...
|
||||
|
||||
if (_sequenceTextList[line].text_mem) {
|
||||
// now fill out the SpriteInfo structure in the
|
||||
// MovieTextObjectStructure
|
||||
FrameHeader frame;
|
||||
|
||||
frame.read(_sequenceTextList[line].text_mem);
|
||||
|
||||
sequenceText[line]->textSprite = new SpriteInfo;
|
||||
|
||||
// center text at bottom of screen
|
||||
sequenceText[line]->textSprite->x = 320 - frame.width / 2;
|
||||
sequenceText[line]->textSprite->y = 440 - frame.height;
|
||||
sequenceText[line]->textSprite->w = frame.width;
|
||||
sequenceText[line]->textSprite->h = frame.height;
|
||||
sequenceText[line]->textSprite->type = RDSPR_DISPLAYALIGN | RDSPR_NOCOMPRESSION;
|
||||
sequenceText[line]->textSprite->data = _sequenceTextList[line].text_mem + FrameHeader::size();
|
||||
}
|
||||
|
||||
// if we've loaded a speech sample for this line...
|
||||
|
||||
if (_sequenceTextList[line].speech_mem) {
|
||||
// for drivers: set up pointer to decompressed wav in
|
||||
// memory
|
||||
|
||||
sequenceText[line]->speechBufferSize = _sequenceTextList[line].speechBufferSize;
|
||||
sequenceText[line]->speech = _sequenceTextList[line].speech_mem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logic::clearSequenceSpeech(MovieTextObject *sequenceText[]) {
|
||||
for (uint i = 0; i < _sequenceTextLines; i++) {
|
||||
// free up the memory used by this MovieTextObject
|
||||
delete sequenceText[i];
|
||||
|
||||
// free up the mem block containing this text sprite
|
||||
if (_sequenceTextList[i].text_mem)
|
||||
free(_sequenceTextList[i].text_mem);
|
||||
|
||||
// free up the mem block containing this speech sample
|
||||
if (_sequenceTextList[i].speech_mem)
|
||||
free(_sequenceTextList[i].speech_mem);
|
||||
}
|
||||
|
||||
// IMPORTANT! Reset the line count ready for the next sequence!
|
||||
_sequenceTextLines = 0;
|
||||
}
|
||||
|
||||
} // End of namespace Sword2
|
||||
|
@ -2102,7 +2102,6 @@ int32 Logic::fnPlaySequence(int32 *params) {
|
||||
// 1 number of frames in the sequence, used for PSX.
|
||||
|
||||
char filename[30];
|
||||
MovieTextObject *sequenceSpeechArray[MAX_SEQUENCE_TEXT_LINES + 1];
|
||||
|
||||
// The original code had some #ifdef blocks for skipping or muting the
|
||||
// cutscenes - fondly described as "the biggest fudge in the history
|
||||
@ -2118,12 +2117,7 @@ int32 Logic::fnPlaySequence(int32 *params) {
|
||||
// Write to walkthrough file (zebug0.txt)
|
||||
debug(5, "PLAYING SEQUENCE \"%s\"", filename);
|
||||
|
||||
// now create the text sprites, if any
|
||||
|
||||
if (_sequenceTextLines)
|
||||
createSequenceSpeech(sequenceSpeechArray);
|
||||
|
||||
// don't want to carry on streaming game music when smacker starts!
|
||||
// don't want to carry on streaming game music when cutscene starts!
|
||||
fnStopMusic(NULL);
|
||||
|
||||
// pause sfx during sequence
|
||||
@ -2131,10 +2125,12 @@ int32 Logic::fnPlaySequence(int32 *params) {
|
||||
|
||||
MoviePlayer *player = makeMoviePlayer(_vm, filename);
|
||||
|
||||
if (player->load(filename, (_sequenceTextLines && !readVar(DEMO)) ? sequenceSpeechArray : NULL)) {
|
||||
player->play(_smackerLeadIn, _smackerLeadOut);
|
||||
if (player->load()) {
|
||||
player->play(_sequenceTextList, _sequenceTextLines, _smackerLeadIn, _smackerLeadOut);
|
||||
}
|
||||
|
||||
_sequenceTextLines = 0;
|
||||
|
||||
delete player;
|
||||
|
||||
// unpause sound fx again, in case we're staying in same location
|
||||
@ -2143,11 +2139,6 @@ int32 Logic::fnPlaySequence(int32 *params) {
|
||||
_smackerLeadIn = 0;
|
||||
_smackerLeadOut = 0;
|
||||
|
||||
// now clear the text sprites, if any
|
||||
|
||||
if (_sequenceTextLines)
|
||||
clearSequenceSpeech(sequenceSpeechArray);
|
||||
|
||||
// now clear the screen in case the Sequence was quitted (using ESC)
|
||||
// rather than fading down to black
|
||||
|
||||
@ -2219,12 +2210,15 @@ int32 Logic::fnAddSequenceText(int32 *params) {
|
||||
// 1 frame number to start the text displaying
|
||||
// 2 frame number to stop the text dispalying
|
||||
|
||||
assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
|
||||
if (!readVar(DEMO)) {
|
||||
assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
|
||||
|
||||
_sequenceTextList[_sequenceTextLines].textNumber = params[0];
|
||||
_sequenceTextList[_sequenceTextLines].startFrame = params[1];
|
||||
_sequenceTextList[_sequenceTextLines].endFrame = params[2];
|
||||
_sequenceTextLines++;
|
||||
}
|
||||
|
||||
_sequenceTextList[_sequenceTextLines].textNumber = params[0];
|
||||
_sequenceTextList[_sequenceTextLines].startFrame = params[1];
|
||||
_sequenceTextList[_sequenceTextLines].endFrame = params[2];
|
||||
_sequenceTextLines++;
|
||||
return IR_CONT;
|
||||
}
|
||||
|
||||
|
@ -24,13 +24,12 @@
|
||||
#ifndef SWORD2_LOGIC_H
|
||||
#define SWORD2_LOGIC_H
|
||||
|
||||
#include "sword2/memory.h"
|
||||
#include "common/endian.h"
|
||||
#include "sword2/animation.h"
|
||||
#include "sword2/memory.h"
|
||||
|
||||
namespace Sword2 {
|
||||
|
||||
struct MovieTextObject;
|
||||
|
||||
#define MAX_events 10
|
||||
|
||||
#define TREE_SIZE 3
|
||||
@ -79,22 +78,8 @@ private:
|
||||
// keeps count of number of text lines to disaply during the sequence
|
||||
uint32 _sequenceTextLines;
|
||||
|
||||
// FOR TEXT LINES IN SEQUENCE PLAYER
|
||||
|
||||
struct SequenceTextInfo {
|
||||
uint32 textNumber;
|
||||
uint16 startFrame;
|
||||
uint16 endFrame;
|
||||
byte *text_mem;
|
||||
uint32 speechBufferSize;
|
||||
uint16 *speech_mem;
|
||||
};
|
||||
|
||||
SequenceTextInfo _sequenceTextList[MAX_SEQUENCE_TEXT_LINES];
|
||||
|
||||
void createSequenceSpeech(MovieTextObject *sequenceText[]);
|
||||
void clearSequenceSpeech(MovieTextObject *sequenceText[]);
|
||||
|
||||
// when not playing a wav we calculate the speech time based upon
|
||||
// length of ascii
|
||||
|
||||
|
@ -704,46 +704,6 @@ int32 Sound::amISpeaking() {
|
||||
return RDSE_QUIET;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function loads and decompresses a list of speech from a cluster, but
|
||||
* does not play it. This is used for cutscene voice-overs, presumably to
|
||||
* avoid having to read from more than one file on the CD during playback.
|
||||
* @param speechId the text line id used to reference the speech
|
||||
* @param buf a pointer to the buffer that will be allocated for the sound
|
||||
*/
|
||||
|
||||
uint32 Sound::preFetchCompSpeech(uint32 speechId, uint16 **buf) {
|
||||
int cd = _vm->_resman->getCD();
|
||||
uint32 numSamples;
|
||||
|
||||
SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1];
|
||||
|
||||
Audio::AudioStream *input = getAudioStream(fh, "speech", cd, speechId, &numSamples);
|
||||
|
||||
if (!input)
|
||||
return 0;
|
||||
|
||||
*buf = NULL;
|
||||
|
||||
// Decompress data into speech buffer.
|
||||
|
||||
uint32 bufferSize = 2 * numSamples;
|
||||
|
||||
*buf = (uint16 *)malloc(bufferSize);
|
||||
if (!*buf) {
|
||||
delete input;
|
||||
fh->file.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 readSamples = input->readBuffer((int16 *)*buf, numSamples);
|
||||
|
||||
fh->file.close();
|
||||
delete input;
|
||||
|
||||
return 2 * readSamples;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function loads, decompresses and plays a line of speech. An error
|
||||
* occurs if speech is already playing.
|
||||
|
@ -269,7 +269,6 @@ public:
|
||||
int32 getSpeechStatus();
|
||||
int32 amISpeaking();
|
||||
int32 playCompSpeech(uint32 speechId, uint8 vol, int8 pan);
|
||||
uint32 preFetchCompSpeech(uint32 speechId, uint16 **buf);
|
||||
int32 stopSpeech();
|
||||
|
||||
int32 streamCompMusic(uint32 musicId, bool loop);
|
||||
|
Loading…
x
Reference in New Issue
Block a user