scummvm/engines/cryo/video.cpp
2018-04-22 00:56:21 +02:00

618 lines
15 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.
*
*/
#include "cryo/cryo.h"
#include "cryo/video.h"
namespace Cryo {
HnmPlayer::HnmPlayer(CryoEngine *vm) : _vm(vm) {
_soundStarted = false;
_pendingSounds = 0;
_timeDrift = 0.0;
_nextFrameTime = 0.0;
_expectedFrameTime = 0.0;
_rate = 0.0;
_useSoundSync = false;
_useSound = true;
_soundChannel = nullptr;
_prevRight = _prevLeft = 0;
_useAdpcm = false;
_customChunkHandler = nullptr;
_preserveColor0 = false;
_safePalette = false;
for (int i = 0; i < 256; i++)
decompTable[i] = 0;
}
// Original name: CLHNM_New
void HnmPlayer::resetInternals() {
_frameNum = 0;
_file = nullptr;
_tmpBuffer[0] = nullptr;
_tmpBuffer[1] = nullptr;
_finalBuffer = nullptr;
_readBuffer = nullptr;
for (int i = 0; i < 256; i++) {
_palette[i].a = 0;
_palette[i].r = 0;
_palette[i].g = 0;
_palette[i].b = 0;
}
}
// Original name: CLHNM_SetFile
void HnmPlayer::setFile(Common::File *file) {
_file = file;
}
// Original name: CLHNM_SetupTimer
void HnmPlayer::setupTimer(float rate) {
_rate = 100.0 / rate;
}
// Original name: CLHNM_ResetInternalTimer
void HnmPlayer::resetInternalTimer() {
_timeDrift = 0.0;
_nextFrameTime = _expectedFrameTime = _vm->_timerTicks;
}
// Original name: CLHNM_Reset
void HnmPlayer::reset() {
_frameNum = 0;
_soundStarted = false;
_pendingSounds = 0;
resetInternalTimer();
}
// Original name: CLHNM_Init
void HnmPlayer::init() {
_customChunkHandler = nullptr;
_preserveColor0 = false;
_useSound = true;
}
// Original name: CLHNM_SetForceZero2Black
void HnmPlayer::setForceZero2Black(bool forceblack) {
_preserveColor0 = forceblack;
}
// Original name: CLHNM_WaitLoop
void HnmPlayer::waitLoop() {
_expectedFrameTime += _rate;
_nextFrameTime = _expectedFrameTime - _timeDrift;
if (_useSoundSync && _vm->_timerTicks > 1000.0 + _nextFrameTime)
_useSound = false;
while (_vm->_timerTicks < _nextFrameTime) {} // waste time
_timeDrift = _vm->_timerTicks - _nextFrameTime;
}
// Original name: CLHNM_WantsSound
void HnmPlayer::wantsSound(bool sound) {
_useSound = sound;
}
// Original name: CLHNM_SetupSound
void HnmPlayer::setupSound(unsigned int rate, bool stereo, bool is16bits) {
_soundChannel = new CSoundChannel(_vm->_mixer, rate, stereo, is16bits);
}
// Original name: CLHNM_CloseSound
void HnmPlayer::closeSound() {
if (_soundChannel) {
_soundChannel->stop();
delete(_soundChannel);
_soundChannel = nullptr;
}
}
// Original name: CLHNM_LoadDecompTable
void HnmPlayer::loadDecompTable(int16 *buffer) {
for (int16 i = 0; i < 256; i++) {
int16 e = *buffer++;
decompTable[i] = LE16(e);
}
}
// Original name: CLHNM_DecompADPCM
void HnmPlayer::decompADPCM(byte *buffer, int16 *output, int size) {
int16 l = _prevLeft;
int16 r = _prevRight;
size &= ~1;
while (size--) {
*output++ = l += decompTable[*buffer++];
*output++ = r += decompTable[*buffer++];
if (l > 512 || r > 512)
error("decompADPCM - Unexpected values");
}
_prevLeft = l;
_prevRight = r;
}
// Original name: CLHNM_ReadHeader
void HnmPlayer::readHeader() {
_header._signature = _file->readUint32BE();
_file->skip(4);
_header._width = _file->readUint16LE();
_header._height = _file->readUint16LE();
_file->skip(4);
_header._numbFrame = _file->readSint32LE();
_file->skip(8);
_header._bufferSize = _file->readSint32LE();
_file->skip(32);
_header._bufferSize += 4096; //TODO: checkme
}
// Original name: CLHNM_GetVersion
int16 HnmPlayer::getVersion() {
if (_header._signature == MKTAG('H','N','M','4'))
return 4;
return -1;
}
// Original name: CLHNM_AllocMemory
void HnmPlayer::allocMemory() {
// TODO: rework this code
_tmpBuffer[0] = (byte *)malloc(_header._bufferSize + 2);
if (!_tmpBuffer[0])
return;
_tmpBuffer[1] = (byte *)malloc(_header._bufferSize + 2);
if (!_tmpBuffer[1]) {
free(_tmpBuffer[0]);
_tmpBuffer[0] = nullptr;
return;
}
_readBuffer = (byte *)malloc(_header._bufferSize + 2);
if (!_readBuffer) {
free(_tmpBuffer[0]);
_tmpBuffer[0] = nullptr;
free(_tmpBuffer[1]);
_tmpBuffer[1] = nullptr;
}
}
// Original name: CLHNM_DeallocMemory
void HnmPlayer::deallocMemory() {
free(_tmpBuffer[0]);
free(_tmpBuffer[1]);
free(_readBuffer);
_tmpBuffer[0] = nullptr;
_tmpBuffer[1] = nullptr;
_readBuffer = nullptr;
}
// Original name: CLHNM_SetFinalBuffer
void HnmPlayer::setFinalBuffer(byte *buffer) {
_finalBuffer = buffer;
}
// Original name: CLHNM_GetFrameNum
int HnmPlayer::getFrameNum() {
return _frameNum;
}
// Original name: CLHNM_TryRead
void HnmPlayer::tryRead(int size) {
_file->read(_readBuffer, size);
}
// Original name: CLHNM_LoadFrame
bool HnmPlayer::loadFrame() {
tryRead(4);
int chunk = *(int *)_readBuffer;
chunk = LE32(chunk);
chunk &= 0xFFFFFF; // upper bit - keyframe mark?
if (!chunk)
return false;
if (chunk - 4 > _header._bufferSize)
error("loadFrame - Chunk size");
tryRead(chunk - 4);
_dataPtr = _readBuffer;
return true;
}
// Original name CLHNM_DecompLempelZiv
void HnmPlayer::decompLempelZiv(byte *buffer, byte *output) {
byte *inp = buffer;
byte *out = output;
unsigned int queue = 0;
int qpos = -1;
//TODO: fix for BE
#define GetBit() ( 1 & ( (qpos >= 0) ? (queue >> qpos--) : (queue = *(unsigned int*)((inp += 4) - 4)) >> ((qpos = 30) + 1) ) )
for (;;) {
if (GetBit()) {
*out++ = *inp++;
} else {
int l, o;
if (GetBit()) {
l = *inp & 7;
o = *(uint16 *)inp >> 3;
inp += 2;
o -= 8192;
if (!l)
l = *inp++;
if (!l)
break;
} else {
l = GetBit() * 2;
l += GetBit();
o = *(inp++) - 256;
}
l += 2;
while (l--) {
*out = *(out + o);
out++;
}
}
}
#undef GetBit
return;
}
// Original name: CLHNM_Desentrelace320
void HnmPlayer::desentrelace320(byte *frame_buffer, byte *final_buffer, uint16 height) {
unsigned int *input = (unsigned int *)frame_buffer;
unsigned int *line0 = (unsigned int *)final_buffer;
unsigned int *line1 = (unsigned int *)(final_buffer + 320);
int count = (height) / 2;
while (count--) {
int16 i;
for (i = 0; i < 320 / 4; i++) {
unsigned int p0 = *input++;
unsigned int p4 = *input++;
#if 0
*line0++ = ((p4 & 0xFF00) >> 8) | ((p4 & 0xFF000000) >> 16) | ((p0 & 0xFF00) << 8) | (p0 & 0xFF000000);
// *line0++ = (p0 & 0xFF000000) | ((p0 & 0xFF00) << 8) | ((p4 & 0xFF000000) >> 16) | ((p4 & 0xFF00) >> 8);
*line1++ = ((p0 & 0xFF0000) << 8) | ((p0 & 0xFF) << 16) | ((p4 & 0xFF0000) >> 8) | (p4 & 0xFF);
#else
*line0++ = (p0 & 0xFF) | ((p0 & 0xFF0000) >> 8) | ((p4 & 0xFF) << 16) | ((p4 & 0xFF0000) << 8);
*line1++ = ((p0 & 0xFF00) >> 8) | ((p0 & 0xFF000000) >> 16) | ((p4 & 0xFF00) << 8) | (p4 & 0xFF000000);
#endif
}
line0 += 320 / 4;
line1 += 320 / 4;
}
}
// Original name: CLHNM_Desentrelace
void HnmPlayer::desentrelace() {
switch (_header._width) {
case 320:
desentrelace320(_newFrameBuffer, _finalBuffer, _header._height);
break;
// case 480:
// CLHNM_Desentrelace480(_newFrameBuffer, finalBuffer, _header._height);
// break;
default:
error("desentrelace - Unexpected width");
}
}
// Original name: CLHNM_DecompUBA
void HnmPlayer::decompUBA(byte *output, byte *curr_buffer, byte *prev_buffer, byte *input, int width, char flags) {
// return;
byte *out_start = output;
byte count;
unsigned int code;
uint16 offs;
byte mode;
byte swap;
if ((flags & 1) == 0) {
//HNM4 classic
int twolinesabove = -(width * 2);
for (;;) {
code = READ_LE_UINT32(input) & 0xFFFFFF; //input++;
count = code & 0x1F;
if (count) {
input += 3;
offs = code >> 9;
//
mode = (code >> 5) & 0xF;
swap = mode >> 3;
byte *ref = ((mode & 1) ? prev_buffer : curr_buffer) + (output - out_start) + (offs * 2) - 32768;
int shft1, shft2;
if (mode & 2) {
// ref += twolinesabove;
shft1 = twolinesabove + 1;
shft2 = 0;
//swap ^= 1;
} else {
shft1 = 0;
shft2 = 1;
}
while (count--) {
byte b0 = ref[shft1];
byte b1 = ref[shft2];
output[swap] = b0;
output[swap ^ 1] = b1;
output += 2;
ref += (mode & 4) ? -2 : 2;
}
} else {
input++;
mode = code & 0xFF; // bits 0..4 are zero
switch (mode) {
case 0:
*(output++) = *(input++);
*(output++) = *(input++);
break;
case 0x20:
output += 2 * *(input++);
break;
case 0x40:
output += 2 * (code >> 8);
input += 2;
break;
case 0x60: {
count = *(input++);
byte color = *(input++);
while (count--) {
*(output++) = color;
*(output++) = color;
}
break;
}
default:
return;
}
}
}
} else {
assert(0);
//HNM4 hires
for (;;) {
code = READ_LE_UINT32(input) & 0xFFFFFF;
input++;
count = code & 0x3F;
if (count) {
mode = (code >> 5) & 0xF;
offs = code >> 9;
//
} else {
mode = code & 0xFF; // bits 0..5 are zero
switch (mode) {
case 0x00:
output += *input++;
break;
case 0x40:
*output++ = *input++;
*(output++ + width) = *input++;
break;
case 0x80:
output += width;
break;
default:
return;
}
}
}
}
}
// Original name: CLHNM_NextElement
bool HnmPlayer::nextElement() {
if (_frameNum == 0) {
resetInternalTimer();
_prevLeft = _prevRight = 0;
}
if (_frameNum == _header._numbFrame)
return false;
if (!loadFrame())
return false;
for (;;) {
int sz = READ_LE_UINT32(_dataPtr) & 0xFFFFFF;
_dataPtr += 4;
int16 id = READ_LE_UINT16(_dataPtr);
_dataPtr += 2;
char h6 = *_dataPtr;
_dataPtr += 1;
char h7 = *_dataPtr;
_dataPtr += 1;
switch (id) {
case MKTAG16('L', 'P'):
changePalette();
_dataPtr += sz - 8;
break;
case MKTAG16('Z', 'I'):
_frameNum++;
selectBuffers();
decompLempelZiv(_dataPtr + 4, _newFrameBuffer);
#if 0
switch (_header._width) {
case 320:
CLBlitter_RawCopy320ASM(_newFrameBuffer, _oldFrameBuffer, _header._height);
break;
case 480:
CLBlitter_RawCopy480ASM(_newFrameBuffer, _oldFrameBuffer, _header._height);
break;
case 640:
CLBlitter_RawCopy640ASM(_newFrameBuffer, _oldFrameBuffer, _header._height);
break;
default:
memcpy(_oldFrameBuffer, _newFrameBuffer, _header._width * _header._height);
}
#else
memcpy(_oldFrameBuffer, _newFrameBuffer, _header._bufferSize); //TODO strange buffer size here
#endif
if (!(h6 & 1))
desentrelace();
else {
// if(_header._width == 640)
// CLBlitter_RawCopy640(_newFrameBuffer, finalBuffer, _header._height);
// else
memcpy(_finalBuffer, _newFrameBuffer, _header._height); //TODO: wrong size?
}
if (!_soundStarted) {
_soundChannel->play();
_soundStarted = true;
}
return true;
case MKTAG16('U', 'I'):
_frameNum++;
selectBuffers();
decompUBA(_newFrameBuffer, _newFrameBuffer, _oldFrameBuffer, _dataPtr, _header._width, h6);
if (!(h6 & 1))
desentrelace();
else {
// if(_header._width == 640)
// CLBlitter_RawCopy640(_newFrameBuffer, _finalBuffer, _header._height);
// else
memcpy(_finalBuffer, _newFrameBuffer, _header._width * _header._height);
}
return true;
case MKTAG16('d', 's'):
case MKTAG16('D', 'S'):
if (_useSound) {
if (!h6) {
int sound_size = sz - 8;
if (!_useAdpcm) {
_soundChannel->queueBuffer(_dataPtr, sound_size - 2, false, _soundStarted);
} else {
#if 0
// Not used in Lost Eden
int16 *sound_buffer = (int16 *)_soundGroup->getNextBuffer();
if (!_pendingSounds) {
const int kDecompTableSize = 256 * sizeof(int16);
loadDecompTable((int16 *)_dataPtr);
decompADPCM(_dataPtr + kDecompTableSize, sound_buffer, sound_size - kDecompTableSize);
_soundGroup->assignDatas(sound_buffer, (sound_size - kDecompTableSize) * 2, false);
} else {
decompADPCM(_dataPtr, sound_buffer, sound_size);
_soundGroup->assignDatas(sound_buffer, sound_size * 2, false);
}
_pendingSounds++;
if (_soundStarted)
_soundGroup->playNextSample(_soundChannel);
#endif
}
} else
error("nextElement - unexpected flag");
}
_dataPtr += sz - 8;
break;
default:
if (_customChunkHandler)
_customChunkHandler(_dataPtr, sz - 8, id, h6, h7);
_dataPtr += sz - 8;
}
}
return true;
}
// Original name: CLHNM_GetSoundChannel
CSoundChannel *HnmPlayer::getSoundChannel() {
return _soundChannel;
}
// Original name: CLHNM_ChangePalette
void HnmPlayer::changePalette() {
CLPalette_GetLastPalette(_palette);
byte *pal = _dataPtr;
if (*(uint16 *)pal == 0xFFFF)
return;
int16 mincolor = 255;
int16 maxcolor = 0;
do {
uint16 fst = *pal++;
uint16 cnt = *pal++;
if (cnt == 0)
cnt = 256;
debug("hnm: setting palette, fst = %d, cnt = %d, last = %d", fst, cnt, fst + cnt - 1);
assert(fst + cnt <= 256);
if (mincolor > fst)
mincolor = fst;
if (maxcolor < fst + cnt)
maxcolor = fst + cnt;
color_t *color = _palette + fst;
if (_safePalette) {
while (cnt--) {
byte r = *pal++;
byte g = *pal++;
byte b = *pal++;
int16 rr = r << 10;
int16 gg = g << 10;
int16 bb = b << 10;
if (color->r != rr || color->g != gg || color->b != bb)
CLBlitter_OneBlackFlash();
color->r = rr;
color->g = gg;
color->b = bb;
color++;
}
} else {
while (cnt--) {
byte r = *pal++;
byte g = *pal++;
byte b = *pal++;
color->r = r << 10;
color->g = g << 10;
color->b = b << 10;
color++;
}
}
} while (*(uint16 *)pal != 0xFFFF);
#if 0
if (preserve_color0) {
_palette[0].r = 0;
_palette[0].g = 0;
_palette[0].b = 0;
}
#endif
// CLBlitter_Send2ScreenNextCopy(_palette, mincolor, maxcolor - mincolor);
CLBlitter_Send2ScreenNextCopy(_palette, 0, 256);
}
// Original name: CLHNM_SelectBuffers
void HnmPlayer::selectBuffers() {
if (_frameNum % 2) {
_newFrameBuffer = _tmpBuffer[1];
_oldFrameBuffer = _tmpBuffer[0];
} else {
_newFrameBuffer = _tmpBuffer[0];
_oldFrameBuffer = _tmpBuffer[1];
}
}
} // namespace Cryo