aboutsummaryrefslogtreecommitdiff
path: root/engines/hugo/file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/hugo/file.cpp')
-rwxr-xr-xengines/hugo/file.cpp924
1 files changed, 924 insertions, 0 deletions
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
new file mode 100755
index 0000000000..0a20c8a67b
--- /dev/null
+++ b/engines/hugo/file.cpp
@@ -0,0 +1,924 @@
+/* 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$
+ *
+ */
+
+/*
+ * This code is based on original Hugo Trilogy source code
+ *
+ * Copyright (c) 1989-1995 David P. Gray
+ *
+ */
+
+#include "common/system.h"
+#include "common/file.h"
+#include "common/savefile.h"
+
+#include "hugo/game.h"
+#include "hugo/hugo.h"
+#include "hugo/file.h"
+#include "hugo/global.h"
+#include "hugo/schedule.h"
+#include "hugo/display.h"
+#include "hugo/util.h"
+
+namespace Hugo {
+FileManager::FileManager(HugoEngine &vm) : _vm(vm) {
+
+}
+
+byte *FileManager::convertPCC(byte *p, uint16 y, uint16 bpl, image_pt dataPtr) {
+// Convert 4 planes (RGBI) data to 8-bit DIB format
+// Return original plane data ptr
+ uint16 r, g, b, i; // Byte index within each plane
+ char bit; // Bit index within a byte
+
+ debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
+
+ dataPtr += y * bpl * 8; // Point to correct DIB line
+ for (r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) // Each byte in all planes
+ for (bit = 7; bit >= 0; bit--) // Each bit in byte
+ *dataPtr++ = (((p[r] >> bit & 1) << 0) |
+ ((p[g] >> bit & 1) << 1) |
+ ((p[b] >> bit & 1) << 2) |
+ ((p[i] >> bit & 1) << 3));
+ return p;
+}
+
+seq_t *FileManager::readPCX(Common::File &f, seq_t *seqPtr, byte *imagePtr, bool firstFl, const char *name) {
+// Read a pcx file of length len. Use supplied seq_p and image_p or
+// allocate space if NULL. Name used for errors. Returns address of seq_p
+// Set first TRUE to initialize b_index (i.e. not reading a sequential image in file).
+
+ struct { // Structure of PCX file header
+ byte mfctr, vers, enc, bpx;
+ uint16 x1, y1, x2, y2; // bounding box
+ uint16 xres, yres;
+ byte palette[48]; // EGA color palette
+ byte vmode, planes;
+ uint16 bytesPerLine; // Bytes per line
+ byte fill2[60];
+ } PCC_header; // Header of a PCC file
+
+ byte c, d; // code and data bytes from PCX file
+ byte pline[XPIX]; // Hold 4 planes of data
+ byte *p = pline; // Ptr to above
+ byte i; // PCX repeat count
+ uint16 bytesPerLine4; // BPL in 4-bit format
+ uint16 size; // Size of image
+ uint16 y = 0; // Current line index
+
+ debugC(1, kDebugFile, "readPCX(..., %s)", name);
+
+ // Read in the PCC header and check consistency
+ PCC_header.mfctr = f.readByte();
+ PCC_header.vers = f.readByte();
+ PCC_header.enc = f.readByte();
+ PCC_header.bpx = f.readByte();
+ PCC_header.x1 = f.readUint16LE();
+ PCC_header.y1 = f.readUint16LE();
+ PCC_header.x2 = f.readUint16LE();
+ PCC_header.y2 = f.readUint16LE();
+ PCC_header.xres = f.readUint16LE();
+ PCC_header.yres = f.readUint16LE();
+ f.read(PCC_header.palette, sizeof(PCC_header.palette));
+ PCC_header.vmode = f.readByte();
+ PCC_header.planes = f.readByte();
+ PCC_header.bytesPerLine = f.readUint16LE();
+ f.read(PCC_header.fill2, sizeof(PCC_header.fill2));
+
+ if (PCC_header.mfctr != 10)
+ Utils::Error(PCCH_ERR, name);
+
+ // Allocate memory for seq_t if NULL
+ if (seqPtr == NULL)
+ if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == NULL)
+ Utils::Error(HEAP_ERR, name);
+
+ // Find size of image data in 8-bit DIB format
+ // Note save of x2 - marks end of valid data before garbage
+ bytesPerLine4 = PCC_header.bytesPerLine * 4; // 4-bit bpl
+ seqPtr->bytesPerLine8 = bytesPerLine4 * 2; // 8-bit bpl
+ seqPtr->lines = PCC_header.y2 - PCC_header.y1 + 1;
+ seqPtr->x2 = PCC_header.x2 - PCC_header.x1 + 1;
+ size = seqPtr->lines * seqPtr->bytesPerLine8;
+
+ // Allocate memory for image data if NULL
+ if (imagePtr == NULL)
+ if ((imagePtr = (byte *)malloc((size_t) size)) == NULL)
+ Utils::Error(HEAP_ERR, name);
+ seqPtr->imagePtr = imagePtr;
+
+ // Process the image data, converting to 8-bit DIB format
+ while (y < seqPtr->lines) {
+ c = f.readByte();
+ if ((c & REP_MASK) == REP_MASK) {
+ d = f.readByte(); // Read data byte
+ for (i = 0; i < (c & LEN_MASK); i++) {
+ *p++ = d;
+ if ((uint16)(p - pline) == bytesPerLine4)
+ p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+ }
+ } else {
+ *p++ = c;
+ if ((uint16)(p - pline) == bytesPerLine4)
+ p = convertPCC(pline, y++, PCC_header.bytesPerLine, imagePtr);
+ }
+ }
+ return seqPtr;
+}
+
+void FileManager::readImage(int objNum, object_t *objPtr) {
+// Read object file of PCC images into object supplied
+ byte x, y, j, k;
+ uint16 x2; // Limit on x in image data
+ seq_t *seqPtr; // Ptr to sequence structure
+ image_pt dibPtr; // Ptr to DIB data
+ objBlock_t objBlock; // Info on file within database
+ bool firstFl = true; // Initializes pcx read function
+
+ debugC(1, kDebugFile, "readImage(%d, object_t *objPtr)", objNum);
+
+ if (!objPtr->seqNumb) // This object has no images
+ return;
+
+ if (_vm.isPacked()) {
+ _objectsArchive.seek((uint32)objNum * sizeof(objBlock_t), SEEK_SET);
+
+ objBlock.objOffset = _objectsArchive.readUint32LE();
+ objBlock.objLength = _objectsArchive.readUint32LE();
+
+ _objectsArchive.seek(objBlock.objOffset, SEEK_SET);
+ } else {
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+ strcat(strcat(strcpy(buf, _vm._picDir), _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ if (!_objectsArchive.open(buf)) {
+ warning("File %s not found, trying again with %s%s", buf, _vm._arrayNouns[objPtr->nounIndex][0], OBJEXT);
+ strcat(strcpy(buf, _vm._arrayNouns[objPtr->nounIndex][0]), OBJEXT);
+ if (!_objectsArchive.open(buf))
+ Utils::Error(FILE_ERR, buf);
+ }
+ }
+
+ // Now read the images into an images list
+ for (j = 0; j < objPtr->seqNumb; j++) { // for each sequence
+ for (k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
+ if (k == 0) { // First image
+ // Read this image - allocate both seq and image memory
+ seqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ objPtr->seqList[j].seqPtr = seqPtr;
+ firstFl = false;
+ } else { // Subsequent image
+ // Read this image - allocate both seq and image memory
+ seqPtr->nextSeqPtr = readPCX(_objectsArchive, NULL, NULL, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
+ seqPtr = seqPtr->nextSeqPtr;
+ }
+
+ // Compute the bounding box - x1, x2, y1, y2
+ // Note use of x2 - marks end of valid data in row
+ x2 = seqPtr->x2;
+ seqPtr->x1 = seqPtr->x2;
+ seqPtr->x2 = 0;
+ seqPtr->y1 = seqPtr->lines;
+ seqPtr->y2 = 0;
+ dibPtr = seqPtr->imagePtr;
+ for (y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2)
+ for (x = 0; x < x2; x++)
+ if (*dibPtr++) { // Some data found
+ if (x < seqPtr->x1)
+ seqPtr->x1 = x;
+ if (x > seqPtr->x2)
+ seqPtr->x2 = x;
+ if (y < seqPtr->y1)
+ seqPtr->y1 = y;
+ if (y > seqPtr->y2)
+ seqPtr->y2 = y;
+ }
+ }
+ seqPtr->nextSeqPtr = objPtr->seqList[j].seqPtr; // loop linked list to head
+ }
+
+ // Set the current image sequence to first or last
+ switch (objPtr->cycling) {
+ case INVISIBLE: // (May become visible later)
+ case ALMOST_INVISIBLE:
+ case NOT_CYCLING:
+ case CYCLE_FORWARD:
+ objPtr->currImagePtr = objPtr->seqList[0].seqPtr;
+ break;
+ case CYCLE_BACKWARD:
+ objPtr->currImagePtr = seqPtr;
+ break;
+ }
+
+ if (!_vm.isPacked())
+ _objectsArchive.close();
+}
+
+void FileManager::readBackground(int screenIndex) {
+// Read a PCX image into dib_a
+ seq_t seq; // Image sequence structure for Read_pcx
+ sceneBlock_t sceneBlock; // Read a database header entry
+
+ debugC(1, kDebugFile, "readBackground(%d)", screenIndex);
+
+ if (_vm.isPacked()) {
+ _sceneryArchive.seek((uint32) screenIndex * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock.scene_off = _sceneryArchive.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive.readUint32LE();
+
+ _sceneryArchive.seek(sceneBlock.scene_off, SEEK_SET);
+ } else {
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+ strcat(strcat(strcpy(buf, _vm._picDir), _vm._screenNames[screenIndex]), BKGEXT);
+ if (!_sceneryArchive.open(buf)) {
+ warning("File %s not found, trying again with %s.ART", buf, _vm._screenNames[screenIndex]);
+ strcat(strcpy(buf, _vm._screenNames[screenIndex]), ".ART");
+ if (!_sceneryArchive.open(buf))
+ Utils::Error(FILE_ERR, buf);
+ }
+ }
+
+ // Read the image into dummy seq and static dib_a
+ readPCX(_sceneryArchive, &seq, _vm.screen().getFrontBuffer(), true, _vm._screenNames[screenIndex]);
+
+ if (!_vm.isPacked())
+ _sceneryArchive.close();
+}
+
+sound_pt FileManager::getSound(int16 sound, uint16 *size) {
+// Read sound (or music) file data. Call with SILENCE to free-up
+// any allocated memory. Also returns size of data
+
+ static sound_hdr_t s_hdr[MAX_SOUNDS]; // Sound lookup table
+ sound_pt soundPtr; // Ptr to sound data
+ Common::File fp; // Handle to SOUND_FILE
+// bool music = sound < NUM_TUNES; // TRUE if music, else sound file
+
+ debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
+
+ // No more to do if SILENCE (called for cleanup purposes)
+ if (sound == _vm._soundSilence)
+ return(NULL);
+
+ // Open sounds file
+ if (!fp.open(SOUND_FILE)) {
+// Error(FILE_ERR, SOUND_FILE);
+ warning("Hugo Error: File not found %s", SOUND_FILE);
+ return(NULL);
+ }
+
+ // If this is the first call, read the lookup table
+ static bool has_read_header = false;
+ if (!has_read_header) {
+ if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr))
+ Utils::Error(FILE_ERR, SOUND_FILE);
+ has_read_header = true;
+ }
+
+ *size = s_hdr[sound].size;
+ if (*size == 0)
+ Utils::Error(SOUND_ERR, SOUND_FILE);
+
+ // Allocate memory for sound or music, if possible
+ if ((soundPtr = (byte *)malloc(s_hdr[sound].size)) == 0) {
+ Utils::Warn(false, "Low on memory");
+ return(NULL);
+ }
+
+ // Seek to data and read it
+ fp.seek(s_hdr[sound].offset, SEEK_SET);
+ if (fp.read(soundPtr, s_hdr[sound].size) != s_hdr[sound].size)
+ Utils::Error(FILE_ERR, SOUND_FILE);
+
+ fp.close();
+
+ return soundPtr;
+}
+
+bool FileManager::fileExists(char *filename) {
+// Return whether file exists or not
+ Common::File f;
+ if (f.open(filename)) {
+ f.close();
+ return true;
+ }
+ return false;
+}
+
+void FileManager::readOverlay(int screenNum, image_pt image, ovl_t overlayType) {
+// Open and read in an overlay file, close file
+ uint32 i;
+ int16 j, k;
+ char data; // Must be 8 bits signed
+ image_pt tmpImage = image; // temp ptr to overlay file
+ sceneBlock_t sceneBlock; // Database header entry
+
+ debugC(1, kDebugFile, "readOverlay(%d, ...)", screenNum);
+
+ if (_vm.isPacked()) {
+ _sceneryArchive.seek((uint32)screenNum * sizeof(sceneBlock_t), SEEK_SET);
+
+ sceneBlock.scene_off = _sceneryArchive.readUint32LE();
+ sceneBlock.scene_len = _sceneryArchive.readUint32LE();
+ sceneBlock.b_off = _sceneryArchive.readUint32LE();
+ sceneBlock.b_len = _sceneryArchive.readUint32LE();
+ sceneBlock.o_off = _sceneryArchive.readUint32LE();
+ sceneBlock.o_len = _sceneryArchive.readUint32LE();
+ sceneBlock.ob_off = _sceneryArchive.readUint32LE();
+ sceneBlock.ob_len = _sceneryArchive.readUint32LE();
+
+ switch (overlayType) {
+ case BOUNDARY:
+ _sceneryArchive.seek(sceneBlock.b_off, SEEK_SET);
+ i = sceneBlock.b_len;
+ break;
+ case OVERLAY:
+ _sceneryArchive.seek(sceneBlock.o_off, SEEK_SET);
+ i = sceneBlock.o_len;
+ break;
+ case OVLBASE:
+ _sceneryArchive.seek(sceneBlock.ob_off, SEEK_SET);
+ i = sceneBlock.ob_len;
+ break;
+ default:
+ Utils::Error(FILE_ERR, "Bad ovl_type");
+ break;
+ }
+ if (i == 0) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+ } else {
+ const char *ovl_ext[] = {".b", ".o", ".ob"};
+ char *buf = (char *) malloc(2048 + 1); // Buffer for file access
+
+ strcat(strcpy(buf, _vm._screenNames[screenNum]), ovl_ext[overlayType]);
+
+ if (!fileExists(buf)) {
+ for (i = 0; i < OVL_SIZE; i++)
+ image[i] = 0;
+ return;
+ }
+
+ if (!_sceneryArchive.open(buf))
+ Utils::Error(FILE_ERR, buf);
+
+// if (eof(f_scenery)) {
+// _lclose(f_scenery);
+// return;
+// }
+ }
+
+ switch (_vm._gameVariant) {
+ case 0: // Hugo 1 DOS and WIN don't pack data
+ case 3:
+ _sceneryArchive.read(tmpImage, OVL_SIZE);
+ break;
+ default:
+ // Read in the overlay file using MAC Packbits. (We're not proud!)
+ k = 0; // byte count
+ do {
+ data = _sceneryArchive.readByte(); // Read a code byte
+ if ((byte)data == 0x80) // Noop
+ k = k;
+ else if (data >= 0) { // Copy next data+1 literally
+ for (i = 0; i <= (byte)data; i++, k++)
+ *tmpImage++ = _sceneryArchive.readByte();
+ } else { // Repeat next byte -data+1 times
+ j = _sceneryArchive.readByte();
+
+ for (i = 0; i < (byte)(-data + 1); i++, k++)
+ *tmpImage++ = j;
+ }
+ } while (k < OVL_SIZE);
+ break;
+ }
+
+ if (!_vm.isPacked())
+ _sceneryArchive.close();
+}
+
+void FileManager::saveSeq(object_t *obj) {
+// Save sequence number and image number in given object
+ byte j, k;
+ seq_t *q;
+ bool found;
+
+ debugC(1, kDebugFile, "saveSeq");
+
+ for (j = 0, found = false; !found && (j < obj->seqNumb); j++) {
+ q = obj->seqList[j].seqPtr;
+ for (k = 0; !found && (k < obj->seqList[j].imageNbr); k++) {
+ if (obj->currImagePtr == q) {
+ found = true;
+ obj->curSeqNum = j;
+ obj->curImageNum = k;
+ } else
+ q = q->nextSeqPtr;
+ }
+ }
+}
+
+void FileManager::restoreSeq(object_t *obj) {
+// Set up cur_seq_p from stored sequence and image number in object
+ int j;
+ seq_t *q;
+
+ debugC(1, kDebugFile, "restoreSeq");
+
+ q = obj->seqList[obj->curSeqNum].seqPtr;
+ for (j = 0; j < obj->curImageNum; j++)
+ q = q->nextSeqPtr;
+ obj->currImagePtr = q;
+}
+
+void FileManager::saveGame(int16 slot, const char *descrip) {
+// Save game to supplied slot (-1 is INITFILE)
+ int i;
+ char path[256]; // Full path of saved game
+
+ debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
+
+ // Get full path of saved game file - note test for INITFILE
+ if (slot == -1)
+ sprintf(path, "%s", _vm._initFilename);
+ else
+ sprintf(path, _vm._saveFilename, slot);
+
+ Common::WriteStream *out = 0;
+ if (!(out = _vm.getSaveFileManager()->openForSaving(path))) {
+ warning("Can't create file '%s', game not saved", path);
+ return;
+ }
+
+ // Write version. We can't restore from obsolete versions
+ out->write(&kSavegameVersion, sizeof(kSavegameVersion));
+
+ // Save description of saved game
+ out->write(descrip, DESCRIPLEN);
+
+ // Save objects
+ for (i = 0; i < _vm._numObj; i++) {
+ // Save where curr_seq_p is pointing to
+ saveSeq(&_vm._objects[i]);
+ out->write(&_vm._objects[i], sizeof(object_t));
+ }
+
+ const status_t &gameStatus = _vm.getGameStatus();
+
+ // Save whether hero image is swapped
+ out->write(&_vm._heroImage, sizeof(_vm._heroImage));
+
+ // Save score
+ int score = _vm.getScore();
+ out->write(&score, sizeof(score));
+
+ // Save story mode
+ out->write(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+
+ // Save jumpexit mode
+ out->write(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+
+ // Save gameover status
+ out->write(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+
+ // Save screen states
+ out->write(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+ // Save points table
+ out->write(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+ // Now save current time and all current events in event queue
+ _vm.scheduler().saveEvents(out);
+
+ // Save palette table
+ _vm.screen().savePal(out);
+
+ // Save maze status
+ out->write(&_maze, sizeof(maze_t));
+
+ out->finalize();
+
+ delete out;
+}
+
+void FileManager::restoreGame(int16 slot) {
+// Restore game from supplied slot number (-1 is INITFILE)
+ int i;
+ char path[256]; // Full path of saved game
+ object_t *p;
+ seqList_t seqList[MAX_SEQUENCES];
+// cmdList *cmds; // Save command list pointer
+ uint16 cmdIndex; // Save command list pointer
+// char ver[sizeof(VER)]; // Compare versions
+
+ debugC(1, kDebugFile, "restoreGame(%d)", slot);
+
+ // Initialize new-game status
+ _vm.initStatus();
+
+ // Get full path of saved game file - note test for INITFILE
+ if (slot == -1)
+ sprintf(path, "%s", _vm._initFilename);
+ else
+ sprintf(path, _vm._saveFilename, slot);
+
+ Common::SeekableReadStream *in = 0;
+ if (!(in = _vm.getSaveFileManager()->openForLoading(path)))
+ return;
+
+ // Check version, can't restore from different versions
+ int saveVersion;
+ in->read(&saveVersion, sizeof(saveVersion));
+ if (saveVersion != kSavegameVersion) {
+ Utils::Error(GEN_ERR, "Savegame of incompatible version");
+ return;
+ }
+
+ // Skip over description
+ in->seek(DESCRIPLEN, SEEK_CUR);
+
+ // If hero image is currently swapped, swap it back before restore
+ if (_vm._heroImage != HERO)
+ _vm.scheduler().swapImages(HERO, _vm._heroImage);
+
+ // Restore objects, retain current seqList which points to dynamic mem
+ // Also, retain cmnd_t pointers
+ for (i = 0; i < _vm._numObj; i++) {
+ p = &_vm._objects[i];
+ memcpy(seqList, p->seqList, sizeof(seqList_t));
+ cmdIndex = p->cmdIndex;
+ in->read(p, sizeof(object_t));
+ p->cmdIndex = cmdIndex;
+ memcpy(p->seqList, seqList, sizeof(seqList_t));
+ }
+
+ in->read(&_vm._heroImage, sizeof(_vm._heroImage));
+
+ // If hero swapped in saved game, swap it
+ if ((i = _vm._heroImage) != HERO)
+ _vm.scheduler().swapImages(HERO, _vm._heroImage);
+ _vm._heroImage = i;
+
+ status_t &gameStatus = _vm.getGameStatus();
+
+ int score;
+ in->read(&score, sizeof(score));
+ _vm.setScore(score);
+
+ in->read(&gameStatus.storyModeFl, sizeof(gameStatus.storyModeFl));
+ in->read(&gameStatus.jumpExitFl, sizeof(gameStatus.jumpExitFl));
+ in->read(&gameStatus.gameOverFl, sizeof(gameStatus.gameOverFl));
+ in->read(_vm._screenStates, sizeof(*_vm._screenStates) * _vm._numScreens);
+
+ // Restore points table
+ in->read(_vm._points, sizeof(point_t) * _vm._numBonuses);
+
+ // Restore ptrs to currently loaded objects
+ for (i = 0; i < _vm._numObj; i++)
+ restoreSeq(&_vm._objects[i]);
+
+ // Now restore time of the save and the event queue
+ _vm.scheduler().restoreEvents(in);
+
+ // Restore palette and change it if necessary
+ _vm.screen().restorePal(in);
+
+ // Restore maze status
+ in->read(&_maze, sizeof(maze_t));
+
+ delete in;
+}
+
+void FileManager::initSavedGame() {
+// Initialize the size of a saved game (from the fixed initial game).
+// If status.initsave is TRUE, or the initial saved game is not found,
+// force a save to create one. Normally the game will be shipped with
+// the initial game file but useful to force a write during development
+// when the size is changeable.
+// The net result is a valid INITFILE, with status.savesize initialized.
+ Common::File f; // Handle of saved game file
+ char path[256]; // Full path of INITFILE
+
+ debugC(1, kDebugFile, "initSavedGame");
+
+ // Get full path of INITFILE
+ sprintf(path, "%s", _vm._initFilename);
+
+
+ // Force save of initial game
+ if (_vm.getGameStatus().initSaveFl)
+ saveGame(-1, "");
+
+ // If initial game doesn't exist, create it
+ Common::SeekableReadStream *in = 0;
+ if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
+ saveGame(-1, "");
+ if (!(in = _vm.getSaveFileManager()->openForLoading(path))) {
+ Utils::Error(WRITE_ERR, path);
+ return;
+ }
+ }
+
+ // Must have an open saved game now
+ _vm.getGameStatus().saveSize = in->size();
+ delete in;
+
+ // Check sanity - maybe disk full or path set to read-only drive?
+ if (_vm.getGameStatus().saveSize == -1)
+ Utils::Error(WRITE_ERR, path);
+}
+
+// Record and playback handling stuff:
+typedef struct {
+// int key; // Character
+ uint32 time; // Time at which character was pressed
+} pbdata_t;
+static pbdata_t pbdata;
+FILE *fpb;
+
+void FileManager::openPlaybackFile(bool playbackFl, bool recordFl) {
+ debugC(1, kDebugFile, "openPlaybackFile(%d, %d)", (playbackFl) ? 1 : 0, (recordFl) ? 1 : 0);
+
+ if (playbackFl) {
+ if (!(fpb = fopen(PBFILE, "r+b")))
+ Utils::Error(FILE_ERR, PBFILE);
+ } else if (recordFl)
+ fpb = fopen(PBFILE, "wb");
+ pbdata.time = 0; // Say no key available
+}
+
+void FileManager::closePlaybackFile() {
+ fclose(fpb);
+}
+
+void FileManager::openDatabaseFiles() {
+//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat
+//This should be tested adequately and should be handled by an error and not by a warning.
+ debugC(1, kDebugFile, "openDatabaseFiles");
+
+ if (!_stringArchive.open(STRING_FILE))
+// Error(FILE_ERR, STRING_FILE);
+ warning("Hugo Error: File not found %s", STRING_FILE);
+ if (_vm.isPacked()) {
+ if (!_sceneryArchive.open(SCENERY_FILE))
+ Utils::Error(FILE_ERR, SCENERY_FILE);
+ if (!_objectsArchive.open(OBJECTS_FILE))
+ Utils::Error(FILE_ERR, OBJECTS_FILE);
+ }
+}
+
+void FileManager::closeDatabaseFiles() {
+// TODO: stringArchive shouldn't be closed in Hugo 1 DOS
+ debugC(1, kDebugFile, "closeDatabaseFiles");
+
+ _stringArchive.close();
+ if (_vm.isPacked()) {
+ _sceneryArchive.close();
+ _objectsArchive.close();
+ }
+}
+
+char *FileManager::fetchString(int index) {
+//TODO : HUGO 1 DOS uses _stringtData instead of a strings.dat
+// Fetch string from file, decode and return ptr to string in memory
+ uint32 off1, off2;
+
+ debugC(1, kDebugFile, "fetchString(%d)", index);
+
+ // Get offset to string[index] (and next for length calculation)
+ _stringArchive.seek((uint32)index * sizeof(uint32), SEEK_SET);
+ if (_stringArchive.read((char *)&off1, sizeof(uint32)) == 0)
+ Utils::Error(FILE_ERR, "String offset");
+ if (_stringArchive.read((char *)&off2, sizeof(uint32)) == 0)
+ Utils::Error(FILE_ERR, "String offset");
+
+ // Check size of string
+ if ((off2 - off1) >= MAX_BOX)
+ Utils::Error(FILE_ERR, "Fetched string too long!");
+
+ // Position to string and read it into gen purpose _textBoxBuffer
+ _stringArchive.seek(off1, SEEK_SET);
+ if (_stringArchive.read(_textBoxBuffer, (uint16)(off2 - off1)) == 0)
+ Utils::Error(FILE_ERR, "Fetch_string");
+
+ // Null terminate, decode and return it
+ _textBoxBuffer[off2-off1] = '\0';
+ _vm.scheduler().decodeString(_textBoxBuffer);
+ return _textBoxBuffer;
+}
+
+void FileManager::printBootText() {
+// Read the encrypted text from the boot file and print it
+ Common::File ofp;
+ int i;
+ char *buf;
+
+ debugC(1, kDebugFile, "printBootText");
+
+ if (!ofp.open(BOOTFILE))
+ Utils::Error(FILE_ERR, BOOTFILE);
+
+ // Allocate space for the text and print it
+ buf = (char *)malloc(_boot.exit_len + 1);
+ if (buf) {
+ // Skip over the boot structure (already read) and read exit text
+ ofp.seek((long)sizeof(_boot), SEEK_SET);
+ if (ofp.read(buf, _boot.exit_len) != (size_t)_boot.exit_len)
+ Utils::Error(FILE_ERR, BOOTFILE);
+
+ // Decrypt the exit text, using CRYPT substring
+ for (i = 0; i < _boot.exit_len; i++)
+ buf[i] ^= CRYPT[i % strlen(CRYPT)];
+
+ buf[i] = '\0';
+ //Box(BOX_OK, buf_p);
+ //MessageBox(hwnd, buf_p, "License", MB_ICONINFORMATION);
+ warning("printBootText(): License: %s", buf);
+ }
+
+ free(buf);
+ ofp.close();
+}
+
+void FileManager::readBootFile() {
+// Reads boot file for program environment. Fatal error if not there or
+// file checksum is bad. De-crypts structure while checking checksum
+ byte checksum;
+ byte *p;
+ Common::File ofp;
+ uint32 i;
+
+ debugC(1, kDebugFile, "readBootFile");
+
+ if (!ofp.open(BOOTFILE))
+ Utils::Error(FILE_ERR, BOOTFILE);
+
+ if (ofp.size() < (int32)sizeof(_boot))
+ Utils::Error(FILE_ERR, BOOTFILE);
+
+ _boot.checksum = ofp.readByte();
+ _boot.registered = ofp.readByte();
+ ofp.read(_boot.pbswitch, sizeof(_boot.pbswitch));
+ ofp.read(_boot.distrib, sizeof(_boot.distrib));
+ _boot.exit_len = ofp.readUint16LE();
+
+ p = (byte *)&_boot;
+ for (i = 0, checksum = 0; i < sizeof(_boot); i++) {
+ checksum ^= p[i];
+ p[i] ^= CRYPT[i % strlen(CRYPT)];
+ }
+ ofp.close();
+
+ if (checksum)
+ Utils::Error(GEN_ERR, "Program startup file invalid");
+}
+
+void FileManager::readConfig() {
+// Read the user's config if it exists
+ Common::File f;
+ fpath_t path;
+ config_t tmpConfig = _config;
+
+ debugC(1, kDebugFile, "readConfig");
+
+ sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE);
+ if (f.open(path)) {
+ // If config format changed, ignore it and use defaults
+ if (f.read(&_config, sizeof(_config)) != sizeof(_config))
+ _config = tmpConfig;
+
+ f.close();
+ }
+}
+
+void FileManager::writeConfig() {
+// Write the user's config
+ FILE *f;
+ fpath_t path;
+
+ debugC(1, kDebugFile, "writeConfig");
+
+ // Write user's config
+ // No error checking in case CD-ROM with no alternate path specified
+ sprintf(path, "%s%s", _vm.getGameStatus().path, CONFIGFILE);
+ if ((f = fopen(path, "w+")) != NULL)
+ fwrite(&_config, sizeof(_config), 1, f);
+
+ fclose(f);
+}
+
+uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
+// Returns address of uif_hdr[id], reading it in if first call
+ static uif_hdr_t UIFHeader[MAX_UIFS]; // Lookup for uif fonts/images
+ static bool firstFl = true;
+ Common::File ip; // Image data file
+
+ debugC(1, kDebugFile, "getUIFHeader(%d)", id);
+
+ // Initialize offset lookup if not read yet
+ if (firstFl) {
+ firstFl = false;
+ // Open unbuffered to do far read
+ if (!ip.open(UIF_FILE))
+ Utils::Error(FILE_ERR, UIF_FILE);
+
+ if (ip.size() < (int32)sizeof(UIFHeader))
+ Utils::Error(FILE_ERR, UIF_FILE);
+
+ for (int i = 0; i < MAX_UIFS; ++i) {
+ UIFHeader[i].size = ip.readUint16LE();
+ UIFHeader[i].offset = ip.readUint32LE();
+ }
+
+ ip.close();
+ }
+ return &UIFHeader[id];
+}
+
+void FileManager::readUIFItem(int16 id, byte *buf) {
+// Read uif item into supplied buffer.
+ Common::File ip; // UIF_FILE handle
+ uif_hdr_t *UIFHeaderPtr; // Lookup table of items
+ seq_t seq; // Dummy seq_t for image data
+
+ debugC(1, kDebugFile, "readUIFItem(%d, ...)", id);
+
+ // Open uif file to read data
+ if (!ip.open(UIF_FILE))
+ Utils::Error(FILE_ERR, UIF_FILE);
+
+ // Seek to data
+ UIFHeaderPtr = getUIFHeader((uif_t)id);
+ ip.seek(UIFHeaderPtr->offset, SEEK_SET);
+
+ // We support pcx images and straight data
+ switch (id) {
+ case UIF_IMAGES: // Read uif images file
+ readPCX(ip, &seq, buf, true, UIF_FILE);
+ break;
+ default: // Read file data into supplied array
+ if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
+ Utils::Error(FILE_ERR, UIF_FILE);
+ break;
+ }
+
+ ip.close();
+}
+
+void FileManager::instructions() {
+// Simple instructions given when F1 pressed twice in a row
+// Only in DOS versions
+#define HELPFILE "help.dat"
+#define EOP '#' /* Marks end of a page in help file */
+
+ Common::File f;
+ char line[1024], *wrkLine;
+ char readBuf[2];
+
+ wrkLine = line;
+ if (!f.open(UIF_FILE))
+ Utils::Error(FILE_ERR, HELPFILE);
+
+ while (f.read(readBuf, 1)) {
+ wrkLine[0] = readBuf[0];
+ do {
+ f.read(wrkLine, 1);
+ } while (*wrkLine++ != EOP);
+ wrkLine[-2] = '\0'; /* Remove EOP and previous CR */
+ Utils::Box(BOX_ANY, line);
+ f.read(wrkLine, 1); /* Remove CR after EOP */
+ }
+ f.close();
+}
+
+} // end of namespace Hugo