aboutsummaryrefslogtreecommitdiff
path: root/engines/parallaction/disk_ns.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/parallaction/disk_ns.cpp')
-rw-r--r--engines/parallaction/disk_ns.cpp1453
1 files changed, 1453 insertions, 0 deletions
diff --git a/engines/parallaction/disk_ns.cpp b/engines/parallaction/disk_ns.cpp
new file mode 100644
index 0000000000..c0bb2691ef
--- /dev/null
+++ b/engines/parallaction/disk_ns.cpp
@@ -0,0 +1,1453 @@
+/* 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 "common/stdafx.h"
+
+#include "graphics/iff.h"
+#include "graphics/surface.h"
+
+#include "parallaction/parallaction.h"
+
+
+namespace Audio {
+ class AudioStream;
+
+ AudioStream *make8SVXStream(Common::ReadStream &input);
+}
+
+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
+
+Archive::Archive() {
+ resetArchivedFile();
+}
+
+void Archive::open(const char *file) {
+ debugC(1, kDebugDisk, "Archive::open(%s)", file);
+
+ if (_archive.isOpen())
+ close();
+
+ char path[PATH_LEN];
+
+ strcpy(path, file);
+ if (!_archive.open(path))
+ error("archive '%s' not found", path);
+
+ _archiveName = file;
+
+ bool isSmallArchive = false;
+ if (_vm->getPlatform() == Common::kPlatformAmiga) {
+ if (_vm->getFeatures() & GF_DEMO) {
+ isSmallArchive = _archive.size() == SIZEOF_SMALL_ARCHIVE;
+ } else if (_vm->getFeatures() & GF_LANG_MULT) {
+ isSmallArchive = (_archive.readUint32BE() != MKID_BE('NDOS'));
+ }
+ }
+
+ _numFiles = (isSmallArchive) ? SMALL_ARCHIVE_FILES_NUM : NORMAL_ARCHIVE_FILES_NUM;
+
+ _archive.seek(ARCHIVE_FILENAMES_OFS);
+ _archive.read(_archiveDir, _numFiles*32);
+
+ _archive.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;
+ _archiveLenghts[i] = _archive.readUint32BE();
+ dataOffset += _archiveLenghts[i];
+ }
+
+ return;
+}
+
+
+void Archive::close() {
+ if (!_archive.isOpen()) return;
+
+ resetArchivedFile();
+
+ _archive.close();
+ _archiveName.clear();
+}
+
+Common::String Archive::name() const {
+ return _archiveName;
+}
+
+bool Archive::openArchivedFile(const char *filename) {
+ debugC(3, kDebugDisk, "Archive::openArchivedFile(%s)", filename);
+
+ resetArchivedFile();
+
+ debugC(3, kDebugDisk, "Archive::openArchivedFile(%s)", filename);
+
+ if (!_archive.isOpen())
+ error("Archive::openArchivedFile: the archive is not open");
+
+ uint16 i = 0;
+ for ( ; i < _numFiles; i++) {
+ if (!scumm_stricmp(_archiveDir[i], filename)) break;
+ }
+ if (i == _numFiles) return false;
+
+ debugC(9, kDebugDisk, "Archive::openArchivedFile: '%s' found in slot %i", filename, i);
+
+ _file = true;
+
+ _fileOffset = _archiveOffsets[i];
+ _fileCursor = _archiveOffsets[i];
+ _fileEndOffset = _archiveOffsets[i] + _archiveLenghts[i];
+
+ _archive.seek(_fileOffset);
+
+ return true;
+}
+
+void Archive::resetArchivedFile() {
+ _file = false;
+ _fileCursor = 0;
+ _fileOffset = 0;
+ _fileEndOffset = 0;
+}
+
+void Archive::closeArchivedFile() {
+ resetArchivedFile();
+}
+
+
+uint32 Archive::size() const {
+ return (_file == true ? _fileEndOffset - _fileOffset : 0);
+}
+
+uint32 Archive::pos() const {
+ return (_file == true ? _fileCursor - _fileOffset : 0 );
+}
+
+bool Archive::eos() const {
+ return (_file == true ? _fileCursor == _fileEndOffset : true );
+}
+
+void Archive::seek(int32 offs, int whence) {
+ assert(_file == true && _fileCursor <= _fileEndOffset);
+
+ switch (whence) {
+ case SEEK_CUR:
+ _fileCursor += offs;
+ break;
+ case SEEK_SET:
+ _fileCursor = _fileOffset + offs;
+ break;
+ case SEEK_END:
+ _fileCursor = _fileEndOffset - offs;
+ break;
+ }
+ assert(_fileCursor <= _fileEndOffset && _fileCursor >= _fileOffset);
+
+ _archive.seek(_fileCursor, SEEK_SET);
+}
+
+uint32 Archive::read(void *dataPtr, uint32 dataSize) {
+// printf("read(%i, %i)\n", file->_cursor, file->_endOffset);
+ if (_file == false)
+ error("Archive::read: no archived file is currently open");
+
+ if (_fileCursor >= _fileEndOffset)
+ error("can't read beyond end of archived file");
+
+ if (_fileEndOffset - _fileCursor < dataSize)
+ dataSize = _fileEndOffset - _fileCursor;
+
+ int32 readBytes = _archive.read(dataPtr, dataSize);
+ _fileCursor += readBytes;
+
+ return readBytes;
+}
+
+
+
+
+
+/*
+ This stream class is just a wrapper around Archive, so
+ deallocation is not a problem. In fact, this class doesn't
+ delete its input (Archive) stream.
+*/
+class DummyArchiveStream : public Common::SeekableReadStream {
+
+ Archive *_input;
+
+public:
+ DummyArchiveStream(Archive &input) : _input(&input) {
+
+ }
+
+ ~DummyArchiveStream() {
+ // this class exists to provide this empty destructor
+ }
+
+ bool eos() const {
+ return _input->eos();
+ }
+
+ uint32 read(void* data, uint32 dataSize) {
+ return _input->read(data, dataSize);
+ }
+
+ uint32 pos() const {
+ return _input->pos();
+ }
+
+ uint32 size() const {
+ return _input->size();
+ }
+
+ void seek(int32 offset, int whence) {
+ _input->seek(offset, whence);
+ }
+
+};
+
+
+
+void Disk_ns::errorFileNotFound(const char *s) {
+ error("File '%s' not found", s);
+}
+
+Disk_ns::Disk_ns(Parallaction *vm) : _vm(vm) {
+
+}
+
+Disk_ns::~Disk_ns() {
+
+}
+
+
+Common::String Disk_ns::selectArchive(const Common::String& name) {
+ Common::String oldName = _resArchive.name();
+ _resArchive.open(name.c_str());
+ return oldName;
+}
+
+void Disk_ns::setLanguage(uint16 language) {
+ debugC(1, kDebugDisk, "setLanguage(%i)", language);
+
+ switch (language) {
+ case 0:
+ strcpy(_languageDir, "it/");
+ break;
+
+ case 1:
+ strcpy(_languageDir, "fr/");
+ break;
+
+ case 2:
+ strcpy(_languageDir, "en/");
+ break;
+
+ case 3:
+ strcpy(_languageDir, "ge/");
+ break;
+
+ default:
+ error("unknown language");
+
+ }
+
+ _languageDir[2] = '\0';
+ _locArchive.open(_languageDir);
+ _languageDir[2] = '/';
+
+ return;
+}
+
+#pragma mark -
+
+
+
+DosDisk_ns::DosDisk_ns(Parallaction* vm) : Disk_ns(vm) {
+
+}
+
+DosDisk_ns::~DosDisk_ns() {
+}
+
+
+//
+// loads a cnv from an external file
+//
+Cnv* DosDisk_ns::loadExternalCnv(const char *filename) {
+// printf("Gfx::loadExternalCnv(%s)...", filename);
+
+ char path[PATH_LEN];
+
+ sprintf(path, "%s.cnv", filename);
+
+ Common::File stream;
+
+ if (!stream.open(path))
+ errorFileNotFound(path);
+
+ uint16 numFrames = stream.readByte();
+ uint16 width = stream.readByte();
+ uint16 height = stream.readByte();
+
+ uint32 decsize = numFrames * width * height;
+ byte *data = (byte*)malloc(decsize);
+ stream.read(data, decsize);
+
+ return new Cnv(numFrames, width, height, data);
+}
+
+StaticCnv *DosDisk_ns::loadExternalStaticCnv(const char *filename) {
+
+ char path[PATH_LEN];
+
+ sprintf(path, "%s.cnv", filename);
+
+ Common::File stream;
+
+ if (!stream.open(path))
+ errorFileNotFound(path);
+
+ StaticCnv *cnv = new StaticCnv;
+
+ stream.skip(1);
+ cnv->_width = stream.readByte();
+ cnv->_height = stream.readByte();
+
+ uint16 size = cnv->_width*cnv->_height;
+
+ cnv->_data0 = (byte*)malloc(size);
+ stream.read(cnv->_data0, size);
+
+ return cnv;
+}
+
+Cnv* DosDisk_ns::loadCnv(const char *filename) {
+// printf("Gfx::loadCnv(%s)\n", filename);
+
+ char path[PATH_LEN];
+
+ strcpy(path, filename);
+ if (!_resArchive.openArchivedFile(path)) {
+ sprintf(path, "%s.pp", filename);
+ if (!_resArchive.openArchivedFile(path))
+ errorFileNotFound(path);
+ }
+
+ uint16 numFrames = _resArchive.readByte();
+ uint16 width = _resArchive.readByte();
+ uint16 height = _resArchive.readByte();
+
+ uint32 decsize = numFrames * width * height;
+ byte *data = (byte*)malloc(decsize);
+
+ Graphics::PackBitsReadStream decoder(_resArchive);
+ decoder.read(data, decsize);
+
+ return new Cnv(numFrames, width, height, data);
+}
+
+Cnv* DosDisk_ns::loadTalk(const char *name) {
+
+ const char *ext = strstr(name, ".talk");
+ if (ext != NULL) {
+ // npc talk
+ return loadCnv(name);
+
+ }
+
+ // character talk
+ char v20[PATH_LEN];
+ char *v24 = const_cast<char*>(name);
+ if (IS_MINI_CHARACTER(v24)) {
+ v24+=4;
+ }
+
+ if (_engineFlags & kEngineTransformedDonna) {
+ sprintf(v20, "%stta", v24);
+ } else {
+ sprintf(v20, "%stal", v24);
+ }
+
+ return loadExternalCnv(v20);
+}
+
+Script* DosDisk_ns::loadLocation(const char *name) {
+
+ char archivefile[PATH_LEN];
+
+ if (IS_MINI_CHARACTER(_vm->_characterName)) {
+ sprintf(archivefile, "%s%s", _vm->_characterName+4, _languageDir);
+ } else {
+ if (IS_DUMMY_CHARACTER(_vm->_characterName)) {
+ strcpy(archivefile, _languageDir);
+ } else {
+ sprintf(archivefile, "%s%s", _vm->_characterName, _languageDir);
+ }
+ }
+
+ strcat(archivefile, name);
+ strcat(archivefile, ".loc");
+
+ debugC(3, kDebugDisk, "DosDisk_ns::loadLocation(%s): trying '%s'", name, archivefile);
+
+ if (!_locArchive.openArchivedFile(archivefile)) {
+ sprintf(archivefile, "%s%s.loc", _languageDir, name);
+ debugC(3, kDebugDisk, "DosDisk_ns::loadLocation(%s): trying '%s'", name, archivefile);
+
+ if (!_locArchive.openArchivedFile(archivefile))
+ errorFileNotFound(name);
+ }
+
+ return new Script(new DummyArchiveStream(_locArchive), true);
+}
+
+Script* DosDisk_ns::loadScript(const char* name) {
+
+ char vC8[PATH_LEN];
+
+ sprintf(vC8, "%s.script", name);
+
+ if (!_resArchive.openArchivedFile(vC8))
+ errorFileNotFound(vC8);
+
+ return new Script(new DummyArchiveStream(_resArchive), true);
+}
+
+StaticCnv* DosDisk_ns::loadHead(const char* name) {
+
+ char path[PATH_LEN];
+
+ if (IS_MINI_CHARACTER(name)) {
+ name += 4;
+ }
+
+ sprintf(path, "%shead", name);
+ path[8] = '\0';
+
+ return loadExternalStaticCnv(path);
+}
+
+
+StaticCnv* DosDisk_ns::loadPointer() {
+ return loadExternalStaticCnv("pointer");
+}
+
+
+Font* DosDisk_ns::loadFont(const char* name) {
+ char path[PATH_LEN];
+ sprintf(path, "%scnv", name);
+ return createFont(name, loadExternalCnv(path));
+}
+
+
+Cnv* DosDisk_ns::loadObjects(const char *name) {
+
+ if (IS_MINI_CHARACTER(name)) {
+ name += 4;
+ }
+
+ char path[PATH_LEN];
+ sprintf(path, "%sobj", name);
+ return loadExternalCnv(path);
+}
+
+
+StaticCnv* DosDisk_ns::loadStatic(const char* name) {
+
+ char path[PATH_LEN];
+
+ strcpy(path, name);
+ if (!_resArchive.openArchivedFile(path)) {
+ sprintf(path, "%s.pp", name);
+ if (!_resArchive.openArchivedFile(path))
+ errorFileNotFound(path);
+ }
+
+ StaticCnv* cnv = new StaticCnv;
+
+ _resArchive.skip(1);
+ cnv->_width = _resArchive.readByte();
+ cnv->_height = _resArchive.readByte();
+
+ uint16 size = cnv->_width*cnv->_height;
+ cnv->_data0 = (byte*)malloc(size);
+
+ Graphics::PackBitsReadStream decoder(_resArchive);
+ decoder.read(cnv->_data0, size);
+
+ return cnv;
+}
+
+Cnv* DosDisk_ns::loadFrames(const char* name) {
+ return loadCnv(name);
+}
+
+//
+// slides (background images) are stored compressed by scanline in a rle fashion
+//
+// the uncompressed data must then be unpacked to get:
+// * 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 b;
+ uint32 i = 0;
+
+ while (!stream->eos()) {
+ b = stream->readByte();
+
+ path[i/8] |= ((b & 0x80) >> 7) << (i & 7);
+ mask[i/4] |= ((b & 0x60) >> 5) << ((i & 3) << 1);
+ screen[i] = b & 0x1F;
+ i++;
+ }
+
+ return;
+}
+
+
+void DosDisk_ns::parseDepths(Common::SeekableReadStream &stream) {
+ _vm->_gfx->_bgLayers[0] = stream.readByte();
+ _vm->_gfx->_bgLayers[1] = stream.readByte();
+ _vm->_gfx->_bgLayers[2] = stream.readByte();
+ _vm->_gfx->_bgLayers[3] = stream.readByte();
+}
+
+
+void DosDisk_ns::parseBackground(Common::SeekableReadStream &stream) {
+
+ stream.read(_vm->_gfx->_palette, BASE_PALETTE_SIZE);
+ _vm->_gfx->setPalette(_vm->_gfx->_palette);
+
+ parseDepths(stream);
+
+ for (uint32 _si = 0; _si < 6; _si++) {
+ _vm->_gfx->_palettefx[_si]._timer = stream.readUint16BE();
+ _vm->_gfx->_palettefx[_si]._step = stream.readUint16BE();
+ _vm->_gfx->_palettefx[_si]._flags = stream.readUint16BE();
+ _vm->_gfx->_palettefx[_si]._first = stream.readByte();
+ _vm->_gfx->_palettefx[_si]._last = stream.readByte();
+ }
+
+}
+
+void DosDisk_ns::loadBackground(const char *filename) {
+
+ if (!_resArchive.openArchivedFile(filename))
+ errorFileNotFound(filename);
+
+ parseBackground(_resArchive);
+
+ byte *bg = (byte*)calloc(1, _vm->_screenSize);
+ byte *mask = (byte*)calloc(1, _vm->_screenMaskSize);
+ byte *path = (byte*)calloc(1, _vm->_screenPathSize);
+
+
+ Graphics::PackBitsReadStream stream(_resArchive);
+ unpackBackground(&stream, bg, mask, path);
+
+ _vm->_gfx->setBackground(bg);
+ _vm->_gfx->setMask(mask);
+ _vm->setPath(path);
+
+ free(bg);
+ free(mask);
+ free(path);
+
+ return;
+}
+
+//
+// read background path and mask from a file
+//
+// mask and path are normally combined (via OR) into the background picture itself
+// read the comment on the top of this file for more
+//
+void DosDisk_ns::loadMaskAndPath(const char *name) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.msk", name);
+
+ if (!_resArchive.openArchivedFile(path))
+ errorFileNotFound(name);
+
+ byte *maskBuf = (byte*)calloc(1, _vm->_screenMaskSize);
+ byte *pathBuf = (byte*)calloc(1, _vm->_screenPathSize);
+
+ parseDepths(_resArchive);
+
+ _resArchive.read(pathBuf, _vm->_screenPathSize);
+ _resArchive.read(maskBuf, _vm->_screenMaskSize);
+
+ _vm->_gfx->setMask(maskBuf);
+ _vm->setPath(pathBuf);
+
+ return;
+}
+
+void DosDisk_ns::loadSlide(const char *filename) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.slide", filename);
+ loadBackground(path);
+}
+
+void DosDisk_ns::loadScenery(const char *name, const char *mask) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.dyn", name);
+ loadBackground(path);
+
+ if (mask != NULL) {
+ // load external masks and paths only for certain locations
+ loadMaskAndPath(mask);
+ }
+
+}
+
+Table* DosDisk_ns::loadTable(const char* name) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.tab", name);
+
+ Common::File stream;
+ if (!stream.open(path))
+ errorFileNotFound(path);
+
+ Table *t = new Table(100);
+
+ fillBuffers(stream);
+ while (scumm_stricmp(_tokens[0], "ENDTABLE")) {
+ t->addData(_tokens[0]);
+ fillBuffers(stream);
+ }
+
+ stream.close();
+
+ return t;
+}
+
+Common::SeekableReadStream* DosDisk_ns::loadMusic(const char* name) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.mid", name);
+
+ Common::File *stream = new Common::File;
+ if (!stream->open(path))
+ errorFileNotFound(path);
+
+ return stream;
+}
+
+
+Common::ReadStream* DosDisk_ns::loadSound(const char* name) {
+ return NULL;
+}
+
+
+
+
+
+
+#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 == NULL || dest == NULL) 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;
+
+ switch (signature) {
+ case 0x50503230: /* PP20 */
+ eff = 4;
+ break;
+ case 0x50504C53: /* PPLS */
+ error("PPLS crunched files are not supported");
+ eff = 8;
+ break;
+ case 0x50583230: /* PX20 */
+ error("PX20 crunched files are not supported");
+ eff = 6;
+ break;
+ 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, true);
+ _dispose = true;
+ }
+
+ ~PowerPackerStream() {
+ if (_dispose) delete _stream;
+ }
+
+ uint32 size() const {
+ return _stream->size();
+ }
+
+ uint32 pos() const {
+ return _stream->pos();
+ }
+
+ bool eos() const {
+ return _stream->eos();
+ }
+
+ void seek(int32 offs, int whence = SEEK_SET) {
+ _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() {
+
+}
+
+#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) == MKID_BE('DLTA')) {
+
+ 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;
+ }
+ }
+
+ if (tempBuffer)
+ free(tempBuffer);
+
+}
+
+StaticCnv* AmigaDisk_ns::makeStaticCnv(Common::SeekableReadStream &stream) {
+
+ stream.skip(1);
+ uint16 width = stream.readByte();
+ uint16 height = stream.readByte();
+
+ assert((width & 7) == 0);
+
+ byte bytesPerPlane = width / 8;
+
+ uint32 rawsize = bytesPerPlane * NUM_PLANES * height;
+ byte *buf = (byte*)malloc(rawsize);
+ stream.read(buf, rawsize);
+
+ uint32 decsize = width * height;
+ byte *data = (byte*)calloc(decsize, 1);
+
+ unpackBitmap(data, buf, 1, bytesPerPlane, height);
+
+ free(buf);
+
+ StaticCnv *cnv = new StaticCnv();
+ cnv->_width = width;
+ cnv->_height = height;
+ cnv->_data0 = data;
+ cnv->_data1 = NULL;
+
+ return cnv;
+}
+
+Cnv* AmigaDisk_ns::makeCnv(Common::SeekableReadStream &stream) {
+
+ uint16 numFrames = stream.readByte();
+ uint16 width = stream.readByte();
+ uint16 height = stream.readByte();
+
+ assert((width & 7) == 0);
+
+ byte bytesPerPlane = width / 8;
+
+ uint32 rawsize = numFrames * bytesPerPlane * NUM_PLANES * height;
+ byte *buf = (byte*)malloc(rawsize);
+ stream.read(buf, rawsize);
+
+ uint32 decsize = numFrames * width * height;
+ byte *data = (byte*)calloc(decsize, 1);
+
+ unpackBitmap(data, buf, numFrames, bytesPerPlane, height);
+
+ free(buf);
+
+ return new Cnv(numFrames, width, height, data);
+}
+#undef NUM_PLANES
+
+Script* AmigaDisk_ns::loadLocation(const char *name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns()::loadLocation '%s'", name);
+
+ char path[PATH_LEN];
+ if (IS_MINI_CHARACTER(_vm->_characterName)) {
+ sprintf(path, "%s%s%s.loc.pp", _vm->_characterName+4, _languageDir, name);
+ } else
+ sprintf(path, "%s%s%s.loc.pp", _vm->_characterName, _languageDir, name);
+
+ if (!_locArchive.openArchivedFile(path)) {
+ sprintf(path, "%s%s.loc.pp", _languageDir, name);
+ if (!_locArchive.openArchivedFile(path)) {
+ errorFileNotFound(name);
+ }
+ }
+
+ debugC(3, kDebugDisk, "location file found: %s", path);
+
+ return new Script(new PowerPackerStream(_locArchive), true);
+}
+
+Script* AmigaDisk_ns::loadScript(const char* name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadScript '%s'", name);
+
+ char vC8[PATH_LEN];
+
+ sprintf(vC8, "%s.script", name);
+
+ if (!_resArchive.openArchivedFile(vC8))
+ errorFileNotFound(vC8);
+
+ return new Script(new DummyArchiveStream(_resArchive), true);
+}
+
+StaticCnv* AmigaDisk_ns::loadPointer() {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadPointer");
+
+ Common::File stream;
+ if (!stream.open("pointer"))
+ errorFileNotFound("pointer");
+
+ return makeStaticCnv(stream);
+}
+
+StaticCnv* AmigaDisk_ns::loadStatic(const char* name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadStatic '%s'", name);
+
+ Common::SeekableReadStream *s = openArchivedFile(name, true);
+ StaticCnv *cnv = makeStaticCnv(*s);
+
+ delete s;
+
+ return cnv;
+}
+
+Common::SeekableReadStream *AmigaDisk_ns::openArchivedFile(const char* name, bool errorOnFileNotFound) {
+ debugC(3, kDebugDisk, "AmigaDisk_ns::openArchivedFile(%s)", name);
+
+ if (_resArchive.openArchivedFile(name)) {
+ return new DummyArchiveStream(_resArchive);
+ }
+
+ char path[PATH_LEN];
+
+ sprintf(path, "%s.pp", name);
+ if (_resArchive.openArchivedFile(path)) {
+ return new PowerPackerStream(_resArchive);
+ }
+
+ sprintf(path, "%s.dd", name);
+ if (_resArchive.openArchivedFile(path)) {
+ return new PowerPackerStream(_resArchive);
+ }
+
+ if (errorOnFileNotFound)
+ errorFileNotFound(name);
+
+ return NULL;
+}
+
+/*
+ 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 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];
+ }
+
+ }
+}
+
+class BackgroundDecoder : public Graphics::ILBMDecoder {
+
+ PaletteFxRange *_range;
+ uint32 _i;
+
+protected:
+ void readCRNG(Common::IFFChunk &chunk) {
+ _range[_i]._timer = chunk.readUint16BE();
+ _range[_i]._step = chunk.readUint16BE();
+ _range[_i]._flags = chunk.readUint16BE();
+ _range[_i]._first = chunk.readByte();
+ _range[_i]._last = chunk.readByte();
+
+ _i++;
+ }
+
+public:
+ BackgroundDecoder(Common::ReadStream &input, Graphics::Surface &surface, byte *&colors, PaletteFxRange *range) :
+ Graphics::ILBMDecoder(input, surface, colors), _range(range), _i(0) {
+ }
+
+ void decode() {
+ Common::IFFChunk *chunk;
+ while ((chunk = nextChunk()) != 0) {
+ switch (chunk->id) {
+ case ID_BMHD:
+ readBMHD(*chunk);
+ break;
+
+ case ID_CMAP:
+ readCMAP(*chunk);
+ break;
+
+ case ID_BODY:
+ readBODY(*chunk);
+ break;
+
+ case ID_CRNG:
+ readCRNG(*chunk);
+ break;
+ }
+ }
+ }
+
+ uint32 getNumRanges() {
+ return _i;
+ }
+};
+
+
+void AmigaDisk_ns::loadBackground(const char *name) {
+
+ Common::SeekableReadStream *s = openArchivedFile(name, true);
+
+ Graphics::Surface surf;
+ byte *pal;
+ BackgroundDecoder decoder(*s, surf, pal, _vm->_gfx->_palettefx);
+ decoder.decode();
+
+ for (uint32 i = 0; i < BASE_PALETTE_COLORS * 3; i++)
+ _vm->_gfx->_palette[i] = pal[i] >> 2;
+ free(pal);
+ _vm->_gfx->setPalette(_vm->_gfx->_palette);
+ _vm->_gfx->setBackground(static_cast<byte*>(surf.pixels));
+ surf.free();
+ delete s;
+
+ return;
+
+}
+
+void AmigaDisk_ns::loadMask(const char *name) {
+ debugC(5, kDebugDisk, "AmigaDisk_ns::loadMask(%s)", name);
+
+ char path[PATH_LEN];
+ sprintf(path, "%s.mask", name);
+
+ Common::SeekableReadStream *s = openArchivedFile(path, false);
+ if (s == NULL) {
+ debugC(5, kDebugDisk, "Mask file not found");
+ return; // no errors if missing mask files: not every location has one
+ }
+
+ s->seek(0x30, SEEK_SET);
+
+ byte r, g, b;
+ for (uint i = 0; i < 4; i++) {
+ r = s->readByte();
+ g = s->readByte();
+ b = s->readByte();
+
+ _vm->_gfx->_bgLayers[i] = (((r << 4) & 0xF00) | (g & 0xF0) | (b >> 4)) & 0xFF;
+
+// printf("rgb = (%x, %x, %x) -> %x\n", r, g, b, _vm->_gfx->_bgLayers[i]);
+ }
+
+
+ s->seek(0x126, SEEK_SET); // HACK: skipping IFF/ILBM header should be done by analysis, not magic
+ Graphics::PackBitsReadStream stream(*s);
+
+ byte *buf = (byte*)malloc(_vm->_screenMaskSize);
+ stream.read(buf, _vm->_screenMaskSize);
+ buildMask(buf);
+
+ _vm->_gfx->setMask(buf);
+ free(buf);
+ delete s;
+
+ return;
+}
+
+void AmigaDisk_ns::loadPath(const char *name) {
+
+ char path[PATH_LEN];
+ sprintf(path, "%s.path", name);
+
+ Common::SeekableReadStream *s = openArchivedFile(path, false);
+ if (s == NULL)
+ return; // no errors if missing path files: not every location has one
+
+
+ s->seek(0x120, SEEK_SET); // HACK: skipping IFF/ILBM header should be done by analysis, not magic
+
+ Graphics::PackBitsReadStream stream(*s);
+ byte *buf = (byte*)malloc(_vm->_screenPathSize);
+ stream.read(buf, _vm->_screenPathSize);
+ _vm->setPath(buf);
+ free(buf);
+ delete s;
+
+ return;
+}
+
+void AmigaDisk_ns::loadScenery(const char* background, const char* mask) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadScenery '%s', '%s'", background, mask);
+
+ char path[PATH_LEN];
+ sprintf(path, "%s.bkgnd", background);
+
+ loadBackground(path);
+
+ if (mask == NULL) {
+ loadMask(background);
+ loadPath(background);
+ } else {
+ loadMask(mask);
+ loadPath(mask);
+ }
+
+ return;
+}
+
+void AmigaDisk_ns::loadSlide(const char *name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadSlide '%s'", name);
+
+ char path[PATH_LEN];
+ sprintf(path, "slides/%s", name);
+ Common::SeekableReadStream *s = openArchivedFile(path, false);
+ if (s)
+ loadBackground(path);
+ else
+ loadBackground(name);
+
+ return;
+}
+
+Cnv* AmigaDisk_ns::loadFrames(const char* name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadFrames '%s'", name);
+
+ Common::SeekableReadStream *s;
+
+ char path[PATH_LEN];
+ sprintf(path, "anims/%s", name);
+
+ s = openArchivedFile(path, false);
+ if (!s)
+ s = openArchivedFile(name, true);
+
+ Cnv *cnv = makeCnv(*s);
+ delete s;
+
+ return cnv;
+}
+
+StaticCnv* 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 = openArchivedFile(path, true);
+ StaticCnv *cnv = makeStaticCnv(*s);
+
+ delete s;
+
+ return cnv;
+}
+
+
+Cnv* AmigaDisk_ns::loadObjects(const char *name) {
+ 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 = openArchivedFile(path, true);
+
+ Cnv *cnv = makeCnv(*s);
+ delete s;
+
+ return cnv;
+}
+
+
+Cnv* AmigaDisk_ns::loadTalk(const char *name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadTalk '%s'", name);
+
+ Common::SeekableReadStream *s;
+
+ char path[PATH_LEN];
+ if (_vm->getFeatures() & GF_DEMO)
+ sprintf(path, "%s.talk", name);
+ else
+ sprintf(path, "talk/%s.talk", name);
+
+ s = openArchivedFile(path, false);
+ if (s == NULL) {
+ s = openArchivedFile(name, true);
+ }
+
+ Cnv *cnv = makeCnv(*s);
+ delete s;
+
+ return cnv;
+}
+
+Table* AmigaDisk_ns::loadTable(const char* name) {
+ debugC(1, kDebugDisk, "AmigaDisk_ns::loadTable '%s'", name);
+
+ char path[PATH_LEN];
+ sprintf(path, "%s.table", name);
+
+ bool dispose = false;
+
+ Common::SeekableReadStream *stream;
+
+ if (!scumm_stricmp(name, "global")) {
+ Common::File *s = new Common::File;
+ if (!s->open(path))
+ errorFileNotFound(path);
+
+ dispose = true;
+ stream = s;
+ } else {
+ if (!(_vm->getFeatures() & GF_DEMO))
+ sprintf(path, "objs/%s.table", name);
+ if (!_resArchive.openArchivedFile(path))
+ errorFileNotFound(path);
+
+ stream = &_resArchive;
+ }
+
+ Table *t = new Table(100);
+
+ fillBuffers(*stream);
+ while (scumm_stricmp(_tokens[0], "ENDTABLE")) {
+ t->addData(_tokens[0]);
+ fillBuffers(*stream);
+ }
+
+ if (dispose)
+ delete stream;
+
+ return t;
+}
+
+Font* AmigaDisk_ns::loadFont(const char* name) {
+ debugC(1, kDebugDisk, "AmigaFullDisk::loadFont '%s'", name);
+
+ char path[PATH_LEN];
+ sprintf(path, "%sfont", name);
+
+ if (_vm->getFeatures() & GF_LANG_IT) {
+ // Italian version has separate font files
+ Common::File stream;
+ if (!stream.open(path))
+ errorFileNotFound(path);
+
+ return createFont(name, stream);
+ } else {
+ if (!_resArchive.openArchivedFile(path))
+ errorFileNotFound(path);
+
+ return createFont(name, _resArchive);
+ }
+}
+
+
+Common::SeekableReadStream* AmigaDisk_ns::loadMusic(const char* name) {
+ return openArchivedFile(name);
+}
+
+Common::ReadStream* AmigaDisk_ns::loadSound(const char* name) {
+ char path[PATH_LEN];
+ sprintf(path, "%s.snd", name);
+
+ openArchivedFile(path);
+
+ return new DummyArchiveStream(_resArchive);
+}
+
+} // namespace Parallaction