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 #include "common/system.h"
25
26 #include "sludge/newfatal.h"
27 #include "sludge/savedata.h"
28 #include "sludge/variable.h"
29
30 #define LOAD_ERROR "Can't load custom data...\n\n"
31
32 namespace Sludge {
33
34 const char CustomSaveHelper::UTF8_CHECKER[] = {'U', 'N', '\xef', '\xbf', '\xbd', 'L', 'O', '\xef', '\xbf', '\xbd', 'C', 'K', 'E', 'D', '\0'};
35 uint16 CustomSaveHelper::_saveEncoding = false;
36 char CustomSaveHelper::_encode1 = 0;
37 char CustomSaveHelper::_encode2 = 0;
38
writeStringEncoded(const Common::String checker,Common::WriteStream * stream)39 void CustomSaveHelper::writeStringEncoded(const Common::String checker, Common::WriteStream *stream) {
40 int len = checker.size();
41
42 stream->writeUint16BE(len);
43 for (int a = 0; a < len; a++) {
44 stream->writeByte(checker[a] ^ _encode1);
45 _encode1 += _encode2;
46 }
47 }
48
readStringEncoded(Common::SeekableReadStream * fp)49 Common::String CustomSaveHelper::readStringEncoded(Common::SeekableReadStream *fp) {
50 int len = fp->readUint16BE();
51 Common::String res = "";
52
53 for (int a = 0; a < len; a++) {
54 res += (char)(fp->readByte() ^ _encode1);
55 _encode1 += _encode2;
56 }
57 return res;
58 }
59
readTextPlain(Common::SeekableReadStream * fp)60 char *CustomSaveHelper::readTextPlain(Common::SeekableReadStream *fp) {
61 int32 startPos;
62
63 uint32 stringSize = 0;
64 bool keepGoing = true;
65 char gotChar;
66 char *reply;
67
68 startPos = fp->pos();
69
70 while (keepGoing) {
71 gotChar = (char)fp->readByte();
72 if ((gotChar == '\n') || (fp->eos())) {
73 keepGoing = false;
74 } else {
75 stringSize++;
76 }
77 }
78
79 if ((stringSize == 0) && (fp->eos())) {
80 return NULL;
81 } else {
82 fp->seek(startPos, SEEK_SET);
83 reply = new char[stringSize + 1];
84 if (reply == NULL)
85 return NULL;
86 uint bytes_read = fp->read(reply, stringSize);
87 if (bytes_read != stringSize && fp->err()) {
88 warning("Reading error in readTextPlain.");
89 }
90 fp->readByte(); // Skip the newline character
91 reply[stringSize] = 0;
92 }
93
94 return reply;
95 }
96
fileToStack(const Common::String & filename,StackHandler * sH)97 bool CustomSaveHelper::fileToStack(const Common::String &filename, StackHandler *sH) {
98 Variable stringVar;
99 stringVar.varType = SVT_NULL;
100 Common::String checker = _saveEncoding ? "[Custom data (encoded)]\r\n" : "[Custom data (ASCII)]\n";
101
102 Common::InSaveFile *fp = g_system->getSavefileManager()->openForLoading(filename);
103
104 if (fp == NULL) {
105 return fatal("No such file", filename); //TODO: false value
106 }
107
108 _encode1 = (byte)_saveEncoding & 255;
109 _encode2 = (byte)(_saveEncoding >> 8);
110
111 for (uint i = 0; i < checker.size(); ++i) {
112 if (fp->readByte() != checker[i]) {
113 delete fp;
114 return fatal(LOAD_ERROR "This isn't a SLUDGE custom data file:", filename);
115 }
116 }
117
118 if (_saveEncoding) {
119 checker = readStringEncoded(fp);
120 if (checker != UTF8_CHECKER) {
121 delete fp;
122 return fatal(LOAD_ERROR "The current file encoding setting does not match the encoding setting used when this file was created:", filename);
123 }
124 }
125
126 for (;;) {
127 if (_saveEncoding) {
128 char i = fp->readByte() ^ _encode1;
129
130 if (fp->eos())
131 break;
132 switch (i) {
133 case 0: {
134 Common::String g = readStringEncoded(fp);
135 stringVar.makeTextVar(g);
136 }
137 break;
138
139 case 1:
140 stringVar.setVariable(SVT_INT, fp->readUint32LE());
141 break;
142
143 case 2:
144 stringVar.setVariable(SVT_INT, fp->readByte());
145 break;
146
147 default:
148 fatal(LOAD_ERROR "Corrupt custom data file:", filename);
149 delete fp;
150 return false;
151 }
152 } else {
153 char *line = readTextPlain(fp);
154 if (!line)
155 break;
156 stringVar.makeTextVar(line);
157 }
158
159 if (sH->first == NULL) {
160 // Adds to the TOP of the array... oops!
161 if (!addVarToStackQuick(stringVar, sH->first))
162 return false;
163 sH->last = sH->first;
164 } else {
165 // Adds to the END of the array... much better
166 if (!addVarToStackQuick(stringVar, sH->last->next))
167 return false;
168 sH->last = sH->last->next;
169 }
170 }
171
172 delete fp;
173
174 return true;
175 }
176
stackToFile(const Common::String & filename,const Variable & from)177 bool CustomSaveHelper::stackToFile(const Common::String &filename, const Variable &from) {
178 Common::OutSaveFile *fp = g_system->getSavefileManager()->openForSaving(filename);
179 if (fp == NULL) {
180 return fatal("Can't create file", filename);
181 }
182
183 VariableStack *hereWeAre = from.varData.theStack -> first;
184
185 _encode1 = (byte)_saveEncoding & 255;
186 _encode2 = (byte)(_saveEncoding >> 8);
187
188 if (_saveEncoding) {
189 fp->writeString("[Custom data (encoded)]\r\n");
190 writeStringEncoded(UTF8_CHECKER, fp);
191 } else {
192 fp->writeString("[Custom data (ASCII)]\n");
193 }
194
195 while (hereWeAre) {
196 if (_saveEncoding) {
197 switch (hereWeAre -> thisVar.varType) {
198 case SVT_STRING:
199 fp->writeByte(_encode1);
200 writeStringEncoded(hereWeAre -> thisVar.varData.theString, fp);
201 break;
202
203 case SVT_INT:
204 // Small enough to be stored as a char
205 if (hereWeAre -> thisVar.varData.intValue >= 0 && hereWeAre -> thisVar.varData.intValue < 256) {
206 fp->writeByte(2 ^ _encode1);
207 fp->writeByte(hereWeAre -> thisVar.varData.intValue);
208 } else {
209 fp->writeByte(1 ^ _encode1);
210 fp->writeUint32LE(hereWeAre -> thisVar.varData.intValue);
211 }
212 break;
213
214 default:
215 fatal("Can't create an encoded custom data file containing anything other than numbers and strings", filename);
216 delete fp;
217 return false;
218 }
219 } else {
220 Common::String makeSureItsText = hereWeAre->thisVar.getTextFromAnyVar();
221 if (makeSureItsText.empty())
222 break;
223 fp->writeString((makeSureItsText + "\n").c_str());
224 }
225
226 hereWeAre = hereWeAre -> next;
227 }
228
229 delete fp;
230
231 return true;
232 }
233
234 } // End of namespace Sludge
235