diff options
Diffstat (limited to 'engines/kyra/graphics/wsamovie.cpp')
-rw-r--r-- | engines/kyra/graphics/wsamovie.cpp | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/engines/kyra/graphics/wsamovie.cpp b/engines/kyra/graphics/wsamovie.cpp new file mode 100644 index 0000000000..6cbda7b6c9 --- /dev/null +++ b/engines/kyra/graphics/wsamovie.cpp @@ -0,0 +1,463 @@ +/* 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 "kyra/graphics/wsamovie.h" +#include "kyra/resource/resource.h" + +#include "common/endian.h" + +namespace Kyra { + +WSAMovie_v1::WSAMovie_v1(KyraEngine_v1 *vm) + : Movie(vm), _frameData(0), _frameOffsTable(0), _offscreenBuffer(0), _deltaBuffer(0) { +} + +WSAMovie_v1::~WSAMovie_v1() { + close(); +} + +int WSAMovie_v1::open(const char *filename, int offscreenDecode, Palette *palBuf) { + close(); + + uint32 flags = 0; + uint32 fileSize; + uint8 *p = _vm->resource()->fileData(filename, &fileSize); + if (!p) + return 0; + + const uint8 *wsaData = p; + _numFrames = READ_LE_UINT16(wsaData); wsaData += 2; + _width = READ_LE_UINT16(wsaData); wsaData += 2; + _height = READ_LE_UINT16(wsaData); wsaData += 2; + _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2; + _offscreenBuffer = NULL; + _flags = 0; + if (_vm->gameFlags().useAltShapeHeader) { + flags = READ_LE_UINT16(wsaData); + wsaData += 2; + } + + uint32 offsPal = 0; + if (flags & 1) { + offsPal = 0x300; + _flags |= WF_HAS_PALETTE; + if (palBuf) + _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300); + } + + if (offscreenDecode) { + _flags |= WF_OFFSCREEN_DECODE; + const int offscreenBufferSize = _width * _height; + _offscreenBuffer = new uint8[offscreenBufferSize]; + memset(_offscreenBuffer, 0, offscreenBufferSize); + } + + if (_numFrames & 0x8000) { + // This is used in the Amiga version. + if (_vm->gameFlags().platform != Common::kPlatformAmiga) + warning("Unhandled wsa flags 0x8000"); + _flags |= WF_FLIPPED; + _numFrames &= 0x7FFF; + } + _currentFrame = _numFrames; + + _deltaBuffer = new uint8[_deltaBufferSize]; + memset(_deltaBuffer, 0, _deltaBufferSize); + + // read frame offsets + _frameOffsTable = new uint32[_numFrames + 2]; + _frameOffsTable[0] = 0; + uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4; + bool firstFrame = true; + + if (frameDataOffs == 0) { + firstFrame = false; + frameDataOffs = READ_LE_UINT32(wsaData); + _flags |= WF_NO_FIRST_FRAME; + } + + for (int i = 1; i < _numFrames + 2; ++i) { + _frameOffsTable[i] = READ_LE_UINT32(wsaData); + if (_frameOffsTable[i]) + _frameOffsTable[i] -= frameDataOffs; + wsaData += 4; + } + + if (!_frameOffsTable[_numFrames + 1]) + _flags |= WF_NO_LAST_FRAME; + + // skip palette + wsaData += offsPal; + + // read frame data + const int frameDataSize = p + fileSize - wsaData; + _frameData = new uint8[frameDataSize]; + memcpy(_frameData, wsaData, frameDataSize); + + // decode first frame + if (firstFrame) + Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize); + + delete[] p; + _opened = true; + + return _numFrames; +} + +void WSAMovie_v1::close() { + if (_opened) { + delete[] _deltaBuffer; + delete[] _offscreenBuffer; + delete[] _frameOffsTable; + delete[] _frameData; + _opened = false; + } +} + +void WSAMovie_v1::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) { + if (frameNum >= _numFrames || frameNum < 0 || !_opened) + return; + + _x = x; + _y = y; + _drawPage = pageNum; + + uint8 *dst = 0; + if (_flags & WF_OFFSCREEN_DECODE) + dst = _offscreenBuffer; + else + dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height); + + if (_currentFrame == _numFrames) { + if (!(_flags & WF_NO_FIRST_FRAME)) { + if (_flags & WF_OFFSCREEN_DECODE) + Screen::decodeFrameDelta(dst, _deltaBuffer); + else + Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, (_flags & WF_XOR) == 0); + } + _currentFrame = 0; + } + + // try to reduce the number of needed frame operations + int diffCount = ABS(_currentFrame - frameNum); + int frameStep = 1; + int frameCount; + if (_currentFrame < frameNum) { + frameCount = _numFrames - frameNum + _currentFrame; + if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME)) + frameStep = -1; + else + frameCount = diffCount; + } else { + frameCount = _numFrames - _currentFrame + frameNum; + if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) { + frameStep = -1; + frameCount = diffCount; + } + } + + // process + if (frameStep > 0) { + uint16 cf = _currentFrame; + while (frameCount--) { + cf += frameStep; + processFrame(cf, dst); + if (cf == _numFrames) + cf = 0; + } + } else { + uint16 cf = _currentFrame; + while (frameCount--) { + if (cf == 0) + cf = _numFrames; + processFrame(cf, dst); + cf += frameStep; + } + } + + // display + _currentFrame = frameNum; + if (_flags & WF_OFFSCREEN_DECODE) { + int pageBackUp = _screen->setCurPage(_drawPage); + + int plotFunc = (flags & 0xFF00) >> 12; + int unk1 = flags & 0xFF; + + _screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2); + + _screen->_curPage = pageBackUp; + } +} + +void WSAMovie_v1::processFrame(int frameNum, uint8 *dst) { + if (!_opened) + return; + assert(frameNum <= _numFrames); + const uint8 *src = _frameData + _frameOffsTable[frameNum]; + Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize); + if (_flags & WF_OFFSCREEN_DECODE) + Screen::decodeFrameDelta(dst, _deltaBuffer); + else + Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, false); +} + +#pragma mark - + +WSAMovieAmiga::WSAMovieAmiga(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _buffer(0) {} + +int WSAMovieAmiga::open(const char *filename, int offscreenDecode, Palette *palBuf) { + int res = WSAMovie_v1::open(filename, offscreenDecode, palBuf); + + if (!res) + return 0; + + _buffer = new uint8[_width * _height]; + assert(_buffer); + return res; +} + +void WSAMovieAmiga::close() { + if (_opened) { + delete[] _buffer; + _buffer = 0; + } + WSAMovie_v1::close(); +} + +void WSAMovieAmiga::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) { + if (frameNum >= _numFrames || frameNum < 0 || !_opened) + return; + + _x = x; + _y = y; + _drawPage = pageNum; + + uint8 *dst; + dst = _buffer; + memset(_buffer, 0, _width * _height); + + if (_currentFrame == _numFrames) { + if (!(_flags & WF_NO_FIRST_FRAME)) { + Screen::decodeFrameDelta(dst, _deltaBuffer, true); + Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0); + + if (_flags & WF_OFFSCREEN_DECODE) { + dst = _offscreenBuffer; + const uint8 *src = _buffer; + int size = _width * _height; + + for (int i = 0; i < size; ++i) + *dst++ ^= *src++; + + dst = _buffer; + } else { + _screen->copyBlockToPage(_drawPage, _x, _y, _width, _height, _buffer); + } + } + _currentFrame = 0; + } + + // try to reduce the number of needed frame operations + int diffCount = ABS(_currentFrame - frameNum); + int frameStep = 1; + int frameCount; + if (_currentFrame < frameNum) { + frameCount = _numFrames - frameNum + _currentFrame; + if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME)) + frameStep = -1; + else + frameCount = diffCount; + } else { + frameCount = _numFrames - _currentFrame + frameNum; + if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) { + frameStep = -1; + frameCount = diffCount; + } + } + + // process + if (frameStep > 0) { + uint16 cf = _currentFrame; + while (frameCount--) { + cf += frameStep; + processFrame(cf, dst); + if (cf == _numFrames) + cf = 0; + } + } else { + uint16 cf = _currentFrame; + while (frameCount--) { + if (cf == 0) + cf = _numFrames; + processFrame(cf, dst); + cf += frameStep; + } + } + + // display + _currentFrame = frameNum; + if (_flags & WF_OFFSCREEN_DECODE) { + int pageBackUp = _screen->setCurPage(_drawPage); + + int plotFunc = (flags & 0xFF00) >> 12; + int unk1 = flags & 0xFF; + + _screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2); + + _screen->_curPage = pageBackUp; + } +} + +void WSAMovieAmiga::processFrame(int frameNum, uint8 *dst) { + if (!_opened) + return; + assert(frameNum <= _numFrames); + + memset(dst, 0, _width * _height); + + const uint8 *src = _frameData + _frameOffsTable[frameNum]; + Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize); + Screen::decodeFrameDelta(dst, _deltaBuffer, true); + Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0); + + src = dst; + dst = 0; + int dstPitch = 0; + if (_flags & WF_OFFSCREEN_DECODE) { + dst = _offscreenBuffer; + dstPitch = _width; + } else { + dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height); + dstPitch = Screen::SCREEN_W; + } + + for (int y = 0; y < _height; ++y) { + for (int x = 0; x < _width; ++x) + *dst++ ^= *src++; + dst += dstPitch - _width; + } +} + +#pragma mark - + +WSAMovie_v2::WSAMovie_v2(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _xAdd(0), _yAdd(0) {} + +int WSAMovie_v2::open(const char *filename, int unk1, Palette *palBuf) { + close(); + + uint32 flags = 0; + uint32 fileSize; + uint8 *p = _vm->resource()->fileData(filename, &fileSize); + if (!p) { + warning("couldn't load wsa file: '%s'", filename); + return 0; + } + + const uint8 *wsaData = p; + _numFrames = READ_LE_UINT16(wsaData); wsaData += 2; + _xAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2; + _yAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2; + _width = READ_LE_UINT16(wsaData); wsaData += 2; + _height = READ_LE_UINT16(wsaData); wsaData += 2; + _deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2; + _offscreenBuffer = NULL; + _flags = 0; + flags = READ_LE_UINT16(wsaData); wsaData += 2; + + uint32 offsPal = 0; + if (flags & 1) { + offsPal = 0x300; + _flags |= WF_HAS_PALETTE; + if (palBuf) + _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300); + } + + if (flags & 2) { + if (_vm->gameFlags().use16ColorMode) { + offsPal = 0x30; + _flags |= WF_HAS_PALETTE; + if (palBuf) + _screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x30); + } + + _flags |= WF_XOR; + } + + + if (!(unk1 & 2)) { + _flags |= WF_OFFSCREEN_DECODE; + const int offscreenBufferSize = _width * _height; + _offscreenBuffer = new uint8[offscreenBufferSize]; + memset(_offscreenBuffer, 0, offscreenBufferSize); + } + + if (_numFrames & 0x8000) { + warning("Unhandled wsa flags 0x80"); + _flags |= 0x80; + _numFrames &= 0x7FFF; + } + _currentFrame = _numFrames; + + _deltaBuffer = new uint8[_deltaBufferSize]; + memset(_deltaBuffer, 0, _deltaBufferSize); + + // read frame offsets + _frameOffsTable = new uint32[_numFrames + 2]; + _frameOffsTable[0] = 0; + uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4; + bool firstFrame = true; + if (frameDataOffs == 0) { + firstFrame = false; + frameDataOffs = READ_LE_UINT32(wsaData); + _flags |= WF_NO_FIRST_FRAME; + } + + for (int i = 1; i < _numFrames + 2; ++i) { + _frameOffsTable[i] = READ_LE_UINT32(wsaData); + if (_frameOffsTable[i]) + _frameOffsTable[i] -= frameDataOffs; + wsaData += 4; + } + + if (!_frameOffsTable[_numFrames + 1]) + _flags |= WF_NO_LAST_FRAME; + + // skip palette + wsaData += offsPal; + + // read frame data + const int frameDataSize = p + fileSize - wsaData; + + _frameData = new uint8[frameDataSize]; + memcpy(_frameData, wsaData, frameDataSize); + + // decode first frame + if (firstFrame) + Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize); + + delete[] p; + _opened = true; + + return _numFrames; +} + +} // End of namespace Kyra |