Initial commit
This commit is contained in:
247
engines/sludge/savedata.cpp
Normal file
247
engines/sludge/savedata.cpp
Normal file
@@ -0,0 +1,247 @@
|
||||
/* 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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/file.h"
|
||||
#include "common/savefile.h"
|
||||
#include "common/system.h"
|
||||
|
||||
#include "sludge/sludge.h"
|
||||
#include "sludge/newfatal.h"
|
||||
#include "sludge/savedata.h"
|
||||
#include "sludge/variable.h"
|
||||
|
||||
#define LOAD_ERROR "Can't load custom data...\n\n"
|
||||
|
||||
namespace Sludge {
|
||||
|
||||
const char CustomSaveHelper::UTF8_CHECKER[] = {'U', 'N', '\xef', '\xbf', '\xbd', 'L', 'O', '\xef', '\xbf', '\xbd', 'C', 'K', 'E', 'D', '\0'};
|
||||
uint16 CustomSaveHelper::_saveEncoding = false;
|
||||
char CustomSaveHelper::_encode1 = 0;
|
||||
char CustomSaveHelper::_encode2 = 0;
|
||||
|
||||
void CustomSaveHelper::writeStringEncoded(const Common::String &checker, Common::WriteStream *stream) {
|
||||
int len = checker.size();
|
||||
|
||||
stream->writeUint16BE(len);
|
||||
for (int a = 0; a < len; a++) {
|
||||
stream->writeByte(checker[a] ^ _encode1);
|
||||
_encode1 += _encode2;
|
||||
}
|
||||
}
|
||||
|
||||
Common::String CustomSaveHelper::readStringEncoded(Common::SeekableReadStream *fp) {
|
||||
int len = fp->readUint16BE();
|
||||
Common::String res = "";
|
||||
|
||||
for (int a = 0; a < len; a++) {
|
||||
res += (char)(fp->readByte() ^ _encode1);
|
||||
_encode1 += _encode2;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
char *CustomSaveHelper::readTextPlain(Common::SeekableReadStream *fp) {
|
||||
int32 startPos;
|
||||
|
||||
uint32 stringSize = 0;
|
||||
bool keepGoing = true;
|
||||
char gotChar;
|
||||
char *reply;
|
||||
|
||||
startPos = fp->pos();
|
||||
|
||||
while (keepGoing) {
|
||||
gotChar = (char)fp->readByte();
|
||||
if ((gotChar == '\n') || (fp->eos())) {
|
||||
keepGoing = false;
|
||||
} else {
|
||||
stringSize++;
|
||||
}
|
||||
}
|
||||
|
||||
if ((stringSize == 0) && (fp->eos())) {
|
||||
return NULL;
|
||||
} else {
|
||||
fp->seek(startPos, SEEK_SET);
|
||||
reply = new char[stringSize + 1];
|
||||
if (reply == NULL)
|
||||
return NULL;
|
||||
uint bytes_read = fp->read(reply, stringSize);
|
||||
if (bytes_read != stringSize && fp->err()) {
|
||||
warning("Reading error in readTextPlain.");
|
||||
}
|
||||
fp->readByte(); // Skip the newline character
|
||||
reply[stringSize] = 0;
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
bool CustomSaveHelper::fileToStack(const Common::String &filename, StackHandler *sH) {
|
||||
Variable stringVar;
|
||||
stringVar.varType = SVT_NULL;
|
||||
Common::String checker = _saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n";
|
||||
|
||||
Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(filename);
|
||||
|
||||
if (fp == NULL) {
|
||||
// Try looking inside game folder
|
||||
Common::File *f = new Common::File();
|
||||
if (!f->open(Common::Path(filename)))
|
||||
return fatal("No such file", filename); //TODO: false value
|
||||
fp = f;
|
||||
|
||||
// WORKAROUND: For Otto Experiment, when looking for the otto.ini
|
||||
// for the first time, include CRLF line ending to checker for both encoded and ASCII
|
||||
// data. This is needed since the original otto.ini that comes with the installer
|
||||
// have CRLF line ending.
|
||||
Common::String gameId = g_sludge->getGameId();
|
||||
if (gameId == "otto") {
|
||||
checker = _saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
_encode1 = (byte)_saveEncoding & 255;
|
||||
_encode2 = (byte)(_saveEncoding >> 8);
|
||||
|
||||
for (uint i = 0; i < checker.size(); ++i) {
|
||||
if (fp->readByte() != checker[i]) {
|
||||
delete fp;
|
||||
return fatal(LOAD_ERROR "This isn't a SLUDGE custom data file:", filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (_saveEncoding) {
|
||||
checker = readStringEncoded(fp);
|
||||
if (checker != UTF8_CHECKER) {
|
||||
delete fp;
|
||||
return fatal(LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename);
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
if (_saveEncoding) {
|
||||
char i = fp->readByte() ^ _encode1;
|
||||
|
||||
if (fp->eos())
|
||||
break;
|
||||
switch (i) {
|
||||
case 0: {
|
||||
Common::String g = readStringEncoded(fp);
|
||||
stringVar.makeTextVar(g);
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
stringVar.setVariable(SVT_INT, fp->readUint32LE());
|
||||
break;
|
||||
|
||||
case 2:
|
||||
stringVar.setVariable(SVT_INT, fp->readByte());
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal(LOAD_ERROR "Corrupt custom data file:", filename);
|
||||
delete fp;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
char *line = readTextPlain(fp);
|
||||
if (!line)
|
||||
break;
|
||||
stringVar.makeTextVar(line);
|
||||
}
|
||||
|
||||
if (sH->first == NULL) {
|
||||
// Adds to the TOP of the array... oops!
|
||||
if (!addVarToStackQuick(stringVar, sH->first))
|
||||
return false;
|
||||
sH->last = sH->first;
|
||||
} else {
|
||||
// Adds to the END of the array... much better
|
||||
if (!addVarToStackQuick(stringVar, sH->last->next))
|
||||
return false;
|
||||
sH->last = sH->last->next;
|
||||
}
|
||||
}
|
||||
|
||||
delete fp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CustomSaveHelper::stackToFile(const Common::String &filename, const Variable &from) {
|
||||
Common::OutSaveFile *fp = g_system->getSavefileManager()->openForSaving(filename);
|
||||
if (fp == NULL) {
|
||||
return fatal("Can't create file", filename);
|
||||
}
|
||||
|
||||
VariableStack *hereWeAre = from.varData.theStack -> first;
|
||||
|
||||
_encode1 = (byte)_saveEncoding & 255;
|
||||
_encode2 = (byte)(_saveEncoding >> 8);
|
||||
|
||||
if (_saveEncoding) {
|
||||
fp->writeString("[Custom data (encoded)]\r\n");
|
||||
writeStringEncoded(UTF8_CHECKER, fp);
|
||||
} else {
|
||||
fp->writeString("[Custom data (ASCII)]\n");
|
||||
}
|
||||
|
||||
while (hereWeAre) {
|
||||
if (_saveEncoding) {
|
||||
switch (hereWeAre -> thisVar.varType) {
|
||||
case SVT_STRING:
|
||||
fp->writeByte(_encode1);
|
||||
writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp);
|
||||
break;
|
||||
|
||||
case SVT_INT:
|
||||
// Small enough to be stored as a char
|
||||
if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) {
|
||||
fp->writeByte(2 ^ _encode1);
|
||||
fp->writeByte(hereWeAre -> thisVar.varData.intValue);
|
||||
} else {
|
||||
fp->writeByte(1 ^ _encode1);
|
||||
fp->writeUint32LE(hereWeAre -> thisVar.varData.intValue);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename);
|
||||
delete fp;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Common::String makeSureItsText = hereWeAre->thisVar.getTextFromAnyVar();
|
||||
|
||||
fp->writeString((makeSureItsText + "\n").c_str());
|
||||
}
|
||||
|
||||
hereWeAre = hereWeAre -> next;
|
||||
}
|
||||
|
||||
delete fp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // End of namespace Sludge
|
||||
Reference in New Issue
Block a user