diff options
author | Torne Wuff <torne@wolfpuppy.org.uk> | 2010-01-17 22:15:13 +0000 |
---|---|---|
committer | Torne Wuff <torne@wolfpuppy.org.uk> | 2010-01-17 22:15:13 +0000 |
commit | 7f28c94eda576e3f972fc05468188986f2e45885 (patch) | |
tree | e03b94613028d16855a5d3df0f4853e077931214 /apps/plugins/frotz/text.c | |
parent | 563f2602f471208cb8544a36539a79dcceaad643 (diff) | |
download | rockbox-7f28c94eda576e3f972fc05468188986f2e45885.tar.gz rockbox-7f28c94eda576e3f972fc05468188986f2e45885.zip |
New plugin: frotz, a Z-machine interpreter, for playing interactive fiction.
The interpreter more or less passes all the tests in the z-machine test suite.
It should build for every target except Archos (for which it is disabled).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@24267 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/frotz/text.c')
-rw-r--r-- | apps/plugins/frotz/text.c | 1109 |
1 files changed, 1109 insertions, 0 deletions
diff --git a/apps/plugins/frotz/text.c b/apps/plugins/frotz/text.c new file mode 100644 index 0000000000..8145cfea29 --- /dev/null +++ b/apps/plugins/frotz/text.c | |||
@@ -0,0 +1,1109 @@ | |||
1 | /* text.c - Text manipulation functions | ||
2 | * Copyright (c) 1995-1997 Stefan Jokisch | ||
3 | * | ||
4 | * This file is part of Frotz. | ||
5 | * | ||
6 | * Frotz is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * Frotz is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA | ||
19 | */ | ||
20 | |||
21 | #include "frotz.h" | ||
22 | |||
23 | enum string_type { | ||
24 | LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY | ||
25 | }; | ||
26 | |||
27 | extern zword object_name (zword); | ||
28 | |||
29 | static zchar decoded[10]; | ||
30 | static zword encoded[3]; | ||
31 | |||
32 | /* | ||
33 | * According to Matteo De Luigi <matteo.de.luigi@libero.it>, | ||
34 | * 0xab and 0xbb were in each other's proper positions. | ||
35 | * Sat Apr 21, 2001 | ||
36 | */ | ||
37 | static zchar zscii_to_latin1[] = { | ||
38 | 0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb, | ||
39 | 0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9, | ||
40 | 0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3, | ||
41 | 0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0, | ||
42 | 0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4, | ||
43 | 0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5, | ||
44 | 0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5, | ||
45 | 0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0, | ||
46 | 0xa3, 0x00, 0x00, 0xa1, 0xbf | ||
47 | }; | ||
48 | |||
49 | /* | ||
50 | * translate_from_zscii | ||
51 | * | ||
52 | * Map a ZSCII character onto the ISO Latin-1 alphabet. | ||
53 | * | ||
54 | */ | ||
55 | |||
56 | zchar translate_from_zscii (zbyte c) | ||
57 | { | ||
58 | |||
59 | if (c == 0xfc) | ||
60 | return ZC_MENU_CLICK; | ||
61 | if (c == 0xfd) | ||
62 | return ZC_DOUBLE_CLICK; | ||
63 | if (c == 0xfe) | ||
64 | return ZC_SINGLE_CLICK; | ||
65 | |||
66 | if (c >= 0x9b && story_id != BEYOND_ZORK) { | ||
67 | |||
68 | if (hx_unicode_table != 0) { /* game has its own Unicode table */ | ||
69 | |||
70 | zbyte N; | ||
71 | |||
72 | LOW_BYTE (hx_unicode_table, N) | ||
73 | |||
74 | if (c - 0x9b < N) { | ||
75 | |||
76 | zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b); | ||
77 | zword unicode; | ||
78 | |||
79 | LOW_WORD (addr, unicode) | ||
80 | |||
81 | return (unicode < 0x100) ? (zchar) unicode : '?'; | ||
82 | |||
83 | } else return '?'; | ||
84 | |||
85 | } else /* game uses standard set */ | ||
86 | |||
87 | if (c <= 0xdf) { | ||
88 | |||
89 | if (c == 0xdc || c == 0xdd) /* Oe and oe ligatures */ | ||
90 | return '?'; /* are not ISO-Latin 1 */ | ||
91 | |||
92 | return zscii_to_latin1[c - 0x9b]; | ||
93 | |||
94 | } else return '?'; | ||
95 | } | ||
96 | |||
97 | return c; | ||
98 | |||
99 | }/* translate_from_zscii */ | ||
100 | |||
101 | /* | ||
102 | * translate_to_zscii | ||
103 | * | ||
104 | * Map an ISO Latin-1 character onto the ZSCII alphabet. | ||
105 | * | ||
106 | */ | ||
107 | |||
108 | zbyte translate_to_zscii (zchar c) | ||
109 | { | ||
110 | int i; | ||
111 | |||
112 | if (c == ZC_SINGLE_CLICK) | ||
113 | return 0xfe; | ||
114 | if (c == ZC_DOUBLE_CLICK) | ||
115 | return 0xfd; | ||
116 | if (c == ZC_MENU_CLICK) | ||
117 | return 0xfc; | ||
118 | |||
119 | if (c >= ZC_LATIN1_MIN) { | ||
120 | |||
121 | if (hx_unicode_table != 0) { /* game has its own Unicode table */ | ||
122 | |||
123 | zbyte N; | ||
124 | int i; | ||
125 | |||
126 | LOW_BYTE (hx_unicode_table, N) | ||
127 | |||
128 | for (i = 0x9b; i < 0x9b + N; i++) { | ||
129 | |||
130 | zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b); | ||
131 | zword unicode; | ||
132 | |||
133 | LOW_WORD (addr, unicode) | ||
134 | |||
135 | if (c == unicode) | ||
136 | return (zbyte) i; | ||
137 | |||
138 | } | ||
139 | |||
140 | return '?'; | ||
141 | |||
142 | } else { /* game uses standard set */ | ||
143 | |||
144 | for (i = 0x9b; i <= 0xdf; i++) | ||
145 | if (c == zscii_to_latin1[i - 0x9b]) | ||
146 | return (zbyte) i; | ||
147 | |||
148 | return '?'; | ||
149 | |||
150 | } | ||
151 | } | ||
152 | |||
153 | if (c == 0) /* Safety thing from David Kinder */ | ||
154 | c = '?'; /* regarding his Unicode patches */ | ||
155 | /* Sept 15, 2002 */ | ||
156 | |||
157 | return c; | ||
158 | |||
159 | }/* translate_to_zscii */ | ||
160 | |||
161 | /* | ||
162 | * alphabet | ||
163 | * | ||
164 | * Return a character from one of the three character sets. | ||
165 | * | ||
166 | */ | ||
167 | |||
168 | static zchar alphabet (int set, int index) | ||
169 | { | ||
170 | |||
171 | if (h_alphabet != 0) { /* game uses its own alphabet */ | ||
172 | |||
173 | zbyte c; | ||
174 | |||
175 | zword addr = h_alphabet + 26 * set + index; | ||
176 | LOW_BYTE (addr, c) | ||
177 | |||
178 | return translate_from_zscii (c); | ||
179 | |||
180 | } else /* game uses default alphabet */ | ||
181 | |||
182 | if (set == 0) | ||
183 | return 'a' + index; | ||
184 | else if (set == 1) | ||
185 | return 'A' + index; | ||
186 | else if (h_version == V1) | ||
187 | return " 0123456789.,!?_#'\"/\\<-:()"[index]; | ||
188 | else | ||
189 | return " ^0123456789.,!?_#'\"/\\-:()"[index]; | ||
190 | |||
191 | }/* alphabet */ | ||
192 | |||
193 | /* | ||
194 | * load_string | ||
195 | * | ||
196 | * Copy a ZSCII string from the memory to the global "decoded" string. | ||
197 | * | ||
198 | */ | ||
199 | |||
200 | static void load_string (zword addr, zword length) | ||
201 | { | ||
202 | int resolution = (h_version <= V3) ? 2 : 3; | ||
203 | int i = 0; | ||
204 | |||
205 | while (i < 3 * resolution) | ||
206 | |||
207 | if (i < length) { | ||
208 | |||
209 | zbyte c; | ||
210 | |||
211 | LOW_BYTE (addr, c) | ||
212 | addr++; | ||
213 | |||
214 | decoded[i++] = translate_from_zscii (c); | ||
215 | |||
216 | } else decoded[i++] = 0; | ||
217 | |||
218 | }/* load_string */ | ||
219 | |||
220 | /* | ||
221 | * encode_text | ||
222 | * | ||
223 | * Encode the Unicode text in the global "decoded" string then write | ||
224 | * the result to the global "encoded" array. (This is used to look up | ||
225 | * words in the dictionary.) Up to V3 the vocabulary resolution is | ||
226 | * two, since V4 it is three words. Because each word contains three | ||
227 | * Z-characters, that makes six or nine Z-characters respectively. | ||
228 | * Longer words are chopped to the proper size, shorter words are are | ||
229 | * padded out with 5's. For word completion we pad with 0s and 31s, | ||
230 | * the minimum and maximum Z-characters. | ||
231 | * | ||
232 | */ | ||
233 | |||
234 | static void encode_text (int padding) | ||
235 | { | ||
236 | static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 }; | ||
237 | static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 }; | ||
238 | static zchar wait[] = { 'w', 'a', 'i', 't', 0 }; | ||
239 | |||
240 | zbyte zchars[12]; | ||
241 | const zchar *ptr = decoded; | ||
242 | zchar c; | ||
243 | int resolution = (h_version <= V3) ? 2 : 3; | ||
244 | int i = 0; | ||
245 | |||
246 | /* Expand abbreviations that some old Infocom games lack */ | ||
247 | |||
248 | if (f_setup.expand_abbreviations) | ||
249 | |||
250 | if (padding == 0x05 && decoded[1] == 0) | ||
251 | |||
252 | switch (decoded[0]) { | ||
253 | case 'g': ptr = again; break; | ||
254 | case 'x': ptr = examine; break; | ||
255 | case 'z': ptr = wait; break; | ||
256 | } | ||
257 | |||
258 | /* Translate string to a sequence of Z-characters */ | ||
259 | |||
260 | while (i < 3 * resolution) | ||
261 | |||
262 | if ((c = *ptr++) != 0) { | ||
263 | |||
264 | int index, set; | ||
265 | zbyte c2; | ||
266 | |||
267 | /* Search character in the alphabet */ | ||
268 | |||
269 | for (set = 0; set < 3; set++) | ||
270 | for (index = 0; index < 26; index++) | ||
271 | if (c == alphabet (set, index)) | ||
272 | goto letter_found; | ||
273 | |||
274 | /* Character not found, store its ZSCII value */ | ||
275 | |||
276 | c2 = translate_to_zscii (c); | ||
277 | |||
278 | zchars[i++] = 5; | ||
279 | zchars[i++] = 6; | ||
280 | zchars[i++] = c2 >> 5; | ||
281 | zchars[i++] = c2 & 0x1f; | ||
282 | |||
283 | continue; | ||
284 | |||
285 | letter_found: | ||
286 | |||
287 | /* Character found, store its index */ | ||
288 | |||
289 | if (set != 0) | ||
290 | zchars[i++] = ((h_version <= V2) ? 1 : 3) + set; | ||
291 | |||
292 | zchars[i++] = index + 6; | ||
293 | |||
294 | } else zchars[i++] = padding; | ||
295 | |||
296 | /* Three Z-characters make a 16bit word */ | ||
297 | |||
298 | for (i = 0; i < resolution; i++) | ||
299 | |||
300 | encoded[i] = | ||
301 | (zchars[3 * i + 0] << 10) | | ||
302 | (zchars[3 * i + 1] << 5) | | ||
303 | (zchars[3 * i + 2]); | ||
304 | |||
305 | encoded[resolution - 1] |= 0x8000; | ||
306 | |||
307 | }/* encode_text */ | ||
308 | |||
309 | /* | ||
310 | * z_check_unicode, test if a unicode character can be read and printed. | ||
311 | * | ||
312 | * zargs[0] = Unicode | ||
313 | * | ||
314 | */ | ||
315 | |||
316 | void z_check_unicode (void) | ||
317 | { | ||
318 | zword c = zargs[0]; | ||
319 | |||
320 | if (c >= 0x20 && c <= 0x7e) | ||
321 | store (3); | ||
322 | else if (c == 0xa0) | ||
323 | store (1); | ||
324 | else if (c >= 0xa1 && c <= 0xff) | ||
325 | store (3); | ||
326 | else | ||
327 | store (0); | ||
328 | |||
329 | }/* z_check_unicode */ | ||
330 | |||
331 | /* | ||
332 | * z_encode_text, encode a ZSCII string for use in a dictionary. | ||
333 | * | ||
334 | * zargs[0] = address of text buffer | ||
335 | * zargs[1] = length of ASCII string | ||
336 | * zargs[2] = offset of ASCII string within the text buffer | ||
337 | * zargs[3] = address to store encoded text in | ||
338 | * | ||
339 | * This is a V5+ opcode and therefore the dictionary resolution must be | ||
340 | * three 16bit words. | ||
341 | * | ||
342 | */ | ||
343 | |||
344 | void z_encode_text (void) | ||
345 | { | ||
346 | int i; | ||
347 | |||
348 | load_string ((zword) (zargs[0] + zargs[2]), zargs[1]); | ||
349 | |||
350 | encode_text (0x05); | ||
351 | |||
352 | for (i = 0; i < 3; i++) | ||
353 | storew ((zword) (zargs[3] + 2 * i), encoded[i]); | ||
354 | |||
355 | }/* z_encode_text */ | ||
356 | |||
357 | /* | ||
358 | * decode_text | ||
359 | * | ||
360 | * Convert encoded text to Unicode. The encoded text consists of 16bit | ||
361 | * words. Every word holds 3 Z-characters (5 bits each) plus a spare | ||
362 | * bit to mark the last word. The Z-characters translate to ZSCII by | ||
363 | * looking at the current current character set. Some select another | ||
364 | * character set, others refer to abbreviations. | ||
365 | * | ||
366 | * There are several different string types: | ||
367 | * | ||
368 | * LOW_STRING - from the lower 64KB (byte address) | ||
369 | * ABBREVIATION - from the abbreviations table (word address) | ||
370 | * HIGH_STRING - from the end of the memory map (packed address) | ||
371 | * EMBEDDED_STRING - from the instruction stream (at PC) | ||
372 | * VOCABULARY - from the dictionary (byte address) | ||
373 | * | ||
374 | * The last type is only used for word completion. | ||
375 | * | ||
376 | */ | ||
377 | |||
378 | #define outchar(c) if (st==VOCABULARY) *ptr++=c; else print_char(c) | ||
379 | |||
380 | static void decode_text (enum string_type st, zword addr) | ||
381 | { | ||
382 | zchar *ptr; | ||
383 | long byte_addr; | ||
384 | zchar c2; | ||
385 | zword code; | ||
386 | zbyte c, prev_c = 0; | ||
387 | int shift_state = 0; | ||
388 | int shift_lock = 0; | ||
389 | int status = 0; | ||
390 | |||
391 | ptr = NULL; /* makes compilers shut up */ | ||
392 | byte_addr = 0; | ||
393 | |||
394 | /* Calculate the byte address if necessary */ | ||
395 | |||
396 | if (st == ABBREVIATION) | ||
397 | |||
398 | byte_addr = (long) addr << 1; | ||
399 | |||
400 | else if (st == HIGH_STRING) { | ||
401 | |||
402 | if (h_version <= V3) | ||
403 | byte_addr = (long) addr << 1; | ||
404 | else if (h_version <= V5) | ||
405 | byte_addr = (long) addr << 2; | ||
406 | else if (h_version <= V7) | ||
407 | byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3); | ||
408 | else /* h_version == V8 */ | ||
409 | byte_addr = (long) addr << 3; | ||
410 | |||
411 | if (byte_addr >= story_size) | ||
412 | runtime_error (ERR_ILL_PRINT_ADDR); | ||
413 | |||
414 | } | ||
415 | |||
416 | /* Loop until a 16bit word has the highest bit set */ | ||
417 | |||
418 | if (st == VOCABULARY) | ||
419 | ptr = decoded; | ||
420 | |||
421 | do { | ||
422 | |||
423 | int i; | ||
424 | |||
425 | /* Fetch the next 16bit word */ | ||
426 | |||
427 | if (st == LOW_STRING || st == VOCABULARY) { | ||
428 | LOW_WORD (addr, code) | ||
429 | addr += 2; | ||
430 | } else if (st == HIGH_STRING || st == ABBREVIATION) { | ||
431 | HIGH_WORD (byte_addr, code) | ||
432 | byte_addr += 2; | ||
433 | } else | ||
434 | CODE_WORD (code) | ||
435 | |||
436 | /* Read its three Z-characters */ | ||
437 | |||
438 | for (i = 10; i >= 0; i -= 5) { | ||
439 | |||
440 | zword abbr_addr; | ||
441 | zword ptr_addr; | ||
442 | |||
443 | c = (code >> i) & 0x1f; | ||
444 | |||
445 | switch (status) { | ||
446 | |||
447 | case 0: /* normal operation */ | ||
448 | |||
449 | if (shift_state == 2 && c == 6) | ||
450 | status = 2; | ||
451 | |||
452 | else if (h_version == V1 && c == 1) | ||
453 | new_line (); | ||
454 | |||
455 | else if (h_version >= V2 && shift_state == 2 && c == 7) | ||
456 | new_line (); | ||
457 | |||
458 | else if (c >= 6) | ||
459 | outchar (alphabet (shift_state, c - 6)); | ||
460 | |||
461 | else if (c == 0) | ||
462 | outchar (' '); | ||
463 | |||
464 | else if (h_version >= V2 && c == 1) | ||
465 | status = 1; | ||
466 | |||
467 | else if (h_version >= V3 && c <= 3) | ||
468 | status = 1; | ||
469 | |||
470 | else { | ||
471 | |||
472 | shift_state = (shift_lock + (c & 1) + 1) % 3; | ||
473 | |||
474 | if (h_version <= V2 && c >= 4) | ||
475 | shift_lock = shift_state; | ||
476 | |||
477 | break; | ||
478 | |||
479 | } | ||
480 | |||
481 | shift_state = shift_lock; | ||
482 | |||
483 | break; | ||
484 | |||
485 | case 1: /* abbreviation */ | ||
486 | |||
487 | ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c; | ||
488 | |||
489 | LOW_WORD (ptr_addr, abbr_addr) | ||
490 | decode_text (ABBREVIATION, abbr_addr); | ||
491 | |||
492 | status = 0; | ||
493 | break; | ||
494 | |||
495 | case 2: /* ZSCII character - first part */ | ||
496 | |||
497 | status = 3; | ||
498 | break; | ||
499 | |||
500 | case 3: /* ZSCII character - second part */ | ||
501 | |||
502 | c2 = translate_from_zscii ((prev_c << 5) | c); | ||
503 | outchar (c2); | ||
504 | |||
505 | status = 0; | ||
506 | break; | ||
507 | |||
508 | } | ||
509 | |||
510 | prev_c = c; | ||
511 | |||
512 | } | ||
513 | |||
514 | } while (!(code & 0x8000)); | ||
515 | |||
516 | if (st == VOCABULARY) | ||
517 | *ptr = 0; | ||
518 | |||
519 | }/* decode_text */ | ||
520 | |||
521 | #undef outchar | ||
522 | |||
523 | /* | ||
524 | * z_new_line, print a new line. | ||
525 | * | ||
526 | * no zargs used | ||
527 | * | ||
528 | */ | ||
529 | |||
530 | void z_new_line (void) | ||
531 | { | ||
532 | |||
533 | new_line (); | ||
534 | |||
535 | }/* z_new_line */ | ||
536 | |||
537 | /* | ||
538 | * z_print, print a string embedded in the instruction stream. | ||
539 | * | ||
540 | * no zargs used | ||
541 | * | ||
542 | */ | ||
543 | |||
544 | void z_print (void) | ||
545 | { | ||
546 | |||
547 | decode_text (EMBEDDED_STRING, 0); | ||
548 | |||
549 | }/* z_print */ | ||
550 | |||
551 | /* | ||
552 | * z_print_addr, print a string from the lower 64KB. | ||
553 | * | ||
554 | * zargs[0] = address of string to print | ||
555 | * | ||
556 | */ | ||
557 | |||
558 | void z_print_addr (void) | ||
559 | { | ||
560 | |||
561 | decode_text (LOW_STRING, zargs[0]); | ||
562 | |||
563 | }/* z_print_addr */ | ||
564 | |||
565 | /* | ||
566 | * z_print_char print a single ZSCII character. | ||
567 | * | ||
568 | * zargs[0] = ZSCII character to be printed | ||
569 | * | ||
570 | */ | ||
571 | |||
572 | void z_print_char (void) | ||
573 | { | ||
574 | |||
575 | print_char (translate_from_zscii (zargs[0])); | ||
576 | |||
577 | }/* z_print_char */ | ||
578 | |||
579 | /* | ||
580 | * z_print_form, print a formatted table. | ||
581 | * | ||
582 | * zargs[0] = address of formatted table to be printed | ||
583 | * | ||
584 | */ | ||
585 | |||
586 | void z_print_form (void) | ||
587 | { | ||
588 | zword count; | ||
589 | zword addr = zargs[0]; | ||
590 | |||
591 | bool first = TRUE; | ||
592 | |||
593 | for (;;) { | ||
594 | |||
595 | LOW_WORD (addr, count) | ||
596 | addr += 2; | ||
597 | |||
598 | if (count == 0) | ||
599 | break; | ||
600 | |||
601 | if (!first) | ||
602 | new_line (); | ||
603 | |||
604 | while (count--) { | ||
605 | |||
606 | zbyte c; | ||
607 | |||
608 | LOW_BYTE (addr, c) | ||
609 | addr++; | ||
610 | |||
611 | print_char (translate_from_zscii (c)); | ||
612 | |||
613 | } | ||
614 | |||
615 | first = FALSE; | ||
616 | |||
617 | } | ||
618 | |||
619 | }/* z_print_form */ | ||
620 | |||
621 | /* | ||
622 | * print_num | ||
623 | * | ||
624 | * Print a signed 16bit number. | ||
625 | * | ||
626 | */ | ||
627 | |||
628 | void print_num (zword value) | ||
629 | { | ||
630 | int i; | ||
631 | |||
632 | /* Print sign */ | ||
633 | |||
634 | if ((short) value < 0) { | ||
635 | print_char ('-'); | ||
636 | value = - (short) value; | ||
637 | } | ||
638 | |||
639 | /* Print absolute value */ | ||
640 | |||
641 | for (i = 10000; i != 0; i /= 10) | ||
642 | if (value >= i || i == 1) | ||
643 | print_char ('0' + (value / i) % 10); | ||
644 | |||
645 | }/* print_num */ | ||
646 | |||
647 | /* | ||
648 | * z_print_num, print a signed number. | ||
649 | * | ||
650 | * zargs[0] = number to print | ||
651 | * | ||
652 | */ | ||
653 | |||
654 | void z_print_num (void) | ||
655 | { | ||
656 | |||
657 | print_num (zargs[0]); | ||
658 | |||
659 | }/* z_print_num */ | ||
660 | |||
661 | /* | ||
662 | * print_object | ||
663 | * | ||
664 | * Print an object description. | ||
665 | * | ||
666 | */ | ||
667 | |||
668 | void print_object (zword object) | ||
669 | { | ||
670 | zword addr = object_name (object); | ||
671 | zword code = 0x94a5; | ||
672 | zbyte length; | ||
673 | |||
674 | LOW_BYTE (addr, length) | ||
675 | addr++; | ||
676 | |||
677 | if (length != 0) | ||
678 | LOW_WORD (addr, code) | ||
679 | |||
680 | if (code == 0x94a5) { /* encoded text 0x94a5 == empty string */ | ||
681 | |||
682 | print_string ("object#"); /* supply a generic name */ | ||
683 | print_num (object); /* for anonymous objects */ | ||
684 | |||
685 | } else decode_text (LOW_STRING, addr); | ||
686 | |||
687 | }/* print_object */ | ||
688 | |||
689 | /* | ||
690 | * z_print_obj, print an object description. | ||
691 | * | ||
692 | * zargs[0] = number of object to be printed | ||
693 | * | ||
694 | */ | ||
695 | |||
696 | void z_print_obj (void) | ||
697 | { | ||
698 | |||
699 | print_object (zargs[0]); | ||
700 | |||
701 | }/* z_print_obj */ | ||
702 | |||
703 | /* | ||
704 | * z_print_paddr, print the string at the given packed address. | ||
705 | * | ||
706 | * zargs[0] = packed address of string to be printed | ||
707 | * | ||
708 | */ | ||
709 | |||
710 | void z_print_paddr (void) | ||
711 | { | ||
712 | |||
713 | decode_text (HIGH_STRING, zargs[0]); | ||
714 | |||
715 | }/* z_print_paddr */ | ||
716 | |||
717 | /* | ||
718 | * z_print_ret, print the string at PC, print newline then return true. | ||
719 | * | ||
720 | * no zargs used | ||
721 | * | ||
722 | */ | ||
723 | |||
724 | void z_print_ret (void) | ||
725 | { | ||
726 | |||
727 | decode_text (EMBEDDED_STRING, 0); | ||
728 | new_line (); | ||
729 | ret (1); | ||
730 | |||
731 | }/* z_print_ret */ | ||
732 | |||
733 | /* | ||
734 | * print_string | ||
735 | * | ||
736 | * Print a string of ASCII characters. | ||
737 | * | ||
738 | */ | ||
739 | |||
740 | void print_string (const char *s) | ||
741 | { | ||
742 | char c; | ||
743 | |||
744 | while ((c = *s++) != 0) | ||
745 | |||
746 | if (c == '\n') | ||
747 | new_line (); | ||
748 | else | ||
749 | print_char (c); | ||
750 | |||
751 | }/* print_string */ | ||
752 | |||
753 | /* | ||
754 | * z_print_unicode | ||
755 | * | ||
756 | * zargs[0] = Unicode | ||
757 | * | ||
758 | */ | ||
759 | |||
760 | void z_print_unicode (void) | ||
761 | { | ||
762 | |||
763 | print_char ((zargs[0] <= 0xff) ? zargs[0] : '?'); | ||
764 | |||
765 | }/* z_print_unicode */ | ||
766 | |||
767 | /* | ||
768 | * lookup_text | ||
769 | * | ||
770 | * Scan a dictionary searching for the given word. The first argument | ||
771 | * can be | ||
772 | * | ||
773 | * 0x00 - find the first word which is >= the given one | ||
774 | * 0x05 - find the word which exactly matches the given one | ||
775 | * 0x1f - find the last word which is <= the given one | ||
776 | * | ||
777 | * The return value is 0 if the search fails. | ||
778 | * | ||
779 | */ | ||
780 | |||
781 | static zword lookup_text (int padding, zword dct) | ||
782 | { | ||
783 | zword entry_addr; | ||
784 | zword entry_count; | ||
785 | zword entry; | ||
786 | zword addr; | ||
787 | zbyte entry_len; | ||
788 | zbyte sep_count; | ||
789 | int resolution = (h_version <= V3) ? 2 : 3; | ||
790 | int entry_number; | ||
791 | int lower, upper; | ||
792 | int i; | ||
793 | bool sorted; | ||
794 | |||
795 | encode_text (padding); | ||
796 | |||
797 | LOW_BYTE (dct, sep_count) /* skip word separators */ | ||
798 | dct += 1 + sep_count; | ||
799 | LOW_BYTE (dct, entry_len) /* get length of entries */ | ||
800 | dct += 1; | ||
801 | LOW_WORD (dct, entry_count) /* get number of entries */ | ||
802 | dct += 2; | ||
803 | |||
804 | if ((short) entry_count < 0) { /* bad luck, entries aren't sorted */ | ||
805 | |||
806 | entry_count = - (short) entry_count; | ||
807 | sorted = FALSE; | ||
808 | |||
809 | } else sorted = TRUE; /* entries are sorted */ | ||
810 | |||
811 | lower = 0; | ||
812 | upper = entry_count - 1; | ||
813 | |||
814 | while (lower <= upper) { | ||
815 | |||
816 | if (sorted) /* binary search */ | ||
817 | entry_number = (lower + upper) / 2; | ||
818 | else /* linear search */ | ||
819 | entry_number = lower; | ||
820 | |||
821 | entry_addr = dct + entry_number * entry_len; | ||
822 | |||
823 | /* Compare word to dictionary entry */ | ||
824 | |||
825 | addr = entry_addr; | ||
826 | |||
827 | for (i = 0; i < resolution; i++) { | ||
828 | LOW_WORD (addr, entry) | ||
829 | if (encoded[i] != entry) | ||
830 | goto continuing; | ||
831 | addr += 2; | ||
832 | } | ||
833 | |||
834 | return entry_addr; /* exact match found, return now */ | ||
835 | |||
836 | continuing: | ||
837 | |||
838 | if (sorted) /* binary search */ | ||
839 | |||
840 | if (encoded[i] > entry) | ||
841 | lower = entry_number + 1; | ||
842 | else | ||
843 | upper = entry_number - 1; | ||
844 | |||
845 | else lower++; /* linear search */ | ||
846 | |||
847 | } | ||
848 | |||
849 | /* No exact match has been found */ | ||
850 | |||
851 | if (padding == 0x05) | ||
852 | return 0; | ||
853 | |||
854 | entry_number = (padding == 0x00) ? lower : upper; | ||
855 | |||
856 | if (entry_number == -1 || entry_number == entry_count) | ||
857 | return 0; | ||
858 | |||
859 | return dct + entry_number * entry_len; | ||
860 | |||
861 | }/* lookup_text */ | ||
862 | |||
863 | /* | ||
864 | * tokenise_text | ||
865 | * | ||
866 | * Translate a single word to a token and append it to the token | ||
867 | * buffer. Every token consists of the address of the dictionary | ||
868 | * entry, the length of the word and the offset of the word from | ||
869 | * the start of the text buffer. Unknown words cause empty slots | ||
870 | * if the flag is set (such that the text can be scanned several | ||
871 | * times with different dictionaries); otherwise they are zero. | ||
872 | * | ||
873 | */ | ||
874 | |||
875 | static void tokenise_text (zword text, zword length, zword from, zword parse, zword dct, bool flag) | ||
876 | { | ||
877 | zword addr; | ||
878 | zbyte token_max, token_count; | ||
879 | |||
880 | LOW_BYTE (parse, token_max) | ||
881 | parse++; | ||
882 | LOW_BYTE (parse, token_count) | ||
883 | |||
884 | if (token_count < token_max) { /* sufficient space left for token? */ | ||
885 | |||
886 | storeb (parse++, token_count + 1); | ||
887 | |||
888 | load_string ((zword) (text + from), length); | ||
889 | |||
890 | addr = lookup_text (0x05, dct); | ||
891 | |||
892 | if (addr != 0 || !flag) { | ||
893 | |||
894 | parse += 4 * token_count; | ||
895 | |||
896 | storew ((zword) (parse + 0), addr); | ||
897 | storeb ((zword) (parse + 2), length); | ||
898 | storeb ((zword) (parse + 3), from); | ||
899 | |||
900 | } | ||
901 | |||
902 | } | ||
903 | |||
904 | }/* tokenise_text */ | ||
905 | |||
906 | /* | ||
907 | * tokenise_line | ||
908 | * | ||
909 | * Split an input line into words and translate the words to tokens. | ||
910 | * | ||
911 | */ | ||
912 | |||
913 | void tokenise_line (zword text, zword token, zword dct, bool flag) | ||
914 | { | ||
915 | zword addr1; | ||
916 | zword addr2; | ||
917 | zbyte length; | ||
918 | zbyte c; | ||
919 | |||
920 | length = 0; /* makes compilers shut up */ | ||
921 | |||
922 | /* Use standard dictionary if the given dictionary is zero */ | ||
923 | |||
924 | if (dct == 0) | ||
925 | dct = h_dictionary; | ||
926 | |||
927 | /* Remove all tokens before inserting new ones */ | ||
928 | |||
929 | storeb ((zword) (token + 1), 0); | ||
930 | |||
931 | /* Move the first pointer across the text buffer searching for the | ||
932 | beginning of a word. If this succeeds, store the position in a | ||
933 | second pointer. Move the first pointer searching for the end of | ||
934 | the word. When it is found, "tokenise" the word. Continue until | ||
935 | the end of the buffer is reached. */ | ||
936 | |||
937 | addr1 = text; | ||
938 | addr2 = 0; | ||
939 | |||
940 | if (h_version >= V5) { | ||
941 | addr1++; | ||
942 | LOW_BYTE (addr1, length) | ||
943 | } | ||
944 | |||
945 | do { | ||
946 | |||
947 | zword sep_addr; | ||
948 | zbyte sep_count; | ||
949 | zbyte separator; | ||
950 | |||
951 | /* Fetch next ZSCII character */ | ||
952 | |||
953 | addr1++; | ||
954 | |||
955 | if (h_version >= V5 && addr1 == text + 2 + length) | ||
956 | c = 0; | ||
957 | else | ||
958 | LOW_BYTE (addr1, c) | ||
959 | |||
960 | /* Check for separator */ | ||
961 | |||
962 | sep_addr = dct; | ||
963 | |||
964 | LOW_BYTE (sep_addr, sep_count) | ||
965 | sep_addr++; | ||
966 | |||
967 | do { | ||
968 | |||
969 | LOW_BYTE (sep_addr, separator) | ||
970 | sep_addr++; | ||
971 | |||
972 | } while (c != separator && --sep_count != 0); | ||
973 | |||
974 | /* This could be the start or the end of a word */ | ||
975 | |||
976 | if (sep_count == 0 && c != ' ' && c != 0) { | ||
977 | |||
978 | if (addr2 == 0) | ||
979 | addr2 = addr1; | ||
980 | |||
981 | } else if (addr2 != 0) { | ||
982 | |||
983 | tokenise_text ( | ||
984 | text, | ||
985 | (zword) (addr1 - addr2), | ||
986 | (zword) (addr2 - text), | ||
987 | token, dct, flag ); | ||
988 | |||
989 | addr2 = 0; | ||
990 | |||
991 | } | ||
992 | |||
993 | /* Translate separator (which is a word in its own right) */ | ||
994 | |||
995 | if (sep_count != 0) | ||
996 | |||
997 | tokenise_text ( | ||
998 | text, | ||
999 | (zword) (1), | ||
1000 | (zword) (addr1 - text), | ||
1001 | token, dct, flag ); | ||
1002 | |||
1003 | } while (c != 0); | ||
1004 | |||
1005 | }/* tokenise_line */ | ||
1006 | |||
1007 | /* | ||
1008 | * z_tokenise, make a lexical analysis of a ZSCII string. | ||
1009 | * | ||
1010 | * zargs[0] = address of string to analyze | ||
1011 | * zargs[1] = address of token buffer | ||
1012 | * zargs[2] = address of dictionary (optional) | ||
1013 | * zargs[3] = set when unknown words cause empty slots (optional) | ||
1014 | * | ||
1015 | */ | ||
1016 | |||
1017 | void z_tokenise (void) | ||
1018 | { | ||
1019 | |||
1020 | /* Supply default arguments */ | ||
1021 | |||
1022 | if (zargc < 3) | ||
1023 | zargs[2] = 0; | ||
1024 | if (zargc < 4) | ||
1025 | zargs[3] = 0; | ||
1026 | |||
1027 | /* Call tokenise_line to do the real work */ | ||
1028 | |||
1029 | tokenise_line (zargs[0], zargs[1], zargs[2], zargs[3] != 0); | ||
1030 | |||
1031 | }/* z_tokenise */ | ||
1032 | |||
1033 | /* | ||
1034 | * completion | ||
1035 | * | ||
1036 | * Scan the vocabulary to complete the last word on the input line | ||
1037 | * (similar to "tcsh" under Unix). The return value is | ||
1038 | * | ||
1039 | * 2 ==> completion is impossible | ||
1040 | * 1 ==> completion is ambiguous | ||
1041 | * 0 ==> completion is successful | ||
1042 | * | ||
1043 | * The function also returns a string in its second argument. In case | ||
1044 | * of 2, the string is empty; in case of 1, the string is the longest | ||
1045 | * extension of the last word on the input line that is common to all | ||
1046 | * possible completions (for instance, if the last word on the input | ||
1047 | * is "fo" and its only possible completions are "follow" and "folly" | ||
1048 | * then the string is "ll"); in case of 0, the string is an extension | ||
1049 | * to the last word that results in the only possible completion. | ||
1050 | * | ||
1051 | */ | ||
1052 | |||
1053 | int completion (const zchar *buffer, zchar *result) | ||
1054 | { | ||
1055 | zword minaddr; | ||
1056 | zword maxaddr; | ||
1057 | zchar *ptr; | ||
1058 | zchar c; | ||
1059 | int len; | ||
1060 | int i; | ||
1061 | |||
1062 | *result = 0; | ||
1063 | |||
1064 | /* Copy last word to "decoded" string */ | ||
1065 | |||
1066 | len = 0; | ||
1067 | |||
1068 | while ((c = *buffer++) != 0) | ||
1069 | |||
1070 | if (c != ' ') { | ||
1071 | |||
1072 | if (len < 9) | ||
1073 | decoded[len++] = c; | ||
1074 | |||
1075 | } else len = 0; | ||
1076 | |||
1077 | decoded[len] = 0; | ||
1078 | |||
1079 | /* Search the dictionary for first and last possible extensions */ | ||
1080 | |||
1081 | minaddr = lookup_text (0x00, h_dictionary); | ||
1082 | maxaddr = lookup_text (0x1f, h_dictionary); | ||
1083 | |||
1084 | if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr) | ||
1085 | return 2; | ||
1086 | |||
1087 | /* Copy first extension to "result" string */ | ||
1088 | |||
1089 | decode_text (VOCABULARY, minaddr); | ||
1090 | |||
1091 | ptr = result; | ||
1092 | |||
1093 | for (i = len; (c = decoded[i]) != 0; i++) | ||
1094 | *ptr++ = c; | ||
1095 | *ptr = 0; | ||
1096 | |||
1097 | /* Merge second extension with "result" string */ | ||
1098 | |||
1099 | decode_text (VOCABULARY, maxaddr); | ||
1100 | |||
1101 | for (i = len, ptr = result; (c = decoded[i]) != 0; i++, ptr++) | ||
1102 | if (*ptr != c) break; | ||
1103 | *ptr = 0; | ||
1104 | |||
1105 | /* Search was ambiguous or successful */ | ||
1106 | |||
1107 | return (minaddr == maxaddr) ? 0 : 1; | ||
1108 | |||
1109 | }/* completion */ | ||