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