aboutsummaryrefslogtreecommitdiff
path: root/engines/lastexpress/data/sequence.cpp
diff options
context:
space:
mode:
authorEugene Sandulenko2010-10-18 19:17:38 +0000
committerEugene Sandulenko2010-10-18 19:17:38 +0000
commit86d650926f9b991b6398e4ad4b0613ac264dfbaa (patch)
tree5e6791249fa5fce7dd3e1a6406dff4bf720ca085 /engines/lastexpress/data/sequence.cpp
parentc92d2bc234f2f73a9629b3622cd5e66c57439cda (diff)
downloadscummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.tar.gz
scummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.tar.bz2
scummvm-rg350-86d650926f9b991b6398e4ad4b0613ac264dfbaa.zip
LASTEXPRESS: Merge in the engine.
svn-id: r53579
Diffstat (limited to 'engines/lastexpress/data/sequence.cpp')
-rw-r--r--engines/lastexpress/data/sequence.cpp475
1 files changed, 475 insertions, 0 deletions
diff --git a/engines/lastexpress/data/sequence.cpp b/engines/lastexpress/data/sequence.cpp
new file mode 100644
index 0000000000..be8b14fc27
--- /dev/null
+++ b/engines/lastexpress/data/sequence.cpp
@@ -0,0 +1,475 @@
+/* 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$
+ *
+ */
+
+// Based on Deniz Oezmen's code: http://oezmen.eu/
+
+#include "lastexpress/data/sequence.h"
+
+#include "lastexpress/debug.h"
+
+namespace LastExpress {
+
+void FrameInfo::read(Common::SeekableReadStream *in, bool isSequence) {
+ // Save the current position
+ int32 basePos = in->pos();
+
+ dataOffset = in->readUint32LE();
+ unknown = in->readUint32LE();
+ paletteOffset = in->readUint32LE();
+ xPos1 = in->readUint32LE();
+ yPos1 = in->readUint32LE();
+ xPos2 = in->readUint32LE();
+ yPos2 = in->readUint32LE();
+ initialSkip = in->readUint32LE();
+ decompressedEndOffset = in->readUint32LE();
+
+ // Read the compression type for NIS files
+ if (!isSequence) {
+ in->seek(basePos + 0x124);
+ } else {
+ hotspot.left = (int16)in->readUint16LE();
+ hotspot.right = (int16)in->readUint16LE();
+ hotspot.top = (int16)in->readUint16LE();
+ hotspot.bottom = (int16)in->readUint16LE();
+ }
+
+ compressionType = in->readByte();
+ subType = (FrameSubType)in->readByte();
+
+ // Sequence information
+ field_2E = in->readByte();
+ keepPreviousFrame = in->readByte();
+ field_30 = in->readByte();
+ field_31 = in->readByte();
+ soundAction = in->readByte();
+ field_33 = in->readByte();
+ position = in->readByte();
+ field_35 = in->readByte();
+ field_36 = in->readUint16LE();
+ field_38 = in->readUint32LE();
+ entityPosition = (EntityPosition)in->readUint16LE();
+ location = in->readUint16LE();
+ next = in->readUint32LE();
+}
+
+
+// AnimFrame
+
+AnimFrame::AnimFrame(Common::SeekableReadStream *in, const FrameInfo &f) : _palette(NULL) {
+ _palSize = 1;
+ // TODO: use just the needed rectangle
+ _image.create(640, 480, 1);
+
+ //debugC(6, kLastExpressDebugGraphics, " Offsets: data=%d, unknown=%d, palette=%d", f.dataOffset, f.unknown, f.paletteOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Position: (%d, %d) - (%d, %d)", f.xPos1, f.yPos1, f.xPos2, f.yPos2);
+ //debugC(6, kLastExpressDebugGraphics, " Initial Skip: %d", f.initialSkip);
+ //debugC(6, kLastExpressDebugGraphics, " Decompressed end offset: %d", f.decompressedEndOffset);
+ //debugC(6, kLastExpressDebugGraphics, " Hotspot: (%d, %d) x (%d, %d)\n", f.hotspot.left, f.hotspot.top, f.hotspot.right, f.hotspot.bottom);
+ //debugC(6, kLastExpressDebugGraphics, " Compression type: %u / %u", f.compressionType, f.subType);
+ //debugC(6, kLastExpressDebugGraphics, " Unknown: %u - %u - %u - %u - %u - %u - %u - %d", f.field_2E, f.field_2F, f.field_30, f.field_31, f.field_33, f.field_35, f.field_36, f.field_38);
+ //debugC(6, kLastExpressDebugGraphics, " Sound action: %u", f.soundAction);
+ //debugC(6, kLastExpressDebugGraphics, " Position: %d", f.position);
+ //debugC(6, kLastExpressDebugGraphics, " Entity Position: %d", f.entityPosition);
+ //debugC(6, kLastExpressDebugGraphics, " Location: %d", f.location);
+ //debugC(6, kLastExpressDebugGraphics, " next: %d", f.next);
+
+ switch (f.compressionType) {
+ case 0:
+ // Empty frame
+ break;
+ case 3:
+ decomp3(in, f);
+ break;
+ case 4:
+ decomp4(in, f);
+ break;
+ case 5:
+ decomp5(in, f);
+ break;
+ case 7:
+ decomp7(in, f);
+ break;
+ case 255:
+ decompFF(in, f);
+ break;
+ default:
+ error("Unknown frame compression: %d", f.compressionType);
+ }
+
+ readPalette(in, f);
+ _rect = Common::Rect((int16)f.xPos1, (int16)f.yPos1, (int16)f.xPos2, (int16)f.yPos2);
+ //_rect.debugPrint(0, "Frame rect:");
+}
+
+AnimFrame::~AnimFrame() {
+ _image.free();
+ delete[] _palette;
+}
+
+Common::Rect AnimFrame::draw(Graphics::Surface *s) {
+ byte *inp = (byte *)_image.pixels;
+ uint16 *outp = (uint16 *)s->pixels;
+ for (int i = 0; i < 640 * 480; i++, inp++, outp++) {
+ if (*inp)
+ *outp = _palette[*inp];
+ }
+ return _rect;
+}
+
+void AnimFrame::readPalette(Common::SeekableReadStream *in, const FrameInfo &f) {
+ // Read the palette
+ in->seek((int)f.paletteOffset);
+ _palette = new uint16[_palSize];
+ for (uint32 i = 0; i < _palSize; i++) {
+ _palette[i] = in->readUint16LE();
+ }
+}
+
+void AnimFrame::decomp3(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0x7, 3);
+}
+
+void AnimFrame::decomp4(Common::SeekableReadStream *in, const FrameInfo &f) {
+ decomp34(in, f, 0xf, 4);
+}
+
+void AnimFrame::decomp34(Common::SeekableReadStream *in, const FrameInfo &f, byte mask, byte shift) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ opcode &= 0x3f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x3f;
+ if (opcode & 0x20) {
+ opcode = ((opcode & 0x1f) << 8) + in->readByte();
+ if (opcode & 0x1000) {
+ out += opcode & 0xfff;
+ continue;
+ }
+ }
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & mask;
+ opcode >>= shift;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp5(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (!(opcode & 0x1f)) {
+ opcode = (uint16)((opcode << 3) + in->readByte());
+ if (opcode & 0x400) {
+ // skip these 10 bits
+ out += (opcode & 0x3ff);
+ } else {
+ out += opcode + 2;
+ }
+ } else {
+ byte value = opcode & 0x1f;
+ opcode >>= 5;
+ if (_palSize <= value)
+ _palSize = value + 1;
+ if (!opcode)
+ opcode = in->readByte();
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ }
+}
+
+void AnimFrame::decomp7(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+ //warning("skip: %d, %d", skip % 640, skip / 640);
+ //warning("size: %d, %d", size % 640, size / 640);
+ //assert (f.yPos1 == skip / 640);
+ //assert (f.yPos2 == size / 640);
+
+ uint32 numBlanks = 640 - (f.xPos2 - f.xPos1);
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+ if (opcode & 0x80) {
+ if (opcode & 0x40) {
+ if (opcode & 0x20) {
+ opcode &= 0x1f;
+ out += numBlanks + opcode + 1;
+ } else {
+ opcode &= 0x1f;
+ if (opcode & 0x10) {
+ opcode = ((opcode & 0xf) << 8) + in->readByte();
+ if (opcode & 0x800) {
+ // skip these 11 bits
+ out += (opcode & 0x7ff);
+ continue;
+ }
+ }
+
+ // skip these 4 bits
+ out += opcode + 2;
+ }
+ } else {
+ opcode &= 0x3f;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ }
+ }
+}
+
+void AnimFrame::decompFF(Common::SeekableReadStream *in, const FrameInfo &f) {
+ byte *p = (byte *)_image.getBasePtr(0, 0);
+
+ uint32 skip = f.initialSkip / 2;
+ uint32 size = f.decompressedEndOffset / 2;
+
+ in->seek((int)f.dataOffset);
+ for (uint32 out = skip; out < size; ) {
+ uint16 opcode = in->readByte();
+
+ if (opcode < 0x80) {
+ if (_palSize <= opcode)
+ _palSize = opcode + 1;
+ // set the given value
+ p[out] = (byte)opcode;
+ out++;
+ } else {
+ if (opcode < 0xf0) {
+ if (opcode < 0xe0) {
+ // copy old part
+ uint32 old = out + ((opcode & 0x7) << 8) + in->readByte() - 2048;
+ opcode = ((opcode >> 3) & 0xf) + 3;
+ for (int i = 0; i < opcode; i++, out++, old++) {
+ p[out] = p[old];
+ }
+ } else {
+ opcode = (opcode & 0xf) + 1;
+ byte value = in->readByte();
+ if (_palSize <= value)
+ _palSize = value + 1;
+ for (int i = 0; i < opcode; i++, out++) {
+ p[out] = value;
+ }
+ }
+ } else {
+ out += ((opcode & 0xf) << 8) + in->readByte();
+ }
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// SEQUENCE
+//////////////////////////////////////////////////////////////////////////
+
+Sequence::~Sequence() {
+ reset();
+}
+
+void Sequence::reset() {
+ _frames.clear();
+ delete _stream;
+ _stream = NULL;
+}
+
+Sequence *Sequence::load(Common::String name, Common::SeekableReadStream *stream, byte field30) {
+ Sequence *sequence = new Sequence(name);
+
+ if (!sequence->load(stream, field30)) {
+ delete sequence;
+ return NULL;
+ }
+
+ return sequence;
+}
+
+bool Sequence::load(Common::SeekableReadStream *stream, byte field30) {
+ if (!stream)
+ return false;
+
+ // Reset data
+ reset();
+
+ _field30 = field30;
+
+ // Keep stream for later decoding of sequence
+ _stream = stream;
+
+ // Read header to get the number of frames
+ _stream->seek(0);
+ uint32 numframes = _stream->readUint32LE();
+ uint32 unknown = _stream->readUint32LE();
+ debugC(3, kLastExpressDebugGraphics, "Number of frames in sequence: %d / unknown=0x%x", numframes, unknown);
+
+ // Store frames information
+ for (uint i = 0; i < numframes; i++) {
+
+ // Move stream to start of frame
+ _stream->seek((int32)(_sequenceHeaderSize + i * _sequenceFrameSize), SEEK_SET);
+ if (_stream->eos())
+ error("Couldn't seek to the current frame data");
+
+ // Check if there is enough data
+ if ((unsigned)(_stream->size() - _stream->pos()) < _sequenceFrameSize)
+ error("The sequence frame does not have a valid header");
+
+ FrameInfo info;
+ info.read(_stream, true);
+ _frames.push_back(info);
+ }
+
+ _isLoaded = true;
+
+ return true;
+}
+
+FrameInfo *Sequence::getFrameInfo(uint16 index) {
+ if (_frames.size() == 0)
+ error("Trying to decode a sequence before loading its data");
+
+ if (index > _frames.size() - 1)
+ error("Invalid sequence frame requested: %d, max %d", index, _frames.size() - 1);
+
+ return &_frames[index];
+}
+
+AnimFrame *Sequence::getFrame(uint16 index) {
+
+ FrameInfo *frame = getFrameInfo(index);
+
+ if (!frame)
+ return NULL;
+
+ // Skip "invalid" frames
+ if (frame->compressionType == 0)
+ return NULL;
+
+ debugC(9, kLastExpressDebugGraphics, "Decoding sequence %s: frame %d / %d", _name.c_str(), index, _frames.size() - 1);
+
+ return new AnimFrame(_stream, *frame);
+}
+
+//////////////////////////////////////////////////////////////////////////
+// SequenceFrame
+SequenceFrame::~SequenceFrame() {
+ if (_dispose && _sequence) {
+ delete _sequence;
+ }
+
+ _sequence = NULL;
+}
+
+Common::Rect SequenceFrame::draw(Graphics::Surface *surface) {
+ if (!_sequence || _frame >= _sequence->count())
+ return Common::Rect();
+
+ AnimFrame *f = _sequence->getFrame(_frame);
+ if (!f)
+ return Common::Rect();
+
+ Common::Rect rect = f->draw(surface);
+
+ delete f;
+
+ return rect;
+}
+
+bool SequenceFrame::setFrame(uint16 frame) {
+ if (!_sequence)
+ return false;
+
+ if (frame < _sequence->count()) {
+ _frame = frame;
+ return true;
+ } else
+ return false;
+}
+
+bool SequenceFrame::nextFrame() {
+ return setFrame(_frame + 1);
+}
+
+FrameInfo *SequenceFrame::getInfo() {
+ if (!_sequence)
+ error("SequenceFrame::getFrameInfo: Invalid sequence!");
+
+ return _sequence->getFrameInfo(_frame);
+}
+
+bool SequenceFrame::equal(const SequenceFrame *other) const {
+ return _sequence->getName() == other->_sequence->getName() && _frame == other->_frame;
+}
+
+} // End of namespace LastExpress