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