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