/engines/made/pmvplayer.cpp
C++ | 279 lines | 182 code | 61 blank | 36 comment | 40 complexity | 83f176c7d851845ef07b817e3914ba09 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
- /* 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 "made/pmvplayer.h"
- #include "made/made.h"
- #include "made/screen.h"
- #include "made/graphics.h"
- #include "common/file.h"
- #include "common/debug.h"
- #include "common/system.h"
- #include "common/events.h"
- #include "audio/decoders/raw.h"
- #include "audio/audiostream.h"
- #include "graphics/surface.h"
- namespace Made {
- PmvPlayer::PmvPlayer(MadeEngine *vm, Audio::Mixer *mixer) : _fd(nullptr), _vm(vm), _mixer(mixer) {
- _audioStream = nullptr;
- _surface = nullptr;
- _aborted = false;
- }
- PmvPlayer::~PmvPlayer() {
- }
- bool PmvPlayer::play(const char *filename) {
- _aborted = false;
- _surface = nullptr;
- _fd = new Common::File();
- if (!_fd->open(filename)) {
- delete _fd;
- return false;
- }
- uint32 chunkType, chunkSize, prevChunkSize = 0;
- readChunk(chunkType, chunkSize); // "MOVE"
- if (chunkType != MKTAG('M','O','V','E')) {
- warning("Unexpected PMV video header, expected 'MOVE'");
- delete _fd;
- return false;
- }
- readChunk(chunkType, chunkSize); // "MHED"
- if (chunkType != MKTAG('M','H','E','D')) {
- warning("Unexpected PMV video header, expected 'MHED'");
- delete _fd;
- return false;
- }
- uint frameDelay = _fd->readUint16LE();
- _fd->skip(4); // always 0?
- uint frameCount = _fd->readUint16LE();
- _fd->skip(4); // always 0?
- uint soundFreq = _fd->readUint16LE();
- // Note: There seem to be weird sound frequencies in PMV videos.
- // Not sure why, but leaving those original frequencies intact
- // results to sound being choppy. Therefore, we set them to more
- // "common" values here (11025 instead of 11127 and 22050 instead
- // of 22254)
- if (soundFreq == 11127)
- soundFreq = 11025;
- if (soundFreq == 22254)
- soundFreq = 22050;
- for (int i = 0; i < 22; i++) {
- int unk = _fd->readUint16LE();
- debug(2, "%i ", unk);
- }
- _mixer->stopAll();
- // Read palette
- _fd->read(_paletteRGB, 768);
- _vm->_screen->setRGBPalette(_paletteRGB);
- uint32 frameNumber = 0;
- uint16 chunkCount = 0;
- uint32 soundSize = 0;
- uint32 soundChunkOfs = 0, palChunkOfs = 0;
- uint32 palSize = 0;
- byte *frameData = 0, *audioData, *soundData, *palData, *imageData;
- bool firstTime = true;
- uint32 soundStartTime = 0, skipFrames = 0;
- uint32 bytesRead;
- uint16 width, height, cmdOffs, pixelOffs, maskOffs, lineSize;
- // TODO: Sound can still be a little choppy. A bug in the decoder or -
- // perhaps more likely - do we have to implement double buffering to
- // get it to work well?
- _audioStream = Audio::makeQueuingAudioStream(soundFreq, false);
- SoundDecoderData *soundDecoderData = new SoundDecoderData();
- while (!_vm->shouldQuit() && !_aborted && !_fd->eos() && frameNumber < frameCount) {
- int32 frameTime = _vm->_system->getMillis();
- readChunk(chunkType, chunkSize);
- if (chunkType != MKTAG('M','F','R','M')) {
- warning("Unknown chunk type");
- }
- // Only reallocate the frame data buffer if its size has changed
- if (prevChunkSize != chunkSize || !frameData) {
- delete[] frameData;
- frameData = new byte[chunkSize];
- }
- prevChunkSize = chunkSize;
- bytesRead = _fd->read(frameData, chunkSize);
- if (bytesRead < chunkSize || _fd->eos())
- break;
- soundChunkOfs = READ_LE_UINT32(frameData + 8);
- palChunkOfs = READ_LE_UINT32(frameData + 16);
- // Handle audio
- if (soundChunkOfs) {
- audioData = frameData + soundChunkOfs - 8;
- chunkSize = READ_LE_UINT16(audioData + 4);
- chunkCount = READ_LE_UINT16(audioData + 6);
- debug(1, "chunkCount = %d; chunkSize = %d; total = %d\n", chunkCount, chunkSize, chunkCount * chunkSize);
- soundSize = chunkCount * chunkSize;
- soundData = (byte *)malloc(soundSize);
- decompressSound(audioData + 8, soundData, chunkSize, chunkCount, NULL, soundDecoderData);
- _audioStream->queueBuffer(soundData, soundSize, DisposeAfterUse::YES, Audio::FLAG_UNSIGNED);
- }
- // Handle palette
- if (palChunkOfs) {
- palData = frameData + palChunkOfs - 8;
- palSize = READ_LE_UINT32(palData + 4);
- decompressPalette(palData + 8, _paletteRGB, palSize);
- _vm->_screen->setRGBPalette(_paletteRGB);
- }
- // Handle video
- imageData = frameData + READ_LE_UINT32(frameData + 12) - 8;
- // frameNum @0
- width = READ_LE_UINT16(imageData + 8);
- height = READ_LE_UINT16(imageData + 10);
- cmdOffs = READ_LE_UINT16(imageData + 12);
- pixelOffs = READ_LE_UINT16(imageData + 16);
- maskOffs = READ_LE_UINT16(imageData + 20);
- lineSize = READ_LE_UINT16(imageData + 24);
- debug(2, "width = %d; height = %d; cmdOffs = %04X; pixelOffs = %04X; maskOffs = %04X; lineSize = %d\n",
- width, height, cmdOffs, pixelOffs, maskOffs, lineSize);
- if (!_surface) {
- _surface = new Graphics::Surface();
- _surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
- }
- decompressMovieImage(imageData, *_surface, cmdOffs, pixelOffs, maskOffs, lineSize);
- if (firstTime) {
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle, _audioStream);
- soundStartTime = g_system->getMillis();
- skipFrames = 0;
- firstTime = false;
- }
- handleEvents();
- updateScreen();
- if (skipFrames == 0) {
- int32 waitTime = (frameNumber * frameDelay) -
- (g_system->getMillis() - soundStartTime) - (_vm->_system->getMillis() - frameTime);
- if (waitTime < 0) {
- skipFrames = -waitTime / frameDelay;
- warning("Video A/V sync broken, skipping %d frame(s)", skipFrames + 1);
- } else if (waitTime > 0)
- g_system->delayMillis(waitTime);
- } else
- skipFrames--;
- frameNumber++;
- }
- delete soundDecoderData;
- delete[] frameData;
- _audioStream->finish();
- _mixer->stopHandle(_audioStreamHandle);
- //delete _audioStream;
- delete _fd;
- if(_surface)
- _surface->free();
- delete _surface;
- return !_aborted;
- }
- void PmvPlayer::readChunk(uint32 &chunkType, uint32 &chunkSize) {
- chunkType = _fd->readUint32BE();
- chunkSize = _fd->readUint32LE();
- debug(2, "ofs = %08X; chunkType = %c%c%c%c; chunkSize = %d\n",
- _fd->pos(),
- (chunkType >> 24) & 0xFF, (chunkType >> 16) & 0xFF, (chunkType >> 8) & 0xFF, chunkType & 0xFF,
- chunkSize);
- }
- void PmvPlayer::handleEvents() {
- Common::Event event;
- while (_vm->_system->getEventManager()->pollEvent(event)) {
- switch (event.type) {
- case Common::EVENT_KEYDOWN:
- if (event.kbd.keycode == Common::KEYCODE_ESCAPE)
- _aborted = true;
- break;
- default:
- break;
- }
- }
- }
- void PmvPlayer::updateScreen() {
- _vm->_system->copyRectToScreen(_surface->getPixels(), _surface->pitch,
- (320 - _surface->w) / 2, (200 - _surface->h) / 2, _surface->w, _surface->h);
- _vm->_system->updateScreen();
- }
- void PmvPlayer::decompressPalette(byte *palData, byte *outPal, uint32 palDataSize) {
- byte *palDataEnd = palData + palDataSize;
- while (palData < palDataEnd) {
- byte count = *palData++;
- byte entry = *palData++;
- if (count == 255 && entry == 255)
- break;
- memcpy(&outPal[entry * 3], palData, (count + 1) * 3);
- palData += (count + 1) * 3;
- }
- }
- }