Initial commit
This commit is contained in:
564
engines/twine/movies.cpp
Normal file
564
engines/twine/movies.cpp
Normal file
@@ -0,0 +1,564 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "twine/movies.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/file.h"
|
||||
#include "common/language.h"
|
||||
#include "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
#include "graphics/palette.h"
|
||||
#include "image/gif.h"
|
||||
#include "twine/audio/music.h"
|
||||
#include "twine/audio/sound.h"
|
||||
#include "twine/input.h"
|
||||
#include "twine/menu/interface.h"
|
||||
#include "twine/renderer/screens.h"
|
||||
#include "twine/resources/hqr.h"
|
||||
#include "twine/resources/resources.h"
|
||||
#include "twine/scene/grid.h"
|
||||
#include "twine/shared.h"
|
||||
#include "twine/twine.h"
|
||||
#include "video/smk_decoder.h"
|
||||
|
||||
namespace TwinE {
|
||||
|
||||
/** FLA Frame Opcode types */
|
||||
enum FlaFrameOpcode {
|
||||
kLoadPalette = 1,
|
||||
kInfo = 2,
|
||||
kPlaySample = 3,
|
||||
kSampleBalance = 4,
|
||||
kStopSample = 5,
|
||||
kDeltaFrame = 6,
|
||||
kBlackFrame = 7,
|
||||
kBrownFrame = 8,
|
||||
kCopy = 9,
|
||||
kFliCopy = 16
|
||||
};
|
||||
|
||||
/** FLA movie sample structure */
|
||||
struct FLASampleStruct {
|
||||
/** Number os samples */
|
||||
int16 sampleNum = 0;
|
||||
/** Sample frequency */
|
||||
int16 freq = 0;
|
||||
/** Numbers of time to repeat */
|
||||
int16 repeat = 0;
|
||||
uint8 balance = 0;
|
||||
uint8 volumeLeft = 0;
|
||||
uint8 volumeRight = 0;
|
||||
};
|
||||
|
||||
/** FLA movie extension */
|
||||
#define FLA_EXT ".fla"
|
||||
|
||||
void Movies::drawKeyFrame(Common::MemoryReadStream &stream, int32 width, int32 height) {
|
||||
uint8 *destPtr = (uint8 *)_flaBuffer;
|
||||
uint8 *startOfLine = destPtr;
|
||||
|
||||
for (int32 y = 0; y < height; ++y) {
|
||||
const int8 lineEntryCount = stream.readByte();
|
||||
|
||||
for (int8 a = 0; a < lineEntryCount; a++) {
|
||||
const int8 rleFlag = stream.readByte();
|
||||
|
||||
if (rleFlag < 0) {
|
||||
const int8 rleCnt = ABS(rleFlag);
|
||||
for (int8 b = 0; b < rleCnt; ++b) {
|
||||
*destPtr++ = stream.readByte();
|
||||
}
|
||||
} else {
|
||||
const char colorFill = stream.readByte();
|
||||
Common::fill(&destPtr[0], &destPtr[rleFlag], colorFill);
|
||||
destPtr += rleFlag;
|
||||
}
|
||||
}
|
||||
|
||||
startOfLine = destPtr = startOfLine + width;
|
||||
}
|
||||
}
|
||||
|
||||
void Movies::drawDeltaFrame(Common::MemoryReadStream &stream, int32 width) {
|
||||
const uint16 skip = stream.readUint16LE() * width;
|
||||
const int32 height = stream.readSint16LE();
|
||||
|
||||
uint8 *destPtr = (uint8 *)_flaBuffer + skip;
|
||||
uint8 *startOfLine = destPtr;
|
||||
for (int32 y = 0; y < height; ++y) {
|
||||
const int8 lineEntryCount = stream.readByte();
|
||||
|
||||
for (int8 a = 0; a < lineEntryCount; ++a) {
|
||||
destPtr += stream.readByte();
|
||||
const int8 rleFlag = stream.readByte();
|
||||
|
||||
if (rleFlag > 0) {
|
||||
for (int8 b = 0; b < rleFlag; ++b) {
|
||||
*destPtr++ = stream.readByte();
|
||||
}
|
||||
} else {
|
||||
const char colorFill = stream.readByte();
|
||||
const int8 rleCnt = ABS(rleFlag);
|
||||
Common::fill(&destPtr[0], &destPtr[rleCnt], colorFill);
|
||||
destPtr += rleCnt;
|
||||
}
|
||||
}
|
||||
|
||||
startOfLine = destPtr = startOfLine + width;
|
||||
}
|
||||
}
|
||||
|
||||
void Movies::scaleFla2x() {
|
||||
uint8 *source = (uint8 *)_flaBuffer;
|
||||
uint8 *dest = (uint8 *)_engine->_imageBuffer.getPixels();
|
||||
|
||||
if (_engine->_cfgfile.Movie == CONF_MOVIE_FLAWIDE) {
|
||||
Common::fill(&dest[0], &dest[_engine->_imageBuffer.w * 40], 0x00);
|
||||
dest += _engine->_imageBuffer.w * 40;
|
||||
}
|
||||
|
||||
for (int32 i = 0; i < FLASCREEN_HEIGHT; i++) {
|
||||
for (int32 j = 0; j < FLASCREEN_WIDTH; j++) {
|
||||
*dest++ = *source;
|
||||
*dest++ = *source++;
|
||||
}
|
||||
if (_engine->_cfgfile.Movie == CONF_MOVIE_FLAWIDE) { // include wide bars
|
||||
memcpy(dest, dest - _engine->_imageBuffer.w, FLASCREEN_WIDTH * 2);
|
||||
dest += FLASCREEN_WIDTH * 2;
|
||||
} else { // stretch the movie like original game.
|
||||
if (i % 2) {
|
||||
memcpy(dest, dest - _engine->_imageBuffer.w, FLASCREEN_WIDTH * 2);
|
||||
dest += FLASCREEN_WIDTH * 2;
|
||||
}
|
||||
if (i % 10) {
|
||||
memcpy(dest, dest - _engine->_imageBuffer.w, FLASCREEN_WIDTH * 2);
|
||||
dest += FLASCREEN_WIDTH * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_engine->_cfgfile.Movie == CONF_MOVIE_FLAWIDE) {
|
||||
Common::fill(&dest[0], &dest[_engine->_imageBuffer.w * 40], 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
void Movies::drawNextFrameFla() {
|
||||
FLASampleStruct sample;
|
||||
|
||||
_frameData.nbFrames = _file.readSint16LE();
|
||||
_frameData.offsetNextFrame = _file.readSint32LE();
|
||||
if (_frameData.offsetNextFrame > _engine->_imageBuffer.w * _engine->_imageBuffer.h) {
|
||||
warning("Skipping video frame - it would exceed the screen buffer: %i", _frameData.offsetNextFrame);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8 *outBuf = (uint8 *)_engine->_imageBuffer.getPixels();
|
||||
_file.read(outBuf, _frameData.offsetNextFrame);
|
||||
|
||||
if ((int32)_frameData.nbFrames <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Common::MemoryReadStream stream(outBuf, _frameData.offsetNextFrame);
|
||||
for (int32 frame = 0; frame < _frameData.nbFrames; ++frame) {
|
||||
const uint16 opcode = stream.readUint16LE();
|
||||
const uint16 opcodeBlockSize = stream.readUint16LE();
|
||||
const int64 pos = stream.pos();
|
||||
|
||||
switch (opcode) {
|
||||
case kLoadPalette: {
|
||||
int16 numOfColor = stream.readSint16LE();
|
||||
int16 startColor = stream.readSint16LE();
|
||||
if (_paletteOrg.size() < (uint)(numOfColor + startColor)) {
|
||||
Graphics::Palette palette(numOfColor + startColor);
|
||||
palette.set(_paletteOrg, 0, _paletteOrg.size());
|
||||
_paletteOrg = palette;
|
||||
}
|
||||
for (int16 i = 0; i < numOfColor; ++i) {
|
||||
const byte r = stream.readByte();
|
||||
const byte g = stream.readByte();
|
||||
const byte b = stream.readByte();
|
||||
_paletteOrg.set(i + startColor, r, g, b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kInfo: {
|
||||
int16 innerOpcpde = stream.readSint16LE();
|
||||
switch (innerOpcpde) {
|
||||
case 1: // fla flute
|
||||
_engine->_music->playMidiFile(26);
|
||||
break;
|
||||
case 2:
|
||||
// FLA movies don't use cross fade
|
||||
// fade out tricky
|
||||
if (_fadeOut != 1) {
|
||||
_engine->_screens->fadeToBlack(_paletteOrg);
|
||||
_fadeOut = 1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
_flagFirst = true;
|
||||
break;
|
||||
case 4:
|
||||
// TODO: fade out for 1 second before we stop it
|
||||
_engine->_music->stopMusicMidi();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kPlaySample: {
|
||||
sample.sampleNum = stream.readSint16LE();
|
||||
sample.freq = stream.readSint16LE();
|
||||
sample.repeat = stream.readSint16LE();
|
||||
sample.balance = stream.readByte();
|
||||
sample.volumeLeft = stream.readByte();
|
||||
sample.volumeRight = stream.readByte();
|
||||
_engine->_sound->playFlaSample(sample.sampleNum, sample.freq, sample.repeat, sample.volumeLeft, sample.volumeRight);
|
||||
break;
|
||||
}
|
||||
case kStopSample: {
|
||||
const int16 sampleNum = stream.readSint16LE();
|
||||
if (sampleNum == -1) {
|
||||
_engine->_sound->stopSamples();
|
||||
} else {
|
||||
_engine->_sound->stopSample(sampleNum);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kDeltaFrame: {
|
||||
drawDeltaFrame(stream, FLASCREEN_WIDTH);
|
||||
if (_fadeOut == 1) {
|
||||
++_fadeOutFrames;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kBrownFrame: {
|
||||
drawKeyFrame(stream, FLASCREEN_WIDTH, _flaHeaderData.ysize);
|
||||
break;
|
||||
}
|
||||
case kBlackFrame: {
|
||||
const Common::Rect rect(0, 0, FLASCREEN_WIDTH - 1, FLASCREEN_HEIGHT - 1);
|
||||
_engine->_interface->box(rect, 0);
|
||||
break;
|
||||
}
|
||||
case kCopy:
|
||||
case kFliCopy: {
|
||||
const Common::Rect rect(0, 0, 80, 200);
|
||||
byte *ptr = (byte *)_engine->_frontVideoBuffer.getPixels();
|
||||
for (int y = rect.top; y < rect.bottom; ++y) {
|
||||
for (int x = rect.left; x < rect.right; ++x) {
|
||||
*ptr++ = stream.readByte();
|
||||
}
|
||||
ptr = ptr + rect.width();
|
||||
}
|
||||
_engine->_frontVideoBuffer.addDirtyRect(rect);
|
||||
break;
|
||||
}
|
||||
case kSampleBalance: {
|
||||
const int16 sampleNum = stream.readSint16LE();
|
||||
/* const uint8 offset = */ stream.readByte();
|
||||
stream.skip(1); // padding
|
||||
/* const int16 balance = */ stream.readSint16LE();
|
||||
const uint8 volumeLeft = stream.readByte();
|
||||
const uint8 volumeRight = stream.readByte();
|
||||
const int32 channelIdx = _engine->_sound->getSampleChannel(sampleNum);
|
||||
_engine->_sound->setChannelBalance(channelIdx, volumeLeft, volumeRight);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stream.seek(pos + opcodeBlockSize);
|
||||
}
|
||||
}
|
||||
|
||||
Movies::Movies(TwinEEngine *engine) : _engine(engine) {}
|
||||
|
||||
#ifdef USE_GIF
|
||||
void Movies::prepareGIF(int index) {
|
||||
Image::GIFDecoder decoder;
|
||||
Common::SeekableReadStream *stream = HQR::makeReadStream(Resources::HQR_FLAGIF_FILE, index);
|
||||
if (stream == nullptr) {
|
||||
warning("Failed to load gif hqr entry with id %i from %s", index, Resources::HQR_FLAGIF_FILE);
|
||||
return;
|
||||
}
|
||||
if (!decoder.loadStream(*stream)) {
|
||||
delete stream;
|
||||
warning("Failed to load gif with id %i from %s", index, Resources::HQR_FLAGIF_FILE);
|
||||
return;
|
||||
}
|
||||
const Graphics::Surface *surface = decoder.getSurface();
|
||||
_engine->setPalette(0, decoder.getPalette().size(), decoder.getPalette().data());
|
||||
Graphics::ManagedSurface& target = _engine->_frontVideoBuffer;
|
||||
const Common::Rect surfaceBounds(0, 0, surface->w, surface->h);
|
||||
target.blitFrom(*surface, surfaceBounds, target.getBounds());
|
||||
debugC(1, TwinE::kDebugMovies, "Show gif with id %i from %s", index, Resources::HQR_FLAGIF_FILE);
|
||||
delete stream;
|
||||
_engine->delaySkip(5000);
|
||||
_engine->setPalette(_engine->_screens->_ptrPal);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Movies::playGIFMovie(const char *flaName) {
|
||||
#ifdef USE_GIF
|
||||
if (!Common::File::exists(Resources::HQR_FLAGIF_FILE)) {
|
||||
warning("%s file doesn't exist", Resources::HQR_FLAGIF_FILE);
|
||||
return;
|
||||
}
|
||||
|
||||
Common::String name(flaName);
|
||||
name.toLowercase();
|
||||
|
||||
debugC(1, TwinE::kDebugMovies, "Play gif %s", name.c_str());
|
||||
// TODO: use the HQR 23th entry (movies informations)
|
||||
// TODO: there are gifs [1-18]
|
||||
if (name == FLA_INTROD) {
|
||||
prepareGIF(3);
|
||||
prepareGIF(4);
|
||||
prepareGIF(5);
|
||||
} else if (name == "bateau" || name == "bateau2") {
|
||||
prepareGIF(7);
|
||||
} else if (name == "navette") {
|
||||
prepareGIF(15);
|
||||
} else if (name == "templebu") {
|
||||
prepareGIF(12);
|
||||
} else if (name == "flute2") {
|
||||
prepareGIF(8); // TODO: same as glass2?
|
||||
} else if (name == "glass2") {
|
||||
prepareGIF(8); // TODO: same as flute2?
|
||||
} else if (name == "surf") {
|
||||
prepareGIF(9);
|
||||
} else if (name == "verser" || name == "verser2") {
|
||||
prepareGIF(10);
|
||||
} else if (name == "neige2") {
|
||||
prepareGIF(11);
|
||||
} else if (name == "capture") {
|
||||
prepareGIF(14); // TODO: same as sendel?
|
||||
} else if (name == "sendel") {
|
||||
prepareGIF(14); // TODO: same as capture?
|
||||
} else if (name == "sendel2") {
|
||||
prepareGIF(17);
|
||||
} else if (name == FLA_DRAGON3) {
|
||||
prepareGIF(1);
|
||||
prepareGIF(2);
|
||||
} else if (name == "baffe" || name.matchString("baffe#")) {
|
||||
prepareGIF(6);
|
||||
} else {
|
||||
warning("unknown gif image: %s", name.c_str());
|
||||
}
|
||||
#else
|
||||
warning("No GIF support compiled in");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Movies::playMovie(const char *name) { // PlayAnimFla
|
||||
if (_engine->isLBA2()) {
|
||||
const int index = _engine->_resources->findSmkMovieIndex(name);
|
||||
if (index == -1) {
|
||||
return false;
|
||||
}
|
||||
return playSmkMovie(name, index);
|
||||
}
|
||||
|
||||
Common::String fileNamePath = name;
|
||||
const size_t n = fileNamePath.findLastOf(".");
|
||||
if (n != Common::String::npos) {
|
||||
fileNamePath.erase(n);
|
||||
}
|
||||
|
||||
if (_engine->_cfgfile.Movie == CONF_MOVIE_FLAGIF) {
|
||||
playGIFMovie(fileNamePath.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
_fadeOut = -1;
|
||||
_fadeOutFrames = 0;
|
||||
|
||||
_file.close();
|
||||
if (!_file.open(Common::Path(fileNamePath + FLA_EXT))) {
|
||||
warning("Failed to open fla movie '%s'", fileNamePath.c_str());
|
||||
playGIFMovie(fileNamePath.c_str());
|
||||
_engine->_screens->setBlackPal();
|
||||
_engine->_screens->clearScreen();
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint32 version = _file.readUint32BE();
|
||||
_file.skip(2); // version field is 5 bytes - and one padding byte
|
||||
_flaHeaderData.numOfFrames = _file.readUint32LE();
|
||||
_flaHeaderData.speed = _file.readByte();
|
||||
_file.skip(1); // padding byte
|
||||
_flaHeaderData.xsize = _file.readUint16LE();
|
||||
_flaHeaderData.ysize = _file.readUint16LE();
|
||||
|
||||
_samplesInFla = (int16)_file.readUint16LE();
|
||||
/*const uint16 offsetFrame1 =*/ _file.readUint16LE();
|
||||
|
||||
// sample number int16
|
||||
// loop int16
|
||||
_file.skip(4 * _samplesInFla);
|
||||
|
||||
bool finished = false;
|
||||
if (version == MKTAG('V', '1', '.', '3')) {
|
||||
int32 currentFrame = 0;
|
||||
|
||||
debugC(1, TwinE::kDebugMovies, "Play fla: %s", name);
|
||||
|
||||
ScopedKeyMap scopedKeyMap(_engine, cutsceneKeyMapId);
|
||||
|
||||
_flagFirst = true;
|
||||
do {
|
||||
FrameMarker frame(_engine, _flaHeaderData.speed);
|
||||
_engine->readKeys();
|
||||
if (_engine->shouldQuit()) {
|
||||
break;
|
||||
}
|
||||
if (currentFrame == _flaHeaderData.numOfFrames) {
|
||||
finished = true;
|
||||
break;
|
||||
}
|
||||
drawNextFrameFla();
|
||||
scaleFla2x();
|
||||
_engine->_frontVideoBuffer.blitFrom(_engine->_imageBuffer, _engine->_imageBuffer.getBounds(), _engine->_frontVideoBuffer.getBounds());
|
||||
|
||||
// Only blit to screen if isn't a fade
|
||||
if (_fadeOut == -1) {
|
||||
if (currentFrame == 0) {
|
||||
// fade in the first frame
|
||||
_engine->_screens->fadeToPal(_paletteOrg);
|
||||
} else {
|
||||
_engine->setPalette(_paletteOrg);
|
||||
}
|
||||
}
|
||||
|
||||
// TRICKY: fade in tricky
|
||||
if (_fadeOutFrames >= 2) {
|
||||
_engine->_screens->fadeToPal(_paletteOrg);
|
||||
_fadeOut = -1;
|
||||
_fadeOutFrames = 0;
|
||||
}
|
||||
|
||||
currentFrame++;
|
||||
} while (!_engine->_input->toggleAbortAction());
|
||||
} else {
|
||||
warning("Unsupported fla version: %u, %s", version, fileNamePath.c_str());
|
||||
}
|
||||
|
||||
// this might happen if the movie was interrupted before it even started to load the palette.
|
||||
if (!_paletteOrg.empty()) {
|
||||
_engine->_screens->fadeToBlack(_paletteOrg);
|
||||
}
|
||||
|
||||
_engine->_sound->stopSamples();
|
||||
_engine->_screens->setBlackPal();
|
||||
_engine->_screens->clearScreen();
|
||||
return finished;
|
||||
}
|
||||
|
||||
class TwineSmackerDecoder : public Video::SmackerDecoder {
|
||||
public:
|
||||
void enableLanguage(int track, int volume) {
|
||||
AudioTrack* audio = getAudioTrack(track);
|
||||
if (audio == nullptr) {
|
||||
return;
|
||||
}
|
||||
audio->setMute(false);
|
||||
audio->setVolume(CLIP<int>(volume, 0, Audio::Mixer::kMaxMixerVolume));
|
||||
}
|
||||
};
|
||||
|
||||
bool Movies::playSmkMovie(const char *name, int index) {
|
||||
assert(_engine->isLBA2());
|
||||
TwineSmackerDecoder decoder;
|
||||
Common::SeekableReadStream *stream = HQR::makeReadStream(TwineResource(Resources::HQR_VIDEO_FILE, index));
|
||||
if (stream == nullptr) {
|
||||
warning("Failed to find smacker video %i", index);
|
||||
return false;
|
||||
}
|
||||
if (!decoder.loadStream(stream)) {
|
||||
warning("Failed to load smacker video %i", index);
|
||||
return false;
|
||||
}
|
||||
const int volume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kMusicSoundType);
|
||||
decoder.setVolume(CLIP<int>(volume, 0, Audio::Mixer::kMaxMixerVolume));
|
||||
decoder.start();
|
||||
|
||||
decoder.setAudioTrack(0); // music
|
||||
if (ConfMan.getInt("audio_language") > 0) {
|
||||
int additionalAudioTrack = -1;
|
||||
if (!scumm_strnicmp(name, "INTRO", 5)) {
|
||||
switch (_engine->getGameLang()) {
|
||||
default:
|
||||
case Common::Language::EN_ANY:
|
||||
case Common::Language::EN_GRB:
|
||||
case Common::Language::EN_USA:
|
||||
additionalAudioTrack = 3;
|
||||
break;
|
||||
case Common::Language::DE_DEU:
|
||||
additionalAudioTrack = 2;
|
||||
break;
|
||||
case Common::Language::FR_FRA:
|
||||
additionalAudioTrack = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const int speechVolume = _engine->_system->getMixer()->getVolumeForSoundType(Audio::Mixer::kSpeechSoundType);
|
||||
debugC(1, TwinE::kDebugMovies, "Play additional speech track: %i (of %i tracks)", additionalAudioTrack, decoder.getAudioTrackCount());
|
||||
decoder.enableLanguage(additionalAudioTrack, speechVolume);
|
||||
} else {
|
||||
debugC(1, TwinE::kDebugMovies, "Disabled smacker speech");
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (decoder.endOfVideo()) {
|
||||
break;
|
||||
}
|
||||
FrameMarker frame(_engine);
|
||||
_engine->_input->readKeys();
|
||||
if (_engine->shouldQuit() || _engine->_input->toggleAbortAction()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (decoder.needsUpdate()) {
|
||||
const Graphics::Surface *frameSurf = decoder.decodeNextFrame();
|
||||
if (!frameSurf) {
|
||||
continue;
|
||||
}
|
||||
if (decoder.hasDirtyPalette()) {
|
||||
_engine->setPalette(0, 256, decoder.getPalette());
|
||||
}
|
||||
|
||||
Graphics::ManagedSurface& target = _engine->_frontVideoBuffer;
|
||||
const Common::Rect frameBounds(0, 0, frameSurf->w, frameSurf->h);
|
||||
target.blitFrom(*frameSurf, frameBounds, target.getBounds());
|
||||
}
|
||||
}
|
||||
|
||||
decoder.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace TwinE
|
||||
Reference in New Issue
Block a user