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