scummvm/engines/parallaction/disk_ns.cpp
2014-02-28 00:27:28 -05:00

1124 lines
29 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 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 "common/config-manager.h"
#include "common/fs.h"
#include "common/iff_container.h"
#include "common/memstream.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "image/iff.h"
#include "parallaction/parser.h"
#include "parallaction/parallaction.h"
namespace Parallaction {
// HACK: Several archives ('de', 'en', 'fr' and 'disk0') in the multi-lingual
// Amiga version of Nippon Safes, and one archive ('fr') in the Amiga Demo of
// Nippon Safes used different internal offsets than all the other archives.
//
// When an archive is opened in the Amiga demo, its size is checked against
// SIZEOF_SMALL_ARCHIVE to detect when the smaller archive is used.
//
// When an archive is opened in Amiga multi-lingual version, the header is
// checked again NDOS to detect when a smaller archive is used.
//
#define SIZEOF_SMALL_ARCHIVE 12778
#define ARCHIVE_FILENAMES_OFS 0x16
#define NORMAL_ARCHIVE_FILES_NUM 384
#define SMALL_ARCHIVE_FILES_NUM 180
#define NORMAL_ARCHIVE_SIZES_OFS 0x3016
#define SMALL_ARCHIVE_SIZES_OFS 0x1696
#define NORMAL_ARCHIVE_DATA_OFS 0x4000
#define SMALL_ARCHIVE_DATA_OFS 0x1966
#define MAX_ARCHIVE_ENTRIES 384
class NSArchive : public Common::Archive {
Common::SeekableReadStream *_stream;
char _archiveDir[MAX_ARCHIVE_ENTRIES][32];
uint32 _archiveLengths[MAX_ARCHIVE_ENTRIES];
uint32 _archiveOffsets[MAX_ARCHIVE_ENTRIES];
uint32 _numFiles;
uint32 lookup(const char *name) const;
public:
NSArchive(Common::SeekableReadStream *stream, Common::Platform platform, uint32 features);
~NSArchive();
Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
bool hasFile(const Common::String &name) const;
int listMembers(Common::ArchiveMemberList &list) const;
const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
};
NSArchive::NSArchive(Common::SeekableReadStream *stream, Common::Platform platform, uint32 features) : _stream(stream) {
if (!_stream) {
error("NSArchive: invalid stream passed to constructor");
}
bool isSmallArchive = false;
if (platform == Common::kPlatformAmiga) {
if (features & GF_DEMO) {
isSmallArchive = stream->size() == SIZEOF_SMALL_ARCHIVE;
} else if (features & GF_LANG_MULT) {
isSmallArchive = (stream->readUint32BE() != MKTAG('N','D','O','S'));
}
}
_numFiles = (isSmallArchive) ? SMALL_ARCHIVE_FILES_NUM : NORMAL_ARCHIVE_FILES_NUM;
_stream->seek(ARCHIVE_FILENAMES_OFS);
_stream->read(_archiveDir, _numFiles*32);
_stream->seek((isSmallArchive) ? SMALL_ARCHIVE_SIZES_OFS : NORMAL_ARCHIVE_SIZES_OFS);
uint32 dataOffset = (isSmallArchive) ? SMALL_ARCHIVE_DATA_OFS : NORMAL_ARCHIVE_DATA_OFS;
for (uint16 i = 0; i < _numFiles; i++) {
_archiveOffsets[i] = dataOffset;
_archiveLengths[i] = _stream->readUint32BE();
dataOffset += _archiveLengths[i];
}
}
NSArchive::~NSArchive() {
delete _stream;
}
uint32 NSArchive::lookup(const char *name) const {
uint32 i = 0;
for ( ; i < _numFiles; i++) {
if (!scumm_stricmp(_archiveDir[i], name)) break;
}
return i;
}
Common::SeekableReadStream *NSArchive::createReadStreamForMember(const Common::String &name) const {
debugC(3, kDebugDisk, "NSArchive::createReadStreamForMember(%s)", name.c_str());
if (name.empty())
return 0;
uint32 index = lookup(name.c_str());
if (index == _numFiles) return 0;
debugC(9, kDebugDisk, "NSArchive::createReadStreamForMember: '%s' found in slot %i", name.c_str(), index);
int offset = _archiveOffsets[index];
int endOffset = _archiveOffsets[index] + _archiveLengths[index];
return new Common::SeekableSubReadStream(_stream, offset, endOffset, DisposeAfterUse::NO);
}
bool NSArchive::hasFile(const Common::String &name) const {
if (name.empty())
return false;
return lookup(name.c_str()) != _numFiles;
}
int NSArchive::listMembers(Common::ArchiveMemberList &list) const {
for (uint32 i = 0; i < _numFiles; i++) {
list.push_back(Common::SharedPtr<Common::GenericArchiveMember>(new Common::GenericArchiveMember(_archiveDir[i], this)));
}
return _numFiles;
}
const Common::ArchiveMemberPtr NSArchive::getMember(const Common::String &name) const {
uint32 index = lookup(name.c_str());
const char *item = 0;
if (index < _numFiles) {
item = _archiveDir[index];
}
return Common::SharedPtr<Common::GenericArchiveMember>(new Common::GenericArchiveMember(item, this));
}
#define HIGHEST_PRIORITY 9
#define NORMAL_ARCHIVE_PRIORITY 5
#define LOW_ARCHIVE_PRIORITY 2
#define LOWEST_ARCHIVE_PRIORITY 1
Disk_ns::Disk_ns(Parallaction *vm) : _vm(vm) {
Common::FSDirectory *baseDir = new Common::FSDirectory(ConfMan.get("path"));
_sset.add("basedir", baseDir, HIGHEST_PRIORITY);
}
Disk_ns::~Disk_ns() {
_sset.clear();
}
void Disk_ns::errorFileNotFound(const char *s) {
error("File '%s' not found", s);
}
Common::SeekableReadStream *Disk_ns::openFile(const char *filename) {
Common::SeekableReadStream *stream = tryOpenFile(filename);
if (!stream)
errorFileNotFound(filename);
return stream;
}
void Disk_ns::addArchive(const Common::String& name, int priority) {
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(name);
if (!stream)
error("Disk_ns::addArchive() couldn't find archive '%s'", name.c_str());
debugC(1, kDebugDisk, "Disk_ns::addArchive(name = %s, priority = %i)", name.c_str(), priority);
NSArchive *arc = new NSArchive(stream, _vm->getPlatform(), _vm->getFeatures());
_sset.add(name, arc, priority);
}
Common::String Disk_ns::selectArchive(const Common::String& name) {
Common::String oldName = _resArchiveName;
if (_sset.hasArchive(name)) {
return oldName;
}
if (!_resArchiveName.empty()) {
_sset.remove(_resArchiveName);
}
_resArchiveName = name;
addArchive(name, NORMAL_ARCHIVE_PRIORITY);
return oldName;
}
void Disk_ns::setLanguage(uint16 language) {
debugC(1, kDebugDisk, "setLanguage(%i)", language);
assert(language < 4);
if (!_language.empty()) {
_sset.remove(_language);
}
static const char *languages[] = { "it", "fr", "en", "ge" };
_language = languages[language];
if (_sset.hasArchive(_language)) {
return;
}
addArchive(_language, LOWEST_ARCHIVE_PRIORITY);
}
#pragma mark -
DosDisk_ns::DosDisk_ns(Parallaction* vm) : Disk_ns(vm) {
}
DosDisk_ns::~DosDisk_ns() {
}
void DosDisk_ns::init() {
// setup permament archives
addArchive("disk1", LOW_ARCHIVE_PRIORITY);
}
Common::SeekableReadStream *DosDisk_ns::tryOpenFile(const char* name) {
debugC(3, kDebugDisk, "DosDisk_ns::tryOpenFile(%s)", name);
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(name);
if (stream)
return stream;
char path[PATH_LEN];
sprintf(path, "%s.pp", name);
return _sset.createReadStreamForMember(path);
}
Script* Disk_ns::loadLocation(const char *name) {
char path[PATH_LEN];
const char *charName = _vm->_char.getBaseName();
// WORKAROUND: Special case for the Multilingual DOS version: during the ending
// sequence, it tries to load a non-existing file using "Dinor" as a character
// name. In this case, the character name should be just "dino".
if (!strcmp(charName, "Dinor"))
charName = "dino";
sprintf(path, "%s%s/%s.loc", charName, _language.c_str(), name);
debugC(3, kDebugDisk, "Disk_ns::loadLocation(%s): trying '%s'", name, path);
Common::SeekableReadStream *stream = tryOpenFile(path);
if (!stream) {
sprintf(path, "%s/%s.loc", _language.c_str(), name);
debugC(3, kDebugDisk, "DosDisk_ns::loadLocation(%s): trying '%s'", name, path);
stream = openFile(path);
}
return new Script(stream, true);
}
Script* Disk_ns::loadScript(const char* name) {
debugC(1, kDebugDisk, "Disk_ns::loadScript '%s'", name);
char path[PATH_LEN];
sprintf(path, "%s.script", name);
Common::SeekableReadStream *stream = openFile(path);
return new Script(stream, true);
}
Cnv *Disk_ns::makeCnv(Common::SeekableReadStream *stream) {
assert(stream);
uint16 numFrames = stream->readByte();
uint16 width = stream->readByte();
assert((width & 7) == 0);
uint16 height = stream->readByte();
uint32 decsize = numFrames * width * height;
byte *data = new byte[decsize];
assert(data);
memset(data, 0, decsize);
decodeCnv(data, numFrames, width, height, stream);
delete stream;
return new Cnv(numFrames, width, height, data, true);
}
void DosDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) {
int32 decsize = numFrames * width * height;
bool packed = (stream->size() - stream->pos()) != decsize;
if (packed) {
Common::PackBitsReadStream decoder(*stream);
decoder.read(data, decsize);
} else {
stream->read(data, decsize);
}
}
Cnv* DosDisk_ns::loadCnv(const char *filename) {
Common::SeekableReadStream *stream = openFile(filename);
assert(stream);
return makeCnv(stream);
}
GfxObj* DosDisk_ns::loadTalk(const char *name) {
const char *ext = strstr(name, ".talk");
if (ext) {
// npc talk
return new GfxObj(0, loadCnv(name), name);
}
char v20[30];
if (g_engineFlags & kEngineTransformedDonna) {
sprintf(v20, "%stta.cnv", name);
} else {
sprintf(v20, "%stal.cnv", name);
}
return new GfxObj(0, loadCnv(v20), name);
}
GfxObj* DosDisk_ns::loadHead(const char* name) {
char path[PATH_LEN];
sprintf(path, "%shead", name);
path[8] = '\0';
strcat(path, ".cnv");
return new GfxObj(0, loadCnv(path));
}
Frames* DosDisk_ns::loadPointer(const char *name) {
char path[PATH_LEN];
sprintf(path, "%s.cnv", name);
return loadCnv(path);
}
Font* DosDisk_ns::loadFont(const char* name) {
char path[PATH_LEN];
sprintf(path, "%scnv.cnv", name);
return createFont(name, loadCnv(path));
}
GfxObj* DosDisk_ns::loadObjects(const char *name, uint8 part) {
char path[PATH_LEN];
sprintf(path, "%sobj.cnv", name);
return new GfxObj(0, loadCnv(path), name);
}
GfxObj* DosDisk_ns::loadStatic(const char* name) {
return new GfxObj(0, loadCnv(name), name);
}
Frames* DosDisk_ns::loadFrames(const char* name) {
return loadCnv(name);
}
/*
Background images are compressed using a RLE algorithm that resembles PackBits.
The uncompressed data is then unpacked as following:
- color data [bits 0-5]
- mask data [bits 6-7] (z buffer)
- path data [bit 8] (walkable areas)
*/
void DosDisk_ns::unpackBackground(Common::ReadStream *stream, byte *screen, byte *mask, byte *path) {
byte storage[128];
uint32 storageLen = 0, len = 0;
uint32 j = 0;
while (1) {
// first extracts packbits variant data
do {
len = stream->readByte();
if (stream->eos())
return;
if (len == 128) {
storageLen = 0;
} else if (len <= 127) {
len++;
for (uint32 i = 0; i < len; i++) {
storage[i] = stream->readByte();
}
storageLen = len;
} else {
len = (256 - len) + 1;
byte v = stream->readByte();
memset(storage, v, len);
storageLen = len;
}
} while (storageLen == 0);
// then unpacks the bits to the destination buffers
for (uint32 i = 0; i < storageLen; i++, j++) {
byte b = storage[i];
path[j/8] |= ((b & 0x80) >> 7) << (j & 7);
mask[j/4] |= ((b & 0x60) >> 5) << ((j & 3) << 1);
screen[j] = b & 0x1F;
}
}
}
void DosDisk_ns::parseDepths(BackgroundInfo &info, Common::SeekableReadStream &stream) {
info.layers[0] = stream.readByte();
info.layers[1] = stream.readByte();
info.layers[2] = stream.readByte();
info.layers[3] = stream.readByte();
}
void DosDisk_ns::createMaskAndPathBuffers(BackgroundInfo &info) {
info._mask = new MaskBuffer;
assert(info._mask);
info._mask->create(info.width, info.height);
info._mask->bigEndian = true;
info._path = new PathBuffer;
assert(info._path);
info._path->create(info.width, info.height);
info._path->bigEndian = true;
}
void DosDisk_ns::loadBackground(BackgroundInfo& info, const char *filename) {
Common::SeekableReadStream *stream = openFile(filename);
info.width = _vm->_screenWidth; // 320
info.height = _vm->_screenHeight; // 200
// read palette
byte tmp[3];
for (uint i = 0; i < 32; i++) {
tmp[0] = stream->readByte();
tmp[1] = stream->readByte();
tmp[2] = stream->readByte();
info.palette.setEntry(i, tmp[0], tmp[1], tmp[2]);
}
// read z coordinates
parseDepths(info, *stream);
// read palette rotation parameters
PaletteFxRange range;
for (uint32 _si = 0; _si < 6; _si++) {
range._timer = stream->readUint16BE();
range._step = stream->readUint16BE();
range._flags = stream->readUint16BE();
range._first = stream->readByte();
range._last = stream->readByte();
info.setPaletteRange(_si, range);
}
// read bitmap, mask and path data and extract them into the 3 buffers
info.bg.create(info.width, info.height, Graphics::PixelFormat::createFormatCLUT8());
createMaskAndPathBuffers(info);
unpackBackground(stream, (byte *)info.bg.getPixels(), info._mask->data, info._path->data);
delete stream;
}
void DosDisk_ns::loadSlide(BackgroundInfo& info, const char *filename) {
char path[PATH_LEN];
sprintf(path, "%s.slide", filename);
loadBackground(info, path);
}
void DosDisk_ns::loadScenery(BackgroundInfo& info, const char *name, const char *mask, const char* path) {
char filename[PATH_LEN];
sprintf(filename, "%s.dyn", name);
// load bitmap
loadBackground(info, filename);
if (mask == 0) {
return;
}
// load external mask and path if present (overwriting the ones loaded by loadBackground)
char maskPath[PATH_LEN];
sprintf(maskPath, "%s.msk", mask);
Common::SeekableReadStream *stream = openFile(maskPath);
assert(stream);
parseDepths(info, *stream);
createMaskAndPathBuffers(info);
stream->read(info._path->data, info._path->size);
stream->read(info._mask->data, info._mask->size);
delete stream;
}
Table* DosDisk_ns::loadTable(const char* name) {
char path[PATH_LEN];
sprintf(path, "%s.tab", name);
return createTableFromStream(100, openFile(path));
}
Common::SeekableReadStream* DosDisk_ns::loadMusic(const char* name) {
char path[PATH_LEN];
sprintf(path, "%s.mid", name);
return openFile(path);
}
Common::SeekableReadStream* DosDisk_ns::loadSound(const char* name) {
return 0;
}
#pragma mark -
/* the decoder presented here is taken from pplib by Stuart Caie. The
* following statement comes from the original source.
*
* pplib 1.0: a simple PowerPacker decompression and decryption library
* placed in the Public Domain on 2003-09-18 by Stuart Caie.
*/
#define PP_READ_BITS(nbits, var) do { \
bit_cnt = (nbits); (var) = 0; \
while (bits_left < bit_cnt) { \
if (buf < src) return 0; \
bit_buffer |= *--buf << bits_left; \
bits_left += 8; \
} \
bits_left -= bit_cnt; \
while (bit_cnt--) { \
(var) = ((var) << 1) | (bit_buffer & 1); \
bit_buffer >>= 1; \
} \
} while (0)
#define PP_BYTE_OUT(byte) do { \
if (out <= dest) return 0; \
*--out = (byte); written++; \
} while (0)
class PowerPackerStream : public Common::SeekableReadStream {
SeekableReadStream *_stream;
bool _dispose;
private:
int ppDecrunchBuffer(byte *src, byte *dest, uint32 src_len, uint32 dest_len) {
byte *buf, *out, *dest_end, *off_lens, bits_left = 0, bit_cnt;
uint32 bit_buffer = 0, x, todo, offbits, offset, written = 0;
if (!src || !dest) return 0;
/* set up input and output pointers */
off_lens = src; src = &src[4];
buf = &src[src_len];
out = dest_end = &dest[dest_len];
/* skip the first few bits */
PP_READ_BITS(src[src_len + 3], x);
/* while there are input bits left */
while (written < dest_len) {
PP_READ_BITS(1, x);
if (x == 0) {
/* bit==0: literal, then match. bit==1: just match */
todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3);
while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); }
/* should we end decoding on a literal, break out of the main loop */
if (written == dest_len) break;
}
/* match: read 2 bits for initial offset bitlength / match length */
PP_READ_BITS(2, x);
offbits = off_lens[x];
todo = x+2;
if (x == 3) {
PP_READ_BITS(1, x);
if (x == 0) offbits = 7;
PP_READ_BITS(offbits, offset);
do { PP_READ_BITS(3, x); todo += x; } while (x == 7);
}
else {
PP_READ_BITS(offbits, offset);
}
if (&out[offset] >= dest_end) return 0; /* match_overflow */
while (todo--) { x = out[offset]; PP_BYTE_OUT(x); }
}
/* all output bytes written without error */
return 1;
}
uint16 getCrunchType(uint32 signature) {
byte eff = 0;
switch (signature) {
case 0x50503230: /* PP20 */
eff = 4;
break;
case 0x50504C53: /* PPLS */
error("PPLS crunched files are not supported");
#if 0
eff = 8;
break;
#endif
case 0x50583230: /* PX20 */
error("PX20 crunched files are not supported");
#if 0
eff = 6;
break;
#endif
default:
eff = 0;
}
return eff;
}
public:
PowerPackerStream(Common::SeekableReadStream &stream) {
_dispose = false;
uint32 signature = stream.readUint32BE();
if (getCrunchType(signature) == 0) {
stream.seek(0, SEEK_SET);
_stream = &stream;
return;
}
stream.seek(-4, SEEK_END);
uint32 decrlen = stream.readUint32BE() >> 8;
byte *dest = (byte *)malloc(decrlen);
uint32 crlen = stream.size() - 4;
byte *src = (byte *)malloc(crlen);
stream.seek(4, SEEK_SET);
stream.read(src, crlen);
ppDecrunchBuffer(src, dest, crlen-8, decrlen);
free(src);
_stream = new Common::MemoryReadStream(dest, decrlen, DisposeAfterUse::YES);
_dispose = true;
}
~PowerPackerStream() {
if (_dispose) delete _stream;
}
int32 size() const {
return _stream->size();
}
int32 pos() const {
return _stream->pos();
}
bool eos() const {
return _stream->eos();
}
bool seek(int32 offs, int whence = SEEK_SET) {
return _stream->seek(offs, whence);
}
uint32 read(void *dataPtr, uint32 dataSize) {
return _stream->read(dataPtr, dataSize);
}
};
AmigaDisk_ns::AmigaDisk_ns(Parallaction *vm) : Disk_ns(vm) {
}
AmigaDisk_ns::~AmigaDisk_ns() {
}
void AmigaDisk_ns::init() {
// setup permament archives
if (_vm->getFeatures() & GF_DEMO) {
addArchive("disk0", LOW_ARCHIVE_PRIORITY);
} else {
addArchive("disk0", LOW_ARCHIVE_PRIORITY);
addArchive("disk1", LOW_ARCHIVE_PRIORITY);
}
}
#define NUM_PLANES 5
/*
unpackFrame transforms images from 5-bitplanes format to
8-bit color-index mode
*/
void AmigaDisk_ns::unpackFrame(byte *dst, byte *src, uint16 planeSize) {
byte s0, s1, s2, s3, s4, mask, t0, t1, t2, t3, t4;
for (uint32 j = 0; j < planeSize; j++) {
s0 = src[j];
s1 = src[j+planeSize];
s2 = src[j+planeSize*2];
s3 = src[j+planeSize*3];
s4 = src[j+planeSize*4];
for (uint32 k = 0; k < 8; k++) {
mask = 1 << (7 - k);
t0 = (s0 & mask ? 1 << 0 : 0);
t1 = (s1 & mask ? 1 << 1 : 0);
t2 = (s2 & mask ? 1 << 2 : 0);
t3 = (s3 & mask ? 1 << 3 : 0);
t4 = (s4 & mask ? 1 << 4 : 0);
*dst++ = t0 | t1 | t2 | t3 | t4;
}
}
}
/*
patchFrame applies DLTA data (dlta) to specified buffer (dst)
*/
void AmigaDisk_ns::patchFrame(byte *dst, byte *dlta, uint16 bytesPerPlane, uint16 height) {
uint32 *dataIndex = (uint32 *)dlta;
uint32 *ofslenIndex = (uint32 *)dlta + 8;
uint16 *base = (uint16 *)dlta;
uint16 wordsPerLine = bytesPerPlane >> 1;
for (uint j = 0; j < NUM_PLANES; j++) {
uint16 *dst16 = (uint16 *)(dst + j * bytesPerPlane * height);
uint16 *data = base + READ_BE_UINT32(dataIndex);
dataIndex++;
uint16 *ofslen = base + READ_BE_UINT32(ofslenIndex);
ofslenIndex++;
while (*ofslen != 0xFFFF) {
uint16 ofs = READ_BE_UINT16(ofslen);
ofslen++;
uint16 size = READ_BE_UINT16(ofslen);
ofslen++;
while (size > 0) {
dst16[ofs] ^= *data++;
ofs += wordsPerLine;
size--;
}
}
}
}
// FIXME: no mask is loaded
void AmigaDisk_ns::unpackBitmap(byte *dst, byte *src, uint16 numFrames, uint16 bytesPerPlane, uint16 height) {
byte *baseFrame = src;
byte *tempBuffer = 0;
uint16 planeSize = bytesPerPlane * height;
for (uint32 i = 0; i < numFrames; i++) {
if (READ_BE_UINT32(src) == MKTAG('D','L','T','A')) {
uint size = READ_BE_UINT32(src + 4);
if (tempBuffer == 0)
tempBuffer = (byte *)malloc(planeSize * NUM_PLANES);
memcpy(tempBuffer, baseFrame, planeSize * NUM_PLANES);
patchFrame(tempBuffer, src + 8, bytesPerPlane, height);
unpackFrame(dst, tempBuffer, planeSize);
src += (size + 8);
dst += planeSize * 8;
} else {
unpackFrame(dst, src, planeSize);
src += planeSize * NUM_PLANES;
dst += planeSize * 8;
}
}
free(tempBuffer);
}
void AmigaDisk_ns::decodeCnv(byte *data, uint16 numFrames, uint16 width, uint16 height, Common::SeekableReadStream *stream) {
byte bytesPerPlane = width / 8;
uint32 rawsize = numFrames * bytesPerPlane * NUM_PLANES * height;
byte *buf = new byte[rawsize];
assert(buf);
stream->read(buf, rawsize);
unpackBitmap(data, buf, numFrames, bytesPerPlane, height);
delete[] buf;
}
#undef NUM_PLANES
Frames* AmigaDisk_ns::loadPointer(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadPointer");
Common::SeekableReadStream *stream = openFile(name);
return makeCnv(stream);
}
GfxObj* AmigaDisk_ns::loadStatic(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadStatic '%s'", name);
Common::SeekableReadStream *s = openFile(name);
return new GfxObj(0, makeCnv(s), name);
}
Common::SeekableReadStream *AmigaDisk_ns::tryOpenFile(const char* name) {
debugC(3, kDebugDisk, "AmigaDisk_ns::tryOpenFile(%s)", name);
PowerPackerStream *ret;
Common::SeekableReadStream *stream = _sset.createReadStreamForMember(name);
if (stream)
return stream;
char path[PATH_LEN];
sprintf(path, "%s.pp", name);
stream = _sset.createReadStreamForMember(path);
if (stream) {
ret = new PowerPackerStream(*stream);
delete stream;
return ret;
}
sprintf(path, "%s.dd", name);
stream = _sset.createReadStreamForMember(path);
if (stream) {
ret = new PowerPackerStream(*stream);
delete stream;
return ret;
}
return 0;
}
/*
FIXME: mask values are not computed correctly for level 1 and 2
NOTE: this routine is only able to build masks for Nippon Safes, since mask widths are hardcoded
into the main loop.
*/
void AmigaDisk_ns::buildMask(byte* buf) {
byte mask1[16] = { 0, 0x80, 0x20, 0xA0, 8, 0x88, 0x28, 0xA8, 2, 0x82, 0x22, 0xA2, 0xA, 0x8A, 0x2A, 0xAA };
byte mask0[16] = { 0, 0x40, 0x10, 0x50, 4, 0x44, 0x14, 0x54, 1, 0x41, 0x11, 0x51, 0x5, 0x45, 0x15, 0x55 };
byte plane0[40];
byte plane1[40];
for (int32 i = 0; i < _vm->_screenHeight; i++) {
memcpy(plane0, buf, 40);
memcpy(plane1, buf+40, 40);
for (uint32 j = 0; j < 40; j++) {
*buf++ = mask0[(plane0[j] & 0xF0) >> 4] | mask1[(plane1[j] & 0xF0) >> 4];
*buf++ = mask0[plane0[j] & 0xF] | mask1[plane1[j] & 0xF];
}
}
}
void AmigaDisk_ns::loadBackground(BackgroundInfo& info, const char *name) {
Common::SeekableReadStream *s = openFile(name);
Image::IFFDecoder decoder;
decoder.loadStream(*s);
info.bg.copyFrom(*decoder.getSurface());
info.width = info.bg.w;
info.height = info.bg.h;
const byte *p = decoder.getPalette();
for (uint i = 0; i < 32; i++) {
byte r = *p >> 2;
p++;
byte g = *p >> 2;
p++;
byte b = *p >> 2;
p++;
info.palette.setEntry(i, r, g, b);
}
const Common::Array<Image::IFFDecoder::PaletteRange> &paletteRanges = decoder.getPaletteRanges();
for (uint j = 0; j < 6 && j < paletteRanges.size(); j++) {
PaletteFxRange range;
range._timer = paletteRanges[j].timer;
range._step = paletteRanges[j].step;
range._flags = paletteRanges[j].flags;
range._first = paletteRanges[j].first;
range._last = paletteRanges[j].last;
info.setPaletteRange(j, range);
}
}
void AmigaDisk_ns::loadMask_internal(BackgroundInfo& info, const char *name) {
debugC(5, kDebugDisk, "AmigaDisk_ns::loadMask_internal(%s)", name);
char path[PATH_LEN];
sprintf(path, "%s.mask", name);
Common::SeekableReadStream *s = tryOpenFile(path);
if (!s) {
debugC(5, kDebugDisk, "Mask file not found");
return; // no errors if missing mask files: not every location has one
}
Image::IFFDecoder decoder;
decoder.setNumRelevantPlanes(2); // use only 2 first bits from each pixel
decoder.setPixelPacking(true); // pack 4 2bit pixels into 1 byte
decoder.loadStream(*s);
const byte *p = decoder.getPalette();
byte r, g, b;
for (uint i = 0; i < 4; i++) {
r = p[i*3];
g = p[i*3+1];
b = p[i*3+2];
info.layers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF;
}
info._mask = new MaskBuffer;
// surface width was shrunk to 1/4th of the bitmap width due to the pixel packing
info._mask->create(decoder.getSurface()->w * 4, decoder.getSurface()->h);
memcpy(info._mask->data, decoder.getSurface()->getPixels(), info._mask->size);
info._mask->bigEndian = true;
}
void AmigaDisk_ns::loadPath_internal(BackgroundInfo& info, const char *name) {
char path[PATH_LEN];
sprintf(path, "%s.path", name);
Common::SeekableReadStream *s = tryOpenFile(path);
if (!s) {
return; // no errors if missing path files: not every location has one
}
Image::IFFDecoder decoder;
decoder.setNumRelevantPlanes(1); // use only first bit from each pixel
decoder.setPixelPacking(true); // pack 8 1bit pixels into 1 byte
decoder.loadStream(*s);
info._path = new PathBuffer;
// surface width was shrunk to 1/8th of the bitmap width due to the pixel packing
info._path->create(decoder.getSurface()->w * 8, decoder.getSurface()->h);
memcpy(info._path->data, decoder.getSurface()->getPixels(), info._path->size);
info._path->bigEndian = true;
}
void AmigaDisk_ns::loadScenery(BackgroundInfo& info, const char* background, const char* mask, const char* path) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadScenery '%s', '%s'", background, mask);
char filename[PATH_LEN];
sprintf(filename, "%s.bkgnd", background);
loadBackground(info, filename);
if (mask == 0) {
loadMask_internal(info, background);
loadPath_internal(info, background);
} else {
loadMask_internal(info, mask);
loadPath_internal(info, mask);
}
return;
}
void AmigaDisk_ns::loadSlide(BackgroundInfo& info, const char *name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadSlide '%s'", name);
loadBackground(info, name);
return;
}
Frames* AmigaDisk_ns::loadFrames(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadFrames '%s'", name);
char path[PATH_LEN];
sprintf(path, "anims/%s", name);
Common::SeekableReadStream *s = tryOpenFile(path);
if (!s)
s = openFile(name);
return makeCnv(s);
}
GfxObj* AmigaDisk_ns::loadHead(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadHead '%s'", name);
char path[PATH_LEN];
sprintf(path, "%s.head", name);
Common::SeekableReadStream *s = openFile(path);
return new GfxObj(0, makeCnv(s), name);
}
GfxObj* AmigaDisk_ns::loadObjects(const char *name, uint8 part) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadObjects");
char path[PATH_LEN];
if (_vm->getFeatures() & GF_DEMO)
sprintf(path, "%s.objs", name);
else
sprintf(path, "objs/%s.objs", name);
Common::SeekableReadStream *s = openFile(path);
return new GfxObj(0, makeCnv(s), name);
}
GfxObj* AmigaDisk_ns::loadTalk(const char *name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadTalk '%s'", name);
char path[PATH_LEN];
if (_vm->getFeatures() & GF_DEMO)
sprintf(path, "%s.talk", name);
else
sprintf(path, "talk/%s.talk", name);
Common::SeekableReadStream *s = tryOpenFile(path);
if (!s) {
s = openFile(name);
}
return new GfxObj(0, makeCnv(s), name);
}
Table* AmigaDisk_ns::loadTable(const char* name) {
debugC(1, kDebugDisk, "AmigaDisk_ns::loadTable '%s'", name);
char path[PATH_LEN];
if (!scumm_stricmp(name, "global")) {
sprintf(path, "%s.table", name);
} else {
if (!(_vm->getFeatures() & GF_DEMO))
sprintf(path, "objs/%s.table", name);
else
sprintf(path, "%s.table", name);
}
return createTableFromStream(100, openFile(path));
}
Font* AmigaDisk_ns::loadFont(const char* name) {
debugC(1, kDebugDisk, "AmigaFullDisk::loadFont '%s'", name);
char path[PATH_LEN];
sprintf(path, "%sfont", name);
Common::SeekableReadStream *stream = openFile(path);
Font *font = createFont(name, *stream);
delete stream;
return font;
}
Common::SeekableReadStream* AmigaDisk_ns::loadMusic(const char* name) {
return tryOpenFile(name);
}
Common::SeekableReadStream* AmigaDisk_ns::loadSound(const char* name) {
char path[PATH_LEN];
sprintf(path, "%s.snd", name);
return tryOpenFile(path);
}
} // End of namespace Parallaction