1 /*
2 * bctext.c - DOS interface, text functions
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 * Or visit http://www.fsf.org/
20 */
21
22 #include <alloc.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <conio.h>
26 #include <dos.h>
27 #include "frotz.h"
28 #include "BCfrotz.h"
29 #include "fontdata.h"
30
31 extern byte far *get_scrnptr(int);
32
33 int current_bg = 0;
34 int current_fg = 0;
35 int current_style = 0;
36 int current_font = 0;
37
38 byte text_bg = 0;
39 byte text_fg = 0;
40 byte bg = 0;
41 byte fg = 0;
42 byte scrn_attr = 0;
43
44 int cursor_x = 0;
45 int cursor_y = 0;
46
47 char latin1_to_ascii[] =
48 " ! c L >o<Y | S '' C a << not- R _ "
49 "^0 +/-^2 ^3 ' my P . , ^1 o >> 1/41/23/4? "
50 "A A A A Ae A AE C E E E E I I I I "
51 "Th N O O O O Oe * O U U U Ue Y Th ss "
52 "a a a a ae a ae c e e e e i i i i "
53 "th n o o o o oe : o u u u ue y th y ";
54
55 char latin1_to_ibm[] = {
56 0x20, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5,
57 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
58 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa,
59 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
60 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80,
61 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
62 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e,
63 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
64 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87,
65 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
66 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6,
67 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98
68 };
69
70 static byte far *graphics_font = NULL;
71 static byte far *mcga_font = NULL;
72 static byte far *mcga_width = NULL;
73 static word far *serif_font = NULL;
74 static byte far *serif_width = NULL;
75
76
77 /*
78 * load_fonts
79 *
80 * Load the proportional and graphics fonts. In the release version all
81 * font data is appended to the end of the executable.
82 *
83 */
load_fonts(void)84 void load_fonts(void)
85 {
86 static chunk_offset[] = {
87 0x6660,
88 0x6300,
89 0x4A40,
90 0x3180,
91 0x18C0,
92 0x00
93 };
94
95 if (display == _MCGA_) {
96 mcga_font = font_data + chunk_offset[1];
97 mcga_width = (byte *) mcga_font + 0x300;
98 } else
99 graphics_font = font_data + chunk_offset[0];
100
101 if (display == _AMIGA_ && user_font != 0) {
102 serif_font = (word *) (font_data + chunk_offset[1 + user_font]);
103 serif_width = (byte *) serif_font + 0x1800;
104 }
105 } /* load_fonts */
106
107
108 /*
109 * os_font_data
110 *
111 * Return true if the given font is available. The font can be
112 *
113 * TEXT_FONT
114 * PICTURE_FONT
115 * GRAPHICS_FONT
116 * FIXED_WIDTH_FONT
117 *
118 * The font size should be stored in "height" and "width". If the given
119 * font is unavailable then these values must _not_ be changed.
120 *
121 */
os_font_data(int font,int * height,int * width)122 int os_font_data(int font, int *height, int *width)
123 {
124 /* All fonts of this interface have the same size */
125 *height = z_header.font_height;
126 *width = z_header.font_width;
127
128 /* Not every font is available in every mode */
129 if (font == TEXT_FONT)
130 return TRUE;
131 if (font == GRAPHICS_FONT && (display == _CGA_ || display >= _EGA_))
132 return TRUE;
133 if (font == FIXED_WIDTH_FONT)
134 return TRUE;
135
136 /* Unavailable font */
137 return FALSE;
138 } /* os_font_data */
139
140
141 /*
142 * switch_scrn_attr
143 *
144 * Parts of the screen are usually erased to background colour. However,
145 * for deleting text in the input routine it can be useful to erase to
146 * the current text background colour. The two colours can be different,
147 * for example when the reverse text style is used. This helper function
148 * toggles between the two possible behaviours.
149 *
150 */
switch_scrn_attr(bool flag)151 void switch_scrn_attr(bool flag)
152 {
153 byte scrn_bg;
154 byte scrn_fg;
155
156 if (flag) {
157 scrn_bg = text_bg;
158 scrn_fg = text_fg;
159 } else {
160 scrn_bg = bg;
161 scrn_fg = fg;
162 }
163
164 if (display <= _TEXT_)
165 scrn_attr = (scrn_bg << 4) | scrn_fg;
166 else if (display == _CGA_)
167 scrn_attr = (scrn_bg != BLACK) ? 0xff : 0x00;
168 else
169 scrn_attr = scrn_bg;
170 } /* switch_scrn_attr */
171
172
173 /*
174 * adjust_style
175 *
176 * Set the current colours. This combines the current colour selection
177 * and the current text style.
178 *
179 */
adjust_style(void)180 static void adjust_style(void)
181 {
182 static byte amiga_palette[][3] = {
183 {0x00, 0x00, 0x00},
184 {0x2a, 0x00, 0x00},
185 {0x00, 0x2a, 0x00},
186 {0x3f, 0x3f, 0x15},
187 {0x00, 0x00, 0x2a},
188 {0x2a, 0x00, 0x2a},
189 {0x00, 0x2a, 0x2a},
190 {0x3f, 0x3f, 0x3f},
191 {0x30, 0x30, 0x30},
192 {0x20, 0x20, 0x20},
193 {0x10, 0x10, 0x10},
194 };
195
196 static byte pc_colour[] = {
197 BLACK,
198 RED,
199 GREEN,
200 YELLOW,
201 BLUE,
202 MAGENTA,
203 CYAN,
204 WHITE,
205 DARKGRAY
206 };
207
208 static byte palette_bg = 0xff;
209 static byte palette_fg = 0xff;
210
211 fg = current_fg;
212 bg = current_bg;
213
214 /* V6 game, Amiga mode: Alter the palette registers if the colours
215 of window 0 have changed. DAC register #79 holds the foreground,
216 DAC register #64 the background colour. */
217
218 if (display == _AMIGA_ && z_header.version == V6 && cwin == 0) {
219 if (fg < 16 && fg != palette_fg) {
220 byte R = amiga_palette[fg - 2][0];
221 byte G = amiga_palette[fg - 2][1];
222 byte B = amiga_palette[fg - 2][2];
223
224 asm mov ax, 0x1010
225 asm mov bx, 79
226 asm mov dh, R
227 asm mov ch, G
228 asm mov cl, B
229 asm int 0x10
230
231 palette_fg = fg;
232 }
233
234 if (bg < 16 && bg != palette_bg) {
235 byte R = amiga_palette[bg - 2][0];
236 byte G = amiga_palette[bg - 2][1];
237 byte B = amiga_palette[bg - 2][2];
238
239 asm mov ax, 0x1010
240 asm mov bx, 64
241 asm mov dh, R
242 asm mov ch, G
243 asm mov cl, B
244 asm int 0x10
245
246 palette_bg = bg;
247
248 }
249
250 }
251
252 /* Handle colours */
253 if (fg < 16) {
254 if (display == _MONO_)
255 fg = (fg == WHITE_COLOUR) ? LIGHTGRAY : BLACK;
256 else if (z_header.version == V6 && display == _AMIGA_)
257 fg = (palette_fg == fg) ? 15 : 0;
258 else
259 fg = pc_colour[fg - 2];
260 } else
261 fg -= 16;
262
263 if (bg < 16) {
264 if (display == _MONO_)
265 bg = (bg == WHITE_COLOUR) ? LIGHTGRAY : BLACK;
266 else if (z_header.version == V6 && display == _AMIGA_)
267 bg = (palette_bg == bg) ? 0 : 15;
268 else
269 bg = pc_colour[bg - 2];
270
271 } else
272 bg -= 16;
273
274 /* Handle reverse text style */
275 if (current_style & REVERSE_STYLE) {
276 text_fg = (user_reverse_fg != -1) ? user_reverse_fg : bg;
277 text_bg = (user_reverse_bg != -1) ? user_reverse_bg : fg;
278 } else {
279 text_fg = fg;
280 text_bg = bg;
281 }
282
283 /* Handle emphasis style */
284 if (current_style & EMPHASIS_STYLE) {
285 if (display == _MONO_ && text_bg == BLACK)
286 text_fg = BLUE; /* blue in monochrome mode is underlined */
287 if (display == _TEXT_)
288 text_fg =
289 (user_emphasis != -1) ? user_emphasis : YELLOW;
290 }
291
292 /* Handle boldface style */
293 if (current_style & BOLDFACE_STYLE) {
294 if (display == _MONO_)
295 text_fg = WHITE;
296 if (display == _TEXT_)
297 text_fg ^= 8;
298
299 }
300
301 /* Set the screen attribute for scrolling and erasing */
302 switch_scrn_attr(FALSE);
303 } /* adjust_style */
304
305
306 /*
307 * os_set_colour
308 *
309 * Set the foreground and background colours which can be:
310 *
311 * DEFAULT_COLOUR
312 * BLACK_COLOUR
313 * RED_COLOUR
314 * GREEN_COLOUR
315 * YELLOW_COLOUR
316 * BLUE_COLOUR
317 * MAGENTA_COLOUR
318 * CYAN_COLOUR
319 * WHITE_COLOUR
320 *
321 * MS-DOS 320 columns MCGA mode only:
322 *
323 * GREY_COLOUR
324 *
325 * Amiga only:
326 *
327 * LIGHTGREY_COLOUR
328 * MEDIUMGREY_COLOUR
329 * DARKGREY_COLOUR
330 *
331 * There may be more colours in the range from 16 to 255; see the remarks
332 * on os_peek_colour.
333 *
334 */
os_set_colour(int new_foreground,int new_background)335 void os_set_colour(int new_foreground, int new_background)
336 {
337 current_fg = new_foreground;
338 current_bg = new_background;
339
340 /* Apply changes */
341 adjust_style();
342
343 } /* os_set_colour */
344
345
346 /*
347 * os_set_text_style
348 *
349 * Set the current text style. Following flags can be set:
350 *
351 * REVERSE_STYLE
352 * BOLDFACE_STYLE
353 * EMPHASIS_STYLE (aka underline aka italics)
354 * FIXED_WIDTH_STYLE
355 *
356 */
os_set_text_style(int new_style)357 void os_set_text_style(int new_style)
358 {
359 current_style = new_style;
360
361 /* Apply changes */
362 adjust_style();
363
364 } /* os_set_text_style */
365
366
367 /*
368 * os_set_font
369 *
370 * Set the font for text output. The interpreter takes care not to
371 * choose fonts which aren't supported by the interface.
372 *
373 */
os_set_font(int new_font)374 void os_set_font(int new_font)
375 {
376 current_font = new_font;
377 } /* os_set_font */
378
379
380 /*
381 * write_pattern
382 *
383 * Helper function for drawing characters in EGA and Amiga mode.
384 *
385 */
write_pattern(byte far * screen,byte val,byte mask)386 void write_pattern(byte far * screen, byte val, byte mask)
387 {
388 if (mask != 0) {
389 if (display == _CGA_) {
390 if (text_bg == BLACK)
391 *screen &= ~mask;
392 if (text_bg == WHITE)
393 *screen |= mask;
394 if (text_fg != text_bg)
395 *screen ^= val;
396 } else if (display == _MCGA_) {
397 byte i;
398
399 for (i = 0x80; (mask & i) != 0; i >>= 1)
400 *screen++ = (val & i) ? text_fg : text_bg;
401 } else {
402 asm mov dx, 0x03cf
403 asm mov al, mask
404 asm out dx, al
405 asm les bx, screen
406 asm mov ch, text_bg
407 asm mov al, es:[bx]
408 asm mov es:[bx], ch
409 asm mov al, val
410 asm out dx, al
411 asm mov ch, text_fg
412 asm mov al, es:[bx]
413 asm mov es:[bx], ch
414 }
415 }
416 } /* write_pattern */
417
418
419 /*
420 * os_display_char
421 *
422 * Display a character of the current font using the current colours and
423 * text style. The cursor moves to the next position. Printable codes are
424 * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to
425 * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph
426 * indentation). The screen should not be scrolled after printing to the
427 * bottom right corner.
428 *
os_display_char(zchar c)429 */ void os_display_char(zchar c)
430 {
431 int width = os_char_width(c);
432
433 /* Handle accented characters */
434 if (c >= ZC_LATIN1_MIN
435 && (story_id != BEYOND_ZORK || (z_header.flags & GRAPHICS_FLAG)))
436 if (display == _CGA_ || display == _MCGA_) {
437 char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN);
438
439 char c1 = *ptr++;
440 char c2 = *ptr++;
441 char c3 = *ptr++;
442
443 os_display_char(c1);
444
445 if (c2 != ' ')
446 os_display_char(c2);
447 if (c3 != ' ')
448 os_display_char(c3);
449
450 return;
451
452 } else if (display == _AMIGA_ && current_font == TEXT_FONT
453 && !(current_style & FIXED_WIDTH_STYLE)
454 && user_font != 0) {
455
456 if (c >= ZC_LATIN1_MIN)
457 c -= 32;
458 } else
459 c = latin1_to_ibm[c - ZC_LATIN1_MIN];
460
461 /* Handle special indentations */
462 if (c == ZC_INDENT) {
463 os_display_char(' ');
464 os_display_char(' ');
465 os_display_char(' ');
466 return;
467 }
468 if (c == ZC_GAP) {
469 os_display_char(' ');
470 os_display_char(' ');
471 return;
472 }
473
474 /* Display character */
475 if (display <= _TEXT_) {
476 asm mov ah, 2
477 asm mov bh, 0
478 asm mov dh, byte ptr cursor_y
479 asm mov dl, byte ptr cursor_x
480 asm int 0x10
481 asm mov ah, 9
482 asm mov bh, 0
483 asm mov bl, byte ptr text_bg
484 asm mov cl, 4
485 asm shl bl, cl
486 asm or bl, byte ptr text_fg
487 asm mov cx, 1
488 asm mov al, byte ptr c
489 asm int 0x10
490 } else {
491
492 void far *table;
493 word mask;
494 word val;
495 byte mask0;
496 byte mask1;
497 int align;
498 int underline;
499 int boldface;
500 int type;
501
502 int shift = (display != _MCGA_) ? cursor_x % 8 : 0;
503 int offset = (display != _MCGA_) ? cursor_x / 8 : cursor_x;
504
505 int i;
506
507 if (current_font == GRAPHICS_FONT) {
508 table = graphics_font + 8 * (c - 32);
509 mask = 0xff;
510 underline = -1;
511 boldface = -1;
512 align = 0;
513 type = 1;
514 } else if (display == _AMIGA_ && current_font == TEXT_FONT
515 && !(current_style & FIXED_WIDTH_STYLE)
516 && user_font != 0) {
517 table = serif_font + 16 * (c - 32);
518 mask = 0xffff << (16 - width);
519 underline = 14;
520 boldface = 1;
521 align = 0;
522 type = 2;
523 } else if (display == _CGA_) {
524 table = (byte far *) MK_FP(0xf000, 0xfa6e) + 8 * c;
525 mask = 0xff;
526 underline = 7;
527 boldface = (user_bold_typing != -1) ? 1 : -1;
528 align = 0;
529 type = 3;
530 } else if (display >= _EGA_) {
531 table = (byte far *) getvect(0x43) + z_header.font_height * c;
532 mask = 0xff;
533 underline = z_header.font_height - 1;
534 boldface = (user_bold_typing != -1) ? 1 : -1;
535 align = 0;
536 type = 3;
537 } else {
538 table = mcga_font + 8 * (c - 32);
539 mask = 0xff & (0xff << (8 - width));
540 underline = 7;
541 boldface = -1;
542 align = (width + 1 - mcga_width[c - 32]) / 2;
543 type = 3;
544 }
545
546 mask0 = mask >> shift;
547 mask1 = mask << (8 - shift);
548
549 if (!(current_style & BOLDFACE_STYLE))
550 boldface = -1;
551 if (!(current_style & EMPHASIS_STYLE))
552 underline = -1;
553
554 if (display >= _EGA_) {
555 outport(0x03ce, 0x0205);
556 outport(0x03ce, 0xff08);
557 }
558
559 for (i = 0; i < z_header.font_height; i++) {
560 byte far *screen = get_scrnptr(cursor_y + i) + offset;
561
562 if (type == 1) {
563 val =
564 *((byte far *) table +
565 8 * i / z_header.font_height);
566 }
567 if (type == 2)
568 val = *((word far *) table + i);
569 if (type == 3)
570 val = *((byte far *) table + i);
571
572 if (align != 0)
573 val >>= align;
574
575 if (boldface == 1)
576 val |= val >> 1;
577 if (underline == i)
578 val ^= mask;
579
580 if (type == 2)
581 write_pattern(screen++, val >> (8 + shift),
582 mask >> (8 + shift));
583
584 write_pattern(screen + 0, val >> shift, mask0);
585 write_pattern(screen + 1, val << (8 - shift), mask1);
586 }
587 }
588
589 /* Move cursor to next position */
590 cursor_x += width;
591 } /* os_display_char */
592
593
594 /*
595 * os_display_string
596 *
597 * Pass a string of characters to os_display_char.
598 *
599 */
os_display_string(const zchar * s)600 void os_display_string(const zchar * s)
601 {
602 zchar c;
603
604 while ((c = *s++) != 0) {
605 if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) {
606
607 int arg = *s++;
608
609 if (c == ZC_NEW_FONT)
610 os_set_font(arg);
611 if (c == ZC_NEW_STYLE)
612 os_set_text_style(arg);
613
614 } else
615 os_display_char(c);
616 }
617 } /* os_display_string */
618
619
620 /*
621 * os_char_width
622 *
623 * Return the width of the character in screen units.
624 *
625 */
626
os_char_width(zchar c)627 int os_char_width(zchar c)
628 {
629 /* Handle accented characters */
630 if (c >= ZC_LATIN1_MIN
631 && (story_id != BEYOND_ZORK || (z_header.flags & GRAPHICS_FLAG)))
632 if (display == _CGA_ || display == _MCGA_) {
633
634 const char *ptr =
635 latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN);
636
637 int width = 0;
638
639 char c1 = *ptr++;
640 char c2 = *ptr++;
641 char c3 = *ptr++;
642
643 width = os_char_width(c1);
644
645 if (c2 != ' ')
646 width += os_char_width(c2);
647 if (c3 != ' ')
648 width += os_char_width(c3);
649
650 return width;
651
652 } else if (display == _AMIGA_ && current_font == TEXT_FONT
653 && !(current_style & FIXED_WIDTH_STYLE)
654 && user_font != 0)
655 if (c >= ZC_LATIN1_MIN)
656 c -= 32;
657
658 /* Handle special indentations */
659 if (c == ZC_INDENT)
660 return 3 * os_char_width(' ');
661 if (c == ZC_GAP)
662 return 2 * os_char_width(' ');
663
664 /* Calculate width */
665 if (display <= _TEXT_)
666 return 1;
667 if (display == _CGA_)
668 return 8;
669 if (display == _EGA_)
670 return 8;
671
672 if (current_font == GRAPHICS_FONT)
673 return 8;
674 if (current_font == FIXED_WIDTH_FONT
675 || (current_style & FIXED_WIDTH_STYLE) || (display == _AMIGA_
676 && user_font == 0))
677 return (display == _AMIGA_) ? 8 : 5;
678
679 if (display == _MCGA_)
680 return mcga_width[c - 32];
681 if (display == _AMIGA_)
682 return serif_width[c - 32] +
683 ((current_style & BOLDFACE_STYLE) ? 1 : 0);
684
685 return 0;
686 } /* os_char_width */
687
688
689 /*
690 * os_string_width
691 *
692 * Calculate the length of a word in screen units. Apart from letters,
693 * the word may contain special codes:
694 *
695 * ZC_NEW_STYLE - next character is a new text style
696 * ZC_NEW_FONT - next character is a new font
697 *
698 */
os_string_width(const zchar * s)699 int os_string_width(const zchar * s)
700 {
701 int width = 0;
702
703 int saved_font = current_font;
704 int saved_style = current_style;
705
706 zchar c;
707
708 while ((c = *s++) != 0) {
709 if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) {
710
711 int arg = *s++;
712
713 if (c == ZC_NEW_FONT)
714 current_font = arg;
715 if (c == ZC_NEW_STYLE)
716 current_style = arg;
717
718 } else
719 width += os_char_width(c);
720 }
721 current_font = saved_font;
722 current_style = saved_style;
723
724 return width;
725 } /* os_string_width */
726
727
728 /*
729 * os_set_cursor
730 *
731 * Place the text cursor at the given coordinates. Top left is (1,1).
732 *
733 */
os_set_cursor(int y,int x)734 void os_set_cursor(int y, int x)
735 {
736 cursor_y = y - 1;
737 cursor_x = x - 1;
738 } /* os_set_cursor */
739
740
741 /*
742 * os_more_prompt
743 *
744 * Display a MORE prompt, wait for a keypress and remove the MORE
745 * prompt from the screen.
746 *
747 */
os_more_prompt(void)748 void os_more_prompt(void)
749 {
750 int saved_x;
751
752 /* Save text font and style */
753 int saved_font = current_font;
754 int saved_style = current_style;
755
756 /* Choose plain text style */
757 current_font = TEXT_FONT;
758 current_style = 0;
759
760 adjust_style();
761
762 /* Wait until the user presses a key */
763 saved_x = cursor_x;
764 os_display_string((zchar *) "[MORE]");
765 os_read_key(0, TRUE);
766
767 os_erase_area(cursor_y + 1,
768 saved_x + 1, cursor_y + z_header.font_height, cursor_x + 1, -1);
769
770 cursor_x = saved_x;
771
772 /* Restore text font and style */
773 current_font = saved_font;
774 current_style = saved_style;
775
776 adjust_style();
777 } /* os_more_prompt */
778