scummvm/engines/grim/savegame.cpp
Christian Krause 5df7e5991d EMI: Fix semantic of IsActorTurning() LUA call
TurnActorTo is mapped to Actor::singleTurnTo() which only
turns the actor (according to its turn rate) by a small amount.

If the turn could not be completed, TurnActorTo returns false.

In EMI, all LUA calls to TurnActorTo are repeated until the
actor does eventually look into the correct direction.

However, for parallel running LUA threads, calls to IsActorTurning
would return false during such a "LUA-controlled" turn.

In the scene with the dart players, the LUA code for the dialog
waits for the dart player to stop moving (and turning) by repeatedly
calling IsActorTurning. Since that function returns false
even if the players are still turning towards the dart board, the
dialog code starts and spawns a parallel LUA thread to use
TurnActorTo to make the dart players looking at Guybrush.

This means, that eventually for each dart player, two LUA
threads with contradicting TurnActorTo directions will run forever
since in none of the threads the final direction is ever reached.

To fix that, a new actor state variable, _singleTurning, is introduced.
It is set to true if an Actor::singleTurnTo() call indicates that
the turn is not complete. It is set to false once the turning has
fininshed.

Since all TurnActorTo calls in EMI are repeated until the function returns
false, it is safe to use that variable to indicate that a "LUA-controlled"
turning is in progress.

In GRIM, that's not the case and so isTurning() is only changed for EMI.
2014-11-30 21:24:04 +01:00

351 lines
9.1 KiB
C++

/* ResidualVM - A 3D game interpreter
*
* ResidualVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the AUTHORS
* 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 "common/endian.h"
#include "common/savefile.h"
#include "common/system.h"
#include "math/vector3d.h"
#include "engines/grim/savegame.h"
#include "engines/grim/color.h"
namespace Grim {
#define SAVEGAME_HEADERTAG 'RSAV'
#define SAVEGAME_FOOTERTAG 'ESAV'
uint SaveGame::SAVEGAME_MAJOR_VERSION = 22;
uint SaveGame::SAVEGAME_MINOR_VERSION = 26;
SaveGame *SaveGame::openForLoading(const Common::String &filename) {
Common::InSaveFile *inSaveFile = g_system->getSavefileManager()->openForLoading(filename);
if (!inSaveFile) {
warning("SaveGame::openForLoading() Error opening savegame file %s", filename.c_str());
return nullptr;
}
SaveGame *save = new SaveGame();
save->_saving = false;
save->_inSaveFile = inSaveFile;
uint32 tag = inSaveFile->readUint32BE();
if (tag != SAVEGAME_HEADERTAG) {
delete save;
return nullptr;
}
save->_majorVersion = inSaveFile->readUint32BE();
save->_minorVersion = inSaveFile->readUint32BE();
return save;
}
SaveGame *SaveGame::openForSaving(const Common::String &filename) {
Common::OutSaveFile *outSaveFile = g_system->getSavefileManager()->openForSaving(filename);
if (!outSaveFile) {
warning("SaveGame::openForSaving() Error creating savegame file %s", filename.c_str());
return nullptr;
}
SaveGame *save = new SaveGame();
save->_saving = true;
save->_outSaveFile = outSaveFile;
outSaveFile->writeUint32BE(SAVEGAME_HEADERTAG);
outSaveFile->writeUint32BE(SAVEGAME_MAJOR_VERSION);
outSaveFile->writeUint32BE(SAVEGAME_MINOR_VERSION);
save->_majorVersion = SAVEGAME_MAJOR_VERSION;
save->_minorVersion = SAVEGAME_MINOR_VERSION;
return save;
}
SaveGame::SaveGame() :
_currentSection(0), _sectionBuffer(nullptr), _majorVersion(0),
_minorVersion(0), _saving(false), _inSaveFile(nullptr), _outSaveFile(nullptr),
_sectionSize(0), _sectionAlloc(0), _sectionPtr(0) {
}
SaveGame::~SaveGame() {
if (_saving) {
_outSaveFile->writeUint32BE(SAVEGAME_FOOTERTAG);
_outSaveFile->finalize();
if (_outSaveFile->err())
warning("SaveGame::~SaveGame() Can't write file. (Disk full?)");
delete _outSaveFile;
} else {
delete _inSaveFile;
}
free(_sectionBuffer);
}
bool SaveGame::isCompatible() const {
return _majorVersion == SAVEGAME_MAJOR_VERSION && _minorVersion <= SAVEGAME_MINOR_VERSION;
}
uint SaveGame::saveMajorVersion() const {
return _majorVersion;
}
uint SaveGame::saveMinorVersion() const {
return _minorVersion;
}
uint32 SaveGame::beginSection(uint32 sectionTag) {
assert(_majorVersion == SAVEGAME_MAJOR_VERSION);
if (_currentSection != 0)
error("Tried to begin a new save game section with ending old section");
_currentSection = sectionTag;
_sectionSize = 0;
if (!_saving) {
uint32 tag = 0;
while (tag != sectionTag) {
tag = _inSaveFile->readUint32BE();
if (tag == SAVEGAME_FOOTERTAG)
error("Unable to find requested section of savegame");
_sectionSize = _inSaveFile->readUint32BE();
_inSaveFile->seek(_sectionSize, SEEK_CUR);
}
if (!_sectionBuffer || _sectionAlloc < _sectionSize) {
_sectionAlloc = _sectionSize;
byte *buff = (byte *)realloc(_sectionBuffer, _sectionAlloc);
if (buff == nullptr) {
free(_sectionBuffer);
error("Could not allocate memory for save game");
}
_sectionBuffer = buff;
}
_inSaveFile->seek(-(int32)_sectionSize, SEEK_CUR);
_inSaveFile->read(_sectionBuffer, _sectionSize);
} else {
if (!_sectionBuffer) {
_sectionAlloc = _allocAmmount;
_sectionBuffer = (byte *)malloc(_sectionAlloc);
}
}
_sectionPtr = 0;
return _sectionSize;
}
void SaveGame::endSection() {
if (_currentSection == 0)
error("Tried to end a save game section without starting a section");
if (_saving) {
_outSaveFile->writeUint32BE(_currentSection);
_outSaveFile->writeUint32BE(_sectionSize);
_outSaveFile->write(_sectionBuffer, _sectionSize);
}
_currentSection = 0;
}
void SaveGame::read(void *data, int size) {
if (_saving)
error("SaveGame::readBlock called when storing a savegame");
if (_currentSection == 0)
error("Tried to read a block without starting a section");
memcpy(data, &_sectionBuffer[_sectionPtr], size);
_sectionPtr += size;
}
uint32 SaveGame::readLEUint32() {
if (_saving)
error("SaveGame::readBlock called when storing a savegame");
if (_currentSection == 0)
error("Tried to read a block without starting a section");
uint32 data = READ_LE_UINT32(&_sectionBuffer[_sectionPtr]);
_sectionPtr += 4;
return data;
}
uint16 SaveGame::readLEUint16() {
if (_saving)
error("SaveGame::readBlock called when storing a savegame");
if (_currentSection == 0)
error("Tried to read a block without starting a section");
uint16 data = READ_LE_UINT16(&_sectionBuffer[_sectionPtr]);
_sectionPtr += 2;
return data;
}
int32 SaveGame::readLESint32() {
if (_saving)
error("SaveGame::readBlock called when storing a savegame");
if (_currentSection == 0)
error("Tried to read a block without starting a section");
int32 data = (int32)READ_LE_UINT32(&_sectionBuffer[_sectionPtr]);
_sectionPtr += 4;
return data;
}
byte SaveGame::readByte() {
if (_saving)
error("SaveGame::readBlock called when storing a savegame");
if (_currentSection == 0)
error("Tried to read a block without starting a section");
byte data = _sectionBuffer[_sectionPtr];
_sectionPtr++;
return data;
}
bool SaveGame::readBool() {
return readByte() != 0;
}
void SaveGame::checkAlloc(int size) {
if (_sectionSize + size > _sectionAlloc) {
while (_sectionSize + size > _sectionAlloc)
_sectionAlloc += _allocAmmount;
_sectionBuffer = (byte *)realloc(_sectionBuffer, _sectionAlloc);
if (!_sectionBuffer)
error("Failed to allocate space for buffer");
}
}
void SaveGame::write(const void *data, int size) {
if (!_saving)
error("SaveGame::writeBlock called when restoring a savegame");
if (_currentSection == 0)
error("Tried to write a block without starting a section");
checkAlloc(size);
memcpy(&_sectionBuffer[_sectionSize], data, size);
_sectionSize += size;
}
void SaveGame::writeLEUint32(uint32 data) {
if (!_saving)
error("SaveGame::writeBlock called when restoring a savegame");
if (_currentSection == 0)
error("Tried to write a block without starting a section");
checkAlloc(4);
WRITE_LE_UINT32(&_sectionBuffer[_sectionSize], data);
_sectionSize += 4;
}
void SaveGame::writeLEUint16(uint16 data) {
if (!_saving)
error("SaveGame::writeBlock called when restoring a savegame");
if (_currentSection == 0)
error("Tried to write a block without starting a section");
checkAlloc(2);
WRITE_LE_UINT16(&_sectionBuffer[_sectionSize], data);
_sectionSize += 2;
}
void SaveGame::writeLESint32(int32 data) {
if (!_saving)
error("SaveGame::writeBlock called when restoring a savegame");
if (_currentSection == 0)
error("Tried to write a block without starting a section");
checkAlloc(4);
WRITE_LE_UINT32(&_sectionBuffer[_sectionSize], (uint32)data);
_sectionSize += 4;
}
void SaveGame::writeBool(bool data) {
writeByte(data);
}
void SaveGame::writeByte(byte data) {
if (!_saving)
error("SaveGame::writeBlock called when restoring a savegame");
if (_currentSection == 0)
error("Tried to write a block without starting a section");
checkAlloc(1);
_sectionBuffer[_sectionSize] = data;
_sectionSize++;
}
void SaveGame::writeVector3d(const Math::Vector3d &vec) {
writeFloat(vec.x());
writeFloat(vec.y());
writeFloat(vec.z());
}
void SaveGame::writeColor(const Grim::Color &color) {
writeByte(color.getRed());
writeByte(color.getGreen());
writeByte(color.getBlue());
}
void SaveGame::writeFloat(float data) {
uint32 v;
memcpy(&v, &data, 4);
writeLEUint32(v);
}
void SaveGame::writeString(const Common::String &string) {
int32 len = string.size();
writeLESint32(len);
write(string.c_str(), len);
}
Math::Vector3d SaveGame::readVector3d() {
float x = readFloat();
float y = readFloat();
float z = readFloat();
return Math::Vector3d(x, y, z);
}
Grim::Color SaveGame::readColor() {
Color color;
color.getRed() = readByte();
color.getGreen() = readByte();
color.getBlue() = readByte();
return color;
}
float SaveGame::readFloat() {
float f;
uint32 v = readLEUint32();
memcpy(&f, &v, 4);
return f;
}
Common::String SaveGame::readString() {
int32 len = readLESint32();
Common::String s((const char *)&_sectionBuffer[_sectionPtr], len);
_sectionPtr += len;
return s;
}
} // end of namespace Grim