1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "glk/agt/agility.h"
24 #include "glk/agt/interp.h"
25 
26 namespace Glk {
27 namespace AGT {
28 
29 /* This module contains a miscellany of things that are somewhat */
30 /* system dependent but not enough so to justify being put in */
31 /* OS_<whatever>.c */
32 /* --writestr() and writeln().*/
33 /* --Hooks for sound, pictures, and fonts.  */
34 /* --yesno() and wait_return() */
35 /* --Some lower level file stuff */
36 /* --main() and command line parseing stuff */
37 
38 #ifndef REPLACE_BNW
39 
40 /* #define DEBUG_BELLS_AND_WHISTLES */
41 
42 /* Warning for fontcmd, pictcmd, musiccmd:
43   These all extract filenames from fontlist, pictlist, pixlist, songlist.
44  Any of these are allowed to be NULL and this should be checked
45  before accessing them.  */
46 
47 #ifdef DEBUG_BELLS_AND_WHISTLES
bnw_report(char * cmdstr,filename * list,int index)48 void bnw_report(char *cmdstr, filename *list, int index) {
49 	writeln("");
50 	writestr(">** ");
51 	writestr(cmdstr);
52 	writestr(" ");
53 	if (list != NULL) {
54 		writestr(list[index]);
55 		writestr(" ");
56 	}
57 	writeln("**<");
58 }
59 #endif /* DEBUG_BELLS_AND_WHISTLES */
60 
fontcmd(int cmd,int font)61 void fontcmd(int cmd, int font)
62 /* 0=Load font, name is fontlist[font]
63    1=Restore original (pre-startup) font
64    2=Set startup font. (<gamename>.FNT)
65 */
66 {
67 #ifdef DEBUG_BELLS_AND_WHISTLES
68 	if (cmd == 0) bnw_report("Loading Font", fontlist, font);
69 	else if (cmd == 1) bnw_report("Restoring original font", NULL, 0);
70 #endif
71 	return;
72 }
73 
pictcmd(int cmd,int pict)74 void pictcmd(int cmd, int pict)
75 /* 1=show global picture, name is pictlist[pict]
76    2=show room picture, name is pixlist[pict]
77    3=show startup picture <gamename>.P..
78   */
79 {
80 #ifdef DEBUG_BELLS_AND_WHISTLES
81 	if (cmd == 1) bnw_report("Showing picture", pictlist, pict);
82 	else if (cmd == 2) bnw_report("Showing pix", pixlist, pict);
83 	agt_waitkey();
84 #endif
85 	return;
86 }
87 
88 
89 
musiccmd(int cmd,int song)90 int musiccmd(int cmd, int song)
91 /* For cmd=1 or 2, the name of the song is songlist[song]
92   The other commands don't take an additional argument.
93    1=play song
94    2=repeat song
95    3=end repeat
96    4=end song
97    5=suspend song
98    6=resume song
99    7=clean-up
100    8=turn sound on
101    9=turn sound off
102    -1=Is a song playing? (0=false, -1=true)
103    -2=Is the sound on?  (0=false, -1=true)
104 */
105 {
106 	if (cmd == 8) sound_on = 1;
107 	else if (cmd == 9) sound_on = 0;
108 #ifdef DEBUG_BELLS_AND_WHISTLES
109 	switch (cmd) {
110 	case 1:
111 		bnw_report("Play song", songlist, song);
112 		break;
113 	case 2:
114 		bnw_report("Repeat song", songlist, song);
115 		break;
116 	case 3:
117 		bnw_report("End repeat", NULL, 0);
118 		break;
119 	case 4:
120 		bnw_report("End song", NULL, 0);
121 		break;
122 	case 5:
123 		bnw_report("Suspend song", NULL, 0);
124 		break;
125 	case 6:
126 		bnw_report("Resume song", NULL, 0);
127 		break;
128 	case 7:
129 		bnw_report("Clean up", NULL, 0);
130 		break;
131 	case 8:
132 		bnw_report("Sound On", NULL, 0);
133 		break;
134 	case 9:
135 		bnw_report("Sound Off", NULL, 0);
136 		break;
137 	case -1:
138 		return yesno("Is song playing?");
139 	case -2:
140 		return 1;
141 	}
142 #endif
143 	return 0;
144 }
145 
146 #endif /* REPLACE_BNW */
147 
148 static char linebuff[100];
149 static int lp;  /* Line pointer */
150 static rbool savenl = 0;
151 static rbool needfill; /* Used for paragraph filling */
152 static rbool quotemode = 0;
153 
debugout(const char * s)154 void debugout(const char *s) {
155 	int i;
156 
157 	if (DEBUG_OUT) {
158 		debugfile->write(s, strlen(s));
159 	} else {
160 		lp = 0;
161 		for (; *s != 0; s++) {
162 			if (curr_x + lp >= screen_width || lp > 80) {
163 				if (lp + curr_x >= screen_width)
164 					lp = screen_width - curr_x - 1;
165 				linebuff[lp] = 0;
166 				agt_puts(linebuff);
167 				agt_newline();
168 				lp = 0;
169 			}
170 			if (*s == '\n') {
171 				linebuff[lp] = 0;
172 				agt_puts(linebuff);
173 				agt_newline();
174 				lp = 0;
175 			} else if (*s == '\t') {
176 				for (i = 0; i < 3; i++) linebuff[lp++] = ' ';
177 			} else if (*s >= 0 && *s <= 9) linebuff[lp++] = ' ';
178 			else linebuff[lp++] = *s;
179 		}
180 		linebuff[lp] = 0;
181 		agt_puts(linebuff);
182 	}
183 }
184 
185 
close_pfile(genfile f,int ft)186 int close_pfile(genfile f, int ft)
187 /* ft=0 for script, 4 for log_in, 5 for log_out */
188 {
189 	delete f;
190 	return 0;
191 }
192 
193 
194 
get_log(void)195 static char *get_log(void)
196 /* Read string from logfile_in */
197 {
198 	char *s;
199 	static int dead_log;
200 
201 	if (!filevalid(log_in, fLOG)) { /* We are finishing up */
202 		if (++dead_log > 100) fatal("Internal error: LOG.");
203 		assert(BATCH_MODE);
204 		s = (char *)rmalloc(2);
205 		s[0] = ' ';
206 		s[1] = 0;
207 		return s;
208 	}
209 
210 	s = (char *)rmalloc(1000);
211 	s[0] = ' ';
212 	s[1] = 0;
213 	(void)textgets(log_in, s, 1000);
214 	if (texteof(log_in)) {  /* Reached end of logfile */
215 		close_pfile(log_in, 1);
216 		log_in = BAD_TEXTFILE;
217 		if (BATCH_MODE) {
218 			writeln("");
219 			writeln("ERROR: Unexpected end of log file.");
220 			agt_quit(); /* This doesn't actually quit; it just sets things
221 			 up so we *will* quit. */
222 			dead_log = 0;
223 		} else {
224 			logflag &= ~2;
225 			fast_replay = 0;
226 		}
227 	} else { /* Need to delay or wait for keypress */
228 		if (logdelay == -1) agt_waitkey();
229 		else agt_delay(logdelay);
230 		if (s[0] != 0) writeln(s);
231 	}
232 	return s;
233 }
234 
235 
put_log(const char * s)236 static void put_log(const char *s)
237 /* Write s to logfile_out */
238 {
239 	textputs(log_out, s);
240 	if (s[strlen(s) - 1] != '\n')
241 		textputs(log_out, "\n");
242 }
243 
244 
agt_readline(int in_type)245 char *agt_readline(int in_type) {
246 	char *s;
247 
248 	if (PURE_INPUT) agt_textcolor(-1);
249 	if (logflag & 2)
250 		s = get_log();
251 	else
252 		s = agt_input(in_type);
253 
254 	if (g_vm->shouldQuit())
255 		return nullptr;
256 
257 	if (PURE_INPUT)
258 		agt_textcolor(-2);
259 
260 	if (logflag & 1)
261 		put_log(s);
262 
263 	return s;
264 }
265 
agt_getchar(void)266 char agt_getchar(void) {
267 	char c, *s, buff[2];
268 
269 	if (PURE_INPUT) agt_textcolor(-1);
270 	if (logflag & 2) {
271 		s = get_log();
272 		c = s[0];
273 		rfree(s);
274 	} else
275 		c = agt_getkey(1);
276 	if (PURE_INPUT) agt_textcolor(-2);
277 	if (logflag & 1) {
278 		buff[0] = c;
279 		buff[1] = 0;
280 		put_log(buff);
281 	}
282 	return c;
283 }
284 
agt_center(rbool b)285 void agt_center(rbool b)
286 /* 1=turn on text centering, 0=turn off */
287 /* At the moment, this is only used for game end messages */
288 /* When it is on, text output with writeln() should be 'centered'; */
289 /* it is up to the interface to decide what that means. */
290 /* writestr() should not be called while centering is on. */
291 {
292 	center_on = b;
293 }
294 
agt_par(rbool b)295 void agt_par(rbool b)
296 /* This has been added for the sake of anyone trying to get this to */
297 /*  work on less-than-80 column screens. My personal opinion is that this */
298 /* is probably hopeless; many AGT games assume 80-column format for   */
299 /* creating tables and ascii graphics. Nevertheless... */
300 /*  Text between an agt_par(1) and an agt_par(0) is logically connected */
301 /* (all part of one description) and so it *might* be possible to reformat */
302 /* it, treating multiple lines as being one paragraph. */
303 /*    At the very least, you should look for blank lines and indentation */
304 /* since a single section of text could contain multiple paragraphs. */
305 /* Sections of text _not_ between an agt_par(1) and an agt_par(0) should */
306 /* be treated as though each line were a new paragraph */
307 {
308 	par_fill_on = b;
309 	if (b == 0 && savenl) agt_newline();
310 	savenl = 0;
311 	needfill = 0;
312 }
313 
314 /* This handles the various format code. They all show up after
315    '\r'; unrecogonized codes are just ignored */
xlat_format_code(uchar c)316 static uchar xlat_format_code(uchar c) {
317 	if (c == 0xFF) {
318 		if (fix_ascii) return trans_ibm[0xFF - 0x80];
319 		else return 0xFF;
320 	}
321 	return 0;
322 }
323 
324 #define FMT_CODE_CNT 15
325 
run_format_code(uchar c)326 static void run_format_code(uchar c) {
327 	if (c < FMT_CODE_CNT)
328 		agt_textcolor(c - 3);
329 }
330 
331 #define format_code(c) ((c>0 && c<=LAST_TEXTCODE)||((uchar)c==FORMAT_CODE))
332 
writestr(const char * s)333 void writestr(const char *s) {
334 	int i, j;
335 	char c;
336 	int endmark, old_x;
337 
338 	if (savenl) {
339 		assert(par_fill_on);
340 		if (!isalnum(s[0])) agt_newline();
341 		else agt_puts(" ");
342 		/* If combining two lines, insert a space between them. */
343 	}
344 	savenl = 0;
345 	i = 0;
346 	lp = 0;
347 
348 	while (s[i] != 0) {
349 		for (; s[i] != 0 && lp < 90 && curr_x + lp < screen_width; i++)
350 			if (s[i] == '\t')
351 				for (j = 0; j < TAB_SIZE && curr_x + lp < screen_width; j++) linebuff[lp++] = ' ';
352 			else if (format_code(s[i])) {
353 				linebuff[lp++] = ' ';    /* Color code */
354 				break;
355 			} else if (s[i] == '\r') { /* New format code */
356 				if (s[i + 1] == 0) continue; /* Bogus format code */
357 				if (((uchar)s[i + 1]) < FMT_CODE_CNT) break;
358 				c = (char)xlat_format_code((uchar)s[++i]);
359 				if (c != 0) linebuff[lp++] = c;
360 			} else if (s[i] == '\n') {
361 				break;
362 			} else linebuff[lp++] = s[i];
363 
364 		linebuff[lp] = 0;
365 
366 		/* Backtrack to last space; in case of formatting codes, we should
367 		 already have one */
368 		endmark = lp;
369 
370 		if (!isspace(s[i]) && !format_code(s[i]) && s[i] != 0) {
371 			/* If we aren't conveniently at a break...*/
372 			do {    /* Find last space */
373 				endmark--;
374 			} while (endmark > 0 && !isspace(linebuff[endmark]));
375 		}
376 
377 		if (endmark == 0 && !isspace(linebuff[endmark])) { /* Can't find a break */
378 			if (curr_x + lp < screen_width) /* Not a line break */
379 				endmark = lp; /* Break at end; it doesn't matter that much */
380 			else  /* We _need_ a line break but are having trouble finding one */
381 				if (curr_x > 0) /* already stuff on this line printed previously */
382 					endmark = 0; /* i.e. print out nothing; move it to next line */
383 				else   /* We have a single word that is longer than our line */
384 					endmark = screen_width; /* Give up */
385 		}
386 
387 		c = linebuff[endmark];
388 		linebuff[endmark] = 0;
389 		old_x = curr_x;
390 
391 		agt_puts(linebuff);
392 
393 		linebuff[endmark] = c;
394 
395 		if (old_x + lp >= screen_width)
396 			/* Need to insert line break and skip any spaces */
397 		{
398 			if (!quotemode) agt_newline();
399 			else return; /* In quote mode, just truncate */
400 
401 			/* Now set up beginning of next line: skip over whitespace */
402 			while (endmark < lp && isspace(linebuff[endmark]))
403 				endmark++;  /* Eliminate EOL whitespace */
404 			if (endmark == lp) {
405 				/* Nothing left; eliminate whitespace at beginning
406 				            of next line */
407 				while (isspace(s[i]) && s[i] != '\r') i++;
408 				lp = endmark = 0;
409 			}
410 			needfill = 1;
411 			if (endmark == lp && s[i] == 0) {
412 				needfill = 2;
413 				return; /* If only spaces left, don't print them */
414 			}
415 		}
416 
417 		/* Now copy remaining text */
418 		for (j = 0; endmark < lp; j++, endmark++) linebuff[j] = linebuff[endmark];
419 		lp = j;
420 
421 		/* Now to deal with format codes */
422 		if ((unsigned char)s[i] == FORMAT_CODE) {
423 			i++;
424 			if (bold_mode) { /* Translate as BOLD toggle */
425 				if (textbold)
426 					agt_textcolor(-2);  /* Turn bold off */
427 				else agt_textcolor(-1); /* Turn bold on */
428 				textbold = !textbold;
429 			} else /* translate as BLACK */
430 				agt_textcolor(0);
431 		} else if (s[i] > 0 && s[i] <= LAST_TEXTCODE)
432 			agt_textcolor(s[i++]);
433 		else if (s[i] == '\r') {
434 			run_format_code((uchar)s[i + 1]);
435 			i += 2;
436 		} else if (s[i] == '\n') {
437 			i += 1;
438 			agt_newline();
439 		}
440 	}
441 }
442 
443 
444 
writeln(const char * s)445 void writeln(const char *s) {
446 	int i, pad;
447 	char *padstr;
448 
449 	if (center_on && (int)strlen(s) + curr_x < screen_width) {
450 		pad = (screen_width - strlen(s)) / 2;
451 		padstr = (char *)rmalloc((pad + 1) * sizeof(char));
452 		for (i = 0; i < pad; i++) padstr[i] = ' ';
453 		padstr[i] = 0;
454 		agt_puts(padstr);
455 		rfree(padstr);
456 	}
457 	writestr(s);
458 	/* needfill=2 if writestr ended with a line that wrapped only
459 	   because of excess spaces (which have been discarded); needfill==1
460 	   if writestr wrapped a line for any reason */
461 	/* If needfill==2, we've already issued a new-line, so don't issue a
462 	   second one. */
463 	/* If needfill==1, set savenl rather than wrapping (writestr will
464 	   then decide to wrap or not depending on whether the next line starts
465 	   with text or nontext), *unless* we are version magx, in which case
466 	   the game author presumably knew what they were doing, so honor their
467 	   wishes. */
468 	if (par_fill_on && needfill == 1)
469 		if (aver >= AGX00) agt_newline();
470 		else savenl = 1;
471 	else if (needfill != 2)
472 		agt_newline();
473 	needfill = 0;
474 }
475 
476 
fixstatchar(uchar c)477 static char fixstatchar(uchar c)
478 /* Eliminate formating characters in the status line */
479 {
480 	if (c == '\t' || c <= LAST_TEXTCODE ||
481 	        (c == FORMAT_CODE) || c == '\r' || c == '\n')
482 		return ' ';
483 	return c;
484 }
485 
print_statline(void)486 void print_statline(void)
487 /* Use strings in l_stat and r_stat */
488 {
489 	int i, j;
490 	char *s, *t;
491 	static rbool lastline = 0; /* Was a non-empty status line printed  last time? */
492 
493 	s = (char *)rmalloc((status_width + 1) * sizeof(char));
494 
495 	/* If both strings empty, don't print the status line */
496 	if (l_stat[0] == 0 && r_stat[0] == 0 && !lastline) return;
497 	lastline = (l_stat[0] || r_stat[0]);
498 
499 	i = status_width - strlen(l_stat) - strlen(r_stat);
500 
501 	j = 0;
502 	if (r_stat[0] == 0) { /* Center the status line */
503 		while (j < i / 2) s[j++] = ' ';
504 		i -= j;
505 	} else if (i > 6) {
506 		s[j++] = ' ';
507 		i -= 2;
508 	}  /* If statline is wide enough, put a
509 					   space on each side */
510 
511 	if ((int)strlen(l_stat) < status_width)  /* Copy left side of status line into s*/
512 		for (t = l_stat; *t != 0; t++) s[j++] = fixstatchar(*t);
513 
514 	for (; i > 0; i--) s[j++] = ' '; /* Insert space between left and right sides */
515 
516 	if (j + (int)strlen(r_stat) <= status_width) /*Copy right side into s */
517 		for (t = r_stat; *t != 0; t++) s[j++] = fixstatchar(*t);
518 
519 	while (j < status_width) s[j++] = ' '; /* Pad any extra width with spaces */
520 	s[j] = 0; /* Put end of string marker */
521 	agt_statline(s); /* Output it */
522 	rfree(s);
523 }
524 
525 
padout(int padleng)526 void padout(int padleng) {
527 	int i;
528 	char *pstr;
529 
530 	if (padleng <= 0) return;
531 	pstr = (char *)rmalloc(padleng + 1);
532 	for (i = 0; i < padleng; i++) pstr[i] = ' ';
533 	pstr[padleng] = 0;
534 	writestr(pstr);
535 	free(pstr);
536 }
537 
textwidth(char * s)538 static int textwidth(char *s) {
539 	int n;
540 
541 	n = 0;
542 	for (; *s != 0; s++) n += (*s == '\t') ? TAB_SIZE : 1;
543 	return n;
544 }
545 
textbox(char * (txt[]),int len,unsigned long flags)546 void textbox(char *(txt[]), int len, unsigned long flags)
547 /* TB_TTL, TB_BOLD, TB_BORDER, TB_CENTER */
548 {
549 	int i, width, padwidth;
550 	int *linewidth;
551 
552 	agt_textcolor(7);
553 	if (flags & TB_BOLD) agt_textcolor(-1);
554 	else agt_textcolor(-2);
555 
556 	linewidth = (int *)rmalloc(len * sizeof(int));
557 
558 	width = 0; /* This contains the maximum width of any line */
559 	for (i = 0; i < len; i++) {
560 		linewidth[i] = textwidth(txt[i]);
561 		if (linewidth[i] > width) width = linewidth[i];
562 	}
563 
564 	agt_makebox(width, len, flags & ~(TB_BOLD | TB_CENTER));
565 	quotemode = 1;  /* So newlines will cause truncation rather than a
566 		   real newline */
567 	for (i = 0; i < len; i++) {
568 		padwidth = width - linewidth[i]; /* Amount of padding we need */
569 		if (flags & TB_CENTER) {
570 			padout(padwidth / 2);
571 			padwidth -= padwidth / 2;
572 		}
573 		writestr(txt[i]);
574 		padout(padwidth);
575 		if (i != len - 1) agt_qnewline();
576 	}
577 	agt_endbox();
578 	quotemode = 0; /* Back to normal */
579 
580 	agt_textcolor(7);
581 	textbold = 0;
582 }
583 
584 
585 #ifndef REPLACE_MENU
586 
agt_menu(const char * header,int size,int width,menuentry * menu)587 int agt_menu(const char *header, int size, int width, menuentry *menu)
588 /* This is _very_ minimal as it stands */
589 {
590 	int i, j;
591 	char sbuff[10];
592 	int numcol, colheight;
593 
594 	if (size == 0) return 0;
595 
596 	width = width + 5;
597 	numcol = screen_width / width;
598 	colheight = size / numcol;
599 	if (size % numcol != 0) colheight++;
600 
601 	writeln(header);
602 	for (i = 0; i < colheight; i++) {
603 		for (j = 0; j < numcol; j++) {
604 			if (j * colheight + i >= size) break;
605 			sprintf(sbuff, "%2d.", j * colheight + i + 1);
606 			writestr(sbuff);
607 			writestr(menu[j * colheight + i]);
608 			if (j < numcol - 1) padout(width - 3 - strlen(menu[j * colheight + i]));
609 		}
610 		writeln("");
611 	}
612 	do {
613 		writestr("Choice:");
614 		i = read_number() - 1;
615 		if (i < 0 || i >= size)
616 			writeln("Please choose an option from the menu.");
617 	} while (i < 0 || i >= size);
618 	return i;
619 }
620 
621 #endif /* REPLACE_MENU */
622 
623 
624 
prompt_out(int n)625 void prompt_out(int n)
626 /* n=1 standard prompt
627    n=2 question prompt */
628 {
629 	agt_textcolor(7);
630 	if (PURE_INPUT && n == 1) agt_textcolor(-1);
631 	if (n == 1) {
632 		agt_newline();
633 		gen_sysmsg(1, ">", MSG_MAIN, NULL);
634 	}
635 	if (n == 2) agt_puts("? ");
636 	agt_textcolor(7);
637 }
638 
agt_waitkey(void)639 void agt_waitkey(void) {
640 	if (BATCH_MODE || fast_replay)
641 		return;
642 	agt_getkey(0);
643 }
644 
645 
wait_return(void)646 void wait_return(void) {
647 	writeln("          --- HIT ANY KEY ---");
648 	agt_waitkey();
649 }
650 
651 
yesno(const char * s)652 rbool yesno(const char *s)
653 /* True for yes, false for no. */
654 {
655 	char c;
656 
657 	writestr(s);
658 	writestr(" ");
659 	c = 'y';
660 	do {
661 		if (c != 'y')
662 			writestr("Please answer <y>es or <n>o. ");
663 		c = tolower(agt_getchar());
664 	} while (c != 'y' && c != 'n' && !quitflag);
665 	return (c == 'y');
666 }
667 
668 
set_test_mode(fc_type fc)669 void set_test_mode(fc_type fc) {
670 	const char *errstr;
671 
672 	log_in = readopen(fc, fLOG, &errstr);
673 
674 	if (make_test) {
675 		if (errstr == NULL)
676 			fatal("Log file already exists.");
677 		log_out = writeopen(fc, fLOG, NULL, &errstr);
678 		if (errstr != NULL)
679 			fatal("Couldn't create log file.");
680 		logflag = 1;
681 		return;
682 	}
683 
684 	logdelay = 0;
685 	if (errstr != NULL)
686 		fatal("Couldn't open log file.");
687 	logflag = 2;
688 
689 	script_on = 1;
690 	scriptfile = writeopen(fc, fSCR, NULL, &errstr);
691 	if (errstr != NULL)
692 		fatal("Couldn't open script file.");
693 }
694 
695 
696 #ifndef REPLACE_GETFILE
697 
698 /* This opens the file refered to by fname and returns it */
uf_open(fc_type fc,filetype ext,rbool rw)699 static genfile uf_open(fc_type fc, filetype ext, rbool rw) {
700 	char *errstr;
701 	genfile f;
702 
703 	if (rw) { /* Check to see if we are overwriting... */
704 		if (fileexist(fc, ext) && ext != fSCR) {
705 			if (!yesno("This file already exists; overwrite?"))
706 				/* That is, DON'T overwrite */
707 				return badfile(ext);
708 		}
709 		f = writeopen(fc, ext, NULL, &errstr);
710 	} else
711 		f = readopen(fc, ext, &errstr);
712 	if (errstr != NULL) writeln(errstr);
713 	return f;
714 }
715 
716 static fc_type last_save = NULL;
717 static fc_type last_log = NULL;
718 static fc_type last_script = NULL;
719 
720 
get_user_file(int ft)721 genfile get_user_file(int ft)
722 /* ft= 0:script, 1:save 2:restore, 3:log(read) 4:log(write)  */
723 /* Should return file in open state, ready to be read or written to,
724    as the case may be */
725 {
726 	/* int extlen;*/
727 	rbool rw;  /* True if writing, false if reading */
728 	filetype ext;
729 	genfile fd;
730 	fc_type def_fc, fc;
731 	char *fname;
732 	char *ftype;
733 	char *p, *q;
734 
735 	switch (ft) {
736 	case 0:
737 		ftype = "script ";
738 		def_fc = last_script;
739 		rw = 1;
740 		ext = fSCR;
741 		break;
742 	case 1:
743 		ftype = "save ";
744 		def_fc = last_save;
745 		rw = 1;
746 		ext = fSAV;
747 		break;
748 	case 2:
749 		ftype = "restore ";
750 		def_fc = last_save;
751 		rw = 0;
752 		ext = fSAV;
753 		break;
754 	case 3:
755 		ftype = "log ";
756 		def_fc = last_log;
757 		rw = 0;
758 		ext = fLOG;
759 		break;
760 	case 4:
761 		ftype = "log ";
762 		def_fc = last_log;
763 		rw = 1;
764 		ext = fLOG;
765 		break;
766 	default:
767 		writeln("<INTERNAL ERROR: invalid file type>");
768 		return badfile(fSAV);
769 	}
770 
771 	writestr(" ");
772 	writestr("Enter ");
773 	if (ftype != NULL) writestr(ftype);
774 	writestr("file name");
775 	if (def_fc != NULL) {
776 		char *s;
777 		s = formal_name(def_fc, ext);
778 		writestr(" (");
779 		writestr(s);
780 		writestr(")");
781 		rfree(s);
782 	}
783 	writestr(": ");
784 
785 	if (PURE_INPUT) agt_textcolor(-1);
786 	fname = agt_input(4);
787 	if (PURE_INPUT) agt_textcolor(-2);
788 
789 	/* Delete whitespace before and after the file name. */
790 	for (p = fname; isspace(*p); p++);
791 	if (*p == 0) { /* Line is all whitespace; use default if there is one */
792 		if (def_fc == NULL) {
793 			writeln("Never mind.");
794 			rfree(fname);
795 			return badfile(ext);
796 		} else {
797 			rfree(fname);
798 			fc = def_fc;
799 		}
800 	} else {   /* Line is _not_ all whitespace: we have a file name */
801 		for (q = fname; *p != 0; p++, q++)
802 			*q = *p;
803 		q--;
804 		while (isspace(*q)) q--;
805 		q++;
806 		*q = 0;
807 		fc = init_file_context(fname, ext);
808 	}
809 
810 	fd = uf_open(fc, ext, rw);
811 
812 	if (!filevalid(fd, ext)) {
813 		if (fc != def_fc) release_file_context(&fc);
814 		return fd;
815 	}
816 
817 	switch (ft) {
818 	case 0:
819 		last_script = fc;
820 		break;
821 	case 1:
822 		last_save = fc;
823 		break;
824 	case 2:
825 		last_save = fc;
826 		break;
827 	case 3:
828 		last_log = fc;
829 		break;
830 	case 4:
831 		last_log = fc;
832 		break;
833 	}
834 	if (fc != def_fc) release_file_context(&def_fc);
835 	return fd;
836 }
837 
838 
set_default_filenames(fc_type fc)839 void set_default_filenames(fc_type fc) {
840 	last_save = convert_file_context(fc, fSAV, NULL);
841 	last_log = convert_file_context(fc, fLOG, NULL);
842 	last_script = convert_file_context(fc, fSCR, NULL);
843 }
844 
845 
846 
847 #endif  /* REPLACE_GETFILE */
848 
849 
850 
851 
script(uchar onp)852 void script(uchar onp) {
853 	if (onp == script_on)
854 		if (onp == 0) writeln("Scripting wasn't on.");
855 		else writeln("Scripting is already on.");
856 	else if (onp == 1) {
857 		scriptfile = get_user_file(0);
858 		if (filevalid(scriptfile, fSCR)) script_on = 1;
859 	} else if (filevalid(scriptfile, fSCR)) {
860 		close_pfile(scriptfile, 0);
861 		scriptfile = BAD_TEXTFILE;
862 		script_on = 0;
863 	}
864 }
865 
866 
logon(void)867 void logon(void) {
868 	if (logflag & 1) {
869 		writeln("Already logging");
870 		return;
871 	}
872 	log_out = get_user_file(4);
873 	if (filevalid(log_out, fLOG))
874 		logflag |= 1;
875 }
876 
replay(int delay)877 void replay(int delay) {
878 	if (logflag & 2) return; /* Nested replays are meaningless */
879 	log_in = get_user_file(3);
880 	if (filevalid(log_in, fLOG)) {
881 		logflag |= 2;
882 		logdelay = delay;
883 	}
884 }
885 
886 
887 /* These two are intended to be called by the platform-dependent
888    interface (e.g. if the user had chosen these from some general purpose
889    menu) */
890 /* They're never called from the rest of the code */
891 
agt_save(void)892 void agt_save(void) {
893 	g_vm->saveGame();
894 }
895 
agt_restore(void)896 void agt_restore(void) {
897 	doing_restore = 1;
898 }
899 
agt_restart(void)900 void agt_restart(void) {
901 	doing_restore = 2;
902 }
903 
agt_quit(void)904 void agt_quit(void) {
905 	doing_restore = 4;
906 }
907 
908 
909 /* This should be rmalloc'd */
910 static fc_type newgame_fc;
911 
new_game(void)912 fc_type new_game(void) {
913 	return newgame_fc;
914 }
915 
agt_newgame(fc_type fc)916 void agt_newgame(fc_type fc) {
917 	newgame_fc = fc;
918 	doing_restore = 3;
919 }
920 
921 #if 0
922 static rbool end_cmd_options;
923 #endif
924 
set_default_options(void)925 void set_default_options(void) {
926 	init_flags();
927 	flag = (rbool *)rmalloc(sizeof(rbool));
928 	debug_parse = 0;
929 	DEBUG_AGT_CMD = 0;
930 	DEBUG_EXEC_VERB = 0;
931 	DEBUG_DISAMBIG = 0;
932 	DEBUG_SMSG = 0;
933 }
934 
helpmsg(void)935 void helpmsg(void) {
936 	/*
937 	  printf(" -i Try to use IBM character set.\n");
938 	  printf(" -1 IRUN Mode: Print messages in first person\n");
939 	  printf(" -h Print out this message\n");
940 	  printf(" -d Debug metacommand execution\n");
941 	  printf(" -t Test mode; see accompanying documentation. Implies -r.\n");
942 	  printf(" -c Create test file.\n");
943 	  printf(" -m Force descriptions to be loaded from disk.\n");
944 	#ifdef OPEN_AS_TEXT
945 	  printf(" -b Open data files as binary files.\n");
946 	#endif
947 	  printf("\nTechnical options (intended for debugging AGiliTy itself).\n");
948 	  printf(" -p Debug parser\n");
949 	  printf(" -x Debug verb execution loop\n");
950 	  printf(" -a Debug disambiguation system\n");
951 	  printf(" -s Debug STANDARD message handler\n");
952 	*/
953 }
954 
955 #if 0
956 static rbool setarg(char **optptr) {
957 	if ((*optptr)[1] == '+') {
958 		(*optptr)++;
959 		return 1;
960 	}
961 	if ((*optptr)[1] == '-') {
962 		(*optptr)++;
963 		return 0;
964 	}
965 	return 1;
966 }
967 #endif
968 
969 #define fixcase(c) tolower(c)
970 
971 #if 0
972 void parse_options(char *opt, char *next) {
973 	/*
974 	if (opt[0]=='-' && opt[1]==0)
975 	{end_cmd_options=1;return;}
976 	for(;*opt!=0;opt++)
977 	switch(fixcase(*opt))
978 	  {
979 	  case 'p': debug_parse=setarg(&opt);break;
980 	  case 'a': DEBUG_DISAMBIG=setarg(&opt);break;
981 	  case 'd': DEBUG_AGT_CMD=setarg(&opt);break;
982 	  case 'x':DEBUG_EXEC_VERB=setarg(&opt);break;
983 	  case 's':DEBUG_SMSG=setarg(&opt);break;
984 	#ifdef MEM_INFO
985 	  case 'M': DEBUG_MEM=setarg(&opt);break;
986 	#endif
987 	  case 'm': descr_maxmem=0; break;
988 	  case 'i': fix_ascii_flag=!setarg(&opt);break;
989 	  case 't': BATCH_MODE=setarg(&opt); break;
990 	  case 'c': make_test=setarg(&opt); break;
991 	  case '1': irun_mode=setarg(&opt);break;
992 	#ifdef OPEN_FILE_AS_TEXT
993 	  case 'b': open_as_binary=setarg(&opt);break;
994 	#endif
995 	  default:printf("Do not recognize option %c\n",*opt);
996 	helpmsg();
997 	exit(EXIT_FAILURE);
998 	  }
999 	*/
1000 }
1001 #endif
1002 
1003 #ifndef REPLACE_MAIN
1004 
main(int argc,char * argv[])1005 int main(int argc, char *argv[]) {
1006 	int i;
1007 	char *gamefile;
1008 
1009 	set_default_options();
1010 	end_cmd_options = 0;
1011 	gamefile = NULL;
1012 	for (i = 1; i < argc; i++)
1013 		if (argv[i][0] == '-' && !end_cmd_options)
1014 			parse_options(argv[i] + 1, argv[i + 1]);
1015 		else if (gamefile == NULL)
1016 			gamefile = argv[i];
1017 		else fatal("Please specify only one game\n");
1018 	if (gamefile == NULL)  {
1019 		helpmsg();
1020 		exit(EXIT_FAILURE);
1021 	}
1022 
1023 	init_interface(argc, argv);
1024 	/* From this point on, MUST use writestr/writeln or may
1025 	   cause problems w/ the interfaces on some platforms
1026 	   that have to keep track of cursor position */
1027 
1028 	run_game(init_file_context(gamefile, fDA1));
1029 	return EXIT_SUCCESS;
1030 }
1031 
1032 #endif /* REPLACE_MAIN */
1033 
1034 } // End of namespace AGT
1035 } // End of namespace Glk
1036