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/util.h"
24 #include "titanic/support/simple_file.h"
25
26 namespace Titanic {
27
readStringFromStream(Common::SeekableReadStream * s)28 CString readStringFromStream(Common::SeekableReadStream *s) {
29 CString result;
30 char c;
31 while ((c = s->readByte()) != '\0')
32 result += c;
33
34 return result;
35 }
36
37 /*------------------------------------------------------------------------*/
38
open(const Common::Path & filename)39 bool File::open(const Common::Path &filename) {
40 if (!Common::File::open(filename))
41 error("Could not open file - %s", filename.toString().c_str());
42 return true;
43 }
44
45 /*------------------------------------------------------------------------*/
46
SimpleFile()47 SimpleFile::SimpleFile(): _inStream(nullptr), _outStream(nullptr), _lineCount(1) {
48 }
49
~SimpleFile()50 SimpleFile::~SimpleFile() {
51 close();
52 }
53
open(Common::SeekableReadStream * stream)54 void SimpleFile::open(Common::SeekableReadStream *stream) {
55 close();
56 _inStream = stream;
57 }
58
open(Common::OutSaveFile * stream)59 void SimpleFile::open(Common::OutSaveFile *stream) {
60 close();
61 _outStream = stream;
62 }
63
close()64 void SimpleFile::close() {
65 if (_outStream) {
66 _outStream->finalize();
67 delete _outStream;
68 _outStream = nullptr;
69 }
70
71 if (_inStream) {
72 delete _inStream;
73 _inStream = nullptr;
74 }
75 }
76
safeRead(void * dst,size_t count)77 void SimpleFile::safeRead(void *dst, size_t count) {
78 if (unsafeRead(dst, count) != count)
79 error("Could not read %d bytes", (int)count);
80 }
81
unsafeRead(void * dst,size_t count)82 size_t SimpleFile::unsafeRead(void *dst, size_t count) {
83 assert(_inStream);
84 return _inStream->read(dst, count);
85 }
86
write(const void * src,size_t count) const87 size_t SimpleFile::write(const void *src, size_t count) const {
88 assert(_outStream);
89 return _outStream->write(src, count);
90 }
91
seek(int offset,int origin)92 void SimpleFile::seek(int offset, int origin) {
93 assert(_inStream);
94 _inStream->seek(offset, origin);
95 }
96
readByte()97 byte SimpleFile::readByte() {
98 byte b;
99 safeRead(&b, 1);
100 return b;
101 }
102
readUint16LE()103 uint SimpleFile::readUint16LE() {
104 uint val;
105 safeRead(&val, 2);
106 return READ_LE_UINT16(&val);
107 }
108
readUint32LE()109 uint SimpleFile::readUint32LE() {
110 uint val;
111 safeRead(&val, 4);
112 return READ_LE_UINT32(&val);
113 }
114
readString()115 CString SimpleFile::readString() {
116 char c;
117 CString result;
118 bool backslashFlag = false;
119
120 // First skip any spaces
121 do {
122 safeRead(&c, 1);
123 } while (Common::isSpace(c));
124
125 // Ensure we've found a starting quote for the string
126 if (c != '"')
127 error("Could not find starting quote");
128
129 bool endFlag = false;
130 while (!endFlag) {
131 // Read the next character
132 safeRead(&c, 1);
133
134 if (backslashFlag) {
135 backslashFlag = false;
136 switch (c) {
137 case 'n':
138 result += '\n';
139 break;
140 case 'r':
141 result += '\r';
142 break;
143 case '\t':
144 result += '\t';
145 break;
146 default:
147 result += c;
148 break;
149 }
150 } else {
151 switch (c) {
152 case '"':
153 endFlag = true;
154 break;
155 case '\\':
156 backslashFlag = true;
157 break;
158 default:
159 result += c;
160 break;
161 }
162 }
163 }
164
165 // Return the string
166 return result;
167 }
168
readNumber()169 int SimpleFile::readNumber() {
170 char c;
171 int result = 0;
172 bool minusFlag = false;
173
174 // First skip any spaces
175 do {
176 safeRead(&c, 1);
177 } while (Common::isSpace(c));
178
179 // Check for prefix sign
180 if (c == '+' || c == '-') {
181 minusFlag = c == '-';
182 safeRead(&c, 1);
183 }
184
185 // Read in the number
186 if (!Common::isDigit(c))
187 error("Invalid number");
188
189 while (Common::isDigit(c)) {
190 result = result * 10 + (c - '0');
191 safeRead(&c, 1);
192 }
193
194 // Finally, if it's a minus value, then negate it
195 if (minusFlag)
196 result = -result;
197
198 return result;
199 }
200
readFloat()201 double SimpleFile::readFloat() {
202 char c;
203 Common::String result;
204
205 // First skip any spaces
206 do {
207 safeRead(&c, 1);
208 } while (Common::isSpace(c));
209
210 // Check for prefix sign
211 if (c == '+' || c == '-') {
212 result += c;
213 safeRead(&c, 1);
214 }
215
216 // Read in the number
217 if (!Common::isDigit(c))
218 error("Invalid number");
219
220 while (Common::isDigit(c) || c == '.') {
221 result += c;
222 safeRead(&c, 1);
223 }
224
225 // Convert to a float and return it
226 float floatValue;
227 sscanf(result.c_str(), "%f", &floatValue);
228
229 return floatValue;
230 }
231
readPoint()232 Point SimpleFile::readPoint() {
233 Point pt;
234 pt.x = readNumber();
235 pt.y = readNumber();
236
237 return pt;
238 }
239
readRect()240 Rect SimpleFile::readRect() {
241 Rect r;
242 r.left = readNumber();
243 r.top = readNumber();
244 r.right = readNumber();
245 r.bottom = readNumber();
246
247 return r;
248 }
249
readBounds()250 Rect SimpleFile::readBounds() {
251 Rect r;
252 r.left = readNumber();
253 r.top = readNumber();
254 r.setWidth(readNumber());
255 r.setHeight(readNumber());
256
257 return r;
258 }
259
readBuffer(char * buffer,size_t count)260 void SimpleFile::readBuffer(char *buffer, size_t count) {
261 CString tempString = readString();
262 if (buffer) {
263 strncpy(buffer, tempString.c_str(), count);
264 buffer[count - 1] = '\0';
265 }
266 }
267
writeUint16LE(uint val)268 void SimpleFile::writeUint16LE(uint val) {
269 byte lo = val & 0xff;
270 byte hi = (val >> 8) & 0xff;
271 write(&lo, 1);
272 write(&hi, 1);
273 }
274
writeUint32LE(uint val)275 void SimpleFile::writeUint32LE(uint val) {
276 uint16 lo = val & 0xffff;
277 uint16 hi = (val >> 16) & 0xff;
278 writeUint16LE(lo);
279 writeUint16LE(hi);
280 }
281
writeLine(const CString & str) const282 void SimpleFile::writeLine(const CString &str) const {
283 write(str.c_str(), str.size());
284 write("\r\n", 2);
285 }
286
writeString(const CString & str) const287 void SimpleFile::writeString(const CString &str) const {
288 if (str.empty())
289 return;
290
291 const char *msgP = str.c_str();
292 char c;
293
294 while ((c = *msgP++) != '\0') {
295 switch (c) {
296 case '\r':
297 write("\\r", 2);
298 break;
299 case '\n':
300 write("\\n", 2);
301 break;
302 case '\t':
303 write("\\t", 2);
304 break;
305 case '\"':
306 write("\\\"", 2);
307 break;
308 case '\\':
309 write("\\\\", 2);
310 break;
311 case '{':
312 write("\\{", 2);
313 break;
314 case '}':
315 write("\\}", 2);
316 break;
317 default:
318 write(&c, 1);
319 break;
320 }
321 }
322 }
323
writeQuotedString(const CString & str) const324 void SimpleFile::writeQuotedString(const CString &str) const {
325 write("\"", 1);
326 writeString(str);
327 write("\" ", 2);
328 }
329
writeQuotedLine(const CString & str,int indent) const330 void SimpleFile::writeQuotedLine(const CString &str, int indent) const {
331 writeIndent(indent);
332 writeQuotedString(str);
333 write("\n", 1);
334 }
335
writeNumber(int val) const336 void SimpleFile::writeNumber(int val) const {
337 CString str = CString::format("%d ", val);
338 write(str.c_str(), str.size());
339 }
340
writeNumberLine(int val,int indent) const341 void SimpleFile::writeNumberLine(int val, int indent) const {
342 writeIndent(indent);
343 writeNumber(val);
344 write("\n", 1);
345 }
346
writeFloat(double val) const347 void SimpleFile::writeFloat(double val) const {
348 Common::String valStr = Common::String::format("%f ", val);
349 write(valStr.c_str(), valStr.size());
350 }
351
writeFloatLine(double val,int indent) const352 void SimpleFile::writeFloatLine(double val, int indent) const {
353 writeIndent(indent);
354 writeFloat(val);
355 write("\n", 1);
356 }
357
writePoint(const Point & pt,int indent) const358 void SimpleFile::writePoint(const Point &pt, int indent) const {
359 writeIndent(indent);
360 writeNumber(pt.x);
361 writeNumber(pt.y);
362 write("\n", 1);
363 }
364
writeRect(const Rect & r,int indent) const365 void SimpleFile::writeRect(const Rect &r, int indent) const {
366 writePoint(Point(r.left, r.top), indent);
367 writePoint(Point(r.right, r.bottom), indent);
368 }
369
writeBounds(const Rect & r,int indent) const370 void SimpleFile::writeBounds(const Rect &r, int indent) const {
371 writePoint(Point(r.left, r.top), indent);
372 writePoint(Point(r.width(), r.height()), indent);
373 }
374
writeFormat(const char * format,...) const375 void SimpleFile::writeFormat(const char *format, ...) const {
376 // Convert the format specifier and params to a string
377 va_list va;
378 va_start(va, format);
379 CString line = CString::vformat(format, va);
380 va_end(va);
381
382 // Write out the string
383 write(format, strlen(format));
384 }
385
writeIndent(uint indent) const386 void SimpleFile::writeIndent(uint indent) const {
387 for (uint idx = 0; idx < indent; ++idx)
388 write("\t", 1);
389 }
390
isClassStart()391 bool SimpleFile::isClassStart() {
392 char c;
393
394 do {
395 safeRead(&c, 1);
396 } while (Common::isSpace(c));
397
398 assert(c == '{' || c == '}');
399 return c == '{';
400 }
401
writeClassStart(const CString & classStr,int indent)402 void SimpleFile::writeClassStart(const CString &classStr, int indent) {
403 write("\n", 1);
404 writeIndent(indent);
405 write("{\n", 2);
406 writeIndent(indent + 1);
407 writeQuotedString(classStr);
408 write("\n", 1);
409 }
410
writeClassEnd(int indent)411 void SimpleFile::writeClassEnd(int indent) {
412 writeIndent(indent);
413 write("}\n", 2);
414 }
415
scanf(const char * format,...)416 bool SimpleFile::scanf(const char *format, ...) {
417 va_list va;
418 va_start(va, format);
419 char c;
420
421 CString formatStr(format);
422 while (!formatStr.empty()) {
423 if (formatStr.hasPrefix(" ")) {
424 formatStr.deleteChar(0);
425
426 safeRead(&c, 1);
427 if (!Common::isSpace(c)) {
428 va_end(va);
429 return false;
430 }
431
432 // Skip over whitespaces
433 skipSpaces();
434 } else if (formatStr.hasPrefix("%d")) {
435 // Read in a number
436 formatStr = CString(formatStr.c_str() + 2);
437 int *param = (int *)va_arg(va, int *);
438 *param = readNumber();
439
440 if (!eos())
441 _inStream->seek(-1, SEEK_CUR);
442 } else if (formatStr.hasPrefix("%s")) {
443 // Read in text until the next space
444 formatStr = CString(formatStr.c_str() + 2);
445 CString *str = (CString *)va_arg(va, CString *);
446 str->clear();
447 while (!eos() && !Common::isSpace(c = readByte()))
448 *str += c;
449
450 if (!eos())
451 _inStream->seek(-1, SEEK_CUR);
452 }
453 }
454
455 skipSpaces();
456 va_end(va);
457 return true;
458 }
459
skipSpaces()460 void SimpleFile::skipSpaces() {
461 char c = ' ';
462 while (!eos() && Common::isSpace(c))
463 safeRead(&c, 1);
464
465 if (!eos())
466 _inStream->seek(-1, SEEK_CUR);
467 }
468
469 /*------------------------------------------------------------------------*/
470
open(const Common::String & filename)471 bool StdCWadFile::open(const Common::String &filename) {
472 Common::File f;
473 CString name = filename;
474
475 // Check for whether it is indeed a file/resource pair
476 int idx = name.indexOf('#');
477
478 if (idx < 0) {
479 // Nope, so open up file for standard reading
480 assert(!name.empty());
481 if (!f.open(name))
482 return false;
483
484 SimpleFile::open(f.readStream(f.size()));
485 f.close();
486 return true;
487 }
488
489 // Split up the name and resource, and get the resource index
490 CString fname = name.left(idx) + ".st";
491 int extPos = name.lastIndexOf('.');
492 CString resStr = name.mid(idx + 1, extPos - idx - 1);
493 int resIndex = resStr.readInt();
494
495 // Open up the index for access
496 if (!f.open(fname))
497 return false;
498 int indexSize = f.readUint32LE() / 4;
499 assert(resIndex < indexSize);
500
501 // Get the specific resource's offset, and size by also
502 // getting the offset of the following resource
503 f.seek(resIndex * 4);
504 uint resOffset = f.readUint32LE();
505 uint nextOffset = (resIndex == (indexSize - 1)) ? f.size() :
506 f.readUint32LE();
507
508 // Read in the resource
509 f.seek(resOffset);
510 Common::SeekableReadStream *stream = f.readStream(nextOffset - resOffset);
511 SimpleFile::open(stream);
512
513 f.close();
514 return true;
515 }
516
517 } // End of namespace Titanic
518