Permalink
Browse files

MUTATIONOFJB: Introduce better animation loader that supports diff fr…

…ames.
  • Loading branch information...
LubomirR committed Mar 25, 2018
1 parent 18bdd91 commit 027f4099fa67d274f7f70c437ff14291dc474eed
@@ -0,0 +1,191 @@
/* 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 "mutationofjb/animationdecoder.h"
#include "mutationofjb/encryptedfile.h"
#include "mutationofjb/util.h"
#include "common/debug.h"
#include "common/translation.h"

namespace MutationOfJB {

AnimationDecoder::AnimationDecoder(const Common::String &fileName) : _fileName(fileName) {
_surface.create(IMAGE_WIDTH, IMAGE_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
}

bool AnimationDecoder::decode(AnimationDecoderCallback *callback) {
EncryptedFile file;
file.open(_fileName);

if (!file.isOpen()) {
reportFileMissingError(_fileName.c_str());

return false;
}

file.seek(0, SEEK_END);
const int32 endPos = file.pos();

// Skip header - we don't need it anyway.
file.seek(0x80);

int frameNo = 0;

while (file.pos() != endPos) {
// Record.
const uint32 length = file.readUint32LE();
const uint16 recordId = file.readUint16LE();
const uint16 subrecords = file.readUint16LE();

// Skip 8 empty bytes.
file.seek(8, SEEK_CUR);

// Subrecords.
if (recordId == 0xF1FA) {
for (int i = 0; i < subrecords; ++i) {
int32 filePos = file.pos();

const uint32 subLength = file.readUint32LE();
const uint16 type = file.readUint16LE();

if (type == 0x0B) {
loadPalette(file);
if (callback) {
callback->onPaletteUpdated(_palette);
}
} else if (type == 0x0F) {
loadFullFrame(file, subLength - 6);
if (callback) {
callback->onFrame(frameNo, _surface);
}
} else if (type == 0x0C) {
loadDiffFrame(file, subLength - 6);
if (callback) {
callback->onFrame(frameNo, _surface);
}
} else {
debug(_("Unsupported record type %02X."), type);
file.seek(subLength - 6, SEEK_CUR);
}

// Makes decoding more robust, because for some reason records might have extra data at the end.
file.seek(filePos + subLength, SEEK_SET);
}
frameNo++;
} else {
file.seek(length - 16, SEEK_CUR);
}
}
file.close();

return true;
}

void AnimationDecoder::loadPalette(Common::SeekableReadStream &file) {
uint16 packets = file.readUint16LE();
const uint8 skipCount = file.readByte();
int copyCount = file.readByte();
if (copyCount == 0) {
copyCount = PALETTE_COLORS;
}

while(packets--) {
file.read(_palette + skipCount * 3, copyCount * 3);

for (int j = skipCount * 3; j < (skipCount + copyCount) * 3; ++j) {
_palette[j] <<= 2; // Uses 6-bit colors.
}
}
}

void AnimationDecoder::loadFullFrame(EncryptedFile &file, uint32 size) {
uint8 *const pixels = reinterpret_cast<uint8 *>(_surface.getPixels());
uint8 *ptr = pixels;
uint32 readBytes = 0;
uint32 lines = 0;

while (readBytes != size) {
if (lines == 200) {
// Some full frames have an unknown byte at the end,
// so break when we encounter all 200 lines.
break;
}

uint8 no = file.readByte();
readBytes++;
while (no--) {
uint8 n = file.readByte();
readBytes++;
if (n < 0x80) {
// RLE - Copy color n times.
uint8 color = file.readByte();
readBytes++;
while(n--) {
*ptr++ = color;
}
} else {
// Take next 0x100 - n bytes as they are.
const uint32 rawlen = 0x100 - n;
file.read(ptr, rawlen);
readBytes += rawlen;
ptr += rawlen;
}
}
lines++;
}
}

void AnimationDecoder::loadDiffFrame(EncryptedFile &file, uint32) {
const uint16 firstLine = file.readUint16LE();
const uint16 numLines = file.readUint16LE();

for (uint16 line = firstLine; line < firstLine + numLines; ++line) {
uint8 *imageData = reinterpret_cast<uint8 *>(_surface.getBasePtr(0, firstLine));

uint8 runs = file.readByte();
while (runs--) {
uint8 localOffset = file.readByte();
uint8 num = file.readByte();

imageData += localOffset;
if (num == 0) {
// Ignore?
debug("Zero RLE number found.");
} else if (num < 0x80) {
file.read(imageData, num);
imageData += num;
} else {
const uint8 color = file.readByte();
const int no = 0x100 - num;
memset(imageData, color, no);
imageData += no;
}

}
}
}

AnimationDecoder::~AnimationDecoder() {
_surface.free();
}

}
@@ -0,0 +1,69 @@
/* 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.
*
*/

#ifndef MUTATIONOFJB_ANIMATIONDECODER_H
#define MUTATIONOFJB_ANIMATIONDECODER_H

#include "common/scummsys.h"
#include "common/str.h"
#include "graphics/surface.h"
#include "mutationofjb/encryptedfile.h"

namespace Common {
class SeekableReadStream;
}

namespace MutationOfJB {

enum {
PALETTE_COLORS = 256,
PALETTE_SIZE = PALETTE_COLORS * 3,
IMAGE_WIDTH = 320,
IMAGE_HEIGHT = 200
};

class AnimationDecoderCallback {
public:
virtual void onFrame(int frameNo, Graphics::Surface &surface) = 0;
virtual void onPaletteUpdated(byte palette[PALETTE_SIZE]) = 0;
virtual ~AnimationDecoderCallback() {}
};

class AnimationDecoder {
public:
AnimationDecoder(const Common::String &fileName);
~AnimationDecoder();
bool decode(AnimationDecoderCallback *callback);

private:
void loadPalette(Common::SeekableReadStream &stream);
void loadFullFrame(EncryptedFile &file, uint32 size);
void loadDiffFrame(EncryptedFile &file, uint32 size);

Common::String _fileName;
Graphics::Surface _surface;
byte _palette[PALETTE_SIZE];
};

}

#endif
@@ -18,6 +18,7 @@ MODULE_OBJS := \
commands/removeitemcommand.o \
commands/saycommand.o \
commands/seqcommand.o \
animationdecoder.o \
debug.o \
detection.o \
encryptedfile.o \
Oops, something went wrong.

0 comments on commit 027f409

Please sign in to comment.