diff options
author | athrxx | 2013-04-07 22:38:25 +0200 |
---|---|---|
committer | athrxx | 2013-04-14 13:12:08 +0200 |
commit | 66a3b2b2accffb28b6d4dcb17b8218ab8fb40d66 (patch) | |
tree | 66147647580d3917e5c944bd2bf96ebef0bb5e00 /engines/kyra | |
parent | a33045f4fa50b9c5d9755ad287e7bc19293db911 (diff) | |
download | scummvm-rg350-66a3b2b2accffb28b6d4dcb17b8218ab8fb40d66.tar.gz scummvm-rg350-66a3b2b2accffb28b6d4dcb17b8218ab8fb40d66.tar.bz2 scummvm-rg350-66a3b2b2accffb28b6d4dcb17b8218ab8fb40d66.zip |
KYRA: (EOB) - add debug function to export save files in original format
Diffstat (limited to 'engines/kyra')
-rw-r--r-- | engines/kyra/debugger.cpp | 53 | ||||
-rw-r--r-- | engines/kyra/debugger.h | 1 | ||||
-rw-r--r-- | engines/kyra/eobcommon.h | 1 | ||||
-rw-r--r-- | engines/kyra/saveload_eob.cpp | 362 | ||||
-rw-r--r-- | engines/kyra/script_eob.cpp | 14 | ||||
-rw-r--r-- | engines/kyra/script_eob.h | 2 | ||||
-rw-r--r-- | engines/kyra/sequences_darkmoon.cpp | 1 |
7 files changed, 421 insertions, 13 deletions
diff --git a/engines/kyra/debugger.cpp b/engines/kyra/debugger.cpp index c0a91ac098..75981958d6 100644 --- a/engines/kyra/debugger.cpp +++ b/engines/kyra/debugger.cpp @@ -483,11 +483,12 @@ Debugger_EoB::Debugger_EoB(EoBCoreEngine *vm) : Debugger(vm), _vm(vm) { void Debugger_EoB::initialize() { DCmd_Register("import_savefile", WRAP_METHOD(Debugger_EoB, cmd_importSaveFile)); + DCmd_Register("save_original", WRAP_METHOD(Debugger_EoB, cmd_saveOriginal)); } bool Debugger_EoB::cmd_importSaveFile(int argc, const char **argv) { if (!_vm->_allowImport) { - DebugPrintf("This command may only be used from the main menu.\n"); + DebugPrintf("This command only works from the main menu.\n"); return true; } @@ -507,6 +508,56 @@ bool Debugger_EoB::cmd_importSaveFile(int argc, const char **argv) { return true; } +bool Debugger_EoB::cmd_saveOriginal(int argc, const char **argv) { + if (!_vm->_runFlag) { + DebugPrintf("This command doesn't work during intro or outro sequences,\nfrom the main menu or from the character generation.\n"); + return true; + } + + Common::String dir = ConfMan.get("savepath"); + if (dir == "None") + dir.clear(); + + Common::FSNode nd(dir); + if (!nd.isDirectory()) + return false; + + if (_vm->game() == GI_EOB1) { + if (argc == 1) { + if (_vm->saveAsOriginalSaveFile()) { + Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA.SAV")); + if (nf.isReadable()) + DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str()); + else + DebugPrintf("Failure.\n"); + } else { + DebugPrintf("Failure.\n"); + } + } else { + DebugPrintf("Syntax: save_original\n (Saves game in original file format to a file which can be used with the orginal game executable.)\n\n"); + } + return true; + + } else if (argc == 2) { + int slot = atoi(argv[1]); + if (slot < 0 || slot > 5) { + DebugPrintf("Slot must be between (including) 0 and 5.\n"); + } else if (_vm->saveAsOriginalSaveFile(slot)) { + Common::FSNode nf = nd.getChild(Common::String::format("EOBDATA%d.SAV", slot)); + if (nf.isReadable()) + DebugPrintf("Saved to file: %s\n\n", nf.getPath().c_str()); + else + DebugPrintf("Failure.\n"); + } else { + DebugPrintf("Failure.\n"); + } + return true; + } + + DebugPrintf("Syntax: save_original <slot>\n (Saves game in original file format to a file which can be used with the orginal game executable.\n A save slot between 0 and 5 must be specified.)\n\n"); + return true; +} + #endif // ENABLE_EOB } // End of namespace Kyra diff --git a/engines/kyra/debugger.h b/engines/kyra/debugger.h index 2b1dcbe505..e4ab39102a 100644 --- a/engines/kyra/debugger.h +++ b/engines/kyra/debugger.h @@ -120,6 +120,7 @@ protected: EoBCoreEngine *_vm; bool cmd_importSaveFile(int argc, const char **argv); + bool cmd_saveOriginal(int argc, const char **argv); }; #endif // ENABLE_EOB diff --git a/engines/kyra/eobcommon.h b/engines/kyra/eobcommon.h index 70200d3049..1a74321364 100644 --- a/engines/kyra/eobcommon.h +++ b/engines/kyra/eobcommon.h @@ -850,6 +850,7 @@ protected: // Default parameters will import all present original save files and push them to the top of the save dialog. bool importOriginalSaveFile(int destSlot, const char *sourceFile = 0); Common::String readOriginalSaveFile(Common::String &file); + bool saveAsOriginalSaveFile(int slot = -1); void *generateMonsterTempData(LevelTempData *tmp); void restoreMonsterTempData(LevelTempData *tmp); diff --git a/engines/kyra/saveload_eob.cpp b/engines/kyra/saveload_eob.cpp index e4f53a852d..1e5b40af83 100644 --- a/engines/kyra/saveload_eob.cpp +++ b/engines/kyra/saveload_eob.cpp @@ -695,6 +695,8 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { in.read(c->effectsRemainder, 4); c->effectFlags = in.readUint32(); if (c->effectFlags && _flags.gameID == GI_EOB1) { + // Spell effect flags are completely different in EOB I. We only use EOB II style flags in ScummVM. + // Doesn't matter much, since these are the temporary spell effects only anyway. warning("EoBCoreEngine::readOriginalSaveFile(): Unhandled character effect flags encountered in original EOB1 save file '%s' ('%s')", file.c_str(), desc.c_str()); c->effectFlags = 0; } @@ -713,6 +715,8 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { _hasTempDataFlags = (_flags.gameID == GI_EOB1) ? in.readUint16() : in.readUint32(); _partyEffectFlags = (_flags.gameID == GI_EOB1) ? in.readUint16() : in.readUint32(); if (_partyEffectFlags && _flags.gameID == GI_EOB1) { + // Spell effect flags are completely different in EOB I. We only use EOB II style flags in ScummVM. + // Doesn't matter much, since these are the temporary spell effects only anyway. warning("EoBCoreEngine::readOriginalSaveFile(): Unhandled party effect flags encountered in original EOB1 save file '%s' ('%s')", file.c_str(), desc.c_str()); _partyEffectFlags = 0; } @@ -737,7 +741,7 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { t->value = in.readSByte(); } - int numParts = (_flags.gameID == GI_EOB1) ? 13 : 18; + int numParts = (_flags.gameID == GI_EOB1) ? 12 : 17; int partSize = (_flags.gameID == GI_EOB1) ? 2040 : 2130; uint32 nextPart = in.pos(); uint8 *cmpData = new uint8[1200]; @@ -831,7 +835,7 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { for (int ii = 0; ii < 5; ii++) { WallOfForce *w = &lw[ii]; w->block = in.readUint16(); - w->duration = in.readUint32(); + w->duration = in.readUint32() * _tickLength; } } @@ -845,10 +849,7 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { _itemTypes = new EoBItemType[65]; memset(_itemTypes, 0, sizeof(EoBItemType) * 65); - if (_flags.gameID == GI_EOB1) - return desc; - - for (int i = 51; i < 65; i++) { + for (int i = 51; i < 57; i++) { EoBItemType *t = &_itemTypes[i]; t->invFlags = in.readUint16(); t->handFlags = in.readUint16(); @@ -868,6 +869,355 @@ Common::String EoBCoreEngine::readOriginalSaveFile(Common::String &file) { return in.err() ? Common::String() : desc; } +static uint32 encodeFrame4(const uint8 *src, uint8 *dst, uint32 insize) { + const uint8 *end = src + insize; + + bool updateCounter = true; + const uint8 *in = src; + + uint8 *out = dst; + uint8 *cntPtr = dst; + + *dst++ = 0x81; + *dst++ = *src++; + + while (src < end) { + const uint8 *src2 = in; + const uint8 *src3 = 0; + uint16 len = 1; + + for (bool loop = true; loop; ) { + uint16 count = 0; + uint16 scansize = end - src - 1; + if (scansize > 64) { + if (src[0] == src[64]) { + for (uint16 i = 0; i < scansize && src[0] == src[i]; ++i) + count++; + if (count > 64) { + updateCounter = false; + *dst++ = 0xFE; + WRITE_LE_UINT16(dst, count); + dst += 2; + *dst++ = src[0]; + src += count; + loop = true; + continue; + } + } + } + + const uint8 *tmp = src2; + + do { + count = src - tmp; + loop = false; + if (count == 0) + break; + + while (count--) { + if (*src == *tmp++) { + loop = true; + break; + } + } + if (!loop) + break; + } while (*(src + len - 1) != *(tmp + len - 2)); + + if (!loop) + break; + + src2 = tmp--; + const uint8 *tsrc = src; + count = end - src; + bool nmatch = false; + + while (count--) { + if (*tsrc++ != *tmp++) { + nmatch = true; + break; + } + } + if (!nmatch) + tmp++; + + count = tmp - src2; + if (count >= len) { + len = count; + src3 = src2 - 1; + } + } + + if (len <= 2) { + for (bool forceLoop = !updateCounter; forceLoop || *cntPtr == 0xBF; forceLoop = false) { + cntPtr = dst; + *dst++ = 0x80; + } + (*cntPtr)++; + *dst++ = *src++; + updateCounter = true; + continue; + + } else if (len > 10 || (src - src3) > 4095) { + if (len <= 64) { + *dst++ = (len - 3) | 0xC0; + } else { + *dst++ = 0xFF; + WRITE_LE_UINT16(dst, len); + dst += 2; + } + WRITE_LE_UINT16(dst, src3 - in); + } else { + WRITE_BE_UINT16(dst, (src - src3) + ((len - 3) << 12)); + } + + dst += 2; + src += len; + updateCounter = false; + } + + *dst++ = 0x80; + + return dst - out; +} + +bool EoBCoreEngine::saveAsOriginalSaveFile(int slot) { + if (_flags.gameID == GI_EOB2 && (slot < 0 || slot > 5)) + return false; + + Common::String dir = ConfMan.get("savepath"); + if (dir == "None") + dir.clear(); + + Common::FSNode nd(dir); + if (!nd.isDirectory()) + return false; + + Common::FSNode nf = nd.getChild(_flags.gameID == GI_EOB1 ? "EOBDATA.SAV" : Common::String::format("EOBDATA%d.SAV", slot)); + Common::WriteStream *out = nf.createWriteStream(); + + if (_flags.gameID == GI_EOB2) { + static const char tempStr[20] = "SCUMMVM EXPORT "; + out->write(tempStr, 20); + } + + completeDoorOperations(); + generateTempData(); + advanceTimers(_restPartyElapsedTime); + _restPartyElapsedTime = 0; + + for (int i = 0; i < 6; i++) + timerSpecialCharacterUpdate(0x30 + i); + + for (int i = 0; i < 6; i++) { + EoBCharacter *c = &_characters[i]; + out->writeByte(c->id); + out->writeByte(c->flags); + out->write(c->name, 11); + out->writeSByte(c->strengthCur); + out->writeSByte(c->strengthMax); + out->writeSByte(c->strengthExtCur); + out->writeSByte(c->strengthExtMax); + out->writeSByte(c->intelligenceCur); + out->writeSByte(c->intelligenceMax); + out->writeSByte(c->wisdomCur); + out->writeSByte(c->wisdomMax); + out->writeSByte(c->dexterityCur); + out->writeSByte(c->dexterityMax); + out->writeSByte(c->constitutionCur); + out->writeSByte(c->constitutionMax); + out->writeSByte(c->charismaCur); + out->writeSByte(c->charismaMax); + if (_flags.gameID == GI_EOB1) { + out->writeSByte(c->hitPointsCur); + out->writeSByte(c->hitPointsMax); + } else { + out->writeSint16LE(c->hitPointsCur); + out->writeSint16LE(c->hitPointsMax); + } + out->writeSByte(c->armorClass); + out->writeByte(c->disabledSlots); + out->writeByte(c->raceSex); + out->writeByte(c->cClass); + out->writeByte(c->alignment); + out->writeSByte(c->portrait); + out->writeByte(c->food); + out->write(c->level, 3); + for (int ii = 0; ii < 3; ii++) + out->writeUint32LE(c->experience[ii]); + out->writeUint32LE(0); + out->write(c->mageSpells, (_flags.gameID == GI_EOB1) ? 30 : 80); + out->write(c->clericSpells, (_flags.gameID == GI_EOB1) ? 30 : 80); + out->writeUint32LE(c->mageSpellsAvailableFlags); + for (int ii = 0; ii < 27; ii++) + out->writeSint16LE(c->inventory[ii]); + uint32 ct = _system->getMillis(); + for (int ii = 0; ii < 10; ii++) + out->writeUint32LE((c->timers[ii] && c->timers[ii] > ct) ? (c->timers[ii] - ct) / _tickLength : 0); + out->write(c->events, 10); + out->write(c->effectsRemainder, 4); + + if (c->effectFlags && _flags.gameID == GI_EOB1) { + // Spell effect flags are completely different in original EOB I. We only use EOB II style flags in ScummVM. + // This doesn't matter much here, since these flags only apply to the temporary spell effects (things like prayer, haste, etc.) anyway. + warning("EoBCoreEngine::saveAsOriginalFile(): Character effect flags lost while exporting original EOB1 save file"); + out->writeUint32LE(0); + } else { + out->writeUint32LE(c->effectFlags); + } + out->writeByte(c->damageTaken); + out->write(c->slotStatus, 5); + for (int ii = 0; ii < 6; ii++) + out->writeByte(0); + } + + out->writeUint16LE(_currentLevel); + if (_flags.gameID == GI_EOB2) + out->writeSint16LE(_currentSub); + out->writeUint16LE(_currentBlock); + out->writeUint16LE(_currentDirection); + out->writeSint16LE(_itemInHand); + if (_flags.gameID == GI_EOB1) { + out->writeUint16LE(_hasTempDataFlags); + out->writeUint16LE(0); + if (_partyEffectFlags) + // Spell effect flags are completely different in original EOB I. We only use EOB II style flags in ScummVM. + // This doesn't matter much here, since these flags only apply to the temporary spell effects (things like prayer, haste, etc.) anyway. + warning("EoBCoreEngine::saveAsOriginalFile(): Party effect flags lost while exporting original EOB1 save file"); + } else { + out->writeUint32LE(_hasTempDataFlags); + out->writeUint32LE(_partyEffectFlags); + } + if (_flags.gameID == GI_EOB2) + out->writeByte(0); + _inf->saveState(out, true); + + int numItems = (_flags.gameID == GI_EOB1) ? 500 : 600; + for (int i = 0; i < numItems; i++) { + EoBItem *t = &_items[i]; + out->writeByte(t->nameUnid); + out->writeByte(t->nameId); + out->writeByte(t->flags); + out->writeSByte(t->icon); + out->writeSByte(t->type); + out->writeSByte(t->pos); + out->writeSint16LE(t->block); + out->writeSint16LE(t->next); + out->writeSint16LE(t->prev); + out->writeByte(t->level); + out->writeSByte(t->value); + } + + int numParts = (_flags.gameID == GI_EOB1) ? 12 : 17; + int partSize = (_flags.gameID == GI_EOB1) ? 2040 : 2130; + uint8 *tempData = new uint8[4096]; + uint8 *cmpData = new uint8[1200]; + + for (int i = 0; i < numParts; i++) { + LevelTempData *l = _lvlTempData[i]; + memset(tempData, 0, 4096); + memset(cmpData, 0, 1200); + + if (!l || !(_hasTempDataFlags & (1 << i))) { + out->write(tempData, partSize); + continue; + } + + _curBlockFile = getBlockFileName(i + 1, 0); + const uint8 *p = getBlockFileData(); + uint16 len = READ_LE_UINT16(p + 4); + p += 6; + + uint8 *d = tempData; + for (int ii = 0; ii < 1024; ii++) { + for (int iii = 0; iii < 4; iii++) + *d++ = l->wallsXorData[ii * len + iii] ^ p[ii * len + iii]; + } + + uint32 outsize = encodeFrame4(tempData, cmpData, 4096); + if (outsize > 1200) + error("Map compression failure: size of map = %d", outsize); + + out->write(cmpData, 1200); + + for (int ii = 0; ii < 30; ii++) { + EoBMonsterInPlay *m = &((EoBMonsterInPlay*)l->monsters)[ii]; + out->writeByte(m->type); + out->writeByte(m->unit); + out->writeUint16LE(m->block); + out->writeByte(m->pos); + out->writeSByte(m->dir); + out->writeByte(m->animStep); + out->writeByte(m->shpIndex); + out->writeSByte(m->mode); + out->writeSByte(m->f_9); + out->writeSByte(m->curAttackFrame); + out->writeSByte(m->spellStatusLeft); + out->writeSint16LE(m->hitPointsMax); + out->writeSint16LE(m->hitPointsCur); + out->writeUint16LE(m->dest); + out->writeUint16LE(m->randItem); + out->writeUint16LE(m->fixedItem); + out->writeByte(m->flags); + out->writeByte(m->idleAnimState); + + if (_flags.gameID == GI_EOB1) + out->writeByte(m->stepsTillRemoteAttack); + else + out->writeByte(m->curRemoteWeapon); + + out->writeByte(m->numRemoteAttacks); + out->writeSByte(m->palette); + + if (_flags.gameID == GI_EOB1) { + out->writeByte(0); + } else { + out->writeByte(m->directionChanged); + out->writeByte(m->stepsTillRemoteAttack); + out->writeByte(m->sub); + } + } + + if (_flags.gameID == GI_EOB1) + continue; + + for (int ii = 0; ii < 5; ii++) { + WallOfForce *w= &((WallOfForce*)l->wallsOfForce)[ii]; + out->writeUint16LE(w->block); + out->writeUint32LE(w->duration / _tickLength); + } + } + + delete[] cmpData; + delete[] tempData; + + out->writeByte(_configMusic ? 1 : 0); + out->writeByte(_configMusic ? 1 : 0); + out->writeByte(_configHpBarGraphs ? 1 : 0); + + for (int i = 51; i < 57; i++) { + EoBItemType *t = &_itemTypes[i]; + out->writeUint16LE(t->invFlags); + out->writeUint16LE(t->handFlags); + out->writeSByte(t->armorClass); + out->writeSByte(t->allowedClasses); + out->writeSByte(t->requiredHands); + out->writeSByte(t->dmgNumDiceS); + out->writeSByte(t->dmgNumPipsS); + out->writeSByte(t->dmgIncS); + out->writeSByte(t->dmgNumDiceL); + out->writeSByte(t->dmgNumPipsL); + out->writeSByte(t->dmgIncL); + out->writeByte(t->unk1); + out->writeUint16LE(t->extraProperties); + } + + out->finalize(); + delete out; + + return true; +} + void *EoBCoreEngine::generateMonsterTempData(LevelTempData *tmp) { EoBMonsterInPlay *m = new EoBMonsterInPlay[30]; memcpy(m, _monsters, sizeof(EoBMonsterInPlay) * 30); diff --git a/engines/kyra/script_eob.cpp b/engines/kyra/script_eob.cpp index e5ccbf2c2e..de4d01b254 100644 --- a/engines/kyra/script_eob.cpp +++ b/engines/kyra/script_eob.cpp @@ -221,10 +221,16 @@ void EoBInfProcessor::loadState(Common::SeekableSubReadStreamEndian &in, bool or _flagTable[i] = in.readUint32(); } -void EoBInfProcessor::saveState(Common::OutSaveFile *out) { - out->writeByte(_preventRest); - for (int i = 0; i < 18; i++) - out->writeUint32BE(_flagTable[i]); +void EoBInfProcessor::saveState(Common::OutSaveFile *out, bool origFile) { + if (_vm->game() == GI_EOB2 || !origFile) + out->writeByte(_preventRest); + int numFlags = (_vm->game() == GI_EOB1 && origFile) ? 13 : 18; + for (int i = 0; i < numFlags; i++) { + if (origFile) + out->writeUint32LE(_flagTable[i]); + else + out->writeUint32BE(_flagTable[i]); + } } void EoBInfProcessor::reset() { diff --git a/engines/kyra/script_eob.h b/engines/kyra/script_eob.h index ff3a5e0fac..8e2dbd8423 100644 --- a/engines/kyra/script_eob.h +++ b/engines/kyra/script_eob.h @@ -48,7 +48,7 @@ public: bool preventRest() const; void loadState(Common::SeekableSubReadStreamEndian &in, bool origFile = false); - void saveState(Common::OutSaveFile *out); + void saveState(Common::OutSaveFile *out, bool origFile = false); void reset(); private: diff --git a/engines/kyra/sequences_darkmoon.cpp b/engines/kyra/sequences_darkmoon.cpp index d2e6e85218..03b15d6950 100644 --- a/engines/kyra/sequences_darkmoon.cpp +++ b/engines/kyra/sequences_darkmoon.cpp @@ -142,7 +142,6 @@ int DarkMoonEngine::mainMenu() { case 3: // transfer party - //seq_playFinale(); menuChoice = -3; break; |