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 "bladerunner/savefile.h"
24 
25 #include "bladerunner/bladerunner.h"
26 #include "bladerunner/boundingbox.h"
27 #include "bladerunner/vector.h"
28 
29 #include "common/rect.h"
30 #include "common/savefile.h"
31 #include "common/system.h"
32 
33 #include "graphics/thumbnail.h"
34 
35 namespace BladeRunner {
36 
list(const Common::String & target)37 SaveStateList SaveFileManager::list(const Common::String &target) {
38 	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
39 	Common::StringArray files = saveFileMan->listSavefiles(target + ".###");
40 
41 	SaveStateList saveList;
42 	for (Common::StringArray::const_iterator fileName = files.begin(); fileName != files.end(); ++fileName) {
43 		Common::InSaveFile *saveFile = saveFileMan->openForLoading(*fileName);
44 		if (saveFile == nullptr || saveFile->err()) {
45 			delete saveFile;
46 			continue;
47 		}
48 
49 		BladeRunner::SaveFileHeader header;
50 		readHeader(*saveFile, header);
51 
52 		int slotNum = atoi(fileName->c_str() + fileName->size() - 3);
53 		saveList.push_back(SaveStateDescriptor(slotNum, header._name));
54 
55 		delete saveFile;
56 	}
57 
58 	Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
59 
60 	return saveList;
61 }
62 
queryMetaInfos(const Common::String & target,int slot)63 SaveStateDescriptor SaveFileManager::queryMetaInfos(const Common::String &target, int slot) {
64 	Common::String filename = Common::String::format("%s.%03d", target.c_str(), slot);
65 	Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(filename);
66 
67 	if (saveFile == nullptr || saveFile->err()) {
68 		return SaveStateDescriptor();
69 	}
70 
71 	BladeRunner::SaveFileHeader header;
72 	if (!BladeRunner::SaveFileManager::readHeader(*saveFile, header, false)) {
73 		delete saveFile;
74 		return SaveStateDescriptor();
75 	}
76 	delete saveFile;
77 
78 	SaveStateDescriptor desc(slot, header._name);
79 	desc.setThumbnail(header._thumbnail);
80 	desc.setSaveDate(header._year, header._month, header._day);
81 	desc.setSaveTime(header._hour, header._minute);
82 	desc.setPlayTime(header._playTime);
83 	return desc;
84 }
85 
openForLoading(const Common::String & target,int slot)86 Common::InSaveFile *SaveFileManager::openForLoading(const Common::String &target, int slot) {
87 	Common::String filename = Common::String::format("%s.%03d", target.c_str(), slot);
88 	return g_system->getSavefileManager()->openForLoading(filename);
89 }
90 
openForSaving(const Common::String & target,int slot)91 Common::OutSaveFile *SaveFileManager::openForSaving(const Common::String &target, int slot) {
92 	Common::String filename = Common::String::format("%s.%03d", target.c_str(), slot);
93 	return g_system->getSavefileManager()->openForSaving(filename);
94 }
95 
remove(const Common::String & target,int slot)96 void SaveFileManager::remove(const Common::String &target, int slot) {
97 	Common::String filename = Common::String::format("%s.%03d", target.c_str(), slot);
98 	g_system->getSavefileManager()->removeSavefile(filename);
99 }
100 
readHeader(Common::SeekableReadStream & in,SaveFileHeader & header,bool skipThumbnail)101 bool SaveFileManager::readHeader(Common::SeekableReadStream &in, SaveFileHeader &header, bool skipThumbnail) {
102 	SaveFileReadStream s(in);
103 	if (s.readUint32BE() != kTag) {
104 		warning("No header found in save file");
105 		return false;
106 	}
107 
108 	header._version = s.readByte();
109 	if (header._version > kVersion) {
110 		warning("Unsupported version of save file %u, supported is %u", header._version, kVersion);
111 		return false;
112 	}
113 
114 	header._name = s.readStringSz(kNameLength);
115 
116 	header._year   = s.readUint16LE();
117 	header._month  = s.readUint16LE();
118 	header._day    = s.readUint16LE();
119 	header._hour   = s.readUint16LE();
120 	header._minute = s.readUint16LE();
121 
122 	header._playTime = 0;
123 	if (header._version >= 2) {
124 		header._playTime = s.readUint32LE();
125 	}
126 
127 	header._thumbnail = nullptr;
128 
129 	// Early check of possible corrupted save file (missing thumbnail and other data)
130 	int32 pos = s.pos();
131 	int32 sizeOfSaveFile = s.size();
132 	if (sizeOfSaveFile > 0 && sizeOfSaveFile < (int32) (pos + 4 + kThumbnailSize)) {
133 		warning("Unexpected end of save file \"%s\" (%02d:%02d %02d/%02d/%04d) reached. Size of file was: %d bytes",
134 		         header._name.c_str(),
135 		         header._hour,
136 		         header._minute,
137 		         header._day,
138 		         header._month,
139 		         header._year,
140 		         sizeOfSaveFile);
141 		return false;
142 	}
143 
144 	if (!skipThumbnail) {
145 		header._thumbnail = new Graphics::Surface(); // freed by ScummVM's smartptr
146 
147 		s.skip(4); //skip size;
148 
149 		uint16 *thumbnailData = (uint16*)malloc(kThumbnailSize); // freed by ScummVM's smartptr
150 		for (uint i = 0; i < kThumbnailSize / 2; ++i) {
151 			thumbnailData[i] = s.readUint16LE();
152 		}
153 
154 		header._thumbnail->init(80, 60, 160, thumbnailData, gameDataPixelFormat());
155 
156 		s.seek(pos);
157 	}
158 
159 	return true;
160 }
161 
writeHeader(Common::WriteStream & out,SaveFileHeader & header)162 bool SaveFileManager::writeHeader(Common::WriteStream &out, SaveFileHeader &header) {
163 	SaveFileWriteStream s(out);
164 
165 	s.writeUint32BE(kTag);
166 	s.writeByte(kVersion);
167 
168 	s.writeStringSz(header._name, kNameLength);
169 
170 	TimeDate td;
171 	g_system->getTimeAndDate(td);
172 	s.writeUint16LE(td.tm_year + 1900);
173 	s.writeUint16LE(td.tm_mon + 1);
174 	s.writeUint16LE(td.tm_mday);
175 	s.writeUint16LE(td.tm_hour);
176 	s.writeUint16LE(td.tm_min);
177 
178 	s.writeUint32LE(header._playTime);
179 
180 	return true;
181 }
182 
SaveFileWriteStream(Common::WriteStream & s)183 SaveFileWriteStream::SaveFileWriteStream(Common::WriteStream &s) : _s(s) {}
184 
debug(char * p)185 void SaveFileWriteStream::debug(char *p) {
186 	write(p, strlen(p) + 1);
187 }
188 
padBytes(int count)189 void SaveFileWriteStream::padBytes(int count) {
190 	for (int i = 0; i < count; ++i) {
191 		writeByte(0);
192 	}
193 }
194 
writeInt(int32 v)195 void SaveFileWriteStream::writeInt(int32 v) {
196 	writeUint32LE(v);
197 }
198 
writeFloat(float v)199 void SaveFileWriteStream::writeFloat(float v) {
200 	writeFloatLE(v);
201 }
202 
writeBool(bool v)203 void SaveFileWriteStream::writeBool(bool v) {
204 	writeUint32LE(v);
205 }
206 
writeStringSz(const Common::String & s,uint sz)207 void SaveFileWriteStream::writeStringSz(const Common::String &s, uint sz) {
208 	uint32 sizeToWrite = MIN(sz, s.size());
209 	write(s.begin(), sizeToWrite);
210 	if (sizeToWrite < sz) {
211 		padBytes(sz - sizeToWrite);
212 	}
213 }
214 
writeVector2(const Vector2 & v)215 void SaveFileWriteStream::writeVector2(const Vector2 &v) {
216 	writeFloatLE(v.x);
217 	writeFloatLE(v.y);
218 }
219 
writeVector3(const Vector3 & v)220 void SaveFileWriteStream::writeVector3(const Vector3 &v) {
221 	writeFloatLE(v.x);
222 	writeFloatLE(v.y);
223 	writeFloatLE(v.z);
224 }
225 
writeRect(const Common::Rect & v)226 void SaveFileWriteStream::writeRect(const Common::Rect &v) {
227 	writeUint32LE(v.left);
228 	writeUint32LE(v.top);
229 	writeUint32LE(v.right);
230 	writeUint32LE(v.bottom);
231 }
232 
writeBoundingBox(const BoundingBox & v,bool serialized)233 void SaveFileWriteStream::writeBoundingBox(const BoundingBox &v, bool serialized) {
234 	float x0, y0, z0, x1, y1, z1;
235 
236 	v.getXYZ(&x0, &y0, &z0, &x1, &y1, &z1);
237 	writeFloatLE(x0);
238 	writeFloatLE(y0);
239 	writeFloatLE(z0);
240 	writeFloatLE(x1);
241 	writeFloatLE(y1);
242 	writeFloatLE(z1);
243 
244 	// Bounding boxes have a lot of extra data that's never actually used
245 	int count = serialized ? 96 : 64;
246 	for (int i = 0; i < count; ++i) {
247 		writeFloatLE(0.0f);
248 	}
249 }
250 
SaveFileReadStream(Common::SeekableReadStream & s)251 SaveFileReadStream::SaveFileReadStream(Common::SeekableReadStream &s) : _s(s) {}
252 
readInt()253 int32 SaveFileReadStream::readInt() {
254 	return readUint32LE();
255 }
256 
readFloat()257 float SaveFileReadStream::readFloat() {
258 	return readFloatLE();
259 }
260 
readBool()261 bool SaveFileReadStream::readBool() {
262 	return readUint32LE();
263 }
264 
readStringSz(uint sz)265 Common::String SaveFileReadStream::readStringSz(uint sz) {
266 	char *buf = new char[sz + 1];
267 	read(buf, sz);
268 	buf[sz] = 0;
269 	Common::String result(buf);
270 	delete[] buf;
271 	return result;
272 }
273 
readVector2()274 Vector2 SaveFileReadStream::readVector2() {
275 	Vector2 result;
276 	result.x = readFloatLE();
277 	result.y = readFloatLE();
278 	return result;
279 }
280 
readVector3()281 Vector3 SaveFileReadStream::readVector3() {
282 	Vector3 result;
283 	result.x = readFloatLE();
284 	result.y = readFloatLE();
285 	result.z = readFloatLE();
286 	return result;
287 }
288 
readRect()289 Common::Rect SaveFileReadStream::readRect() {
290 	Common::Rect result;
291 	result.left = readUint32LE();
292 	result.top = readUint32LE();
293 	result.right = readUint32LE();
294 	result.bottom = readUint32LE();
295 	return result;
296 }
297 
readBoundingBox(bool serialized)298 BoundingBox SaveFileReadStream::readBoundingBox(bool serialized) {
299 	float x0, y0, z0, x1, y1, z1;
300 
301 	x0 = readFloatLE();
302 	y0 = readFloatLE();
303 	z0 = readFloatLE();
304 
305 	x1 = readFloatLE();
306 	y1 = readFloatLE();
307 	z1 = readFloatLE();
308 
309 	// Bounding boxes have a lot of extra data that's never actually used, and there two formats for storing bounding boxes.
310 	int count = serialized ? 96 : 64;
311 	for (int i = 0; i < count; ++i) {
312 		readFloatLE();
313 	}
314 
315 	return BoundingBox(x0, y0, z0, x1, y1, z1);
316 }
317 
318 
319 
320 } // End of namespace BladeRunner
321