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/zcode/processor.h"
24 #include "glk/zcode/quetzal.h"
25 
26 namespace Glk {
27 namespace ZCode {
28 
console_read_input(int max,zchar * buf,zword timeout,bool continued)29 zchar Processor::console_read_input(int max, zchar *buf, zword timeout, bool continued) {
30 	return os_read_line(max, buf, timeout, max, continued);
31 }
32 
console_read_key(zword timeout)33 zchar Processor::console_read_key(zword timeout) {
34 	return os_read_key(timeout, 0);
35 }
36 
scrollback_char(zchar c)37 void Processor::scrollback_char(zchar c) {
38 	if (c == ZC_INDENT)
39 		{ scrollback_char (' '); scrollback_char (' '); scrollback_char (' '); return; }
40 	if (c == ZC_GAP)
41 		{ scrollback_char (' '); scrollback_char (' '); return; }
42 
43 	os_scrollback_char(c);
44 }
45 
scrollback_word(const zchar * s)46 void Processor::scrollback_word(const zchar *s) {
47 	int i;
48 
49 	for (i = 0; s[i] != 0; i++) {
50 		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
51 			i++;
52 		else
53 			scrollback_char(s[i]);
54 	}
55 }
56 
scrollback_write_input(const zchar * buf,zchar key)57 void Processor::scrollback_write_input(const zchar *buf, zchar key) {
58 	int i;
59 
60 	for (i = 0; buf[i] != 0; i++)
61 		scrollback_char (buf[i]);
62 
63 	if (key == ZC_RETURN)
64 		scrollback_char ('\n');
65 }
66 
scrollback_erase_input(const zchar * buf)67 void Processor::scrollback_erase_input(const zchar *buf) {
68 	int width;
69 	int i;
70 
71 	for (i = 0, width = 0; buf[i] != 0; i++)
72 		width++;
73 
74 	os_scrollback_erase(width);
75 
76 }
77 
stream_mssg_on()78 void Processor::stream_mssg_on() {
79 	flush_buffer();
80 
81 	if (ostream_screen)
82 		screen_mssg_on();
83 	if (ostream_script && enable_scripting)
84 	script_mssg_on();
85 
86 	message = true;
87 }
88 
stream_mssg_off()89 void Processor::stream_mssg_off() {
90 	flush_buffer();
91 
92 	if (ostream_screen)
93 		screen_mssg_off();
94 	if (ostream_script && enable_scripting)
95 		script_mssg_off();
96 
97 	message = false;
98 }
99 
stream_char(zchar c)100 void Processor::stream_char(zchar c) {
101 	if (ostream_screen)
102 		screen_char(c);
103 	if (ostream_script && enable_scripting)
104 		script_char(c);
105 	if (enable_scripting)
106 		scrollback_char(c);
107 }
108 
stream_word(const zchar * s)109 void Processor::stream_word(const zchar *s) {
110 	if (ostream_memory && !message)
111 		memory_word(s);
112 	else {
113 		if (ostream_screen)
114 			screen_word(s);
115 		if (ostream_script && enable_scripting)
116 			script_word(s);
117 		if (enable_scripting)
118 			scrollback_word(s);
119 	}
120 }
121 
stream_new_line()122 void Processor::stream_new_line() {
123 	if (ostream_memory && !message)
124 		memory_new_line();
125 	else {
126 		if (ostream_screen)
127 			screen_new_line();
128 		if (ostream_script && enable_scripting)
129 			script_new_line();
130 		if (enable_scripting)
131 			os_scrollback_char ('\n');
132 	}
133 }
134 
stream_read_key(zword timeout,zword routine,bool hot_keys)135 zchar Processor::stream_read_key(zword timeout, zword routine, bool hot_keys) {
136 	zchar key = ZC_BAD;
137 
138 	flush_buffer();
139 
140 	// Read key from current input stream
141 continue_input:
142 
143 	do {
144 		if (istream_replay)
145 			key = replay_read_key();
146 		else
147 			key = console_read_key(timeout);
148 		if (shouldQuit())
149 			return ZC_BAD;
150 	} while (key == ZC_BAD);
151 
152 	// Copy key to the command file
153 	if (ostream_record && !istream_replay)
154 		record_write_key(key);
155 
156 	// Handle timeouts
157 	if (key == ZC_TIME_OUT)
158 		if (direct_call(routine) == 0)
159 			goto continue_input;
160 
161 	// Return key
162 	return key;
163 }
164 
stream_read_input(int max,zchar * buf,zword timeout,zword routine,bool hot_keys,bool no_scripting)165 zchar Processor::stream_read_input(int max, zchar *buf, zword timeout, zword routine,
166 			  bool hot_keys, bool no_scripting) {
167 	zchar key = ZC_BAD;
168 	flush_buffer();
169 
170 	// Remove initial input from the transscript file or from the screen
171 	if (ostream_script && enable_scripting && !no_scripting)
172 		script_erase_input(buf);
173 
174 	// Read input line from current input stream
175 continue_input:
176 
177 	do {
178 		if (istream_replay)
179 			key = replay_read_input(buf);
180 		else
181 			key = console_read_input(max, buf, timeout, key != ZC_BAD);
182 		if (shouldQuit())
183 			return ZC_BAD;
184 	} while (key == ZC_BAD);
185 
186 	// Copy input line to the command file
187 	if (ostream_record && !istream_replay)
188 		record_write_input(buf, key);
189 
190 	// Handle timeouts
191 	if (key == ZC_TIME_OUT)
192 		if (direct_call(routine) == 0)
193 			goto continue_input;
194 
195 	// Copy input line to transscript file or to the screen
196 	if (ostream_script && enable_scripting && !no_scripting)
197 		script_write_input(buf, key);
198 
199 	// Return terminating key
200 	return key;
201 }
202 
script_open()203 void Processor::script_open() {
204 	h_flags &= ~SCRIPTING_FLAG;
205 
206 	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript,
207 		filemode_WriteAppend);
208 	sfp = glk_stream_open_file(fref, filemode_WriteAppend);
209 
210 	if (sfp != nullptr) {
211 		sfp->setPosition(0, seekmode_End);
212 
213 		h_flags |= SCRIPTING_FLAG;
214 
215 		script_valid = true;
216 		ostream_script = true;
217 
218 		script_width = 0;
219 	} else {
220 		print_string("Cannot open file\n");
221 	}
222 
223 	SET_WORD(H_FLAGS, h_flags);
224 }
225 
script_close()226 void Processor::script_close() {
227 	h_flags &= ~SCRIPTING_FLAG;
228 	SET_WORD(H_FLAGS, h_flags);
229 
230 	glk_stream_close(sfp);
231 	ostream_script = false;
232 }
233 
script_new_line()234 void Processor::script_new_line() {
235 	script_char('\n');
236 	script_width = 0;
237 }
238 
script_char(zchar c)239 void Processor::script_char(zchar c) {
240 	if (c == ZC_INDENT && script_width != 0)
241 		c = ' ';
242 
243 	if (c == ZC_INDENT) {
244 		script_char(' ');
245 		script_char(' ');
246 		script_char(' ');
247 		return;
248 	}
249 	if (c == ZC_GAP) {
250 		script_char(' ');
251 		script_char(' ');
252 		return;
253 	}
254 
255 	sfp->putCharUni(c);
256 	script_width++;
257 }
258 
script_word(const zchar * s)259 void Processor::script_word(const zchar *s) {
260 	int width;
261 	int i;
262 
263 	if (*s == ZC_INDENT && script_width != 0)
264 		script_char(*s++);
265 
266 	for (i = 0, width = 0; s[i] != 0; i++) {
267 		if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT)
268 			i++;
269 		else if (s[i] == ZC_GAP)
270 			width += 3;
271 		else if (s[i] == ZC_INDENT)
272 			width += 2;
273 		else
274 			width += 1;
275 	}
276 
277 	if (_script_cols != 0 && script_width + width > _script_cols) {
278 		if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP)
279 			s++;
280 
281 		script_new_line();
282 	}
283 
284 	for (i = 0; s[i] != 0; i++) {
285 		if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE)
286 			i++;
287 		else
288 			script_char(s[i]);
289 	}
290 }
291 
script_write_input(const zchar * buf,zchar key)292 void Processor::script_write_input(const zchar *buf, zchar key) {
293 	int width;
294 	int i;
295 
296 	for (i = 0, width = 0; buf[i] != 0; i++)
297 		width++;
298 
299 	if (_script_cols != 0 && script_width + width > _script_cols)
300 		script_new_line();
301 
302 	for (i = 0; buf[i] != 0; i++)
303 		script_char(buf[i]);
304 
305 	if (key == ZC_RETURN)
306 		script_new_line();
307 }
308 
script_erase_input(const zchar * buf)309 void Processor::script_erase_input(const zchar *buf) {
310 	int width;
311 	int i;
312 
313 	for (i = 0, width = 0; buf[i] != 0; i++)
314 		width++;
315 
316 	sfp->setPosition(-width, seekmode_Current);
317 	script_width -= width;
318 }
319 
script_mssg_on()320 void Processor::script_mssg_on() {
321 	if (script_width != 0)
322 		script_new_line();
323 
324 	script_char(ZC_INDENT);
325 }
326 
script_mssg_off()327 void Processor::script_mssg_off() {
328 	script_new_line();
329 }
330 
record_open()331 void Processor::record_open() {
332 	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Write);
333 	if ((rfp = glk_stream_open_file(fref, filemode_Write)) != nullptr)
334 		ostream_record = true;
335 	else
336 		print_string("Cannot open file\n");
337 }
338 
record_close()339 void Processor::record_close() {
340 	glk_stream_close(rfp);
341 	ostream_record = false;
342 }
343 
record_code(int c,bool force_encoding)344 void Processor::record_code(int c, bool force_encoding) {
345 	if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) {
346 		int i;
347 
348 		rfp->putChar('[');
349 
350 		for (i = 10000; i != 0; i /= 10)
351 			if (c >= i || i == 1)
352 				rfp->putChar('0' + (c / i) % 10);
353 
354 		rfp->putChar(']');
355 	} else {
356 		rfp->putChar(c);
357 	}
358 }
359 
record_char(zchar c)360 void Processor::record_char(zchar c) {
361 	if (c != ZC_RETURN) {
362 		if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) {
363 			record_code(translate_to_zscii(c), false);
364 			if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
365 				record_code(mouse_x, true);
366 				record_code(mouse_y, true);
367 			}
368 		} else {
369 			record_code(1000 + c - ZC_HKEY_MIN, true);
370 		}
371 	}
372 }
373 
record_write_key(zchar key)374 void Processor::record_write_key(zchar key) {
375 	record_char(key);
376 	rfp->putChar('\n');
377 }
378 
record_write_input(const zchar * buf,zchar key)379 void Processor::record_write_input(const zchar *buf, zchar key) {
380 	zchar c;
381 
382 	while ((c = *buf++) != 0)
383 		record_char(c);
384 
385 	record_write_key(key);
386 }
387 
replay_open()388 void Processor::replay_open() {
389 	frefid_t fref = glk_fileref_create_by_prompt(fileusage_Transcript, filemode_Read);
390 	if ((pfp = glk_stream_open_file(fref, filemode_Read)) != nullptr)
391 		istream_replay = true;
392 	else
393 		print_string("Cannot open file\n");
394 }
395 
replay_close()396 void Processor::replay_close() {
397 	glk_stream_close(pfp);
398 	istream_replay = false;
399 }
400 
replay_code()401 int Processor::replay_code() {
402 	int c;
403 
404 	if ((c = pfp->getChar()) == '[') {
405 		int c2;
406 
407 		c = 0;
408 
409 		while ((c2 = pfp->getChar()) != EOF && c2 >= '0' && c2 <= '9')
410 			c = 10 * c + c2 - '0';
411 
412 		return (c2 == ']') ? c : EOF;
413 	} else {
414 		return c;
415 	}
416 }
417 
replay_char()418 zchar Processor::replay_char() {
419 	int c;
420 
421 	if ((c = replay_code()) != EOF) {
422 		if (c != '\n') {
423 			if (c < 1000) {
424 
425 				c = translate_from_zscii(c);
426 
427 				if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) {
428 					mouse_x = replay_code();
429 					mouse_y = replay_code();
430 				}
431 
432 				return c;
433 			} else {
434 				return ZC_HKEY_MIN + c - 1000;
435 			}
436 		}
437 
438 		pfp->unputBuffer("\n", 1);
439 		return ZC_RETURN;
440 
441 	} else {
442 		return ZC_BAD;
443 	}
444 }
445 
replay_read_key()446 zchar Processor::replay_read_key() {
447 	zchar key = replay_char();
448 
449 	if (pfp->getChar() != '\n') {
450 		replay_close();
451 		return ZC_BAD;
452 	} else {
453 		return key;
454 	}
455 }
456 
replay_read_input(zchar * buf)457 zchar Processor::replay_read_input(zchar *buf) {
458 	zchar c;
459 
460 	for (;;) {
461 		c = replay_char();
462 
463 		if (c == ZC_BAD || is_terminator(c))
464 			break;
465 
466 		*buf++ = c;
467 	}
468 
469 	*buf = 0;
470 
471 	if (pfp->getChar() != '\n') {
472 		replay_close();
473 		return ZC_BAD;
474 	} else {
475 		return c;
476 	}
477 }
478 
479 
z_input_stream()480 void Processor::z_input_stream() {
481 	flush_buffer();
482 
483 	if (zargs[0] == 0 && istream_replay)
484 		replay_close();
485 	if (zargs[0] == 1 && !istream_replay)
486 		replay_open();
487 }
488 
z_output_stream()489 void Processor::z_output_stream() {
490 	flush_buffer();
491 
492 	switch ((short) zargs[0]) {
493 	case 1:
494 		ostream_screen = true;
495 		break;
496 	case -1:
497 		ostream_screen = false;
498 		break;
499 	case  2:
500 		if (!ostream_script)
501 			script_open();
502 		break;
503 	case -2:
504 		if (ostream_script)
505 			script_close();
506 		break;
507 	case 3:
508 		memory_open(zargs[1], zargs[2], zargc >= 3);
509 		break;
510 	case -3:
511 		memory_close();
512 		break;
513 	case 4:
514 		if (!ostream_record)
515 			record_open();
516 		break;
517 	case -4:
518 		if (ostream_record)
519 			record_close();
520 		break;
521 	default:
522 		break;
523 	}
524 }
525 
z_restart()526 void Processor::z_restart() {
527 	flush_buffer();
528 
529 	os_restart_game(RESTART_BEGIN);
530 
531 	seed_random(0);
532 
533 	if (!first_restart) {
534 		story_fp->seek(0);
535 
536 		if (story_fp->read(zmp, h_dynamic_size) != h_dynamic_size)
537 			error("Story file read error");
538 
539 	} else {
540 		first_restart = false;
541 	}
542 
543 	restart_header();
544 	restart_screen();
545 
546 	_sp = _fp = _stack + STACK_SIZE;
547 	_frameCount = 0;
548 
549 	if (h_version != V6 && h_version != V9) {
550 		offset_t pc = (offset_t)h_start_pc;
551 		SET_PC(pc);
552 	} else {
553 		SET_PC(0);
554 		call(h_start_pc, 0, nullptr, 0);
555 	}
556 
557 	os_restart_game(RESTART_END);
558 }
559 
z_save()560 void Processor::z_save() {
561 	bool success = false;
562 
563 	if (zargc != 0) {
564 		// Open auxilary file
565 		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
566 			filemode_Write, 0);
567 		if (ref != nullptr) {
568 			// Write data
569 			strid_t f = glk_stream_open_file(ref, filemode_Write);
570 
571 			glk_put_buffer_stream(f, (const char *)zmp + zargs[0], zargs[1]);
572 
573 			glk_stream_close(f);
574 			success = true;
575 		}
576 	} else {
577 		success = saveGame().getCode() == Common::kNoError;
578 	}
579 
580 	if (h_version <= V3)
581 		branch(success);
582 	else
583 		store(success);
584 }
585 
z_restore()586 void Processor::z_restore() {
587 	bool success = false;
588 
589 	if (zargc != 0) {
590 		frefid_t ref = glk_fileref_create_by_prompt(fileusage_Data | fileusage_BinaryMode,
591 			filemode_Read, 0);
592 		if (ref != nullptr) {
593 			// Write data
594 			strid_t f = glk_stream_open_file(ref, filemode_Read);
595 
596 			glk_get_buffer_stream(f, (char *)zmp + zargs[0], zargs[1]);
597 
598 			glk_stream_close(f);
599 			success = true;
600 		}
601 	} else {
602 		success = loadGame().getCode() == Common::kNoError;
603 	}
604 
605 	int result = success ? 2 : -1;
606 	if (h_version <= V3)
607 		branch(result);
608 	else
609 		store(result);
610 }
611 
z_verify()612 void Processor::z_verify() {
613 	zword checksum = 0;
614 
615 	// Sum all bytes in story file except header bytes
616 	story_fp->seek(64);
617 
618 	for (uint i = 64; i < story_size; i++)
619 		checksum += story_fp->readByte();
620 
621 	// Branch if the checksums are equal
622 	branch(checksum == h_checksum);
623 }
624 
625 } // End of namespace ZCode
626 } // End of namespace Glk
627