mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
618 lines
15 KiB
C++
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
|
|
|