aboutsummaryrefslogtreecommitdiff
path: root/engines
diff options
context:
space:
mode:
authorPaul Gilbert2011-07-16 18:33:20 +1000
committerPaul Gilbert2011-07-16 18:33:20 +1000
commitce070cdd3c664bc5fca80770a40e669fd07a83a0 (patch)
treef899e2a7622c340d223ecd04a02a3093315254c9 /engines
parent10627dccfa51eefccc6aa2a468f3a2fa246e88b6 (diff)
downloadscummvm-rg350-ce070cdd3c664bc5fca80770a40e669fd07a83a0.tar.gz
scummvm-rg350-ce070cdd3c664bc5fca80770a40e669fd07a83a0.tar.bz2
scummvm-rg350-ce070cdd3c664bc5fca80770a40e669fd07a83a0.zip
CGE: Implemented basic savegame support
I've slightly modified the behaviour of the original - rather than prompting each time the user starts for a name, it now only prompts the first time, and uses the entered name as a save description for a slot 0 savegame
Diffstat (limited to 'engines')
-rw-r--r--engines/cge/cge.h23
-rw-r--r--engines/cge/cge_main.cpp314
2 files changed, 253 insertions, 84 deletions
diff --git a/engines/cge/cge.h b/engines/cge/cge.h
index 9894cc07bc..2d67b2218a 100644
--- a/engines/cge/cge.h
+++ b/engines/cge/cge.h
@@ -25,7 +25,9 @@
#include "cge/general.h"
#include "common/random.h"
+#include "common/savefile.h"
#include "common/serializer.h"
+#include "common/str.h"
#include "engines/engine.h"
#include "gui/debugger.h"
#include "graphics/surface.h"
@@ -56,11 +58,27 @@ enum CallbackType {
#define POCKET_NX 8
+#define CGE_SAVEGAME_VERSION 1
+
+struct SavegameHeader {
+ uint8 version;
+ Common::String saveName;
+ Graphics::Surface *thumbnail;
+ int saveYear, saveMonth, saveDay;
+ int saveHour, saveMinutes;
+ int totalFrames;
+};
+
class CGEEngine : public Engine {
private:
uint32 _lastFrame;
void tick();
void syncHeader(Common::Serializer &s);
+ bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header);
+ void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
+ void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny = false);
+ bool savegameExists(int slotNumber);
+ Common::String generateSaveName(int slot);
public:
CGEEngine(OSystem *syst, const ADGameDescription *gameDescription);
~CGEEngine();
@@ -83,6 +101,7 @@ public:
bool _game;
int _now;
int _lev;
+ char _usrFnam[15];
Common::RandomSource _randomSource;
byte * _mini;
@@ -100,7 +119,7 @@ public:
void quit();
void resetQSwitch();
void optionTouch(int opt, uint16 mask);
- void loadGame(XFile &file, bool tiny);
+ bool loadGame(int slotNumber, SavegameHeader *header = NULL, bool tiny = false);
void setMapBrick(int x, int z);
void switchMapping();
void loadSprite(const char *fname, int ref, int cav, int col, int row, int pos);
@@ -129,7 +148,7 @@ public:
void setIRQ();
void setDMA();
void mainLoop();
- void saveGame(Common::WriteStream *file);
+ void saveGame(int slotNumber, const Common::String &desc);
void switchMusic();
void selectPocket(int n);
void expandSprite(Sprite *spr);
diff --git a/engines/cge/cge_main.cpp b/engines/cge/cge_main.cpp
index b43360c0b5..3ea82e3099 100644
--- a/engines/cge/cge_main.cpp
+++ b/engines/cge/cge_main.cpp
@@ -30,6 +30,9 @@
#include "common/savefile.h"
#include "common/serializer.h"
#include "common/str.h"
+#include "graphics/palette.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
#include "cge/general.h"
#include "cge/sound.h"
#include "cge/startup.h"
@@ -86,7 +89,8 @@ Snail *_snail_;
// 1.01 - 17VII95 - default savegame with sound ON
// coditionals EVA for 2-month evaluation version
-static char _usrFnam[15] = "\0ɱ%^þúȼ´ ÇÉ";
+const char *SAVEGAME_STR = "SCUMMVM_CGE";
+#define SAVEGAME_STR_SIZE 11
//--------------------------------------------------------------------------
@@ -137,84 +141,237 @@ void CGEEngine::syncHeader(Common::Serializer &s) {
}
}
-void CGEEngine::loadGame(XFile &file, bool tiny = false) {
- Sprite *spr;
- int i;
+bool CGEEngine::loadGame(int slotNumber, SavegameHeader *header, bool tiny) {
+ Common::MemoryReadStream *readStream;
+ SavegameHeader saveHeader;
- // Read the data into a data buffer
- int size = file.size() - file.mark();
- byte *dataBuffer = (byte *)malloc(size);
- file.read(dataBuffer, size);
- Common::MemoryReadStream readStream(dataBuffer, size, DisposeAfterUse::YES);
- Common::Serializer s(&readStream, NULL);
+ if (slotNumber == -1) {
+ // Loading the data for the initial game state
+ SVG0FILE file = SVG0FILE(SVG0NAME);
+ int size = file.size();
+ byte *dataBuffer = (byte *)malloc(size);
+ file.read(dataBuffer, size);
+ readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
- // Synchronise header data
- syncHeader(s);
+ } else {
+ // Open up the savgame file
+ Common::String slotName = generateSaveName(slotNumber);
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
+
+ // Read the data into a data buffer
+ int size = saveFile->size();
+ byte *dataBuffer = (byte *)malloc(size);
+ saveFile->read(dataBuffer, size);
+ readStream = new Common::MemoryReadStream(dataBuffer, size, DisposeAfterUse::YES);
+ }
- if (Startup::_core < CORE_HIG)
- _music = false;
+ // Check to see if it's a ScummVM savegame or not
+ char buffer[SAVEGAME_STR_SIZE + 1];
+ readStream->read(buffer, SAVEGAME_STR_SIZE + 1);
- if (Startup::_soundOk == 1 && Startup::_mode == 0) {
- _sndDrvInfo.Vol2._d = _volume[0];
- _sndDrvInfo.Vol2._m = _volume[1];
- sndSetVolume();
- }
+ if (strncmp(buffer, SAVEGAME_STR, SAVEGAME_STR_SIZE + 1) != 0) {
+ // It's not, so rewind back to the start
+ readStream->seek(0);
- if (! tiny) { // load sprites & pocket
- while (readStream.pos() < readStream.size()) {
- Sprite S(this, NULL);
- S.sync(s);
-
- S._prev = S._next = NULL;
- spr = (scumm_stricmp(S._file + 2, "MUCHA") == 0) ? new Fly(this, NULL)
- : new Sprite(this, NULL);
- if (spr == NULL)
- error("No core");
- *spr = S;
- _vga->_spareQ->append(spr);
+ if (header)
+ // Header wanted where none exists, so return false
+ return false;
+ } else {
+ // Found header
+ if (!readSavegameHeader(readStream, saveHeader)) {
+ delete readStream;
+ return false;
}
- for (i = 0; i < POCKET_NX; i++) {
- register int r = _pocref[i];
- delete _pocket[i];
- _pocket[i] = (r < 0) ? NULL : _vga->_spareQ->locate(r);
+ if (header) {
+ *header = saveHeader;
+ delete readStream;
+ return true;
}
+
+ // Delete the thumbnail
+ delete saveHeader.thumbnail;
+
+ // If we're loading the auto-save slot, load the name
+ if (slotNumber == 0)
+ strncpy(_usrFnam, saveHeader.saveName.c_str(), 8);
}
+
+ // Get in the savegame
+ syncGame(readStream, NULL, tiny);
+
+ delete readStream;
+ return true;
+}
+
+/**
+ * Returns true if a given savegame exists
+ */
+bool CGEEngine::savegameExists(int slotNumber) {
+ Common::String slotName = generateSaveName(slotNumber);
+
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(slotName);
+ bool result = saveFile != NULL;
+ delete saveFile;
+ return result;
}
+/**
+ * Support method that generates a savegame name
+ * @param slot Slot number
+ */
+Common::String CGEEngine::generateSaveName(int slot) {
+ return Common::String::format("%s.%03d", _targetName.c_str(), slot);
+}
void CGEEngine::saveSound() {
+ warning("STUB: CGEEngine::saveSound");
+ /* Convert to saving any such needed data in ScummVM configuration file
+
CFile cfg(usrPath(progName(CFG_EXT)), WRI);
if (!cfg._error)
cfg.write(&_sndDrvInfo, sizeof(_sndDrvInfo) - sizeof(_sndDrvInfo.Vol2));
+ */
+}
+
+void CGEEngine::saveGame(int slotNumber, const Common::String &desc) {
+ // Set up the serializer
+ Common::String slotName = generateSaveName(slotNumber);
+ Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
+
+ // Write out the ScummVM savegame header
+ SavegameHeader header;
+ header.saveName = desc;
+ header.version = CGE_SAVEGAME_VERSION;
+ writeSavegameHeader(saveFile, header);
+
+ // Write out the data of the savegame
+ syncGame(NULL, saveFile);
+
+ // Finish writing out game data
+ saveFile->finalize();
+ delete saveFile;
}
+void CGEEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
+ // Write out a savegame header
+ out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);
+
+ out->writeByte(CGE_SAVEGAME_VERSION);
+
+ // Write savegame name
+ out->write(header.saveName.c_str(), header.saveName.size() + 1);
+
+ // Get the active palette
+ uint8 thumbPalette[256 * 3];
+ g_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
+
+ // Create a thumbnail and save it
+ Graphics::Surface *thumb = new Graphics::Surface();
+ Graphics::Surface *s = _vga->_page[1];
+ ::createThumbnail(thumb, (const byte *)s->pixels, SCR_WID, SCR_HIG, thumbPalette);
+ Graphics::saveThumbnail(*out, *thumb);
+ delete thumb;
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ out->writeSint16LE(td.tm_year + 1900);
+ out->writeSint16LE(td.tm_mon + 1);
+ out->writeSint16LE(td.tm_mday);
+ out->writeSint16LE(td.tm_hour);
+ out->writeSint16LE(td.tm_min);
+}
-void CGEEngine::saveGame(Common::WriteStream *file) {
+void CGEEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream, bool tiny) {
Sprite *spr;
int i;
- for (i = 0; i < POCKET_NX; i++) {
- register Sprite *s = _pocket[i];
- _pocref[i] = (s) ? s->_ref : -1;
- }
+ Common::Serializer s(readStream, writeStream);
- _volume[0] = _sndDrvInfo.Vol2._d;
- _volume[1] = _sndDrvInfo.Vol2._m;
+ if (s.isSaving()) {
+ for (i = 0; i < POCKET_NX; i++) {
+ register Sprite *s = _pocket[i];
+ _pocref[i] = (s) ? s->_ref : -1;
+ }
- Common::Serializer s(NULL, file);
+ _volume[0] = _sndDrvInfo.Vol2._d;
+ _volume[1] = _sndDrvInfo.Vol2._m;
+ }
// Synchronise header data
syncHeader(s);
- // Loop through saving the sprite data
- for (spr = _vga->_spareQ->first(); spr; spr = spr->_next) {
- if ((spr->_ref >= 1000) && !s.err())
- spr->sync(s);
+ if (s.isSaving()) {
+ // Loop through saving the sprite data
+ for (spr = _vga->_spareQ->first(); spr; spr = spr->_next) {
+ if ((spr->_ref >= 1000) && !s.err())
+ spr->sync(s);
+ }
+ } else {
+ // Loading game
+ if (Startup::_core < CORE_HIG)
+ _music = false;
+
+ if (Startup::_soundOk == 1 && Startup::_mode == 0) {
+ _sndDrvInfo.Vol2._d = _volume[0];
+ _sndDrvInfo.Vol2._m = _volume[1];
+ sndSetVolume();
+ }
+
+ if (! tiny) { // load sprites & pocket
+ while (readStream->pos() < readStream->size()) {
+ Sprite S(this, NULL);
+ S.sync(s);
+
+ S._prev = S._next = NULL;
+ spr = (scumm_stricmp(S._file + 2, "MUCHA") == 0) ? new Fly(this, NULL)
+ : new Sprite(this, NULL);
+ if (spr == NULL)
+ error("No core");
+ *spr = S;
+ _vga->_spareQ->append(spr);
+ }
+
+ for (i = 0; i < POCKET_NX; i++) {
+ register int r = _pocref[i];
+ delete _pocket[i];
+ _pocket[i] = (r < 0) ? NULL : _vga->_spareQ->locate(r);
+ }
+ }
}
+}
+
+bool CGEEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) {
+ header.thumbnail = NULL;
+
+ // Get the savegame version
+ header.version = in->readByte();
+ if (header.version > CGE_SAVEGAME_VERSION)
+ return false;
+
+ // Read in the string
+ header.saveName.clear();
+ char ch;
+ while ((ch = (char)in->readByte()) != '\0') header.saveName += ch;
+
+ // Get the thumbnail
+ header.thumbnail = new Graphics::Surface();
+ if (!Graphics::loadThumbnail(*in, *header.thumbnail)) {
+ delete header.thumbnail;
+ header.thumbnail = NULL;
+ return false;
+ }
+
+ // Read in save date/time
+ header.saveYear = in->readSint16LE();
+ header.saveMonth = in->readSint16LE();
+ header.saveDay = in->readSint16LE();
+ header.saveHour = in->readSint16LE();
+ header.saveMinutes = in->readSint16LE();
+
+ return true;
- // Finish writing out game data
- file->finalize();
}
void CGEEngine::heroCover(int cvr) {
@@ -483,9 +640,7 @@ void CGEEngine::qGame() {
saveSound();
// Write out the user's progress
- Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(Common::String(_usrFnam));
- saveGame(saveFile);
- delete saveFile;
+ saveGame(0, _usrFnam);
_vga->sunset();
_finis = true;
@@ -773,6 +928,7 @@ void CGEEngine::takeName() {
if (GetText::_ptr)
SNPOST_(SNKILL, -1, 0, GetText::_ptr);
else {
+ memset(_usrFnam, 0, 15);
GetText *tn = new GetText(this, _text->getText(GETNAME_PROMPT), _usrFnam, 8);
if (tn) {
tn->setName(_text->getText(GETNAME_TITLE));
@@ -1290,25 +1446,15 @@ void CGEEngine::tick() {
void CGEEngine::loadUser() {
// set scene
- if (Startup::_mode == 0) { // user .SVG file found
- CFile cfile = CFile(usrPath(_usrFnam), REA, RCrypt);
- loadGame(cfile);
+ if (Startup::_mode == 0) {
+ // user .SVG file found - load it from slot 0
+ loadGame(0, NULL);
} else {
if (Startup::_mode == 1) {
- SVG0FILE file = SVG0FILE(SVG0NAME);
- loadGame(file);
+ // Load initial game state savegame
+ loadGame(-1, NULL);
} else {
- // TODO: I think this was only used by the original developers to create the initial
- // game state savegame. Verify this is the case, and if so remove this block
- loadScript(progName(INI_EXT));
- _music = true;
-
- Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(
- Common::String(SVG0NAME));
- saveGame(saveFile);
- delete saveFile;
-
- error("Ok [%s]", SVG0NAME);
+ error("Creating setup savegames not supported");
}
}
loadScript(progName(IN0_EXT));
@@ -1533,17 +1679,22 @@ bool CGEEngine::showTitle(const char *name) {
_vga->copyPage(0, 1);
_vga->_showQ->append(_mouse);
//Mouse.On();
- _heart->_enable = true;
- for (takeName(); GetText::_ptr;) {
- mainLoop();
- if (_eventManager->_quitFlag)
- return false;
- }
- _heart->_enable = false;
- if (_keyboard->last() == Enter && *_usrFnam)
+
+ // For ScummVM, skip prompting for name if a savegame in slot 0 already exists
+ if (savegameExists(0)) {
+ strcpy(_usrFnam, "User");
usr_ok = true;
- if (usr_ok)
- strcat(_usrFnam, SVG_EXT);
+ } else {
+ _heart->_enable = true;
+ for (takeName(); GetText::_ptr;) {
+ mainLoop();
+ if (_eventManager->_quitFlag)
+ return false;
+ }
+ _heart->_enable = false;
+ if (_keyboard->last() == Enter && *_usrFnam)
+ usr_ok = true;
+ }
//Mouse.Off();
_vga->_showQ->clear();
_vga->copyPage(0, 2);
@@ -1551,10 +1702,9 @@ bool CGEEngine::showTitle(const char *name) {
}
if (usr_ok && Startup::_mode == 0) {
- const char *n = usrPath(_usrFnam);
- if (CFile::exist(n)) {
- CFile file = CFile(n, REA, RCrypt);
- loadGame(file, true); // only system vars
+ if (savegameExists(0)) {
+ // Load the savegame
+ loadGame(0, NULL, true); // only system vars
_vga->setColors(Vga::_sysPal, 64);
_vga->update();
if (_flag[3]) { //flag FINIS