Initial commit
This commit is contained in:
160
engines/groovie/video/player.cpp
Normal file
160
engines/groovie/video/player.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
/* 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 "common/debug.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "groovie/video/player.h"
|
||||
#include "groovie/groovie.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "common/debug-channels.h"
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
VideoPlayer::VideoPlayer(GroovieEngine *vm) :
|
||||
_vm(vm), _syst(vm->_system), _file(nullptr), _audioStream(nullptr), _fps(0), _overrideSpeed(false), _flags(0),
|
||||
_begunPlaying(false), _millisBetweenFrames(0), _lastFrameTime(0), _frameTimeDrift(0) {
|
||||
|
||||
_startTime = _syst->getMillis();
|
||||
|
||||
int16 h = g_system->getOverlayHeight();
|
||||
|
||||
_subtitles.setBBox(Common::Rect(20, h - 120, g_system->getOverlayWidth() - 20, h - 20));
|
||||
_subtitles.setColor(0xff, 0xff, 0xff);
|
||||
_subtitles.setFont("LiberationSans-Regular.ttf");
|
||||
}
|
||||
|
||||
bool VideoPlayer::load(Common::SeekableReadStream *file, uint16 flags) {
|
||||
_file = file;
|
||||
_flags = flags;
|
||||
_overrideSpeed = false;
|
||||
_startTime = _syst->getMillis();
|
||||
|
||||
stopAudioStream();
|
||||
_fps = loadInternal();
|
||||
|
||||
if (_fps != 0) {
|
||||
setOverrideSpeed(_overrideSpeed);
|
||||
_begunPlaying = false;
|
||||
return true;
|
||||
} else {
|
||||
_file = nullptr;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPlayer::setOverrideSpeed(bool isOverride) {
|
||||
_overrideSpeed = isOverride;
|
||||
if (_fps != 0) {
|
||||
if (isOverride)
|
||||
_millisBetweenFrames = 1000.0f / 26.0f;
|
||||
else
|
||||
_millisBetweenFrames = 1000.0f / float(_fps);
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPlayer::fastForward() {
|
||||
_millisBetweenFrames = 0;
|
||||
_frameTimeDrift = 0;
|
||||
stopAudioStream();
|
||||
}
|
||||
|
||||
bool VideoPlayer::isFastForwarding() {
|
||||
return DebugMan.isDebugChannelEnabled(kDebugFast) || _millisBetweenFrames <= 0;
|
||||
}
|
||||
|
||||
bool VideoPlayer::playFrame() {
|
||||
bool end = true;
|
||||
|
||||
// Process the next frame while the file is open
|
||||
if (_file) {
|
||||
end = playFrameInternal();
|
||||
|
||||
_subtitles.drawSubtitle(_lastFrameTime - _startTime);
|
||||
}
|
||||
|
||||
// The file has been completely processed
|
||||
if (end) {
|
||||
_file = nullptr;
|
||||
|
||||
// Wait for pending audio
|
||||
if (_audioStream) {
|
||||
if (_audioStream->endOfData() || isFastForwarding()) {
|
||||
// Mark the audio stream as finished (no more data will be appended)
|
||||
_audioStream->finish();
|
||||
_audioStream = nullptr;
|
||||
} else {
|
||||
// Don't end if there's still audio playing
|
||||
end = false;
|
||||
}
|
||||
}
|
||||
|
||||
unloadSubtitles();
|
||||
}
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
void VideoPlayer::unloadSubtitles() {
|
||||
if (_subtitles.isLoaded()) {
|
||||
_subtitles.close();
|
||||
g_system->hideOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
void VideoPlayer::waitFrame() {
|
||||
if (isFastForwarding()) {
|
||||
return;
|
||||
}
|
||||
uint32 currTime = _syst->getMillis();
|
||||
if (!_begunPlaying) {
|
||||
_begunPlaying = true;
|
||||
_lastFrameTime = currTime;
|
||||
_frameTimeDrift = 0.0f;
|
||||
|
||||
if (_subtitles.isLoaded()) {
|
||||
g_system->showOverlay(false);
|
||||
g_system->clearOverlay();
|
||||
}
|
||||
} else {
|
||||
uint32 millisDiff = currTime - _lastFrameTime;
|
||||
float fMillis = _millisBetweenFrames + _frameTimeDrift;
|
||||
// use floorf instead of roundf, because delayMillis often slightly over-sleeps
|
||||
uint32 millisSleep = MAX(0.0f, floorf(fMillis) - float(millisDiff));
|
||||
|
||||
if (millisSleep > 0) {
|
||||
debugC(7, kDebugVideo, "Groovie::Player: Delaying %d (currTime=%d, _lastFrameTime=%d, millisDiff=%d, _millisBetweenFrame=%.2f, _frameTimeDrift=%.2f)",
|
||||
millisSleep, currTime, _lastFrameTime, millisDiff, _millisBetweenFrames, _frameTimeDrift);
|
||||
_syst->delayMillis(millisSleep);
|
||||
currTime = _syst->getMillis();
|
||||
debugC(7, kDebugVideo, "Groovie::Player: Finished delay at %d", currTime);
|
||||
}
|
||||
|
||||
_frameTimeDrift = fMillis - float(currTime - _lastFrameTime);
|
||||
if (abs(_frameTimeDrift) >= _millisBetweenFrames) {
|
||||
_frameTimeDrift = 0;
|
||||
}
|
||||
debugC(6, kDebugVideo, "Groovie::Player: Frame displayed at %d (%f FPS), _frameTimeDrift=%.2f", currTime, 1000.0 / (currTime - _lastFrameTime), _frameTimeDrift);
|
||||
_lastFrameTime = currTime;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of Groovie namespace
|
||||
89
engines/groovie/video/player.h
Normal file
89
engines/groovie/video/player.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROOVIE_VIDEO_PLAYER_H
|
||||
#define GROOVIE_VIDEO_PLAYER_H
|
||||
|
||||
#include "common/system.h"
|
||||
#include "video/subtitles.h"
|
||||
|
||||
namespace Audio {
|
||||
class QueuingAudioStream;
|
||||
}
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
class GroovieEngine;
|
||||
|
||||
class VideoPlayer {
|
||||
public:
|
||||
VideoPlayer(GroovieEngine *vm);
|
||||
virtual ~VideoPlayer() {}
|
||||
|
||||
bool load(Common::SeekableReadStream *file, uint16 flags);
|
||||
bool playFrame();
|
||||
virtual void resetFlags() {}
|
||||
virtual void setOrigin(int16 x, int16 y) {}
|
||||
virtual void stopAudioStream() = 0;
|
||||
void fastForward();
|
||||
bool isFastForwarding();
|
||||
virtual void drawString(Graphics::Surface *surface, const Common::String &text, int posx, int posy, uint32 color, bool blackBackground) {}
|
||||
virtual void copyfgtobg(uint8 arg) {}
|
||||
void setOverrideSpeed(bool isOverride);
|
||||
|
||||
void loadSubtitles(const char *fname) { _subtitles.loadSRTFile(fname); }
|
||||
void unloadSubtitles();
|
||||
|
||||
virtual bool isFileHandled() { return false; }
|
||||
|
||||
protected:
|
||||
// To be implemented by subclasses
|
||||
virtual uint16 loadInternal() = 0;
|
||||
virtual bool playFrameInternal() = 0;
|
||||
|
||||
bool getOverrideSpeed() const { return _overrideSpeed; }
|
||||
|
||||
GroovieEngine *_vm;
|
||||
OSystem *_syst;
|
||||
Common::SeekableReadStream *_file;
|
||||
uint16 _flags;
|
||||
Audio::QueuingAudioStream *_audioStream;
|
||||
|
||||
|
||||
private:
|
||||
// Synchronization stuff
|
||||
bool _begunPlaying;
|
||||
bool _overrideSpeed;
|
||||
uint16 _fps;
|
||||
float _millisBetweenFrames;
|
||||
uint32 _lastFrameTime;
|
||||
float _frameTimeDrift;
|
||||
uint32 _startTime;
|
||||
|
||||
Video::Subtitles _subtitles;
|
||||
|
||||
protected:
|
||||
virtual void waitFrame();
|
||||
};
|
||||
|
||||
} // End of Groovie namespace
|
||||
|
||||
#endif // GROOVIE_VIDEO_PLAYER_H
|
||||
1137
engines/groovie/video/roq.cpp
Normal file
1137
engines/groovie/video/roq.cpp
Normal file
File diff suppressed because it is too large
Load Diff
138
engines/groovie/video/roq.h
Normal file
138
engines/groovie/video/roq.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROOVIE_VIDEO_ROQ_H
|
||||
#define GROOVIE_VIDEO_ROQ_H
|
||||
|
||||
#include "groovie/video/player.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Video {
|
||||
class VideoDecoder;
|
||||
}
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
class GroovieEngine;
|
||||
|
||||
struct ROQBlockHeader {
|
||||
uint16 type;
|
||||
uint32 size;
|
||||
uint16 param;
|
||||
};
|
||||
|
||||
class ROQPlayer : public VideoPlayer {
|
||||
public:
|
||||
ROQPlayer(GroovieEngine *vm);
|
||||
~ROQPlayer();
|
||||
void setOrigin(int16 x, int16 y) override;
|
||||
|
||||
Audio::SoundHandle getSoundHandle() {
|
||||
return _soundHandle;
|
||||
}
|
||||
|
||||
void drawString(Graphics::Surface *surface, const Common::String &text, int posx, int posy, uint32 color, bool blackBackground) override;
|
||||
void copyfgtobg(uint8 arg) override;
|
||||
|
||||
bool isFileHandled() override { return _isFileHandled; }
|
||||
|
||||
protected:
|
||||
void waitFrame() override;
|
||||
uint16 loadInternal() override;
|
||||
bool playFrameInternal() override;
|
||||
void stopAudioStream() override;
|
||||
virtual void createAudioStream(bool stereo);
|
||||
|
||||
Audio::SoundHandle _soundHandle;
|
||||
Graphics::Surface *_bg, *_screen, *_overBuf;
|
||||
Graphics::Surface *_currBuf, *_prevBuf;
|
||||
|
||||
private:
|
||||
bool readBlockHeader(ROQBlockHeader &blockHeader);
|
||||
|
||||
bool isValidBlockHeaderType(uint16 blockHeaderType);
|
||||
bool processBlock();
|
||||
bool processBlockInfo(ROQBlockHeader &blockHeader);
|
||||
bool processBlockQuadCodebook(ROQBlockHeader &blockHeader);
|
||||
bool processBlockQuadVector(ROQBlockHeader &blockHeader);
|
||||
void processBlockQuadVectorBlock(int baseX, int baseY);
|
||||
void processBlockQuadVectorBlockSub(int baseX, int baseY);
|
||||
bool processBlockStill(ROQBlockHeader &blockHeader);
|
||||
bool processBlockSoundMono(ROQBlockHeader &blockHeader);
|
||||
bool processBlockSoundStereo(ROQBlockHeader &blockHeader);
|
||||
bool processBlockAudioContainer(ROQBlockHeader &blockHeader);
|
||||
bool playFirstFrame() { return _flagNoPlay; }; // _alpha && !_flagOverlay; }
|
||||
void clearOverlay();
|
||||
void dumpAllSurfaces(const Common::String &funcname);
|
||||
|
||||
void paint2(byte i, int destx, int desty);
|
||||
void paint4(byte i, int destx, int desty);
|
||||
void paint8(byte i, int destx, int desty);
|
||||
void copy(byte size, int destx, int desty, int dx, int dy);
|
||||
|
||||
// Origin
|
||||
int16 _origX, _origY;
|
||||
//int16 _screenOffset;
|
||||
void calcStartStop(int &start, int &stop, int origin, int length);
|
||||
|
||||
// Block coding type
|
||||
byte getCodingType();
|
||||
uint16 _codingType;
|
||||
byte _codingTypeCount;
|
||||
|
||||
// Codebooks
|
||||
uint16 _num2blocks;
|
||||
uint16 _num4blocks;
|
||||
uint32 _codebook2[256 * 4];
|
||||
byte _codebook4[256 * 4];
|
||||
|
||||
// Flags
|
||||
bool _flagNoPlay; //!< Play only first frame and do not print the image to the screen
|
||||
bool _flagOverlay; //!< If _flagNoPlay is set. Copy frame to the foreground otherwise to the background
|
||||
bool _altMotionDecoder; // Some ROQ vids use a variation on the copy codeblock
|
||||
bool _flagMasked; //!< Clear the video instead of play it, used in pente
|
||||
|
||||
// Buffers
|
||||
void redrawRestoreArea(int screenOffset, bool force);
|
||||
void buildShowBuf();
|
||||
byte _scaleX, _scaleY;
|
||||
byte _offScale;
|
||||
int8 _motionOffX, _motionOffY;
|
||||
bool _interlacedVideo;
|
||||
bool _dirty;
|
||||
byte _alpha;
|
||||
bool _firstFrame;
|
||||
Common::Rect *_restoreArea; // Area to be repainted by foreground
|
||||
|
||||
Video::VideoDecoder *_videoDecoder;
|
||||
bool _isFileHandled;
|
||||
};
|
||||
|
||||
class ROQSoundPlayer : public ROQPlayer {
|
||||
public:
|
||||
ROQSoundPlayer(GroovieEngine *vm);
|
||||
~ROQSoundPlayer();
|
||||
void createAudioStream(bool stereo) override;
|
||||
};
|
||||
|
||||
} // End of Groovie namespace
|
||||
|
||||
#endif // GROOVIE_VIDEO_ROQ_H
|
||||
584
engines/groovie/video/vdx.cpp
Normal file
584
engines/groovie/video/vdx.cpp
Normal file
@@ -0,0 +1,584 @@
|
||||
/* 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 "groovie/video/vdx.h"
|
||||
#include "groovie/graphics.h"
|
||||
#include "groovie/groovie.h"
|
||||
#include "groovie/lzss.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/mixer.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "graphics/paletteman.h"
|
||||
|
||||
#define TILE_SIZE 4 // Size of each tile on the image: only ever seen 4 so far
|
||||
#define VDX_IDENT 0x9267 // 37479
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
VDXPlayer::VDXPlayer(GroovieEngine *vm) :
|
||||
VideoPlayer(vm), _origX(0), _origY(0), _flagOnePrev(false),
|
||||
_fg(&_vm->_graphicsMan->_foreground), _bg(&_vm->_graphicsMan->_background) {
|
||||
}
|
||||
|
||||
VDXPlayer::~VDXPlayer() {
|
||||
//delete _audioStream;
|
||||
}
|
||||
|
||||
void VDXPlayer::resetFlags() {
|
||||
_flagOnePrev = false;
|
||||
}
|
||||
|
||||
void VDXPlayer::setOrigin(int16 x, int16 y) {
|
||||
_origX = x;
|
||||
_origY = y;
|
||||
}
|
||||
|
||||
void VDXPlayer::stopAudioStream() {
|
||||
if (_audioStream) {
|
||||
g_system->getMixer()->stopHandle(_soundHandle);
|
||||
}
|
||||
_audioStream = nullptr;
|
||||
}
|
||||
|
||||
uint16 VDXPlayer::loadInternal() {
|
||||
if (DebugMan.isDebugChannelEnabled(kDebugVideo)) {
|
||||
int8 i;
|
||||
debugN(1, "Groovie::VDX: New VDX: bitflags are ");
|
||||
for (i = 15; i >= 0; i--) {
|
||||
debugN(1, "%d", _flags & (1 << i)? 1 : 0);
|
||||
if (i % 4 == 0) {
|
||||
debugN(1, " ");
|
||||
}
|
||||
}
|
||||
debug(1, " <- 0 ");
|
||||
}
|
||||
// Flags:
|
||||
// - 1 Puzzle piece? Skip palette, don't redraw full screen, draw still to b/ack buffer
|
||||
// - 2 Transparent color is 0xFF
|
||||
// - 5 Skip still chunks
|
||||
// - 7
|
||||
// - 8 Just show the first frame
|
||||
// - 9 Start a palette fade in
|
||||
_flagZero = ((_flags & (1 << 0)) != 0);
|
||||
_flagOne = ((_flags & (1 << 1)) != 0);
|
||||
_flag2Byte = (_flags & (1 << 2)) ? 0xFF : 0x00;
|
||||
_flagThree = ((_flags & (1 << 3)) != 0);
|
||||
_flagFour = ((_flags & (1 << 4)) != 0);
|
||||
_flagFive = ((_flags & (1 << 5)) != 0);
|
||||
_flagSix = ((_flags & (1 << 6)) != 0);
|
||||
_flagSeven = ((_flags & (1 << 7)) != 0);
|
||||
_flagEight = ((_flags & (1 << 8)) != 0);
|
||||
_flagNine = ((_flags & (1 << 9)) != 0);
|
||||
|
||||
// Enable highspeed if we're not obeying fps, and not marked as special
|
||||
// This will be disabled in chunk audio if we're actually an audio vdx
|
||||
if (_vm->_modeSpeed == kGroovieSpeedFast && ((_flags & (1 << 15)) == 0))
|
||||
setOverrideSpeed(true);
|
||||
|
||||
if (_flagOnePrev && !_flagOne && !_flagEight) {
|
||||
_flagSeven = true;
|
||||
}
|
||||
|
||||
// Save _flagOne for the next video
|
||||
_flagOnePrev = _flagOne;
|
||||
|
||||
//_flagTransparent = _flagOne;
|
||||
_flagFirstFrame = _flagEight;
|
||||
//_flagSkipPalette = _flagSeven;
|
||||
_flagSkipPalette = false;
|
||||
//_flagSkipStill = _flagFive || _flagSeven;
|
||||
//_flagUpdateStill = _flagNine || _flagSix;
|
||||
|
||||
// Begin reading the file
|
||||
debugC(1, kDebugVideo, "Groovie::VDX: Playing video");
|
||||
|
||||
if (_file->readUint16LE() != VDX_IDENT) {
|
||||
error("Groovie::VDX: This does not appear to be a 7th guest VDX file");
|
||||
return 0;
|
||||
} else {
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: VDX file identified correctly");
|
||||
}
|
||||
|
||||
uint16 tmp;
|
||||
|
||||
// Skip unknown data: 6 bytes, ref Martine
|
||||
tmp = _file->readUint16LE();
|
||||
debugC(2, kDebugVideo, "Groovie::VDX: Martine1 = 0x%04X", tmp);
|
||||
tmp = _file->readUint16LE();
|
||||
debugC(2, kDebugVideo, "Groovie::VDX: Martine2 = 0x%04X", tmp);
|
||||
tmp = _file->readUint16LE();
|
||||
debugC(2, kDebugVideo, "Groovie::VDX: Martine3 (FPS?) = %d", tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool VDXPlayer::playFrameInternal() {
|
||||
byte currRes = 0x80;
|
||||
Common::ReadStream *vdxData = nullptr;
|
||||
while (currRes == 0x80) {
|
||||
currRes = _file->readByte();
|
||||
|
||||
// Skip unknown data: 1 byte, ref Edward
|
||||
byte tmp = _file->readByte();
|
||||
|
||||
uint32 compSize = _file->readUint32LE();
|
||||
uint8 lengthmask = _file->readByte();
|
||||
uint8 lengthbits = _file->readByte();
|
||||
|
||||
if (_file->eos())
|
||||
break;
|
||||
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: Edward = 0x%04X", tmp);
|
||||
|
||||
// Read the chunk data and decompress if needed
|
||||
if (compSize)
|
||||
vdxData = _file->readStream(compSize);
|
||||
|
||||
if (lengthmask && lengthbits) {
|
||||
Common::ReadStream *decompData = new LzssReadStream(vdxData, lengthmask, lengthbits);
|
||||
delete vdxData;
|
||||
vdxData = decompData;
|
||||
}
|
||||
|
||||
// Use the current chunk
|
||||
switch (currRes) {
|
||||
case 0x00:
|
||||
debugC(6, kDebugVideo, "Groovie::VDX: Replay frame");
|
||||
break;
|
||||
case 0x20:
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: Still frame");
|
||||
getStill(vdxData);
|
||||
break;
|
||||
case 0x25:
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: Animation frame");
|
||||
getDelta(vdxData);
|
||||
break;
|
||||
case 0x80:
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: Sound resource");
|
||||
chunkSound(vdxData);
|
||||
break;
|
||||
default:
|
||||
error("Groovie::VDX: Invalid resource type: %d", currRes);
|
||||
}
|
||||
delete vdxData;
|
||||
vdxData = nullptr;
|
||||
}
|
||||
|
||||
// Wait until the current frame can be shown
|
||||
waitFrame();
|
||||
|
||||
// TODO: Move it to a better place
|
||||
// Update the screen
|
||||
if (currRes == 0x25) {
|
||||
//if (_flagSeven) {
|
||||
//_vm->_graphicsMan->mergeFgAndBg();
|
||||
//}
|
||||
_vm->_graphicsMan->updateScreen(_bg);
|
||||
}
|
||||
|
||||
// Report the end of the video if we reached the end of the file or if we
|
||||
// just wanted to play one frame.
|
||||
if (_file->eos() || _flagFirstFrame) {
|
||||
_origX = _origY = 0;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint16 vdxBlockMapLookup[] = {
|
||||
0xc800, 0xec80, 0xfec8, 0xffec, 0xfffe, 0x3100, 0x7310, 0xf731,
|
||||
0xff73, 0xfff7, 0x6c80, 0x36c8, 0x136c, 0x6310, 0xc631, 0x8c63,
|
||||
0xf000, 0xff00, 0xfff0, 0x1111, 0x3333, 0x7777, 0x6666, 0xcccc,
|
||||
0x0ff0, 0x00ff, 0xffcc, 0x0076, 0xff33, 0x0ee6, 0xccff, 0x6770,
|
||||
0x33ff, 0x6ee0, 0x4800, 0x2480, 0x1248, 0x0024, 0x0012, 0x2100,
|
||||
0x4210, 0x8421, 0x0042, 0x0084, 0xf888, 0x0044, 0x0032, 0x111f,
|
||||
0x22e0, 0x4c00, 0x888f, 0x4470, 0x2300, 0xf111, 0x0e22, 0x00c4,
|
||||
0xf33f, 0xfccf, 0xff99, 0x99ff, 0x4444, 0x2222, 0xccee, 0x7733,
|
||||
0x00f8, 0x00f1, 0x00bb, 0x0cdd, 0x0f0f, 0x0f88, 0x13f1, 0x19b3,
|
||||
0x1f80, 0x226f, 0x27ec, 0x3077, 0x3267, 0x37e4, 0x38e3, 0x3f90,
|
||||
0x44cf, 0x4cd9, 0x4c99, 0x5555, 0x603f, 0x6077, 0x6237, 0x64c9,
|
||||
0x64cd, 0x6cd9, 0x70ef, 0x0f00, 0x00f0, 0x0000, 0x4444, 0x2222
|
||||
};
|
||||
|
||||
void VDXPlayer::getDelta(Common::ReadStream *in) {
|
||||
uint16 k, l;
|
||||
|
||||
// Get the size of the local palette
|
||||
uint16 palSize = in->readUint16LE();
|
||||
|
||||
// Load the palette if it isn't empty
|
||||
if (palSize) {
|
||||
uint16 palBitField[16];
|
||||
|
||||
// Load the bit field
|
||||
for (l = 0; l < 16; l++) {
|
||||
palBitField[l] = in->readUint16LE();
|
||||
}
|
||||
|
||||
// Load the actual palette
|
||||
for (l = 0; l < 16; l++) {
|
||||
int flag = 1 << 15;
|
||||
for (uint16 j = 0; j < 16; j++) {
|
||||
int palIndex = (l * 16) + j;
|
||||
|
||||
if (flag & palBitField[l]) {
|
||||
for (k = 0; k < 3; k++) {
|
||||
_palBuf[(palIndex * 3) + k] = in->readByte();
|
||||
}
|
||||
}
|
||||
flag = flag >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the palette
|
||||
if (!_flagSeven) {
|
||||
//if (!_flagSix && !_flagSeven) {
|
||||
setPalette(_palBuf);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 currOpCode = in->readByte();
|
||||
uint8 param1, param2, param3;
|
||||
|
||||
uint16 currentLine = 0;
|
||||
uint32 offset = 0;
|
||||
while (!in->eos()) {
|
||||
byte colors[16];
|
||||
if (currOpCode < 0x60) {
|
||||
param1 = in->readByte();
|
||||
param2 = in->readByte();
|
||||
expandColorMap(colors, vdxBlockMapLookup[currOpCode], param1, param2);
|
||||
decodeBlockDelta(offset, colors, 640);
|
||||
offset += TILE_SIZE;
|
||||
} else if (currOpCode > 0x7f) {
|
||||
param1 = in->readByte();
|
||||
param2 = in->readByte();
|
||||
param3 = in->readByte();
|
||||
expandColorMap(colors, (param1 << 8) + currOpCode, param2, param3);
|
||||
decodeBlockDelta(offset, colors, 640);
|
||||
offset += TILE_SIZE;
|
||||
} else switch (currOpCode) {
|
||||
case 0x60: /* Fill tile with the 16 colors given as parameters */
|
||||
for (l = 0; l < 16; l++) {
|
||||
colors[l] = in->readByte();
|
||||
}
|
||||
decodeBlockDelta(offset, colors, 640);
|
||||
offset += TILE_SIZE;
|
||||
break;
|
||||
case 0x61: /* Skip to the end of this line, next block is start of next */
|
||||
/* Note this is used at the end of EVERY line */
|
||||
currentLine++;
|
||||
offset = currentLine * TILE_SIZE * 640;
|
||||
break;
|
||||
case 0x62:
|
||||
case 0x63:
|
||||
case 0x64:
|
||||
case 0x65:
|
||||
case 0x66:
|
||||
case 0x67:
|
||||
case 0x68:
|
||||
case 0x69:
|
||||
case 0x6a:
|
||||
case 0x6b: /* Skip next param1 blocks (within line) */
|
||||
offset += (currOpCode - 0x62) * TILE_SIZE;
|
||||
break;
|
||||
case 0x6c:
|
||||
case 0x6d:
|
||||
case 0x6e:
|
||||
case 0x6f:
|
||||
case 0x70:
|
||||
case 0x71:
|
||||
case 0x72:
|
||||
case 0x73:
|
||||
case 0x74:
|
||||
case 0x75: /* Next param1 blocks are filled with color param2 */
|
||||
param1 = currOpCode - 0x6b;
|
||||
param2 = in->readByte();
|
||||
for (l = 0; l < 16; l++) {
|
||||
colors[l] = param2;
|
||||
}
|
||||
for (k = 0; k < param1; k++) {
|
||||
decodeBlockDelta(offset, colors, 640);
|
||||
offset += TILE_SIZE;
|
||||
}
|
||||
break;
|
||||
case 0x76:
|
||||
case 0x77:
|
||||
case 0x78:
|
||||
case 0x79:
|
||||
case 0x7a:
|
||||
case 0x7b:
|
||||
case 0x7c:
|
||||
case 0x7d:
|
||||
case 0x7e:
|
||||
case 0x7f: /* Next bytes contain colors to fill the next param1 blocks in the current line*/
|
||||
param1 = currOpCode - 0x75;
|
||||
for (k = 0; k < param1; k++) {
|
||||
param2 = in->readByte();
|
||||
for (l = 0; l < 16; l++) {
|
||||
colors[l] = param2;
|
||||
}
|
||||
decodeBlockDelta(offset, colors, 640);
|
||||
offset += TILE_SIZE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("Groovie::VDX: Broken somehow");
|
||||
}
|
||||
currOpCode = in->readByte();
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::getStill(Common::ReadStream *in) {
|
||||
uint16 numXTiles = in->readUint16LE();
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: numXTiles=%d", numXTiles);
|
||||
uint16 numYTiles = in->readUint16LE();
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: numYTiles=%d", numYTiles);
|
||||
|
||||
// It's skipped in the original:
|
||||
uint16 colorDepth = in->readUint16LE();
|
||||
debugC(5, kDebugVideo, "Groovie::VDX: colorDepth=%d", colorDepth);
|
||||
|
||||
uint16 imageWidth = TILE_SIZE * numXTiles;
|
||||
|
||||
uint8 mask = 0;
|
||||
byte *buf;
|
||||
if (_flagOne) {
|
||||
// Paint to the foreground
|
||||
buf = (byte *)_fg->getPixels();
|
||||
if (_flag2Byte) {
|
||||
mask = 0xff;
|
||||
} else {
|
||||
mask = 0;
|
||||
}
|
||||
|
||||
// TODO: Verify this is the right procedure. Couldn't find it on the
|
||||
// disassembly, but it's required to work properly
|
||||
_flagFirstFrame = true;
|
||||
} else {
|
||||
// Paint to the background
|
||||
buf = (byte *)_bg->getPixels();
|
||||
}
|
||||
|
||||
// Read the palette
|
||||
in->read(_palBuf, 3 * 256);
|
||||
|
||||
if (_flagSeven) {
|
||||
_flagFive = true;
|
||||
}
|
||||
|
||||
// Skip the frame when flag 5 is set, unless flag 1 is set
|
||||
if (!_flagFive || _flagOne) {
|
||||
|
||||
byte colors[16];
|
||||
for (uint16 j = 0; j < numYTiles; j++) {
|
||||
byte *currentTile = buf + j * TILE_SIZE * imageWidth;
|
||||
for (uint16 i = numXTiles; i; i--) {
|
||||
uint8 color1 = in->readByte();
|
||||
uint8 color0 = in->readByte();
|
||||
uint16 colorMap = in->readUint16LE();
|
||||
expandColorMap(colors, colorMap, color1, color0);
|
||||
decodeBlockStill(currentTile, colors, 640, mask);
|
||||
|
||||
currentTile += TILE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply the palette
|
||||
if (_flagNine) {
|
||||
// Flag 9 starts a fade in
|
||||
if (!isFastForwarding())
|
||||
fadeIn(_palBuf);
|
||||
else
|
||||
setPalette(_palBuf);
|
||||
} else {
|
||||
if (!_flagOne && !_flagSeven) {
|
||||
// Actually apply the palette
|
||||
setPalette(_palBuf);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_flagOne) {
|
||||
_vm->_graphicsMan->updateScreen(_bg);
|
||||
}
|
||||
/*
|
||||
if (_flagSix) {
|
||||
if (_flagOne) {
|
||||
_vm->_graphicsMan->updateScreen(_fg);
|
||||
} else {
|
||||
_vm->_graphicsMan->updateScreen(_bg);
|
||||
}
|
||||
_flagSix = 0;
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
// Skip the remaining data
|
||||
debugC(10, kDebugVideo, "Groovie::VDX: Skipping still frame");
|
||||
while (!in->eos()) {
|
||||
in->readByte();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::expandColorMap(byte *out, uint16 colorMap, uint8 color1, uint8 color0) {
|
||||
// It's a bit faster to start from the end
|
||||
out += 16;
|
||||
for (int i = 16; i; i--) {
|
||||
// Set the corresponding color
|
||||
// The following is an optimized version of:
|
||||
// *--out = (colorMap & 1) ? color1 : color0;
|
||||
uint8 selector = -(colorMap & 1);
|
||||
*--out = (selector & color1) | (~selector & color0);
|
||||
|
||||
// Update the flag map to test the next color
|
||||
colorMap >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::decodeBlockStill(byte *buf, byte *colors, uint16 imageWidth, uint8 mask) {
|
||||
assert(TILE_SIZE == 4);
|
||||
|
||||
for (int y = TILE_SIZE; y; y--) {
|
||||
if (_flagOne) {
|
||||
// TODO: optimize with bit logic?
|
||||
for (int x = 0; x < TILE_SIZE; x++) {
|
||||
// 0xff pixels don't modify the buffer
|
||||
if (*colors != 0xff) {
|
||||
// Write the color
|
||||
*buf = *colors | mask;
|
||||
// Note: if the mask is 0, it paints the image
|
||||
// else, it paints the image's mask using 0xff
|
||||
}
|
||||
|
||||
// Point to the next color
|
||||
colors++;
|
||||
|
||||
// Point to the next pixel
|
||||
buf++;
|
||||
}
|
||||
|
||||
// Point to the start of the next line
|
||||
buf += imageWidth - TILE_SIZE;
|
||||
} else {
|
||||
*((uint32 *)buf) = *((uint32 *)colors);
|
||||
colors += 4;
|
||||
|
||||
// Point to the start of the next line
|
||||
buf += imageWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::decodeBlockDelta(uint32 offset, byte *colors, uint16 imageWidth) {
|
||||
assert(TILE_SIZE == 4);
|
||||
|
||||
byte *dest;
|
||||
// TODO: Verify just the else block is required
|
||||
//if (_flagOne) {
|
||||
// Paint to the foreground
|
||||
//dest = (byte *)_fg->getPixels() + offset;
|
||||
//} else {
|
||||
dest = (byte *)_bg->getPixels() + offset;
|
||||
//}
|
||||
|
||||
// Move the pointers to the beginning of the current block
|
||||
int32 blockOff = _origX + _origY * imageWidth;
|
||||
dest += blockOff;
|
||||
byte *fgBuf = nullptr;
|
||||
if (_flagSeven) {
|
||||
fgBuf = (byte *)_fg->getPixels() + offset + blockOff;
|
||||
//byte *bgBuf = (byte *)_bg->getPixels() + offset + blockOff;
|
||||
}
|
||||
|
||||
for (int y = TILE_SIZE; y; y--) {
|
||||
if (_flagSeven) {
|
||||
// Paint mask
|
||||
for (int x = 0; x < TILE_SIZE; x++) {
|
||||
// TODO: this can probably be optimized with bit logic
|
||||
if (fgBuf[x] != 0xff) {
|
||||
if (*colors == 0xff) {
|
||||
dest[x] = fgBuf[x];
|
||||
} else {
|
||||
dest[x] = *colors;
|
||||
}
|
||||
}
|
||||
colors++;
|
||||
}
|
||||
fgBuf += imageWidth;
|
||||
} else {
|
||||
// Paint directly
|
||||
*((uint32 *)dest) = *((uint32 *)colors);
|
||||
colors += 4;
|
||||
}
|
||||
|
||||
// Move to the next line
|
||||
dest += imageWidth;
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::chunkSound(Common::ReadStream *in) {
|
||||
if (getOverrideSpeed())
|
||||
setOverrideSpeed(false);
|
||||
|
||||
if (!_audioStream && !isFastForwarding()) {
|
||||
_audioStream = Audio::makeQueuingAudioStream(22050, false);
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle, _audioStream);
|
||||
}
|
||||
|
||||
byte *data = (byte *)malloc(60000);
|
||||
int chunksize = in->read(data, 60000);
|
||||
if (!isFastForwarding()) {
|
||||
_audioStream->queueBuffer(data, chunksize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
|
||||
} else {
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
void VDXPlayer::fadeIn(uint8 *targetpal) {
|
||||
// Don't do anything if we're asked to skip palette changes
|
||||
if (_flagSkipPalette)
|
||||
return;
|
||||
|
||||
// TODO: Is it required? If so, move to an appropriate place
|
||||
// Copy the foreground to the background
|
||||
memcpy((byte *)_vm->_graphicsMan->_foreground.getPixels(), (byte *)_vm->_graphicsMan->_background.getPixels(), 640 * 320);
|
||||
|
||||
// Start a fadein
|
||||
_vm->_graphicsMan->fadeIn(targetpal);
|
||||
|
||||
// Show the background
|
||||
_vm->_graphicsMan->updateScreen(_bg);
|
||||
}
|
||||
|
||||
void VDXPlayer::setPalette(uint8 *palette) {
|
||||
if (_flagSkipPalette)
|
||||
return;
|
||||
|
||||
debugC(7, kDebugVideo, "Groovie::VDX: Setting palette");
|
||||
_syst->getPaletteManager()->setPalette(palette, 0, 256);
|
||||
}
|
||||
|
||||
} // End of Groovie namespace
|
||||
85
engines/groovie/video/vdx.h
Normal file
85
engines/groovie/video/vdx.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROOVIE_VIDEO_VDX_H
|
||||
#define GROOVIE_VIDEO_VDX_H
|
||||
|
||||
#include "groovie/video/player.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
}
|
||||
|
||||
namespace Groovie {
|
||||
|
||||
class VDXPlayer : public VideoPlayer {
|
||||
public:
|
||||
VDXPlayer(GroovieEngine *vm);
|
||||
~VDXPlayer() override;
|
||||
void resetFlags() override;
|
||||
void setOrigin(int16 x, int16 y) override;
|
||||
|
||||
protected:
|
||||
uint16 loadInternal() override;
|
||||
bool playFrameInternal() override;
|
||||
void stopAudioStream() override;
|
||||
|
||||
private:
|
||||
Graphics::Surface *_fg, *_bg;
|
||||
uint8 _palBuf[3 * 256];
|
||||
Audio::SoundHandle _soundHandle;
|
||||
|
||||
// Origin
|
||||
int16 _origX, _origY;
|
||||
|
||||
// Video flags
|
||||
bool _flagZero;
|
||||
bool _flagOne;
|
||||
bool _flagOnePrev;
|
||||
byte _flag2Byte;
|
||||
bool _flagThree;
|
||||
bool _flagFour;
|
||||
bool _flagFive;
|
||||
bool _flagSix;
|
||||
bool _flagSeven;
|
||||
bool _flagEight;
|
||||
bool _flagNine;
|
||||
|
||||
//bool _flagSkipStill;
|
||||
bool _flagSkipPalette;
|
||||
bool _flagFirstFrame;
|
||||
//bool _flagTransparent;
|
||||
//bool _flagUpdateStill;
|
||||
|
||||
void getStill(Common::ReadStream *in);
|
||||
void getDelta(Common::ReadStream *in);
|
||||
void expandColorMap(byte *out, uint16 colorMap, uint8 color1, uint8 color0);
|
||||
void decodeBlockStill(byte *buf, byte *colors, uint16 imageWidth, uint8 mask);
|
||||
void decodeBlockDelta(uint32 offset, byte *colors, uint16 imageWidth);
|
||||
void chunkSound(Common::ReadStream *in);
|
||||
void setPalette(uint8 *palette);
|
||||
void fadeIn(uint8 *palette);
|
||||
};
|
||||
|
||||
} // End of Groovie namespace
|
||||
|
||||
#endif // GROOVIE_VIDEO_VDX_H
|
||||
Reference in New Issue
Block a user