scummvm/engines/agos/saveload.cpp

1709 lines
38 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "common/file.h"
#include "common/savefile.h"
#include "common/textconsole.h"
#include "common/translation.h"
#include "gui/about.h"
#include "gui/message.h"
#include "agos/agos.h"
#include "agos/intern.h"
namespace AGOS {
int AGOSEngine::countSaveGames() {
Common::InSaveFile *f = NULL;
Common::StringArray filenames;
uint i = 1;
char slot[4];
int slotNum;
bool marks[256];
char *prefix = genSaveName(998);
prefix[strlen(prefix)-3] = '*';
prefix[strlen(prefix)-2] = '\0';
memset(marks, false, 256 * sizeof(bool)); //assume no savegames for this title
filenames = _saveFileMan->listSavefiles(prefix);
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file){
//Obtain the last 3 digits of the filename, since they correspond to the save slot
slot[0] = file->c_str()[file->size()-3];
slot[1] = file->c_str()[file->size()-2];
slot[2] = file->c_str()[file->size()-1];
slot[3] = '\0';
slotNum = atoi(slot);
if (slotNum >= 0 && slotNum < 256)
marks[slotNum] = true; //mark this slot as valid
}
while (i < 256) {
if (marks[i] &&
(f = _saveFileMan->openForLoading(genSaveName(i)))) {
i++;
delete f;
} else
break;
}
return i;
}
#ifdef ENABLE_AGOS2
char *AGOSEngine_PuzzlePack::genSaveName(int slot) {
static char buf[20];
if (getGameId() == GID_DIMP)
sprintf(buf, "dimp.sav");
else
sprintf(buf, "swampy.sav");
return buf;
}
char *AGOSEngine_Feeble::genSaveName(int slot) {
static char buf[20];
sprintf(buf, "feeble.%.3d", slot);
return buf;
}
#endif
char *AGOSEngine_Simon2::genSaveName(int slot) {
static char buf[20];
sprintf(buf, "simon2.%.3d", slot);
return buf;
}
char *AGOSEngine_Simon1::genSaveName(int slot) {
static char buf[20];
sprintf(buf, "simon1.%.3d", slot);
return buf;
}
char *AGOSEngine_Waxworks::genSaveName(int slot) {
static char buf[20];
if (getPlatform() == Common::kPlatformPC)
sprintf(buf, "waxworks-pc.%.3d", slot);
else
sprintf(buf, "waxworks.%.3d", slot);
return buf;
}
char *AGOSEngine_Elvira2::genSaveName(int slot) {
static char buf[20];
if (getPlatform() == Common::kPlatformPC)
sprintf(buf, "elvira2-pc.%.3d", slot);
else
sprintf(buf, "elvira2.%.3d", slot);
return buf;
}
char *AGOSEngine_Elvira1::genSaveName(int slot) {
static char buf[20];
sprintf(buf, "elvira1.%.3d", slot);
return buf;
}
char *AGOSEngine::genSaveName(int slot) {
static char buf[20];
sprintf(buf, "pn.%.3d", slot);
return buf;
}
#ifdef ENABLE_AGOS2
void AGOSEngine_Feeble::quickLoadOrSave() {
// Quick loading and saving isn't possible in The Feeble Files or Puzzle Pack.
}
#endif
void AGOSEngine::quickLoadOrSave() {
// The function uses segments of code from the original game scripts
// to allow quick loading and saving, but isn't perfect.
//
// Unfortuntely this allows loading and saving in locations,
// which aren't supported, and will not restore correctly:
// Any overhead maps in Simon the Sorcerer 2
// Various locations in Elvira 1/2 and Waxworks where saving
// was disabled
// The floppy disk demo of Simon the Sorcerer 1 doesn't work.
if (getFeatures() & GF_DEMO)
return;
bool success;
Common::String buf;
char *filename = genSaveName(_saveLoadSlot);
if (_saveLoadType == 2) {
Subroutine *sub;
success = loadGame(genSaveName(_saveLoadSlot));
if (!success) {
buf = Common::String::format(_("Failed to load game state from file:\n\n%s"), filename);
} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
drawIconArray(2, me(), 0, 0);
setBitFlag(97, true);
sub = getSubroutineByID(100);
startSubroutine(sub);
} else if (getGameType() == GType_WW) {
sub = getSubroutineByID(66);
startSubroutine(sub);
} else if (getGameType() == GType_ELVIRA2) {
sub = getSubroutineByID(87);
startSubroutine(sub);
setBitFlag(7, false);
sub = getSubroutineByID(19);
startSubroutine(sub);
printStats();
sub = getSubroutineByID(28);
startSubroutine(sub);
setBitFlag(17, false);
sub = getSubroutineByID(207);
startSubroutine(sub);
sub = getSubroutineByID(71);
startSubroutine(sub);
} else if (getGameType() == GType_ELVIRA1) {
drawIconArray(2, me(), 0, 0);
sub = getSubroutineByID(265);
startSubroutine(sub);
sub = getSubroutineByID(129);
startSubroutine(sub);
sub = getSubroutineByID(131);
startSubroutine(sub);
}
} else {
success = saveGame(_saveLoadSlot, _saveLoadName);
if (!success)
buf = Common::String::format(_("Failed to save game state to file:\n\n%s"), filename);
}
if (!success) {
GUI::MessageDialog dialog(buf, "OK");
dialog.runModal();
} else if (_saveLoadType == 1) {
buf = Common::String::format(_("Successfully saved game state in file:\n\n%s"), filename);
GUI::TimedMessageDialog dialog(buf, 1500);
dialog.runModal();
}
_saveLoadType = 0;
}
bool AGOSEngine_Waxworks::confirmOverWrite(WindowBlock *window) {
Subroutine *sub = getSubroutineByID(80);
if (sub != NULL)
startSubroutineEx(sub);
if (_variableArray[253] == 0)
return true;
return false;
}
bool AGOSEngine_Elvira2::confirmOverWrite(WindowBlock *window) {
// Original verison never confirmed
return true;
}
bool AGOSEngine::confirmOverWrite(WindowBlock *window) {
const char *message1, *message2, *message3;
switch (_language) {
case Common::FR_FRA:
message1 = "\rFichier d/j; existant.\r\r";
message2 = " Ecrire pardessus ?\r\r";
message3 = " Oui Non";
break;
case Common::DE_DEU:
message1 = "\rDatei existiert bereits.\r\r";
message2 = " Ueberschreiben ?\r\r";
message3 = " Ja Nein";
break;
default:
message1 = "\r File already exists.\r\r";
message2 = " Overwrite it ?\r\r";
message3 = " Yes No";
break;
}
printScroll();
window->textColumn = 0;
window->textRow = 0;
window->textColumnOffset = 0;
window->textLength = 0; // Difference
for (; *message1; message1++)
windowPutChar(window, *message1);
for (; *message2; message2++)
windowPutChar(window, *message2);
for (; *message3; message3++)
windowPutChar(window, *message3);
if (confirmYesOrNo(120, 78) == 0x7FFF)
return true;
return false;
}
int16 AGOSEngine::matchSaveGame(const char *name, uint16 max) {
Common::InSaveFile *in;
char dst[10];
uint16 slot;
memset(dst, 0, sizeof(dst));
for (slot = 0; slot < max; slot++) {
if ((in = _saveFileMan->openForLoading(genSaveName(slot)))) {
in->read(dst, 8);
delete in;
if (!scumm_stricmp(name, dst)) {
return slot;
}
}
}
return -1;
}
void AGOSEngine::userGame(bool load) {
WindowBlock *window = _windowArray[4];
const char *message1;
int i = 0, numSaveGames;
char *name;
char buf[10];
numSaveGames = countSaveGames();
uint32 saveTime = getTime();
haltAnimation();
restart:
printScroll();
window->textColumn = 0;
window->textRow = 0;
window->textColumnOffset = 0;
window->textLength = 0; // Difference
switch (_language) {
case Common::FR_FRA:
message1 = "\rIns/rez disquette de\rsauvegarde de jeux &\rentrez nom de fichier:\r\r ";
break;
case Common::DE_DEU:
message1 = "\rLege Spielstandsdiskette ein. Dateinamen eingeben:\r\r ";
break;
default:
message1 = "\r Insert savegame data disk & enter filename:\r\r ";
break;
}
for (; *message1; message1++)
windowPutChar(window, *message1);
memset(buf, 0, 10);
name = buf;
_saveGameNameLen = 0;
while (!shouldQuit()) {
windowPutChar(window, 128);
_keyPressed.reset();
while (!shouldQuit()) {
delay(10);
if (_keyPressed.ascii && _keyPressed.ascii < 128) {
i = _keyPressed.ascii;
break;
}
}
userGameBackSpace(_windowArray[4], 8);
if (i == 10 || i == 13) {
break;
} else if (i == 8) {
// do_backspace
if (_saveGameNameLen) {
_saveGameNameLen--;
name[_saveGameNameLen] = 0;
userGameBackSpace(_windowArray[4], 8);
}
} else if (i >= 32 && _saveGameNameLen != 8) {
name[_saveGameNameLen++] = i;
windowPutChar(_windowArray[4], i);
}
}
if (_saveGameNameLen != 0) {
int16 slot = matchSaveGame(name, numSaveGames);
if (!load) {
if (slot >= 0 && !confirmOverWrite(window))
goto restart;
if (slot < 0)
slot = numSaveGames;
if (!saveGame(slot, name))
fileError(_windowArray[4], true);
} else {
if (slot < 0) {
fileError(_windowArray[4], false);
} else {
if (!loadGame(genSaveName(slot)))
fileError(_windowArray[4], false);
}
}
printStats();
}
restartAnimation();
_gameStoppedClock = getTime() - saveTime + _gameStoppedClock;
}
void AGOSEngine_Elvira2::listSaveGames(char *dst) {
Common::InSaveFile *in;
uint y, slot;
const uint8 num = (getGameType() == GType_WW) ? 3 : 4;
disableFileBoxes();
WindowBlock *window = _windowArray[num];
window->textRow = 0;
window->textColumn = 0;
window->textColumnOffset = 4;
windowPutChar(window, 12);
memset(dst, 0, 200);
slot = _saveLoadRowCurPos;
for (y = 0; y < 8; y++) {
window->textColumn = 0;
window->textColumnOffset = (getGameType() == GType_ELVIRA2) ? 4 : 0;
window->textLength = 0;
if ((in = _saveFileMan->openForLoading(genSaveName(slot++)))) {
in->read(dst, 8);
delete in;
const char *name = dst;
for (; *name; name++)
windowPutChar(window, *name);
enableBox(200 + y * 3 + 0);
}
dst+= 8;
if (getGameType() == GType_WW) {
window->textColumn = 7;
window->textColumnOffset = 4;
} else if (getGameType() == GType_ELVIRA2) {
window->textColumn = 8;
window->textColumnOffset = 0;
}
window->textLength = 0;
if ((in = _saveFileMan->openForLoading(genSaveName(slot++)))) {
in->read(dst, 8);
delete in;
const char *name = dst;
for (; *name; name++)
windowPutChar(window, *name);
enableBox(200 + y * 3 + 1);
}
dst+= 8;
window->textColumn = 15;
window->textColumnOffset = (getGameType() == GType_ELVIRA2) ? 4 : 0;
window->textLength = 0;
if ((in = _saveFileMan->openForLoading(genSaveName(slot++)))) {
in->read(dst, 8);
delete in;
const char *name = dst;
for (; *name; name++)
windowPutChar(window, *name);
enableBox(200 + y * 3 + 2);
}
dst+= 8;
windowPutChar(window, 13);
}
window->textRow = 9;
window->textColumn = 0;
window->textColumnOffset = 4;
window->textLength = 0;
_saveGameNameLen = 0;
}
void AGOSEngine_Elvira2::userGame(bool load) {
uint32 saveTime;
int i, numSaveGames;
char *name;
bool b;
char buf[200];
_saveOrLoad = load;
saveTime = getTime();
if (getGameType() == GType_ELVIRA2)
haltAnimation();
numSaveGames = countSaveGames();
_numSaveGameRows = numSaveGames;
_saveLoadRowCurPos = 1;
_saveLoadEdit = false;
const uint8 num = (getGameType() == GType_WW) ? 3 : 4;
listSaveGames(buf);
if (!load) {
WindowBlock *window = _windowArray[num];
int16 slot = -1;
name = buf + 192;
while (!shouldQuit()) {
windowPutChar(window, 128);
_saveLoadEdit = true;
i = userGameGetKey(&b, buf, 128);
if (b) {
if (i <= 223) {
if (!confirmOverWrite(window)) {
listSaveGames(buf);
continue;
}
if (!saveGame(_saveLoadRowCurPos + i, buf + i * 8))
fileError(_windowArray[num], true);
}
goto get_out;
}
userGameBackSpace(_windowArray[num], 8);
if (i == 10 || i == 13) {
slot = matchSaveGame(name, numSaveGames);
if (slot >= 0) {
if (!confirmOverWrite(window)) {
listSaveGames(buf);
continue;
}
}
break;
} else if (i == 8) {
// do_backspace
if (_saveGameNameLen) {
_saveGameNameLen--;
name[_saveGameNameLen] = 0;
userGameBackSpace(_windowArray[num], 8);
}
} else if (i >= 32 && _saveGameNameLen != 8) {
name[_saveGameNameLen++] = i;
windowPutChar(_windowArray[num], i);
}
}
if (_saveGameNameLen != 0) {
if (slot < 0)
slot = numSaveGames;
if (!saveGame(slot, buf + 192))
fileError(_windowArray[num], true);
}
} else {
i = userGameGetKey(&b, buf, 128);
if (i != 225) {
if (!loadGame(genSaveName(_saveLoadRowCurPos + i)))
fileError(_windowArray[num], false);
}
}
get_out:;
disableFileBoxes();
_gameStoppedClock = getTime() - saveTime + _gameStoppedClock;
if (getGameType() == GType_ELVIRA2)
restartAnimation();
}
int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) {
HitArea *ha;
*b = true;
_keyPressed.reset();
while (!shouldQuit()) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
do {
if (_saveLoadEdit && _keyPressed.ascii && _keyPressed.ascii < maxChar) {
*b = false;
return _keyPressed.ascii;
}
delay(10);
} while (_lastHitArea3 == 0 && !shouldQuit());
ha = _lastHitArea;
if (ha == NULL || ha->id < 200) {
} else if (ha->id == 225) {
return ha->id;
} else if (ha->id == 224) {
_saveGameNameLen = 0;
_saveLoadRowCurPos += 24;
if (_saveLoadRowCurPos >= _numSaveGameRows)
_saveLoadRowCurPos = 1;
listSaveGames(buf);
} else if (ha->id < 224) {
return ha->id - 200;
}
}
return 225;
}
void AGOSEngine_Simon1::listSaveGames(char *dst) {
Common::InSaveFile *in;
uint16 i, slot, lastSlot;
disableFileBoxes();
showMessageFormat("\xC");
memset(dst, 0, 108);
slot = _saveLoadRowCurPos;
while (_saveLoadRowCurPos + 6 > slot) {
if (!(in = _saveFileMan->openForLoading(genSaveName(slot))))
break;
in->read(dst, 18);
delete in;
lastSlot = slot;
if (slot < 10) {
showMessageFormat(" ");
} else if (_language == Common::HE_ISR) {
lastSlot = (slot % 10) * 10;
lastSlot += slot / 10;
}
showMessageFormat("%d", lastSlot);
if (_language == Common::HE_ISR && !(slot % 10))
showMessageFormat("0");
showMessageFormat(".%s\n", dst);
dst += 18;
slot++;
}
if (!_saveOrLoad) {
if (_saveLoadRowCurPos + 6 == slot) {
slot++;
} else {
if (slot < 10)
showMessageFormat(" ");
showMessageFormat("%d.\n", slot);
}
} else {
if (_saveLoadRowCurPos + 6 == slot) {
if ((in = _saveFileMan->openForLoading(genSaveName(slot)))) {
slot++;
delete in;
}
}
}
_saveDialogFlag = true;
i = slot - _saveLoadRowCurPos;
if (i != 7) {
i++;
if (!_saveOrLoad)
i++;
_saveDialogFlag = false;
}
if (!--i)
return;
do {
enableBox(208 + i - 1);
} while (--i);
}
const byte hebrewKeyTable[96] = {
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 90, 45, 85, 47, 48, 49, 50,
51, 52, 53, 54, 55, 56, 57, 83, 83, 90, 61, 85, 63, 35, 89, 80, 65, 66, 87,
75, 82, 73, 79, 71, 76, 74, 86, 78, 77, 84, 47, 88, 67, 64, 69, 68, 44, 81,
72, 70, 91, 92, 93, 94, 95, 96, 89, 80, 65, 66, 87, 75, 82, 73, 79, 71, 76,
74, 86, 78, 77, 84, 47, 88, 67, 64, 69, 68, 44, 81, 72, 70,
123, 124, 125, 126, 127,
};
void AGOSEngine_Simon1::userGame(bool load) {
uint32 saveTime;
int i, numSaveGames, result;
WindowBlock *window;
char *name;
bool b;
char buf[108];
int maxChar = (_language == Common::HE_ISR) ? 155: 128;
_saveOrLoad = load;
saveTime = getTime();
numSaveGames = countSaveGames();
if (!load)
numSaveGames++;
numSaveGames -= 6;
if (numSaveGames < 0)
numSaveGames = 0;
numSaveGames++;
_numSaveGameRows = numSaveGames;
_saveLoadRowCurPos = 1;
if (!load)
_saveLoadRowCurPos = numSaveGames;
_saveLoadEdit = false;
restart:;
i = userGameGetKey(&b, buf, maxChar);
if (i == 205)
goto get_out;
if (!load) {
// if_1
if_1:;
result = i;
disableBox(208 + i);
leaveHitAreaById(208 + i);
window = _windowArray[5];
window->textRow = result;
// init x offset with a 2 character savegame number + a period (18 pix)
if (_language == Common::HE_ISR) {
window->textColumn = 3;
window->textColumnOffset = 6;
} else {
window->textColumn = 2;
window->textColumnOffset = 2;
}
window->textLength = 3;
name = buf + i * 18;
// now process entire savegame name to get correct x offset for cursor
_saveGameNameLen = 0;
while (name[_saveGameNameLen]) {
if (_language == Common::HE_ISR) {
byte width = 6;
if (name[_saveGameNameLen] >= 64 && name[_saveGameNameLen] < 91)
width = _hebrewCharWidths [name[_saveGameNameLen] - 64];
window->textLength++;
window->textColumnOffset -= width;
if (window->textColumnOffset < width) {
window->textColumnOffset += 8;
window->textColumn++;
}
} else {
window->textLength++;
window->textColumnOffset += 6;
if (name[_saveGameNameLen] == 'i' || name[_saveGameNameLen] == 'l')
window->textColumnOffset -= 2;
if (window->textColumnOffset >= 8) {
window->textColumnOffset -= 8;
window->textColumn++;
}
}
_saveGameNameLen++;
}
while (!shouldQuit()) {
windowPutChar(window, 127);
_saveLoadEdit = true;
i = userGameGetKey(&b, buf, maxChar);
if (b) {
if (i == 205)
goto get_out;
enableBox(208 + result);
if (_saveLoadEdit) {
userGameBackSpace(_windowArray[5], 8);
}
goto if_1;
}
if (!_saveLoadEdit) {
enableBox(208 + result);
goto restart;
}
if (_language == Common::HE_ISR) {
if (i >= 128)
i -= 64;
else if (i >= 32)
i = hebrewKeyTable[i - 32];
}
userGameBackSpace(_windowArray[5], 8);
if (i == 10 || i == 13) {
break;
} else if (i == 8) {
// do_backspace
if (_saveGameNameLen) {
byte m, x;
_saveGameNameLen--;
m = name[_saveGameNameLen];
if (_language == Common::HE_ISR)
x = 8;
else
x = (name[_saveGameNameLen] == 'i' || name[_saveGameNameLen] == 'l') ? 1 : 8;
name[_saveGameNameLen] = 0;
userGameBackSpace(_windowArray[5], x, m);
}
} else if (i >= 32 && _saveGameNameLen != 17) {
name[_saveGameNameLen++] = i;
windowPutChar(_windowArray[5], i);
}
}
if (!saveGame(_saveLoadRowCurPos + result, buf + result * 18))
fileError(_windowArray[5], true);
} else {
if (!loadGame(genSaveName(_saveLoadRowCurPos + i)))
fileError(_windowArray[5], false);
}
get_out:;
disableFileBoxes();
_gameStoppedClock = getTime() - saveTime + _gameStoppedClock;
}
int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) {
HitArea *ha;
*b = true;
if (!_saveLoadEdit) {
listSaveGames(buf);
}
_keyPressed.reset();
while (!shouldQuit()) {
_lastHitArea = NULL;
_lastHitArea3 = NULL;
do {
if (_saveLoadEdit && _keyPressed.ascii && _keyPressed.ascii < maxChar) {
*b = false;
return _keyPressed.ascii;
}
delay(10);
} while (_lastHitArea3 == 0 && !shouldQuit());
ha = _lastHitArea;
if (ha == NULL || ha->id < 205) {
} else if (ha->id == 205) {
return ha->id;
} else if (ha->id == 206) {
if (_saveLoadRowCurPos != 1) {
if (_saveLoadRowCurPos < 7)
_saveLoadRowCurPos = 1;
else
_saveLoadRowCurPos -= 6;
_saveLoadEdit = false;
listSaveGames(buf);
}
} else if (ha->id == 207) {
if (_saveDialogFlag) {
_saveLoadRowCurPos += 6;
if (_saveLoadRowCurPos >= _numSaveGameRows)
_saveLoadRowCurPos = _numSaveGameRows;
_saveLoadEdit = false;
listSaveGames(buf);
}
} else if (ha->id < 214) {
return ha->id - 208;
}
}
return 205;
}
void AGOSEngine::disableFileBoxes() {
int i;
if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
for (i = 208; i != 214; i++)
disableBox(i);
} else {
for (i = 200; i != 224; i++)
disableBox(i);
}
}
void AGOSEngine::userGameBackSpace(WindowBlock *window, int x, byte b) {
byte oldTextColor;
windowPutChar(window, x, b);
oldTextColor = window->textColor;
window->textColor = window->fillColor;
if (_language == Common::HE_ISR) {
x = 128;
} else {
x += 120;
if (x != 128)
x = 129;
}
windowPutChar(window, x);
window->textColor = oldTextColor;
windowPutChar(window, 8);
}
void AGOSEngine::fileError(WindowBlock *window, bool saveError) {
const char *message1, *message2;
if (saveError) {
switch (_language) {
case Common::RU_RUS:
if (getGameType() == GType_SIMON2) {
message1 = "\r Mf sowrap+fts+.";
message2 = "\r Nzjb#a ejs#a.";
} else {
message1 = "\r Mf sowrap]fts].";
message2 = "\r Nzjb_a ejs_a.";
}
break;
case Common::PL_POL:
message1 = "\r Blad zapisu. ";
message2 = "\rBlad dysku. ";
break;
case Common::ES_ESP:
message1 = "\r Error al salvar";
message2 = "\r Intenta con otro disco";
break;
case Common::IT_ITA:
message1 = "\r Salvataggio non riuscito";
message2 = "\r Prova un""\x27""altro disco";
break;
case Common::FR_FRA:
message1 = "\r Echec sauvegarde";
message2 = "\rEssayez une autre disquette";
break;
case Common::DE_DEU:
message1 = "\r Sicherung erfolglos.";
message2 = "\rVersuche eine andere Diskette.";
break;
default:
message1 = "\r Save failed.";
message2 = "\r Disk error.";
break;
}
} else {
switch (_language) {
case Common::RU_RUS:
if (getGameType() == GType_SIMON2) {
message1 = "\r Mf ^adruhafts+.";
message2 = "\r Takm pf pakefp.";
} else {
message1 = "\r Mf ^adruhafts].";
message2 = "\r Takm pf pakefp.";
}
break;
case Common::PL_POL:
message1 = "\r Blad odczytu. ";
message2 = "\r Nie znaleziono pliku.";
break;
case Common::ES_ESP:
message1 = "\r Error al cargar";
message2 = "\r Archivo no encontrado";
break;
case Common::IT_ITA:
message1 = "\r Caricamento non riuscito";
message2 = "\r File non trovato";
break;
case Common::FR_FRA:
message1 = "\r Echec chargement";
message2 = "\r Fichier introuvable";
break;
case Common::DE_DEU:
message1 = "\r Laden erfolglos.";
message2 = "\r Datei nicht gefunden.";
break;
default:
message1 = "\r Load failed.";
message2 = "\r File not found.";
break;
}
}
if (getGameType() == GType_ELVIRA1) {
printScroll();
window->textColumn = 0;
window->textRow = 0;
window->textColumnOffset = 0;
window->textLength = 0; // Difference
} else {
windowPutChar(window, 12);
}
for (; *message1; message1++)
windowPutChar(window, *message1);
for (; *message2; message2++)
windowPutChar(window, *message2);
waitWindow(window);
}
uint16 readItemID(Common::SeekableReadStream *f) {
uint32 val = f->readUint32BE();
if (val == 0xFFFFFFFF)
return 0;
return val + 1;
}
void writeItemID(Common::WriteStream *f, uint16 val) {
if (val == 0)
f->writeUint32BE(0xFFFFFFFF);
else
f->writeUint32BE(val - 1);
}
bool AGOSEngine::loadGame(const char *filename, bool restartMode) {
char ident[100];
Common::SeekableReadStream *f = NULL;
uint num, item_index, i;
_videoLockOut |= 0x100;
if (restartMode) {
// Load restart state
f = _archives.createReadStreamForMember(filename);
} else {
f = _saveFileMan->openForLoading(filename);
}
if (f == NULL) {
_videoLockOut &= ~0x100;
return false;
}
if (!restartMode) {
f->read(ident, 8);
}
num = f->readUint32BE();
if (f->readUint32BE() != 0xFFFFFFFF || num != _itemArrayInited - 1) {
delete f;
_videoLockOut &= ~0x100;
return false;
}
f->readUint32BE();
f->readUint32BE();
_noParentNotify = true;
// add all timers
killAllTimers();
for (num = f->readUint32BE(); num; num--) {
uint32 timeout = f->readUint32BE();
uint16 subroutine_id = f->readUint16BE();
addTimeEvent(timeout, subroutine_id);
}
item_index = 1;
for (num = _itemArrayInited - 1; num; num--) {
Item *item = _itemArrayPtr[item_index++], *parent_item;
parent_item = derefItem(readItemID(f));
setItemParent(item, parent_item);
item->state = f->readUint16BE();
item->classFlags = f->readUint16BE();
SubObject *o = (SubObject *)findChildOfType(item, kObjectType);
if (o) {
o->objectSize = f->readUint16BE();
o->objectWeight = f->readUint16BE();
}
SubPlayer *p = (SubPlayer *)findChildOfType(item, kPlayerType);
if (p) {
p->score = f->readUint32BE();
p->level = f->readUint16BE();
p->size = f->readUint16BE();
p->weight = f->readUint16BE();
p->strength = f->readUint16BE();
}
SubUserFlag *u = (SubUserFlag *)findChildOfType(item, kUserFlagType);
if (u) {
for (i = 0; i != 8; i++) {
u->userFlags[i] = f->readUint16BE();
}
u->userItems[0] = readItemID(f);
}
}
// read the variables
for (i = 0; i != _numVars; i++) {
writeVariable(i, f->readUint16BE());
}
if (f->err()) {
error("load failed");
}
delete f;
_noParentNotify = false;
_videoLockOut &= ~0x100;
return true;
}
bool AGOSEngine::saveGame(uint slot, const char *caption) {
Common::OutSaveFile *f;
uint item_index, num_item, i;
TimeEvent *te;
uint32 curTime = getTime();
uint32 gsc = _gameStoppedClock;
_videoLockOut |= 0x100;
f = _saveFileMan->openForSaving(genSaveName(slot));
if (f == NULL) {
_videoLockOut &= ~0x100;
return false;
}
f->write(caption, 8);
f->writeUint32BE(_itemArrayInited - 1);
f->writeUint32BE(0xFFFFFFFF);
f->writeUint32BE(curTime);
f->writeUint32BE(0);
i = 0;
for (te = _firstTimeStruct; te; te = te->next)
i++;
f->writeUint32BE(i);
for (te = _firstTimeStruct; te; te = te->next) {
f->writeUint32BE(te->time - curTime + gsc);
f->writeUint16BE(te->subroutine_id);
}
item_index = 1;
for (num_item = _itemArrayInited - 1; num_item; num_item--) {
Item *item = _itemArrayPtr[item_index++];
writeItemID(f, item->parent);
f->writeUint16BE(item->state);
f->writeUint16BE(item->classFlags);
SubObject *o = (SubObject *)findChildOfType(item, kObjectType);
if (o) {
f->writeUint16BE(o->objectSize);
f->writeUint16BE(o->objectWeight);
}
SubPlayer *p = (SubPlayer *)findChildOfType(item, kPlayerType);
if (p) {
f->writeUint32BE(p->score);
f->writeUint16BE(p->level);
f->writeUint16BE(p->size);
f->writeUint16BE(p->weight);
f->writeUint16BE(p->strength);
}
SubUserFlag *u = (SubUserFlag *)findChildOfType(item, kUserFlagType);
if (u) {
for (i = 0; i != 8; i++) {
f->writeUint16BE(u->userFlags[i]);
}
writeItemID(f, u->userItems[0]);
}
}
// write the variables
for (i = 0; i != _numVars; i++) {
f->writeUint16BE(readVariable(i));
}
f->finalize();
bool result = !f->err();
delete f;
_videoLockOut &= ~0x100;
return result;
}
bool AGOSEngine_Elvira2::loadGame(const char *filename, bool restartMode) {
char ident[100];
Common::SeekableReadStream *f = NULL;
uint num, item_index, i, j;
_videoLockOut |= 0x100;
if (restartMode) {
// Load restart state
f = _archives.createReadStreamForMember(filename);
} else {
f = _saveFileMan->openForLoading(filename);
}
if (f == NULL) {
_videoLockOut &= ~0x100;
return false;
}
if (getGameType() == GType_PP) {
// No caption
} else if (getGameType() == GType_FF) {
f->read(ident, 100);
} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
f->read(ident, 18);
} else if (!restartMode) {
f->read(ident, 8);
}
num = f->readUint32BE();
if (f->readUint32BE() != 0xFFFFFFFF || num != _itemArrayInited - 1) {
delete f;
_videoLockOut &= ~0x100;
return false;
}
f->readUint32BE();
f->readUint32BE();
_noParentNotify = true;
// add all timers
killAllTimers();
for (num = f->readUint32BE(); num; num--) {
uint32 timeout = f->readUint32BE();
uint16 subroutine_id = f->readUint16BE();
addTimeEvent(timeout, subroutine_id);
}
if (getGameType() == GType_WW && getPlatform() == Common::kPlatformPC) {
for (uint s = 0; s < _numRoomStates; s++) {
_roomStates[s].state = f->readUint16BE();
_roomStates[s].classFlags = f->readUint16BE();
_roomStates[s].roomExitStates = f->readUint16BE();
}
f->readUint16BE();
uint16 room = _currentRoom;
_currentRoom = f->readUint16BE();
if (_roomsListPtr) {
byte *p = _roomsListPtr;
if (room == _currentRoom) {
for (;;) {
uint16 minNum = READ_BE_UINT16(p); p += 2;
if (minNum == 0)
break;
uint16 maxNum = READ_BE_UINT16(p); p += 2;
for (uint16 z = minNum; z <= maxNum; z++) {
uint16 itemNum = z + 2;
Item *item = derefItem(itemNum);
num = (itemNum - _itemArrayInited);
item->state = _roomStates[num].state;
item->classFlags = _roomStates[num].classFlags;
SubRoom *subRoom = (SubRoom *)findChildOfType(item, kRoomType);
subRoom->roomExitStates = _roomStates[num].roomExitStates;
}
}
} else {
for (;;) {
uint16 minNum = READ_BE_UINT16(p); p += 2;
if (minNum == 0)
break;
uint16 maxNum = READ_BE_UINT16(p); p += 2;
for (uint16 z = minNum; z <= maxNum; z++) {
uint16 itemNum = z + 2;
Item *item = derefItem(itemNum);
item->parent = 0;
}
}
}
}
if (room != _currentRoom) {
_roomsListPtr = 0;
loadRoomItems(_currentRoom);
}
}
item_index = 1;
for (num = _itemArrayInited - 1; num; num--) {
Item *item = _itemArrayPtr[item_index++], *parent_item;
if ((getGameType() == GType_WW && getPlatform() == Common::kPlatformAmiga) ||
getGameType() == GType_ELVIRA2) {
parent_item = derefItem(readItemID(f));
setItemParent(item, parent_item);
} else {
uint parent = f->readUint16BE();
uint next = f->readUint16BE();
parent_item = derefItem(parent);
setItemParent(item, parent_item);
if (parent_item == NULL) {
item->parent = parent;
item->next = next;
}
}
item->state = f->readUint16BE();
item->classFlags = f->readUint16BE();
SubRoom *r = (SubRoom *)findChildOfType(item, kRoomType);
if (r) {
r->roomExitStates = f->readUint16BE();
}
SubSuperRoom *sr = (SubSuperRoom *)findChildOfType(item, kSuperRoomType);
if (sr) {
uint16 n = sr->roomX * sr->roomY * sr->roomZ;
for (i = j = 0; i != n; i++)
sr->roomExitStates[j++] = f->readUint16BE();
}
SubObject *o = (SubObject *)findChildOfType(item, kObjectType);
if (o) {
o->objectFlags = f->readUint32BE();
i = o->objectFlags & 1;
for (j = 1; j < 16; j++) {
if (o->objectFlags & (1 << j)) {
o->objectFlagValue[i++] = f->readUint16BE();
}
}
}
SubUserFlag *u = (SubUserFlag *)findChildOfType(item, kUserFlagType);
if (u) {
for (i = 0; i != 4; i++) {
u->userFlags[i] = f->readUint16BE();
}
}
}
// read the variables
for (i = 0; i != _numVars; i++) {
writeVariable(i, f->readUint16BE());
}
// read the items in item store
for (i = 0; i != _numItemStore; i++) {
if (getGameType() == GType_WW && getPlatform() == Common::kPlatformAmiga) {
_itemStore[i] = derefItem(f->readUint16BE() / 16);
} else if (getGameType() == GType_ELVIRA2) {
if (getPlatform() == Common::kPlatformPC) {
_itemStore[i] = derefItem(readItemID(f));
} else {
_itemStore[i] = derefItem(f->readUint16BE() / 18);
}
} else {
_itemStore[i] = derefItem(f->readUint16BE());
}
}
// Read the bits in array 1
for (i = 0; i != _numBitArray1; i++)
_bitArray[i] = f->readUint16BE();
// Read the bits in array 2
for (i = 0; i != _numBitArray2; i++)
_bitArrayTwo[i] = f->readUint16BE();
// Read the bits in array 3
for (i = 0; i != _numBitArray3; i++)
_bitArrayThree[i] = f->readUint16BE();
if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
_superRoomNumber = f->readUint16BE();
}
if (f->err()) {
error("load failed");
}
delete f;
_noParentNotify = false;
_videoLockOut &= ~0x100;
// The floppy disk versions of Simon the Sorcerer 2 block changing
// to scrolling rooms, if the copy protection fails. But the copy
// protection flags are never set in the CD version.
// Setting this copy protection flag, allows saved games to be shared
// between all versions of Simon the Sorcerer 2.
if (getGameType() == GType_SIMON2) {
setBitFlag(135, 1);
}
return true;
}
bool AGOSEngine_Elvira2::saveGame(uint slot, const char *caption) {
Common::OutSaveFile *f;
uint item_index, num_item, i, j;
TimeEvent *te;
uint32 curTime = getTime();
uint32 gsc = _gameStoppedClock;
_videoLockOut |= 0x100;
f = _saveFileMan->openForSaving(genSaveName(slot));
if (f == NULL) {
_videoLockOut &= ~0x100;
return false;
}
if (getGameType() == GType_PP) {
// No caption
} else if (getGameType() == GType_FF) {
f->write(caption, 100);
} else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) {
f->write(caption, 18);
} else {
f->write(caption, 8);
}
f->writeUint32BE(_itemArrayInited - 1);
f->writeUint32BE(0xFFFFFFFF);
f->writeUint32BE(curTime);
f->writeUint32BE(0);
i = 0;
for (te = _firstTimeStruct; te; te = te->next)
i++;
f->writeUint32BE(i);
if (getGameType() == GType_FF && _clockStopped)
gsc += (getTime() - _clockStopped);
for (te = _firstTimeStruct; te; te = te->next) {
f->writeUint32BE(te->time - curTime + gsc);
f->writeUint16BE(te->subroutine_id);
}
if (getGameType() == GType_WW && getPlatform() == Common::kPlatformPC) {
if (_roomsListPtr) {
byte *p = _roomsListPtr;
for (;;) {
uint16 minNum = READ_BE_UINT16(p); p += 2;
if (minNum == 0)
break;
uint16 maxNum = READ_BE_UINT16(p); p += 2;
for (uint16 z = minNum; z <= maxNum; z++) {
uint16 itemNum = z + 2;
Item *item = derefItem(itemNum);
uint16 num = (itemNum - _itemArrayInited);
_roomStates[num].state = item->state;
_roomStates[num].classFlags = item->classFlags;
SubRoom *subRoom = (SubRoom *)findChildOfType(item, kRoomType);
_roomStates[num].roomExitStates = subRoom->roomExitStates;
}
}
}
for (uint s = 0; s < _numRoomStates; s++) {
f->writeUint16BE(_roomStates[s].state);
f->writeUint16BE(_roomStates[s].classFlags);
f->writeUint16BE(_roomStates[s].roomExitStates);
}
f->writeUint16BE(0);
f->writeUint16BE(_currentRoom);
}
item_index = 1;
for (num_item = _itemArrayInited - 1; num_item; num_item--) {
Item *item = _itemArrayPtr[item_index++];
if ((getGameType() == GType_WW && getPlatform() == Common::kPlatformAmiga) ||
getGameType() == GType_ELVIRA2) {
writeItemID(f, item->parent);
} else {
f->writeUint16BE(item->parent);
f->writeUint16BE(item->next);
}
f->writeUint16BE(item->state);
f->writeUint16BE(item->classFlags);
SubRoom *r = (SubRoom *)findChildOfType(item, kRoomType);
if (r) {
f->writeUint16BE(r->roomExitStates);
}
SubSuperRoom *sr = (SubSuperRoom *)findChildOfType(item, kSuperRoomType);
if (sr) {
uint16 n = sr->roomX * sr->roomY * sr->roomZ;
for (i = j = 0; i != n; i++)
f->writeUint16BE(sr->roomExitStates[j++]);
}
SubObject *o = (SubObject *)findChildOfType(item, kObjectType);
if (o) {
f->writeUint32BE(o->objectFlags);
i = o->objectFlags & 1;
for (j = 1; j < 16; j++) {
if (o->objectFlags & (1 << j)) {
f->writeUint16BE(o->objectFlagValue[i++]);
}
}
}
SubUserFlag *u = (SubUserFlag *)findChildOfType(item, kUserFlagType);
if (u) {
for (i = 0; i != 4; i++) {
f->writeUint16BE(u->userFlags[i]);
}
}
}
// write the variables
for (i = 0; i != _numVars; i++) {
f->writeUint16BE(readVariable(i));
}
// write the items in item store
for (i = 0; i != _numItemStore; i++) {
if (getGameType() == GType_WW && getPlatform() == Common::kPlatformAmiga) {
f->writeUint16BE(itemPtrToID(_itemStore[i]) * 16);
} else if (getGameType() == GType_ELVIRA2) {
if (getPlatform() == Common::kPlatformPC) {
writeItemID(f, itemPtrToID(_itemStore[i]));
} else {
f->writeUint16BE(itemPtrToID(_itemStore[i]) * 18);
}
} else {
f->writeUint16BE(itemPtrToID(_itemStore[i]));
}
}
// Write the bits in array 1
for (i = 0; i != _numBitArray1; i++)
f->writeUint16BE(_bitArray[i]);
// Write the bits in array 2
for (i = 0; i != _numBitArray2; i++)
f->writeUint16BE(_bitArrayTwo[i]);
// Write the bits in array 3
for (i = 0; i != _numBitArray3; i++)
f->writeUint16BE(_bitArrayThree[i]);
if (getGameType() == GType_ELVIRA2 || getGameType() == GType_WW) {
f->writeUint16BE(_superRoomNumber);
}
f->finalize();
bool result = !f->err();
delete f;
_videoLockOut &= ~0x100;
return result;
}
// Personal Nightmare specific
bool AGOSEngine_PN::badload(int8 errorNum) {
if (errorNum == -2)
return 0;
// Load error recovery routine
// Clear any stack
while (_stackbase != NULL) {
dumpstack();
}
// Restart from process 1
_tagOfActiveDoline = 1;
_dolineReturnVal = 3;
return 1;
}
void AGOSEngine_PN::getFilename() {
_noScanFlag = 1;
clearInputLine();
memset(_saveFile, 0, sizeof(_saveFile));
while (!shouldQuit() && !strlen(_saveFile)) {
const char *msg = "File name : ";
pcf((unsigned char)'\n');
while (*msg)
pcf((unsigned char)*msg++);
interact(_saveFile, 8);
pcf((unsigned char)'\n');
_noScanFlag = 0;
}
}
int AGOSEngine_PN::loadFile(char *name) {
Common::InSaveFile *f;
haltAnimation();
f = _saveFileMan->openForLoading(name);
if (f == NULL) {
restartAnimation();
return -2;
}
f->read(_saveFile, 8);
if (f->readByte() != 41) {
restartAnimation();
delete f;
return -2;
}
if (f->readByte() != 33) {
restartAnimation();
delete f;
return -2;
}
// TODO: Make endian safe
if (!f->read(_dataBase + _quickptr[2], (int)(_quickptr[6] - _quickptr[2]))) {
restartAnimation();
delete f;
return -1;
}
delete f;
restartAnimation();
dbtosysf();
return 0;
}
int AGOSEngine_PN::saveFile(char *name) {
Common::OutSaveFile *f;
sysftodb();
haltAnimation();
f = _saveFileMan->openForSaving(name);
if (f == NULL) {
restartAnimation();
const char *msg = "Couldn't save. ";
pcf((unsigned char)'\n');
while (*msg)
pcf((unsigned char)*msg++);
return 0;
}
f->write(_saveFile, 8);
f->writeByte(41);
f->writeByte(33);
// TODO: Make endian safe
if (!f->write(_dataBase + _quickptr[2], (int)(_quickptr[6] - _quickptr[2]))) {
delete f;
restartAnimation();
error("Couldn't save ");
return 0; // for compilers that don't support NORETURN
}
f->finalize();
delete f;
restartAnimation();
return 1;
}
void AGOSEngine_PN::sysftodb() {
uint32 pos = _quickptr[2];
int ct = 0;
while (ct < (getptr(49L) / 2)) {
_dataBase[pos] = (uint8)(_variableArray[ct] % 256);
_dataBase[pos + 1] = (uint8)(_variableArray[ct] / 256);
pos+=2;
ct++;
}
}
void AGOSEngine_PN::dbtosysf() {
uint32 pos = _quickptr[2];
int ct = 0;
while (ct < (getptr(49L) / 2)) {
_variableArray[ct] = _dataBase[pos] + 256 * _dataBase[pos + 1];
pos += 2;
ct++;
}
}
} // End of namespace AGOS