PRINCE: Game saving - basic implementation

This commit is contained in:
lukaslw 2014-07-29 02:25:42 +02:00
parent 822d1786b0
commit 08d781b6ad
11 changed files with 729 additions and 148 deletions

View file

@ -20,19 +20,10 @@
*
*/
#include "base/plugins.h"
#include "engines/advancedDetector.h"
#include "prince/prince.h"
#include "prince/detection.h"
namespace Prince {
struct PrinceGameDescription {
ADGameDescription desc;
int gameType;
};
int PrinceEngine::getGameType() const {
return _gameDescription->gameType;
}
@ -49,78 +40,6 @@ Common::Language PrinceEngine::getLanguage() const {
return _gameDescription->desc.language;
}
}
static const PlainGameDescriptor princeGames[] = {
{"prince", "Prince Game"},
{0, 0}
};
namespace Prince {
static const PrinceGameDescription gameDescriptions[] = {
// German
{
{
"prince",
"Galador",
AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
0
},
// Polish
{
{
"prince",
"Ksiaze i Tchorz",
AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
1
},
{ AD_TABLE_END_MARKER, 0 }
};
} // End of namespace Prince
using namespace Prince;
// we match from data too, to stop detection from a non-top-level directory
const static char *directoryGlobs[] = {
"all",
0
};
class PrinceMetaEngine : public AdvancedMetaEngine {
public:
PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) {
_singleid = "prince";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
virtual const char *getName() const {
return "Prince Engine";
}
virtual const char *getOriginalCopyright() const {
return "Copyright (C)";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
};
bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const {
using namespace Prince;
const PrinceGameDescription *gd = (const PrinceGameDescription *)desc;
@ -131,17 +50,26 @@ bool PrinceMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGa
}
bool PrinceMetaEngine::hasFeature(MetaEngineFeature f) const {
return false;
return
(f == kSupportsDeleteSave) ||
(f == kSavesSupportMetaInfo) ||
(f == kSavesSupportThumbnail) ||
(f == kSavesSupportCreationDate) ||
(f == kSupportsListSaves);
}
bool Prince::PrinceEngine::hasFeature(EngineFeature f) const {
return false;//(f == kSupportsRTL);
return
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime);
}
} // End of namespace Prince
#if PLUGIN_ENABLED_DYNAMIC(PRINCE)
REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, PrinceMetaEngine);
REGISTER_PLUGIN_DYNAMIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine);
#else
REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, PrinceMetaEngine);
REGISTER_PLUGIN_STATIC(PRINCE, PLUGIN_TYPE_ENGINE, Prince::PrinceMetaEngine);
#endif
/* vim: set tabstop=4 noexpandtab: */

101
engines/prince/detection.h Normal file
View file

@ -0,0 +1,101 @@
/* 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.
*
*/
#ifndef PRINCE_DETECTION_H
#define PRINCE_DETECTION_H
#include "prince/prince.h"
#include "engines/advancedDetector.h"
namespace Prince {
struct PrinceGameDescription {
ADGameDescription desc;
int gameType;
};
static const PlainGameDescriptor princeGames[] = {
{"prince", "Prince Game"},
{0, 0}
};
static const PrinceGameDescription gameDescriptions[] = {
{
{
"prince",
"Galador",
AD_ENTRY1s("databank.ptc", "5fa03833177331214ec1354761b1d2ee", 3565031),
Common::DE_DEU,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
0
},
{
{
"prince",
"Ksiaze i Tchorz",
AD_ENTRY1s("databank.ptc", "48ec9806bda9d152acbea8ce31c93c49", 3435298),
Common::PL_POL,
Common::kPlatformWindows,
ADGF_NO_FLAGS,
GUIO1(GUIO_NONE)
},
1
},
{ AD_TABLE_END_MARKER, 0 }
};
// we match from data too, to stop detection from a non-top-level directory
const static char *directoryGlobs[] = {
"all",
0
};
class PrinceMetaEngine : public AdvancedMetaEngine {
public:
PrinceMetaEngine() : AdvancedMetaEngine(Prince::gameDescriptions, sizeof(Prince::PrinceGameDescription), princeGames) {
_singleid = "prince";
_maxScanDepth = 2;
_directoryGlobs = directoryGlobs;
}
virtual const char *getName() const {
return "Prince Engine";
}
virtual const char *getOriginalCopyright() const {
return "Copyright (C)";
}
virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const;
virtual bool hasFeature(MetaEngineFeature f) const;
virtual int getMaximumSaveSlot() const;
virtual SaveStateList listSaves(const char *target) const;
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
virtual void removeSaveState(const char *target, int slot) const;
};
} // End of namespace Prince
#endif

View file

@ -41,7 +41,7 @@ Hero::Hero(PrinceEngine *vm, GraphicsMan *graph) : _vm(vm), _graph(graph)
, _shadZoomFactor(0), _shadScaleValue(0), _shadLineLen(0), _shadDrawX(0), _shadDrawY(0)
, _frameXSize(0), _frameYSize(0), _scaledFrameXSize(0), _scaledFrameYSize(0), _color(0)
, _coords(nullptr), _dirTab(nullptr), _currCoords(nullptr), _currDirTab(nullptr), _step(0)
, _maxBoredom(200), _turnAnim(0), _leftRightMainDir(0), _upDownMainDir(0)
, _maxBoredom(200), _turnAnim(0), _leftRightMainDir(0), _upDownMainDir(0), _animSetNr(0)
{
_zoomBitmap = (byte *)malloc(kZoomBitmapLen);
_shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize);
@ -57,6 +57,8 @@ Hero::~Hero() {
}
bool Hero::loadAnimSet(uint32 animSetNr) {
_animSetNr = animSetNr;
if (animSetNr > sizeof(heroSetTable)) {
return false;
}

View file

@ -182,11 +182,11 @@ public:
uint16 _currHeight; // height of current anim phase
Common::Array<int> _inventory; // Inventory array of items
Common::Array<int> _inventory2; // Inventory2 array of items
Common::Array<byte> _inventory; // Inventory array of items
Common::Array<byte> _inventory2; // Inventory2 array of items
// Font subtitiles font
int _color; // Color Subtitles color
// AnimSet number of animation set
int _color; // subtitles color
uint32 _animSetNr; // number of animation set
Common::Array<Animation *> _moveSet; // MoveAnims MoveSet
int16 _turnAnim;
byte *_zoomBitmap;

View file

@ -19,7 +19,8 @@ MODULE_OBJS = \
hero.o \
hero_set.o \
cursor.o \
pscr.o
pscr.o \
saveload.o
# This module can be built as a plugin
ifeq ($(ENABLE_PRINCE), DYNAMIC_PLUGIN)

View file

@ -323,7 +323,7 @@ void PrinceEngine::init() {
_objSlot = new int[kMaxObjects];
for (int i = 0; i < kMaxObjects; i++) {
_objSlot[i] = -1;
_objSlot[i] = 0xFF;
}
}
@ -458,7 +458,7 @@ bool PrinceEngine::loadLocation(uint16 locationNr) {
_mainHero->setShadowScale(_script->getShadowScale(_locationNr));
for (uint i = 0; i < _mobList.size(); i++) {
_mobList[i]._visible = _script->getMobVisible(i);
_mobList[i]._visible = _script->getMobVisible(_room->_mobs, i);
}
freeDrawNodes();
@ -992,7 +992,7 @@ int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobLis
//mob_obj
if (mob->_mask < kMaxObjects) {
int nr = _objSlot[mob->_mask];
if (nr != -1) {
if (nr != 0xFF) {
Object &obj = *_objList[nr];
Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height);
if (objectRect.contains(mousePosCamera)) {
@ -1682,7 +1682,7 @@ void PrinceEngine::freeZoomObject(int slot) {
void PrinceEngine::showObjects() {
for (int i = 0; i < kMaxObjects; i++) {
int nr = _objSlot[i];
if (nr != -1) {
if (nr != 0xFF) {
Graphics::Surface *objSurface = nullptr;
if ((_objList[nr]->_flags & 0x8000)) {
_objList[nr]->_zoomTime--;
@ -4508,7 +4508,6 @@ void PrinceEngine::mainLoop() {
}
}
}
if (_debugger->_locationNr != _locationNr)
loadLocation(_debugger->_locationNr);
if (_debugger->_cursorNr != _cursorNr)

View file

@ -31,6 +31,8 @@
#include "common/rect.h"
#include "common/events.h"
#include "common/endian.h"
#include "common/savefile.h"
#include "common/serializer.h"
#include "image/bmp.h"
@ -51,6 +53,7 @@
namespace Prince {
struct PrinceGameDescription;
struct SavegameHeader;
class PrinceEngine;
class GraphicsMan;
@ -252,6 +255,17 @@ public:
virtual ~PrinceEngine();
virtual bool hasFeature(EngineFeature f) const;
virtual bool canSaveGameStateCurrently();
virtual bool canLoadGameStateCurrently();
virtual Common::Error saveGameState(int slot, const Common::String &desc);
virtual Common::Error loadGameState(int slot);
static bool readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header);
Common::String generateSaveName(int slot);
void writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header);
void syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream);
bool loadGame(int slotNumber);
void resetGame();
int getGameType() const;
const char *getGameId() const;
@ -313,6 +327,7 @@ public:
static const int kMaxNormAnims = 64;
static const int kMaxBackAnims = 64;
static const int kMaxObjects = 64;
static const int kMaxMobs = 64;
Common::Array<AnimListItem> _animList;
Common::Array<BackgroundAnim> _backAnimList;

508
engines/prince/saveload.cpp Normal file
View file

@ -0,0 +1,508 @@
/* 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 "prince/prince.h"
#include "prince/graphics.h"
#include "prince/detection.h"
#include "prince/flags.h"
#include "prince/script.h"
#include "prince/hero.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/config-manager.h"
#include "common/memstream.h"
#include "graphics/thumbnail.h"
#include "graphics/surface.h"
#include "graphics/palette.h"
#include "graphics/scaler.h"
namespace Prince {
#define kBadSVG 99
#define kSavegameVersion 1
#define kSavegameStrSize 14
#define kSavegameStr "SCUMMVM_PRINCE"
class InterpreterFlags;
class Interpreter;
struct SavegameHeader {
uint8 version;
Common::String saveName;
Graphics::Surface *thumbnail;
int saveYear, saveMonth, saveDay;
int saveHour, saveMinutes;
};
int PrinceMetaEngine::getMaximumSaveSlot() const {
return 99;
}
SaveStateList PrinceMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
Common::String pattern = target;
pattern += ".???";
filenames = saveFileMan->listSavefiles(pattern);
sort(filenames.begin(), filenames.end()); // Sort (hopefully ensuring we are sorted numerically..)
SaveStateList saveList;
for (Common::StringArray::const_iterator filename = filenames.begin(); filename != filenames.end(); filename++) {
// Obtain the last 3 digits of the filename, since they correspond to the save slot
int slotNum = atoi(filename->c_str() + filename->size() - 3);
if (slotNum >= 0 && slotNum <= 99) {
Common::InSaveFile *file = saveFileMan->openForLoading(*filename);
if (file) {
Prince::SavegameHeader header;
// Check to see if it's a ScummVM savegame or not
char buffer[kSavegameStrSize + 1];
file->read(buffer, kSavegameStrSize + 1);
if (!strncmp(buffer, kSavegameStr, kSavegameStrSize + 1)) {
// Valid savegame
if (Prince::PrinceEngine::readSavegameHeader(file, header)) {
saveList.push_back(SaveStateDescriptor(slotNum, header.saveName));
if (header.thumbnail) {
header.thumbnail->free();
delete header.thumbnail;
}
}
} else {
// Must be an original format savegame
saveList.push_back(SaveStateDescriptor(slotNum, "Unknown"));
}
delete file;
}
}
}
return saveList;
}
SaveStateDescriptor PrinceMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
Common::InSaveFile *f = g_system->getSavefileManager()->openForLoading(fileName);
if (f) {
Prince::SavegameHeader header;
// Check to see if it's a ScummVM savegame or not
char buffer[kSavegameStrSize + 1];
f->read(buffer, kSavegameStrSize + 1);
bool hasHeader = !strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) &&
Prince::PrinceEngine::readSavegameHeader(f, header);
delete f;
if (!hasHeader) {
// Original savegame perhaps?
SaveStateDescriptor desc(slot, "Unknown");
return desc;
} else {
// Create the return descriptor
SaveStateDescriptor desc(slot, header.saveName);
desc.setThumbnail(header.thumbnail);
desc.setSaveDate(header.saveYear, header.saveMonth, header.saveDay);
desc.setSaveTime(header.saveHour, header.saveMinutes);
return desc;
}
}
return SaveStateDescriptor();
}
bool PrinceEngine::readSavegameHeader(Common::InSaveFile *in, SavegameHeader &header) {
header.thumbnail = nullptr;
// Get the savegame version
header.version = in->readByte();
if (header.version > kSavegameVersion)
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 = Graphics::loadThumbnail(*in);
if (!header.thumbnail)
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;
}
void PrinceMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String fileName = Common::String::format("%s.%03d", target, slot);
g_system->getSavefileManager()->removeSavefile(fileName);
}
// TODO
bool PrinceEngine::canSaveGameStateCurrently() {
return true;
}
// TODO
bool PrinceEngine::canLoadGameStateCurrently() {
return true;
}
Common::Error PrinceEngine::saveGameState(int slot, const Common::String &desc) {
// Set up the serializer
Common::String slotName = generateSaveName(slot);
Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(slotName);
// Write out the ScummVM savegame header
SavegameHeader header;
header.saveName = desc;
header.version = kSavegameVersion;
writeSavegameHeader(saveFile, header);
// Write out the data of the savegame
syncGame(nullptr, saveFile);
// Finish writing out game data
saveFile->finalize();
delete saveFile;
return Common::kNoError;
}
Common::String PrinceEngine::generateSaveName(int slot) {
return Common::String::format("%s.%03d", _targetName.c_str(), slot);
}
void PrinceEngine::writeSavegameHeader(Common::OutSaveFile *out, SavegameHeader &header) {
// Write out a savegame header
out->write(kSavegameStr, kSavegameStrSize + 1);
out->writeByte(kSavegameVersion);
// Write savegame name
out->write(header.saveName.c_str(), header.saveName.size() + 1);
// Get the active palette
uint8 thumbPalette[256 * 3];
_system->getPaletteManager()->grabPalette(thumbPalette, 0, 256);
// Create a thumbnail and save it
Graphics::Surface *thumb = new Graphics::Surface();
Graphics::Surface *s = _graph->_frontScreen; // check inventory / map etc..
::createThumbnail(thumb, (const byte *)s->getPixels(), s->w, s->h, thumbPalette);
Graphics::saveThumbnail(*out, *thumb);
thumb->free();
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 PrinceEngine::syncGame(Common::SeekableReadStream *readStream, Common::WriteStream *writeStream) {
int emptyRoom = 0x00;
int normRoom = 0xFF;
byte endInv = 0xFF;
Common::Serializer s(readStream, writeStream);
if (s.isSaving()) {
// Flag values
for (int i = 0; i < _flags->kMaxFlags; i++) {
uint32 value = _flags->getFlagValue((Flags::Id)(_flags->kFlagMask + i));
s.syncAsUint32LE(value);
}
// Dialog data
for (uint32 i = 0; i < _dialogDatSize; i++) {
byte value = _dialogDat[i];
s.syncAsByte(value);
}
// Location number
s.syncAsUint16LE(_locationNr);
// Rooms
for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) {
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
if (room->_mobs) {
s.syncAsByte(normRoom);
} else {
s.syncAsByte(emptyRoom);
delete room;
continue;
}
// Mobs
for (int mobId = 0; mobId < kMaxMobs; mobId++) {
byte value = _script->getMobVisible(room->_mobs, mobId);
s.syncAsByte(value);
}
// Background animations
for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) {
uint32 value = _script->getBackAnimId(_room->_backAnim, backAnimSlot);
s.syncAsUint32LE(value);
}
// Objects
for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) {
byte value = _script->getObjId(room->_obj, objectSlot);
s.syncAsByte(value);
}
delete room;
}
// Main hero
s.syncAsUint16LE(_mainHero->_visible);
s.syncAsUint16LE(_mainHero->_middleX);
s.syncAsUint16LE(_mainHero->_middleY);
s.syncAsUint16LE(_mainHero->_lastDirection);
s.syncAsUint32LE(_mainHero->_color);
s.syncAsUint16LE(_mainHero->_maxBoredom);
s.syncAsUint32LE(_mainHero->_animSetNr);
for (uint inv1Slot = 0; inv1Slot < _mainHero->_inventory.size(); inv1Slot++) {
s.syncAsByte(_mainHero->_inventory[inv1Slot]);
}
s.syncAsByte(endInv);
for (uint inv2Slot = 0; inv2Slot < _mainHero->_inventory2.size(); inv2Slot++) {
s.syncAsByte(_mainHero->_inventory2[inv2Slot]);
}
s.syncAsByte(endInv);
// Second hero
s.syncAsUint16LE(_secondHero->_visible);
s.syncAsUint16LE(_secondHero->_middleX);
s.syncAsUint16LE(_secondHero->_middleY);
s.syncAsUint16LE(_secondHero->_lastDirection);
s.syncAsUint32LE(_secondHero->_color);
s.syncAsUint16LE(_secondHero->_maxBoredom);
s.syncAsUint32LE(_secondHero->_animSetNr);
for (uint inv1Slot = 0; inv1Slot < _secondHero->_inventory.size(); inv1Slot++) {
s.syncAsByte(_secondHero->_inventory[inv1Slot]);
}
s.syncAsByte(endInv);
for (uint inv2Slot = 0; inv2Slot < _secondHero->_inventory2.size(); inv2Slot++) {
s.syncAsByte(_secondHero->_inventory2[inv2Slot]);
}
s.syncAsByte(endInv);
} else {
// Flag values
for (int i = 0; i < _flags->kMaxFlags; i++) {
uint32 value = 0;
s.syncAsUint32LE(value);
_flags->setFlagValue((Flags::Id)(_flags->kFlagMask + i), value);
}
// Dialog data
for (uint32 i = 0; i < _dialogDatSize; i++) {
byte value = 0;
s.syncAsByte(value);
_dialogDat[i] = value;
}
// Location number
int restoreRoom = 0;
s.syncAsUint16LE(restoreRoom);
_flags->setFlagValue(Flags::RESTOREROOM, restoreRoom);
// Rooms
for (int roomId = 0; roomId < _script->kMaxRooms; roomId++) {
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
byte roomType = emptyRoom;
s.syncAsByte(roomType);
if (roomType == emptyRoom) {
delete room;
continue;
}
// Mobs
for (int mobId = 0; mobId < kMaxMobs; mobId++) {
byte value = 0;
s.syncAsByte(value);
_script->setMobVisible(room->_mobs, mobId, value);
}
// Background animations
for (int backAnimSlot = 0; backAnimSlot < kMaxBackAnims; backAnimSlot++) {
uint32 value = 0;
s.syncAsUint32LE(value);
_script->setBackAnimId(_room->_backAnim, backAnimSlot, value);
}
// Objects
for (int objectSlot = 0; objectSlot < kMaxObjects; objectSlot++) {
byte value = 0;
s.syncAsByte(value);
_script->setObjId(room->_obj, objectSlot, value);
}
delete room;
}
// Main hero
s.syncAsUint16LE(_mainHero->_visible);
s.syncAsUint16LE(_mainHero->_middleX);
s.syncAsUint16LE(_mainHero->_middleY);
s.syncAsUint16LE(_mainHero->_lastDirection);
s.syncAsUint32LE(_mainHero->_color);
s.syncAsUint16LE(_mainHero->_maxBoredom);
s.syncAsUint32LE(_mainHero->_animSetNr);
_mainHero->loadAnimSet(_mainHero->_animSetNr);
_mainHero->_inventory.clear();
byte invId = endInv;
while (1) {
s.syncAsByte(invId);
if (invId == endInv) {
break;
}
_mainHero->_inventory.push_back(invId);
}
_mainHero->_inventory2.clear();
invId = endInv;
while (1) {
s.syncAsByte(invId);
if (invId == endInv) {
break;
}
_mainHero->_inventory.push_back(invId);
}
// Second hero
s.syncAsUint16LE(_secondHero->_visible);
s.syncAsUint16LE(_secondHero->_middleX);
s.syncAsUint16LE(_secondHero->_middleY);
s.syncAsUint16LE(_secondHero->_lastDirection);
s.syncAsUint32LE(_secondHero->_color);
s.syncAsUint16LE(_secondHero->_maxBoredom);
s.syncAsUint32LE(_secondHero->_animSetNr);
_secondHero->loadAnimSet(_secondHero->_animSetNr);
_secondHero->_inventory.clear();
invId = endInv;
while (1) {
s.syncAsByte(invId);
if (invId == endInv) {
break;
}
_secondHero->_inventory.push_back(invId);
}
_secondHero->_inventory2.clear();
invId = endInv;
while (1) {
s.syncAsByte(invId);
if (invId == endInv) {
break;
}
_secondHero->_inventory.push_back(invId);
}
// Script
_interpreter->setBgOpcodePC(0);
_interpreter->setFgOpcodePC(_script->_scriptInfo.restoreGame);
}
}
Common::Error PrinceEngine::loadGameState(int slot) {
if (!loadGame(slot)) {
return Common::kReadingFailed;
}
return Common::kNoError;
}
bool PrinceEngine::loadGame(int slotNumber) {
Common::MemoryReadStream *readStream;
// Open up the savegame 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);
delete saveFile;
// Check to see if it's a ScummVM savegame or not
char buffer[kSavegameStrSize + 1];
readStream->read(buffer, kSavegameStrSize + 1);
if (strncmp(buffer, kSavegameStr, kSavegameStrSize + 1) != 0) {
delete readStream;
return false;
} else {
SavegameHeader saveHeader;
if (!readSavegameHeader(readStream, saveHeader)) {
delete readStream;
return false;
}
// Delete the thumbnail
saveHeader.thumbnail->free();
delete saveHeader.thumbnail;
}
// Get in the savegame
syncGame(readStream, nullptr);
delete readStream;
// TODO
//syncSpeechSettings();
return true;
}
} // End of namespace Prince

View file

@ -148,12 +148,16 @@ uint32 Script::getStartGameOffset() {
return _scriptInfo.startGame;
}
bool Script::getMobVisible(int mob) {
return _data[_vm->_room->_mobs + mob];
uint32 Script::getLocationInitScript(int initRoomTableOffset, int roomNr) {
return (uint32)READ_UINT32(&_data[initRoomTableOffset + roomNr * 4]);
}
void Script::setMobVisible(int mob, int value) {
_data[_vm->_room->_mobs + mob] = value;
byte Script::getMobVisible(int roomMobOffset, uint16 mob) {
return _data[roomMobOffset + mob];
}
void Script::setMobVisible(int roomMobOffset, uint16 mob, byte value) {
_data[roomMobOffset + mob] = value;
}
uint8 *Script::getRoomOffset(int locationNr) {
@ -185,12 +189,21 @@ uint8 *Script::getHeroAnimName(int offset) {
return &_data[offset];
}
void Script::setBackAnimId(int offset, int animId) {
WRITE_UINT32(&_data[offset], animId);
uint32 Script::getBackAnimId(int roomBackAnimOffset, int slot) {
uint32 animId = READ_UINT32(&_data[roomBackAnimOffset + slot * 4]);
return animId;
}
void Script::setObjId(int offset, int objId) {
_data[offset] = objId;
void Script::setBackAnimId(int roomBackAnimOffset, int slot, int animId) {
WRITE_UINT32(&_data[roomBackAnimOffset + slot * 4], animId);
}
byte Script::getObjId(int roomObjOffset, int slot) {
return _data[roomObjOffset + slot];
}
void Script::setObjId(int roomObjOffset, int slot, byte objectId) {
_data[roomObjOffset + slot] = objectId;
}
int Script::scanMobEvents(int mobMask, int dataEventOffset) {
@ -232,7 +245,9 @@ int Script::scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask
return -1;
}
void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int offset) {
void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset) {
int offset = roomBackAnimOffset + slot * 4;
BackgroundAnim newBackgroundAnim;
@ -311,20 +326,15 @@ void Script::installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList,
}
}
void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int offset) {
void Script::installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset) {
for (int i = 0; i < _vm->kMaxBackAnims; i++) {
installSingleBackAnim(backAnimList, i, offset);
offset += 4;
installSingleBackAnim(backAnimList, i, roomBackAnimOffset);
}
}
void Script::installObjects(int offset) {
for (int i = 0; i < _vm->kMaxObjects; i++) {
if (_data[offset] != 0xFF) {
_vm->_objSlot[i] = i;
} else {
_vm->_objSlot[i] = -1;
}
_vm->_objSlot[i] = _data[offset];
offset++;
}
}
@ -382,11 +392,11 @@ void InterpreterFlags::resetAllFlags() {
}
void InterpreterFlags::setFlagValue(Flags::Id flagId, uint32 value) {
_flags[(uint32)flagId - FLAG_MASK] = value;
_flags[(uint32)flagId - kFlagMask] = value;
}
uint32 InterpreterFlags::getFlagValue(Flags::Id flagId) {
return _flags[(uint32)flagId - FLAG_MASK];
return _flags[(uint32)flagId - kFlagMask];
}
Interpreter::Interpreter(PrinceEngine *vm, Script *script, InterpreterFlags *flags) :
@ -497,6 +507,14 @@ void Interpreter::setResult(byte value) {
_result = value;
}
void Interpreter::setBgOpcodePC(uint32 value) {
_bgOpcodePC = value;
}
void Interpreter::setFgOpcodePC(uint32 value) {
_fgOpcodePC = value;
}
template <typename T>
T Interpreter::readScript() {
T data = _script->read<T>(_currentInstruction);
@ -506,7 +524,7 @@ T Interpreter::readScript() {
uint16 Interpreter::readScriptFlagValue() {
uint16 value = readScript<uint16>();
if (value & InterpreterFlags::FLAG_MASK) {
if (value & InterpreterFlags::kFlagMask) {
return _flags->getFlagValue((Flags::Id)value);
}
return value;
@ -567,8 +585,7 @@ void Interpreter::O_PUTOBJECT() {
uint16 objectId = readScriptFlagValue();
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
int offset = room->_obj + slot;
_vm->_script->setObjId(offset, objectId);
_vm->_script->setObjId(room->_obj, slot, objectId);
if (_vm->_locationNr == roomId) {
_vm->_objSlot[slot] = objectId;
}
@ -581,10 +598,9 @@ void Interpreter::O_REMOBJECT() {
uint16 slot = readScriptFlagValue();
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
int offset = room->_obj + slot;
_vm->_script->setObjId(offset, 0xFF);
_vm->_script->setObjId(room->_obj, slot, 0xFF);
if (_vm->_locationNr == roomId) {
_vm->_objSlot[slot] = -1;
_vm->_objSlot[slot] = 0xFF;
}
delete room;
debugInterpreter("O_REMOBJECT roomId %d slot %d", roomId, slot);
@ -653,10 +669,9 @@ void Interpreter::O_PUTBACKANIM() {
int32 animId = readScript<uint32>();
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
int offset = room->_backAnim + slot * 4;
_vm->_script->setBackAnimId(offset, animId);
_vm->_script->setBackAnimId(room->_backAnim, slot, animId);
if (_vm->_locationNr == roomId) {
_vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, offset);
_vm->_script->installSingleBackAnim(_vm->_backAnimList, slot, room->_backAnim);
}
delete room;
debugInterpreter("O_PUTBACKANIM roomId %d, slot %d, animId %d", roomId, slot, animId);
@ -670,8 +685,7 @@ void Interpreter::O_REMBACKANIM() {
}
Room *room = new Room();
room->loadRoom(_script->getRoomOffset(roomId));
int offset = room->_backAnim + slot * 4;
_vm->_script->setBackAnimId(offset, 0);
_vm->_script->setBackAnimId(room->_backAnim, slot, 0);
delete room;
debugInterpreter("O_REMBACKANIM roomId %d, slot %d", roomId, slot);
}
@ -1019,22 +1033,24 @@ void Interpreter::O_CLSTEXT() {
debugInterpreter("O_CLSTEXT slot %d", slot);
}
// TODO - check if need this for saving
void Interpreter::O_CALLTABLE() {
uint16 flag = readScript<uint16>();
int32 table = readScript<uint32>();
debugInterpreter("O_CALLTABLE flag %d, table %d", flag, table);
// makes a call from script function table
// must read table pointer from _code and
// use table entry as next opcode
Flags::Id flagId = readScriptFlagId();
int roomNr = _flags->getFlagValue(flagId);
int32 tableOffset = readScript<uint32>();
int initLocationScript = _script->getLocationInitScript(tableOffset, roomNr);
if (initLocationScript) {
_stack[_stacktop] = _currentInstruction;
_stacktop++;
_currentInstruction = initLocationScript;
}
debugInterpreter("O_CALLTABLE loc %d", roomNr);
}
void Interpreter::O_CHANGEMOB() {
uint16 mob = readScriptFlagValue();
uint16 value = readScriptFlagValue();
value ^= 1;
_vm->_script->setMobVisible(mob, value);
_vm->_script->setMobVisible(_vm->_room->_mobs, mob, value);
_vm->_mobList[mob]._visible = value;
debugInterpreter("O_CHANGEMOB mob %d, value %d", mob, value);
}
@ -1527,7 +1543,7 @@ void Interpreter::O_BACKANIMRANGE() {
uint16 low = readScriptFlagValue();
uint16 high = readScriptFlagValue();
if (animId != 0xFFFF) {
if (animId & InterpreterFlags::FLAG_MASK) {
if (animId & InterpreterFlags::kFlagMask) {
animId = _flags->getFlagValue((Flags::Id)animId);
}
}

View file

@ -96,6 +96,8 @@ private:
class Script {
public:
static const int16 kMaxRooms = 60;
Script(PrinceEngine *vm);
~Script();
@ -133,23 +135,30 @@ public:
}
uint32 getStartGameOffset();
uint32 getLocationInitScript(int initRoomTableOffset, int roomNr);
int16 getLightX(int locationNr);
int16 getLightY(int locationNr);
int32 getShadowScale(int locationNr);
uint8 *getRoomOffset(int locationNr);
int32 getOptionStandardOffset(int option);
uint8 *getHeroAnimName(int offset);
void setBackAnimId(int offset, int animId);
void setObjId(int offset, int objId);
void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int offset);
void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int offset);
void installBackAnims(Common::Array<BackgroundAnim> &backAnimList, int roomBackAnimOffset);
void installSingleBackAnim(Common::Array<BackgroundAnim> &backAnimList, int slot, int roomBackAnimOffset);
void installObjects(int offset);
bool loadAllMasks(Common::Array<Mask> &maskList, int offset);
int scanMobEvents(int mobMask, int dataEventOffset);
int scanMobEventsWithItem(int mobMask, int dataEventOffset, int itemMask);
bool getMobVisible(int mob);
void setMobVisible(int mob, int value);
byte getMobVisible(int roomMobOffset, uint16 mob);
void setMobVisible(int roomMobOffset, uint16 mob, byte value);
uint32 getBackAnimId(int roomBackAnimOffset, int slot);
void setBackAnimId(int roomBackAnimOffset, int slot, int animId);
byte getObjId(int roomObjOffset, int slot);
void setObjId(int roomObjOffset, int slot, byte objectId);
const char *getString(uint32 offset) {
return (const char *)(&_data[offset]);
@ -171,11 +180,10 @@ public:
void resetAllFlags();
static const uint16 FLAG_MASK = 0x8000;
static const uint16 kFlagMask = 0x8000;
static const uint16 kMaxFlags = 2000;
private:
static const uint16 MAX_FLAGS = 2000;
int32 _flags[MAX_FLAGS];
int32 _flags[kMaxFlags];
};
class Interpreter {
@ -189,6 +197,9 @@ public:
int getLastOPCode();
int getFgOpcodePC();
void setBgOpcodePC(uint32 value);
void setFgOpcodePC(uint32 value);
uint32 getCurrentString();
void setCurrentString(uint32 value);