scummvm/engines/gob/imd.cpp

1250 lines
31 KiB
C++

/* 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 2
* 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "common/stdafx.h"
#include "common/endian.h"
#include "gob/gob.h"
#include "gob/imd.h"
#include "gob/global.h"
#include "gob/util.h"
#include "gob/dataio.h"
#include "gob/draw.h"
#include "gob/game.h"
#include "gob/inter.h"
#include "gob/palanim.h"
#include "gob/sound.h"
#include "gob/video.h"
namespace Gob {
ImdPlayer::ImdPlayer(GobEngine *vm) : _vm(vm) {
_curImd = 0;
_curFile[0] = 0;
_curX = 0;
_curY = 0;
_left = 0;
_top = 0;
_right = 0;
_bottom = 0;
_frameData = 0;
_vidBuffer = 0;
_frontSurf = 21;
_backSurf = 21;
_frontMem = 0;
_frameDelay = 0;
_noSound = true;
_soundStartTime = 0;
_skipFrames = 0;
_soundFreq = 0;
_soundSliceSize = 0;
_soundSlicesCount = 0;
_soundSliceLength = 0;
_soundStage = 0;
_audioStream = 0;
}
ImdPlayer::~ImdPlayer() {
if (_curImd) {
delete[] _curImd->palette;
delete[] _curImd->framesPos;
delete[] _curImd->frameCoords;
delete[] _curImd->extraPalette;
}
delete[] _frameData;
delete[] _vidBuffer;
delete[] _frontMem;
delete _curImd;
}
// flag bits: 0 = read and set palette
// 1 = read palette
ImdPlayer::Imd *ImdPlayer::loadImdFile(const char *path, SurfaceDesc *surfDesc, int8 flags) {
Imd *imdPtr;
int16 handle;
char buf[18];
uint32 framesPosPos = 0;
uint32 framesCordsPos = 0;
strncpy0(buf, path, 17);
if (!strchr(buf, '.')) {
buf[13] = 0;
strcat(buf, ".IMD");
}
handle = _vm->_dataIO->openData(buf);
if (handle < 0) {
warning("Can't open IMD \"%s\"", buf);
return 0;
}
imdPtr = new Imd;
assert(imdPtr);
memset(imdPtr, 0, sizeof(Imd));
imdPtr->handle = _vm->_dataIO->readUint16(handle);
imdPtr->verMin = _vm->_dataIO->readUint16(handle);
imdPtr->framesCount = _vm->_dataIO->readUint16(handle);
imdPtr->x = _vm->_dataIO->readUint16(handle);
imdPtr->y = _vm->_dataIO->readUint16(handle);
imdPtr->width = _vm->_dataIO->readUint16(handle);
imdPtr->height = _vm->_dataIO->readUint16(handle);
imdPtr->field_E = _vm->_dataIO->readUint16(handle);
imdPtr->curFrame = _vm->_dataIO->readUint16(handle);
if ((imdPtr->handle != 0) || ((imdPtr->verMin & 0xFF) < 2)) {
warning("%s: Version incorrect (%d,%X)", buf, imdPtr->handle, imdPtr->verMin);
_vm->_dataIO->closeData(handle);
delete imdPtr;
return 0;
}
imdPtr->handle = handle;
imdPtr->surfDesc = surfDesc;
imdPtr->firstFramePos = imdPtr->curFrame;
imdPtr->curFrame = 0;
if ((imdPtr->verMin & 0x800) && ((flags & 3) != 3))
imdPtr->extraPalette = new Video::Color[256];
if (flags & 3) {
imdPtr->palette = new Video::Color[256];
assert(imdPtr->palette);
_vm->_dataIO->readData(handle, (byte *) imdPtr->palette, 768);
} else
_vm->_dataIO->seekData(handle, 768, SEEK_CUR);
if ((flags & 3) == 1)
_vm->_video->setPalette(imdPtr->palette);
if ((imdPtr->verMin & 0xFF) >= 3) {
imdPtr->stdX = _vm->_dataIO->readUint16(handle);
if (imdPtr->stdX > 1) {
warning("%s: More than one standard coordinate quad found (%d)",
buf, imdPtr->stdX);
finishImd(imdPtr);
return 0;
}
if (imdPtr->stdX != 0) {
imdPtr->stdX = _vm->_dataIO->readUint16(handle);
imdPtr->stdY = _vm->_dataIO->readUint16(handle);
imdPtr->stdWidth = _vm->_dataIO->readUint16(handle);
imdPtr->stdHeight = _vm->_dataIO->readUint16(handle);
} else
imdPtr->stdX = -1;
} else
imdPtr->stdX = -1;
if ((imdPtr->verMin & 0xFF) >= 4) {
framesPosPos = _vm->_dataIO->readUint32(handle);
if (framesPosPos != 0) {
imdPtr->framesPos = new int32[imdPtr->framesCount];
assert(imdPtr->framesPos);
}
}
if (imdPtr->verMin & 0x8000)
framesCordsPos = _vm->_dataIO->readUint32(handle);
_noSound = true;
_soundStage = 0;
if (imdPtr->verMin & 0x4000) {
_soundFreq = _vm->_dataIO->readUint16(handle);
_soundSliceSize = _vm->_dataIO->readUint16(handle);
_soundSlicesCount = _vm->_dataIO->readUint16(handle);
if (_soundFreq < 0)
_soundFreq = -_soundFreq;
if (_soundSlicesCount < 0)
_soundSlicesCount = -_soundSlicesCount - 1;
if (_soundSlicesCount > 40) {
warning("%s: More than 40 sound slices found (%d)",
buf, _soundSlicesCount);
finishImd(imdPtr);
return 0;
}
_soundSliceLength = 1000 / (_soundFreq / _soundSliceSize);
_soundStage = 1;
_noSound = false;
_audioStream = Audio::makeAppendableAudioStream(_soundFreq, 0);
}
if (imdPtr->verMin & 0x2000) {
imdPtr->frameDataSize = _vm->_dataIO->readUint16(handle);
if (imdPtr->frameDataSize == 0) {
imdPtr->frameDataSize = _vm->_dataIO->readUint32(handle);
imdPtr->vidBufferSize = _vm->_dataIO->readUint32(handle);
} else
imdPtr->vidBufferSize = _vm->_dataIO->readUint16(handle);
} else {
imdPtr->frameDataSize = imdPtr->width * imdPtr->height + 500;
if (!(imdPtr->field_E & 0x100) || (imdPtr->field_E & 0x1000))
imdPtr->vidBufferSize = imdPtr->frameDataSize;
}
if (imdPtr->framesPos) {
_vm->_dataIO->seekData(handle, framesPosPos, SEEK_SET);
for (int i = 0; i < imdPtr->framesCount; i++)
imdPtr->framesPos[i] = _vm->_dataIO->readUint32(handle);
}
if (imdPtr->verMin & 0x8000) {
_vm->_dataIO->seekData(handle, framesCordsPos, SEEK_SET);
imdPtr->frameCoords = new ImdCoord[imdPtr->framesCount];
assert(imdPtr->frameCoords);
for (int i = 0; i < imdPtr->framesCount; i++) {
imdPtr->frameCoords[i].left = _vm->_dataIO->readUint16(handle);
imdPtr->frameCoords[i].top = _vm->_dataIO->readUint16(handle);
imdPtr->frameCoords[i].right = _vm->_dataIO->readUint16(handle);
imdPtr->frameCoords[i].bottom = _vm->_dataIO->readUint16(handle);
}
}
_vm->_dataIO->seekData(handle, imdPtr->firstFramePos, SEEK_SET);
return imdPtr;
}
void ImdPlayer::finishImd(ImdPlayer::Imd *&imdPtr) {
if (!imdPtr)
return;
if (_soundStage == 2)
_vm->_snd->stopSound(0);
_vm->_dataIO->closeData(imdPtr->handle);
delete[] imdPtr->frameCoords;
delete[] imdPtr->palette;
delete[] imdPtr->framesPos;
delete[] imdPtr->extraPalette;
delete imdPtr;
if (_audioStream) {
_audioStream->finish();
_vm->_mixer->stopHandle(_audioHandle);
_audioStream = 0;
}
}
int8 ImdPlayer::openImd(const char *path, int16 x, int16 y,
int16 startFrame, int16 flags) {
const char *src;
byte *vidMem;
SurfaceDesc *surfDesc;
if (!_curImd)
_curFile[0] = 0;
src = strrchr(path, '\\');
src = !src ? path : src + 1;
if ((path[0] != 0) && scumm_stricmp(_curFile, src)) {
closeImd();
_curImd = loadImdFile(path, 0, 3);
if (!_curImd)
return 0;
_curX = _curImd->x;
_curY = _curImd->y;
strncpy0(_curFile, src, 17);
delete[] _frameData;
_frameData = new byte[_curImd->frameDataSize + 500];
assert(_frameData);
memset(_frameData, 0, _curImd->frameDataSize + 500);
delete[] _vidBuffer;
_vidBuffer = new byte[_curImd->vidBufferSize + 500];
assert(_vidBuffer);
memset(_vidBuffer, 0, _curImd->vidBufferSize + 500);
if (!(flags & 0x100)) {
if (_vm->_global->_videoMode == 0x14) {
_backSurf = (flags & 0x80) ? 20 : 21;
if (!(_curImd->field_E & 0x100) || (_curImd->field_E & 0x2000)) {
setXY(_curImd, 0, 0);
_curImd->surfDesc =
_vm->_video->initSurfDesc(0x13,
_curImd->width, _curImd->height, 0);
} else {
_curImd->surfDesc = _vm->_draw->_spritesArray[_frontSurf];
if ((x != -1) || (y != -1)) {
_curX = x != -1 ? x : _curX;
_curY = y != -1 ? y : _curY;
setXY(_curImd, _curX, _curY);
}
}
if (flags & 0x40) {
_curX = x != -1 ? x : _curX;
_curY = y != -1 ? y : _curY;
if (_curImd->surfDesc->_vidMode == 0x14) {
surfDesc = _vm->_video->initSurfDesc(0x13,
_curImd->width, _curImd->height, 0);
_vm->_video->drawSprite(_vm->_draw->_spritesArray[21],
surfDesc, _curX, _curY,
_curX + _curImd->width - 1, _curY + _curImd->height - 1,
0, 0, 0);
vidMem = _curImd->surfDesc->getVidMem();
for (int i = 0; i < _curImd->height; i++)
for (int j = 0; j < _curImd->width; j++, vidMem++) {
*(vidMem) = *(surfDesc->getVidMem() +
(j / 4) + (surfDesc->getWidth() / 4 * i));
}
surfDesc = 0;
}
}
} else {
if ((x != -1) || (y != -1)) {
_curX = (x != -1) ? x : _curX;
_curY = (y != -1) ? y : _curY;
setXY(_curImd, _curX, _curY);
}
_backSurf = (flags & 0x80) ? 20 : 21;
_curImd->surfDesc = _vm->_draw->_spritesArray[_backSurf];
}
}
}
if (!_curImd)
return 0;
if (startFrame == -1) {
closeImd();
return 0;
}
_curX = (x != -1) ? x : _curX;
_curY = (y != -1) ? y : _curY;
WRITE_VAR(7, _curImd->framesCount);
return 1;
}
void ImdPlayer::closeImd(void) {
finishImd(_curImd);
delete[] _frameData;
delete[] _vidBuffer;
_frameData = 0;
_vidBuffer = 0;
_curImd = 0;
}
void ImdPlayer::setXY(ImdPlayer::Imd *imdPtr, int16 x, int16 y) {
int i;
if (imdPtr->stdX != -1) {
imdPtr->stdX = imdPtr->stdX - imdPtr->x + x;
imdPtr->stdY = imdPtr->stdY - imdPtr->y + y;
}
if (imdPtr->frameCoords) {
for (i = 0; i < imdPtr->framesCount; i++) {
if (imdPtr->frameCoords[i].left != -1) {
imdPtr->frameCoords[i].left =
imdPtr->frameCoords[i].left - imdPtr->x + x;
imdPtr->frameCoords[i].top =
imdPtr->frameCoords[i].top - imdPtr->y + y;
imdPtr->frameCoords[i].right =
imdPtr->frameCoords[i].right - imdPtr->x + x;
imdPtr->frameCoords[i].bottom =
imdPtr->frameCoords[i].bottom - imdPtr->y + y;
}
}
}
imdPtr->x = x;
imdPtr->y = y;
}
void ImdPlayer::drawFrame(Imd *imdPtr, int16 frame, int16 x, int16 y,
SurfaceDesc *dest) {
if (!dest)
dest = _vm->_draw->_frontSurface;
if (frame == 0)
_vm->_video->drawSprite(imdPtr->surfDesc, dest, 0, 0,
imdPtr->width - 1, imdPtr->height - 1, x, y, 0);
else if (imdPtr->frameCoords && (imdPtr->frameCoords[frame].left != -1))
_vm->_video->drawSprite(imdPtr->surfDesc, dest,
imdPtr->frameCoords[frame].left, imdPtr->frameCoords[frame].top,
imdPtr->frameCoords[frame].right, imdPtr->frameCoords[frame].bottom,
imdPtr->frameCoords[frame].left + x,
imdPtr->frameCoords[frame].top + y, 0);
else if (imdPtr->stdX != -1)
_vm->_video->drawSprite(imdPtr->surfDesc, dest,
imdPtr->stdX, imdPtr->stdY, imdPtr->stdX + imdPtr->stdWidth - 1,
imdPtr->stdY + imdPtr->stdHeight - 1, x + imdPtr->stdX,
y + imdPtr->stdY, 0);
else
_vm->_video->drawSprite(imdPtr->surfDesc, dest, 0, 0,
imdPtr->width - 1, imdPtr->height - 1, x, y, 0);
}
void ImdPlayer::renderFrame(Imd *imdPtr) {
int16 imdX, imdY;
int16 imdW, imdH;
int16 sW;
uint16 pixCount, pixWritten;
uint8 type;
byte *imdVidMem;
byte *imdVidMemBak;
byte *dataPtr = 0;
byte *srcPtr = 0;
dataPtr = _frameData;
imdX = imdPtr->x;
imdY = imdPtr->y;
imdW = imdPtr->width;
imdH = imdPtr->height;
sW = imdPtr->surfDesc->getWidth();
imdVidMem = imdPtr->surfDesc->getVidMem() + sW * imdY + imdX;
type = *dataPtr++;
srcPtr = dataPtr;
if (type & 0x10) { // Palette data
type ^= 0x10;
dataPtr += 49;
}
srcPtr = dataPtr;
if (type & 0x80) { // Frame data is compressed
srcPtr = _vidBuffer;
type &= 0x7F;
if ((type == 2) && (imdW == sW)) {
frameUncompressor(imdVidMem, dataPtr);
return;
} else
frameUncompressor(srcPtr, dataPtr);
}
if (type == 2) { // Whole block
for (int i = 0; i < imdH; i++) {
memcpy(imdVidMem, srcPtr, imdW);
srcPtr += imdW;
imdVidMem += sW;
}
} else if (type == 1) { // Sparse block
imdVidMemBak = imdVidMem;
for (int i = 0; i < imdH; i++) {
pixWritten = 0;
while (pixWritten < imdW) {
pixCount = *srcPtr++;
if (pixCount & 0x80) { // data
pixCount = MIN((pixCount & 0x7F) + 1, imdW - pixWritten);
memcpy(imdVidMem, srcPtr, pixCount);
pixWritten += pixCount;
imdVidMem += pixCount;
srcPtr += pixCount;
} else { // "hole"
pixCount = (pixCount + 1) % 256;
pixWritten += pixCount;
imdVidMem += pixCount;
}
}
imdVidMemBak += sW;
imdVidMem = imdVidMemBak;
}
} else if (type == 0x42) { // Whole quarter-wide block
for (int i = 0; i < imdH; i++) {
imdVidMemBak = imdVidMem;
for (int j = 0; j < imdW; j += 4, imdVidMem += 4, srcPtr++)
memset(imdVidMem, *srcPtr, 4);
imdVidMemBak += sW;
imdVidMem = imdVidMemBak;
}
} else if ((type & 0xF) == 2) { // Whole half-high block
for (; imdH > 1; imdH -= 2, imdVidMem += sW + sW, srcPtr += imdW) {
memcpy(imdVidMem, srcPtr, imdW);
memcpy(imdVidMem + sW, srcPtr, imdW);
}
if (imdH == -1)
memcpy(imdVidMem, srcPtr, imdW);
} else { // Sparse half-high block
imdVidMemBak = imdVidMem;
for (int i = 0; i < imdH; i += 2) {
pixWritten = 0;
while (pixWritten < imdW) {
pixCount = *srcPtr++;
if (pixCount & 0x80) { // data
pixCount = MIN((pixCount & 0x7F) + 1, imdW - pixWritten);
memcpy(imdVidMem, srcPtr, pixCount);
memcpy(imdVidMem + sW, srcPtr, pixCount);
pixWritten += pixCount;
imdVidMem += pixCount;
srcPtr += pixCount;
} else { // "hole"
pixCount = (pixCount + 1) % 256;
pixWritten += pixCount;
imdVidMem += pixCount;
}
}
imdVidMemBak += sW + sW;
imdVidMem = imdVidMemBak;
}
}
}
void ImdPlayer::frameUncompressor(byte *dest, byte *src) {
int i;
byte buf[4370];
uint16 chunkLength;
uint16 frameLength;
uint16 bufPos1;
uint16 bufPos2;
uint16 tmp;
uint8 chunkBitField;
uint8 chunkCount;
bool mode;
frameLength = READ_LE_UINT16(src);
src += 4;
if ((READ_LE_UINT16(src) == 0x1234) && (READ_LE_UINT16(src + 2) == 0x5678)) {
src += 4;
bufPos1 = 273;
mode = 1; // 123Ch (cmp al, 12h)
} else {
bufPos1 = 4078;
mode = 0; // 275h (jnz +2)
}
memset(buf, 32, bufPos1);
chunkCount = 1;
chunkBitField = 0;
while (frameLength > 0) {
chunkCount--;
if (chunkCount == 0) {
tmp = *src++;
chunkCount = 8;
chunkBitField = tmp;
}
if (chunkBitField % 2) {
chunkBitField >>= 1;
buf[bufPos1] = *src;
*dest++ = *src++;
bufPos1 = (bufPos1 + 1) % 4096;
frameLength--;
continue;
}
chunkBitField >>= 1;
tmp = READ_LE_UINT16(src);
src += 2;
chunkLength = ((tmp & 0xF00) >> 8) + 3;
if ((mode && ((chunkLength & 0xFF) == 0x12)) ||
(!mode && (chunkLength == 0)))
chunkLength = *src++ + 0x12;
bufPos2 = (tmp & 0xFF) + ((tmp >> 4) & 0x0F00);
if (((tmp + chunkLength) >= 4096) ||
((chunkLength + bufPos1) >= 4096)) {
for (i = 0; i < chunkLength; i++, dest++) {
*dest = buf[bufPos2];
buf[bufPos1] = buf[bufPos2];
bufPos1 = (bufPos1 + 1) % 4096;
bufPos2 = (bufPos2 + 1) % 4096;
}
} else if (((tmp + chunkLength) < bufPos1) ||
((chunkLength + bufPos1) < bufPos2)) {
memcpy(dest, buf + bufPos2, chunkLength);
memmove(buf + bufPos1, buf + bufPos2, chunkLength);
dest += chunkLength;
bufPos1 += chunkLength;
bufPos2 += chunkLength;
} else {
for (i = 0; i < chunkLength; i++, dest++, bufPos1++, bufPos2++) {
*dest = buf[bufPos2];
buf[bufPos1] = buf[bufPos2];
}
}
frameLength -= chunkLength;
}
}
void ImdPlayer::play(const char *path, int16 x, int16 y, bool interruptible) {
int16 mouseX;
int16 mouseY;
int16 buttons;
_vm->_util->setFrameRate(12);
if (!openImd(path, x, y, 0, 2))
return;
_vm->_video->fillRect(_vm->_draw->_frontSurface, x, y,
x + _curImd->width - 1, y + _curImd->height - 1, 0);
for (int i = 0; i < _curImd->framesCount; i++) {
play(i, 4, 0, 255, 0, _curImd->framesCount - 1);
if (_vm->_quitRequested || (interruptible &&
(_vm->_game->checkKeys(&mouseX, &mouseY, &buttons, 0) == 0x11B)))
break;
}
closeImd();
}
void ImdPlayer::play(const char *path, int16 x, int16 y, int16 startFrame,
int16 frames, bool fade, bool interruptible) {
int16 mouseX;
int16 mouseY;
int16 buttons = 0;
int endFrame;
_vm->_util->setFrameRate(12);
if (!openImd(path, x, y, 0, 0))
return;
_vm->_video->fillRect(_vm->_draw->_frontSurface, x, y,
x + _curImd->width - 1, y + _curImd->height - 1, 0);
if (fade)
_vm->_palAnim->fade(0, -2, 0);
endFrame = frames > 0 ? frames : _curImd->framesCount;
for (int i = startFrame; i < endFrame; i++) {
view(_curImd, i);
drawFrame(_curImd, i, x, y);
if (fade) {
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
fade = false;
}
_vm->_video->waitRetrace();
if (_vm->_quitRequested || (interruptible &&
(_vm->_game->checkKeys(&mouseX, &mouseY, &buttons, 0) == 0x11B))) {
_vm->_palAnim->fade(0, -2, 0);
_vm->_video->clearSurf(_vm->_draw->_frontSurface);
memset((char *) _vm->_draw->_vgaPalette, 0, 768);
WRITE_VAR(4, buttons);
WRITE_VAR(0, 0x11B);
WRITE_VAR(57, (uint32) -1);
break;
}
_vm->_util->waitEndFrame();
}
if (frames < 0) {
endFrame = _curImd->framesCount + frames;
for (int i = _curImd->framesCount - 1; i >= endFrame; i--) {
seekFrame(_curImd, i, SEEK_SET, true);
drawFrame(_curImd, i, x, y);
_vm->_video->waitRetrace();
if (_vm->_quitRequested || (interruptible &&
(_vm->_game->checkKeys(&mouseX, &mouseY, &buttons, 0) == 0x11B))) {
_vm->_palAnim->fade(0, -2, 0);
_vm->_video->clearSurf(_vm->_draw->_frontSurface);
memset((char *) _vm->_draw->_vgaPalette, 0, 768);
WRITE_VAR(4, buttons);
WRITE_VAR(0, 0x11B);
WRITE_VAR(57, (uint32) -1);
break;
}
_vm->_util->waitEndFrame();
}
}
closeImd();
}
void ImdPlayer::play(int16 frame, uint16 palCmd,
int16 palStart, int16 palEnd, int16 palFrame, int16 lastFrame) {
uint32 viewRet = 0;
SurfaceDesc *surfDescBak;
bool modifiedPal = false;
_vm->_draw->_showCursor = 0;
if ((frame < 0) || (frame > lastFrame))
return;
palCmd &= 0x3F;
if ((frame == palFrame) || ((frame == lastFrame) && (palCmd == 8))) {
modifiedPal = true;
_vm->_draw->_applyPal = true;
if (palCmd >= 4)
copyPalette(palStart, palEnd);
}
if (modifiedPal && (palCmd == 8) && (_backSurf == 20))
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
if (_curImd->surfDesc) {
if (_curImd->surfDesc->_vidMode == 0x14) {
if ((_frontMem == _vm->_draw->_frontSurface->getVidMem()) &&
(_frontSurf == 20)) {
_vm->_draw->_frontSurface->swap(_vm->_draw->_backSurface);
viewRet = view(_curImd, frame);
_vm->_draw->_frontSurface->swap(_vm->_draw->_backSurface);
} else
viewRet = view(_curImd, frame);
if (_frontSurf == 21)
_vm->_draw->invalidateRect(_left, _top, _right, _bottom);
} else {
if ((_curImd->field_E & 0x100) &&
(_vm->_global->_videoMode == 0x14) &&
(_frontSurf == 20) &&
(checkFrameType(_curImd, frame) & 0x8000) &&
(_backSurf == 21)) {
surfDescBak = _curImd->surfDesc;
if (_frontMem == _vm->_draw->_spritesArray[20]->getVidMem())
_curImd->surfDesc = _vm->_draw->_spritesArray[21];
else
_curImd->surfDesc = _vm->_draw->_spritesArray[20];
setXY(_curImd, _curX, _curY);
viewRet = view(_curImd, frame);
_curImd->surfDesc = surfDescBak;
setXY(_curImd, 0, 0);
} else {
viewRet = view(_curImd, frame);
if (!(viewRet & 0x800))
drawFrame(frame);
}
}
} else
viewRet = view(_curImd, frame);
if (modifiedPal && (palCmd == 16)) {
if (_backSurf == 21)
_vm->_draw->forceBlit();
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
_vm->_draw->_noInvalidated = true;
}
if (viewRet & 0x10) {
copyPalette(palStart, palEnd);
if (_backSurf == 20)
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
else
_vm->_draw->_applyPal = true;
}
if (modifiedPal && (palCmd == 8) && (_backSurf == 21))
_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
if (!(viewRet & 0x800)) {
if (_vm->_draw->_cursorIndex == -1) {
if (_frontSurf == 20)
flipFrontMem();
else
_vm->_draw->blitInvalidated();
} else
_vm->_draw->animateCursor(-1);
}
if (modifiedPal && ((palCmd == 2) || (palCmd == 4)))
_vm->_palAnim->fade(_vm->_global->_pPaletteDesc, -2, 0);
// To allow quitting, etc. during IMDs
_vm->_util->processInput();
if (_vm->_quitRequested)
return;
if (_soundStage != 2) {
if (viewRet & 0x800) {
if (_frameDelay == 0)
_vm->_util->delay(30);
else {
_frameDelay -= 30;
if (_frameDelay < 0)
_frameDelay = 0;
}
} else
_vm->_util->waitEndFrame();
}
_vm->_inter->animPalette();
}
inline void ImdPlayer::drawFrame(int16 frame) {
if (_backSurf == 21) {
if (_vm->_global->_videoMode == 0x14) {
if (_frontSurf == 21) {
_vm->_draw->_frontSurface->swap(_vm->_draw->_spritesArray[21]);
drawFrame(_curImd, frame, _curX, _curY);
_vm->_draw->_frontSurface->swap(_vm->_draw->_spritesArray[21]);
_vm->_draw->invalidateRect(_curX + _left, _curY + _top,
_curX + _right, _curY + _bottom);
} else {
if (_frontMem == _vm->_draw->_spritesArray[20]->getVidMem()) {
_vm->_draw->_frontSurface->swap(_vm->_draw->_spritesArray[21]);
drawFrame(_curImd, frame, _curX, _curY);
_vm->_draw->_frontSurface->swap(_vm->_draw->_spritesArray[21]);
} else
drawFrame(_curImd, frame, _curX, _curY);
}
} else
_vm->_draw->invalidateRect(_left, _top, _right, _bottom);
} else if (_vm->_global->_videoMode == 0x14)
drawFrame(_curImd, frame, _curX, _curY);
}
inline void ImdPlayer::copyPalette(int16 palStart, int16 palEnd) {
if ((palStart == -1) || (palEnd == -1))
memcpy((char *) _vm->_global->_pPaletteDesc->vgaPal,
(char *) _curImd->palette, 768);
else
memcpy(((char *) (_vm->_global->_pPaletteDesc->vgaPal)) +
palStart * 3, ((char *) (_curImd->palette)) + palStart * 3,
(palEnd - palStart + 1) * 3);
}
inline void ImdPlayer::flipFrontMem() {
if (_frontMem == _vm->_draw->_frontSurface->getVidMem())
_frontMem = _vm->_draw->_backSurface->getVidMem();
else
_frontMem = _vm->_draw->_frontSurface->getVidMem();
}
uint16 ImdPlayer::checkFrameType(Imd *imdPtr, int16 frame) {
uint16 retVal = 0;
uint32 posBak;
uint32 tmp;
uint16 cmd;
int16 frameBak;
if (!imdPtr)
return 0x8000;
posBak = _vm->_dataIO->getPos(imdPtr->handle);
frameBak = imdPtr->curFrame;
if (imdPtr->curFrame != frame) {
retVal |= 0x2000;
seekFrame(imdPtr, frame, SEEK_SET);
}
do {
if (frame != 0) {
if (imdPtr->stdX != -1)
retVal |= 0x1000;
if (imdPtr->frameCoords && (imdPtr->frameCoords[frame].left != -1))
retVal |= 0x400;
}
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
if ((cmd & 0xFFF8) == 0xFFF0) {
if (cmd == 0xFFF0) {
_vm->_dataIO->seekData(imdPtr->handle, 2, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
}
if (cmd == 0xFFF1) {
retVal = 0x8000;
continue;
} else if (cmd == 0xFFF2) { // Skip (16 bit)
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
_vm->_dataIO->seekData(imdPtr->handle, cmd, SEEK_CUR);
retVal = 0x8000;
continue;
} else if (cmd == 0xFFF3) { // Skip (32 bit)
tmp = _vm->_dataIO->readUint32(imdPtr->handle);
_vm->_dataIO->seekData(imdPtr->handle, cmd, SEEK_CUR);
retVal = 0x8000;
continue;
}
}
// Jump to frame
if (cmd == 0xFFFD) {
frame = _vm->_dataIO->readUint16(imdPtr->handle);
if (imdPtr->framesPos) {
_vm->_dataIO->seekData(imdPtr->handle,
imdPtr->framesPos[frame], SEEK_SET);
retVal |= 0x200;
continue;
}
break;
}
// Next sound slice data
if (cmd == 0xFF00) {
_vm->_dataIO->seekData(imdPtr->handle,
_soundSliceSize, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
// Initial sound data (all slices)
} else if (cmd == 0xFF01) {
_vm->_dataIO->seekData(imdPtr->handle,
_soundSliceSize * _soundSlicesCount, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
}
// Frame video data
if (cmd != 0) {
_vm->_dataIO->readData(imdPtr->handle, _frameData, 5);
retVal |= _frameData[0];
} else
retVal |= 0x800;
break;
} while (true);
_vm->_dataIO->seekData(imdPtr->handle, posBak, SEEK_SET);
imdPtr->curFrame = frameBak;
return retVal;
}
void ImdPlayer::seekFrame(Imd *imdPtr, int16 frame, int16 from, bool restart) {
uint32 framePos = 0;
if (!imdPtr)
return;
if (from == SEEK_CUR)
frame += imdPtr->curFrame;
else if (from == SEEK_END)
frame = imdPtr->framesCount - frame - 1;
if (frame >= imdPtr->framesCount)
return;
if (frame == 0) {
framePos = imdPtr->firstFramePos;
} else if (frame == 1) {
framePos = imdPtr->firstFramePos;
_vm->_dataIO->seekData(imdPtr->handle, framePos, SEEK_SET);
framePos += _vm->_dataIO->readUint16(imdPtr->handle) + 4;
} else if (imdPtr->framesPos) {
framePos = imdPtr->framesPos[frame];
} else if (restart && (_soundStage == 0)) {
for (int i = 0; i <= frame; i++)
view(_curImd, i);
} else
error("%s: Frame %d is not directly accessible", _curFile, frame);
_vm->_dataIO->seekData(imdPtr->handle, framePos, SEEK_SET);
imdPtr->curFrame = frame;
}
uint32 ImdPlayer::view(Imd *imdPtr, int16 frame) {
uint32 retVal = 0;
uint32 cmd = 0;
int16 xBak, yBak, heightBak, widthBak;
bool hasNextCmd = false;
bool startSound = false;
if (!imdPtr)
return 0x8000;
if (frame != imdPtr->curFrame) {
retVal |= 0x2000;
seekFrame(imdPtr, frame, SEEK_SET);
}
_left = xBak = imdPtr->x;
_top = yBak = imdPtr->y;
_bottom = heightBak= imdPtr->height;
_right = widthBak = imdPtr->width;
_right += _left - 1;
_bottom += _top - 1;
if ((frame == 0) && (imdPtr->verMin & 0x800))
_vm->_video->setPalette(imdPtr->palette);
do {
if (frame != 0) {
if (imdPtr->stdX != -1) {
_left = imdPtr->x = imdPtr->stdX;
_top = imdPtr->y = imdPtr->stdY;
_right = imdPtr->width = imdPtr->stdWidth;
_bottom = imdPtr->height = imdPtr->stdHeight;
_right += _left - 1;
_bottom += _top - 1;
retVal |= 0x1000;
}
if (imdPtr->frameCoords &&
(imdPtr->frameCoords[frame].left != -1)) {
_left = imdPtr->x = imdPtr->frameCoords[frame].left;
_top = imdPtr->y = imdPtr->frameCoords[frame].top;
_right = imdPtr->width =
imdPtr->frameCoords[frame].right - imdPtr->x + 1;
_bottom = imdPtr->height =
imdPtr->frameCoords[frame].bottom - imdPtr->y + 1;
_right += _left - 1;
_bottom += _top - 1;
retVal |= 0x400;
}
}
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
if ((cmd & 0xFFF8) == 0xFFF0) {
if (cmd == 0xFFF0) {
_vm->_dataIO->seekData(imdPtr->handle, 2, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
}
if (cmd == 0xFFF1) {
retVal = 0x8000;
continue;
} else if (cmd == 0xFFF2) { // Skip (16 bit)
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
_vm->_dataIO->seekData(imdPtr->handle, cmd, SEEK_CUR);
retVal = 0x8000;
continue;
} else if (cmd == 0xFFF3) { // Skip (32 bit)
cmd = _vm->_dataIO->readUint32(imdPtr->handle);
_vm->_dataIO->seekData(imdPtr->handle, cmd, SEEK_CUR);
retVal = 0x8000;
continue;
}
}
if (_soundStage != 0) {
byte *soundBuf;
if (!hasNextCmd)
waitEndSoundSlice();
// Next sound slice data
if (cmd == 0xFF00) {
if (!hasNextCmd && !_noSound) {
soundBuf = new byte[_soundSliceSize];
assert(soundBuf);
_vm->_dataIO->readData(imdPtr->handle, soundBuf,
_soundSliceSize);
_vm->_snd->convToSigned(soundBuf, _soundSliceSize);
_audioStream->queueBuffer(soundBuf, _soundSliceSize);
} else
_vm->_dataIO->seekData(imdPtr->handle,
_soundSliceSize, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
// Initial sound data (all slices)
} else if (cmd == 0xFF01) {
int dataLength = _soundSliceSize * _soundSlicesCount;
if (!hasNextCmd && !_noSound) {
soundBuf = new byte[dataLength];
assert(soundBuf);
_vm->_dataIO->readData(imdPtr->handle, soundBuf, dataLength);
_vm->_snd->convToSigned(soundBuf, dataLength);
_soundStage = 1;
startSound = true;
_audioStream->queueBuffer(soundBuf, dataLength);
} else
_vm->_dataIO->seekData(imdPtr->handle, dataLength, SEEK_CUR);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
// Empty sound slice
} else if (!hasNextCmd && (!_noSound)) {
soundBuf = new byte[_soundSliceSize];
assert(soundBuf);
memset(soundBuf, 0, _soundSliceSize);
_audioStream->queueBuffer(soundBuf, _soundSliceSize);
}
}
// Set palette
if (cmd == 0xFFF4) {
_vm->_dataIO->seekData(imdPtr->handle, 2, SEEK_CUR);
retVal |= 0x10;
if (imdPtr->extraPalette) {
_vm->_dataIO->readData(imdPtr->handle,
(byte *) imdPtr->extraPalette, 768);
_vm->_video->setPalette(imdPtr->extraPalette);
} else if (imdPtr->palette)
_vm->_dataIO->readData(imdPtr->handle,
(byte *) imdPtr->palette, 768);
else
_vm->_dataIO->readData(imdPtr->handle, _frameData, 768);
cmd = _vm->_dataIO->readUint16(imdPtr->handle);
}
hasNextCmd = false;
// Jump to frame
if (cmd == 0xFFFD) {
frame = _vm->_dataIO->readUint16(imdPtr->handle);
if (imdPtr->framesPos) {
imdPtr->curFrame = frame;
_vm->_dataIO->seekData(imdPtr->handle,
imdPtr->framesPos[frame], SEEK_SET);
hasNextCmd = true;
retVal |= 0x200;
}
} else if (cmd == 0xFFFC) {
retVal |= 1;
cmd = _vm->_dataIO->readUint32(imdPtr->handle);
_vm->_dataIO->readData(imdPtr->handle, _frameData, cmd + 2);
if (imdPtr->surfDesc) {
int16 left = imdPtr->x;
int16 top = imdPtr->y;
int16 right = imdPtr->width + left;
int16 bottom = imdPtr->height + top;
if (imdPtr->surfDesc->getWidth() < right) {
left = 0;
right = imdPtr->width;
}
if (imdPtr->surfDesc->getWidth() < right)
right = imdPtr->surfDesc->getWidth();
if (imdPtr->surfDesc->getHeight() < bottom) {
top = 0;
bottom = imdPtr->height;
}
if (imdPtr->surfDesc->getHeight() < bottom)
bottom = imdPtr->surfDesc->getHeight();
imdPtr->x = left;
imdPtr->y = top;
imdPtr->height = bottom - top;
imdPtr->width = right - left;
renderFrame(imdPtr);
}
retVal |= _frameData[0];
// Frame video data
} else if (cmd != 0) {
_vm->_dataIO->readData(imdPtr->handle, _frameData, cmd + 2);
if (imdPtr->surfDesc)
renderFrame(imdPtr);
retVal |= _frameData[0];
} else
retVal |= 0x800;
} while (hasNextCmd);
if (startSound) {
_vm->_snd->stopSound(0);
_vm->_mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_audioHandle, _audioStream);
_soundStartTime = _vm->_util->getTimeKey();
_skipFrames = 0;
_soundStage = 2;
}
imdPtr->x = xBak;
imdPtr->y = yBak;
imdPtr->width = widthBak;
imdPtr->height = heightBak;
imdPtr->curFrame++;
if ((imdPtr->curFrame == imdPtr->framesCount) && (_soundStage == 2)) {
waitEndSoundSlice();
_audioStream->finish();
_vm->_mixer->stopHandle(_audioHandle);
_audioStream = 0;
}
return retVal;
}
inline void ImdPlayer::waitEndSoundSlice() {
if (_soundStage != 2)
return;
if (_skipFrames == 0) {
_vm->_video->retrace();
int32 waitTime = (_curImd->curFrame * _soundSliceLength) -
(_vm->_util->getTimeKey() - _soundStartTime);
if (waitTime < 0) {
_skipFrames = -waitTime / _soundSliceLength;
warning("IMD A/V sync broken, skipping %d frame(s)", _skipFrames + 1);
} else if (waitTime > 0)
_vm->_util->delay(waitTime);
} else
_skipFrames--;
}
} // End of namespace Gob