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