aboutsummaryrefslogtreecommitdiff
path: root/engines/groovie/roq.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/groovie/roq.cpp')
-rw-r--r--engines/groovie/roq.cpp403
1 files changed, 403 insertions, 0 deletions
diff --git a/engines/groovie/roq.cpp b/engines/groovie/roq.cpp
new file mode 100644
index 0000000000..589251da31
--- /dev/null
+++ b/engines/groovie/roq.cpp
@@ -0,0 +1,403 @@
+/* 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.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "groovie/groovie.h"
+#include "groovie/roq.h"
+
+#include "sound/mixer.h"
+
+namespace Groovie {
+
+ROQPlayer::ROQPlayer(GroovieEngine *vm) :
+ VideoPlayer(vm) {
+}
+
+ROQPlayer::~ROQPlayer() {
+}
+
+uint16 ROQPlayer::loadInternal() {
+ // Begin reading the file
+ debugC(1, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Loading video");
+
+ // Read the file header
+ ROQBlockHeader blockHeader;
+ if (!readBlockHeader(blockHeader)) {
+ return 0;
+ }
+ if (blockHeader.type != 0x1084 || blockHeader.size != 0 || blockHeader.param != 0) {
+ return 0;
+ }
+
+ // Hardcoded FPS
+ return 25;
+}
+
+bool ROQPlayer::playFrameInternal() {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Playing frame");
+
+ // Process the needed blocks until the next video frame
+ bool endframe = false;
+ while (!endframe && !_file->eos()) {
+ endframe = processBlock();
+ }
+
+ // Wait until the current frame can be shown
+ waitFrame();
+
+ // Update the screen
+ _syst->updateScreen();
+
+ // Return whether the video has ended
+ return _file->eos();
+}
+
+bool ROQPlayer::readBlockHeader(ROQBlockHeader &blockHeader) {
+ if (_file->eos()) {
+ return false;
+ } else {
+ blockHeader.type = _file->readUint16LE();
+ blockHeader.size = _file->readUint32LE();
+ blockHeader.param = _file->readUint16LE();
+
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block type = 0x%02X", blockHeader.type);
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block size = 0x%08X", blockHeader.size);
+ debugC(10, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Block param = 0x%04X", blockHeader.param);
+
+ return true;
+ }
+}
+
+bool ROQPlayer::processBlock() {
+ // Read the header of the block
+ ROQBlockHeader blockHeader;
+ if (!readBlockHeader(blockHeader)) {
+ return true;
+ }
+
+ bool ok = true;
+ bool endframe = false;
+ switch (blockHeader.type) {
+ case 0x1001: // Video info
+ ok = processBlockInfo(blockHeader);
+ break;
+ case 0x1002: // Quad codebook definition
+ ok = processBlockQuadCodebook(blockHeader);
+ break;
+ case 0x1011: // Quad vector quantised video frame
+ ok = processBlockQuadVector(blockHeader);
+ endframe = true;
+ break;
+ case 0x1012: // Still image (JPEG)
+ ok = processBlockStill(blockHeader);
+ endframe = true;
+ break;
+ case 0x1013: // Hang
+ //warning("Groovie::ROQ: Hang block (skipped)");
+ break;
+ case 0x1020: // Mono sound samples
+ ok = processBlockSoundMono(blockHeader);
+ break;
+ case 0x1021: // Stereo sound samples
+ ok = processBlockSoundStereo(blockHeader);
+ break;
+ case 0x1030: // Audio container
+ ok = processBlockAudioContainer(blockHeader);
+ break;
+ default:
+ error("Groovie::ROQ: Unknown block type: 0x%04X", blockHeader.type);
+ ok = false;
+ }
+
+ // End the frame when the graphics have been modified or when there's an error
+ return endframe || !ok;
+}
+
+bool ROQPlayer::processBlockInfo(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing info block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1001 || blockHeader.size != 8 || blockHeader.param != 0) {
+ return false;
+ }
+
+ uint16 tmp;
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "w = %d\n", tmp);
+ if (tmp != 640) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "h = %d\n", tmp);
+ if (tmp != 320) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk1 = %d\n", tmp);
+ if (tmp != 8) {
+ return false;
+ }
+ tmp = _file->readUint16LE();
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "unk2 = %d\n", tmp);
+ if (tmp != 4) {
+ return false;
+ }
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadCodebook(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad codebook block");
+
+ // Get the number of 2x2 pixel blocks
+ _num2blocks = blockHeader.param >> 8;
+ if (_num2blocks == 0) {
+ _num2blocks = 256;
+ }
+
+ // Get the number of 4x4 pixel blocks
+ _num4blocks = blockHeader.param & 0xFF;
+ if (_num4blocks == 0 && (blockHeader.size > (uint32)_num2blocks * 6)) {
+ _num4blocks = 256;
+ }
+
+ _file->skip(_num2blocks * 6);
+ _file->skip(_num4blocks * 4);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadVector(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector block");
+ _file->skip(blockHeader.size);
+ return true;
+
+ // Get the mean motion vectors
+ //byte Mx = blockHeader.param >> 8;
+ //byte My = blockHeader.param & 0xFF;
+
+ int32 ends =_file->pos() + blockHeader.size;
+ int numblocks = (640 / 8) * (320 / 8);
+ for (int j = 0; j < numblocks && ends > _file->pos(); j++) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "doing block %d/%d\n", j, numblocks);
+ uint16 codingType = _file->readUint16LE();
+ for (int i = 0; i < 8; i++) {
+ switch (codingType >> 14) {
+ case 0: // MOT: Skip block
+ //printf("coding type 0\n");
+ break;
+ case 1: { // FCC: Copy an existing block
+ //printf("coding type 1\n");
+ byte argument;
+ argument = _file->readByte();
+ //byte Dx = Mx + (argument >> 4);
+ //byte Dy = My + (argument & 0x0F);
+ // Dx = X + 8 - (argument >> 4) - Mx
+ // Dy = Y + 8 - (argument & 0x0F) - My
+ break;
+ }
+ case 2: { // SLD: Quad vector quantisation
+ //printf("coding type 2\n");
+ byte argument = _file->readByte();
+ if (argument > _num4blocks) {
+ //error("invalid 4x4 block %d of %d", argument, _num4blocks);
+ }
+ // Upsample the 4x4 pixel block
+ break;
+ }
+ case 3: // CCC:
+ //printf("coding type 3:\n");
+ processBlockQuadVectorSub(blockHeader);
+ break;
+ }
+ codingType <<= 2;
+ }
+ }
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Should have ended at %d, and has ended at %d\n", ends, _file->pos());
+ return true;
+}
+
+bool ROQPlayer::processBlockQuadVectorSub(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing quad vector sub block");
+
+ // Get the mean motion vectors
+ //byte Mx = blockHeader.param >> 8;
+ //byte My = blockHeader.param & 0xFF;
+
+ uint16 codingType = _file->readUint16LE();
+ for (int i = 0; i < 4; i++) {
+ switch (codingType >> 14) {
+ case 0: // MOT: Skip block
+ //printf("coding type 0\n");
+ break;
+ case 1: { // FCC: Copy an existing block
+ //printf("coding type 1\n");
+ byte argument;
+ argument = _file->readByte();
+ //byte Dx = Mx + (argument >> 4);
+ //byte Dy = My + (argument & 0x0F);
+ // Dx = X + 8 - (argument >> 4) - Mx
+ // Dy = Y + 8 - (argument & 0x0F) - My
+ break;
+ }
+ case 2: { // SLD: Quad vector quantisation
+ //printf("coding type 2\n");
+ byte argument = _file->readByte();
+ if (argument > _num2blocks) {
+ //error("invalid 2x2 block: %d of %d", argument, _num2blocks);
+ }
+ break;
+ }
+ case 3:
+ //printf("coding type 3\n");
+ _file->readByte();
+ _file->readByte();
+ _file->readByte();
+ _file->readByte();
+ break;
+ }
+ codingType <<= 2;
+ }
+ return true;
+}
+
+bool ROQPlayer::processBlockStill(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing still (JPEG) block");
+ //Common::ReadStream *jpegData = new Common::SubReadStream(_file, blockHeader.size);
+ //Graphics::JPEG jpegFrame;
+ //jpegFrame.read(jpegData);
+ /*
+ Common::File save;
+ save.open("dump.jpg", Common::File::kFileWriteMode);
+ save.write(data, blockHeader.size);
+ save.close();
+ */
+ error("JPEG!");
+ return true;
+}
+
+bool ROQPlayer::processBlockSoundMono(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing mono sound block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1020) {
+ return false;
+ }
+
+ // Initialize the audio stream if needed
+ if (!_audioStream) {
+ byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE;
+#ifdef SCUMM_LITTLE_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ _audioStream = Audio::makeAppendableAudioStream(22050, flags);
+ Audio::SoundHandle sound_handle;
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
+ }
+
+ // Create the audio buffer
+ int16 *buffer = new int16[blockHeader.size];
+
+ // Initialize the prediction with the block parameter
+ int16 prediction = blockHeader.param ^ 0x8000;
+
+ // Process the data
+ for (uint16 i = 0; i < blockHeader.size; i++) {
+ int16 data = _file->readByte();
+ if (data < 0x80) {
+ prediction += data * data;
+ } else {
+ data -= 0x80;
+ prediction -= data * data;
+ }
+ buffer[i] = prediction;
+ }
+
+ // Queue the read buffer
+ _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockSoundStereo(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing stereo sound block");
+
+ // Verify the block header
+ if (blockHeader.type != 0x1021) {
+ return false;
+ }
+
+ // Initialize the audio stream if needed
+ if (!_audioStream) {
+ byte flags = Audio::Mixer::FLAG_16BITS | Audio::Mixer::FLAG_AUTOFREE | Audio::Mixer::FLAG_STEREO;
+#ifdef SCUMM_LITTLE_ENDIAN
+ flags |= Audio::Mixer::FLAG_LITTLE_ENDIAN;
+#endif
+ _audioStream = Audio::makeAppendableAudioStream(22050, flags);
+ Audio::SoundHandle sound_handle;
+ ::g_engine->_mixer->playInputStream(Audio::Mixer::kPlainSoundType, &sound_handle, _audioStream);
+ }
+
+ // Create the audio buffer
+ int16 *buffer = new int16[blockHeader.size];
+
+ // Initialize the prediction with the block parameter
+ int16 predictionLeft = (blockHeader.param & 0xFF00) ^ 0x8000;
+ int16 predictionRight = (blockHeader.param << 8) ^ 0x8000;
+ bool left = true;
+
+ // Process the data
+ for (uint16 i = 0; i < blockHeader.size; i++) {
+ int16 data = _file->readByte();
+ if (left) {
+ if (data < 0x80) {
+ predictionLeft += data * data;
+ } else {
+ data -= 0x80;
+ predictionLeft -= data * data;
+ }
+ buffer[i] = predictionLeft;
+ } else {
+ if (data < 0x80) {
+ predictionRight += data * data;
+ } else {
+ data -= 0x80;
+ predictionRight -= data * data;
+ }
+ buffer[i] = predictionRight;
+ }
+ left = !left;
+ }
+
+ // Queue the read buffer
+ _audioStream->queueBuffer((byte *)buffer, blockHeader.size * 2);
+
+ return true;
+}
+
+bool ROQPlayer::processBlockAudioContainer(ROQBlockHeader &blockHeader) {
+ debugC(5, kGroovieDebugVideo | kGroovieDebugAll, "Groovie::ROQ: Processing audio container block: 0x%04X", blockHeader.param);
+ return true;
+}
+
+} // End of Groovie namespace