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 "glk/streams.h"
24 #include "glk/conf.h"
25 #include "glk/events.h"
26 #include "glk/glk.h"
27 #include "glk/windows.h"
28 #include "gui/saveload.h"
29 #include "common/file.h"
30 #include "common/savefile.h"
31 #include "common/translation.h"
32 
33 namespace Glk {
34 
Stream(Streams * streams,bool readable,bool writable,uint rock,bool unicode)35 Stream::Stream(Streams *streams, bool readable, bool writable, uint rock, bool unicode) :
36 		_streams(streams), _readable(readable), _writable(writable), _rock(0), _unicode(unicode),
37 		_readCount(0), _writeCount(0), _prev(nullptr), _next(nullptr) {
38 	if (g_vm->gli_register_obj)
39 		_dispRock = (*g_vm->gli_register_obj)(this, gidisp_Class_Stream);
40 }
41 
~Stream()42 Stream::~Stream() {
43 	_streams->removeStream(this);
44 
45 	if (g_vm->gli_unregister_obj)
46 		(*g_vm->gli_unregister_obj)(this, gidisp_Class_Stream, _dispRock);
47 }
48 
getNext(uint * rock) const49 Stream *Stream::getNext(uint *rock) const {
50 	Stream *stream = _next;
51 	if (rock)
52 		*rock = stream ? stream->_rock : 0;
53 	return stream;
54 }
55 
fillResult(StreamResult * result)56 void Stream::fillResult(StreamResult *result) {
57 	if (result) {
58 		result->_readCount = _readCount;
59 		result->_writeCount = _writeCount;
60 	}
61 }
62 
close(StreamResult * result)63 void Stream::close(StreamResult *result) {
64 	// Get the read/write totals
65 	fillResult(result);
66 
67 	// Remove the stream
68 	delete this;
69 }
70 
setZColors(uint fg,uint bg)71 void Stream::setZColors(uint fg, uint bg) {
72 	if (_writable && g_conf->_styleHint)
73 		Windows::_forceRedraw = true;
74 }
75 
setReverseVideo(bool reverse)76 void Stream::setReverseVideo(bool reverse) {
77 	if (_writable && g_conf->_styleHint)
78 		Windows::_forceRedraw = true;
79 }
80 
81 /*--------------------------------------------------------------------------*/
82 
~WindowStream()83 WindowStream::~WindowStream() {
84 	_window->_stream = nullptr;
85 }
86 
close(StreamResult * result)87 void WindowStream::close(StreamResult *result) {
88 	warning("cannot close window stream");
89 }
90 
putChar(unsigned char ch)91 void WindowStream::putChar(unsigned char ch) {
92 	if (!_writable)
93 		return;
94 	++_writeCount;
95 
96 	if (_window->_lineRequest || _window->_lineRequestUni) {
97 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
98 			_window->cancelLineEvent(nullptr);
99 			g_vm->_events->_forceClick = false;
100 		} else {
101 			warning("putChar: window has pending line request");
102 		}
103 	}
104 
105 	_window->putCharUni(ch);
106 	if (_window->_echoStream)
107 		_window->_echoStream->putChar(ch);
108 }
109 
putCharUni(uint32 ch)110 void WindowStream::putCharUni(uint32 ch) {
111 	if (!_writable)
112 		return;
113 	++_writeCount;
114 
115 	if (_window->_lineRequest || _window->_lineRequestUni) {
116 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
117 			_window->cancelLineEvent(nullptr);
118 			g_vm->_events->_forceClick = false;
119 		} else {
120 			warning("putCharUni: window has pending line request");
121 		}
122 	}
123 
124 	_window->putCharUni(ch);
125 	if (_window->_echoStream)
126 		_window->_echoStream->putCharUni(ch);
127 }
128 
putBuffer(const char * buf,size_t len)129 void WindowStream::putBuffer(const char *buf, size_t len) {
130 	if (!_writable)
131 		return;
132 	_writeCount += len;
133 
134 	if (_window->_lineRequest || _window->_lineRequestUni) {
135 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
136 			_window->cancelLineEvent(nullptr);
137 			g_vm->_events->_forceClick = false;
138 		} else {
139 			warning("putBuffer: window has pending line request");
140 		}
141 	}
142 
143 	for (size_t lx = 0; lx < len; lx++, buf++)
144 		_window->putCharUni(*buf);
145 	if (_window->_echoStream)
146 		_window->_echoStream->putBuffer(buf, len);
147 }
148 
putBufferUni(const uint32 * buf,size_t len)149 void WindowStream::putBufferUni(const uint32 *buf, size_t len) {
150 	if (!_writable)
151 		return;
152 	_writeCount += len;
153 
154 	if (_window->_lineRequest || _window->_lineRequestUni) {
155 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
156 			_window->cancelLineEvent(nullptr);
157 			g_vm->_events->_forceClick = false;
158 		} else {
159 			warning("putBuffer: window has pending line request");
160 		}
161 	}
162 
163 	for (size_t lx = 0; lx < len; lx++, buf++)
164 		_window->putCharUni(*buf);
165 	if (_window->_echoStream)
166 		_window->_echoStream->putBufferUni(buf, len);
167 }
168 
unputBuffer(const char * buf,size_t len)169 void WindowStream::unputBuffer(const char *buf, size_t len) {
170 	uint lx;
171 	const char *cx;
172 
173 	if (!_writable)
174 		return;
175 
176 	if (_window->_lineRequest || _window->_lineRequestUni) {
177 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
178 			_window->cancelLineEvent(nullptr);
179 			g_vm->_events->_forceClick = false;
180 		} else {
181 			warning("unput_buffer: window has pending line request");
182 			return;
183 		}
184 	}
185 
186 	for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
187 		if (!_window->unputCharUni(*cx))
188 			break;
189 		_writeCount--;
190 	}
191 	if (_window->_echoStream)
192 		_window->_echoStream->unputBuffer(buf, len);
193 }
194 
unputBufferUni(const uint32 * buf,size_t len)195 void WindowStream::unputBufferUni(const uint32 *buf, size_t len) {
196 	uint lx;
197 	const uint32 *cx;
198 
199 	if (!_writable)
200 		return;
201 
202 	if (_window->_lineRequest || _window->_lineRequestUni) {
203 		if (g_conf->_safeClicks && g_vm->_events->_forceClick) {
204 			_window->cancelLineEvent(nullptr);
205 			g_vm->_events->_forceClick = false;
206 		} else {
207 			warning("unput_buffer: window has pending line request");
208 			return;
209 		}
210 	}
211 
212 	for (lx = 0, cx = buf + len - 1; lx < len; lx++, cx--) {
213 		if (!_window->unputCharUni(*cx))
214 			break;
215 		_writeCount--;
216 	}
217 
218 	if (_window->_echoStream)
219 		_window->_echoStream->unputBufferUni(buf, len);
220 }
221 
setStyle(uint val)222 void WindowStream::setStyle(uint val) {
223 	if (!_writable)
224 		return;
225 
226 	if (val >= style_NUMSTYLES)
227 		val = 0;
228 
229 	_window->_attr.style = val;
230 	if (_window->_echoStream)
231 		_window->_echoStream->setStyle(val);
232 }
233 
setHyperlink(uint linkVal)234 void WindowStream::setHyperlink(uint linkVal) {
235 	if (_writable)
236 		_window->_attr.hyper = linkVal;
237 }
238 
setZColors(uint fg,uint bg)239 void WindowStream::setZColors(uint fg, uint bg) {
240 	if (!_writable || !g_conf->_styleHint)
241 		return;
242 
243 	uint fore = fg, back = bg;
244 
245 	if (fg != zcolor_Transparent && fg != zcolor_Cursor) {
246 		PropFontInfo *info = &g_conf->_propInfo;
247 
248 		if (fg == zcolor_Default) {
249 			_window->_attr.fgset = false;
250 			_window->_attr.fgcolor = 0;
251 			Windows::_overrideFgSet = false;
252 			Windows::_overrideFgVal = 0;
253 
254 			info->_moreColor = info->_moreSave;
255 			info->_caretColor = info->_caretSave;
256 			info->_linkColor = info->_linkSave;
257 		} else if (fg != zcolor_Current) {
258 			_window->_attr.fgset = true;
259 			_window->_attr.fgcolor = fg;
260 			Windows::_overrideFgSet = true;
261 			Windows::_overrideFgVal = fg;
262 
263 			info->_moreColor = fore;
264 			info->_caretColor = fore;
265 			info->_linkColor = fore;
266 		}
267 	}
268 
269 	if (/*bg != zcolor_Transparent &&*/ bg != zcolor_Cursor) {
270 		if (bg == zcolor_Default) {
271 			_window->_attr.bgset = false;
272 			_window->_attr.bgcolor = 0;
273 			Windows::_overrideBgSet = false;
274 			Windows::_overrideBgVal = 0;
275 
276 			g_conf->_windowColor = g_conf->_windowSave;
277 			g_conf->_borderColor = g_conf->_borderSave;
278 		} else if (bg != zcolor_Current) {
279 			_window->_attr.bgset = true;
280 			_window->_attr.bgcolor = bg;
281 			Windows::_overrideBgSet = true;
282 			Windows::_overrideBgVal = bg;
283 
284 			g_conf->_windowColor = back;
285 			g_conf->_borderColor = back;
286 		}
287 	}
288 
289 	Windows::_overrideReverse = !(fg == zcolor_Default && bg == zcolor_Default);
290 	Windows::_forceRedraw = true;
291 
292 	if (_window->_echoStream)
293 		_window->_echoStream->setZColors(fg, bg);
294 }
295 
setReverseVideo(bool reverse)296 void WindowStream::setReverseVideo(bool reverse) {
297 	if (!_writable || !g_conf->_styleHint)
298 		return;
299 
300 	_window->_attr.reverse = reverse;
301 	if (_window->_echoStream)
302 		_window->_echoStream->setReverseVideo(reverse);
303 
304 	Windows::_forceRedraw = true;
305 }
306 
307 /*--------------------------------------------------------------------------*/
308 
MemoryStream(Streams * streams,void * buf,size_t buflen,FileMode mode,uint rock,bool unicode)309 MemoryStream::MemoryStream(Streams *streams, void *buf, size_t buflen, FileMode mode, uint rock, bool unicode) :
310 	Stream(streams, mode != filemode_Write, mode != filemode_Read, rock, unicode),
311 	_buf(buf), _bufLen(buflen), _bufPtr(buf) {
312 	assert(_buf || !_bufLen);
313 	assert(mode == filemode_Read || mode == filemode_Write || mode == filemode_ReadWrite);
314 
315 	if (unicode)
316 		_bufEnd = (uint32 *)buf + buflen;
317 	else
318 		_bufEnd = (byte *)buf + buflen;
319 	_bufEof = mode == filemode_Write ? _buf : _bufEnd;
320 
321 	if (g_vm->gli_register_arr)
322 		_arrayRock = (*g_vm->gli_register_arr)(buf, buflen, unicode ?  "&+#!Iu" : "&+#!Cn");
323 }
324 
~MemoryStream()325 MemoryStream::~MemoryStream() {
326 	if (g_vm->gli_unregister_arr) {
327 		const char *typedesc = _unicode ? "&+#!Iu" : "&+#!Cn";
328 		(*g_vm->gli_unregister_arr)(_buf, _bufLen, typedesc, _arrayRock);
329 	}
330 }
331 
putChar(unsigned char ch)332 void MemoryStream::putChar(unsigned char ch) {
333 	if (!_writable)
334 		return;
335 	++_writeCount;
336 
337 	if (_bufPtr < _bufEnd) {
338 		if (_unicode) {
339 			*((uint32 *)_bufPtr) = ch;
340 			_bufPtr = ((uint32 *)_bufPtr) + 1;
341 		} else {
342 			*((unsigned char *)_bufPtr) = ch;
343 			_bufPtr = ((unsigned char *)_bufPtr) + 1;
344 		}
345 
346 		if (_bufPtr > _bufEof)
347 			_bufEof = _bufPtr;
348 	}
349 }
350 
putCharUni(uint32 ch)351 void MemoryStream::putCharUni(uint32 ch) {
352 	if (!_writable)
353 		return;
354 	++_writeCount;
355 
356 	if (_bufPtr < _bufEnd) {
357 		if (_unicode) {
358 			*((uint32 *)_bufPtr) = ch;
359 			_bufPtr = ((uint32 *)_bufPtr) + 1;
360 		} else {
361 			*((unsigned char *)_bufPtr) = (unsigned char)ch;
362 			_bufPtr = ((unsigned char *)_bufPtr) + 1;
363 		}
364 		if (_bufPtr > _bufEof)
365 			_bufEof = _bufPtr;
366 	}
367 }
368 
putBuffer(const char * buf,size_t len)369 void MemoryStream::putBuffer(const char *buf, size_t len) {
370 	size_t lx;
371 
372 	if (!_writable)
373 		return;
374 	_writeCount += len;
375 
376 	if (_bufPtr >= _bufEnd) {
377 		len = 0;
378 	} else {
379 		if (!_unicode) {
380 			unsigned char *bp = (unsigned char *)_bufPtr;
381 			if (bp + len > (unsigned char *)_bufEnd) {
382 				lx = (bp + len) - (unsigned char *)_bufEnd;
383 				if (lx < len)
384 					len -= lx;
385 				else
386 					len = 0;
387 			}
388 			if (len) {
389 				memmove(bp, buf, len);
390 				bp += len;
391 				if (bp > (unsigned char *)_bufEof)
392 					_bufEof = bp;
393 			}
394 			_bufPtr = bp;
395 		} else {
396 			uint32 *bp = (uint32 *)_bufPtr;
397 			if (bp + len > (uint32 *)_bufEnd) {
398 				lx = (bp + len) - (uint32 *)_bufEnd;
399 				if (lx < len)
400 					len -= lx;
401 				else
402 					len = 0;
403 			}
404 			if (len) {
405 				uint i;
406 				for (i = 0; i < len; i++)
407 					bp[i] = buf[i];
408 				bp += len;
409 				if (bp > (uint32 *)_bufEof)
410 					_bufEof = bp;
411 			}
412 			_bufPtr = bp;
413 		}
414 	}
415 }
416 
putBufferUni(const uint32 * buf,size_t len)417 void MemoryStream::putBufferUni(const uint32 *buf, size_t len) {
418 	size_t lx;
419 
420 	if (!_writable)
421 		return;
422 	_writeCount += len;
423 
424 	if (_bufPtr >= _bufEnd) {
425 		len = 0;
426 	} else {
427 		if (!_unicode) {
428 			unsigned char *bp = (unsigned char *)_bufPtr;
429 			if (bp + len > (unsigned char *)_bufEnd) {
430 				lx = (bp + len) - (unsigned char *)_bufEnd;
431 				if (lx < len)
432 					len -= lx;
433 				else
434 					len = 0;
435 			}
436 			if (len) {
437 				uint i;
438 				for (i = 0; i < len; i++) {
439 					uint32 ch = buf[i];
440 					if (ch > 0xff)
441 						ch = '?';
442 					bp[i] = (unsigned char)ch;
443 				}
444 				bp += len;
445 				if (bp > (unsigned char *)_bufEof)
446 					_bufEof = bp;
447 			}
448 			_bufPtr = bp;
449 		} else {
450 			uint32 *bp = (uint32 *)_bufPtr;
451 			if (bp + len > (uint32 *)_bufEnd) {
452 				lx = (bp + len) - (uint32 *)_bufEnd;
453 				if (lx < len)
454 					len -= lx;
455 				else
456 					len = 0;
457 			}
458 			if (len) {
459 				memmove(bp, buf, len * 4);
460 				bp += len;
461 				if (bp > (uint32 *)_bufEof)
462 					_bufEof = bp;
463 			}
464 			_bufPtr = bp;
465 		}
466 	}
467 }
468 
getPosition() const469 uint MemoryStream::getPosition() const {
470 	if (_unicode)
471 		return ((uint32 *)_bufPtr - (uint32 *)_buf);
472 	else
473 		return ((unsigned char *)_bufPtr - (unsigned char *)_buf);
474 }
475 
setPosition(int pos,uint seekMode)476 void MemoryStream::setPosition(int pos, uint seekMode) {
477 	if (!_unicode) {
478 		if (seekMode == seekmode_Current)
479 			pos = ((unsigned char *)_bufPtr - (unsigned char *)_buf) + pos;
480 		else if (seekMode == seekmode_End)
481 			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf) + pos;
482 		else {
483 			// pos = pos
484 		}
485 
486 		if (pos < 0)
487 			pos = 0;
488 		if (pos > ((unsigned char *)_bufEof - (unsigned char *)_buf))
489 			pos = ((unsigned char *)_bufEof - (unsigned char *)_buf);
490 		_bufPtr = (unsigned char *)_buf + pos;
491 	} else {
492 		if (seekMode == seekmode_Current)
493 			pos = ((uint32 *)_bufPtr - (uint32 *)_buf) + pos;
494 		else if (seekMode == seekmode_End)
495 			pos = ((uint32 *)_bufEof - (uint32 *)_buf) + pos;
496 
497 		if (pos < 0)
498 			pos = 0;
499 		if (pos > ((uint32 *)_bufEof - (uint32 *)_buf))
500 			pos = ((uint32 *)_bufEof - (uint32 *)_buf);
501 		_bufPtr = (uint32 *)_buf + pos;
502 	}
503 }
504 
getChar()505 int MemoryStream::getChar() {
506 	if (!_readable)
507 		return -1;
508 
509 	if (_bufPtr < _bufEnd) {
510 		if (!_unicode) {
511 			unsigned char ch;
512 			ch = *((unsigned char *)_bufPtr);
513 			_bufPtr = ((unsigned char *)_bufPtr) + 1;
514 			_readCount++;
515 			return ch;
516 		} else {
517 			uint32 ch;
518 			ch = *((uint32 *)_bufPtr);
519 			_bufPtr = ((uint32 *)_bufPtr) + 1;
520 			_readCount++;
521 			if (ch > 0xff)
522 				ch = '?';
523 			return ch;
524 		}
525 	} else {
526 		return -1;
527 	}
528 }
529 
getCharUni()530 int MemoryStream::getCharUni() {
531 	if (!_readable)
532 		return -1;
533 
534 	if (_bufPtr < _bufEnd) {
535 		if (!_unicode) {
536 			unsigned char ch;
537 			ch = *((unsigned char *)_bufPtr);
538 			_bufPtr = ((unsigned char *)_bufPtr) + 1;
539 			_readCount++;
540 			return ch;
541 		} else {
542 			uint32 ch;
543 			ch = *((uint32 *)_bufPtr);
544 			_bufPtr = ((uint32 *)_bufPtr) + 1;
545 			_readCount++;
546 			return ch;
547 		}
548 	} else {
549 		return -1;
550 	}
551 }
552 
getBuffer(char * buf,uint len)553 uint MemoryStream::getBuffer(char *buf, uint len) {
554 	if (!_readable)
555 		return 0;
556 
557 	if (_bufPtr >= _bufEnd) {
558 		len = 0;
559 	} else {
560 		if (!_unicode) {
561 			unsigned char *bp = (unsigned char *)_bufPtr;
562 			if (bp + len > (unsigned char *)_bufEnd) {
563 				uint lx;
564 				lx = (bp + len) - (unsigned char *)_bufEnd;
565 				if (lx < len)
566 					len -= lx;
567 				else
568 					len = 0;
569 			}
570 
571 			if (len) {
572 				memcpy(buf, bp, len);
573 				bp += len;
574 				if (bp > (unsigned char *)_bufEof)
575 					_bufEof = bp;
576 			}
577 
578 			_readCount += len;
579 			_bufPtr = bp;
580 		} else {
581 			uint32 *bp = (uint32 *)_bufPtr;
582 			if (bp + len > (uint32 *)_bufEnd) {
583 				uint lx;
584 				lx = (bp + len) - (uint32 *)_bufEnd;
585 				if (lx < len)
586 					len -= lx;
587 				else
588 					len = 0;
589 			}
590 			if (len) {
591 				uint i;
592 				for (i = 0; i < len; i++) {
593 					uint32 ch = *bp++;
594 					if (ch > 0xff)
595 						ch = '?';
596 					*buf++ = (char)ch;
597 				}
598 				if (bp > (uint32 *)_bufEof)
599 					_bufEof = bp;
600 			}
601 
602 			_readCount += len;
603 			_bufPtr = bp;
604 		}
605 	}
606 
607 	return len;
608 }
609 
getBufferUni(uint32 * buf,uint len)610 uint MemoryStream::getBufferUni(uint32 *buf, uint len) {
611 	if (!_readable)
612 		return 0;
613 
614 	if (_bufPtr >= _bufEnd) {
615 		len = 0;
616 	} else {
617 		if (!_unicode) {
618 			unsigned char *bp = (unsigned char *)_bufPtr;
619 			if (bp + len > (unsigned char *)_bufEnd) {
620 				uint lx;
621 				lx = (bp + len) - (unsigned char *)_bufEnd;
622 				if (lx < len)
623 					len -= lx;
624 				else
625 					len = 0;
626 			}
627 			if (len) {
628 				uint i;
629 				for (i = 0; i < len; i++)
630 					buf[i] = bp[i];
631 				bp += len;
632 				if (bp > (unsigned char *)_bufEof)
633 					_bufEof = bp;
634 			}
635 			_readCount += len;
636 			_bufPtr = bp;
637 		} else {
638 			uint32 *bp = (uint32 *)_bufPtr;
639 			if (bp + len > (uint32 *)_bufEnd) {
640 				uint lx;
641 				lx = (bp + len) - (uint32 *)_bufEnd;
642 				if (lx < len)
643 					len -= lx;
644 				else
645 					len = 0;
646 			}
647 			if (len) {
648 				memcpy(buf, bp, len * 4);
649 				bp += len;
650 				if (bp > (uint32 *)_bufEof)
651 					_bufEof = bp;
652 			}
653 			_readCount += len;
654 			_bufPtr = bp;
655 		}
656 	}
657 
658 	return len;
659 }
660 
getLine(char * buf,uint len)661 uint MemoryStream::getLine(char *buf, uint len) {
662 	uint lx;
663 	bool gotNewline;
664 
665 	if (len == 0)
666 		return 0;
667 
668 	len -= 1; // for the terminal null
669 	if (!_unicode) {
670 		if (_bufPtr >= _bufEnd) {
671 			len = 0;
672 		} else {
673 			if ((char *)_bufPtr + len > (char *)_bufEnd) {
674 				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
675 				if (lx < len)
676 					len -= lx;
677 				else
678 					len = 0;
679 			}
680 		}
681 
682 		gotNewline = false;
683 		for (lx = 0; lx < len && !gotNewline; lx++) {
684 			buf[lx] = ((char *)_bufPtr)[lx];
685 			gotNewline = (buf[lx] == '\n');
686 		}
687 
688 		buf[lx] = '\0';
689 		_bufPtr = ((char *)_bufPtr) + lx;
690 	} else {
691 		if (_bufPtr >= _bufEnd) {
692 			len = 0;
693 		} else {
694 			if ((char *)_bufPtr + len > (char *)_bufEnd) {
695 				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
696 				if (lx < len)
697 					len -= lx;
698 				else
699 					len = 0;
700 			}
701 		}
702 
703 		gotNewline = false;
704 		for (lx = 0; lx < len && !gotNewline; lx++) {
705 			uint32 ch;
706 			ch = ((uint32 *)_bufPtr)[lx];
707 			if (ch >= 0x100)
708 				ch = '?';
709 			buf[lx] = (char)ch;
710 			gotNewline = (ch == '\n');
711 		}
712 
713 		buf[lx] = '\0';
714 		_bufPtr = ((uint32 *)_bufPtr) + lx;
715 	}
716 
717 	_readCount += lx;
718 	return lx;
719 }
720 
getLineUni(uint32 * ubuf,uint len)721 uint MemoryStream::getLineUni(uint32 *ubuf, uint len) {
722 	bool gotNewline;
723 	int lx;
724 
725 	if (!_readable || len == 0)
726 		return 0;
727 
728 	len -= 1; // for the terminal null
729 	if (!_unicode) {
730 		if (_bufPtr >= _bufEnd) {
731 			len = 0;
732 		} else {
733 			if ((char *)_bufPtr + len > (char *)_bufEnd) {
734 				lx = ((char *)_bufPtr + len) - (char *)_bufEnd;
735 				if (lx < (int)len)
736 					len -= lx;
737 				else
738 					len = 0;
739 			}
740 		}
741 		gotNewline = false;
742 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
743 			ubuf[lx] = ((unsigned char *)_bufPtr)[lx];
744 			gotNewline = (ubuf[lx] == '\n');
745 		}
746 		ubuf[lx] = '\0';
747 		_bufPtr = ((unsigned char *)_bufPtr) + lx;
748 	} else {
749 		if (_bufPtr >= _bufEnd) {
750 			len = 0;
751 		} else {
752 			if ((uint32 *)_bufPtr + len > (uint32 *)_bufEnd) {
753 				lx = ((uint32 *)_bufPtr + len) - (uint32 *)_bufEnd;
754 				if (lx < (int)len)
755 					len -= lx;
756 				else
757 					len = 0;
758 			}
759 		}
760 		gotNewline = false;
761 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
762 			uint32 ch;
763 			ch = ((uint32 *)_bufPtr)[lx];
764 			ubuf[lx] = ch;
765 			gotNewline = (ch == '\n');
766 		}
767 		ubuf[lx] = '\0';
768 		_bufPtr = ((uint32 *)_bufPtr) + lx;
769 	}
770 
771 	_readCount += lx;
772 	return lx;
773 }
774 
775 /*--------------------------------------------------------------------------*/
776 
ensureOp(FileMode mode)777 void IOStream::ensureOp(FileMode mode) {
778 	// No implementation
779 }
780 
putChar(unsigned char ch)781 void IOStream::putChar(unsigned char ch) {
782 	if (!_writable)
783 		return;
784 	++_writeCount;
785 
786 	ensureOp(filemode_Write);
787 	if (!_unicode) {
788 		_outStream->writeByte(ch);
789 	} else if (_textFile) {
790 		putCharUtf8((uint)ch);
791 	} else {
792 		_outStream->writeUint32BE(ch);
793 	}
794 
795 	_outStream->flush();
796 }
797 
putCharUni(uint32 ch)798 void IOStream::putCharUni(uint32 ch) {
799 	if (!_writable)
800 		return;
801 	++_writeCount;
802 
803 	ensureOp(filemode_Write);
804 	if (!_unicode) {
805 		if (ch >= 0x100)
806 			ch = '?';
807 		_outStream->writeByte(ch);
808 	} else if (_textFile) {
809 		putCharUtf8(ch);
810 	} else {
811 		_outStream->writeUint32BE(ch);
812 	}
813 
814 	_outStream->flush();
815 }
816 
putBuffer(const char * buf,size_t len)817 void IOStream::putBuffer(const char *buf, size_t len) {
818 	if (!_writable)
819 		return;
820 	_writeCount += len;
821 
822 	ensureOp(filemode_Write);
823 	for (size_t lx = 0; lx < len; lx++) {
824 		unsigned char ch = ((const unsigned char *)buf)[lx];
825 		if (!_unicode) {
826 			_outStream->writeByte(ch);
827 		} else if (_textFile) {
828 			putCharUtf8((uint)ch);
829 		} else {
830 			_outStream->writeUint32BE(ch);
831 		}
832 	}
833 
834 	_outStream->flush();
835 }
836 
putBufferUni(const uint32 * buf,size_t len)837 void IOStream::putBufferUni(const uint32 *buf, size_t len) {
838 	if (!_writable)
839 		return;
840 	_writeCount += len;
841 
842 
843 	ensureOp(filemode_Write);
844 	for (size_t lx = 0; lx < len; lx++) {
845 		uint32 ch = buf[lx];
846 		if (!_unicode) {
847 			if (ch >= 0x100)
848 				ch = '?';
849 			_outStream->writeByte(ch);
850 		} else if (_textFile) {
851 			putCharUtf8(ch);
852 		} else {
853 			_outStream->writeUint32BE(ch);
854 		}
855 	}
856 
857 	_outStream->flush();
858 }
859 
putCharUtf8(uint val)860 void IOStream::putCharUtf8(uint val) {
861 	if (val < 0x80) {
862 		_outStream->writeByte(val);
863 	} else if (val < 0x800) {
864 		_outStream->writeByte((0xC0 | ((val & 0x7C0) >> 6)));
865 		_outStream->writeByte((0x80 | (val & 0x03F)));
866 	} else if (val < 0x10000) {
867 		_outStream->writeByte((0xE0 | ((val & 0xF000) >> 12)));
868 		_outStream->writeByte((0x80 | ((val & 0x0FC0) >> 6)));
869 		_outStream->writeByte((0x80 | (val & 0x003F)));
870 	} else if (val < 0x200000) {
871 		_outStream->writeByte((0xF0 | ((val & 0x1C0000) >> 18)));
872 		_outStream->writeByte((0x80 | ((val & 0x03F000) >> 12)));
873 		_outStream->writeByte((0x80 | ((val & 0x000FC0) >> 6)));
874 		_outStream->writeByte((0x80 | (val & 0x00003F)));
875 	} else {
876 		_outStream->writeByte('?');
877 	}
878 }
879 
getCharUtf8()880 int IOStream::getCharUtf8() {
881 	uint res;
882 	uint val0, val1, val2, val3;
883 
884 	if (_inStream->eos())
885 		return -1;
886 	val0 = _inStream->readByte();
887 	if (val0 < 0x80) {
888 		res = val0;
889 		return res;
890 	}
891 
892 	if ((val0 & 0xe0) == 0xc0) {
893 		if (_inStream->eos()) {
894 			warning("incomplete two-byte character");
895 			return -1;
896 		}
897 
898 		val1 = _inStream->readByte();
899 		if ((val1 & 0xc0) != 0x80) {
900 			warning("malformed two-byte character");
901 			return '?';
902 		}
903 
904 		res = (val0 & 0x1f) << 6;
905 		res |= (val1 & 0x3f);
906 		return res;
907 	}
908 
909 	if ((val0 & 0xf0) == 0xe0) {
910 		val1 = _inStream->readByte();
911 		val2 = _inStream->readByte();
912 		if (_inStream->eos()) {
913 			warning("incomplete three-byte character");
914 			return -1;
915 		}
916 		if ((val1 & 0xc0) != 0x80) {
917 			warning("malformed three-byte character");
918 			return '?';
919 		}
920 		if ((val2 & 0xc0) != 0x80) {
921 			warning("malformed three-byte character");
922 			return '?';
923 		}
924 
925 		res = (((val0 & 0xf) << 12) & 0x0000f000);
926 		res |= (((val1 & 0x3f) << 6) & 0x00000fc0);
927 		res |= (((val2 & 0x3f)) & 0x0000003f);
928 		return res;
929 	}
930 
931 	if ((val0 & 0xf0) == 0xf0) {
932 		if ((val0 & 0xf8) != 0xf0) {
933 			warning("malformed four-byte character");
934 			return '?';
935 		}
936 
937 		val1 = _inStream->readByte();
938 		val2 = _inStream->readByte();
939 		val3 = _inStream->readByte();
940 		if (_inStream->eos()) {
941 			warning("incomplete four-byte character");
942 			return -1;
943 		}
944 		if ((val1 & 0xc0) != 0x80) {
945 			warning("malformed four-byte character");
946 			return '?';
947 		}
948 		if ((val2 & 0xc0) != 0x80) {
949 			warning("malformed four-byte character");
950 			return '?';
951 		}
952 		if ((val3 & 0xc0) != 0x80) {
953 			warning("malformed four-byte character");
954 			return '?';
955 		}
956 
957 		res = (((val0 & 0x7) << 18) & 0x1c0000);
958 		res |= (((val1 & 0x3f) << 12) & 0x03f000);
959 		res |= (((val2 & 0x3f) << 6) & 0x000fc0);
960 		res |= (((val3 & 0x3f)) & 0x00003f);
961 		return res;
962 	}
963 
964 	warning("malformed character");
965 	return '?';
966 }
967 
getPosition() const968 uint IOStream::getPosition() const {
969 	return _outStream ? _outStream->pos() : _inStream->pos();
970 }
971 
setPosition(int pos,uint seekMode)972 void IOStream::setPosition(int pos, uint seekMode) {
973 	_lastOp = 0;
974 	if (_unicode)
975 		pos *= 4;
976 
977 	if (_inStream) {
978 		_inStream->seek(pos, SEEK_SET);
979 	} else {
980 		Common::SeekableWriteStream *ws =
981 			dynamic_cast<Common::SeekableWriteStream *>(_outStream);
982 
983 		if (ws)
984 			ws->seek(pos, SEEK_SET);
985 		else
986 			error("seek not supported for writing files");
987 	}
988 }
989 
getChar()990 int IOStream::getChar() {
991 	if (!_readable)
992 		return -1;
993 
994 	ensureOp(filemode_Read);
995 	int res;
996 	if (!_unicode) {
997 		res = _inStream->eos() ? -1 : _inStream->readByte();
998 	} else if (_textFile) {
999 		res = getCharUtf8();
1000 	} else {
1001 		uint32 ch;
1002 		res = _inStream->readByte();
1003 		if (_inStream->eos())
1004 			return -1;
1005 		ch = (res & 0xFF);
1006 		res = _inStream->readByte();
1007 		if (_inStream->eos())
1008 			return -1;
1009 		ch = (ch << 8) | (res & 0xFF);
1010 		res = _inStream->readByte();
1011 		if (_inStream->eos())
1012 			return -1;
1013 		ch = (ch << 8) | (res & 0xFF);
1014 		res = _inStream->readByte();
1015 		if (_inStream->eos())
1016 			return -1;
1017 		ch = (ch << 8) | (res & 0xFF);
1018 		res = ch;
1019 	}
1020 	if (res != -1) {
1021 		_readCount++;
1022 		if (res >= 0x100)
1023 			return '?';
1024 		return (int)res;
1025 	} else {
1026 		return -1;
1027 	}
1028 }
1029 
getCharUni()1030 int IOStream::getCharUni() {
1031 	if (!_readable)
1032 		return -1;
1033 
1034 	ensureOp(filemode_Read);
1035 	int res;
1036 	if (!_unicode) {
1037 		res = _inStream->readByte();
1038 	} else if (_textFile) {
1039 		res = getCharUtf8();
1040 	} else {
1041 		uint32 ch;
1042 		res = _inStream->readByte();
1043 		if (res == -1)
1044 			return -1;
1045 		ch = (res & 0xFF);
1046 		res = _inStream->readByte();
1047 		if (res == -1)
1048 			return -1;
1049 		ch = (ch << 8) | (res & 0xFF);
1050 		res = _inStream->readByte();
1051 		if (res == -1)
1052 			return -1;
1053 		ch = (ch << 8) | (res & 0xFF);
1054 		res = _inStream->readByte();
1055 		if (res == -1)
1056 			return -1;
1057 		ch = (ch << 8) | (res & 0xFF);
1058 		res = ch;
1059 	}
1060 	if (res != -1) {
1061 		_readCount++;
1062 		return (int)res;
1063 	} else {
1064 		return -1;
1065 	}
1066 }
1067 
getBuffer(char * buf,uint len)1068 uint IOStream::getBuffer(char *buf, uint len) {
1069 	ensureOp(filemode_Read);
1070 	if (!_unicode) {
1071 		uint res;
1072 		res = _inStream->read(buf, len);
1073 		_readCount += res;
1074 		return res;
1075 	} else if (_textFile) {
1076 		uint lx;
1077 		for (lx = 0; lx < len; lx++) {
1078 			uint32 ch;
1079 			ch = getCharUtf8();
1080 			if (ch == (uint)-1)
1081 				break;
1082 			_readCount++;
1083 			if (ch >= 0x100)
1084 				ch = '?';
1085 			buf[lx] = (char)ch;
1086 		}
1087 		return lx;
1088 	} else {
1089 		uint lx;
1090 		for (lx = 0; lx < len; lx++) {
1091 			int res;
1092 			uint32 ch;
1093 			res = _inStream->readByte();
1094 			if (res == -1)
1095 				break;
1096 			ch = (res & 0xFF);
1097 			res = _inStream->readByte();
1098 			if (res == -1)
1099 				break;
1100 			ch = (ch << 8) | (res & 0xFF);
1101 			res = _inStream->readByte();
1102 			if (res == -1)
1103 				break;
1104 			ch = (ch << 8) | (res & 0xFF);
1105 			res = _inStream->readByte();
1106 			if (res == -1)
1107 				break;
1108 			ch = (ch << 8) | (res & 0xFF);
1109 			_readCount++;
1110 			if (ch >= 0x100)
1111 				ch = '?';
1112 			buf[lx] = (char)ch;
1113 		}
1114 		return lx;
1115 	}
1116 }
1117 
getBufferUni(uint32 * buf,uint len)1118 uint IOStream::getBufferUni(uint32 *buf, uint len) {
1119 	if (!_readable)
1120 		return 0;
1121 
1122 	ensureOp(filemode_Read);
1123 	if (!_unicode) {
1124 		uint lx;
1125 		for (lx = 0; lx < len; lx++) {
1126 			int res;
1127 			uint32 ch;
1128 			res = _inStream->readByte();
1129 			if (res == -1)
1130 				break;
1131 			ch = (res & 0xFF);
1132 			_readCount++;
1133 			buf[lx] = ch;
1134 		}
1135 		return lx;
1136 	} else if (_textFile) {
1137 		uint lx;
1138 		for (lx = 0; lx < len; lx++) {
1139 			uint32 ch;
1140 			ch = getCharUtf8();
1141 			if (ch == (uint)-1)
1142 				break;
1143 			_readCount++;
1144 			buf[lx] = ch;
1145 		}
1146 		return lx;
1147 	} else {
1148 		uint lx;
1149 		for (lx = 0; lx < len; lx++) {
1150 			int res;
1151 			uint32 ch;
1152 			res = _inStream->readByte();
1153 			if (res == -1)
1154 				break;
1155 			ch = (res & 0xFF);
1156 			res = _inStream->readByte();
1157 			if (res == -1)
1158 				break;
1159 			ch = (ch << 8) | (res & 0xFF);
1160 			res = _inStream->readByte();
1161 			if (res == -1)
1162 				break;
1163 			ch = (ch << 8) | (res & 0xFF);
1164 			res = _inStream->readByte();
1165 			if (res == -1)
1166 				break;
1167 			ch = (ch << 8) | (res & 0xFF);
1168 			_readCount++;
1169 			buf[lx] = ch;
1170 		}
1171 		return lx;
1172 	}
1173 }
1174 
getLine(char * buf,uint len)1175 uint IOStream::getLine(char *buf, uint len) {
1176 	uint lx;
1177 	bool gotNewline;
1178 
1179 	if (len == 0)
1180 		return 0;
1181 
1182 	ensureOp(filemode_Read);
1183 	if (!_unicode) {
1184 		char *res = buf;
1185 		for (; len > 0; ++res, --len) {
1186 			*res = _inStream->readByte();
1187 			if (*res == '\n')
1188 				break;
1189 		}
1190 		*res = '\0';
1191 
1192 		lx = strlen(buf);
1193 		_readCount += lx;
1194 		return lx;
1195 	} else if (_textFile) {
1196 		len -= 1; // for the terminal null
1197 		gotNewline = false;
1198 		for (lx = 0; lx < len && !gotNewline; lx++) {
1199 			uint32 ch;
1200 			ch = getCharUtf8();
1201 			if (ch == (uint)-1)
1202 				break;
1203 			_readCount++;
1204 			if (ch >= 0x100)
1205 				ch = '?';
1206 			buf[lx] = (char)ch;
1207 			gotNewline = (ch == '\n');
1208 		}
1209 		buf[lx] = '\0';
1210 		return lx;
1211 	} else {
1212 		len -= 1; // for the terminal null
1213 		gotNewline = false;
1214 		for (lx = 0; lx < len && !gotNewline; lx++) {
1215 			int res;
1216 			uint32 ch;
1217 			res = _inStream->readByte();
1218 			if (res == -1)
1219 				break;
1220 			ch = (res & 0xFF);
1221 			res = _inStream->readByte();
1222 			if (res == -1)
1223 				break;
1224 			ch = (ch << 8) | (res & 0xFF);
1225 			res = _inStream->readByte();
1226 			if (res == -1)
1227 				break;
1228 			ch = (ch << 8) | (res & 0xFF);
1229 			res = _inStream->readByte();
1230 			if (res == -1)
1231 				break;
1232 			ch = (ch << 8) | (res & 0xFF);
1233 			_readCount++;
1234 			if (ch >= 0x100)
1235 				ch = '?';
1236 			buf[lx] = (char)ch;
1237 			gotNewline = (ch == '\n');
1238 		}
1239 
1240 		buf[lx] = '\0';
1241 		return lx;
1242 	}
1243 }
1244 
getLineUni(uint32 * ubuf,uint len)1245 uint IOStream::getLineUni(uint32 *ubuf, uint len) {
1246 	bool gotNewline;
1247 	int lx;
1248 
1249 	if (!_readable || len == 0)
1250 		return 0;
1251 
1252 	ensureOp(filemode_Read);
1253 	if (!_unicode) {
1254 		len -= 1; // for the terminal null
1255 		gotNewline = false;
1256 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
1257 			int res;
1258 			uint32 ch;
1259 			res = _inStream->readByte();
1260 			if (res == -1)
1261 				break;
1262 			ch = (res & 0xFF);
1263 			_readCount++;
1264 			ubuf[lx] = ch;
1265 			gotNewline = (ch == '\n');
1266 		}
1267 		ubuf[lx] = '\0';
1268 		return lx;
1269 	} else if (_textFile) {
1270 		len -= 1; // for the terminal null
1271 		gotNewline = false;
1272 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
1273 			uint32 ch;
1274 			ch = getCharUtf8();
1275 			if (ch == (uint)-1)
1276 				break;
1277 			_readCount++;
1278 			ubuf[lx] = ch;
1279 			gotNewline = (ch == '\n');
1280 		}
1281 		ubuf[lx] = '\0';
1282 		return lx;
1283 	} else {
1284 		len -= 1; // for the terminal null
1285 		gotNewline = false;
1286 		for (lx = 0; lx < (int)len && !gotNewline; lx++) {
1287 			int res;
1288 			uint32 ch;
1289 			res = _inStream->readByte();
1290 			if (res == -1)
1291 				break;
1292 			ch = (res & 0xFF);
1293 			res = _inStream->readByte();
1294 			if (res == -1)
1295 				break;
1296 			ch = (ch << 8) | (res & 0xFF);
1297 			res = _inStream->readByte();
1298 			if (res == -1)
1299 				break;
1300 			ch = (ch << 8) | (res & 0xFF);
1301 			res = _inStream->readByte();
1302 			if (res == -1)
1303 				break;
1304 			ch = (ch << 8) | (res & 0xFF);
1305 			_readCount++;
1306 			ubuf[lx] = ch;
1307 			gotNewline = (ch == '\n');
1308 		}
1309 		ubuf[lx] = '\0';
1310 		return lx;
1311 	}
1312 }
1313 
1314 /*--------------------------------------------------------------------------*/
1315 
FileStream(Streams * streams,frefid_t fref,uint fmode,uint rock,bool unicode)1316 FileStream::FileStream(Streams *streams, frefid_t fref, uint fmode, uint rock, bool unicode) :
1317 		IOStream(streams, fmode == filemode_Read, fmode != filemode_Read, rock, unicode),
1318 		_inSave(nullptr), _outSave(nullptr) {
1319 
1320 	_textFile = fref->_textMode;
1321 	Common::String fname = fref->_slotNumber == -1 ? fref->_filename : fref->getSaveName();
1322 
1323 	if (fmode == filemode_Write || fmode == filemode_ReadWrite || fmode == filemode_WriteAppend) {
1324 		_outSave = g_system->getSavefileManager()->openForSaving(fname, false);
1325 		if (!_outSave)
1326 			error("Could open file for writing - %s", fname.c_str());
1327 		setStream(_outSave);
1328 
1329 	} else if (fmode == filemode_Read) {
1330 		if (_file.open(fname)) {
1331 			setStream(&_file);
1332 		} else {
1333 			_inSave = g_system->getSavefileManager()->openForLoading(fname);
1334 			setStream(_inSave);
1335 
1336 			if (!_inSave)
1337 				error("Could not open for reading - %s", fname.c_str());
1338 		}
1339 	}
1340 }
1341 
~FileStream()1342 FileStream::~FileStream() {
1343 	_file.close();
1344 	delete _inSave;
1345 	if (_outSave) {
1346 		_outSave->finalize();
1347 		delete _outSave;
1348 	}
1349 }
1350 
1351 /*--------------------------------------------------------------------------*/
1352 
Streams()1353 Streams::Streams() : _streamList(nullptr), _currentStream(nullptr) {
1354 }
1355 
~Streams()1356 Streams::~Streams() {
1357 	for (Stream *currStream = _streamList, *nextStream; currStream; currStream = nextStream) {
1358 		nextStream = currStream->_next;
1359 		delete currStream;
1360 	}
1361 }
1362 
openFileStream(frefid_t fref,uint fmode,uint rock,bool unicode)1363 FileStream *Streams::openFileStream(frefid_t fref, uint fmode, uint rock, bool unicode) {
1364 	FileStream *stream = new FileStream(this, fref, fmode, rock, unicode);
1365 	addStream(stream);
1366 	return stream;
1367 }
1368 
openStream(Common::SeekableReadStream * rs,uint rock)1369 IOStream *Streams::openStream(Common::SeekableReadStream *rs, uint rock) {
1370 	IOStream *stream = new IOStream(this, rs, rock);
1371 	addStream(stream);
1372 	return stream;
1373 }
1374 
openStream(Common::WriteStream * ws,uint rock)1375 IOStream *Streams::openStream(Common::WriteStream *ws, uint rock) {
1376 	IOStream *stream = new IOStream(this, ws, rock);
1377 	addStream(stream);
1378 	return stream;
1379 }
1380 
openWindowStream(Window * window)1381 WindowStream *Streams::openWindowStream(Window *window) {
1382 	WindowStream *stream = new WindowStream(this, window);
1383 	addStream(stream);
1384 	return stream;
1385 }
1386 
openMemoryStream(void * buf,size_t buflen,FileMode mode,uint rock,bool unicode)1387 MemoryStream *Streams::openMemoryStream(void *buf, size_t buflen, FileMode mode, uint rock, bool unicode) {
1388 	MemoryStream *stream = new MemoryStream(this, buf, buflen, mode, rock, unicode);
1389 	addStream(stream);
1390 	return stream;
1391 }
1392 
addStream(Stream * stream)1393 void Streams::addStream(Stream *stream) {
1394 	stream->_next = _streamList;
1395 	_streamList = stream;
1396 	if (stream->_next)
1397 		stream->_next->_prev = stream;
1398 }
1399 
removeStream(Stream * stream)1400 void Streams::removeStream(Stream *stream) {
1401 	Stream *prev = stream->_prev;
1402 	Stream *next = stream->_next;
1403 
1404 	if (prev)
1405 		prev->_next = next;
1406 	else
1407 		_streamList = next;
1408 	if (next)
1409 		next->_prev = prev;
1410 
1411 	// Remove the stream as the echo stream of any window
1412 	for (Windows::iterator i = g_vm->_windows->begin(); i != g_vm->_windows->end(); ++i) {
1413 		if ((*i)->_echoStream == stream)
1414 			(*i)->_echoStream = nullptr;
1415 	}
1416 
1417 	if (_currentStream == stream)
1418 		_currentStream = nullptr;
1419 }
1420 
getFirst(uint * rock)1421 Stream *Streams::getFirst(uint *rock) {
1422 	if (rock)
1423 		*rock = _streamList ? _streamList->_rock : 0;
1424 	return _streamList;
1425 }
1426 
1427 
createByPrompt(uint usage,FileMode fmode,uint rock)1428 frefid_t Streams::createByPrompt(uint usage, FileMode fmode, uint rock) {
1429 	switch (usage & fileusage_TypeMask) {
1430 	case fileusage_SavedGame: {
1431 		if (fmode == filemode_Write) {
1432 			// Select a savegame slot
1433 			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
1434 
1435 			int slot = dialog->runModalWithCurrentTarget();
1436 			if (slot >= 0) {
1437 				Common::String desc = dialog->getResultString();
1438 				return createRef(slot, desc, usage, rock);
1439 			}
1440 		} else if (fmode == filemode_Read) {
1441 			// Load a savegame slot
1442 			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
1443 
1444 			int slot = dialog->runModalWithCurrentTarget();
1445 			if (slot >= 0) {
1446 				return createRef(slot, "", usage, rock);
1447 			}
1448 		} else {
1449 			error("Unsupport file mode");
1450 		}
1451 		break;
1452 	}
1453 
1454 	case fileusage_Transcript:
1455 		return createRef("transcript.txt", fmode, rock);
1456 
1457 	default:
1458 		error("Unsupport file mode");
1459 		break;
1460 	}
1461 
1462 	return nullptr;
1463 }
1464 
createRef(int slot,const Common::String & desc,uint usage,uint rock)1465 frefid_t Streams::createRef(int slot, const Common::String &desc, uint usage, uint rock) {
1466 	frefid_t fref = new FileReference();
1467 	fref->_slotNumber = slot;
1468 	fref->_description = desc;
1469 	fref->_textMode = ((usage & fileusage_TextMode) != 0);
1470 	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
1471 
1472 	_fileReferences.push_back(FileRefArray::value_type(fref));
1473 	return fref;
1474 }
1475 
createRef(const Common::String & filename,uint usage,uint rock)1476 frefid_t Streams::createRef(const Common::String &filename, uint usage, uint rock) {
1477 	frefid_t fref = new FileReference();
1478 	fref->_filename = filename;
1479 	fref->_textMode = ((usage & fileusage_TextMode) != 0);
1480 	fref->_fileType = (FileUsage)(usage & fileusage_TypeMask);
1481 
1482 	_fileReferences.push_back(FileRefArray::value_type(fref));
1483 	return fref;
1484 }
1485 
createTemp(uint usage,uint rock)1486 frefid_t Streams::createTemp(uint usage, uint rock) {
1487 	return createRef(Common::String::format("%s.tmp", g_vm->getTargetName().c_str()),
1488 	                 usage, rock);
1489 }
1490 
createFromRef(frefid_t fref,uint usage,uint rock)1491 frefid_t Streams::createFromRef(frefid_t fref, uint usage, uint rock) {
1492 	return createRef(fref->_filename, usage, rock);
1493 }
1494 
deleteRef(frefid_t fref)1495 void Streams::deleteRef(frefid_t fref) {
1496 	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
1497 		if (_fileReferences[idx].get() == fref) {
1498 			_fileReferences.remove_at(idx);
1499 			return;
1500 		}
1501 	}
1502 }
1503 
iterate(frefid_t fref,uint * rock)1504 frefid_t Streams::iterate(frefid_t fref, uint *rock) {
1505 	// Find reference following the specified one
1506 	int index = -1;
1507 	for (uint idx = 0; idx < _fileReferences.size(); ++idx) {
1508 		if (fref == nullptr || _fileReferences[idx].get() == fref) {
1509 			if (idx < (_fileReferences.size() - 1))
1510 				index = idx + 1;
1511 			break;
1512 		}
1513 	}
1514 
1515 	if (index != -1) {
1516 		if (rock)
1517 			*rock = _fileReferences[index].get()->_rock;
1518 		return _fileReferences[index].get();
1519 	}
1520 
1521 	if (rock)
1522 		*rock = 0;
1523 	return nullptr;
1524 }
1525 
1526 /*--------------------------------------------------------------------------*/
1527 
FileReference()1528 FileReference::FileReference() : _rock(0), _slotNumber(-1), _fileType(fileusage_Data), _textMode(false) {
1529 	if (g_vm->gli_register_obj)
1530 		_dispRock = (*g_vm->gli_register_obj)(this, gidisp_Class_Fileref);
1531 }
1532 
FileReference(int slot,const Common::String & desc,uint usage,uint rock)1533 FileReference::FileReference(int slot, const Common::String &desc, uint usage, uint rock) :
1534 		_rock(rock), _slotNumber(slot), _description(desc),
1535 		_fileType((FileUsage)(usage & fileusage_TypeMask)), _textMode(usage & fileusage_TextMode) {
1536 	if (g_vm->gli_register_obj)
1537 		_dispRock = (*g_vm->gli_register_obj)(this, gidisp_Class_Fileref);
1538 }
1539 
~FileReference()1540 FileReference::~FileReference() {
1541 	if (g_vm->gli_unregister_obj)
1542 		(*g_vm->gli_unregister_obj)(this, gidisp_Class_Fileref, _dispRock);
1543 }
1544 
getSaveName() const1545 const Common::String FileReference::getSaveName() const {
1546 	assert(_slotNumber != -1);
1547 	return g_vm->getSaveName(_slotNumber);
1548 }
1549 
exists() const1550 bool FileReference::exists() const {
1551 	Common::String filename;
1552 
1553 	if (_slotNumber == -1) {
1554 		if (Common::File::exists(_filename))
1555 			return true;
1556 		filename = _filename;
1557 	} else {
1558 		filename = getSaveName();
1559 	}
1560 
1561 	// Check for a savegame (or other file in the save folder) with that name
1562 	Common::InSaveFile *inSave = g_system->getSavefileManager()->openForLoading(filename);
1563 	bool result = inSave != nullptr;
1564 	delete inSave;
1565 	return result;
1566 }
1567 
deleteFile()1568 void FileReference::deleteFile() {
1569 	Common::String filename = (_slotNumber == -1) ? _filename : getSaveName();
1570 	g_system->getSavefileManager()->removeSavefile(filename);
1571 }
1572 
1573 } // End of namespace Glk
1574