1 /*
2 * sf_resource.c - SDL interface, resource-handling 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 "sf_frotz.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 /* for access() */
29 #include <unistd.h>
30
31 #include <stdarg.h>
32
33 #include <SDL.h>
34
35 #include "../blorb/blorb.h"
36 #include "../blorb/blorblow.h"
37
38 zword hx_flags;
39 zword hx_fore_colour;
40 zword hx_back_colour;
41
42 extern z_header_t z_header;
43 extern bb_map_t *blorb_map;
44
45 extern FILE *blorb_fp;
46
47
48 /* various data */
49 bool m_tandy = 0;
50 int m_v6scale;
51 double m_gfxScale_w = 1.0;
52 double m_gfxScale_h = 1.0;
53 ulong m_defaultFore;
54 ulong m_defaultBack;
55 ulong m_colours[11];
56 ulong m_nonStdColours[NON_STD_COLS];
57 int m_nonStdIndex;
58 bool m_exitPause = 0;
59 bool m_lineInput = 0;
60 bool m_IsInfocomV6 = false;
61 bool m_morePrompts = 1;
62 bool m_localfiles = false;
63 char *m_fontdir = NULL;
64 bool m_aafonts = 0;
65 char m_names_format = 0;
66 char *m_reslist_file = NULL;
67 char *m_setupfile = ".sfrotzrc";
68 extern int m_frequency;
69
70 bool sdl_active;
71
72 static int countedpics = 0;
73 static int maxlegalpic = 0;
74 static int releaseno = 0;
75
76 static char *ResDir = "./";
77 static char *ResPict = "PIC%d";
78 static char *ResSnd = "SND%d";
79
80 int AcWidth = 640, AcHeight = 400;
81 int option_scrollback_buffer = 0;
82
checkwidths()83 static void checkwidths()
84 {
85 bb_resolution_t *reso;
86 reso = bb_get_resolution(blorb_map);
87 if (reso) {
88 /* ignore small resolution hints */
89 if ((reso->px) && (reso->px >= AcWidth))
90 AcWidth = reso->px;
91 if ((reso->py) && (reso->py >= AcHeight))
92 AcHeight = reso->py;
93 }
94 }
95
96
sf_cleanup_resources(void)97 static void sf_cleanup_resources(void)
98 {
99 if (blorb_map)
100 bb_destroy_map(blorb_map);
101 blorb_map = NULL;
102 if (blorb_fp)
103 fclose(blorb_fp);
104 blorb_fp = NULL;
105 }
106
107 static void load_local_resources(void);
108
109 /*
110 * sf_load_resources
111 *
112 * Perform additional resource loading after the blorb map is built.
113 *
114 */
sf_load_resources(void)115 int sf_load_resources(void)
116 {
117 CLEANREG(sf_cleanup_resources);
118
119 if (blorb_map) {
120 checkwidths();
121 bb_count_resources(blorb_map, bb_ID_Pict, &countedpics, NULL,
122 &maxlegalpic);
123 releaseno = bb_get_release_num(blorb_map);
124 }
125
126 if ((m_reslist_file))
127 load_local_resources();
128
129 return 0;
130 }
131
132
133 /*
134 * pathopen
135 *
136 * Given a standard Unix-style path and a filename, search the path for
137 * that file. If found, return a pointer to that file
138 *
139 */
pathopen(const char * name,const char * path,const char * mode)140 static FILE *pathopen(const char *name, const char *path, const char *mode)
141 {
142 FILE *fp;
143 char *buf;
144 char *bp, lastch;
145
146 lastch = 'a'; /* makes compiler shut up */
147
148 /*
149 * If the path variable doesn't end in a "/" a "/"
150 * will be added, so the buffer needs to be long enough
151 * for the path + / + name + \0
152 */
153 buf = malloc(strlen(path) + strlen(name) + 2);
154
155 while (*path) {
156 bp = buf;
157 while (*path && *path != OS_PATHSEP)
158 lastch = *bp++ = *path++;
159 if (lastch != OS_DIRSEP)
160 *bp++ = OS_DIRSEP;
161 memcpy(bp, name, strlen(name) * sizeof(char));
162 if ((fp = fopen(buf, mode)) != NULL) {
163 free(buf);
164 return fp;
165 }
166 if (*path)
167 path++;
168 }
169 free(buf);
170 return NULL;
171 } /* pathopen */
172
173
174 /*
175 * os_path_open
176 *
177 * Open a file in the current directory. If this fails, then search the
178 * directories in the ZCODE_PATH environmental variable. If that's not
179 * defined, search INFOCOM_PATH.
180 *
181 */
os_path_open(const char * name,const char * mode,long * size)182 FILE *os_path_open(const char *name, const char *mode, long *size)
183 {
184 FILE *fp;
185 char *p;
186
187 /* Let's see if the file is in the currect directory */
188 /* or if the user gave us a full path. */
189 if ((fp = fopen(name, mode)))
190 return fp;
191
192 /* If zcodepath is defined in a config file, check that path. */
193 /* If we find the file a match in that path, great. */
194 /* Otherwise, check some environmental variables. */
195 if (f_setup.zcode_path != NULL) {
196 if ((fp = pathopen(name, f_setup.zcode_path, mode)) != NULL)
197 return fp;
198 }
199 if ( (p = getenv(PATH1) ) == NULL)
200 p = getenv(PATH2);
201
202 if (p != NULL) {
203 fp = pathopen(name, p, mode);
204 return fp;
205 }
206 return NULL; /* give up */
207 } /* os_path_open() */
208
209
210 /*
211 * os_picture_data
212 *
213 * Return true if the given picture is available. If so, store the
214 * picture width and height in the appropriate variables. Picture
215 * number 0 is a special case: Write the highest legal picture number
216 * and the picture file release number into the height and width
217 * variables respectively when this picture number is asked for.
218 *
219 */
os_picture_data(int picture,int * height,int * width)220 int os_picture_data(int picture, int *height, int *width)
221 {
222 if (maxlegalpic) {
223 if (picture == 0) {
224 *height = maxlegalpic;
225 *width = bb_get_release_num(blorb_map);
226 return 1;
227 } else {
228 sf_picture *res = sf_getpic(picture);
229 if (res) {
230 *height = m_gfxScale_h * res->height;
231 *width = m_gfxScale_w * res->width;
232 return 1;
233 }
234 }
235 }
236 *height = 0;
237 *width = 0;
238 return 0;
239 }
240
241
242 /*
243 * os_menu
244 *
245 * Add to or remove a menu item. Action can be:
246 * MENU_NEW - Add a new menu with the given title
247 * MENU_ADD - Add a new menu item with the given text
248 * MENU_REMOVE - Remove the menu at the given index
249 *
250 */
os_menu(int action,int menu,const zword * text)251 void os_menu(int action, int menu, const zword * text)
252 {
253 /* switch (action)
254 {
255 case MENU_NEW:
256 theWnd->AddNewMenu(menu,text);
257 break;
258 case MENU_ADD:
259 theWnd->AddMenuItem(menu,text);
260 break;
261 case MENU_REMOVE:
262 theWnd->RemoveMenu(menu);
263 break;
264 }*/
265 }
266
267
268 /*
269 * os_random_seed
270 *
271 * Return an appropriate random seed value in the range from 0 to
272 * 32767, possibly by using the current system time.
273 *
274 * this is a provisional workaround (time granularity is at best 1s)
275 */
276 #include <time.h>
os_random_seed(void)277 int os_random_seed(void)
278 {
279 if (m_random_seed == -1) {
280 return ((int)(time(NULL))) & 32767;
281 }
282 return m_random_seed;
283 }
284
285
286 /* The following assumes Unicode */
287 /*
288 * os_scrollback_char
289 *
290 * Write a character to the scrollback buffer.
291 *
292 */
os_scrollback_char(zword c)293 void os_scrollback_char(zword c)
294 {
295 if (option_scrollback_buffer == 0)
296 return;
297 if (c == 13)
298 c = 10;
299 if (option_scrollback_buffer == 1) { /* Latin-1 */
300 if (c > 255)
301 c = '?';
302 putchar(c);
303 } else { /* UTF8 */
304 if (c < 0x80)
305 putchar(c);
306 else {
307 putchar(0xc0 + (c >> 6));
308 putchar(0x80 + (c & 0x3f));
309 }
310 }
311 }
312
313
314 /*
315 * os_scrollback_erase
316 *
317 * Remove characters from the scrollback buffer.
318 *
319 */
os_scrollback_erase(int erase)320 void os_scrollback_erase(int erase)
321 {
322 if (option_scrollback_buffer)
323 while (erase--)
324 putchar(8);
325 }
326
327
328 /*
329 * os_restart_game
330 *
331 * This routine allows the interface to interfere with the process of
332 * restarting a game at various stages:
333 *
334 * RESTART_BEGIN - restart has just begun
335 * RESTART_WPROP_SET - window properties have been initialised
336 * RESTART_END - restart is complete
337 *
338 */
os_restart_game(int stage)339 void os_restart_game(int stage)
340 {
341 /* Show Beyond Zork's title screen */
342 if ((stage == RESTART_END) && (story_id == BEYOND_ZORK)) {
343 int w, h;
344 if (os_picture_data(1, &h, &w)) {
345 sf_fillrect(0, 0, 0, 10000, 10000);
346 os_draw_picture(1, 1, 1);
347 sf_read_key(0, false, false, false);
348 }
349 }
350 }
351
352
353 #define DEFAULT_GAMMA 2.2
354 double m_gamma = DEFAULT_GAMMA;
355
sf_initcolours()356 void sf_initcolours()
357 {
358 int i;
359
360 sf_setgamma(m_gamma);
361
362 /* Standard Z-Machine colours */
363 m_colours[0] = RGB5ToTrue(0x0000); /* black */
364 m_colours[1] = RGB5ToTrue(0x001D); /* red */
365 m_colours[2] = RGB5ToTrue(0x0340); /* green */
366 m_colours[3] = RGB5ToTrue(0x03BD); /* yellow */
367 m_colours[4] = RGB5ToTrue(0x59A0); /* blue */
368 m_colours[5] = RGB5ToTrue(0x7C1F); /* magenta */
369 m_colours[6] = RGB5ToTrue(0x77A0); /* cyan */
370 m_colours[7] = RGB5ToTrue(0x7FFF); /* white */
371 m_colours[8] = RGB5ToTrue(0x5AD6); /* light grey */
372 m_colours[9] = RGB5ToTrue(0x4631); /* medium grey */
373 m_colours[10] = RGB5ToTrue(0x2D6B); /* dark grey */
374
375 for (i = 0; i < NON_STD_COLS; i++)
376 m_nonStdColours[i] = 0xFFFFFFFF;
377 m_nonStdIndex = 0;
378
379 }
380
381
382 /* Read in settings */
sf_readsettings(void)383 void sf_readsettings(void)
384 {
385 char *p;
386
387 sf_InitProfile(m_setupfile);
388
389 m_aafonts = sf_GetProfileInt("Fonts", "antialias", 0);
390 m_fontdir = sf_GetProfileString("Fonts", "fontdir", NULL);
391
392 m_fontfiles[0] = sf_GetProfileString("Fonts", "textroman", NULL);
393 m_fontfiles[1] = sf_GetProfileString("Fonts", "textbold", NULL);
394 m_fontfiles[2] = sf_GetProfileString("Fonts", "textitalic", NULL);
395 m_fontfiles[3] = sf_GetProfileString("Fonts", "textbolditalic", NULL);
396 m_fontfiles[4] = sf_GetProfileString("Fonts", "fixedroman", NULL);
397 m_fontfiles[5] = sf_GetProfileString("Fonts", "fixedbold", NULL);
398 m_fontfiles[6] = sf_GetProfileString("Fonts", "fixeditalic", NULL);
399 m_fontfiles[7] = sf_GetProfileString("Fonts", "fixedbolditalic", NULL);
400 m_fontfiles[8] = sf_GetProfileString("Fonts", "graphics", NULL);
401
402 ResDir = sf_GetProfileString("Resources", "Dir", ResDir);
403 ResPict = sf_GetProfileString("Resources", "Pict", ResPict);
404 ResSnd = sf_GetProfileString("Resources", "Snd", ResSnd);
405
406 if (f_setup.interpreter_number == 0) {
407 z_header.interpreter_number =
408 sf_GetProfileInt("Interpreter", "Number", INTERP_AMIGA);
409 } else
410 z_header.interpreter_number = f_setup.interpreter_number;
411
412 f_setup.err_report_mode =
413 sf_GetProfileInt("Interpreter", "Error Reporting", ERR_REPORT_ONCE);
414 f_setup.ignore_errors =
415 sf_GetProfileInt("Interpreter", "Ignore Errors", 0);
416 f_setup.expand_abbreviations =
417 sf_GetProfileInt("Interpreter", "Expand Abbreviations", 0);
418 m_tandy =
419 sf_GetProfileInt("Interpreter", "Tandy Bit", 0) ? true : false;
420 f_setup.script_cols =
421 sf_GetProfileInt("Interpreter", "Wrap Script Lines", 1) ? 80 : 0;
422
423 if ((p = sf_GetProfileString("Interpreter", "SaveNames", NULL)))
424 m_names_format = p[0];
425
426 AcWidth = sf_GetProfileInt("Window", "AcWidth", AcWidth);
427 AcHeight = sf_GetProfileInt("Window", "AcHeight", AcHeight);
428
429 m_frequency = sf_GetProfileInt("Audio", "Frequency", m_frequency);
430 m_v6scale = sf_GetProfileInt("Display", "Infocom V6 Scaling", 2);
431 m_gfxScale_w = 1.0;
432 m_gfxScale_h = 1.0;
433 m_defaultFore = (sf_GetProfileInt("Display", "Foreground", 0xffffff));
434 m_defaultBack = (sf_GetProfileInt("Display", "Background", 0x800000));
435 m_morePrompts =
436 sf_GetProfileInt("Display", "Show More Prompts", 1) ? true : false;
437 m_gamma = sf_GetProfileDouble("Display", "Gamma", DEFAULT_GAMMA);
438 sf_initcolours();
439
440 /* If the leading character of m_fontdir is not PATH_SEPARATOR,
441 * we should look in $HOME for our font files and directories.
442 */
443 if (m_fontdir != NULL && m_fontdir[0] != PATH_SEPARATOR) {
444 char *m_fontdir_temp;
445 char *myhome;
446 size_t fontdir_len, homedir_len;
447
448 myhome = strdup(getenv(HOMEDIR));
449
450 fontdir_len = strlen(m_fontdir);
451 homedir_len = strlen(myhome);
452
453 m_fontdir_temp = malloc(((fontdir_len + homedir_len) * sizeof(char)) + 3);
454
455 if ((m_fontdir[0] == '~') && (m_fontdir[1] == PATH_SEPARATOR))
456 snprintf(m_fontdir_temp, fontdir_len + homedir_len + 3, "%s%c%s", myhome, PATH_SEPARATOR, m_fontdir+2);
457 else
458 snprintf(m_fontdir_temp, fontdir_len + homedir_len + 3, "%s%c%s", myhome, PATH_SEPARATOR, m_fontdir);
459
460 free(m_fontdir);
461 m_fontdir = strdup(m_fontdir_temp);
462 }
463
464 sf_FinishProfile();
465 }
466
467
468 /* Get a colour */
sf_GetColour(int colour)469 ulong sf_GetColour(int colour)
470 {
471 /* Standard colours */
472 if ((colour >= BLACK_COLOUR) && (colour <= DARKGREY_COLOUR))
473 return m_colours[colour - BLACK_COLOUR];
474
475 /* Default colours */
476 if (colour == 16)
477 return m_defaultFore;
478 if (colour == 17)
479 return m_defaultBack;
480
481 /* Non standard colours */
482 if ((colour >= 18) && (colour < 256)) {
483 if (m_nonStdColours[colour - 18] != 0xFFFFFFFF)
484 return m_nonStdColours[colour - 18];
485 }
486 return m_colours[0];
487 }
488
489
490 /* Get a default colour */
sf_GetDefaultColour(bool fore)491 ulong sf_GetDefaultColour(bool fore)
492 {
493 if (m_IsInfocomV6)
494 return sf_GetColour(fore ? WHITE_COLOUR : BLACK_COLOUR);
495 return fore ? m_defaultFore : m_defaultBack;
496 }
497
498
499 /* Get an index for a non-standard colour */
sf_GetColourIndex(ulong colour)500 int sf_GetColourIndex(ulong colour)
501 {
502 int i, index = -1;
503 /* Is this a standard colour? */
504 for (i = 0; i < 11; i++) {
505 if (m_colours[i] == colour)
506 return i + BLACK_COLOUR;
507 }
508
509 /* Is this a default colour? */
510 if (m_defaultFore == colour)
511 return 16;
512 if (m_defaultBack == colour)
513 return 17;
514
515 /* Is this colour already in the table? */
516 for (i = 0; i < NON_STD_COLS; i++) {
517 if (m_nonStdColours[i] == colour)
518 return i + 18;
519 }
520
521 /* Find a free colour index */
522 while (index == -1) {
523 if (colour_in_use(m_nonStdIndex + 18) == 0) {
524 m_nonStdColours[m_nonStdIndex] = colour;
525 index = m_nonStdIndex + 18;
526 }
527
528 m_nonStdIndex++;
529 if (m_nonStdIndex >= NON_STD_COLS)
530 m_nonStdIndex = 0;
531 }
532 return index;
533 }
534
535
536 /*
537 * os_set_colour
538 *
539 * Set the foreground and background colours which can be:
540 *
541 * 1
542 * BLACK_COLOUR
543 * RED_COLOUR
544 * GREEN_COLOUR
545 * YELLOW_COLOUR
546 * BLUE_COLOUR
547 * MAGENTA_COLOUR
548 * CYAN_COLOUR
549 * WHITE_COLOUR
550 * TRANSPARENT_COLOUR
551 *
552 * Amiga only:
553 *
554 * LIGHTGREY_COLOUR
555 * MEDIUMGREY_COLOUR
556 * DARKGREY_COLOUR
557 *
558 * There may be more colours in the range from 16 to 255; see the
559 * remarks about os_peek_colour.
560 *
561 */
os_set_colour(int new_foreground,int new_background)562 void os_set_colour(int new_foreground, int new_background)
563 {
564 SF_textsetting *ts = sf_curtextsetting();
565 sf_flushtext();
566 if (new_foreground == 1)
567 ts->fore = sf_GetDefaultColour(true);
568 else if (new_foreground < 256)
569 ts->fore = sf_GetColour(new_foreground);
570 ts->foreDefault = (new_foreground == 1);
571
572 if (new_background == 1)
573 ts->back = sf_GetDefaultColour(false);
574 else if (new_background < 256)
575 ts->back = sf_GetColour(new_background);
576 ts->backDefault = (new_background == 1);
577 ts->backTransparent = (new_background == 15);
578 }
579
580
581 /*
582 * os_from_true_cursor
583 *
584 * Given a true colour, return an appropriate colour index.
585 *
586 */
os_from_true_colour(zword colour)587 int os_from_true_colour(zword colour)
588 {
589 return sf_GetColourIndex(RGB5ToTrue(colour));
590 }
591
592
593 /*
594 * os_to_true_cursor
595 *
596 * Given a colour index, return the appropriate true colour.
597 *
598 */
os_to_true_colour(int index)599 zword os_to_true_colour(int index)
600 {
601 return TrueToRGB5(sf_GetColour(index));
602 }
603
604
605 /*
606 * os_init_screen
607 *
608 * Initialise the IO interface. Prepare screen and other devices
609 * (mouse, sound card). Set various OS depending story file header
610 * entries:
611 *
612 * z_header.config (aka flags 1)
613 * z_header.flags (aka flags 2)
614 * z_header.screen_cols (aka screen width in characters)
615 * z_header.screen_rows (aka screen height in lines)
616 * z_header.screen_width
617 * z_header.screen_height
618 * z_header.font_height (defaults to 1)
619 * z_header.font_width (defaults to 1)
620 * z_header.default_foreground
621 * z_header.default_background
622 * z_header.interpreter_number
623 * z_header.interpreter_version
624 * z_header.user_name (optional; not used by any game)
625 *
626 * Finally, set reserve_mem to the amount of memory (in bytes) that
627 * should not be used for multiple undo and reserved for later use.
628 *
629 */
os_init_screen(void)630 void os_init_screen(void)
631 {
632
633 sf_initvideo(AcWidth, AcHeight, (m_fullscreen != -1));
634 sf_load_resources();
635
636 /* Set the graphics scaling */
637 if (sf_IsInfocomV6() || (story_id == BEYOND_ZORK)) {
638 m_gfxScale_w = (double)m_v6scale;
639 m_gfxScale_h = (double)m_v6scale;
640 } else {
641 m_gfxScale_w = 1.0;
642 m_gfxScale_h = 1.0;
643 }
644 m_gfxScale_w *= (double) AcWidth /640.0;
645 m_gfxScale_h *= (double) AcHeight /400.0;
646
647 /* Set the configuration */
648 if (z_header.version == V3) {
649 z_header.config |= CONFIG_SPLITSCREEN;
650 z_header.config |= CONFIG_PROPORTIONAL;
651 if (m_tandy)
652 z_header.config |= CONFIG_TANDY;
653 else
654 z_header.config &= ~CONFIG_TANDY;
655 }
656 if (z_header.version >= V4) {
657 z_header.config |= CONFIG_BOLDFACE;
658 z_header.config |= CONFIG_EMPHASIS;
659 z_header.config |= CONFIG_FIXED;
660 z_header.config |= CONFIG_TIMEDINPUT;
661 }
662 if (z_header.version >= V5)
663 z_header.config |= CONFIG_COLOUR;
664 if (z_header.version == V6) {
665 if (blorb_map) {
666 z_header.config |= CONFIG_PICTURES;
667 z_header.config |= CONFIG_SOUND;
668 }
669 }
670
671 z_header.interpreter_version = 'F';
672 if (z_header.version == V6) {
673 z_header.default_foreground =
674 sf_GetColourIndex(sf_GetDefaultColour(true));
675 z_header.default_background =
676 sf_GetColourIndex(sf_GetDefaultColour(false));
677 } else {
678 z_header.default_foreground = 1;
679 z_header.default_background = 1;
680 }
681
682 os_set_font(FIXED_WIDTH_FONT);
683 os_set_text_style(0);
684
685 {
686 int H, W;
687 os_font_data(FIXED_WIDTH_FONT, &H, &W);
688 z_header.font_width = (zbyte) W;
689 z_header.font_height = (zbyte) H;
690 }
691
692 z_header.screen_width = (zword) AcWidth;
693 z_header.screen_height = (zword) AcHeight;
694 z_header.screen_cols = (zbyte) (z_header.screen_width / z_header.font_width);
695 z_header.screen_rows = (zbyte) (z_header.screen_height / z_header.font_height);
696
697 /* Check for sound */
698 if ((z_header.version == V3) && (z_header.flags & OLD_SOUND_FLAG)) {
699 if (((blorb_map == NULL) && (m_localfiles == 0))
700 || (!sf_initsound()))
701 z_header.flags &= ~OLD_SOUND_FLAG;
702 } else if ((z_header.version >= V4) && (z_header.flags & SOUND_FLAG)) {
703 if (((blorb_map == NULL) && (m_localfiles == 0))
704 || (!sf_initsound()))
705 z_header.flags &= ~SOUND_FLAG;
706 }
707
708 if (z_header.version >= V5) {
709 zword mask = 0;
710 if (z_header.version == V6)
711 mask |= TRANSPARENT_FLAG;
712
713 /* Mask out any unsupported bits in the extended flags */
714 hx_flags &= mask;
715
716 hx_fore_colour = TrueToRGB5(sf_GetDefaultColour(true));
717 hx_back_colour = TrueToRGB5(sf_GetDefaultColour(false));
718 }
719 }
720
721
722 /*
723 * os_fatal
724 *
725 * Display error message and stop interpreter.
726 *
727 */
os_fatal(const char * s,...)728 void os_fatal(const char *s, ...)
729 {
730 va_list m;
731
732 fprintf(stderr, "\n%s: ", sf_msgstring(IDS_FATAL));
733 va_start(m, s);
734 vfprintf(stderr, s, m);
735 va_end(m);
736 fprintf(stderr, "\n");
737
738 if (sdl_active) {
739 os_set_text_style(0);
740 os_display_string((zchar *) "\n\n");
741 os_beep(BEEP_HIGH);
742 os_set_text_style(BOLDFACE_STYLE);
743
744 os_display_string((zchar *) "Fatal error: ");
745 os_set_text_style(0);
746 os_display_string((zchar *) s);
747 os_display_string((zchar *) "\n\n");
748 new_line();
749 flush_buffer();
750 }
751
752 if (f_setup.ignore_errors) {
753 if (sdl_active) {
754 os_display_string((zchar *) "Continuing anyway...");
755 new_line();
756 new_line();
757 }
758 fprintf(stderr, "Continuing anyway...\n");
759 return;
760 }
761
762 if (sdl_active) {
763 os_reset_screen();
764 SDL_Quit();
765 }
766 sf_cleanup_all();
767 exit(EXIT_FAILURE);
768 }
769
770
771 /* If true, running one of Infocom's V6 games */
sf_IsInfocomV6()772 bool sf_IsInfocomV6()
773 {
774 switch (story_id) {
775 case ARTHUR:
776 case JOURNEY:
777 case SHOGUN:
778 case ZORK_ZERO:
779 return true;
780 default:
781 break;
782 }
783 return false;
784 }
785
786
787 /* If true, this picture has an adaptive palette */
sf_IsAdaptive(int picture)788 bool sf_IsAdaptive(int picture)
789 {
790 bb_result_t result;
791 bool adaptive = FALSE;
792
793 if (blorb_map == NULL) return FALSE;
794
795 if (bb_load_chunk_by_type
796 (blorb_map, bb_method_Memory, &result, bb_ID_APal, 0) == bb_err_None) {
797 for (int i = 0; i < (int)result.length; i += 4) {
798 unsigned char *data =
799 ((unsigned char *)result.data.ptr) + i;
800 int entry =
801 (data[0] << 24) | (data[1] << 16) | (data[2] << 8) |
802 data[3];
803 if (picture == entry) {
804 adaptive = TRUE;
805 break;
806 }
807 }
808 }
809 bb_unload_chunk(blorb_map, result.chunknum);
810 return adaptive;
811 }
812
813
814 #define LOCAL_MEM -1
815 #define LOCAL_FILE -2
816
sf_freeresource(myresource * res)817 void sf_freeresource(myresource * res)
818 {
819 int cnu;
820
821 if (!res)
822 return;
823
824 cnu = res->bbres.chunknum;
825
826 if (cnu == LOCAL_MEM) {
827 if (res->bbres.data.ptr)
828 free(res->bbres.data.ptr);
829 return;
830 }
831 if (cnu == LOCAL_FILE) {
832 if (res->file)
833 fclose(res->file);
834 return;
835 }
836
837 if ((blorb_map) && (cnu >= 0))
838 bb_unload_chunk(blorb_map, cnu);
839 }
840
841
findlocal(int ispic,int num,int * size)842 static FILE *findlocal(int ispic, int num, int *size)
843 {
844 FILE *f;
845 char *tpl, buf[MAX_FILE_NAME + 1];
846
847 tpl = ispic ? ResPict : ResSnd;
848 strcpy(buf, ResDir);
849 sprintf(buf + strlen(buf), tpl, num);
850 f = fopen(buf, "rb");
851 if (!f)
852 return f;
853 fseek(f, 0, SEEK_END);
854 *size = ftell(f);
855 fseek(f, 0, SEEK_SET);
856 return f;
857 }
858
859
860 static FILE *findfromlist(int ispic, int num, int *size);
861
862
loadlocal(int num,int ispic,int method,myresource * res)863 static int loadlocal(int num, int ispic, int method, myresource * res)
864 {
865 FILE *f;
866 int size;
867 byte hd[4];
868
869 f = findlocal(ispic, num, &size);
870 if (!f)
871 f = findfromlist(ispic, num, &size);
872 if (!f)
873 return bb_err_NotFound;
874
875 fread(hd, 1, 4, f);
876 fseek(f, 0, SEEK_SET);
877 res->type = 0;
878 if (ispic) {
879 if (hd[0] == 0xff && hd[1] == 0xd8)
880 res->type = bb_ID_JPEG;
881 else if (hd[0] == 0x89 && hd[1] == 0x50)
882 res->type = bb_ID_PNG;
883 } else {
884 if (memcmp(hd, "FORM", 4) == 0)
885 res->type = bb_ID_FORM;
886 else if (memcmp(hd, "OggS", 4) == 0)
887 res->type = bb_ID_OGGV;
888 else
889 res->type = bb_ID_MOD;
890 }
891 if (!res->type) {
892 fclose(f);
893 return bb_err_NotFound;
894 }
895
896 res->bbres.data.startpos = 0;
897 res->file = f;
898 res->bbres.length = size;
899 if (method == bb_method_FilePos)
900 res->bbres.chunknum = LOCAL_FILE;
901 else {
902 void *ptr;
903 res->bbres.chunknum = LOCAL_MEM;
904 ptr = res->bbres.data.ptr = malloc(size);
905 if (ptr)
906 fread(ptr, 1, size, f);
907 fclose(f);
908 if (!ptr)
909 return bb_err_NotFound;
910 }
911
912 return bb_err_None;
913 }
914
915
sf_getresource(int num,int ispic,int method,myresource * res)916 int sf_getresource(int num, int ispic, int method, myresource * res)
917 {
918 int st;
919 ulong usage;
920
921 res->bbres.data.ptr = NULL;
922 res->file = NULL;
923
924 if (m_localfiles)
925 if ((st = loadlocal(num, ispic, method, res)) == bb_err_None)
926 return st;
927
928 if (!blorb_map)
929 return bb_err_NotFound;
930
931 if (ispic)
932 usage = bb_ID_Pict;
933 else
934 usage = bb_ID_Snd;
935 /* XXX Should use bb_load_resource_{pict,snd} with auxdata? */
936 st = bb_load_resource(blorb_map, method, (bb_result_t *) res, usage, num);
937 if (st == bb_err_None) {
938 res->type = blorb_map->chunks[res->bbres.chunknum].type;
939 if (method == bb_method_FilePos)
940 res->file = blorb_fp;
941 }
942 return st;
943 }
944
945 /***************/
946
947 typedef struct {
948 void *next;
949 int num, ispic;
950 ulong type;
951 char *name;
952 } LLENTRY;
953
954 static LLENTRY *Lpics = NULL, *Lsnds = NULL;
955
956 static int numlocal = 0, numlocalpic = 0, numlocalsnd = 0;
957 static int p_ispic, p_num;
958 static ulong p_type;
959 static char *p_name;
960
961
cleanLLENTRY(LLENTRY * e)962 static void cleanLLENTRY(LLENTRY * e)
963 {
964 while (e) {
965 LLENTRY *n = e->next;
966 if (e->name)
967 free(e->name);
968 free(e);
969 e = n;
970 }
971 }
972
973
cleanlocallist()974 static void cleanlocallist()
975 {
976 cleanLLENTRY(Lpics);
977 Lpics = NULL;
978 cleanLLENTRY(Lsnds);
979 Lsnds = NULL;
980 }
981
982
parseline(char * s)983 static int parseline(char *s)
984 {
985 char *p, p3;
986 int n;
987 p = strtok(s, " \t\n");
988 if (!p)
989 return 0;
990 if (strcmp(p, "Pict") == 0)
991 p_ispic = 1;
992 else if (strcmp(p, "Snd") == 0)
993 p_ispic = 0;
994 else
995 return -1;
996 p = strtok(NULL, " \t\n");
997 if (!p)
998 return -1;
999 p_num = atoi(p);
1000 p = strtok(NULL, " \t\n");
1001 if (!p)
1002 return -1;
1003 n = strlen(p);
1004 if (n < 3)
1005 return -1;
1006 if (p[3])
1007 p3 = p[3];
1008 else
1009 p3 = ' ';
1010 p_type = bb_make_id(p[0], p[1], p[2], p3);
1011 p = strtok(NULL, " \t\n");
1012 if (!p)
1013 return -1;
1014 p_name = p;
1015 return 1;
1016 }
1017
1018
load_local_resources(void)1019 static void load_local_resources(void)
1020 {
1021 FILE *f;
1022 LLENTRY *e;
1023 char s[256];
1024 int st;
1025 f = fopen(m_reslist_file, "r");
1026 if (!f)
1027 return;
1028 CLEANREG(cleanlocallist);
1029 for (;;) {
1030 fgets(s, 254, f);
1031 if (feof(f))
1032 break;
1033 st = parseline(s);
1034 if (st < 1)
1035 continue;
1036 e = calloc(1, sizeof(LLENTRY));
1037 if (e) {
1038 e->num = p_num;
1039 e->ispic = p_ispic;
1040 e->type = p_type;
1041 e->name = strdup(p_name);
1042 if (p_ispic) {
1043 e->next = Lpics;
1044 Lpics = e;
1045 numlocalpic++;
1046 if (p_num > maxlegalpic)
1047 maxlegalpic = p_num;
1048 } else {
1049 e->next = Lsnds;
1050 Lsnds = e;
1051 numlocalsnd++;
1052 }
1053 }
1054 }
1055 numlocal = numlocalpic + numlocalsnd;
1056 if (numlocal)
1057 m_localfiles = 1;
1058 fclose(f);
1059 }
1060
1061
findfromlist(int ispic,int num,int * size)1062 static FILE *findfromlist(int ispic, int num, int *size)
1063 {
1064 FILE *f;
1065 LLENTRY *l;
1066 char buf[MAX_FILE_NAME + 1];
1067
1068 if (ispic)
1069 l = Lpics;
1070 else
1071 l = Lsnds;
1072 while (l) {
1073 if (l->num == num)
1074 break;
1075 l = l->next;
1076 }
1077 if (!l)
1078 return NULL;
1079
1080 strcpy(buf, ResDir);
1081 strcat(buf, l->name);
1082
1083 f = fopen(buf, "rb");
1084 if (!f)
1085 return f;
1086 fseek(f, 0, SEEK_END);
1087 *size = ftell(f);
1088 fseek(f, 0, SEEK_SET);
1089 return f;
1090 }
1091
1092
os_init_setup(void)1093 void os_init_setup(void)
1094 {
1095 sf_setdialog();
1096 sf_initloader();
1097 sdl_active = FALSE;
1098 }
1099