aboutsummaryrefslogtreecommitdiff
path: root/engines/agi/loader_v3.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/agi/loader_v3.cpp')
-rw-r--r--engines/agi/loader_v3.cpp390
1 files changed, 390 insertions, 0 deletions
diff --git a/engines/agi/loader_v3.cpp b/engines/agi/loader_v3.cpp
new file mode 100644
index 0000000000..dc419a7afe
--- /dev/null
+++ b/engines/agi/loader_v3.cpp
@@ -0,0 +1,390 @@
+/* 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 "agi/agi.h"
+#include "agi/lzw.h"
+
+#include "common/config-manager.h"
+#include "common/fs.h"
+
+namespace Agi {
+
+int AgiLoader_v3::version() {
+ return 3;
+}
+
+void AgiLoader_v3::setIntVersion(int ver) {
+ _intVersion = ver;
+}
+
+int AgiLoader_v3::getIntVersion() {
+ return _intVersion;
+}
+
+int AgiLoader_v3::detectGame() {
+ int ec = errUnk;
+ bool found = false;
+
+ FSList fslist;
+ FilesystemNode dir(ConfMan.get("path"));
+
+ if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) {
+ warning("AgiEngine: invalid game path '%s'", dir.path().c_str());
+ return errInvalidAGIFile;
+ }
+
+ for (FSList::const_iterator file = fslist.begin();
+ file != fslist.end() && !found; ++file) {
+ Common::String f = file->name();
+ f.toLowercase();
+
+ if (f.hasSuffix("vol.0")) {
+ memset(_vm->_game.name, 0, 8);
+ strncpy(_vm->_game.name, f.c_str(), MIN((uint)8, f.size() > 5 ? f.size() - 5 : f.size()));
+ debugC(3, kDebugLevelMain, "game.name = %s", _vm->_game.name);
+ _intVersion = 0x3149; // setup for 3.002.149
+ ec = _vm->v3IdGame();
+
+ found = true;
+ }
+ }
+
+ if (!found) {
+ debugC(3, kDebugLevelMain, "not found");
+ ec = errInvalidAGIFile;
+ }
+
+ return ec;
+}
+
+int AgiLoader_v3::loadDir(struct AgiDir *agid, Common::File *fp,
+ uint32 offs, uint32 len) {
+ int ec = errOK;
+ uint8 *mem;
+ unsigned int i;
+
+ fp->seek(offs, SEEK_SET);
+ if ((mem = (uint8 *)malloc(len + 32)) != NULL) {
+ fp->read(mem, len);
+
+ /* set all directory resources to gone */
+ for (i = 0; i < MAX_DIRS; i++) {
+ agid[i].volume = 0xff;
+ agid[i].offset = _EMPTY;
+ }
+
+ /* build directory entries */
+ for (i = 0; i < len; i += 3) {
+ agid[i / 3].volume = *(mem + i) >> 4;
+ agid[i / 3].offset = READ_BE_UINT24(mem + i) & (uint32) _EMPTY;
+ }
+
+ free(mem);
+ } else {
+ ec = errNotEnoughMemory;
+ }
+
+ return ec;
+}
+
+struct agi3vol {
+ uint32 sddr;
+ uint32 len;
+};
+
+int AgiLoader_v3::init() {
+ int ec = errOK;
+ struct agi3vol agiVol3[4];
+ int i;
+ uint16 xd[4];
+ Common::File fp;
+ Common::String path;
+
+ if (_vm->getPlatform() == Common::kPlatformAmiga) {
+ path = Common::String("dirs");
+ _vm->_game.name[0] = 0; // Empty prefix
+ } else if (_vm->getFeatures() & GF_MACGOLDRUSH) {
+ path = "grdir";
+ _vm->_game.name[0] = 0; // Empty prefix
+ } else {
+ path = Common::String(_vm->_game.name) + DIR_;
+ }
+
+ if (!fp.open(path)) {
+ printf("Failed to open \"%s\"\n", path.c_str());
+ return errBadFileOpen;
+ }
+ /* build offset table for v3 directory format */
+ fp.read(&xd, 8);
+ fp.seek(0, SEEK_END);
+
+ for (i = 0; i < 4; i++)
+ agiVol3[i].sddr = READ_LE_UINT16((uint8 *) & xd[i]);
+
+ agiVol3[0].len = agiVol3[1].sddr - agiVol3[0].sddr;
+ agiVol3[1].len = agiVol3[2].sddr - agiVol3[1].sddr;
+ agiVol3[2].len = agiVol3[3].sddr - agiVol3[2].sddr;
+ agiVol3[3].len = fp.pos() - agiVol3[3].sddr;
+
+ if (agiVol3[3].len > 256 * 3)
+ agiVol3[3].len = 256 * 3;
+
+ fp.seek(0, SEEK_SET);
+
+ /* read in directory files */
+ ec = loadDir(_vm->_game.dirLogic, &fp, agiVol3[0].sddr, agiVol3[0].len);
+
+ if (ec == errOK) {
+ ec = loadDir(_vm->_game.dirPic, &fp, agiVol3[1].sddr, agiVol3[1].len);
+ }
+
+ if (ec == errOK) {
+ ec = loadDir(_vm->_game.dirView, &fp, agiVol3[2].sddr, agiVol3[2].len);
+ }
+
+ if (ec == errOK) {
+ ec = loadDir(_vm->_game.dirSound, &fp, agiVol3[3].sddr, agiVol3[3].len);
+ }
+
+ return ec;
+}
+
+int AgiLoader_v3::deinit() {
+ int ec = errOK;
+
+#if 0
+ /* unload words */
+ agiV3UnloadWords();
+
+ /* unload objects */
+ agiV3UnloadObjects();
+#endif
+
+ return ec;
+}
+
+int AgiLoader_v3::unloadResource(int t, int n) {
+ switch (t) {
+ case rLOGIC:
+ _vm->unloadLogic(n);
+ break;
+ case rPICTURE:
+ _vm->_picture->unloadPicture(n);
+ break;
+ case rVIEW:
+ _vm->unloadView(n);
+ break;
+ case rSOUND:
+ _vm->_sound->unloadSound(n);
+ break;
+ }
+
+ return errOK;
+}
+
+/*
+ * This function does noting but load a raw resource into memory.
+ * If further decoding is required, it must be done by another
+ * routine.
+ *
+ * NULL is returned if unsucsessful.
+ */
+uint8 *AgiLoader_v3::loadVolRes(AgiDir *agid) {
+ char x[MAX_PATH];
+ uint8 *data = NULL, *compBuffer;
+ Common::File fp;
+ Common::String path;
+
+ debugC(3, kDebugLevelResources, "(%p)", (void *)agid);
+ sprintf(x, "vol.%i", agid->volume);
+ path = Common::String(_vm->_game.name) + x;
+
+ if (agid->offset != _EMPTY && fp.open(path)) {
+ fp.seek(agid->offset, SEEK_SET);
+ fp.read(&x, 7);
+
+ if (READ_BE_UINT16((uint8 *) x) != 0x1234) {
+#if 0
+ /* FIXME */
+ deinitVideoMode();
+#endif
+ debugC(3, kDebugLevelResources, "path = %s", path.c_str());
+ debugC(3, kDebugLevelResources, "offset = %d", agid->offset);
+ debugC(3, kDebugLevelResources, "x = %x %x", x[0], x[1]);
+ error("ACK! BAD RESOURCE");
+
+ g_system->quit();
+ }
+
+ agid->len = READ_LE_UINT16((uint8 *) x + 3); /* uncompressed size */
+ agid->clen = READ_LE_UINT16((uint8 *) x + 5); /* compressed len */
+
+ compBuffer = (uint8 *)calloc(1, agid->clen + 32);
+ fp.read(compBuffer, agid->clen);
+
+ if (x[2] & 0x80 || agid->len == agid->clen) {
+ /* do not decompress */
+ data = compBuffer;
+
+#if 0
+ /* CM: added to avoid problems in
+ * convert_v2_v3_pic() when clen > len
+ * e.g. Sierra demo 4, first picture
+ * (Tue Mar 16 13:13:43 EST 1999)
+ */
+ agid->len = agid->clen;
+
+ /* Now removed to fix Gold Rush! in demo4 */
+#endif
+ } else {
+ /* it is compressed */
+ data = (uint8 *)calloc(1, agid->len + 32);
+ lzwExpand(compBuffer, data, agid->len);
+ free(compBuffer);
+ agid->flags |= RES_COMPRESSED;
+ }
+
+ fp.close();
+ } else {
+ /* we have a bad volume resource */
+ /* set that resource to NA */
+ agid->offset = _EMPTY;
+ }
+
+ return data;
+}
+
+/*
+ * Loads a resource into memory, a raw resource is loaded in
+ * with above routine, then further decoded here.
+ */
+int AgiLoader_v3::loadResource(int t, int n) {
+ int ec = errOK;
+ uint8 *data = NULL;
+
+ if (n > MAX_DIRS)
+ return errBadResource;
+
+ switch (t) {
+ case rLOGIC:
+ /* load resource into memory, decrypt messages at the end
+ * and build the message list (if logic is in memory)
+ */
+ if (~_vm->_game.dirLogic[n].flags & RES_LOADED) {
+ /* if logic is already in memory, unload it */
+ unloadResource(rLOGIC, n);
+
+ /* load raw resource into data */
+ data = loadVolRes(&_vm->_game.dirLogic[n]);
+ _vm->_game.logics[n].data = data;
+
+ /* uncompressed logic files need to be decrypted */
+ if (data != NULL) {
+ /* resloaded flag gets set by decode logic */
+ /* needed to build string table */
+ ec = _vm->decodeLogic(n);
+ _vm->_game.logics[n].sIP = 2;
+ } else {
+ ec = errBadResource;
+ }
+
+ /*logics[n].sIP=2; *//* saved IP = 2 */
+ /*logics[n].cIP=2; *//* current IP = 2 */
+
+ _vm->_game.logics[n].cIP = _vm->_game.logics[n].sIP;
+ }
+
+ /* if logic was cached, we get here */
+ /* reset code pointers incase it was cached */
+
+ _vm->_game.logics[n].cIP = _vm->_game.logics[n].sIP;
+ break;
+ case rPICTURE:
+ /* if picture is currently NOT loaded *OR* cacheing is off,
+ * unload the resource (caching==off) and reload it
+ */
+ if (~_vm->_game.dirPic[n].flags & RES_LOADED) {
+ unloadResource(rPICTURE, n);
+ data = loadVolRes(&_vm->_game.dirPic[n]);
+ if (data != NULL) {
+ data = _vm->_picture->convertV3Pic(data, _vm->_game.dirPic[n].len);
+ _vm->_game.pictures[n].rdata = data;
+ _vm->_game.dirPic[n].flags |= RES_LOADED;
+ } else {
+ ec = errBadResource;
+ }
+ }
+ break;
+ case rSOUND:
+ if (_vm->_game.dirSound[n].flags & RES_LOADED)
+ break;
+
+ data = loadVolRes(&_vm->_game.dirSound[n]);
+ if (data != NULL) {
+ // Freeing of the raw resource from memory is delegated to the createFromRawResource-function
+ _vm->_game.sounds[n] = AgiSound::createFromRawResource(data, _vm->_game.dirSound[n].len, n, *_vm->_sound);
+ _vm->_game.dirSound[n].flags |= RES_LOADED;
+ } else {
+ ec = errBadResource;
+ }
+ break;
+ case rVIEW:
+ /* Load a VIEW resource into memory...
+ * Since VIEWS alter the view table ALL the time can we
+ * cache the view? or must we reload it all the time?
+ */
+ /* load a raw view from a VOL file into data */
+ if (_vm->_game.dirView[n].flags & RES_LOADED)
+ break;
+
+ unloadResource(rVIEW, n);
+ data = loadVolRes(&_vm->_game.dirView[n]);
+ if (data != NULL) {
+ _vm->_game.views[n].rdata = data;
+ _vm->_game.dirView[n].flags |= RES_LOADED;
+ ec = _vm->decodeView(n);
+ } else {
+ ec = errBadResource;
+ }
+ break;
+ default:
+ ec = errBadResource;
+ break;
+ }
+
+ return ec;
+}
+
+int AgiLoader_v3::loadObjects(const char *fname) {
+ return _vm->loadObjects(fname);
+}
+
+int AgiLoader_v3::loadWords(const char *fname) {
+ return _vm->loadWords(fname);
+}
+
+} // End of namespace Agi