1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/savefile.h"
24
25 #include "sludge/allfiles.h"
26 #include "sludge/moreio.h"
27 #include "sludge/newfatal.h"
28 #include "sludge/savedata.h"
29 #include "sludge/variable.h"
30
31 #define LOAD_ERROR "Can't load custom data...\n\n"
32
33 namespace Sludge {
34
35 const char CustomSaveHelper::UTF8_CHECKER[] = {'U', 'N', '\xef', '\xbf', '\xbd', 'L', 'O', '\xef', '\xbf', '\xbd', 'C', 'K', 'E', 'D', '\0'};
36 uint16 CustomSaveHelper::_saveEncoding = false;
37 char CustomSaveHelper::_encode1 = 0;
38 char CustomSaveHelper::_encode2 = 0;
39
writeStringEncoded(const Common::String checker,Common::WriteStream * stream)40 void CustomSaveHelper::writeStringEncoded(const Common::String checker, Common::WriteStream *stream) {
41 int len = checker.size();
42
43 stream->writeUint16BE(len);
44 for (int a = 0; a < len; a++) {
45 stream->writeByte(checker[a] ^ _encode1);
46 _encode1 += _encode2;
47 }
48 }
49
readStringEncoded(Common::SeekableReadStream * fp)50 Common::String CustomSaveHelper::readStringEncoded(Common::SeekableReadStream *fp) {
51 int len = fp->readUint16BE();
52 Common::String res = "";
53
54 for (int a = 0; a < len; a++) {
55 res += (char)(fp->readByte() ^ _encode1);
56 _encode1 += _encode2;
57 }
58 return res;
59 }
60
readTextPlain(Common::SeekableReadStream * fp)61 char *CustomSaveHelper::readTextPlain(Common::SeekableReadStream *fp) {
62 int32 startPos;
63
64 uint32 stringSize = 0;
65 bool keepGoing = true;
66 char gotChar;
67 char *reply;
68
69 startPos = fp->pos();
70
71 while (keepGoing) {
72 gotChar = (char)fp->readByte();
73 if ((gotChar == '\n') || (fp->eos())) {
74 keepGoing = false;
75 } else {
76 stringSize++;
77 }
78 }
79
80 if ((stringSize == 0) && (fp->eos())) {
81 return NULL;
82 } else {
83 fp->seek(startPos, SEEK_SET);
84 reply = new char[stringSize + 1];
85 if (reply == NULL)
86 return NULL;
87 uint bytes_read = fp->read(reply, stringSize);
88 if (bytes_read != stringSize && fp->err()) {
89 warning("Reading error in readTextPlain.");
90 }
91 fp->readByte(); // Skip the newline character
92 reply[stringSize] = 0;
93 }
94
95 return reply;
96 }
97
fileToStack(const Common::String & filename,StackHandler * sH)98 bool CustomSaveHelper::fileToStack(const Common::String &filename, StackHandler *sH) {
99 Variable stringVar;
100 stringVar.varType = SVT_NULL;
101 Common::String checker = _saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n";
102
103 Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(filename);
104
105 if (fp == NULL) {
106 return fatal("No such file", filename); //TODO: false value
107 }
108
109 _encode1 = (byte)_saveEncoding & 255;
110 _encode2 = (byte)(_saveEncoding >> 8);
111
112 for (uint i = 0; i < checker.size(); ++i) {
113 if (fp->readByte() != checker[i]) {
114 delete fp;
115 return fatal(LOAD_ERROR "This isn't a SLUDGE custom data file:", filename);
116 }
117 }
118
119 if (_saveEncoding) {
120 checker = readStringEncoded(fp);
121 if (checker != UTF8_CHECKER) {
122 delete fp;
123 return fatal(LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename);
124 }
125 }
126
127 for (;;) {
128 if (_saveEncoding) {
129 char i = fp->readByte() ^ _encode1;
130
131 if (fp->eos())
132 break;
133 switch (i) {
134 case 0: {
135 Common::String g = readStringEncoded(fp);
136 stringVar.makeTextVar(g);
137 }
138 break;
139
140 case 1:
141 stringVar.setVariable(SVT_INT, fp->readUint32LE());
142 break;
143
144 case 2:
145 stringVar.setVariable(SVT_INT, fp->readByte());
146 break;
147
148 default:
149 fatal(LOAD_ERROR "Corrupt custom data file:", filename);
150 delete fp;
151 return false;
152 }
153 } else {
154 char *line = readTextPlain(fp);
155 if (!line)
156 break;
157 stringVar.makeTextVar(line);
158 }
159
160 if (sH->first == NULL) {
161 // Adds to the TOP of the array... oops!
162 if (!addVarToStackQuick(stringVar, sH->first))
163 return false;
164 sH->last = sH->first;
165 } else {
166 // Adds to the END of the array... much better
167 if (!addVarToStackQuick(stringVar, sH->last->next))
168 return false;
169 sH->last = sH->last->next;
170 }
171 }
172
173 delete fp;
174
175 return true;
176 }
177
stackToFile(const Common::String & filename,const Variable & from)178 bool CustomSaveHelper::stackToFile(const Common::String &filename, const Variable &from) {
179 Common::OutSaveFile *fp = g_system->getSavefileManager()->openForSaving(filename);
180 if (fp == NULL) {
181 return fatal("Can't create file", filename);
182 }
183
184 VariableStack *hereWeAre = from.varData.theStack -> first;
185
186 _encode1 = (byte)_saveEncoding & 255;
187 _encode2 = (byte)(_saveEncoding >> 8);
188
189 if (_saveEncoding) {
190 fp->writeString("[Custom data (encoded)]\r\n");
191 writeStringEncoded(UTF8_CHECKER, fp);
192 } else {
193 fp->writeString("[Custom data (ASCII)]\n");
194 }
195
196 while (hereWeAre) {
197 if (_saveEncoding) {
198 switch (hereWeAre -> thisVar.varType) {
199 case SVT_STRING:
200 fp->writeByte(_encode1);
201 writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp);
202 break;
203
204 case SVT_INT:
205 // Small enough to be stored as a char
206 if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) {
207 fp->writeByte(2 ^ _encode1);
208 fp->writeByte(hereWeAre -> thisVar.varData.intValue);
209 } else {
210 fp->writeByte(1 ^ _encode1);
211 fp->writeUint32LE(hereWeAre -> thisVar.varData.intValue);
212 }
213 break;
214
215 default:
216 fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename);
217 delete fp;
218 return false;
219 }
220 } else {
221 Common::String makeSureItsText = hereWeAre->thisVar.getTextFromAnyVar();
222 if (makeSureItsText.empty())
223 break;
224 fp->writeString((makeSureItsText + "\n").c_str());
225 }
226
227 hereWeAre = hereWeAre -> next;
228 }
229
230 delete fp;
231
232 return true;
233 }
234
235 } // End of namespace Sludge
236