1 /*
2 * sf_util.c - SDL interface, startup 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 #include <time.h>
28
29 #include <libgen.h>
30
31 #include <SDL.h>
32 #include <zlib.h>
33
34 #ifdef __WIN32__
35 #include <io.h>
36 #endif
37
38 #ifdef UNIX
39 #include <unistd.h>
40 #endif
41
42 extern f_setup_t f_setup;
43
44 typedef void (*CLEANFUNC)();
45
46 typedef struct cfstruct cfrec;
47
48 static void print_version(void);
49
50 enum { USAGE_NORMAL, USAGE_EXTENDED };
51
52 struct cfstruct {
53 CLEANFUNC func;
54 cfrec *next;
55 const char *name;
56 };
57
58 static cfrec *cflist = NULL;
59
60 static int getcolor(char *);
61
62
sf_regcleanfunc(void * f,const char * p)63 void sf_regcleanfunc(void *f, const char *p)
64 {
65 cfrec *n = calloc(1, sizeof(cfrec));
66 if (n) {
67 if (!p)
68 p = "";
69 n->func = (CLEANFUNC) f;
70 n->name = p;
71 n->next = cflist;
72 cflist = n;
73 }
74 }
75
76
sf_cleanup_all()77 void sf_cleanup_all()
78 {
79 while (cflist) {
80 cfrec *n = cflist->next;
81 if (cflist->func)
82 cflist->func();
83 free(cflist);
84 cflist = n;
85 }
86 }
87
88
89 /*
90 * os_reset_screen
91 *
92 * Reset the screen before the program ends.
93 *
94 */
os_reset_screen(void)95 void os_reset_screen(void)
96 {
97 sf_flushdisplay();
98
99 if (m_exitPause) {
100 const char *hit = sf_msgstring(IDS_HIT_KEY_EXIT);
101 os_set_font(TEXT_FONT);
102 os_set_text_style(0);
103 screen_new_line();
104
105 while (*hit)
106 os_display_char((*hit++));
107 sf_read_key(0, true, false, false);
108 }
109
110 sf_cleanup_all();
111 }
112 /*
113 * os_quit
114 *
115 * Immediately and cleanly exit, passing along exit status.
116 *
117 */
os_quit(int status)118 void os_quit(int status)
119 {
120 exit(status);
121 } /* os_quit */
122
123 int user_background = -1;
124 int user_foreground = -1;
125 int user_emphasis = -1;
126 int user_bold_typing = -1;
127 int user_reverse_bg = -1;
128 int user_reverse_fg = -1;
129 int user_screen_height = -1;
130 int user_screen_width = -1;
131 int user_tandy_bit = -1;
132 int user_font = 1;
133 int m_random_seed = -1;
134 int m_fullscreen = -1;
135 int m_reqW = 0, m_reqH = 0;
136 int m_vga_fonts = 0;
137 extern char *m_setupfile;
138 extern char m_names_format;
139 static char user_names_format = 0;
140 extern char *m_reslist_file;
141 extern int option_scrollback_buffer;
142
143 static char *info_header =
144 "FROTZ V%s - SDL graphics and audio interface.\n"
145 "An interpreter for all Infocom and other Z-Machine games.\n\n"
146 "Syntax: sfrotz [options] story-file\n";
147
148 static char *info[] = {
149 "-a watch attribute setting",
150 "-A watch attribute testing",
151 "-b <colourname> background colour",
152 "-c # context lines",
153 "-f <colorname> foreground colour",
154 "-F fullscreen mode",
155 "-H # screen height",
156 "-i ignore runtime errors",
157 "-I # interpreter number",
158 "-l # left margin",
159 "-L <file> load this save file",
160 "-o watch object movement",
161 "-O watch object locating",
162 "-P alter piracy opcode",
163 "-q quiet (disable sound)",
164 "-r # right margin",
165 "-s # random number seed value",
166 "-S # transcript width",
167 "-t set Tandy bit",
168 "-u # slots for multiple undo",
169 "-W # screen width",
170 "-x expand abbreviations g/x/z",
171 "-X show extended options",
172 "-v show version information",
173 "-Z # error checking (see below)",
174 NULL
175 };
176
177 static char *info_footer =
178 "\nError checking: 0 none, 1 first only (default), 2 all, 3 exit after any error.";
179
180 static char *extended_header = "\nExtended Options\n";
181
182 static char *extended_options[] = {
183 "-@ <file> use resources in <file>",
184 "-% use local resources",
185 "-F run in fullscreen mode.",
186 "-m # set timer interrupt cycle to # milliseconds",
187 "-N <mode> add date or number to save filename",
188 "-T use in-game requests for filenames",
189 "-V force VGA fonts",
190 NULL
191 };
192
193 static char *footer =
194 "More options and information are in the manual page. Type \"man sfrotz\".\n";
195
196
197 #define WIDCOL 40
usage(int type)198 static void usage(int type)
199 {
200 char **p;
201 int i = 0, len = 0;
202
203 printf(info_header, VERSION);
204
205 if (type == USAGE_NORMAL)
206 p = info;
207 else {
208 p = extended_options;
209 puts(extended_header);
210 }
211
212 while (*p) {
213 if (i && type == USAGE_NORMAL) {
214 while (len > 0) {
215 fputc(' ', stdout);
216 len--;
217 }
218 puts(*p);
219 } else {
220 fputs(" ", stdout);
221 fputs(*p, stdout);
222 if (type == USAGE_NORMAL)
223 len = WIDCOL - strlen(*p) - 2;
224 else
225 fputs("\n", stdout);
226 }
227 i = 1 - i;
228 p++;
229 }
230 if (i)
231 fputc('\n', stdout);
232
233 if (type == USAGE_NORMAL)
234 puts(info_footer);
235 puts(footer);
236
237 }
238
239
240 /*
241 * parse_options
242 *
243 * Parse program options and set global flags accordingly.
244 *
245 */
246
247 static const char *progname = NULL;
248
249 /*
250 extern char *optarg;
251 extern int optind;
252 */
253
254 extern int m_timerinterval;
255
256 static char *options = "@:%aAb:B:c:f:FH:iI:l:L:m:N:oOPqr:s:S:tTu:vVW:xXZ:";
257
limit(int v,int m,int M)258 static int limit(int v, int m, int M)
259 {
260 if (v < m)
261 return m;
262 if (v > M)
263 return M;
264 return v;
265 }
266
267
parse_options(int argc,char ** argv)268 static void parse_options(int argc, char **argv)
269 {
270 int c;
271
272 do {
273 int num = 0, copt = 0;;
274
275 c = zgetopt(argc, argv, options);
276
277 if (zoptarg != NULL) {
278 num = atoi(zoptarg);
279 copt = zoptarg[0];
280 }
281
282 if (c == '%')
283 m_localfiles = true;
284 if (c == 'a')
285 f_setup.attribute_assignment = 1;
286 if (c == 'A')
287 f_setup.attribute_testing = 1;
288 if (c == 'b')
289 user_background = getcolor(zoptarg);
290 if (c == 'B')
291 option_scrollback_buffer = num;
292 if (c == 'c')
293 f_setup.context_lines = num;
294 if (c == 'm')
295 m_timerinterval = limit(num, 10, 1000000);
296 if (c == 'N')
297 user_names_format = copt;
298 if (c == '@')
299 m_reslist_file = zoptarg;
300 if (c == 'f')
301 user_foreground = getcolor(zoptarg);
302 if (c == 'F')
303 m_fullscreen = 1;
304 if (c == 'H')
305 user_screen_height = num;
306 if (c == 'i')
307 f_setup.ignore_errors = 1;
308 if (c == 'I')
309 f_setup.interpreter_number = num;
310 if (c == 'l')
311 f_setup.left_margin = num;
312 if (c == 'L') {
313 f_setup.restore_mode = TRUE;
314 f_setup.tmp_save_name = strdup(zoptarg);
315 }
316 if (c == 'q')
317 m_no_sound = 1;
318 if (c == 'o')
319 f_setup.object_movement = 1;
320 if (c == 'O')
321 f_setup.object_locating = 1;
322 if (c == 'P')
323 f_setup.piracy = 1;
324 if (c == 'r')
325 f_setup.right_margin = num;
326 if (c == 's')
327 m_random_seed = num;
328 if (c == 'S')
329 f_setup.script_cols = num;
330 if (c == 't')
331 user_tandy_bit = 1;
332 if (c == 'T')
333 sf_osdialog = NULL;
334 if (c == 'u')
335 f_setup.undo_slots = num;
336 if (c == 'v')
337 print_version();
338 if (c == 'V')
339 m_vga_fonts = 1;
340 if (c == 'W')
341 user_screen_width = num;
342 if (c == 'x')
343 f_setup.expand_abbreviations = 1;
344 if (c == 'X') {
345 usage(USAGE_EXTENDED);
346 os_quit(EXIT_SUCCESS);
347 }
348 if (c == 'Z')
349 if (num >= ERR_REPORT_NEVER && num <= ERR_REPORT_FATAL)
350 f_setup.err_report_mode = num;
351 if (c == '?')
352 zoptind = argc;
353 } while (c != EOF && c != '?');
354
355 } /* parse_options */
356
357
print_version(void)358 static void print_version(void)
359 {
360 printf("FROTZ V%s\tSDL interface.\n", VERSION);
361 printf("Commit date:\t%s\n", GIT_DATE);
362 printf("Git commit:\t%s\n", GIT_HASH);
363 printf(" Frotz was originally written by Stefan Jokisch.\n");
364 printf
365 (" It complies with standard 1.0 of Graham Nelson's specification.\n");
366 printf(" It was ported to Unix by Galen Hazelwood.\n");
367 printf
368 (" The core and SDL port are maintained by David Griffith.\n");
369 printf(" Frotz's homepage is https://661.org/proj/if/frotz/\n\n");
370 os_quit(EXIT_SUCCESS);
371 }
372
373
374 /**
375 * Like dirname except well defined.
376 * Does not modify path. Always returns a new string (caller must free).
377 */
new_dirname(const char * path)378 static char *new_dirname(const char *path)
379 {
380 char *p = strdup(path), *p2 = strdup(dirname(p));
381 free(p);
382 return p2;
383 }
384
385
386 /**
387 * Like basename except well defined.
388 * Does not modify path. Always returns a new string (caller must free).
389 */
new_basename(const char * path)390 static char *new_basename(const char *path)
391 {
392 char *p = strdup(path), *p2 = strdup(basename(p));
393 free(p);
394 return p2;
395 }
396
397
398 /*
399 * os_process_arguments
400 *
401 * Handle command line switches.
402 * Some variables may be set to activate special features of Frotz.
403 *
404 */
os_process_arguments(int argc,char * argv[])405 void os_process_arguments(int argc, char *argv[])
406 {
407 char *p;
408
409 zoptarg = NULL;
410
411 sf_installhandlers();
412 sf_readsettings();
413 parse_options(argc, argv);
414
415 if (argv[zoptind] == NULL) {
416 usage(USAGE_NORMAL);
417 os_quit(EXIT_SUCCESS);
418 }
419 f_setup.story_file = strdup(argv[zoptind]);
420
421 if (argv[zoptind + 1] != NULL)
422 f_setup.blorb_file = argv[zoptind + 1];
423
424 /* Strip path and extension off the story file name */
425 f_setup.story_name = new_basename(f_setup.story_file);
426
427 /* Now strip off the extension. */
428 p = strrchr(f_setup.story_name, '.');
429 if ((p != NULL) &&
430 ((strcmp(p, EXT_BLORB2) == 0) ||
431 (strcmp(p, EXT_BLORB3) == 0) || (strcmp(p, EXT_BLORB4) == 0))) {
432 /* blorb_ext = strdup(p); */
433 } else
434 /* blorb_ext = strdup(EXT_BLORB); */
435
436 /* Get rid of extensions with 1 to 6 character extensions. */
437 /* This will take care of an extension like ".zblorb". */
438 /* More than that, there might be something weird going on */
439 /* which is not our concern. */
440 if (p != NULL) {
441 if (strlen(p) >= 2 && strlen(p) <= 7) {
442 *p = '\0'; /* extension removed */
443 }
444 }
445 f_setup.story_path = new_dirname(argv[zoptind]);
446
447 /* Create nice default file names */
448 f_setup.script_name =
449 malloc((strlen(f_setup.story_name) +
450 strlen(EXT_SCRIPT)) * sizeof(char) + 1);
451 memcpy(f_setup.script_name, f_setup.story_name, strlen(f_setup.story_name) * sizeof(char));
452 strncat(f_setup.script_name, EXT_SCRIPT, strlen(EXT_SCRIPT) + 1);
453
454 f_setup.command_name =
455 malloc((strlen(f_setup.story_name) +
456 strlen(EXT_COMMAND)) * sizeof(char) + 1);
457 memcpy(f_setup.command_name, f_setup.story_name, strlen(f_setup.story_name) * sizeof(char));
458 strncat(f_setup.command_name, EXT_COMMAND, strlen(EXT_COMMAND) + 1);
459
460 if (!f_setup.restore_mode) {
461 f_setup.save_name =
462 malloc((strlen(f_setup.story_name) +
463 strlen(EXT_SAVE)) * sizeof(char) + 1);
464 memcpy(f_setup.save_name, f_setup.story_name, strlen(f_setup.story_name) * sizeof(char));
465 strncat(f_setup.save_name, EXT_SAVE, strlen(EXT_SAVE) + 1);
466 } else { /* Set our auto load save as the name_save */
467 f_setup.save_name =
468 malloc((strlen(f_setup.tmp_save_name) +
469 strlen(EXT_SAVE)) * sizeof(char) + 1);
470 memcpy(f_setup.save_name, f_setup.tmp_save_name, strlen(f_setup.tmp_save_name) * sizeof(char));
471 free(f_setup.tmp_save_name);
472 }
473
474 f_setup.aux_name =
475 malloc((strlen(f_setup.story_name) +
476 strlen(EXT_AUX)) * sizeof(char) + 1);
477 memcpy(f_setup.aux_name, f_setup.story_name, strlen(f_setup.story_name) * sizeof(char));
478 strncat(f_setup.aux_name, EXT_AUX, strlen(EXT_AUX) + 1);
479
480 /* Save the executable file name */
481 progname = argv[0];
482
483 if (user_screen_width > 0)
484 AcWidth = user_screen_width;
485 if (user_screen_height > 0)
486 AcHeight = user_screen_height;
487
488 if (user_names_format)
489 m_names_format = user_names_format;
490
491 if (user_background != -1)
492 m_defaultBack = sf_GetColour(user_background);
493 if (user_foreground != -1)
494 m_defaultFore = sf_GetColour(user_foreground);
495 if (user_tandy_bit != -1)
496 m_tandy = user_tandy_bit;
497
498 sf_initfonts();
499 } /* os_process_arguments */
500
501
502 #ifdef WIN32
503 #include <windows.h>
504 #else
505 #include <time.h>
506 #include <sys/time.h>
507 #endif
508
sf_sleep(int msecs)509 void sf_sleep(int msecs)
510 {
511 SDL_Delay(msecs);
512 }
513
514 #ifdef WIN32
sf_ticks(void)515 unsigned long sf_ticks(void)
516 {
517 return (GetTickCount());
518 }
519 #else
sf_ticks(void)520 unsigned long sf_ticks(void)
521 {
522 struct timeval now;
523 static struct timeval start;
524 static int started = 0;
525 unsigned long ticks;
526 now.tv_sec = now.tv_usec = 0;
527 gettimeofday(&now, NULL);
528 if (!started) {
529 started = 1;
530 start = now;
531 }
532 ticks =
533 (now.tv_sec - start.tv_sec) * 1000 + (now.tv_usec -
534 start.tv_usec) / 1000;
535 return ticks;
536 }
537 #endif
538
539
getextension(int flag)540 static char *getextension(int flag)
541 {
542 char *ext = EXT_AUX;
543
544 if (flag == FILE_SAVE || flag == FILE_RESTORE)
545 ext = EXT_SAVE;
546 else if (flag == FILE_SCRIPT)
547 ext = EXT_SCRIPT;
548 else if (flag == FILE_RECORD || flag == FILE_PLAYBACK)
549 ext = EXT_COMMAND;
550
551 return ext;
552 }
553
554
newfile(int flag)555 static bool newfile(int flag)
556 {
557 if (flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD)
558 return true;
559 return false;
560 }
561
562
563 static char buf[FILENAME_MAX];
564
getnumbername(const char * def,char * ext)565 static const char *getnumbername(const char *def, char *ext)
566 {
567 int len, number = 0;
568 strcpy(buf, f_setup.story_name);
569 len = strlen(buf);
570 for (;;) {
571 sprintf(buf + len, "%03d%s", number++, ext);
572 if (access(buf, F_OK))
573 break;
574 }
575 return buf;
576 }
577
578
getdatename(const char * def,char * ext)579 static const char *getdatename(const char *def, char *ext)
580 {
581 int len;
582
583 time_t t;
584 struct tm *tm;
585 time(&t);
586 tm = localtime(&t);
587
588 strcpy(buf, f_setup.story_name);
589 len = strlen(buf);
590 sprintf(buf + len, "%04d%02d%02d%02d%02d%s",
591 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
592 tm->tm_hour, tm->tm_min, ext);
593 return buf;
594 }
595
596
597 static int ingame_read_file_name(char *file_name, const char *default_name,
598 int flag);
599 static int dialog_read_file_name(char *file_name, const char *default_name,
600 int flag);
601
602 /*
603 * os_read_file_name
604 *
605 * Return the name of a file. Flag can be one of:
606 *
607 * FILE_SAVE - Save game file
608 * FILE_RESTORE - Restore game file
609 * FILE_SCRIPT - Transscript file
610 * FILE_RECORD - Command file for recording
611 * FILE_PLAYBACK - Command file for playback
612 * FILE_SAVE_AUX - Save auxilary ("preferred settings") file
613 * FILE_LOAD_AUX - Load auxilary ("preferred settings") file
614 *
615 * The length of the file name is limited by MAX_FILE_NAME. Ideally
616 * an interpreter should open a file requester to ask for the file
617 * name. If it is unable to do that then this function should call
618 * print_string and read_string to ask for a file name.
619 *
620 * Return value is NULL is there was a problem
621 */
os_read_file_name(const char * default_name,int flag)622 char *os_read_file_name(const char *default_name, int flag)
623 {
624 int st;
625 const char *initname = default_name;
626 char file_name[FILENAME_MAX + 1];
627
628 if (newfile(flag)) {
629 char *ext = getextension(flag);
630 if (m_names_format == 'd')
631 initname = getdatename(initname, ext);
632 else if (m_names_format == 'n')
633 initname = getnumbername(initname, ext);
634 }
635
636 /* If we're restoring a game before the interpreter starts,
637 * and our filename is already provided with the -L flag,
638 * just go ahead silently.
639 */
640 if (f_setup.restore_mode) {
641 strncpy(file_name, f_setup.save_name, FILENAME_MAX);
642 } else {
643 st = dialog_read_file_name(file_name, initname, flag);
644 if (st == SF_NOTIMP)
645 st = ingame_read_file_name(file_name, initname, flag);
646
647 if (!st)
648 return NULL;
649 }
650
651 return strdup(file_name);
652 }
653
654
ingame_read_file_name(char * file_name,const char * default_name,int flag)655 static int ingame_read_file_name(char *file_name, const char *default_name,
656 int flag)
657 {
658 char *extension;
659 FILE *fp;
660 bool terminal;
661 bool result;
662
663 bool saved_replay = istream_replay;
664 bool saved_record = ostream_record;
665
666 /* Turn off playback and recording temporarily */
667 istream_replay = FALSE;
668 ostream_record = FALSE;
669
670 /* Select appropriate extension */
671 extension = getextension(flag);
672
673 /* Input file name (reserve four bytes for a file name extension) */
674 print_string("Enter file name (\"");
675 print_string(extension);
676 print_string("\" will be added).\nDefault is \"");
677 print_string(default_name);
678 print_string("\": ");
679
680 #ifdef USE_UTF8
681 {
682 zchar z_name[FILENAME_MAX + 1];
683 zchar *zp;
684 int i = 0;
685 read_string(FILENAME_MAX - 4, z_name);
686 zp = z_name;
687 while (*zp) {
688 if (*zp <= 0x7f) {
689 if (i > FILENAME_MAX - 4)
690 break;
691 file_name[i++] = *zp;
692 } else if (*zp > 0x7ff) {
693 if (i > FILENAME_MAX - 6)
694 break;
695 file_name[i++] = 0xe0 | ((*zp >> 12) & 0xf);
696 file_name[i++] = 0x80 | ((*zp >> 6) & 0x3f);
697 file_name[i++] = 0x80 | (*zp & 0x3f);
698 } else {
699 if (i > FILENAME_MAX - 5)
700 break;
701 file_name[i++] = 0xc0 | ((*zp >> 6) & 0x1f);
702 file_name[i++] = 0x80 | (*zp & 0x3f);
703 }
704 zp++;
705 }
706 file_name[i] = 0;
707 }
708 #else
709 read_string(MAX_FILE_NAME - 4, (zchar *) file_name);
710 #endif
711
712 /* Use the default name if nothing was typed */
713 if (file_name[0] == 0)
714 strcpy(file_name, default_name);
715 if (strchr(file_name, '.') == NULL)
716 strcat(file_name, extension);
717
718 /* Make sure it is safe to use this file name */
719 result = TRUE;
720
721 /* OK if the file is opened for reading */
722 if (!newfile(flag))
723 goto finished;
724
725 /* OK if the file does not exist */
726 if ((fp = fopen(file_name, "rb")) == NULL)
727 goto finished;
728
729 /* OK if this is a pseudo-file (like PRN, CON, NUL) */
730 terminal = isatty(fileno(fp));
731
732 fclose(fp);
733
734 if (terminal)
735 goto finished;
736
737 /* OK if user wants to overwrite */
738 result = read_yes_or_no("Overwrite existing file");
739
740 finished:
741
742 /* Restore state of playback and recording */
743 istream_replay = saved_replay;
744 ostream_record = saved_record;
745
746 return result;
747 } /* os_read_file_name */
748
749
dialog_read_file_name(char * file_name,const char * default_name,int flag)750 static int dialog_read_file_name(char *file_name, const char *default_name,
751 int flag)
752 {
753 int filter = 0;
754 int title = 0, st;
755 char *res;
756
757 sf_flushdisplay();
758
759 switch (flag) {
760 case FILE_SAVE:
761 filter = IDS_SAVE_FILTER;
762 title = IDS_SAVE_TITLE;
763 break;
764 case FILE_RESTORE:
765 filter = IDS_SAVE_FILTER;
766 title = IDS_RESTORE_TITLE;
767 break;
768 case FILE_SCRIPT:
769 filter = IDS_SCRIPT_FILTER;
770 title = IDS_SCRIPT_TITLE;
771 break;
772 case FILE_RECORD:
773 filter = IDS_RECORD_FILTER;
774 title = IDS_RECORD_TITLE;
775 break;
776 case FILE_PLAYBACK:
777 filter = IDS_RECORD_FILTER;
778 title = IDS_PLAYBACK_TITLE;
779 break;
780 case FILE_SAVE_AUX:
781 filter = IDS_AUX_FILTER;
782 title = IDS_SAVE_AUX_TITLE;
783 break;
784 case FILE_LOAD_AUX:
785 filter = IDS_AUX_FILTER;
786 title = IDS_LOAD_AUX_TITLE;
787 break;
788 default:
789 return 0;
790 }
791
792 st = sf_user_fdialog(!newfile(flag), default_name, sf_msgstring(filter),
793 sf_msgstring(title), &res);
794 if (st == SF_NOTIMP)
795 return st;
796 if (st == 0) {
797 strncpy(file_name, res, MAX_FILE_NAME);
798 file_name[MAX_FILE_NAME - 1] = 0;
799 return 1;
800 }
801 return 0;
802 }
803
804 static char *rc = NULL;
805
sf_FinishProfile()806 void sf_FinishProfile()
807 {
808 if (!rc)
809 return;
810 free(rc);
811 rc = NULL;
812 }
813
814
sf_InitProfile(const char * fn)815 void sf_InitProfile(const char *fn)
816 {
817 FILE *f;
818 int size;
819 char *s, *d;
820 char *my_fn;
821 char *homedir;
822 int len;
823
824 if (!fn)
825 return;
826
827 homedir = strdup(getenv(HOMEDIR));
828 len = ((strlen(homedir) + strlen(fn) + 1) * sizeof(char)) + 1;
829 my_fn = malloc(len);
830 snprintf(my_fn, len, "%s/%s", homedir, fn);
831
832 f = fopen(fn, "rb");
833 if (!f) {
834 f = fopen(my_fn, "rb");
835 if (!f)
836 return;
837 }
838 fseek(f, 0, SEEK_END);
839 size = ftell(f);
840 if (!size) {
841 fclose(f);
842 return;
843 }
844 rc = malloc(size + 1);
845 if (!rc) {
846 fclose(f);
847 return;
848 }
849 fseek(f, 0, 0);
850 fread(rc, 1, size, f);
851 fclose(f);
852 rc[size] = 0;
853
854 s = d = rc;
855
856 while (*s) {
857 if (*s == '#') {
858 while ((*s) && (*s != '\n'))
859 s++;
860 if (!*s)
861 break;
862 } else
863 *d++ = *s++;
864 }
865 *d = 0;
866
867 CLEANREG(sf_FinishProfile);
868 }
869
870
findsect(const char * sect)871 static char *findsect(const char *sect)
872 {
873 int ns = strlen(sect);
874 char *r = rc;
875 while (r) {
876 r = strchr(r, '[');
877 if (!r)
878 return NULL;
879 r++;
880 if (strncmp(r, sect, ns))
881 continue;
882 return (r + ns);
883 }
884 return NULL;
885 }
886
887
findid(const char * sect,const char * id)888 static char *findid(const char *sect, const char *id)
889 {
890 int nid = strlen(id);
891 char *r, *sav, *rq, *fnd = NULL;
892 r = findsect(sect);
893 if (!r)
894 return NULL;
895 sav = strchr(r, '[');
896 if (sav)
897 *sav = 0;
898 while (r) {
899 r = strstr(r, id);
900 if (!r)
901 break;
902 rq = r + nid;
903 if ((*(byte *) (r - 1) <= ' ')
904 && ((*rq == ' ') || (*rq == '='))) {
905 while (*rq)
906 if (*rq++ == '=')
907 break;
908 if (*rq) {
909 fnd = rq;
910 break;
911 }
912 }
913 r = rq;
914 }
915 if (sav)
916 *sav = '[';
917 return fnd;
918 }
919
920
sf_GetProfileInt(const char * sect,const char * id,int def)921 int sf_GetProfileInt(const char *sect, const char *id, int def)
922 {
923 if (rc) {
924 char *p = findid(sect, id);
925 if (p)
926 def = atoi(p);
927 }
928 return def;
929 }
930
931
sf_GetProfileDouble(const char * sect,const char * id,double def)932 double sf_GetProfileDouble(const char *sect, const char *id, double def)
933 {
934 if (rc) {
935 char *p = findid(sect, id);
936 if (p)
937 def = atof(p);
938 }
939 return def;
940 }
941
942
sf_GetProfileString(const char * sect,const char * id,char * def)943 char *sf_GetProfileString(const char *sect, const char *id, char *def)
944 {
945 char *q = NULL, sav = 0;
946 if (rc) {
947 char *p = findid(sect, id);
948 if (p) {
949 int quoted = 0;
950 for (; *p; p++) {
951 if (*p == '\"') {
952 quoted = 1;
953 p++;
954 break;
955 }
956 if ((byte) (*p) > ' ')
957 break;
958 }
959 if (*p) {
960 if (quoted)
961 q = strchr(p, '\"');
962 if (!q) {
963 q = p;
964 while (*q > ' ')
965 q++;
966 sav = *q;
967 *q = 0;
968 }
969 }
970 def = p;
971 }
972 }
973 if (def)
974 def = strdup(def);
975 if (sav)
976 *q = sav;
977 return def;
978 }
979
980
981 /* A. Local file header:
982 *
983 * local file header signature 0 4 bytes (0x04034b50)
984 * version needed to extract 4 2 bytes
985 * general purpose bit flag 6 2 bytes
986 * compression method 8 2 bytes
987 * last mod file time 10 2 bytes
988 * last mod file date 12 2 bytes
989 * crc-32 14 4 bytes
990 * compressed size 18 4 bytes
991 * uncompressed size 22 4 bytes
992 * file name length 26 2 bytes
993 * extra field length 28 2 bytes
994 *
995 * file name (variable size)
996 * extra field (variable size)
997 */
998
999 #define plong( b) (((int)((b)[3]) << 24) + ((int)((b)[2]) << 16) +\
1000 ((int)((b)[1]) << 8) + (int)((b)[0]))
1001
1002 #define pshort( b) (((int)((b)[1]) << 8) + (int)((b)[0]))
1003
1004
myin(void * d,byte ** b)1005 static unsigned myin(void *d, byte ** b)
1006 {
1007 return 0;
1008 }
1009
1010
myout(void * udata,byte * b,unsigned n)1011 static int myout(void *udata, byte * b, unsigned n)
1012 {
1013 memmove(udata, b, n);
1014 udata += n;
1015 return 0;
1016 }
1017
1018
myunzip(int csize,byte * cdata,byte * udata)1019 static int myunzip(int csize, byte * cdata, byte * udata)
1020 {
1021 byte window[32768];
1022 z_stream z;
1023 int st;
1024
1025 memset(&z, 0, sizeof(z));
1026
1027 st = inflateBackInit(&z, 15, window);
1028 if (st)
1029 return st;
1030
1031 z.next_in = cdata;
1032 z.avail_in = csize;
1033
1034 for (;;) {
1035 st = inflateBack(&z, myin, NULL, myout, udata);
1036 if (st == Z_STREAM_END)
1037 break;
1038 if (st)
1039 return st;
1040 }
1041
1042 st = inflateBackEnd(&z);
1043 return st;
1044 }
1045
1046
sf_pkread(FILE * f,int foffs,void ** out,int * size)1047 int sf_pkread(FILE * f, int foffs, void **out, int *size)
1048 {
1049 byte hd[30];
1050 byte *data, *cdata;
1051 int csize, usize, cmet, skip, st;
1052
1053 fseek(f, foffs, SEEK_SET);
1054 fread(hd, 1, 30, f);
1055 cmet = pshort(hd + 8);
1056 if (cmet != 8)
1057 return -10;
1058 csize = plong(hd + 18);
1059 usize = plong(hd + 22);
1060 if (csize <= 0)
1061 return -11;
1062 if (usize <= 0)
1063 return -12;
1064 data = malloc(usize);
1065 if (!data)
1066 return -13;
1067 cdata = malloc(csize);
1068 if (!cdata) {
1069 free(data);
1070 return -14;
1071 }
1072 skip = pshort(hd + 26) + pshort(hd + 28);
1073 fseek(f, foffs + 30 + skip, SEEK_SET);
1074 fread(cdata, 1, csize, f);
1075
1076 st = myunzip(csize, cdata, data);
1077
1078 free(cdata);
1079 if (st) {
1080 free(data);
1081 return st;
1082 }
1083 *out = (void *)data;
1084 *size = usize;
1085 return st;
1086 }
1087
1088
1089 /*
1090 * getcolor
1091 *
1092 * Figure out what color this string might indicate and returns an integer
1093 * corresponding to the color macros defined in frotz.h.
1094 *
1095 */
getcolor(char * value)1096 static int getcolor(char *value)
1097 {
1098 int num;
1099
1100 /* Be case-insensitive */
1101 for (num = 0; value[num] !=0; num++)
1102 value[num] = tolower((int) value[num]);
1103
1104 if (strcmp(value, "black") == 0)
1105 return BLACK_COLOUR;
1106 if (strcmp(value, "red") == 0)
1107 return RED_COLOUR;
1108 if (strcmp(value, "green") == 0)
1109 return GREEN_COLOUR;
1110 if (strcmp(value, "blue") == 0)
1111 return BLUE_COLOUR;
1112 if (strcmp(value, "magenta") == 0)
1113 return MAGENTA_COLOUR;
1114 if (strcmp(value, "cyan") == 0)
1115 return CYAN_COLOUR;
1116 if (strcmp(value, "white") == 0)
1117 return WHITE_COLOUR;
1118 if (strcmp(value, "yellow") == 0)
1119 return YELLOW_COLOUR;
1120
1121 if (strcmp(value, "purple") == 0)
1122 return MAGENTA_COLOUR;
1123 if (strcmp(value, "violet") == 0)
1124 return MAGENTA_COLOUR;
1125 if (strcmp(value, "aqua") == 0)
1126 return CYAN_COLOUR;
1127
1128 /* If we can't tell what that string means,
1129 * we tell caller to use the default.
1130 */
1131
1132 return -1;
1133 } /* getcolor */
1134
1135
1136 /************************/
1137
1138 #include <stdio.h>
1139 #include <stdlib.h>
1140 #include <string.h>
1141 #include <time.h>
1142 #include <sys/stat.h>
1143 #ifdef WIN32
1144 #include <windows.h>
1145 #else
1146 #include <unistd.h>
1147 #endif
1148
1149 #ifndef WIN32
1150 #define _stat stat
1151 #endif
1152