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