aboutsummaryrefslogtreecommitdiff
path: root/engines/sword1/control.cpp
diff options
context:
space:
mode:
authorFilippos Karapetis2008-11-18 16:31:55 +0000
committerFilippos Karapetis2008-11-18 16:31:55 +0000
commit7650b61d27aa848d0f22f59c43d23787c04a65b7 (patch)
tree4edfb32d3d705c345308dbda6352c8244ea9d51c /engines/sword1/control.cpp
parentbdbae740a60cdf153a1ab082329afa92f56ffd22 (diff)
downloadscummvm-rg350-7650b61d27aa848d0f22f59c43d23787c04a65b7.tar.gz
scummvm-rg350-7650b61d27aa848d0f22f59c43d23787c04a65b7.tar.bz2
scummvm-rg350-7650b61d27aa848d0f22f59c43d23787c04a65b7.zip
Applied a slightly modified version of my patch #2307224 - "BS1: Save/load overhaul"
svn-id: r35111
Diffstat (limited to 'engines/sword1/control.cpp')
-rw-r--r--engines/sword1/control.cpp302
1 files changed, 233 insertions, 69 deletions
diff --git a/engines/sword1/control.cpp b/engines/sword1/control.cpp
index 49cd2c4b34..78b09e5c82 100644
--- a/engines/sword1/control.cpp
+++ b/engines/sword1/control.cpp
@@ -23,6 +23,7 @@
*
*/
+#include <time.h> // for extended infos
#include "common/file.h"
#include "common/util.h"
@@ -31,6 +32,7 @@
#include "common/system.h"
#include "common/config-manager.h"
+#include "graphics/thumbnail.h"
#include "gui/message.h"
#include "sword1/control.h"
@@ -509,6 +511,8 @@ void Control::setupMainPanel(void) {
}
void Control::setupSaveRestorePanel(bool saving) {
+ readSavegameDescriptions();
+
FrameHeader *savePanel = _resMan->fetchFrame(_resMan->openFetchRes(SR_WINDOW), 0);
uint16 panelX = (640 - _resMan->getUint16(savePanel->width)) / 2;
uint16 panelY = (480 - _resMan->getUint16(savePanel->height)) / 2;
@@ -690,22 +694,20 @@ bool Control::keyAccepted(uint16 ascii) {
void Control::handleSaveKey(Common::KeyState kbd) {
if (_selectedSavegame < 255) {
- uint8 len = strlen((char*)_saveNames[_selectedSavegame]);
+ uint8 len = _saveNames[_selectedSavegame].size();
if ((kbd.keycode == Common::KEYCODE_BACKSPACE) && len) // backspace
- _saveNames[_selectedSavegame][len - 1] = '\0';
+ _saveNames[_selectedSavegame].deleteLastChar();
else if (keyAccepted(kbd.ascii) && (len < 31)) {
- _saveNames[_selectedSavegame][len] = kbd.ascii;
- _saveNames[_selectedSavegame][len + 1] = '\0';
+ _saveNames[_selectedSavegame].insertChar(kbd.ascii, len);
}
showSavegameNames();
}
}
bool Control::saveToFile(void) {
- if ((_selectedSavegame == 255) || !strlen((char*)_saveNames[_selectedSavegame]))
+ if ((_selectedSavegame == 255) || _saveNames[_selectedSavegame].size() == 0)
return false; // no saveslot selected or no name entered
saveGameToFile(_selectedSavegame);
- writeSavegameDescriptions();
return true;
}
@@ -717,33 +719,40 @@ bool Control::restoreFromFile(void) {
}
void Control::readSavegameDescriptions(void) {
- Common::InSaveFile *inf;
- inf = _saveFileMan->openForLoading("SAVEGAME.INF");
- _saveScrollPos = _saveFiles = 0;
- _selectedSavegame = 255;
- for (uint8 cnt = 0; cnt < 64; cnt++) {
- memset(_saveNames[cnt], 0, sizeof(_saveNames[cnt]));
- }
- if (inf) {
- uint8 curFileNum = 0;
- uint8 ch;
- do {
- uint8 pos = 0;
- do {
- ch = inf->readByte();
- if (pos < sizeof(_saveNames[curFileNum]) - 1) {
- if ((ch == 10) || (ch == 255) || (inf->eos()))
- _saveNames[curFileNum][pos++] = '\0';
- else if (ch >= 32)
- _saveNames[curFileNum][pos++] = ch;
- }
- } while ((ch != 10) && (ch != 255) && (!inf->eos()));
- curFileNum++;
- } while ((ch != 255) && (!inf->eos()));
- _saveFiles = curFileNum;
- _numSaves = _saveFiles;
+ char saveName[40];
+ Common::String pattern = ConfMan.get("gameid") + ".???";
+ Common::StringList filenames = _saveFileMan->listSavefiles(pattern.c_str());
+ sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
+
+ int num = 0;
+ int slotNum = 0;
+ for (Common::StringList::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ // Obtain the last 3 digits of the filename, since they correspond to the save slot
+ slotNum = atoi(file->c_str() + file->size() - 3);
+
+ while (num < slotNum) {
+ _saveNames.push_back("");
+ num++;
+ }
+
+ if (slotNum >= 0 && slotNum <= 999) {
+ num++;
+ Common::InSaveFile *in = _saveFileMan->openForLoading(file->c_str());
+ if (in) {
+ in->readUint32LE(); // header
+ in->read(saveName, 40);
+ _saveNames.push_back(saveName);
+ delete in;
+ }
+ }
}
- delete inf;
+
+ for (int i = _saveNames.size(); i < 1000; i++)
+ _saveNames.push_back("");
+
+ _saveScrollPos = 0;
+ _selectedSavegame = 255;
+ _saveFiles = _numSaves = _saveNames.size();
}
int Control::displayMessage(const char *altButton, const char *message, ...) {
@@ -760,43 +769,59 @@ int Control::displayMessage(const char *altButton, const char *message, ...) {
return result;
}
-void Control::writeSavegameDescriptions(void) {
- Common::OutSaveFile *outf;
- outf = _saveFileMan->openForSaving("SAVEGAME.INF");
+bool Control::savegamesExist(void) {
+ Common::String pattern = ConfMan.get("gameid") + ".???";
+ Common::StringList saveNames = _saveFileMan->listSavefiles(pattern.c_str());
+ return saveNames.size() > 0;
+}
- if (!outf) {
- // Display an error message, and do nothing
- displayMessage(0, "Can't create SAVEGAME.INF. (%s)", _saveFileMan->popErrorDesc().c_str());
+void Control::checkForOldSaveGames() {
+ Common::InSaveFile *inf = _saveFileMan->openForLoading("SAVEGAME.INF");
+
+ if (!inf) {
+ delete inf;
return;
}
- // if the player accidently clicked the last slot and then deselected it again,
- // we'd still have _saveFiles == 64, so get rid of the empty end.
- while (strlen((char*)_saveNames[_saveFiles - 1]) == 0)
- _saveFiles--;
- for (uint8 cnt = 0; cnt < _saveFiles; cnt++) {
- int len = strlen((char*)_saveNames[cnt]);
- if (len > 0)
- outf->write(_saveNames[cnt], len);
- if (cnt < _saveFiles - 1)
- outf->writeByte(10);
- else
- outf->writeByte(255);
+ GUI::MessageDialog dialog0(
+ "ScummVM found that you have old savefiles for Broken Sword 1 that should be converted.\n"
+ "The old save game format is no longer supported, so you will not be able to load your games if you don't convert them.\n\n"
+ "Press OK to convert them now, otherwise you will be asked again the next time you start the game.\n", "OK", "Cancel");
+
+ int choice = dialog0.runModal();
+ if (choice == GUI::kMessageCancel) {
+ // user pressed cancel
+ return;
}
- outf->finalize();
- if (outf->ioFailed())
- displayMessage(0, "Can't write to SAVEGAME.INF. Device full? (%s)", _saveFileMan->popErrorDesc().c_str());
- delete outf;
-}
-bool Control::savegamesExist(void) {
- bool retVal = false;
- Common::InSaveFile *inf;
- inf = _saveFileMan->openForLoading("SAVEGAME.INF");
- if (inf)
- retVal = true;
- delete inf;
- return retVal;
+ // Convert every save slot we find in the index file to the new format
+ uint8 saveName[32];
+ uint8 slot = 0;
+ uint8 ch;
+
+ memset(saveName, 0, sizeof(saveName));
+
+ do {
+ uint8 pos = 0;
+ do {
+ ch = inf->readByte();
+ if (pos < sizeof(saveName) - 1) {
+ if ((ch == 10) || (ch == 255) || (inf->eos()))
+ saveName[pos++] = '\0';
+ else if (ch >= 32)
+ saveName[pos++] = ch;
+ }
+ } while ((ch != 10) && (ch != 255) && (!inf->eos()));
+
+ if (pos > 1) // if the slot has a description
+ convertSaveGame(slot, (char*)saveName);
+ slot++;
+ } while ((ch != 255) && (!inf->eos()));
+
+ delete inf;
+
+ // Delete index file
+ _saveFileMan->removeSavefile("SAVEGAME.INF");
}
void Control::showSavegameNames(void) {
@@ -805,7 +830,7 @@ void Control::showSavegameNames(void) {
uint8 textMode = TEXT_LEFT_ALIGN;
uint16 ycoord = _saveButtons[cnt].y + 2;
uint8 str[40];
- sprintf((char*)str, "%d. %s", cnt + _saveScrollPos + 1, _saveNames[cnt + _saveScrollPos]);
+ sprintf((char*)str, "%d. %s", cnt + _saveScrollPos + 1, _saveNames[cnt + _saveScrollPos].c_str());
if (cnt + _saveScrollPos == _selectedSavegame) {
textMode |= TEXT_RED_FONT;
ycoord += 2;
@@ -821,10 +846,10 @@ void Control::saveNameSelect(uint8 id, bool saving) {
_buttons[id - BUTTON_SAVE_SELECT1]->setSelected(1);
uint8 num = (id - BUTTON_SAVE_SELECT1) + _saveScrollPos;
if (saving && (_selectedSavegame != 255)) // the player may have entered something, clear it again
- strcpy((char*)_saveNames[_selectedSavegame], (char*)_oldName);
+ _saveNames[_selectedSavegame] = _oldName;
if (num < _saveFiles) {
_selectedSavegame = num;
- strcpy((char*)_oldName, (char*)_saveNames[num]); // save for later
+ _oldName = _saveNames[num]; // save for later
} else {
if (!saving)
_buttons[id - BUTTON_SAVE_SELECT1]->setSelected(0); // no save in slot, deselect it
@@ -832,7 +857,7 @@ void Control::saveNameSelect(uint8 id, bool saving) {
if (_saveFiles <= num)
_saveFiles = num + 1;
_selectedSavegame = num;
- _oldName[0] = '\0';
+ _oldName.clear();
}
}
if (_selectedSavegame < 255)
@@ -950,7 +975,7 @@ void Control::renderVolumeBar(uint8 id, uint8 volL, uint8 volR) {
void Control::saveGameToFile(uint8 slot) {
char fName[15];
uint16 cnt;
- sprintf(fName, "SAVEGAME.%03d", slot);
+ sprintf(fName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
uint16 liveBuf[TOTAL_SECTIONS];
Common::OutSaveFile *outf;
outf = _saveFileMan->openForSaving(fName);
@@ -960,6 +985,31 @@ void Control::saveGameToFile(uint8 slot) {
return;
}
+ outf->writeUint32LE(SAVEGAME_HEADER);
+ outf->write(_saveNames[slot].c_str(), 40);
+ outf->writeByte(SAVEGAME_VERSION);
+
+ // FIXME: at this point, we can't save a thumbnail of the game screen, as the save menu is shown
+#if 0
+ outf->writeByte(HAS_THUMBNAIL);
+
+ // Thumbnail
+ Graphics::saveThumbnail(*outf);
+#else
+ outf->writeByte(NO_THUMBNAIL);
+#endif
+
+ // Date / time
+ tm curTime;
+ _system->getTimeAndDate(curTime);
+
+ uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | (curTime.tm_year + 1900) & 0xFFFF;
+ uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | (curTime.tm_min) & 0xFF;
+
+ outf->writeUint32BE(saveDate);
+ outf->writeUint16BE(saveTime);
+ // TODO: played time
+
_objMan->saveLiveList(liveBuf);
for (cnt = 0; cnt < TOTAL_SECTIONS; cnt++)
outf->writeUint16LE(liveBuf[cnt]);
@@ -987,7 +1037,7 @@ void Control::saveGameToFile(uint8 slot) {
bool Control::restoreGameFromFile(uint8 slot) {
char fName[15];
uint16 cnt;
- sprintf(fName, "SAVEGAME.%03d", slot);
+ sprintf(fName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
Common::InSaveFile *inf;
inf = _saveFileMan->openForLoading(fName);
if (!inf) {
@@ -996,6 +1046,36 @@ bool Control::restoreGameFromFile(uint8 slot) {
return false;
}
+ uint saveHeader = inf->readUint32LE();
+ if (saveHeader != SAVEGAME_HEADER) {
+ // Display an error message, and do nothing
+ displayMessage(0, "Save game '%s' is corrupt", fName);
+ return false;
+ }
+
+ inf->skip(40); // skip description
+ uint8 saveVersion = inf->readByte();
+
+ if (saveVersion != SAVEGAME_VERSION) {
+ warning("Different save game version");
+ return false;
+ }
+
+ bool hasThumbnail = inf->readByte();
+
+ if (hasThumbnail) {
+ // We don't need the thumbnail here, so just read it and discard it
+ Graphics::Surface *thumbnail = new Graphics::Surface();
+ assert(thumbnail);
+ Graphics::loadThumbnail(*inf, *thumbnail);
+ delete thumbnail;
+ thumbnail = 0;
+ }
+
+ inf->readUint32BE(); // save date
+ inf->readUint16BE(); // save time
+ // TODO: played time
+
_restoreBuf = (uint8*)malloc(
TOTAL_SECTIONS * 2 +
NUM_SCRIPT_VARS * 4 +
@@ -1026,6 +1106,90 @@ bool Control::restoreGameFromFile(uint8 slot) {
return true;
}
+bool Control::convertSaveGame(uint8 slot, char* desc) {
+ char oldFileName[15];
+ char newFileName[40];
+ sprintf(oldFileName, "SAVEGAME.%03d", slot);
+ sprintf(newFileName, "%s.%03d", ConfMan.get("gameid").c_str(), slot);
+ uint8 *saveData;
+ int dataSize;
+
+ // Check if the new file already exists
+ Common::InSaveFile *testSave = _saveFileMan->openForLoading(newFileName);
+
+ if (testSave) {
+ delete testSave;
+
+ char msg[200];
+ sprintf(msg, "Target new save game already exists!\n"
+ "Would you like to keep the old save game (%s) or the new one (%s)?\n",
+ oldFileName, newFileName);
+ GUI::MessageDialog dialog0(msg, "Keep the old one", "Keep the new one");
+
+ int choice = dialog0.runModal();
+ if (choice == GUI::kMessageCancel) {
+ // User chose to keep the new game, so delete the old one
+ _saveFileMan->removeSavefile(oldFileName);
+ return true;
+ }
+ }
+
+ Common::InSaveFile *oldSave = _saveFileMan->openForLoading(oldFileName);
+ if (!oldSave) {
+ // Display a warning message and do nothing
+ warning("Can't open file '%s'", oldFileName);
+ return false;
+ }
+
+ // Read data from old type of save game
+ dataSize = oldSave->size();
+ saveData = new uint8[dataSize];
+ oldSave->read(saveData, dataSize);
+ delete oldSave;
+
+ // Now write the save data to a new type of save game
+ Common::OutSaveFile *newSave;
+ newSave = _saveFileMan->openForSaving(newFileName);
+ if (!newSave) {
+ // Display a warning message and do nothing
+ warning("Unable to create file '%s'. (%s)", newFileName, _saveFileMan->popErrorDesc().c_str());
+ free(saveData);
+ saveData = NULL;
+ return false;
+ }
+
+ newSave->writeUint32LE(SAVEGAME_HEADER);
+ newSave->write(desc, 40);
+ newSave->writeByte(SAVEGAME_VERSION);
+ newSave->writeByte(NO_THUMBNAIL);
+
+ // Date / time
+ tm curTime;
+ _system->getTimeAndDate(curTime);
+
+ uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | (curTime.tm_year + 1900) & 0xFFFF;
+ uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | (curTime.tm_min) & 0xFF;
+
+ newSave->writeUint32BE(saveDate);
+ newSave->writeUint16BE(saveTime);
+ // TODO: played time
+
+ newSave->write(saveData, dataSize);
+
+ newSave->finalize();
+ if (newSave->ioFailed())
+ warning("Couldn't write to file '%s'. Device full?", newFileName);
+ delete newSave;
+
+ // Delete old save
+ _saveFileMan->removeSavefile(oldFileName);
+
+ // Cleanup
+ free(saveData);
+ saveData = NULL;
+ return true;
+}
+
void Control::doRestore(void) {
uint8 *bufPos = _restoreBuf;
_objMan->loadLiveList((uint16*)bufPos);