diff options
Diffstat (limited to 'engines/dreamweb/saveload.cpp')
-rw-r--r-- | engines/dreamweb/saveload.cpp | 377 |
1 files changed, 311 insertions, 66 deletions
diff --git a/engines/dreamweb/saveload.cpp b/engines/dreamweb/saveload.cpp index 45a1c8ef7a..cd32e4fa34 100644 --- a/engines/dreamweb/saveload.cpp +++ b/engines/dreamweb/saveload.cpp @@ -22,9 +22,11 @@ #include "dreamweb/dreamweb.h" #include "engines/metaengine.h" +#include "graphics/thumbnail.h" #include "gui/saveload.h" #include "common/config-manager.h" #include "common/translation.h" +#include "common/serializer.h" namespace DreamGen { @@ -32,6 +34,16 @@ namespace DreamGen { Room g_madeUpRoomDat; +void syncReelRoutine(Common::Serializer &s, ReelRoutine *reel) { + s.syncAsByte(reel->reallocation); + s.syncAsByte(reel->mapX); + s.syncAsByte(reel->mapY); + s.syncAsUint16LE(reel->_reelPointer); + s.syncAsByte(reel->period); + s.syncAsByte(reel->counter); + s.syncAsByte(reel->b7); +} + void DreamGenContext::loadGame() { if (data.byte(kCommandtype) != 246) { data.byte(kCommandtype) = 246; @@ -72,7 +84,7 @@ void DreamGenContext::doLoad(int savegameId) { { kOpsx+176,kOpsx+192,kOpsy+60,kOpsy+76,&DreamGenContext::getBackToOps }, { kOpsx+128,kOpsx+190,kOpsy+12,kOpsy+100,&DreamGenContext::actualLoad }, { kOpsx+2,kOpsx+92,kOpsy+4,kOpsy+81,&DreamGenContext::selectSlot }, - { 0,320,0,200,&DreamGenContext::blank }, + { 0,320,0,200,&DreamBase::blank }, { 0xFFFF,0,0,0,0 } }; checkCoords(loadlist); @@ -121,7 +133,7 @@ void DreamGenContext::doLoad(int savegameId) { data.word(kTextaddressy) = 182; data.byte(kTextlen) = 240; startup(); - workToScreen(); + workToScreenCPP(); data.byte(kGetback) = 4; } @@ -168,7 +180,7 @@ void DreamGenContext::saveGame() { { kOpsx+176,kOpsx+192,kOpsy+60,kOpsy+76,&DreamGenContext::getBackToOps }, { kOpsx+128,kOpsx+190,kOpsy+12,kOpsy+100,&DreamGenContext::actualSave }, { kOpsx+2,kOpsx+92,kOpsy+4,kOpsy+81,&DreamGenContext::selectSlot }, - { 0,320,0,200,&DreamGenContext::blank }, + { 0,320,0,200,&DreamBase::blank }, { 0xFFFF,0,0,0,0 } }; checkCoords(savelist); @@ -191,20 +203,15 @@ void DreamGenContext::saveGame() { return; } - // TODO: Check if this 2 is a constant - uint8 descbuf[17] = { 2, 0 }; - strncpy((char*)descbuf+1, game_description.c_str(), 16); + char descbuf[17] = { 2, 0 }; + Common::strlcpy((char*)descbuf + 1, game_description.c_str(), 16); unsigned int desclen = game_description.size(); if (desclen > 15) desclen = 15; // zero terminate, and pad with ones descbuf[++desclen] = 0; - while (desclen < 17) + while (desclen < 16) descbuf[++desclen] = 1; - if (savegameId < 7) - memcpy(data.ptr(kSavenames + 17*savegameId, 17), descbuf, 17); - - savePosition(savegameId, descbuf); // TODO: The below is copied from actualsave getRidOfTemp(); @@ -213,17 +220,23 @@ void DreamGenContext::saveGame() { data.word(kTextaddressy) = 182; data.byte(kTextlen) = 240; redrawMainScrn(); + workToScreenCPP(); // show the main screen without the mouse pointer + + // We need to save after the scene has been redrawn, to capture the + // correct screen thumbnail + savePosition(savegameId, descbuf); + workToScreenM(); data.byte(kGetback) = 4; } } -void DreamGenContext::namesToOld() { - memcpy(getSegment(data.word(kBuffers)).ptr(kZoomspace, 0), data.ptr(kSavenames, 0), 17*4); +void DreamBase::namesToOld() { + memcpy(_saveNamesOld, _saveNames, 17*7); } -void DreamGenContext::oldToNames() { - memcpy(data.ptr(kSavenames, 0), getSegment(data.word(kBuffers)).ptr(kZoomspace, 0), 17*4); +void DreamBase::oldToNames() { + memcpy(_saveNames, _saveNamesOld, 17*7); } void DreamGenContext::saveLoad() { @@ -239,13 +252,77 @@ void DreamGenContext::saveLoad() { doSaveLoad(); } -void DreamGenContext::showMainOps() { +void DreamGenContext::doSaveLoad() { + data.byte(kPointerframe) = 0; + data.word(kTextaddressx) = 70; + data.word(kTextaddressy) = 182-8; + data.byte(kTextlen) = 181; + data.byte(kManisoffscreen) = 1; + clearWork(); + createPanel2(); + underTextLine(); + getRidOfAll(); + loadSaveBox(); + showOpBox(); + showMainOps(); + workToScreenCPP(); + + RectWithCallback opsList[] = { + { kOpsx+59,kOpsx+114,kOpsy+30,kOpsy+76,&DreamGenContext::getBackFromOps }, + { kOpsx+10,kOpsx+77,kOpsy+10,kOpsy+59,&DreamBase::DOSReturn }, + { kOpsx+128,kOpsx+190,kOpsy+16,kOpsy+100,&DreamGenContext::discOps }, + { 0,320,0,200,&DreamBase::blank }, + { 0xFFFF,0,0,0,0 } + }; + + bool firstOps = true; + + do { // restart ops + if (firstOps) { + firstOps = false; + } else { + showOpBox(); + showMainOps(); + workToScreenM(); + } + data.byte(kGetback) = 0; + + do { // wait ops + if (data.byte(kQuitrequested)) { + data.byte(kManisoffscreen) = 0; + return; + } + + readMouse(); + showPointer(); + vSync(); + dumpPointer(); + dumpTextLine(); + delPointer(); + checkCoords(opsList); + } while (!data.byte(kGetback)); + } while (data.byte(kGetback) == 2); + + data.word(kTextaddressx) = 13; + data.word(kTextaddressy) = 182; + data.byte(kTextlen) = 240; + if (data.byte(kGetback) != 4) { + getRidOfTemp(); + restoreAll(); + redrawMainScrn(); + workToScreenM(); + data.byte(kCommandtype) = 200; + } + data.byte(kManisoffscreen) = 0; +} + +void DreamBase::showMainOps() { showFrame(tempGraphics(), kOpsx+10, kOpsy+10, 8, 0); showFrame(tempGraphics(), kOpsx+59, kOpsy+30, 7, 0); showFrame(tempGraphics(), kOpsx+128+4, kOpsy+12, 1, 0); } -void DreamGenContext::showDiscOps() { +void DreamBase::showDiscOps() { showFrame(tempGraphics(), kOpsx+128+4, kOpsy+12, 1, 0); showFrame(tempGraphics(), kOpsx+10, kOpsy+10, 9, 0); showFrame(tempGraphics(), kOpsx+59, kOpsy+30, 10, 0); @@ -263,7 +340,7 @@ void DreamGenContext::actualSave() { unsigned int slot = data.byte(kCurrentslot); - const uint8 *desc = data.ptr(kSavenames + 17*slot, 16); + const char *desc = &_saveNames[17*slot]; if (desc[1] == 0) // The actual description string starts at desc[1] return; @@ -290,7 +367,7 @@ void DreamGenContext::actualLoad() { unsigned int slot = data.byte(kCurrentslot); - const uint8 *desc = data.ptr(kSavenames + 17*slot, 16); + const char *desc = &_saveNames[17*slot]; if (desc[1] == 0) // The actual description string starts at desc[1] return; @@ -298,7 +375,7 @@ void DreamGenContext::actualLoad() { data.byte(kGetback) = 1; } -void DreamGenContext::savePosition(unsigned int slot, const uint8 *descbuf) { +void DreamBase::savePosition(unsigned int slot, const char *descbuf) { const Room ¤tRoom = g_roomData[data.byte(kLocation)]; @@ -311,7 +388,11 @@ void DreamGenContext::savePosition(unsigned int slot, const uint8 *descbuf) { madeUpRoom.facing = data.byte(kFacing); madeUpRoom.b27 = 255; - openForSave(slot); + Common::String filename = engine->getSavegameFilename(slot); + debug(1, "savePosition: slot %d filename %s", slot, filename.c_str()); + Common::OutSaveFile *outSaveFile = engine->getSaveFileManager()->openForSaving(filename); + if (!outSaveFile) // TODO: Do proper error handling! + error("save could not be opened for writing"); // Initialize new header FileHeader header; @@ -325,33 +406,68 @@ void DreamGenContext::savePosition(unsigned int slot, const uint8 *descbuf) { // fill length fields in savegame file header uint16 len[6] = { 17, kLengthofvars, kLengthofextra, - 4*kNumchanges, 48, kLenofreelrouts }; + 4*kNumchanges, 48, kNumReelRoutines*8+1 }; for (int i = 0; i < 6; ++i) header.setLen(i, len[i]); - engine->writeToSaveFile((const uint8 *)&header, sizeof(FileHeader)); - engine->writeToSaveFile(descbuf, len[0]); - engine->writeToSaveFile(data.ptr(kStartvars, len[1]), len[1]); - engine->writeToSaveFile(getSegment(data.word(kExtras)).ptr(kExframedata, len[2]), len[2]); - engine->writeToSaveFile(getSegment(data.word(kBuffers)).ptr(kListofchanges, len[3]), len[3]); + // Write a new section with data that we need for ScummVM (version, + // thumbnail, played time etc). We don't really care for its size, + // so we just set it to a magic number. + header.setLen(6, SCUMMVM_BLOCK_MAGIC_SIZE); + + outSaveFile->write((const uint8 *)&header, sizeof(FileHeader)); + outSaveFile->write(descbuf, len[0]); + outSaveFile->write(data.ptr(kStartvars, len[1]), len[1]); + outSaveFile->write(getSegment(data.word(kExtras)).ptr(kExframedata, len[2]), len[2]); + outSaveFile->write(getSegment(data.word(kBuffers)).ptr(kListofchanges, len[3]), len[3]); // len[4] == 48, which is sizeof(Room) plus 16 for 'Roomscango' - engine->writeToSaveFile((const uint8 *)&madeUpRoom, sizeof(Room)); - engine->writeToSaveFile(data.ptr(kRoomscango, 16), 16); + outSaveFile->write((const uint8 *)&madeUpRoom, sizeof(Room)); + outSaveFile->write(data.ptr(kRoomscango, 16), 16); + + // TODO: Convert more to serializer? + Common::Serializer s(0, outSaveFile); + for (unsigned int i = 0; i < kNumReelRoutines; ++i) { + syncReelRoutine(s, &_reelRoutines[i]); + } + // Terminator + s.syncAsByte(_reelRoutines[kNumReelRoutines].reallocation); + + // ScummVM data block + outSaveFile->writeUint32BE(SCUMMVM_HEADER); + outSaveFile->writeByte(SAVEGAME_VERSION); + TimeDate curTime; + g_system->getTimeAndDate(curTime); + uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF); + uint32 saveTime = ((curTime.tm_hour & 0xFF) << 16) | (((curTime.tm_min) & 0xFF) << 8) | ((curTime.tm_sec) & 0xFF); + uint32 playTime = g_engine->getTotalPlayTime() / 1000; + outSaveFile->writeUint32LE(saveDate); + outSaveFile->writeUint32LE(saveTime); + outSaveFile->writeUint32LE(playTime); + Graphics::saveThumbnail(*outSaveFile); + + outSaveFile->finalize(); + if (outSaveFile->err()) { + // TODO: Do proper error handling + warning("an error occurred while writing the savegame"); + } - engine->writeToSaveFile(data.ptr(kReelroutines, len[5]), len[5]); - closeFile(); + delete outSaveFile; } -void DreamGenContext::loadPosition(unsigned int slot) { +void DreamBase::loadPosition(unsigned int slot) { data.word(kTimecount) = 0; clearChanges(); - openForLoad(slot); + Common::String filename = engine->getSavegameFilename(slot); + debug(1, "loadPosition: slot %d filename %s", slot, filename.c_str()); + Common::InSaveFile *inSaveFile = engine->getSaveFileManager()->openForLoading(filename); + if (!inSaveFile) // TODO: Do proper error handling! + error("save could not be opened for reading"); FileHeader header; - engine->readFromSaveFile((uint8 *)&header, sizeof(FileHeader)); + inSaveFile->read((uint8 *)&header, sizeof(FileHeader)); // read segment lengths from savegame file header int len[6]; @@ -361,56 +477,92 @@ void DreamGenContext::loadPosition(unsigned int slot) { ::error("Error loading save: description buffer isn't 17 bytes"); if (slot < 7) { - engine->readFromSaveFile(data.ptr(kSavenames + 17*slot, len[0]), len[0]); + inSaveFile->read(&_saveNames[17*slot], len[0]); } else { // The savenames buffer only has room for 7 descriptions uint8 namebuf[17]; - engine->readFromSaveFile(namebuf, 17); + inSaveFile->read(namebuf, 17); } - engine->readFromSaveFile(data.ptr(kStartvars, len[1]), len[1]); - engine->readFromSaveFile(getSegment(data.word(kExtras)).ptr(kExframedata, len[2]), len[2]); - engine->readFromSaveFile(getSegment(data.word(kBuffers)).ptr(kListofchanges, len[3]), len[3]); + inSaveFile->read(data.ptr(kStartvars, len[1]), len[1]); + inSaveFile->read(getSegment(data.word(kExtras)).ptr(kExframedata, len[2]), len[2]); + inSaveFile->read(getSegment(data.word(kBuffers)).ptr(kListofchanges, len[3]), len[3]); // len[4] == 48, which is sizeof(Room) plus 16 for 'Roomscango' // Note: the values read into g_madeUpRoomDat are only used in actualLoad, // which is (almost) immediately called after this function - engine->readFromSaveFile((uint8 *)&g_madeUpRoomDat, sizeof(Room)); - engine->readFromSaveFile(data.ptr(kRoomscango, 16), 16); + inSaveFile->read((uint8 *)&g_madeUpRoomDat, sizeof(Room)); + inSaveFile->read(data.ptr(kRoomscango, 16), 16); - engine->readFromSaveFile(data.ptr(kReelroutines, len[5]), len[5]); - - closeFile(); -} - -// Count number of save files, and load their descriptions into kSavenames -unsigned int DreamGenContext::scanForNames() { - unsigned int count = 0; - - FileHeader header; - - for (unsigned int slot = 0; slot < 7; ++slot) { + // TODO: Use serializer for more + Common::Serializer s(inSaveFile, 0); + for (unsigned int i = 0; i < kNumReelRoutines; ++i) { + syncReelRoutine(s, &_reelRoutines[i]); + } + // Terminator + s.syncAsByte(_reelRoutines[kNumReelRoutines].reallocation); + + // Check if there's a ScummVM data block + if (header.len(6) == SCUMMVM_BLOCK_MAGIC_SIZE) { + uint32 tag = inSaveFile->readUint32BE(); + if (tag != SCUMMVM_HEADER) { + warning("ScummVM data block found, but the block header is incorrect - skipping"); + delete inSaveFile; + return; + } - if (!openForLoad(slot)) continue; + byte version = inSaveFile->readByte(); + if (version > SAVEGAME_VERSION) { + warning("ScummVM data block found, but it has been saved with a newer version of ScummVM - skipping"); + delete inSaveFile; + return; + } - ++count; + inSaveFile->skip(4); // saveDate + inSaveFile->skip(4); // saveTime + uint32 playTime = inSaveFile->readUint32LE(); + g_engine->setTotalPlayTime(playTime * 1000); - engine->readFromSaveFile((uint8 *)&header, sizeof(FileHeader)); + // The thumbnail data follows, but we don't need it here + } - if (header.len(0) != 17) { - ::warning("Error loading save: description buffer isn't 17 bytes"); - closeFile(); - continue; - } + delete inSaveFile; +} - // NB: Only possible if slot < 7 - engine->readFromSaveFile(data.ptr(kSavenames + 17*slot, 17), 17); +// Count number of save files, and load their descriptions into _saveNames +unsigned int DreamGenContext::scanForNames() { + // Initialize the first 7 slots (like the original code expects) + for (unsigned int slot = 0; slot < 7; ++slot) { + _saveNames[17 * slot + 0] = 2; + _saveNames[17 * slot + 1] = 0; + for (int i = 2; i < 17; ++i) + _saveNames[17 * slot + i] = 1; // initialize with 1's + } - closeFile(); + Common::SaveFileManager *saveFileMan = g_system->getSavefileManager(); + Common::StringArray files = saveFileMan->listSavefiles("DREAMWEB.D??"); + Common::sort(files.begin(), files.end()); + + SaveStateList saveList; + for (uint i = 0; i < files.size(); ++i) { + const Common::String &file = files[i]; + Common::InSaveFile *stream = saveFileMan->openForLoading(file); + if (!stream) + error("cannot open save file %s", file.c_str()); + char name[17] = {}; + stream->seek(0x61); + stream->read(name, sizeof(name) - 1); + delete stream; + + int slotNum = atoi(file.c_str() + file.size() - 2); + SaveStateDescriptor sd(slotNum, name); + saveList.push_back(sd); + if (slotNum < 7) + Common::strlcpy(&_saveNames[17 * slotNum + 1], name, 16); // the first character is unused } - al = (uint8)count; + al = saveList.size() <= 7 ? (uint8)saveList.size() : 7; - return count; + return saveList.size(); } void DreamGenContext::loadOld() { @@ -436,4 +588,97 @@ void DreamGenContext::loadSaveBox() { loadIntoTemp("DREAMWEB.G08"); } +// show savegame names (original interface), and set kCursorpos +void DreamBase::showNames() { + for (int slot = 0; slot < 7; ++slot) { + // The first character of the savegame name is unused + Common::String name(&_saveNames[17*slot + 1]); + + if (slot != data.byte(kCurrentslot)) { + printDirect((const uint8 *)name.c_str(), kOpsx + 21, kOpsy + 10*slot + 10, 200, false); + continue; + } + if (data.byte(kLoadingorsave) != 2) { + data.word(kCharshift) = 91; + printDirect((const uint8 *)name.c_str(), kOpsx + 21, kOpsy + 10*slot + 10, 200, false); + data.word(kCharshift) = 0; + continue; + } + + int pos = name.size(); + data.byte(kCursorpos) = pos; + name += '/'; // cursor character + printDirect((const uint8 *)name.c_str(), kOpsx + 21, kOpsy + 10*slot + 10, 200, false); + } +} + +void DreamGenContext::checkInput() { + if (data.byte(kLoadingorsave) == 3) + return; + + readKey(); + + // The first character of the savegame name is unused + char *name = &_saveNames[17*data.byte(kCurrentslot) + 1]; + + if (data.byte(kCurrentkey) == 0) { + return; + } else if (data.byte(kCurrentkey) == 13) { + data.byte(kLoadingorsave) = 3; + } else if (data.byte(kCurrentkey) == 8) { + if (data.byte(kCursorpos) == 0) + return; + + --data.byte(kCursorpos); + name[data.byte(kCursorpos)] = 0; + name[data.byte(kCursorpos)+1] = 1; + } else { + if (data.byte(kCursorpos) == 14) + return; + + name[data.byte(kCursorpos)] = data.byte(kCurrentkey); + name[data.byte(kCursorpos)+1] = 0; + name[data.byte(kCursorpos)+2] = 1; + ++data.byte(kCursorpos); + } + + showOpBox(); + showNames(); + showSlots(); + showSaveOps(); + workToScreenM(); +} + +void DreamGenContext::selectSlot() { + if (data.byte(kCommandtype) != 244) { + data.byte(kCommandtype) = 244; + commandOnly(45); + } + + if (data.word(kMousebutton) != 1 || data.word(kMousebutton) == data.word(kOldbutton)) + return; // noselslot + if (data.byte(kLoadingorsave) == 3) + data.byte(kLoadingorsave)--; + + oldToNames(); + int y = data.word(kMousey) - (kOpsy + 4); + if (y < 11) + data.byte(kCurrentslot) = 0; + else + data.byte(kCurrentslot) = y / 11; + + delPointer(); + showOpBox(); + showSlots(); + showNames(); + if (data.byte(kLoadingorsave) == 1) + showLoadOps(); + else + showSaveOps(); + readMouse(); + showPointer(); + workToScreen(); + delPointer(); +} + } // End of namespace DreamGen |