2010-08-17 09:28:20 +00:00
|
|
|
/* 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/savefile.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) {
|
2010-09-05 20:37:38 +00:00
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
|
2010-09-05 20:37:38 +00:00
|
|
|
FileManager::~FileManager() {
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
debugC(2, kDebugFile, "convertPCC(byte *p, %d, %d, image_pt data_p)", y, bpl);
|
|
|
|
|
|
|
|
dataPtr += y * bpl * 8; // Point to correct DIB line
|
2010-10-03 08:08:42 +00:00
|
|
|
for (int16 r = 0, g = bpl, b = g + bpl, i = b + bpl; r < bpl; r++, g++, b++, i++) { // Each byte in all planes
|
|
|
|
for (int8 bit = 7; bit >= 0; bit--) { // Each bit in byte
|
2010-08-17 09:28:20 +00:00
|
|
|
*dataPtr++ = (((p[r] >> bit & 1) << 0) |
|
|
|
|
((p[g] >> bit & 1) << 1) |
|
|
|
|
((p[b] >> bit & 1) << 2) |
|
|
|
|
((p[i] >> bit & 1) << 3));
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
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).
|
|
|
|
debugC(1, kDebugFile, "readPCX(..., %s)", name);
|
|
|
|
|
|
|
|
// Read in the PCC header and check consistency
|
2010-10-03 08:08:42 +00:00
|
|
|
static PCC_header_t PCC_header;
|
2010-08-17 09:28:20 +00:00
|
|
|
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)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(PCCH_ERR, "%s", name);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
// Allocate memory for seq_t if 0
|
|
|
|
if (seqPtr == 0) {
|
|
|
|
if ((seqPtr = (seq_t *)malloc(sizeof(seq_t))) == 0)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(HEAP_ERR, "%s", name);
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
// Find size of image data in 8-bit DIB format
|
|
|
|
// Note save of x2 - marks end of valid data before garbage
|
2010-10-03 08:08:42 +00:00
|
|
|
uint16 bytesPerLine4 = PCC_header.bytesPerLine * 4; // 4-bit bpl
|
2010-08-17 09:28:20 +00:00
|
|
|
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;
|
2010-10-03 08:08:42 +00:00
|
|
|
// Size of the image
|
|
|
|
uint16 size = seqPtr->lines * seqPtr->bytesPerLine8;
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Allocate memory for image data if NULL
|
2010-10-03 08:08:42 +00:00
|
|
|
if (imagePtr == 0) {
|
|
|
|
if ((imagePtr = (byte *)malloc((size_t) size)) == 0)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(HEAP_ERR, "%s", name);
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
seqPtr->imagePtr = imagePtr;
|
|
|
|
|
|
|
|
// Process the image data, converting to 8-bit DIB format
|
2010-10-03 08:08:42 +00:00
|
|
|
uint16 y = 0; // Current line index
|
|
|
|
byte pline[XPIX]; // Hold 4 planes of data
|
|
|
|
byte *p = pline; // Ptr to above
|
2010-08-17 09:28:20 +00:00
|
|
|
while (y < seqPtr->lines) {
|
2010-10-03 08:08:42 +00:00
|
|
|
byte c = f.readByte();
|
2010-08-17 09:28:20 +00:00
|
|
|
if ((c & REP_MASK) == REP_MASK) {
|
2010-10-03 08:08:42 +00:00
|
|
|
byte d = f.readByte(); // Read data byte
|
|
|
|
for (int i = 0; i < (c & LEN_MASK); i++) {
|
2010-08-17 09:28:20 +00:00
|
|
|
*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
|
|
|
|
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);
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
objBlock_t objBlock; // Info on file within database
|
2010-08-17 09:28:20 +00:00
|
|
|
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))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", buf);
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
bool firstFl = true; // Initializes pcx read function
|
|
|
|
seq_t *seqPtr = 0; // Ptr to sequence structure
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
// Now read the images into an images list
|
2010-10-03 08:08:42 +00:00
|
|
|
for (int j = 0; j < objPtr->seqNumb; j++) { // for each sequence
|
|
|
|
for (int k = 0; k < objPtr->seqList[j].imageNbr; k++) { // each image
|
2010-08-17 09:28:20 +00:00
|
|
|
if (k == 0) { // First image
|
|
|
|
// Read this image - allocate both seq and image memory
|
2010-10-03 08:08:42 +00:00
|
|
|
seqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
|
2010-08-17 09:28:20 +00:00
|
|
|
objPtr->seqList[j].seqPtr = seqPtr;
|
|
|
|
firstFl = false;
|
|
|
|
} else { // Subsequent image
|
|
|
|
// Read this image - allocate both seq and image memory
|
2010-10-03 08:08:42 +00:00
|
|
|
seqPtr->nextSeqPtr = readPCX(_objectsArchive, 0, 0, firstFl, _vm._arrayNouns[objPtr->nounIndex][0]);
|
2010-08-17 09:28:20 +00:00
|
|
|
seqPtr = seqPtr->nextSeqPtr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Compute the bounding box - x1, x2, y1, y2
|
|
|
|
// Note use of x2 - marks end of valid data in row
|
2010-10-03 08:08:42 +00:00
|
|
|
uint16 x2 = seqPtr->x2;
|
2010-08-17 09:28:20 +00:00
|
|
|
seqPtr->x1 = seqPtr->x2;
|
|
|
|
seqPtr->x2 = 0;
|
|
|
|
seqPtr->y1 = seqPtr->lines;
|
|
|
|
seqPtr->y2 = 0;
|
2010-10-03 08:08:42 +00:00
|
|
|
|
|
|
|
image_pt dibPtr = seqPtr->imagePtr;
|
|
|
|
for (int y = 0; y < seqPtr->lines; y++, dibPtr += seqPtr->bytesPerLine8 - x2) {
|
|
|
|
for (int x = 0; x < x2; x++) {
|
|
|
|
if (*dibPtr++) { // Some data found
|
2010-08-17 09:28:20 +00:00
|
|
|
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;
|
|
|
|
}
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
2010-10-15 06:08:42 +00:00
|
|
|
assert(seqPtr);
|
2010-08-17 09:28:20 +00:00
|
|
|
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;
|
2010-09-16 23:03:03 +00:00
|
|
|
default:
|
|
|
|
warning("Unexpected cycling: %d", objPtr->cycling);
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!_vm.isPacked())
|
|
|
|
_objectsArchive.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
|
|
|
|
debugC(1, kDebugFile, "getSound(%d, %d)", sound, *size);
|
|
|
|
|
|
|
|
// No more to do if SILENCE (called for cleanup purposes)
|
|
|
|
if (sound == _vm._soundSilence)
|
2010-10-03 08:08:42 +00:00
|
|
|
return 0;
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Open sounds file
|
2010-10-03 08:08:42 +00:00
|
|
|
Common::File fp; // Handle to SOUND_FILE
|
2010-08-17 09:28:20 +00:00
|
|
|
if (!fp.open(SOUND_FILE)) {
|
|
|
|
warning("Hugo Error: File not found %s", SOUND_FILE);
|
2010-10-03 08:08:42 +00:00
|
|
|
return 0;
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If this is the first call, read the lookup table
|
|
|
|
static bool has_read_header = false;
|
2010-10-03 08:08:42 +00:00
|
|
|
static sound_hdr_t s_hdr[MAX_SOUNDS]; // Sound lookup table
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
if (!has_read_header) {
|
|
|
|
if (fp.read(s_hdr, sizeof(s_hdr)) != sizeof(s_hdr))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", SOUND_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
has_read_header = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*size = s_hdr[sound].size;
|
|
|
|
if (*size == 0)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(SOUND_ERR, "%s", SOUND_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Allocate memory for sound or music, if possible
|
2010-10-03 08:08:42 +00:00
|
|
|
sound_pt soundPtr = (byte *)malloc(s_hdr[sound].size); // Ptr to sound data
|
|
|
|
if (soundPtr == 0) {
|
|
|
|
Utils::Warn("%s", "Low on memory");
|
|
|
|
return 0;
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", SOUND_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
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::saveSeq(object_t *obj) {
|
|
|
|
// Save sequence number and image number in given object
|
|
|
|
debugC(1, kDebugFile, "saveSeq");
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
bool found = false;
|
|
|
|
for (int j = 0; !found && (j < obj->seqNumb); j++) {
|
|
|
|
seq_t *q = obj->seqList[j].seqPtr;
|
|
|
|
for (int k = 0; !found && (k < obj->seqList[j].imageNbr); k++) {
|
2010-08-17 09:28:20 +00:00
|
|
|
if (obj->currImagePtr == q) {
|
|
|
|
found = true;
|
|
|
|
obj->curSeqNum = j;
|
|
|
|
obj->curImageNum = k;
|
2010-10-03 08:08:42 +00:00
|
|
|
} else {
|
2010-08-17 09:28:20 +00:00
|
|
|
q = q->nextSeqPtr;
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileManager::restoreSeq(object_t *obj) {
|
|
|
|
// Set up cur_seq_p from stored sequence and image number in object
|
|
|
|
debugC(1, kDebugFile, "restoreSeq");
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
seq_t *q = obj->seqList[obj->curSeqNum].seqPtr;
|
|
|
|
for (int j = 0; j < obj->curImageNum; j++)
|
2010-08-17 09:28:20 +00:00
|
|
|
q = q->nextSeqPtr;
|
|
|
|
obj->currImagePtr = q;
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileManager::saveGame(int16 slot, const char *descrip) {
|
|
|
|
// Save game to supplied slot (-1 is INITFILE)
|
|
|
|
debugC(1, kDebugFile, "saveGame(%d, %s)", slot, descrip);
|
|
|
|
|
|
|
|
// Get full path of saved game file - note test for INITFILE
|
2010-10-10 23:24:57 +00:00
|
|
|
Common::String path; // Full path of saved game
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
if (slot == -1)
|
2010-10-10 23:24:57 +00:00
|
|
|
path = _vm._initFilename;
|
2010-08-17 09:28:20 +00:00
|
|
|
else
|
2010-10-10 23:24:57 +00:00
|
|
|
path = Common::String::printf(_vm._saveFilename.c_str(), slot);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
2010-10-10 23:24:57 +00:00
|
|
|
Common::WriteStream *out = _vm.getSaveFileManager()->openForSaving(path);
|
|
|
|
if (!out) {
|
|
|
|
warning("Can't create file '%s', game not saved", path.c_str());
|
2010-08-17 09:28:20 +00:00
|
|
|
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
|
2010-10-03 08:08:42 +00:00
|
|
|
for (int i = 0; i < _vm._numObj; i++) {
|
2010-08-17 09:28:20 +00:00
|
|
|
// 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)
|
|
|
|
debugC(1, kDebugFile, "restoreGame(%d)", slot);
|
|
|
|
|
|
|
|
// Initialize new-game status
|
|
|
|
_vm.initStatus();
|
|
|
|
|
|
|
|
// Get full path of saved game file - note test for INITFILE
|
2010-10-10 23:24:57 +00:00
|
|
|
Common::String path; // Full path of saved game
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
if (slot == -1)
|
2010-10-10 23:24:57 +00:00
|
|
|
path = _vm._initFilename;
|
2010-08-17 09:28:20 +00:00
|
|
|
else
|
2010-10-10 23:24:57 +00:00
|
|
|
path = Common::String::printf(_vm._saveFilename.c_str(), slot);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
2010-10-10 23:24:57 +00:00
|
|
|
Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(path);
|
|
|
|
if (!in)
|
2010-08-17 09:28:20 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
// Check version, can't restore from different versions
|
|
|
|
int saveVersion;
|
|
|
|
in->read(&saveVersion, sizeof(saveVersion));
|
|
|
|
if (saveVersion != kSavegameVersion) {
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(GEN_ERR, "%s", "Savegame of incompatible version");
|
2010-08-17 09:28:20 +00:00
|
|
|
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
|
2010-10-03 08:08:42 +00:00
|
|
|
for (int i = 0; i < _vm._numObj; i++) {
|
|
|
|
object_t *p = &_vm._objects[i];
|
|
|
|
seqList_t seqList[MAX_SEQUENCES];
|
2010-08-17 09:28:20 +00:00
|
|
|
memcpy(seqList, p->seqList, sizeof(seqList_t));
|
2010-10-03 08:08:42 +00:00
|
|
|
uint16 cmdIndex = p->cmdIndex;
|
2010-08-17 09:28:20 +00:00
|
|
|
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
|
2010-10-03 08:08:42 +00:00
|
|
|
int heroImg = _vm._heroImage;
|
|
|
|
if (heroImg != HERO)
|
2010-08-17 09:28:20 +00:00
|
|
|
_vm.scheduler().swapImages(HERO, _vm._heroImage);
|
2010-10-03 08:08:42 +00:00
|
|
|
_vm._heroImage = heroImg;
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
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
|
2010-10-03 08:08:42 +00:00
|
|
|
for (int i = 0; i < _vm._numObj; i++)
|
2010-08-17 09:28:20 +00:00
|
|
|
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.
|
|
|
|
debugC(1, kDebugFile, "initSavedGame");
|
|
|
|
|
|
|
|
// Force save of initial game
|
|
|
|
if (_vm.getGameStatus().initSaveFl)
|
|
|
|
saveGame(-1, "");
|
|
|
|
|
|
|
|
// If initial game doesn't exist, create it
|
2010-10-10 23:24:57 +00:00
|
|
|
Common::SeekableReadStream *in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
|
|
|
|
if (!in) {
|
2010-08-17 09:28:20 +00:00
|
|
|
saveGame(-1, "");
|
2010-10-10 23:24:57 +00:00
|
|
|
in = _vm.getSaveFileManager()->openForLoading(_vm._initFilename);
|
|
|
|
if (!in) {
|
|
|
|
Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
|
2010-08-17 09:28:20 +00:00
|
|
|
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)
|
2010-10-10 23:24:57 +00:00
|
|
|
Utils::Error(WRITE_ERR, "%s", _vm._initFilename.c_str());
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", PBFILE);
|
2010-10-03 08:08:42 +00:00
|
|
|
} else if (recordFl) {
|
2010-08-17 09:28:20 +00:00
|
|
|
fpb = fopen(PBFILE, "wb");
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
pbdata.time = 0; // Say no key available
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileManager::closePlaybackFile() {
|
|
|
|
fclose(fpb);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileManager::printBootText() {
|
|
|
|
// Read the encrypted text from the boot file and print it
|
|
|
|
debugC(1, kDebugFile, "printBootText");
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
Common::File ofp;
|
2010-08-18 17:51:44 +00:00
|
|
|
if (!ofp.open(BOOTFILE)) {
|
2010-08-18 15:33:59 +00:00
|
|
|
if (_vm._gameVariant == 3) {
|
|
|
|
//TODO initialize properly _boot structure
|
|
|
|
warning("printBootText - Skipping as H1 Dos may be a freeware");
|
|
|
|
return;
|
2010-10-03 08:08:42 +00:00
|
|
|
} else {
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", BOOTFILE);
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
2010-08-18 17:51:44 +00:00
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Allocate space for the text and print it
|
2010-10-03 08:08:42 +00:00
|
|
|
char *buf = (char *)malloc(_boot.exit_len + 1);
|
2010-08-17 09:28:20 +00:00
|
|
|
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)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", BOOTFILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Decrypt the exit text, using CRYPT substring
|
2010-10-03 08:08:42 +00:00
|
|
|
int i;
|
2010-08-17 09:28:20 +00:00
|
|
|
for (i = 0; i < _boot.exit_len; i++)
|
|
|
|
buf[i] ^= CRYPT[i % strlen(CRYPT)];
|
|
|
|
|
|
|
|
buf[i] = '\0';
|
2010-08-27 09:48:53 +00:00
|
|
|
//Box(BOX_OK, "%s", buf_p);
|
2010-08-17 09:28:20 +00:00
|
|
|
//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
|
|
|
|
debugC(1, kDebugFile, "readBootFile");
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
Common::File ofp;
|
2010-08-18 17:51:44 +00:00
|
|
|
if (!ofp.open(BOOTFILE)) {
|
2010-08-18 15:33:59 +00:00
|
|
|
if (_vm._gameVariant == 3) {
|
|
|
|
//TODO initialize properly _boot structure
|
|
|
|
warning("readBootFile - Skipping as H1 Dos may be a freeware");
|
|
|
|
return;
|
2010-10-03 08:08:42 +00:00
|
|
|
} else {
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", BOOTFILE);
|
2010-10-03 08:08:42 +00:00
|
|
|
}
|
2010-08-18 17:51:44 +00:00
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
if (ofp.size() < (int32)sizeof(_boot))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", BOOTFILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
_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();
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
byte *p = (byte *)&_boot;
|
|
|
|
|
|
|
|
byte checksum = 0;
|
|
|
|
for (uint32 i = 0; i < sizeof(_boot); i++) {
|
2010-08-17 09:28:20 +00:00
|
|
|
checksum ^= p[i];
|
|
|
|
p[i] ^= CRYPT[i % strlen(CRYPT)];
|
|
|
|
}
|
|
|
|
ofp.close();
|
|
|
|
|
|
|
|
if (checksum)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(GEN_ERR, "%s", "Program startup file invalid");
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uif_hdr_t *FileManager::getUIFHeader(uif_t id) {
|
|
|
|
// Returns address of uif_hdr[id], reading it in if first call
|
|
|
|
debugC(1, kDebugFile, "getUIFHeader(%d)", id);
|
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
static bool firstFl = true;
|
|
|
|
static uif_hdr_t UIFHeader[MAX_UIFS]; // Lookup for uif fonts/images
|
|
|
|
|
2010-08-17 09:28:20 +00:00
|
|
|
// Initialize offset lookup if not read yet
|
|
|
|
if (firstFl) {
|
|
|
|
firstFl = false;
|
|
|
|
// Open unbuffered to do far read
|
2010-10-03 08:08:42 +00:00
|
|
|
Common::File ip; // Image data file
|
2010-08-17 09:28:20 +00:00
|
|
|
if (!ip.open(UIF_FILE))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", UIF_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
if (ip.size() < (int32)sizeof(UIFHeader))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", UIF_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
debugC(1, kDebugFile, "readUIFItem(%d, ...)", id);
|
|
|
|
|
|
|
|
// Open uif file to read data
|
2010-10-03 08:08:42 +00:00
|
|
|
Common::File ip; // UIF_FILE handle
|
2010-08-17 09:28:20 +00:00
|
|
|
if (!ip.open(UIF_FILE))
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", UIF_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
|
|
|
|
// Seek to data
|
2010-10-03 08:08:42 +00:00
|
|
|
uif_hdr_t *UIFHeaderPtr = getUIFHeader((uif_t)id);
|
2010-08-17 09:28:20 +00:00
|
|
|
ip.seek(UIFHeaderPtr->offset, SEEK_SET);
|
|
|
|
|
|
|
|
// We support pcx images and straight data
|
2010-10-03 08:08:42 +00:00
|
|
|
seq_t dummySeq; // Dummy seq_t for image data
|
2010-08-17 09:28:20 +00:00
|
|
|
switch (id) {
|
|
|
|
case UIF_IMAGES: // Read uif images file
|
2010-10-03 08:08:42 +00:00
|
|
|
readPCX(ip, &dummySeq, buf, true, UIF_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
break;
|
|
|
|
default: // Read file data into supplied array
|
|
|
|
if (ip.read(buf, UIFHeaderPtr->size) != UIFHeaderPtr->size)
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Error(FILE_ERR, "%s", UIF_FILE);
|
2010-08-17 09:28:20 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ip.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileManager::instructions() {
|
|
|
|
// Simple instructions given when F1 pressed twice in a row
|
|
|
|
// Only in DOS versions
|
|
|
|
|
|
|
|
Common::File f;
|
2010-08-18 16:14:38 +00:00
|
|
|
if (!f.open(HELPFILE)) {
|
|
|
|
warning("help.dat not found");
|
|
|
|
return;
|
|
|
|
}
|
2010-08-17 09:28:20 +00:00
|
|
|
|
2010-10-03 08:08:42 +00:00
|
|
|
char readBuf[2];
|
2010-08-17 09:28:20 +00:00
|
|
|
while (f.read(readBuf, 1)) {
|
2010-10-03 08:08:42 +00:00
|
|
|
char line[1024], *wrkLine;
|
|
|
|
wrkLine = line;
|
2010-08-17 09:28:20 +00:00
|
|
|
wrkLine[0] = readBuf[0];
|
2010-08-18 16:14:38 +00:00
|
|
|
wrkLine++;
|
2010-08-17 09:28:20 +00:00
|
|
|
do {
|
|
|
|
f.read(wrkLine, 1);
|
|
|
|
} while (*wrkLine++ != EOP);
|
2010-10-03 08:08:42 +00:00
|
|
|
wrkLine[-2] = '\0'; // Remove EOP and previous CR
|
2010-08-27 09:48:53 +00:00
|
|
|
Utils::Box(BOX_ANY, "%s", line);
|
2010-08-18 16:14:38 +00:00
|
|
|
wrkLine = line;
|
2010-10-03 08:08:42 +00:00
|
|
|
f.read(readBuf, 2); // Remove CRLF after EOP
|
2010-08-17 09:28:20 +00:00
|
|
|
}
|
|
|
|
f.close();
|
|
|
|
}
|
|
|
|
|
2010-08-27 09:48:53 +00:00
|
|
|
} // End of namespace Hugo
|
2010-09-05 20:37:38 +00:00
|
|
|
|