220 lines
6.1 KiB
C++
220 lines
6.1 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 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 "watchmaker/3d/movie.h"
|
|
#include "watchmaker/3d/dds_header.h"
|
|
#include "watchmaker/file_utils.h"
|
|
#include "watchmaker/windows_hacks.h"
|
|
#include "watchmaker/work_dirs.h"
|
|
|
|
namespace Watchmaker {
|
|
|
|
gMovie::gMovie(Common::SharedPtr<Common::SeekableReadStream> stream, Texture *texture, const Common::String &name) : _name(name), _stream(stream), _texture(texture) {
|
|
_numFrames = stream->readUint16LE();
|
|
_width = stream->readUint16LE();
|
|
_height = stream->readUint16LE();
|
|
_keyFrame = stream->readByte();
|
|
_frameRate = stream->readByte();
|
|
|
|
_header = DDSHeader(*stream);
|
|
_numBlocks = _width * _height / 16;
|
|
_curFrame = 0xFFFF;
|
|
|
|
_frameOffsets = new uint32[_numFrames] {};
|
|
if (!_frameOffsets) {
|
|
error("gLoadMovie FAILED: Can't alloc Movie->frameOffsets struct");
|
|
}
|
|
|
|
_buffer = new uint8[bufferSize()] {};
|
|
_surfaceBuffer = new uint8[_header.dataSize()] {};
|
|
_frameStream = new Common::MemoryReadStream(_surfaceBuffer, _header.dataSize(), DisposeAfterUse::NO);
|
|
if (!_buffer) {
|
|
error("gLoadMovie FAILED: Can't alloc Movie->buffer struct");
|
|
}
|
|
|
|
//read frame offsets
|
|
for (int i = 0; i < _numFrames; i++) {
|
|
_frameOffsets[i] = _stream->readUint32LE();
|
|
}
|
|
|
|
_startTime = 0;
|
|
}
|
|
|
|
gMovie::~gMovie() {
|
|
delete[] _frameOffsets;
|
|
delete[] _buffer;
|
|
delete[] _surfaceBuffer;
|
|
delete _frameStream;
|
|
}
|
|
|
|
Common::SharedPtr<gMovie> gLoadMovie(WorkDirs &workDirs, const char *TextName, Texture *texture) {
|
|
//convert .avi name in .wmm
|
|
Common::String finalName = replaceExtension(TextName, "wmm");
|
|
|
|
auto stream = workDirs.resolveFile(finalName);
|
|
if (!stream) {
|
|
DebugLogFile("gLoadMovie FAILED: Can't find movie file\n");
|
|
return nullptr;
|
|
}
|
|
auto Movie = Common::SharedPtr<gMovie>(new gMovie(stream, texture, TextName));
|
|
Movie->_name = TextName;
|
|
if (!Movie) {
|
|
DebugLogFile("gLoadMovie FAILED: Can't alloc Movie struct");
|
|
return nullptr;
|
|
}
|
|
|
|
// Movie->frameRate=240;
|
|
return Movie;
|
|
}
|
|
|
|
void gMovie::loadThisFrameData(uint16 frame) {
|
|
_stream->seek(_frameOffsets[frame], SEEK_SET);
|
|
//read frame data
|
|
int32 size = 0;
|
|
if ((frame + 1) == _numFrames) {
|
|
size = _stream->size() - _frameOffsets[frame];
|
|
} else {
|
|
size = _frameOffsets[frame + 1] - _frameOffsets[frame];
|
|
}
|
|
assert(size <= (int32)bufferSize());
|
|
_stream->read(_buffer, size);
|
|
}
|
|
|
|
//build a new frame by difference from previous
|
|
void gMovie::buildNewFrame(byte *surf, uint16 frame) {
|
|
loadThisFrameData(frame);
|
|
|
|
DWORD bitArraySize = _numBlocks >> 3;
|
|
byte *buf = &_buffer[bitArraySize];
|
|
WORD curBlock = 0;
|
|
|
|
for (int i = 0; i < bitArraySize; i++) {
|
|
byte block = _buffer[i];
|
|
if (!block) {
|
|
curBlock += 8;
|
|
continue; //everything is equal
|
|
}
|
|
|
|
for (int j = 0; j < 8; j++, curBlock++) {
|
|
if (block & (1 << j)) {
|
|
memcpy(&surf[curBlock << 3], buf, 8);
|
|
buf += 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool gMovie::setFrame(uint16 newFrame) {
|
|
warning("Set Frame: %d\t%s", newFrame, _name.c_str());
|
|
if (_curFrame == newFrame)
|
|
return true;
|
|
|
|
//do we have to replace the whole frame or do we have to built it?
|
|
bool rebuildFrame = true;
|
|
if (_curFrame == 0xFFFF) rebuildFrame = false;
|
|
else if (!(newFrame % _keyFrame)) rebuildFrame = false; //it's a keyframe
|
|
|
|
_header.dataSize();
|
|
#if 0
|
|
DDSURFACEDESC2 ddsd2;
|
|
ddsd2.dwSize = sizeof(DDSURFACEDESC2);
|
|
|
|
if ((mv->surf->Lock(NULL, &ddsd2, DDLOCK_NOSYSLOCK | DDLOCK_WAIT, NULL))) { // Lock and fill with the dds
|
|
DebugLogFile("gMovie_SetFrame: Can't lock surface DDS");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
if (!rebuildFrame) {
|
|
loadThisFrameData(newFrame);
|
|
memcpy(_surfaceBuffer, _buffer, _header.dataSize());
|
|
} else {
|
|
if ((_curFrame + 1) != newFrame) { //we can't directly build this frame because the current frame is not his previous
|
|
uint16 startFrame;
|
|
uint16 prevKey = (newFrame / _keyFrame) * _keyFrame;
|
|
|
|
if ((_curFrame > newFrame) || (_curFrame < prevKey)) {
|
|
loadThisFrameData(prevKey);
|
|
memcpy(_surfaceBuffer, _buffer, _header.dataSize());
|
|
startFrame = prevKey + 1;
|
|
} else startFrame = _curFrame + 1;
|
|
for (uint16 i = startFrame; i < newFrame; i++) {
|
|
buildNewFrame(_surfaceBuffer, i);
|
|
}
|
|
}
|
|
buildNewFrame(_surfaceBuffer, newFrame);
|
|
}
|
|
|
|
_frameStream->seek(0, SEEK_SET);
|
|
auto tex = loadDdsTexture(*_frameStream, _header);
|
|
_texture->assignData(*tex);
|
|
#if 0
|
|
if (mat->Texture->lpDDSurface->Blt(NULL, mv->surf, NULL, DDBLT_WAIT, NULL) != DD_OK) {
|
|
DebugLogFile("gMovie_SetFrame: Can't Blit DDS texture");
|
|
return NULL;
|
|
}
|
|
#endif
|
|
_curFrame = newFrame;
|
|
|
|
return true;
|
|
}
|
|
|
|
//*********************************************************************************************
|
|
bool gMovie::updateMovie() {
|
|
int16 newFrame = 0;
|
|
|
|
if (_paused)
|
|
return TRUE;
|
|
|
|
warning("Update Movie: %s", _name.c_str());
|
|
|
|
if ((_curFrame == 0xFFFF) || (!_startTime)) {
|
|
_startTime = timeGetTime();
|
|
newFrame = 0;
|
|
} else {
|
|
// Use the time to find which frame we should be drawing
|
|
uint32 curTime = timeGetTime();
|
|
DWORD elapsedTime = curTime - _startTime;
|
|
newFrame = (WORD)((float)elapsedTime / (1000.f / (float)_frameRate));
|
|
|
|
if (newFrame >= _numFrames) {
|
|
_startTime = curTime;
|
|
newFrame = 0;
|
|
}
|
|
}
|
|
|
|
return setFrame(newFrame);
|
|
}
|
|
|
|
int gMovie::frameSize(int index) {
|
|
if ((index + 1) < _numFrames) {
|
|
return _frameOffsets[index + 1] - _frameOffsets[index];
|
|
} else {
|
|
return _stream->size() - _frameOffsets[index];
|
|
}
|
|
}
|
|
|
|
uint32 gMovie::bufferSize() const {
|
|
return (_numBlocks / 8) + 8 * _numBlocks; //bit array + max different blocks
|
|
}
|
|
|
|
|
|
} // End of namespace Watchmaker
|