mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-24 05:01:43 +00:00
1037 lines
24 KiB
C++
1037 lines
24 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "glk/agt/agility.h"
|
|
#include "glk/agt/interp.h"
|
|
|
|
namespace Glk {
|
|
namespace AGT {
|
|
|
|
/* This module contains a miscellany of things that are somewhat */
|
|
/* system dependent but not enough so to justify being put in */
|
|
/* OS_<whatever>.c */
|
|
/* --writestr() and writeln().*/
|
|
/* --Hooks for sound, pictures, and fonts. */
|
|
/* --yesno() and wait_return() */
|
|
/* --Some lower level file stuff */
|
|
/* --main() and command line parseing stuff */
|
|
|
|
#ifndef REPLACE_BNW
|
|
|
|
/* #define DEBUG_BELLS_AND_WHISTLES */
|
|
|
|
/* Warning for fontcmd, pictcmd, musiccmd:
|
|
These all extract filenames from fontlist, pictlist, pixlist, songlist.
|
|
Any of these are allowed to be NULL and this should be checked
|
|
before accessing them. */
|
|
|
|
#ifdef DEBUG_BELLS_AND_WHISTLES
|
|
void bnw_report(char *cmdstr, filename *list, int index) {
|
|
writeln("");
|
|
writestr(">** ");
|
|
writestr(cmdstr);
|
|
writestr(" ");
|
|
if (list != NULL) {
|
|
writestr(list[index]);
|
|
writestr(" ");
|
|
}
|
|
writeln("**<");
|
|
}
|
|
#endif /* DEBUG_BELLS_AND_WHISTLES */
|
|
|
|
void fontcmd(int cmd, int font)
|
|
/* 0=Load font, name is fontlist[font]
|
|
1=Restore original (pre-startup) font
|
|
2=Set startup font. (<gamename>.FNT)
|
|
*/
|
|
{
|
|
#ifdef DEBUG_BELLS_AND_WHISTLES
|
|
if (cmd == 0) bnw_report("Loading Font", fontlist, font);
|
|
else if (cmd == 1) bnw_report("Restoring original font", NULL, 0);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void pictcmd(int cmd, int pict)
|
|
/* 1=show global picture, name is pictlist[pict]
|
|
2=show room picture, name is pixlist[pict]
|
|
3=show startup picture <gamename>.P..
|
|
*/
|
|
{
|
|
#ifdef DEBUG_BELLS_AND_WHISTLES
|
|
if (cmd == 1) bnw_report("Showing picture", pictlist, pict);
|
|
else if (cmd == 2) bnw_report("Showing pix", pixlist, pict);
|
|
agt_waitkey();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
int musiccmd(int cmd, int song)
|
|
/* For cmd=1 or 2, the name of the song is songlist[song]
|
|
The other commands don't take an additional argument.
|
|
1=play song
|
|
2=repeat song
|
|
3=end repeat
|
|
4=end song
|
|
5=suspend song
|
|
6=resume song
|
|
7=clean-up
|
|
8=turn sound on
|
|
9=turn sound off
|
|
-1=Is a song playing? (0=false, -1=true)
|
|
-2=Is the sound on? (0=false, -1=true)
|
|
*/
|
|
{
|
|
if (cmd == 8) sound_on = 1;
|
|
else if (cmd == 9) sound_on = 0;
|
|
#ifdef DEBUG_BELLS_AND_WHISTLES
|
|
switch (cmd) {
|
|
case 1:
|
|
bnw_report("Play song", songlist, song);
|
|
break;
|
|
case 2:
|
|
bnw_report("Repeat song", songlist, song);
|
|
break;
|
|
case 3:
|
|
bnw_report("End repeat", NULL, 0);
|
|
break;
|
|
case 4:
|
|
bnw_report("End song", NULL, 0);
|
|
break;
|
|
case 5:
|
|
bnw_report("Suspend song", NULL, 0);
|
|
break;
|
|
case 6:
|
|
bnw_report("Resume song", NULL, 0);
|
|
break;
|
|
case 7:
|
|
bnw_report("Clean up", NULL, 0);
|
|
break;
|
|
case 8:
|
|
bnw_report("Sound On", NULL, 0);
|
|
break;
|
|
case 9:
|
|
bnw_report("Sound Off", NULL, 0);
|
|
break;
|
|
case -1:
|
|
return yesno("Is song playing?");
|
|
case -2:
|
|
return 1;
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
#endif /* REPLACE_BNW */
|
|
|
|
static char linebuff[100];
|
|
static int lp; /* Line pointer */
|
|
static rbool savenl = 0;
|
|
static rbool needfill; /* Used for paragraph filling */
|
|
static rbool quotemode = 0;
|
|
|
|
void debugout(const char *s) {
|
|
int i;
|
|
|
|
if (DEBUG_OUT) {
|
|
debugfile->write(s, strlen(s));
|
|
} else {
|
|
lp = 0;
|
|
for (; *s != 0; s++) {
|
|
if (curr_x + lp >= screen_width || lp > 80) {
|
|
if (lp + curr_x >= screen_width)
|
|
lp = screen_width - curr_x - 1;
|
|
linebuff[lp] = 0;
|
|
agt_puts(linebuff);
|
|
agt_newline();
|
|
lp = 0;
|
|
}
|
|
if (*s == '\n') {
|
|
linebuff[lp] = 0;
|
|
agt_puts(linebuff);
|
|
agt_newline();
|
|
lp = 0;
|
|
} else if (*s == '\t') {
|
|
for (i = 0; i < 3; i++) linebuff[lp++] = ' ';
|
|
} else if (*s >= 0 && *s <= 9) linebuff[lp++] = ' ';
|
|
else linebuff[lp++] = *s;
|
|
}
|
|
linebuff[lp] = 0;
|
|
agt_puts(linebuff);
|
|
}
|
|
}
|
|
|
|
|
|
int close_pfile(genfile f, int ft)
|
|
/* ft=0 for script, 4 for log_in, 5 for log_out */
|
|
{
|
|
delete f;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static char *get_log(void)
|
|
/* Read string from logfile_in */
|
|
{
|
|
char *s;
|
|
static int dead_log;
|
|
|
|
if (!filevalid(log_in, fLOG)) { /* We are finishing up */
|
|
if (++dead_log > 100) fatal("Internal error: LOG.");
|
|
assert(BATCH_MODE);
|
|
s = (char *)rmalloc(2);
|
|
s[0] = ' ';
|
|
s[1] = 0;
|
|
return s;
|
|
}
|
|
|
|
s = (char *)rmalloc(1000);
|
|
s[0] = ' ';
|
|
s[1] = 0;
|
|
(void)readln(log_in, s, 1000);
|
|
if (texteof(log_in)) { /* Reached end of logfile */
|
|
close_pfile(log_in, 1);
|
|
log_in = BAD_TEXTFILE;
|
|
if (BATCH_MODE) {
|
|
writeln("");
|
|
writeln("ERROR: Unexpected end of log file.");
|
|
agt_quit(); /* This doesn't actually quit; it just sets things
|
|
up so we *will* quit. */
|
|
dead_log = 0;
|
|
} else {
|
|
logflag &= ~2;
|
|
fast_replay = 0;
|
|
if (s[0] != 0) writestr(s);
|
|
}
|
|
} else { /* Need to delay or wait for keypress */
|
|
if (logdelay == -1) agt_waitkey();
|
|
else agt_delay(logdelay);
|
|
if (s[0] != 0) writestr(s);
|
|
}
|
|
return s;
|
|
}
|
|
|
|
|
|
static void put_log(const char *s)
|
|
/* Write s to logfile_out */
|
|
{
|
|
textputs(log_out, s);
|
|
if (s[strlen(s) - 1] != '\n')
|
|
textputs(log_out, "\n");
|
|
}
|
|
|
|
|
|
char *agt_readline(int in_type) {
|
|
char *s;
|
|
|
|
if (PURE_INPUT) agt_textcolor(-1);
|
|
if (logflag & 2) {
|
|
s = get_log();
|
|
agt_newline();
|
|
} else
|
|
s = agt_input(in_type);
|
|
|
|
if (g_vm->shouldQuit())
|
|
return nullptr;
|
|
|
|
if (PURE_INPUT)
|
|
agt_textcolor(-2);
|
|
|
|
if (logflag & 1)
|
|
put_log(s);
|
|
|
|
return s;
|
|
}
|
|
|
|
char agt_getchar(void) {
|
|
char c, *s, buff[2];
|
|
|
|
if (PURE_INPUT) agt_textcolor(-1);
|
|
if (logflag & 2) {
|
|
s = get_log();
|
|
c = s[0];
|
|
rfree(s);
|
|
} else
|
|
c = agt_getkey(1);
|
|
if (PURE_INPUT) agt_textcolor(-2);
|
|
if (logflag & 1) {
|
|
buff[0] = c;
|
|
buff[1] = 0;
|
|
put_log(buff);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void agt_center(rbool b)
|
|
/* 1=turn on text centering, 0=turn off */
|
|
/* At the moment, this is only used for game end messages */
|
|
/* When it is on, text output with writeln() should be 'centered'; */
|
|
/* it is up to the interface to decide what that means. */
|
|
/* writestr() should not be called while centering is on. */
|
|
{
|
|
center_on = b;
|
|
}
|
|
|
|
void agt_par(rbool b)
|
|
/* This has been added for the sake of anyone trying to get this to */
|
|
/* work on less-than-80 column screens. My personal opinion is that this */
|
|
/* is probably hopeless; many AGT games assume 80-column format for */
|
|
/* creating tables and ascii graphics. Nevertheless... */
|
|
/* Text between an agt_par(1) and an agt_par(0) is logically connected */
|
|
/* (all part of one description) and so it *might* be possible to reformat */
|
|
/* it, treating multiple lines as being one paragraph. */
|
|
/* At the very least, you should look for blank lines and indentation */
|
|
/* since a single section of text could contain multiple paragraphs. */
|
|
/* Sections of text _not_ between an agt_par(1) and an agt_par(0) should */
|
|
/* be treated as though each line were a new paragraph */
|
|
{
|
|
par_fill_on = b;
|
|
if (b == 0 && savenl) agt_newline();
|
|
savenl = 0;
|
|
needfill = 0;
|
|
}
|
|
|
|
/* This handles the various format code. They all show up after
|
|
'\r'; unrecogonized codes are just ignored */
|
|
static uchar xlat_format_code(uchar c) {
|
|
if (c == 0xFF) {
|
|
if (fix_ascii) return trans_ibm[0xFF - 0x80];
|
|
else return 0xFF;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define FMT_CODE_CNT 15
|
|
|
|
static void run_format_code(uchar c) {
|
|
if (c < FMT_CODE_CNT)
|
|
agt_textcolor(c - 3);
|
|
}
|
|
|
|
#define format_code(c) ((c>0 && c<=LAST_TEXTCODE)||((uchar)c==FORMAT_CODE))
|
|
|
|
void writestr(const char *s) {
|
|
int i, j;
|
|
char c;
|
|
int endmark, old_x;
|
|
|
|
if (savenl) {
|
|
assert(par_fill_on);
|
|
if (!isalnum(s[0])) agt_newline();
|
|
else agt_puts(" ");
|
|
/* If combining two lines, insert a space between them. */
|
|
}
|
|
savenl = 0;
|
|
i = 0;
|
|
lp = 0;
|
|
|
|
while (s[i] != 0) {
|
|
for (; s[i] != 0 && lp < 90 && curr_x + lp < screen_width; i++)
|
|
if (s[i] == '\t')
|
|
for (j = 0; j < TAB_SIZE && curr_x + lp < screen_width; j++) linebuff[lp++] = ' ';
|
|
else if (format_code(s[i])) {
|
|
linebuff[lp++] = ' '; /* Color code */
|
|
break;
|
|
} else if (s[i] == '\r') { /* New format code */
|
|
if (s[i + 1] == 0) continue; /* Bogus format code */
|
|
if (((uchar)s[i + 1]) < FMT_CODE_CNT) break;
|
|
c = (char)xlat_format_code((uchar)s[++i]);
|
|
if (c != 0) linebuff[lp++] = c;
|
|
} else if (s[i] == '\n') {
|
|
break;
|
|
} else linebuff[lp++] = s[i];
|
|
|
|
linebuff[lp] = 0;
|
|
|
|
/* Backtrack to last space; in case of formatting codes, we should
|
|
already have one */
|
|
endmark = lp;
|
|
|
|
if (!isspace(s[i]) && !format_code(s[i]) && s[i] != 0) {
|
|
/* If we aren't conveniently at a break...*/
|
|
do { /* Find last space */
|
|
endmark--;
|
|
} while (endmark > 0 && !isspace(linebuff[endmark]));
|
|
}
|
|
|
|
if (endmark == 0 && !isspace(linebuff[endmark])) { /* Can't find a break */
|
|
if (curr_x + lp < screen_width) /* Not a line break */
|
|
endmark = lp; /* Break at end; it doesn't matter that much */
|
|
else /* We _need_ a line break but are having trouble finding one */
|
|
if (curr_x > 0) /* already stuff on this line printed previously */
|
|
endmark = 0; /* i.e. print out nothing; move it to next line */
|
|
else /* We have a single word that is longer than our line */
|
|
endmark = screen_width; /* Give up */
|
|
}
|
|
|
|
c = linebuff[endmark];
|
|
linebuff[endmark] = 0;
|
|
old_x = curr_x;
|
|
|
|
agt_puts(linebuff);
|
|
|
|
linebuff[endmark] = c;
|
|
|
|
if (old_x + lp >= screen_width)
|
|
/* Need to insert line break and skip any spaces */
|
|
{
|
|
if (!quotemode) agt_newline();
|
|
else return; /* In quote mode, just truncate */
|
|
|
|
/* Now set up beginning of next line: skip over whitespace */
|
|
while (endmark < lp && isspace(linebuff[endmark]))
|
|
endmark++; /* Eliminate EOL whitespace */
|
|
if (endmark == lp) {
|
|
/* Nothing left; eliminate whitespace at beginning
|
|
of next line */
|
|
while (isspace(s[i]) && s[i] != '\r') i++;
|
|
lp = endmark = 0;
|
|
}
|
|
needfill = 1;
|
|
if (endmark == lp && s[i] == 0) {
|
|
needfill = 2;
|
|
return; /* If only spaces left, don't print them */
|
|
}
|
|
}
|
|
|
|
/* Now copy remaining text */
|
|
for (j = 0; endmark < lp; j++, endmark++) linebuff[j] = linebuff[endmark];
|
|
lp = j;
|
|
|
|
/* Now to deal with format codes */
|
|
if ((unsigned char)s[i] == FORMAT_CODE) {
|
|
i++;
|
|
if (bold_mode) { /* Translate as BOLD toggle */
|
|
if (textbold)
|
|
agt_textcolor(-2); /* Turn bold off */
|
|
else agt_textcolor(-1); /* Turn bold on */
|
|
textbold = !textbold;
|
|
} else /* translate as BLACK */
|
|
agt_textcolor(0);
|
|
} else if (s[i] > 0 && s[i] <= LAST_TEXTCODE)
|
|
agt_textcolor(s[i++]);
|
|
else if (s[i] == '\r') {
|
|
run_format_code((uchar)s[i + 1]);
|
|
i += 2;
|
|
} else if (s[i] == '\n') {
|
|
i += 1;
|
|
agt_newline();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void writeln(const char *s) {
|
|
int i, pad;
|
|
char *padstr;
|
|
|
|
if (center_on && (int)strlen(s) + curr_x < screen_width) {
|
|
pad = (screen_width - strlen(s)) / 2;
|
|
padstr = (char *)rmalloc((pad + 1) * sizeof(char));
|
|
for (i = 0; i < pad; i++) padstr[i] = ' ';
|
|
padstr[i] = 0;
|
|
agt_puts(padstr);
|
|
rfree(padstr);
|
|
}
|
|
writestr(s);
|
|
/* needfill=2 if writestr ended with a line that wrapped only
|
|
because of excess spaces (which have been discarded); needfill==1
|
|
if writestr wrapped a line for any reason */
|
|
/* If needfill==2, we've already issued a new-line, so don't issue a
|
|
second one. */
|
|
/* If needfill==1, set savenl rather than wrapping (writestr will
|
|
then decide to wrap or not depending on whether the next line starts
|
|
with text or nontext), *unless* we are version magx, in which case
|
|
the game author presumably knew what they were doing, so honor their
|
|
wishes. */
|
|
if (par_fill_on && needfill == 1)
|
|
if (aver >= AGX00) agt_newline();
|
|
else savenl = 1;
|
|
else if (needfill != 2)
|
|
agt_newline();
|
|
needfill = 0;
|
|
}
|
|
|
|
|
|
static char fixstatchar(uchar c)
|
|
/* Eliminate formating characters in the status line */
|
|
{
|
|
if (c == '\t' || c <= LAST_TEXTCODE ||
|
|
(c == FORMAT_CODE) || c == '\r' || c == '\n')
|
|
return ' ';
|
|
return c;
|
|
}
|
|
|
|
void print_statline(void)
|
|
/* Use strings in l_stat and r_stat */
|
|
{
|
|
int i, j;
|
|
char *s, *t;
|
|
static rbool lastline = 0; /* Was a non-empty status line printed last time? */
|
|
|
|
s = (char *)rmalloc((status_width + 1) * sizeof(char));
|
|
|
|
/* If both strings empty, don't print the status line */
|
|
if (l_stat[0] == 0 && r_stat[0] == 0 && !lastline) return;
|
|
lastline = (l_stat[0] || r_stat[0]);
|
|
|
|
i = status_width - strlen(l_stat) - strlen(r_stat);
|
|
|
|
j = 0;
|
|
if (r_stat[0] == 0) { /* Center the status line */
|
|
while (j < i / 2) s[j++] = ' ';
|
|
i -= j;
|
|
} else if (i > 6) {
|
|
s[j++] = ' ';
|
|
i -= 2;
|
|
} /* If statline is wide enough, put a
|
|
space on each side */
|
|
|
|
if ((int)strlen(l_stat) < status_width) /* Copy left side of status line into s*/
|
|
for (t = l_stat; *t != 0; t++) s[j++] = fixstatchar(*t);
|
|
|
|
for (; i > 0; i--) s[j++] = ' '; /* Insert space between left and right sides */
|
|
|
|
if (j + (int)strlen(r_stat) <= status_width) /*Copy right side into s */
|
|
for (t = r_stat; *t != 0; t++) s[j++] = fixstatchar(*t);
|
|
|
|
while (j < status_width) s[j++] = ' '; /* Pad any extra width with spaces */
|
|
s[j] = 0; /* Put end of string marker */
|
|
agt_statline(s); /* Output it */
|
|
rfree(s);
|
|
}
|
|
|
|
|
|
void padout(int padleng) {
|
|
int i;
|
|
char *pstr;
|
|
|
|
if (padleng <= 0) return;
|
|
pstr = (char *)rmalloc(padleng + 1);
|
|
for (i = 0; i < padleng; i++) pstr[i] = ' ';
|
|
pstr[padleng] = 0;
|
|
writestr(pstr);
|
|
free(pstr);
|
|
}
|
|
|
|
static int textwidth(char *s) {
|
|
int n;
|
|
|
|
n = 0;
|
|
for (; *s != 0; s++) n += (*s == '\t') ? TAB_SIZE : 1;
|
|
return n;
|
|
}
|
|
|
|
void textbox(char *(txt[]), int len, unsigned long flags)
|
|
/* TB_TTL, TB_BOLD, TB_BORDER, TB_CENTER */
|
|
{
|
|
int i, width, padwidth;
|
|
int *linewidth;
|
|
|
|
agt_textcolor(7);
|
|
if (flags & TB_BOLD) agt_textcolor(-1);
|
|
else agt_textcolor(-2);
|
|
|
|
linewidth = (int *)rmalloc(len * sizeof(int));
|
|
|
|
width = 0; /* This contains the maximum width of any line */
|
|
for (i = 0; i < len; i++) {
|
|
linewidth[i] = textwidth(txt[i]);
|
|
if (linewidth[i] > width) width = linewidth[i];
|
|
}
|
|
|
|
agt_makebox(width, len, flags & ~(TB_BOLD | TB_CENTER));
|
|
quotemode = 1; /* So newlines will cause truncation rather than a
|
|
real newline */
|
|
for (i = 0; i < len; i++) {
|
|
padwidth = width - linewidth[i]; /* Amount of padding we need */
|
|
if (flags & TB_CENTER) {
|
|
padout(padwidth / 2);
|
|
padwidth -= padwidth / 2;
|
|
}
|
|
writestr(txt[i]);
|
|
padout(padwidth);
|
|
if (i != len - 1) agt_qnewline();
|
|
}
|
|
agt_endbox();
|
|
quotemode = 0; /* Back to normal */
|
|
|
|
agt_textcolor(7);
|
|
textbold = 0;
|
|
}
|
|
|
|
|
|
#ifndef REPLACE_MENU
|
|
|
|
int agt_menu(const char *header, int size, int width, menuentry *menu)
|
|
/* This is _very_ minimal as it stands */
|
|
{
|
|
int i, j;
|
|
char sbuff[10];
|
|
int numcol, colheight;
|
|
|
|
if (size == 0) return 0;
|
|
|
|
width = width + 5;
|
|
numcol = screen_width / width;
|
|
colheight = size / numcol;
|
|
if (size % numcol != 0) colheight++;
|
|
|
|
writeln(header);
|
|
for (i = 0; i < colheight; i++) {
|
|
for (j = 0; j < numcol; j++) {
|
|
if (j * colheight + i >= size) break;
|
|
Common::sprintf_s(sbuff, "%2d.", j * colheight + i + 1);
|
|
writestr(sbuff);
|
|
writestr(menu[j * colheight + i]);
|
|
if (j < numcol - 1) padout(width - 3 - strlen(menu[j * colheight + i]));
|
|
}
|
|
writeln("");
|
|
}
|
|
do {
|
|
writestr("Choice:");
|
|
i = read_number() - 1;
|
|
if (i < 0 || i >= size)
|
|
writeln("Please choose an option from the menu.");
|
|
} while (!quitflag && (i < 0 || i >= size));
|
|
return i;
|
|
}
|
|
|
|
#endif /* REPLACE_MENU */
|
|
|
|
|
|
|
|
void prompt_out(int n)
|
|
/* n=1 standard prompt
|
|
n=2 question prompt */
|
|
{
|
|
agt_textcolor(7);
|
|
if (PURE_INPUT && n == 1) agt_textcolor(-1);
|
|
if (n == 1) {
|
|
agt_newline();
|
|
gen_sysmsg(1, ">", MSG_MAIN, nullptr);
|
|
}
|
|
if (n == 2) agt_puts("? ");
|
|
agt_textcolor(7);
|
|
}
|
|
|
|
void agt_waitkey(void) {
|
|
if (BATCH_MODE || fast_replay)
|
|
return;
|
|
agt_getkey(0);
|
|
}
|
|
|
|
|
|
void wait_return(void) {
|
|
writeln(" --- HIT ANY KEY ---");
|
|
agt_waitkey();
|
|
}
|
|
|
|
|
|
rbool yesno(const char *s)
|
|
/* True for yes, false for no. */
|
|
{
|
|
char c;
|
|
|
|
writestr(s);
|
|
writestr(" ");
|
|
c = 'y';
|
|
do {
|
|
if (c != 'y')
|
|
writestr("Please answer <y>es or <n>o. ");
|
|
c = tolower(agt_getchar());
|
|
} while (c != 'y' && c != 'n' && !quitflag);
|
|
return (c == 'y');
|
|
}
|
|
|
|
|
|
void set_test_mode(fc_type fc) {
|
|
const char *errstr;
|
|
|
|
log_in = readopen(fc, fLOG, &errstr);
|
|
|
|
if (make_test) {
|
|
if (errstr == nullptr)
|
|
fatal("Log file already exists.");
|
|
log_out = writeopen(fc, fLOG, nullptr, &errstr);
|
|
if (errstr != nullptr)
|
|
fatal("Couldn't create log file.");
|
|
logflag = 1;
|
|
return;
|
|
}
|
|
|
|
logdelay = 0;
|
|
if (errstr != nullptr)
|
|
fatal("Couldn't open log file.");
|
|
logflag = 2;
|
|
|
|
script_on = 1;
|
|
scriptfile = writeopen(fc, fSCR, nullptr, &errstr);
|
|
if (errstr != nullptr)
|
|
fatal("Couldn't open script file.");
|
|
}
|
|
|
|
|
|
#ifndef REPLACE_GETFILE
|
|
|
|
/* This opens the file referred to by fname and returns it */
|
|
static genfile uf_open(fc_type fc, filetype ext, rbool rw) {
|
|
char *errstr;
|
|
genfile f;
|
|
|
|
if (rw) { /* Check to see if we are overwriting... */
|
|
if (fileexist(fc, ext) && ext != fSCR) {
|
|
if (!yesno("This file already exists; overwrite?"))
|
|
/* That is, DON'T overwrite */
|
|
return badfile(ext);
|
|
}
|
|
f = writeopen(fc, ext, NULL, &errstr);
|
|
} else
|
|
f = readopen(fc, ext, &errstr);
|
|
if (errstr != NULL) writeln(errstr);
|
|
return f;
|
|
}
|
|
|
|
static fc_type last_save = NULL;
|
|
static fc_type last_log = NULL;
|
|
static fc_type last_script = NULL;
|
|
|
|
|
|
genfile get_user_file(int ft)
|
|
/* ft= 0:script, 1:save 2:restore, 3:log(read) 4:log(write) */
|
|
/* Should return file in open state, ready to be read or written to,
|
|
as the case may be */
|
|
{
|
|
/* int extlen;*/
|
|
rbool rw; /* True if writing, false if reading */
|
|
filetype ext;
|
|
genfile fd;
|
|
fc_type def_fc, fc;
|
|
char *fname;
|
|
char *ftype;
|
|
char *p, *q;
|
|
|
|
switch (ft) {
|
|
case 0:
|
|
ftype = "script ";
|
|
def_fc = last_script;
|
|
rw = 1;
|
|
ext = fSCR;
|
|
break;
|
|
case 1:
|
|
ftype = "save ";
|
|
def_fc = last_save;
|
|
rw = 1;
|
|
ext = fSAV;
|
|
break;
|
|
case 2:
|
|
ftype = "restore ";
|
|
def_fc = last_save;
|
|
rw = 0;
|
|
ext = fSAV;
|
|
break;
|
|
case 3:
|
|
ftype = "log ";
|
|
def_fc = last_log;
|
|
rw = 0;
|
|
ext = fLOG;
|
|
break;
|
|
case 4:
|
|
ftype = "log ";
|
|
def_fc = last_log;
|
|
rw = 1;
|
|
ext = fLOG;
|
|
break;
|
|
default:
|
|
writeln("<INTERNAL ERROR: invalid file type>");
|
|
return badfile(fSAV);
|
|
}
|
|
|
|
writestr(" ");
|
|
writestr("Enter ");
|
|
if (ftype != NULL) writestr(ftype);
|
|
writestr("file name");
|
|
if (def_fc != NULL) {
|
|
char *s;
|
|
s = formal_name(def_fc, ext);
|
|
writestr(" (");
|
|
writestr(s);
|
|
writestr(")");
|
|
rfree(s);
|
|
}
|
|
writestr(": ");
|
|
|
|
if (PURE_INPUT) agt_textcolor(-1);
|
|
fname = agt_input(4);
|
|
if (PURE_INPUT) agt_textcolor(-2);
|
|
|
|
/* Delete whitespace before and after the file name. */
|
|
for (p = fname; isspace(*p); p++);
|
|
if (*p == 0) { /* Line is all whitespace; use default if there is one */
|
|
if (def_fc == NULL) {
|
|
writeln("Never mind.");
|
|
rfree(fname);
|
|
return badfile(ext);
|
|
} else {
|
|
rfree(fname);
|
|
fc = def_fc;
|
|
}
|
|
} else { /* Line is _not_ all whitespace: we have a file name */
|
|
for (q = fname; *p != 0; p++, q++)
|
|
*q = *p;
|
|
q--;
|
|
while (isspace(*q)) q--;
|
|
q++;
|
|
*q = 0;
|
|
fc = init_file_context(fname, ext);
|
|
}
|
|
|
|
fd = uf_open(fc, ext, rw);
|
|
|
|
if (!filevalid(fd, ext)) {
|
|
if (fc != def_fc) release_file_context(&fc);
|
|
return fd;
|
|
}
|
|
|
|
switch (ft) {
|
|
case 0:
|
|
last_script = fc;
|
|
break;
|
|
case 1:
|
|
last_save = fc;
|
|
break;
|
|
case 2:
|
|
last_save = fc;
|
|
break;
|
|
case 3:
|
|
last_log = fc;
|
|
break;
|
|
case 4:
|
|
last_log = fc;
|
|
break;
|
|
}
|
|
if (fc != def_fc) release_file_context(&def_fc);
|
|
return fd;
|
|
}
|
|
|
|
|
|
void set_default_filenames(fc_type fc) {
|
|
last_save = convert_file_context(fc, fSAV, NULL);
|
|
last_log = convert_file_context(fc, fLOG, NULL);
|
|
last_script = convert_file_context(fc, fSCR, NULL);
|
|
}
|
|
|
|
|
|
|
|
#endif /* REPLACE_GETFILE */
|
|
|
|
|
|
|
|
|
|
void script(uchar onp) {
|
|
if (onp == script_on)
|
|
if (onp == 0) writeln("Scripting wasn't on.");
|
|
else writeln("Scripting is already on.");
|
|
else if (onp == 1) {
|
|
scriptfile = get_user_file(0);
|
|
if (filevalid(scriptfile, fSCR)) script_on = 1;
|
|
} else if (filevalid(scriptfile, fSCR)) {
|
|
close_pfile(scriptfile, 0);
|
|
scriptfile = BAD_TEXTFILE;
|
|
script_on = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void logon(void) {
|
|
if (logflag & 1) {
|
|
writeln("Already logging");
|
|
return;
|
|
}
|
|
log_out = get_user_file(4);
|
|
if (filevalid(log_out, fLOG))
|
|
logflag |= 1;
|
|
}
|
|
|
|
void replay(int delay) {
|
|
if (logflag & 2) return; /* Nested replays are meaningless */
|
|
log_in = get_user_file(3);
|
|
if (filevalid(log_in, fLOG)) {
|
|
logflag |= 2;
|
|
logdelay = delay;
|
|
}
|
|
}
|
|
|
|
|
|
/* These two are intended to be called by the platform-dependent
|
|
interface (e.g. if the user had chosen these from some general purpose
|
|
menu) */
|
|
/* They're never called from the rest of the code */
|
|
|
|
void agt_save(void) {
|
|
g_vm->saveGame();
|
|
}
|
|
|
|
void agt_restore(void) {
|
|
doing_restore = 1;
|
|
}
|
|
|
|
void agt_restart(void) {
|
|
doing_restore = 2;
|
|
}
|
|
|
|
void agt_quit(void) {
|
|
doing_restore = 4;
|
|
}
|
|
|
|
|
|
/* This should be rmalloc'd */
|
|
static fc_type newgame_fc;
|
|
|
|
fc_type new_game(void) {
|
|
return newgame_fc;
|
|
}
|
|
|
|
void agt_newgame(fc_type fc) {
|
|
newgame_fc = fc;
|
|
doing_restore = 3;
|
|
}
|
|
|
|
#if 0
|
|
static rbool end_cmd_options;
|
|
#endif
|
|
|
|
void set_default_options(void) {
|
|
init_flags();
|
|
flag = (rbool *)rmalloc(sizeof(rbool));
|
|
debug_parse = 0;
|
|
DEBUG_AGT_CMD = 0;
|
|
DEBUG_EXEC_VERB = 0;
|
|
DEBUG_DISAMBIG = 0;
|
|
DEBUG_SMSG = 0;
|
|
}
|
|
|
|
void helpmsg(void) {
|
|
/*
|
|
printf(" -i Try to use IBM character set.\n");
|
|
printf(" -1 IRUN Mode: Print messages in first person\n");
|
|
printf(" -h Print out this message\n");
|
|
printf(" -d Debug metacommand execution\n");
|
|
printf(" -t Test mode; see accompanying documentation. Implies -r.\n");
|
|
printf(" -c Create test file.\n");
|
|
printf(" -m Force descriptions to be loaded from disk.\n");
|
|
#ifdef OPEN_AS_TEXT
|
|
printf(" -b Open data files as binary files.\n");
|
|
#endif
|
|
printf("\nTechnical options (intended for debugging AGiliTy itself).\n");
|
|
printf(" -p Debug parser\n");
|
|
printf(" -x Debug verb execution loop\n");
|
|
printf(" -a Debug disambiguation system\n");
|
|
printf(" -s Debug STANDARD message handler\n");
|
|
*/
|
|
}
|
|
|
|
#if 0
|
|
static rbool setarg(char **optptr) {
|
|
if ((*optptr)[1] == '+') {
|
|
(*optptr)++;
|
|
return 1;
|
|
}
|
|
if ((*optptr)[1] == '-') {
|
|
(*optptr)++;
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
#define fixcase(c) tolower(c)
|
|
|
|
#if 0
|
|
void parse_options(char *opt, char *next) {
|
|
/*
|
|
if (opt[0]=='-' && opt[1]==0)
|
|
{end_cmd_options=1;return;}
|
|
for(;*opt!=0;opt++)
|
|
switch(fixcase(*opt))
|
|
{
|
|
case 'p': debug_parse=setarg(&opt);break;
|
|
case 'a': DEBUG_DISAMBIG=setarg(&opt);break;
|
|
case 'd': DEBUG_AGT_CMD=setarg(&opt);break;
|
|
case 'x':DEBUG_EXEC_VERB=setarg(&opt);break;
|
|
case 's':DEBUG_SMSG=setarg(&opt);break;
|
|
#ifdef MEM_INFO
|
|
case 'M': DEBUG_MEM=setarg(&opt);break;
|
|
#endif
|
|
case 'm': descr_maxmem=0; break;
|
|
case 'i': fix_ascii_flag=!setarg(&opt);break;
|
|
case 't': BATCH_MODE=setarg(&opt); break;
|
|
case 'c': make_test=setarg(&opt); break;
|
|
case '1': irun_mode=setarg(&opt);break;
|
|
#ifdef OPEN_FILE_AS_TEXT
|
|
case 'b': open_as_binary=setarg(&opt);break;
|
|
#endif
|
|
default:printf("Do not recognize option %c\n",*opt);
|
|
helpmsg();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
*/
|
|
}
|
|
#endif
|
|
|
|
#ifndef REPLACE_MAIN
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int i;
|
|
char *gamefile;
|
|
|
|
set_default_options();
|
|
end_cmd_options = 0;
|
|
gamefile = NULL;
|
|
for (i = 1; i < argc; i++)
|
|
if (argv[i][0] == '-' && !end_cmd_options)
|
|
parse_options(argv[i] + 1, argv[i + 1]);
|
|
else if (gamefile == NULL)
|
|
gamefile = argv[i];
|
|
else fatal("Please specify only one game\n");
|
|
if (gamefile == NULL) {
|
|
helpmsg();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
init_interface(argc, argv);
|
|
/* From this point on, MUST use writestr/writeln or may
|
|
cause problems w/ the interfaces on some platforms
|
|
that have to keep track of cursor position */
|
|
|
|
run_game(init_file_context(gamefile, fDA1));
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|
|
#endif /* REPLACE_MAIN */
|
|
|
|
} // End of namespace AGT
|
|
} // End of namespace Glk
|