Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

895
engines/glk/agt/agil.cpp Normal file
View File

@@ -0,0 +1,895 @@
/* 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"
#include "common/str.h"
namespace Glk {
namespace AGT {
/* ------------------------------------------------------------------- */
/* Description Pointers */
descr_ptr intro_ptr;
descr_ptr title_ptr, ins_ptr; /* Only defined if agx_file is true */
descr_ptr *err_ptr; /* [NUM_ERR];*/
descr_ptr *msg_ptr; /* [MAX_MSG];*/
descr_ptr *help_ptr, *room_ptr, *special_ptr; /*[ROOM] */
descr_ptr *noun_ptr, *text_ptr, *turn_ptr, /* [NOUN] */
*push_ptr, *pull_ptr, *play_ptr;
descr_ptr *talk_ptr, *ask_ptr, *creat_ptr; /* [CREAT] */
descr_ptr *quest_ptr, *ans_ptr; /* [MAX_QUEST] */
tline *question, *answer; /* [MAX_QUEST] */
/* ------------------------------------------------------------------------ */
/* Dynamically allocated data blocks (which are pointed to from elsewhere) */
char **dict; /* dict[n] points to the nth dictionary word */
long dp; /* Dictionary pointer: number of words in dict */
#define DICT_INIT 12*1024 /* Starting size of dictstr */
#define DICT_GRAN 1024 /* Granularity of dictstr size requests
must be at least 4. */
char *dictstr; /* Pointer to memory block containing dict words */
long dictstrptr, dictstrsize;
/* dictstrptr points to the first unused byte in dictstr.
dictstrsize points to the end of the space currently allocated for
dictstr.
*/
char *static_str; /*Static string space */
long ss_end; /* Pointer to end of used space in above */
long ss_size; /* Current size of static string space */
word *syntbl; /* Synonym list space */
slist synptr; /* Points to end of used space */
long syntbl_size; /* Long so we can catch overflows */
long descr_maxmem;
char *mem_descr; /* Copy of descriptor in memory */
/* ------------------------------------------------------------------------ */
/* Data structures used internally by agtread.c */
/*The following are all set to NULL after agtread finishes. */
long *cmd_ptr; /* ME only;Points to cmd start locs in gamefile.*/
long *room_name, *noun_sdesc, *noun_pos, *creat_sdesc;
long *t_pictlist, *t_pixlist, *t_songlist, *t_fontlist;
/* These are only used by agtout (to allow the AGT reading routines to
pass back the count of nouns inside the given object) */
integer *room_inside, *noun_inside, *creat_inside;
/* This is used to translate ASCII codes */
uchar fixchar[256];
rbool text_file; /* Set if we are currently opening a binary file. */
#ifdef OPEN_AS_TEXT
rbool open_as_binary; /* Open text files as binary, anyhow. */
#endif
/* The following are AGT 'purity' flags; they turn off features of */
/* my interpreter that are not fully consistent with the original AGT */
/* and so could break some games. Some of these are trivial improvements; */
/* some are more radical and should be used with caution. Several are */
/* only useful if a game was designed with them in mind. */
/* In all cases, setting the flag to 1 more closely follows the */
/* behavior of the original interpreters */
/* WARNING: Most of these haven't been tested in the non-default state. */
/* Most of these will eventually become variables settable by the user */
/* or from a (possibly game-specific) configuration file */
rbool PURE_INPUT = 1; /* Is the input line bold? */
rbool PURE_TONE = 0; /* Is low level sound enabled? */
/*-------------------------------------------------------------------*/
/* Misc. things to support the tokenizer and the dictionry. */
/*-------------------------------------------------------------------*/
/* The following should not be changed without also changing the
wtype enum statement in interp.h to match */
static const char *ext_voc[] = {
"the", "my", "a", "an", /* These 4 are ignored in canonical AGT */
"then", ".", ";", "and", "," , "its", "all", "undo", "look", "g",
"pick", "go", "exits", "talk", "take", "door", "again", "but", "except",
"scene", "everything", "listexit", "listexits", "close",
"verb", "noun", "adjective", "prep", "object", "name", "step",
" any", "either", "both", "everyone", "everybody",
"he", "she", "it", "they", "him", "her", "them", "is", "are", "oops",
"was", "were", "scream",
/* Everything between 'in' and 'about' should be a preposition */
"in", "out", "into", "at", "to", "across", "inside", "with", "near", "for",
"of", "behind", "beside", "on", "off", "under", "from", "through",
"toward", "towards", "between", "around", "upon", "thru",
"by", "over", "up", "down",
"about"
};
/* 'about' must be the last element of this list */
/*-------------------------------------------------------------------*/
/* Routines to read in and use various auxiliary files. */
/* (.TTL, .INS, .VOC, .CFG) */
/*-------------------------------------------------------------------*/
static rbool emptyline(unsigned char *s)
/* Check if s consists only of white space and control characters */
{
unsigned char *p;
for (p = s; *p != 0; p++)
if (!rspace(*p) && *p > 26) return 0;
return 1;
}
static void print_title(fc_type fc) {
int height;
signed char center_mode; /* Center title? */
descr_line *buff;
char *s;
rbool skip_line; /* Skip first line: it has COLORS */
if (agx_file)
buff = read_descr(title_ptr.start, title_ptr.size);
else
buff = read_ttl(fc);
if (buff == nullptr) {
writeln("");
writeln("");
s = formal_name(fc, fNONE);
if (s != nullptr) {
s[0] = toupper(s[0]);
agt_center(1);
agt_textcolor(-1);
writeln(s);
agt_textcolor(-2);
agt_center(0);
rfree(s);
}
writeln("");
writeln("");
if (aver < AGX00)
writeln("This game was created with Malmberg and Welch's Adventure "
"Game Toolkit; it is being executed by");
else writeln("This game is being executed by ");
writeln("");
height = 0;
} else {
if (buff[0] != nullptr && strncasecmp(buff[0], "COLORS", 6) == 0) {
/* Do screen colors */
skip_line = 1;
} else skip_line = 0;
/* Compute height and count the number of non-empty lines
starting with spaces. We use height as a loop variable
and center_mode to store the count temporarily. */
center_mode = 0;
for (height = skip_line; buff[height] != nullptr; height++)
if (!emptyline((uchar *)buff[height])) {
if (rspace(buff[height][0])) center_mode++;
else center_mode--;
}
if (box_title || aver == AGTCOS) center_mode = TB_CENTER;
else /* includes aver==AGT135 */
if (center_mode <= 0) center_mode = TB_CENTER;
else center_mode = TB_NOCENT;
if (!bold_mode) agt_textcolor(-1);
agt_clrscr();
textbox(buff + skip_line, height - skip_line, center_mode |
(bold_mode ? 0 : TB_BOLD) | TB_TTL |
(box_title ? TB_BORDER : 0));
if (!bold_mode) agt_textcolor(-2); /* Bold off */
} /* End printing of title proper */
if (agx_file)
free_descr(buff);
else
free_ttl(buff);
agt_textcolor(7);
agt_center(1);
if (buff != nullptr) {
if (aver < AGX00 && height <= screen_height - 6)
writeln("[Created with Malmberg and Welch's Adventure Game Toolkit]");
if (height <= screen_height - 9) writeln("");
if (height <= screen_height - 5) writeln("This game is being executed by");
}
agt_textcolor(-1);
s = (char *)rmalloc(80);
if (height <= screen_height - 5)
Common::sprintf_s(s, 80, "AGiliTy: "
"The (Mostly) Universal AGT Interpreter %s", version_str);
else
Common::sprintf_s(s, 80, "Being run by AGiliTy %s, "
"Copyright (C) 1996-99,2001 Robert Masenten",
version_str);
writeln(s);
rfree(s);
agt_textcolor(-2);
if (height <= screen_height - 5)
writeln("Copyright (C) 1996-99,2001 by Robert Masenten");
if (height <= screen_height - 3) writeln(portstr);
if (height <= screen_height - 10) writeln("");
agt_center(0);
}
/* .INS reading routines -------------------------------------- */
void print_instructions(fc_type fc) {
char *buffer;
uchar *s;
writeln("INSTRUCTIONS:");
if (open_ins_file(fc, 1)) { /* Instruction file exists */
while (nullptr != (buffer = read_ins_line())) {
for (s = (uchar *)buffer; *s != 0; s++) *s = trans_ascii[*s];
writeln(buffer);
}
}
writeln("");
}
/* Routines to build the verb menu from the .VOC information */
static void build_verbmenu(void) {
int i, n;
char *p, *d;
verbmenu = (menuentry *)rmalloc(vm_size * sizeof(menuentry));
vm_width = 0;
for (i = 0; i < vm_size; i++) {
p = verbmenu[i];
d = dict[verbinfo[i].verb];
n = 0;
for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d;
if (verbinfo[i].prep != 0 && n + 1 < MENU_WIDTH) {
*p++ = ' ';
d = dict[verbinfo[i].prep];
*p++ = toupper(*d++);
for (; n < MENU_WIDTH && *d != 0; p++, d++, n++) *p = *d;
}
verbmenu[i][0] = toupper(verbmenu[i][0]);
*p = 0;
if (n > vm_width) vm_width = n;
}
}
/* .CFG reading routines -------------------------------------------- */
#define opt(s) (strcasecmp(optstr[0],s)==0)
/* These are the interpreter specific options; this is called
from cfg_option in agtdata.c. */
void agil_option(int optnum, char *optstr[], rbool setflag, rbool lastpass) {
if (opt("ibm_char")) fix_ascii_flag = !setflag;
else if (!lastpass) return; /* On the first pass through the game specific
file, we ignore all but the above options */
else if (opt("tone")) PURE_TONE = setflag;
else if (opt("input_bold")) PURE_INPUT = setflag;
else if (opt("force_load")) FORCE_VERSION = setflag;
else if (opt("stable_random")) stable_random = setflag;
else if (!agt_option(optnum, optstr, setflag)) /* Platform specific options */
rprintf("Invalid option %s\n", optstr[0]);
}
/*-------------------------------------------------------------------*/
/* Tokeniser: Split input into words and look them up in dictionary */
/*-------------------------------------------------------------------*/
static rbool noise_word(word w) {
if (w == ext_code[wthe] || w == ext_code[wa] || w == ext_code[wan]) return 1;
if (w == ext_code[wmy]) return 1;
if (aver >= AGT18 && aver <= AGT18MAX && w == ext_code[wis]) return 1;
return 0;
}
static rbool check_dot(char *prevtext, int prevcnt, char *lookahead)
/* This routine is devoted to trying to figure out whether
we should treat '.' as punctuation or as a letter. */
/* It returns true if '.' should be treated as punctuation. */
/* prevtext=the current word, as far as it has been parsed.
prevcnt=the number of letters in prevtext
[which is *not* 0 terminated] */
/* lookahead=the rest of the current input line *after* the period. */
{
int i, endword, restcnt;
if (!PURE_DOT) return 1; /* No words with periods in them, so it must
be punctuation. */
/* We just start scanning the dictionary to see if any of them
are possible matches, looking ahead as necessary. */
/* Find the next unambiguous word end. This ignores possible
word ends caused by periods. */
for (endword = 0; lookahead[endword] != 0; endword++)
if (isspace(lookahead[endword]) ||
lookahead[endword] == ',' || lookahead[endword] == ';')
break;
for (i = 0; i < dp; i++) {
if (i == ext_code[wp]) continue; /* Ignore matches with the word ".". */
/* If it doesn't contain a '.' at the right location, there is no
point in continuing. */
restcnt = strlen(dict[i]);
if (restcnt <= prevcnt || dict[i][prevcnt] != '.') continue;
/* Now make sure the previous characters are correct */
if (strncasecmp(prevtext, dict[i], prevcnt) != 0) continue;
/* Finally, compare the trailing text. This is complicated by
the fact that the trailing text could itself contain ambiguous '.'s */
restcnt -= prevcnt + 1; /* Number of characters in dict entry after '.' */
if (restcnt > endword) continue; /* Dictionary entry is longer than
following text */
/* Check to see if the dictionary entry can be found in the lookahead
buffer */
if (strncasecmp(lookahead, dict[i] + prevcnt + 1, restcnt) != 0) continue;
if (restcnt == endword) return 0; /* We have a match */
/* At this point, we know that restcnt<endword and the dictionary
entry matches as far as restcnt. */
/* endword ignores '.', though, so it could be we have a match
but are missing it because it is period-terminated. Check this. */
if (lookahead[restcnt] == '.') return 0;
/* Otherwise, no match... try again with the next word... */
}
return 1; /* No matches: treat it as punctuation. */
}
static void tokenise(char *buff)
/* Convert input string into vocabulary codes */
/* 0 here denotes an unrecognized word and -1 marks the end. */
{
int ip_, j, k;
rbool punctuation;
j = 0;
ip_ = 0;
k = 0; /* k is the character pointer */
for (k = 0;; k++) {
/* If PURE_DOT is set, then there are periods in some of the dictionary
words, so '.' could be a letter or punctuation-- we have to examine
context to figure out which. */
if (buff[k] == '.' && PURE_DOT)
/* Note: check_dot is in agtdata.c, since it needs to access
internal implementation details of the dictionary */
punctuation = check_dot(in_text[ip_], j, buff + k + 1);
else
punctuation = (buff[k] == ',' || buff[k] == ';' || buff[k] == '.');
if (buff[k] != 0 && !isspace(buff[k]) && !punctuation) {
if (j < WORD_LENG - 1)
in_text[ip_][j++] = buff[k];
} else if (j > 0) { /* End of word: add it to input */
in_text[ip_][j] = 0;
input[ip_] = search_dict(in_text[ip_]);
if (input[ip_] == -1) input[ip_] = 0;
else if (input[ip_] == 0) input[ip_] = ext_code[w_any]; /* _Real_ 'ANY' */
if (!noise_word(input[ip_])) ip_ += 1;
/* i.e. if not one of the four ignored words, advance */
j = 0;
} /* If j=0 and not punct, then no new word; just skip the whitespace */
if (punctuation) {
in_text[ip_][0] = buff[k];
in_text[ip_][1] = 0;
input[ip_] = search_dict(in_text[ip_]);
if (input[ip_] == -1) input[ip_] = 0;
j = 0;
ip_++;
}
if (ip_ >= MAXINPUT - 1) {
writeln("Too many words in input; ignoring rest of line.");
break;
}
if (buff[k] == 0) break;
}
input[ip_] = -1;
in_text[ip_][0] = 0;
}
/*-------------------------------------------------------------------*/
/* Main game loop: Get player input and call the parser. */
/*-------------------------------------------------------------------*/
static void game_end(void) {
rbool done_flag;
char *s;
if (winflag || deadflag) {
writeln("");
writeln("");
agt_center(1);
if (winflag)
gen_sysmsg(148, "***** $You$ have won! *****", MSG_MAIN, nullptr);
if (deadflag)
gen_sysmsg(147, "***** $You$ have died! *****", MSG_MAIN, nullptr);
writeln("");
writeln("");
agt_center(0);
}
if (deadflag && !endflag) {
if (curr_lives > 1) { /* Resurrection code */
if (curr_lives == max_lives)
gen_sysmsg(151, "Hmmm.... so $you$'ve gotten $your$self killed. "
"Would you like me to try a resurrection?", MSG_MAIN, nullptr);
else gen_sysmsg(152, "<Sigh> $You$'ve died *again*. "
"Would you like me to try another resurrection?",
MSG_MAIN, nullptr);
if (yesno("? ")) { /* Now do resurrection */
curr_lives--;
quitflag = deadflag = 0;
gen_sysmsg(154,
"$You$ emerge coughing from a cloud of dark green smoke.",
MSG_MAIN, nullptr);
writeln("");
loc = resurrect_room - first_room;
newlife_flag = 1;
set_statline();
do_look = do_autoverb = 1;
newroom();
return;
} else writeln("As you wish...");
} else if (max_lives > 1)
gen_sysmsg(153, "$You$'ve used up all of $your$ lives.", MSG_MAIN, nullptr);
}
writeln("");
print_score();
writeln("");
done_flag = quitflag; /* If player has QUIT, don't ask again */
while (!done_flag && !quitflag) {
writestr("Would you like to ");
if (restart_state != nullptr) writestr("restart, ");
writestr("restore");
if (undo_state != nullptr && can_undo)
writestr(", undo,");
else if (restart_state != nullptr) writestr(",");
writestr(" or quit? ");
s = agt_readline(5);
if (strncasecmp(s, "RESTART", 7) == 0)
if (restart_state != nullptr) {
restart_game();
done_flag = 1;
} else writeln("Sorry, I'm unable to do that because of limited memory.");
else if (strncasecmp(s, "RESTORE", 7) == 0)
if (g_vm->loadGame().getCode() == Common::kNoError) {
done_flag = 1;
} else writeln("(RESTORE failed)");
else if (strncasecmp(s, "UNDO", 4) == 0)
if (can_undo && undo_state != nullptr) {
putstate(undo_state);
done_flag = 1;
} else writeln("Insufficiant memory to support UNDO");
else if (toupper(s[0]) == 'Q') {
quitflag = 1;
done_flag = 1;
}
}
set_statline();
}
static void parse_loop(void)
/* This exists to deal with THEN lists; parse() handles the indiviudual
commands */
{
for (ip = 0; ip >= 0 && ip < MAXINPUT && input[ip] != -1;) {
if (!parse() || quitflag || winflag || deadflag || endflag) break;
if (doing_restore) break;
if (ip >= 0 && ip < MAXINPUT && input[ip] != -1)
writeln(""); /* Insert blank lines between commands when dealing
with THEN lists */
}
}
static long rm_start_size;
static char memstr[100];
static void mainloop(void) {
char *s;
doing_restore = 0;
while (!quitflag) {
if (DEBUG_MEM) {
Common::sprintf_s(memstr,
"A:%ld F:%ld Delta:%ld Size:%ld+%ld=%ld (%ld left)\n",
ralloc_cnt, rfree_cnt, ralloc_cnt - rfree_cnt,
rm_start_size, rm_size - rm_start_size, rm_size,
rm_freesize);
writeln(memstr);
}
rm_size = 0; /* Reset it to zero */
rm_freesize = get_rm_freesize();
if (!menu_mode) {
prompt_out(1);
s = agt_readline(0);
if (g_vm->shouldQuit())
return;
agt_newline();
if (!doing_restore) tokenise(s); /* Tokenizes into input */
rfree(s);
if (!doing_restore) parse_loop();
} else
menu_cmd();
if (doing_restore) {
if (doing_restore == 1)
g_vm->loadGame();
else if (doing_restore == 2)
restart_game();
else if (doing_restore == 3 || doing_restore == 4)
return; /* Quit or New game requested */
doing_restore = 0;
}
if (winflag || deadflag || endflag || quitflag)
game_end();
}
}
/*-------------------------------------------------------------------*/
/* Start up and shut down: Routines to initialise the game state and */
/* clean up after the game ends. */
/*-------------------------------------------------------------------*/
static int init(void) {
int i, can_save;
uchar *tmp1, *tmp2;
init_vals();
init_creat_fix();
if (!agx_file) dict[0][0] = 0; /* Turn "ANY" into "" */
l_stat[0] = r_stat[0] = 0; /* Clear the status line */
/* lactor=lobj=lnoun=NULL;*/
tscore = old_score = objscore = 0;
turncnt = 0;
curr_time = startup_time;
loc = start_room - first_room;
cmd_saveable = 0;
first_visit_flag = newlife_flag = room_firstdesc = 1;
curr_lives = max_lives;
/* Note: flag[0] is the debugging flag and is set elsewhere */
if (FLAG_NUM < 0) FLAG_NUM = 0;
dbgflagptr = flag = (rbool *)rrealloc(flag, sizeof(rbool) * (FLAG_NUM + 1));
for (i = 1; i <= FLAG_NUM; i++)
flag[i] = 0;
dbgcntptr = agt_counter = (short *)rmalloc(sizeof(short) * (CNT_NUM + 1));
for (i = 0; i <= CNT_NUM; i++) {
agt_counter[i] = -1;
}
dbgvarptr = agt_var = (long *)rmalloc(sizeof(*agt_var) * (VAR_NUM + 1));
for (i = 0; i <= VAR_NUM; i++)
agt_var[i] = 0;
for (i = 0; i <= maxnoun - first_noun; i++) {
if (noun[i].position == nullptr || noun[i].position[0] == '\0')
noun[i].pos_prep = 0;
else noun[i].pos_prep = -1;
noun[i].pos_name = 0;
noun[i].initpos = noun[i].position;
}
nomatch_aware = 0; /* By default, not aware. */
smart_look = 1; /* By default, LOOK <x> --> EXAMINE */
for (i = 0; i < last_cmd; i++) {
if (command[i].nouncmd == -1 || command[i].objcmd == -1
|| command[i].noun_adj == -1 || command[i].obj_adj == -1
|| command[i].prep == -1)
nomatch_aware = 1;
if (command[i].verbcmd == ext_code[wlook] &&
(command[i].nouncmd > 0 || command[i].noun_adj > 0
|| command[i].objcmd > 0 || command[i].obj_adj > 0
|| command[i].prep > 0))
smart_look = 0;
}
pictable = (integer *)rmalloc(sizeof(int) * maxpict);
for (i = 0; i < maxpict; i++) pictable[i] = i;
init_state_sys(); /* Initialize the system for saving and restoring
game states */
tmp1 = (uchar *)rmalloc(MEM_MARGIN); /* Preserve some work space */
tmp2 = getstate(nullptr); /* Make sure we have space to save */
if (tmp2 == nullptr) can_save = 0;
else can_save = 1;
if (tmp2 != nullptr)
undo_state = getstate(nullptr);
else undo_state = nullptr;
if (undo_state != nullptr)
restart_state = getstate(nullptr);
else restart_state = nullptr;
rfree(tmp1);
rfree(tmp2);
rm_start_size = get_rm_size();
rm_freesize = get_rm_freesize();
return can_save;
}
static void ext_dict(void)
/* Enter the vocabulary extensions into the dictionary */
{
wtype i;
for (i = wthe; i <= wabout; i = (wtype)((int)i + 1))
ext_code[i] = add_dict(ext_voc[i]);
}
static void fix_dummy(void) {
int i;
/* At this point, all occurrences in the game file of the dictionary
words have been converted to dictionary indices, and so as long as
we don't change the dictionary index values, we can change the contents
without interfering with the metacommand scanner (since it compares
dictionary indices, not actual strings) */
if (!PURE_DUMMY) {
for (i = 0; i < DUMB_VERB; i++)
dict[ syntbl[auxsyn[i + BASE_VERB]] ][5] = ' ';
/* Convert underscores into spaces:
i.e. 'dummy_verb5' -> 'dummy verb5' */
dict[ syntbl[auxsyn[21]] ][6] = ' '; /* change_locations */
dict[ syntbl[auxsyn[55]] ][5] = ' '; /* magic_word */
}
if (!PURE_SUBNAME) /* Replace the 'e' by a space */
for (i = 0; i < MAX_SUB; i++)
Common::sprintf_s(dict[sub_name[i]], strlen(dict[sub_name[i]]) + 1, "subroutin %d", i + 1);
/* This must be no longer than 25 characters with the terminating null */
/* Now set PURE_DOT based on whether any dictionary word
contains a period. */
if (aver >= AGT18 && aver <= AGT18MAX) PURE_DOT = 0;
else {
PURE_DOT = FORCE_PURE_DOT;
for (i = 0; i < dp && !PURE_DOT; i++)
if (strchr(dict[i], '.') != nullptr && /* i.e. dict[i] contains period */
i != ext_code[wp]) /* The period itself _is_ a dictionary word:
avoid this false match */
PURE_DOT = 1;
}
}
/* This is a hack to get rid of the "What Now?" prompt. */
static void fix_prompt(void) {
descr_line *d;
if (err_ptr == nullptr) return;
d = read_descr(err_ptr[0].start, err_ptr[0].size);
if (d == nullptr) return;
if (strncasecmp(d[0], "What Now?", 9) == 0)
err_ptr[0].size = err_ptr[0].start = 0;
free_descr(d);
}
void close_game(void); /* Called by setup_game, and so needs
to be defined here. */
static fc_type setup_game(fc_type fc)
/* game_name is the common filename of the AGT game files */
{
int can_save;
char choice;
rbool have_ins;
bold_mode = 0;
rm_acct = 1;
rm_trap = 1;
rm_size = ralloc_cnt = rfree_cnt = 0;
mars_fix = 0;
no_auxsyn = 0;
debug_disambig = 0;
debug_any = 1;
dbg_nomsg = 1; /* Suppress output of MSG arguments to metacommands */
textbold = 0;
debug_mode = 0;
aver = 0;
verboseflag = 1;
notify_flag = 0;
logflag = 0;
menu_mode = 0;
fast_replay = 0;
stable_random = BATCH_MODE || make_test;
if (make_test) BATCH_MODE = 0;
hold_fc = fc;
set_default_filenames(fc);
init_stack();
read_config(agt_globalfile(0), 1); /* Global configuration file */
/* Now that we *have* PATH information, go looking for the games */
/* At the very least, it creates an rmalloc'd copy of game_name */
read_config(openfile(fc, fCFG, nullptr, 0), 0);
text_file = 0;
/* First pass through game specific config file */
build_trans_ascii();
#ifdef PROFILE
resetwatch();
#endif
writeln("Loading game...");
if (!read_agx(fc, 0) && !readagt(fc, 0))
fatal("Unable to load game.");
#ifdef PROFILE
writeln(stopwatch());
agt_waitkey();
#endif
if (have_opt)
menu_mode = opt_data[5]; /* See agtread.c for discussion of OPT file
format */
text_file = 1;
read_config(openfile(fc, fCFG, nullptr, 0), 1); /*Game specific config file*/
text_file = 0;
if (min_ver > AGIL_VERID) {
if (FORCE_VERSION)
agtwarn("This game requires a later version of AGiliTy.", 0);
else
fatal("This game requires a later version of AGiliTy.");
}
sort_cmd();
ext_dict();
build_verbmenu();
fix_dummy(); /* Prevent player from calling dummy verbs or subroutines by
typing 'Subroutine n' on the command line */
can_save = init();
if (!agx_file) open_descr(fc);
fix_prompt(); /* Kill off 'What Now?' prompt. */
if (BATCH_MODE || make_test)
set_test_mode(fc);
start_interface(fc);
fontcmd(2, 0); /* Set initial font */
if (intro_first && intro_ptr.size > 0) {
agt_clrscr();
print_descr(intro_ptr, 1);
wait_return();
}
if (aver >= AGTME10)
pictcmd(3, 0); /* Show title image, if there is one */
print_title(fc);
have_ins = open_ins_file(fc, 0);
if (have_ins) {
do {
writestr("Choose <I>nstructions, or <other> to start the game");
choice = tolower(agt_getchar()); /* Wait for keypress */
if (g_vm->shouldQuit())
return nullptr;
agt_clrscr();
if (have_ins && choice == 'i')
print_instructions(fc);
} while (choice == 'i');
}
close_ins_file();
if (!intro_first && intro_ptr.size > 0) {
print_descr(intro_ptr, 1);
wait_return();
agt_clrscr();
}
if (maxroom < first_room) {
close_game();
error("Invalid first room");
}
set_statline();
if (can_save == 0) {
writeln("[Insufficiant memory to support SAVE, RESTORE, or UNDO]");
} else if (undo_state == nullptr)
writeln("[Insufficiant memory to support UNDO]");
do_look = do_autoverb = 1;
newroom();
rm_acct = 1; /* Turn on memory allocation accounting */
return fc;
}
/* We need to import save_lnoun from exec.c so that we can free it. */
extern parse_rec *save_lnoun;
void close_game(void) {
if (agx_file)
agx_close_descr();
else
close_descr();
fontcmd(1, -1); /* Restore original font */
musiccmd(7, -1); /* Clean up */
close_interface();
/* Now free everything in sight; this _shouldn't_ be necessary,
but why take chances? */
free_all_agtread();
rfree(restart_state);
rfree(undo_state);
rfree(pictable);
rfree(save_lnoun);
rfree(verbptr);
rfree(verbend);
rfree(agt_counter);
rfree(agt_var);
free_creat_fix();
flag = (rbool *)rrealloc(flag, sizeof(rbool)); /* Preserve the debugging flag */
if (DEBUG_MEM)
debug("\n\nAlloc:%ld Freed:%ld Difference:%ld\n", ralloc_cnt,
rfree_cnt, ralloc_cnt - rfree_cnt);
}
void run_game(fc_type fc) {
doing_restore = 0;
rm_acct = 1;
rm_trap = 1;
rm_size = ralloc_cnt = rfree_cnt = 0;
read_config(agt_globalfile(0), 1); /* Global configuration file:
get PATH information*/
fix_file_context(fc, fDA1);
do {
if (doing_restore == 3) {
release_file_context(&fc);
fc = setup_game(new_game());
} else setup_game(fc);
doing_restore = 0;
if (!g_vm->shouldQuit())
mainloop();
close_game();
if (g_vm->shouldQuit())
break;
} while (doing_restore == 3);
release_file_context(&fc);
}
} // End of namespace AGT
} // End of namespace Glk

1219
engines/glk/agt/agility.h Normal file

File diff suppressed because it is too large Load Diff

106
engines/glk/agt/agt.cpp Normal file
View File

@@ -0,0 +1,106 @@
/* 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/agt.h"
#include "glk/quetzal.h"
#include "common/config-manager.h"
#include "glk/agt/agility.h"
#include "glk/agt/interp.h"
#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
AGT *g_vm;
extern void glk_main();
extern int glk_startup_code();
extern void gagt_finalizer();
AGT::AGT(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
gagt_main_window(nullptr), gagt_status_window(nullptr), gagt_gamefile(nullptr),
gagt_game_message(nullptr), gagt_delay_mode(DELAY_SHORT), gagt_font_mode(FONT_AUTOMATIC),
gagt_transcript_stream(nullptr), gagt_inputlog_stream(nullptr),
gagt_readlog_stream(nullptr), gagt_replacement_enabled(true),
gagt_extended_status_enabled(true), gagt_abbreviations_enabled(true),
gagt_commands_enabled(true), gagt_clean_exit_test(false) {
g_vm = this;
}
void AGT::runGame() {
initialize();
glk_main();
gagt_finalizer();
}
void AGT::initialize() {
_gameFile.close();
gagt_gamefile = getFilename().c_str();
initializeSettings();
glk_startup_code();
}
void AGT::initializeSettings() {
// Delay
if (ConfMan.hasKey("delay")) {
Common::String delay = ConfMan.get("delay");
switch (tolower(delay.firstChar())) {
case 'f':
// Full
gagt_delay_mode = DELAY_FULL;
break;
case 's':
// Short
gagt_delay_mode = DELAY_SHORT;
break;
case 'n':
case 'o':
// None/off
gagt_delay_mode = DELAY_OFF;
break;
default:
break;
}
}
// Boolean flags
if (ConfMan.hasKey("replacement"))
gagt_replacement_enabled = ConfMan.getBool("replacement");
if (ConfMan.hasKey("abbreviations"))
gagt_abbreviations_enabled = ConfMan.getBool("abbreviations");
if (ConfMan.hasKey("extended_status"))
gagt_extended_status_enabled = ConfMan.getBool("extended_status");
if (ConfMan.hasKey("commands"))
gagt_commands_enabled = ConfMan.getBool("commands");
}
Common::Error AGT::readSaveData(Common::SeekableReadStream *rs) {
return loadgame(rs);
}
Common::Error AGT::writeGameData(Common::WriteStream *ws) {
return savegame(ws);
}
} // End of namespace AGT
} // End of namespace Glk

125
engines/glk/agt/agt.h Normal file
View File

@@ -0,0 +1,125 @@
/* 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/>.
*
*/
/* Based on Agility interpreter version 1.1.2 */
#ifndef GLK_AGT
#define GLK_AGT
#include "common/scummsys.h"
#include "glk/glk_api.h"
namespace Glk {
namespace AGT {
enum DelayMode {
DELAY_FULL, DELAY_SHORT, DELAY_OFF
};
enum FontMode {
FONT_AUTOMATIC, FONT_FIXED_WIDTH, FONT_PROPORTIONAL, FONT_DEBUG
};
/**
* AGT Adams game interpreter
*/
class AGT : public GlkAPI {
public:
const char *gagt_gamefile; /* Name of game file. */
const char *gagt_game_message; /* Error message. */
DelayMode gagt_delay_mode;
/**
* We use two Glk windows; one is two lines at the top of the display area
* for status, and the other is the remainder of the display area, used for,
* well, everything else. Where a particular Glk implementation won't do
* more than one window, the status window remains NULL.
*/
winid_t gagt_main_window, gagt_status_window;
/**
* Transcript stream and input log. These are NULL if there is no current
* collection of these strings.
*/
strid_t gagt_transcript_stream, gagt_inputlog_stream;
/**
* Input read log stream, for reading back an input log
*/
strid_t gagt_readlog_stream;
/* Options that may be turned off or set by command line flags. */
FontMode gagt_font_mode;
bool gagt_replacement_enabled, gagt_extended_status_enabled,
gagt_abbreviations_enabled, gagt_commands_enabled;
/**
* Flag to set if we want to test for a clean exit. Without this it's a
* touch tricky sometimes to corner AGiliTy into calling exit() for us; it
* tends to require a broken game file.
*/
bool gagt_clean_exit_test;
private:
/**
* Handles initialization
*/
void initialize();
/**
* Handles flag setup from configuration
*/
void initializeSettings();
public:
/**
* Constructor
*/
AGT(OSystem *syst, const GlkGameDescription &gameDesc);
/**
* Returns the running interpreter type
*/
InterpreterType getInterpreterType() const override {
return INTERPRETER_AGT;
}
/**
* Execute the game
*/
void runGame() override;
/**
* Load a savegame from the passed Quetzal file chunk stream
*/
Common::Error readSaveData(Common::SeekableReadStream *rs) override;
/**
* Save the game. The passed write stream represents access to the UMem chunk
* in the Quetzal save file that will be created
*/
Common::Error writeGameData(Common::WriteStream *ws) override;
};
extern AGT *g_vm;
} // End of namespace AGT
} // End of namespace Glk
#endif

1744
engines/glk/agt/agtread.cpp Normal file

File diff suppressed because it is too large Load Diff

1478
engines/glk/agt/agxfile.cpp Normal file

File diff suppressed because it is too large Load Diff

600
engines/glk/agt/auxfile.cpp Normal file
View File

@@ -0,0 +1,600 @@
/* 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 "common/str.h"
namespace Glk {
namespace AGT {
/* ------------------------------------------------------------------- */
/* Purity flag initialization */
/* Logically, these belong in agtdata.c, but I wanted to keep them */
/* near the CFG reading routines. */
/* ------------------------------------------------------------------- */
/* The following are AGT 'purity' flags; they turn off features of */
/* my interpreter that are not fully consistent with the original AGT */
/* and so could break some games. Some of these are trivial improvements; */
/* some are more radical and should be used with caution. Several are */
/* only useful if a game was designed with them in mind. */
/* In all cases, setting the flag to 1 more closely follows the */
/* behavior of the original interpreters */
/* WARNING: Many of these haven't been tested extenstivly in the non-default
state. */
rbool PURE_ANSWER = 0; /* For ME questions, requires that AND-separated
answers be in the same order in the player's
answer as they are in the game file. According
to the AGT documentation, AND should ignore
the order, but the original AGT interpreters
(at least the one I've tested) don't conform
to this. */
rbool PURE_TIME = 1; /* Set to 0 causes time to always be increased
by delta_time rather than by a random amount
between 0 and delta_time. Only really of any use
to a game author who wanted to write a game
explicitly for AGiliTy. */
/* rbool PURE_BOLD=1; Set to 0 causes the backslash to toggle bold on and
off for all versions of AGT, not just 1.8x.
I can think of no reason to do this unless
you are an AGT author who wants to use the 1.8x
bold feature with the Master's Edition compiler. */
rbool PURE_AND = 1; /* increment the turn counter for each noun in a
chain of <noun> AND <noun> AND ... If 0, the turn
counter will only be incremented by one in such a case.
(need to do something about metacommands, as well...) */
rbool PURE_METAVERB = 1; /* If set, ANY and AFTER commands are run even
if you type in a metaverb (SAVE, RESTORE,...
that is, any verb that doesn't cause time to
pass). Verb specific metacommands are _always_
run. */
rbool PURE_ROOMTITLE = 1; /* If 0, the interpreter will print out room
names before room descriptions even for
pre-ME games */
rbool PURE_SYN = 0; /* Treats synonyms as nouns when parsing: that is, they
must show up only as the last word and they have the
same priority as noun matches during disambiguation.
If this is 0, then synonyms can appear anywhere in
the name the player types in but are still
disambiguated as nouns. */
rbool PURE_NOUN = 0; /* _Requires_ a noun to end a word. This is only
imperfectly supported: if there are no other
possible matches the parser will take the adjective-
only one anyhow. Frankly, I can't think of any reason
to set this to 1, but it's included for completeness
sake (and for any AGT Purists out there :-) ) */
rbool PURE_ADJ = 1; /* Picks noun/syn-matches over pure adj matches
when disambiguating. This is redundant if PURE_NOUN=1
since in that case pure adjective matches will
be rejected anyhow. */
rbool PURE_DUMMY = 0; /* If set, the player can running dummy verbs
in the game by typing 'dummy_verb3'; otherwise,
this will produce an error message */
rbool PURE_SUBNAME = 0; /* If set, the player can run subroutines from
the parse line by typing (e.g.) 'subroutine4'
(yes, the original AGT interpreters actually
allow this). If cleared, this cheat isn't
available */
rbool PURE_PROSUB = 0; /* If clear, then $you$ substitutions are done
everywhere $$ substitutions are, even in
messages written by the game author.
If set, these substitutions are only made
in internal game messages */
rbool PURE_HOSTILE = 1; /* =0 Will allow you to leave a room with a hostile
creature if you go back the way you came */
rbool PURE_ALL = 1; /* =0 will cause the parser to expand ALL */
rbool PURE_DISAMBIG = 1; /* =0 will cause intelligent disambiguation */
rbool PURE_GETHOSTILE = 1; /* =0 will prevent the player from picking things
up in a room with a hostile creature */
rbool PURE_OBJ_DESC = 1; /* =0 prevents [providing light] messages
from being shown */
rbool PURE_ERROR = 0; /* =1 means no GAME ERROR messages will be printed
out */
rbool PURE_SIZE = 1; /* =0 eliminates size/weight limits on how many
things the player can wear or carry. (But it's
still impossible to pick things up that are
in themselves larger than the player's capacity) */
rbool PURE_GRAMMAR = 1; /* =0 prints error messages if the player uses a
built in verb with an extra object.
(e.g. YELL CHAIR). Otherwise, the extra object
will just be ignored. */
rbool PURE_SYSMSG = 1; /* =0 causes AGiliTy to always use the default
messages even if the game file has its own
standard error messages. */
rbool PURE_AFTER = 1; /* =0 causes LOOK and other end-of-turn events
to happen *before* AFTER commands run. */
rbool PURE_PROPER = 1; /* Don't automatically treat creatures as proper nouns */
rbool TWO_CYCLE = 0; /* AGT 1.83-style two-cycle metacommand execution. */
rbool FORCE_VERSION = 0; /* Load even if the version is wrong. */
/*-------------------------------------------------------------------------*/
/* .CFG reading routines */
/*-------------------------------------------------------------------------*/
/* The main interpreter handles configuration in this order:
1) Global configuration file
2) First pass through game specific CFG to get the settings for
SLASH_BOLD and IBM_CHAR which we need to know _before_ reading
in the game.
3) Read in the game.
4) Main pass through game specific CFG. Doing it here ensures that
its settings will override those in the gamefile.
Secondary programs (such as agt2agx) usually only call this once, for
the game specific configuration file.
*/
#define opt(s) (strcasecmp(optstr[0],s)==0)
static void cfg_option(int optnum, char *optstr[], rbool lastpass)
/* This is passed each of the options; it is responsible for parsing
them or passing them on to the platform-specific option handler
agt_option() */
/* lastpass is set if it is the last pass through this configuration
file; it is false only on the first pass through the game specific
configuration file during the run of the main interpreter */
{
rbool setflag;
if (optnum == 0 || optstr[0] == nullptr) return;
if (strncasecmp(optstr[0], "no_", 3) == 0) {
optstr[0] += 3;
setflag = 0;
} else setflag = 1;
if (opt("slash_bold")) bold_mode = setflag;
else if (!lastpass) {
/* On the first pass, we ignore all but a few options */
agil_option(optnum, optstr, setflag, lastpass);
return;
} else if (opt("irun")) irun_mode = setflag;
else if (opt("block_hostile")) PURE_HOSTILE = setflag;
else if (opt("get_hostile")) PURE_GETHOSTILE = setflag;
else if (opt("debug")) {
if (!agx_file && aver <= AGTME10) debug_mode = setflag;
if (setflag == 0) debug_mode = 0; /* Can always turn debugging support off */
} else if (opt("pure_answer")) PURE_ANSWER = setflag;
else if (opt("const_time")) PURE_TIME = !setflag;
else if (opt("fix_multinoun")) PURE_AND = !setflag;
else if (opt("fix_metaverb")) PURE_METAVERB = !setflag;
else if (opt("roomtitle")) PURE_ROOMTITLE = !setflag;
else if (opt("pure_synonym")) PURE_SYN = setflag;
else if (opt("adj_noun")) PURE_ADJ = !setflag;
else if (opt("pure_dummy")) PURE_DUMMY = setflag;
else if (opt("pure_subroutine")) PURE_SUBNAME = setflag;
else if (opt("pronoun_subs")) PURE_PROSUB = !setflag;
else if (opt("verbose")) verboseflag = setflag;
else if (opt("fixed_font")) font_status = 1 + !setflag;
else if (opt("alt_any")) mars_fix = setflag;
else if (opt("smart_disambig")) PURE_DISAMBIG = !setflag;
else if (opt("expand_all")) PURE_ALL = !setflag;
else if (opt("object_notes")) PURE_OBJ_DESC = setflag;
else if (opt("error")) PURE_ERROR = !setflag;
else if (opt("ignore_size")) PURE_SIZE = !setflag;
else if (opt("check_grammar")) PURE_GRAMMAR = !setflag;
else if (opt("default_errors")) PURE_SYSMSG = !setflag;
else if (opt("pure_after")) PURE_AFTER = !setflag;
else if (opt("proper_creature")) PURE_PROPER = !setflag;
else agil_option(optnum, optstr, setflag, lastpass);
}
#undef opt
/* Returns false if it there are too many tokens on the line */
rbool parse_config_line(char *buff, rbool lastpass) {
char *opt[50], *p;
int optc;
optc = 0;
opt[0] = nullptr;
for (p = buff; *p; p++) {
if (isspace(*p)) { /* Whitespace */
if (opt[optc] != nullptr) { /*... which means this is the first whitespace */
if (optc == 50) return 0; /* Too many */
opt[++optc] = nullptr;
}
*p = 0;
} else /* No whitespace */
if (opt[optc] == nullptr) /* ...this is the first non-whitespace */
opt[optc] = p;
}
if (opt[optc] != nullptr) opt[++optc] = nullptr;
cfg_option(optc, opt, lastpass);
return 1;
}
/* For the meaning of lastpass, see comments to cfg_option() above */
void read_config(genfile cfgfile, rbool lastpass) {
char buff[100];
if (lastpass) {
/* Default to smart disambiguation for 1.5 onwards */
if (aver >= AGT15) PURE_DISAMBIG = 0;
}
if (!filevalid(cfgfile, fCFG)) return;
while (readln(cfgfile, buff, 99)) {
if (buff[0] == '#') continue; /* Comments */
/* Now we parse the line into words, with opt[] pointing at the words
and optc counting how many there are. */
if (!parse_config_line(buff, lastpass))
rprintf("Too many tokens on configuration line.\n");
}
readclose(cfgfile);
}
/*-------------------------------------------------------------------------*/
/* Read OPT file */
/* (most of these routines used to be in agil.c) */
/*-------------------------------------------------------------------------*/
/* .OPT reading routines */
/* I've put the comments on the format here because they don't really
belong anywhere else. (Maybe in agility.h, but I don't want to further
clutter that already quite cluttered file with something as peripheral
as this) */
/* OPT file format: the .OPT file consists of 14 bytes. They are:
0 Screen size(0=43/50 rows, 1=25 rows)
1 Status line(1=top, 0=none, -1=bottom)
2 Unknown, always seems to be 0
3 Put box around status line?
4 Sound on?
5 Menus on?
6 Fixed input line?
7 Print transcript?
8 Height of menus (3, 4, 5, 6, 7, or 8)
9 Unknown, always seems to be 0
10-13 Color scheme: output/status/input/menu, specified in DOS attribute
format (Bbbbffff, B=blink, b=backround, f=foreground,
MSB of foreground specifies intensity ("bold") ). */
/* The interpreter ignores almost all of this. */
void read_opt(fc_type fc) {
const char *errstr;
genfile optfile;
have_opt = 0;
optfile = openbin(fc, fOPT, nullptr, 0);
if (filevalid(optfile, fOPT)) {
if (!binread(optfile, opt_data, 14, 1, &errstr))
fatal("Invalid OPT file.");
have_opt = 1;
readclose(optfile);
}
}
/*-------------------------------------------------------------------------*/
/* Read and process TTL */
/* (most of these routines used to be in agil.c) */
/*-------------------------------------------------------------------------*/
/* Shades of Gray uses a custom interpreter that prints out the names
of the authors as the program loads. */
/* Normally I wouldn't bother with this, but Shades of Gray is probably
the best known of all AGT games */
#define SOGCREDIT 7
static const char *sogauthor[SOGCREDIT] = {
"Mark \"Sam\" Baker",
"Steve \"Aaargh\" Bauman",
"Belisana \"The\" Magnificent",
"Mike \"of Locksley\" Laskey",
"Judith \"Teela Brown\" Pintar",
"Hercules \"The Loyal\" SysOp",
"Cindy \"Nearly Amelia\" Yans"
};
static rbool check_dollar(char *s)
/* Determines if s consists of an empty string with a single dollar sign
and possibly whitespace */
{
rbool dfound;
dfound = 0;
for (; *s != 0; s++)
if (*s == '$' && !dfound) dfound = 1;
else if (!rspace(*s)) return 0;
return dfound;
}
descr_line *read_ttl(fc_type fc) {
genfile ttlfile;
int i, j, height;
descr_line *buff;
ttlfile = openfile(fc, fTTL, nullptr, 0);
/* "Warning: Could not open title file '%s'." */
if (!filevalid(ttlfile, fTTL)) return nullptr;
build_fixchar();
buff = (descr_line *)rmalloc(sizeof(descr_line));
i = 0;
while (nullptr != (buff[i] = readln(ttlfile, nullptr, 0))) {
if (strncmp(buff[i], "END OF FILE", 11) == 0) break;
else if (aver >= AGT18 && aver <= AGT18MAX && check_dollar(buff[i]))
statusmode = 4;
else {
for (j = 0; buff[i][j] != 0; j++)
buff[i][j] = fixchar[(uchar)buff[i][j]];
/* Advance i and set the next pointer to NULL */
buff = (descr_line *)rrealloc(buff, sizeof(descr_line) * (++i + 1));
buff[i] = nullptr;
}
rfree(buff[i]);
}
readclose(ttlfile);
rfree(buff[i]);
while (buff[i] == nullptr || strlen(buff[i]) <= 1) { /* Discard 'empty' lines */
if (i == 0) break;
rfree(buff[i]);
i--;
}
height = i;
if (aver == AGTCOS && ver == 4 && height >= 17) /* SOGGY */
for (i = 0; i < SOGCREDIT; i++)
if (strlen(sogauthor[i]) + 9 + i < strlen(buff[i + 7]))
memcpy(buff[i + 7] + 9 + i, sogauthor[i], strlen(sogauthor[i]));
return buff;
}
void free_ttl(descr_line *title) {
int i;
if (title == nullptr) return;
for (i = 0; title[i] != nullptr; i++)
rfree(title[i]);
rfree(title);
}
/*-------------------------------------------------------------------------*/
/* Read and convert VOC */
/* (most of these routines used to be in agil.c) */
/*-------------------------------------------------------------------------*/
static const char *newvoc[] = { "1 Menu", "1 Restart", "1 Undo" };
static int newindex = 0; /* Points into newvoc */
void add_verbrec(const char *verb_line, rbool addnew) {
char s[3];
Common::String verbStr(verb_line);
while (!verbStr.empty() && rspace(verbStr.firstChar()))
verbStr.deleteChar(0);
if (verbStr.empty() || verbStr.hasPrefix("!"))
return; /* Comment or empty line */
/* The following guarantees automatic initialization of the verbrec structures */
if (!addnew)
while (newindex < 3 && strcasecmp(verbStr.c_str() + 2, newvoc[newindex] + 2) > 0)
add_verbrec(newvoc[newindex++], 1);
verbinfo = (verbentry_rec *)rrealloc(verbinfo, (vm_size + 1) * sizeof(verbentry_rec));
s[0] = verbStr.firstChar();
s[1] = 0;
verbinfo[vm_size].objnum = strtol(s, nullptr, 10) - 1;
verbStr.deleteChar(0);
verbStr.deleteChar(0);
verbinfo[vm_size].verb = verbinfo[vm_size].prep = 0;
uint idx = 0;
while (idx < verbStr.size()) {
while (idx < verbStr.size() && !rspace(verbStr[idx]))
++idx;
if (idx < verbStr.size()) {
verbStr.setChar('\0', idx);
++idx;
}
verbinfo[vm_size].verb = search_dict(verbStr.c_str());
if (verbinfo[vm_size].verb == -1) {
verbinfo[vm_size].verb = 0;
return;
}
if (idx < verbStr.size()) {
verbinfo[vm_size].prep = search_dict(verbStr.c_str() + idx);
if (verbinfo[vm_size].prep == -1)
verbinfo[vm_size].prep = 0;
}
}
vm_size++;
}
void init_verbrec(void)
/* Need to insert special verbs into verbinfo */
/* Fill in vnum field */
/* UNDO, RESTART, MENU */
{
verbinfo = nullptr;
vm_size = 0;
newindex = 0;
if (freeze_mode) newindex = 1; /* Don't include MENU option if we can't
use it. */
}
void finish_verbrec(void) {
for (; newindex < 3; newindex++) add_verbrec(newvoc[newindex], 1);
}
void read_voc(fc_type fc) {
char linbuf[80];
genfile vocfile;
init_verbrec();
vocfile = openfile(fc, fVOC, nullptr, 0);
if (filevalid(vocfile, fVOC)) { /* Vocabulary file exists */
while (readln(vocfile, linbuf, 79))
add_verbrec(linbuf, 0);
readclose(vocfile);
finish_verbrec();
}
}
/*-------------------------------------------------------------------------*/
/* Read INS file */
/* (most of these routines used to be in agil.c) */
/*-------------------------------------------------------------------------*/
static genfile insfile = BAD_TEXTFILE;
static char *ins_buff;
static descr_line *ins_descr = nullptr;
static int ins_line; /* Current instruction line */
/* Return 1 on success, 0 on failure */
rbool open_ins_file(fc_type fc, rbool report_error) {
ins_buff = nullptr;
ins_line = 0;
if (ins_descr != nullptr) return 1;
if (filevalid(insfile, fINS)) {
textrewind(insfile);
return 1;
}
if (agx_file) {
ins_descr = read_descr(ins_ptr.start, ins_ptr.size);
if (ins_descr != nullptr) return 1;
/* Note that if the AGX file doesn't contain an INS block, we
don't immediately give up but try opening <fname>.INS */
}
insfile = openfile(fc, fINS,
report_error
? "Sorry, Instructions aren't available for this game"
: nullptr,
0);
return (filevalid(insfile, fINS));
}
char *read_ins_line(void) {
if (ins_descr) {
if (ins_descr[ins_line] != nullptr)
return ins_descr[ins_line++];
else return nullptr;
} else {
rfree(ins_buff);
ins_buff = readln(insfile, nullptr, 0);
return ins_buff;
}
}
void close_ins_file(void) {
if (ins_descr) {
free_descr(ins_descr);
ins_descr = nullptr;
} else if (filevalid(insfile, fINS)) {
rfree(ins_buff);
readclose(insfile);
insfile = BAD_TEXTFILE;
}
}
descr_line *read_ins(fc_type fc) {
descr_line *txt;
char *buff;
int i;
i = 0;
txt = nullptr;
if (open_ins_file(fc, 0)) { /* Instruction file exists */
while (nullptr != (buff = read_ins_line())) {
/* Enlarge txt; we use (i+2) here to leave space for the trailing \0 */
txt = (descr_line *)rrealloc(txt, sizeof(descr_ptr) * (i + 2));
txt[i++] = rstrdup(buff);
}
if (txt != nullptr)
txt[i] = nullptr; /* There is space for this since we used (i+2) above */
close_ins_file();
}
return txt;
}
void free_ins(descr_line *instr) {
int i;
if (instr == nullptr) return;
for (i = 0; instr[i] != nullptr; i++)
rfree(instr[i]);
rfree(instr);
}
/* Character translation routines, used by agtread.c and read_ttl() */
void build_fixchar(void) {
int i;
for (i = 0; i < 256; i++) {
if (i == '\r' || i == '\n') fixchar[i] = ' ';
else if (i == '\\' && bold_mode) fixchar[i] = FORMAT_CODE;
else if (i >= 0x80 && fix_ascii_flag)
fixchar[i] = trans_ibm[i & 0x7f];
else if (i == 0) /* Fix color and blink codes */
fixchar[i] = FORMAT_CODE;
else fixchar[i] = i;
}
}
} // End of namespace AGT
} // End of namespace Glk

287
engines/glk/agt/config.h Normal file
View File

@@ -0,0 +1,287 @@
/* 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/>.
*
*/
#ifndef GLK_AGT_CONFIG
#define GLK_AGT_CONFIG
#include "glk/glk_api.h"
#include "common/stream.h"
namespace Glk {
namespace AGT {
/*
This file contains most of the configuration information
including the platform-dependent #define statements
It's in three major sections:
--Platform specific defines for various platforms, each
surrounded by "#ifdef <platform>" and "#endif"
--Various defaults
--Filename extensions
Ideally, a port to a new platform should only need to modify this
file, the makefile, os_<whatever>.c, and possibly filename.c. (In
practice, you may also need to tweak the high-level I/O code
in interface.c or the memory-allocation code in util.c. If you
find yourself needing to do more than that, get in touch with me.) */
#undef _WIN32 /* GARGLK */
/* Default to PLAIN platform */
/* At the moment, you can replace this with LINUX, HPUX, AMIGA, */
/* MSDOS, SUN, or NEXT; some of these may require the correct os_... */
/* file to work */
/* (In particular, AMIGA requires David Kinder's os_amiga.c file) */
/* The actual platform specific defines don't start until a few */
/* lines down, past the #includes and the definition of global */
#ifndef PLAIN
#define PLAIN
#endif
/* ------------------------------------------------------------------- */
/* PLATFORM SPECIFIC DEFINITIONS, ETC. */
/* See agility.doc or porting.txt for more information. */
/* Things you can currently define: */
/* fix_ascii: 1=translate IBM character set, 0=don't */
/* NEED_STR_CMP: define if strcasecmp() not defined on your system */
/* NEED_STRN_CMP: define if strncasecmp() not defined on your system */
/* HAVE_STRDUP: define if strdup() exists on your system */
/* REPLACE_GETFILE: define if you replace the default get_user_file(). */
/* REPLACE_MENU if you replace agt_menu(). */
/* REPLACE_MAIN: define if you replace the default main(). */
/* (replacements should be defined in the relevant os_<platform>.c file) */
/* DA1,DA2,...DA6,DSS,pTTL: file name extensions for the various AGT
files */
/* HAVE_SLEEP if your platform has the sleep() function */
/* BUFF_SIZE is the maximum size of the buffer to use when reading
in files. Regardless, it will be made no bigger than the file
being read in and no smaller than the record size; thus setting
it to 0 will cause the smallest buffer to always be used and
setting this to 1MB will in practice always use a buffer the
sizs of the file. It defaults to 32K */
/* CBUF_SIZE is the maximum size of the buffer used for reading in
the Master's Edition DA6 files; the size of the buffer in bytes
is twice this value (since an individual token is two bytes long). */
/* DESCR_BUFFSIZE is the maximum size of the description text block before
the interpreter will read it from disk rather than storing it in
memory during play. At the moment this only affects AGX games;
original AGT games always use the disk. */
/* DOHASH to use a hash table for dictionary searches; the only
reason not to have this would be memory */
/* HASHBITS determines the size of the hash table: (2^HASHBITS)*sizeof(word);
the hash table must be at least as large as the dictionary.
In practice this means HASHBITS should be at least 12;
this is the current default. */
/* MAXSTRUC The maximum size (in chars) which a single data structure can
be on this platform. This defaults to 1MB (i.e. no limit for
practical purposes). In practice I know of no game files that
require any structures bigger than about 30K. */
/* LOWMEM Define this if you are low on memory. At the moment this
only saves a few K.*/
/* PORTSTR Is the string describing this particular port.
e.g. #define PORTSTR "OrfDOS Port by R.J. Wright" */
/* UNIX_IO if you have Unix-like low level file I/O functions.
(MS-DOS, for example, does). This speeds up the reading
of the large game data files on some platforms. If this is
defined, READFLAG, WRITEFLAG, and FILE_PERM also need to
be defined. (Giving the flags needed for opening a file for
reading or writing, and the file permissions to be given to newly
created files. */
/* OPEN_AS_TEXT Define to cause text files to be opened as text files. */
/* PREFIX_EXT Add filename extensions at the beginning of the name,
rather than at the end. */
/* PATH_SEP, if defined, is a string containing all characters which
can be used to separate the path from the filename. */
/* pathtest(s) is a macro that should check whether the given string
is an absolute path. If this is left undefined, then _all_
paths will be treated as absolute. You don't need to define
this if you are replacing filename.c. */
/* ------------------------------------------------------------------- */
/* force16 is used purely for debugging purposes, to make sure that
everything works okay even with 16-bit ints */
/* #define force16 */
#define DOHASH
/*
* The Glk port is very similar to plain ASCII, to give it the best
* chance at success on multiple Glk platforms. The only basic change
* is to turn off IBM character translations; Glk works in ISO 8859
* Latin-1, which can offer slightly closer translation of the IBM
* code page 437 characters that the simpler mappings in the core
* AGiliTy code. The os_glk.c module handles the translations.
*/
#ifdef GARGLK
#define NEED_STR_CMP /* Inherited from PLAIN. */
#define NEED_STRN_CMP /* Inherited from PLAIN. */
#define BUFF_SIZE 0 /* Inherited from PLAIN. */
#define CBUF_SIZE (5000L) /* Inherited from PLAIN. */
#define INBUFF_SIZE (1024) /* Inherited from PLAIN. */
#define fix_ascii 0 /* os_glk.c does translations. */
#define MAXSTRUC (1024L*1024L) /* 32Kb from PLAIN is too small for
several games (including Soggy). */
#define PORTSTR "Glk version" /* Identify ourselves discreetly. */
#define REPLACE_GETFILE /* Override get_user_file. */
#define REPLACE_MAIN /* Override main. */
#define fnamecmp strcasecmp /* Case insensitive filename compare. */
#undef PLAIN
#endif
/* PLAIN should always come last, giving everyone else a chance
to #undef it. */
#ifdef PLAIN /* This should work if nothing else does */
#define NEED_STR_CMP
#define NEED_STRN_CMP
#define BUFF_SIZE 0
#define CBUF_SIZE (5000L)
#define INBUFF_SIZE (1024) /* Used by Magx */
#define MAXSTRUC (32L*1024L) /* IIRC, 32K is the minimum required by
the ANSI standard */
#define PORTSTR "Pure ANSI C version"
#endif
/* __GNUC__ */
/* ------------------------------------------------------------------- */
/* DEFAULTS FOR "PLATFORM SPECIFIC" DEFINES */
/* ------------------------------------------------------------------- */
#ifdef __STRICT_ANSI__
#define NEED_STR_CMP
#define NEED_STRN_CMP
#endif
#ifndef fix_ascii
#define fix_ascii 1 /* Translate IBM character set by default */
#endif
#ifndef BUFF_SIZE
#ifdef LOWMEM
#define BUFF_SIZE 0 /* i.e. unbuffered */
#else
#define BUFF_SIZE (32L*1024L) /* 32K */
#endif
#endif /* BUFF_SIZE */
#ifndef MAXSTRUC
#define MAXSTRUC (1024L*1024L)
#endif
#ifndef DESCR_BUFFSIZE
#define DESCR_BUFFSIZE 0 /* Always load descriptions from disk */
#endif
#ifndef HASHBITS
#ifdef LOWMEM
#define HASHBITS 12 /* 4K entries */
#else
#define HASHBITS 13 /* 8K entries in hash table */
#endif
#endif /* HASHBITS */
#ifndef fnamecmp /* Used to compare filenames */
#define fnamecmp strcmp
#endif
#ifndef fnamencmp /* Also used to compare filenames */
#define fnamencmp strncmp
#endif
/* If DOSFARDATA hasn't been defined, define it as the empty string. */
#ifndef DOSFARDATA
#define DOSFARDATA
#endif
/* ---------------------------------------------------------------------- */
/* FILENAME EXTENSIONS */
/* These are the various filename extensions for the different data files.*/
/* ---------------------------------------------------------------------- */
/* The following are only used by the interpreter, agtout, and agt2agx */
#ifndef DA1
#define DA1 ".da1" /* General info (text file) */
#define DA2 ".da2" /* Rooms */
#define DA3 ".da3" /* Items */
#define DA4 ".da4" /* Creatures */
#define DA5 ".da5" /* Commands, headers */
#define DA6 ".da6" /* Commands, code (Master's Ed only) */
#define DSS ".d$$" /* Description strings */
#define pHNT ".hnt" /* Popup hint file; not used yet. */
#define pOPT ".opt" /* Interface specification file */
#endif
/* The following are only used by the Magx compiler */
#ifndef pAGT
#define pAGT ".agt"
#define pDAT ".dat"
#define pMSG ".msg"
#define pCMD ".cmd"
#define pSTD ".std"
#define AGTpSTD "agt.std" /* Default error message file */
#endif
/* The following are used by both the interpreter and the compiler */
#ifndef pAGX
#define pAGX ".agx" /* Extension for new Adventure Game eXecutable format */
#define pTTL ".ttl" /* Title file */
#define pINS ".ins" /* Instruction file */
#define pVOC ".voc" /* Menu vocabulary file */
#define pCFG ".cfg" /* Game configuration file */
#define pEXT "." /* Separator between extension and base of filename */
#endif
#ifndef pSAV
#define pSAV ".sav" /* Extension for save files */
#endif
#ifndef pSCR
#define pSCR ".scr" /* Script file */
#endif
#ifndef pLOG
#define pLOG ".log" /* LOG/REPLAY file */
#endif
/* Finally, two potentially platform dependent type defintions,
for binary and text files respectively. Don't change these
unless you are also changing filename.c */
typedef Common::Stream *genfile;
typedef char *file_id_type; /* i.e. the filename */
#define NO_FILE_ID NULL
#define BAD_TEXTFILE NULL
#define BAD_BINFILE NULL
} // End of namespace AGT
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,873 @@
/* 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"
#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
static void d_moveobj(int obj, int dest)
/* 1=the player, -1=unknown: ask */
{
if (obj == -1) {
writestr("Which object? ");
obj = read_number();
if (obj != 1 && !tnoun(obj) && !tcreat(obj)) {
writeln("Invalid object");
return;
}
}
if (dest == -1) {
writestr("To where? ");
dest = read_number();
if (dest != 1 && dest != 0 && !tnoun(dest) && !tcreat(dest) && !troom(dest)) {
writeln("Invalid object");
return;
}
}
if (obj != 1)
it_move(obj, dest);
else {
if (!troom(dest)) {
writeln("Player can only be moved to a room");
return;
}
goto_room(dest - first_room);
}
}
static int print_objid(int obj) {
char buff[10];
char *s;
int n;
Common::sprintf_s(buff, "%4d: ", obj);
writestr(buff);
s = objname(obj);
for (n = 0; s[n] != 0; n++)
if (s[n] <= 8 || (uchar)s[n] == 0xFF) s[n] = ' '; /* Strip out format codes */
writestr(s);
n = strlen(s);
rfree(s);
return n;
}
static void d_listroom() {
int i;
writeln(" ROOM");
writeln(" ------");
writeln("");
for (i = 0; i <= maxroom - first_room; i++) {
print_objid(i + first_room);
writeln("");
}
}
#define SEPLENG 27 /* Width between beginning of object column and
location column */
static void d_listnoun() {
int i;
int len;
writestr(" NOUN ");
padout(SEPLENG - 6);
writeln(" LOCATION ");
writestr(" ------");
padout(SEPLENG - 6);
writeln(" ----------");
writeln("");
len = SEPLENG - print_objid(1);
padout(len);
writestr("[");
print_objid(loc);
writeln("]");
nounloop(i) {
len = print_objid(i + first_noun);
len = SEPLENG - len;
if (len > 0) padout(len);
writestr("[");
print_objid(noun[i].location);
writeln("]");
}
}
static void d_listcreat() {
int i;
int len;
writestr(" CREATURE ");
padout(SEPLENG - 11);
writeln(" LOCATION ");
writestr(" ----------");
padout(SEPLENG - 11);
writeln(" ----------");
writeln("");
creatloop(i) {
len = print_objid(i + first_creat);
len = SEPLENG - len;
if (len > 0) padout(len);
writestr(" [");
print_objid(creature[i].location);
writeln("]");
}
}
static void writetbl(const char *s, int width)
/* This writes out s and then prints out any additional spaces needed
to make the output string *width* wide. */
{
writestr(s);
width = width - strlen(s);
if (width > 0) padout(width);
}
static void var_edit(int vtype)
/* vtype=0 for variable, 1 for counter, 2 for flag */
{
long n;
int i;
int imax;
char sbuff[30];
switch (vtype) {
case 0:
imax = VAR_NUM;
break;
case 1:
imax = CNT_NUM;
break;
case 2:
imax = FLAG_NUM;
break;
default:
writeln("INTERNAL ERROR: Invalid vtype.");
return;
}
for (;;) {
agt_clrscr();
writeln("");
switch (vtype) {
case 0:
writeln("Variables");
break;
case 1:
writeln("Counters (-1 means the counter is off)");
break;
case 2:
writeln("Flags ( f=false [OFF] and t=true [ON] )");
break;
default:
break;
}
writeln("");
for (i = 0; i <= imax; i++) {
switch (vtype) {
case 0:
Common::sprintf_s(sbuff, "[Var%3d]=%4ld", i, (long)agt_var[i]);
break;
case 1:
Common::sprintf_s(sbuff, "[Cnt%3d]=%4ld", i, (long)agt_counter[i]);
break;
case 2:
Common::sprintf_s(sbuff, "%3d%c", i, flag[i] ? 't' : 'f');
break;
default:
break;
}
writetbl(sbuff, (vtype == 2) ? 5 : 20);
}
writeln("");
writeln("");
for (;;) {
switch (vtype) {
case 0:
writestr("Variable to change");
break;
case 1:
writestr("Counter to change");
break;
case 2:
writestr("Flag to toggle");
break;
default:
break;
}
writestr(" (-1 to quit): ");
i = read_number();
if (i < 0) return;
if (i <= imax) {
if (vtype != 2) {
if (vtype == 0)
Common::sprintf_s(sbuff, "[Var%d]=%ld", i, (long)agt_var[i]);
else Common::sprintf_s(sbuff, "[Cnt%d]=%ld (-1 means it's off)",
i, (long)agt_counter[i]);
writestr(sbuff);
writestr("; new value = ");
n = read_number();
if (vtype == 0)
agt_var[i] = n;
else if (n < -1 || n > (((long)1) << 15) - 1)
writeln("Invalid value for a counter.");
else agt_counter[i] = n;
} else flag[i] = !flag[i];
break;
} else
writeln("Invalid index.");
}
}
}
/* Routines to edit user strings */
static void edit_str() {
int i, j;
char buff[10];
char *tmpstr;
if (MAX_USTR == 0 || userstr == nullptr) {
writeln("This game doesn't contain any user strings");
return;
}
for (;;) {
agt_clrscr();
writeln("User Definable Strings");
writeln("");
for (i = 0; i < MAX_USTR; i++) {
Common::sprintf_s(buff, "%2d:", i + 1);
writestr(buff);
writeln(userstr[i]);
}
writestr(" (0 to quit): ");
i = read_number();
if (i == 0) return;
if (i > 0 && i <= MAX_USTR) {
writeln("Enter new string:");
tmpstr = agt_readline(3);
j = strlen(tmpstr) - 1;
if (j > 0 && tmpstr[j] == '\n') tmpstr[j] = 0;
strncpy(userstr[i - 1], tmpstr, 80);
} else writeln("Invalid string number");
}
}
static uchar attrcol; /* Determines which column the attribute is put in */
static uchar attrwidth; /* Number of attribute columns */
static void next_col() {
if (++attrcol == attrwidth) {
writeln("");
attrcol = 0;
} else
padout(10);
}
static void writeattr(const char *attrname, rbool attrval) {
writestr(attrname);
padout(15 - strlen(attrname));
if (attrval) writestr("yes");
else writestr("no ");
next_col();
}
static void writegender(const char *gendername, uchar genderval) {
writestr(gendername);
padout(15 - strlen(gendername) - 3);
switch (genderval) {
case 2:
writestr("Male ");
break;
case 1:
writestr("Female");
break;
case 0:
writestr("Thing");
break;
default:
break;
}
next_col();
}
static void writeprop(const char *propname, int obj) {
writestr(propname);
writestr(" [");
print_objid(obj);
writeln("]");
}
static int writedir(int index, int dir, int obj) {
char sbuff[40];
Common::sprintf_s(sbuff, "%2d.%-2s %d", index, exitname[dir], obj);
writestr(sbuff);
return strlen(sbuff);
}
void writenum(const char *propname, int n) {
char sbuff[20];
writestr(propname);
Common::sprintf_s(sbuff, "%4d", n);
writeln(sbuff);
}
static void writeflags(const char *flagname, int32 flags) {
int i;
char sbuff[5];
writestr(flagname);
for (i = 0; i < 32; i++) {
if (flags & 1) {
Common::sprintf_s(sbuff, "%2d ", i);
writestr(sbuff);
} else
writestr(" ");
flags >>= 1;
if (i % 12 == 11) {
writeln("");
padout(strlen(flagname));
}
}
writeln("");
}
static void readflags(int32 *flags) {
long n;
writestr("Room flag to toggle (0-31)? ");
n = read_number();
if (n <= 31 && n >= 0)
*flags ^= (((long)1) << n);
}
static long readval(const char *prompt, int type) {
long val;
for (;;) {
writestr(prompt);
writestr(" ");
val = read_number();
if (argvalid(type, val)) return val;
writeln("Invalid value.");
}
}
static uchar readgender() {
char c;
writestr("Gender (M/F/N): ");
for (;;) {
c = tolower(agt_getchar());
switch (c) {
case 'm':
return 2;
case 'w':
case 'f':
return 1;
case 'n':
case 't':
return 0;
default: ;/* Do nothing */
}
}
}
static void edit_objattr(int obj) {
int i, k, kprop, n;
long v;
for (;;) {
k = 1;
agt_clrscr();
print_objid(obj);
writeln("");
if (oflag_cnt > 0) {
writeln("ObjFlags:");
for (i = 0; i < oflag_cnt; i++)
if (have_objattr(0, obj, i)) {
v = op_objflag(2, obj, i);
rprintf("%2d. ObjProp%2d:%c %-40s\n", k++, i, (v ? '+' : '-'),
get_objattr_str(AGT_OBJFLAG, i, v));
}
writeln("");
}
kprop = k;
if (oprop_cnt > 0) {
writeln("ObjProps:");
for (i = 0; i < oprop_cnt; i++)
if (have_objattr(1, obj, i)) {
v = op_objprop(2, obj, i, 0);
rprintf("%2d. ObjFlag%2d: [%3ld] %-40s\n", k++, i, v,
get_objattr_str(AGT_OBJPROP, i, v));
}
writeln("");
}
writestr("Field to change (0 to return to main view)? ");
n = read_number();
if (n == 0) return;
if (n < 1 || n >= k) continue;
k = 0;
if (n < kprop) { /* Attribute */
for (i = 0; i < oflag_cnt; i++)
if (have_objattr(0, obj, i))
if (n == ++k) break;
if (n == k && have_objattr(0, obj, i))
op_objflag(3, obj, i); /* Toggle it */
} else { /* Property */
for (i = 0; i < oprop_cnt; i++)
if (have_objattr(1, obj, i))
if (n == ++k) break;
if (n == k && have_objattr(1, obj, i))
op_objprop(1, obj, i, readval("New value:", AGT_NUM));
}
}
}
static void room_edit(int i) {
int n, j;
for (;;) {
agt_clrscr();
writestr("ROOM ");
print_objid(i + first_room);
writeln("");
writeln("");
attrcol = 0;
attrwidth = 2;
writeattr("1.*WinGame:", room[i].win);
writeattr("4. Seen:", room[i].seen);
writeattr("2.*EndGame:", room[i].end);
writeattr("5. LockedDoor:", room[i].locked_door);
writeattr("3.*Die:", room[i].killplayer);
writeln("");
writeln("");
writeprop("6.*Key =", room[i].key);
writeprop("7. Light =", room[i].light);
writenum("8. Points =", room[i].points);
writeprop("9. Class = ", room[i].oclass);
writeln("");
writeln("EXITS:");
for (j = 0; j < 12; j++) {
n = writedir(j + 10, j, room[i].path[j]);
if (j % 4 == 3) writeln("");
else padout(15 - n);
}
writeprop("22. SPECIAL:", room[i].path[12]);
writeflags("23. Room Flags:", room[i].flag_noun_bits);
writeln("24. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
/* writeln(""); */
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
room[i].win = !room[i].win;
break;
case 2:
room[i].end = !room[i].end;
break;
case 3:
room[i].killplayer = !room[i].killplayer;
break;
case 4:
room[i].seen = !room[i].seen;
break;
case 5:
room[i].locked_door = !room[i].locked_door;
break;
case 6:
room[i].key = readval("Key = ", AGT_ITEM | AGT_NONE);
break;
case 7:
room[i].light = readval("Light = ", AGT_ITEM | AGT_NONE | AGT_SELF);
break;
case 8:
room[i].points = readval("Points = ", AGT_NUM);
break;
case 9:
room[i].oclass = readval("Class = ", AGT_ROOM | AGT_NONE);
break;
case 22:
room[i].path[12] = readval("SPECIAL: ", AGT_NUM);
break;
case 23:
readflags(&room[i].flag_noun_bits);
break;
case 24:
edit_objattr(i + first_room);
break;
default:
if (n >= 10 && n < 22) { /* Direction */
room[i].path[n - 10] = readval(exitname[n - 10], AGT_NUM);
} else writeln("Invalid field");
}
}
}
#define tog(x) {x=!x;break;}
static void noun_edit(int i) {
int n;
for (;;) {
agt_clrscr();
/* writeln("");*/
writestr("NOUN ");
print_objid(i + first_noun);
/* writeln("");*/
/* writeln("");*/
writeprop(" Location=", noun[i].location);
writeln("");
attrcol = 0;
attrwidth = 3;
writeattr(" 1.*Pushable:", noun[i].pushable);
writeattr(" 8.*Lockable:", noun[i].lockable);
writeattr("15.*Drinkable:", noun[i].drinkable);
writeattr(" 2.*Pullable:", noun[i].pullable);
writeattr(" 9.*Light:", noun[i].light);
writeattr("16.*Poisonous:", noun[i].poisonous);
writeattr(" 3.*Turnable:", noun[i].turnable);
writeattr("10.*Plural:", noun[i].plural);
writeattr("17. Open:", noun[i].open);
writeattr(" 4.*Playable:", noun[i].playable);
writeattr("11. Movable:", noun[i].movable);
writeattr("18. Locked:", noun[i].locked);
writeattr(" 5.*Readable:", noun[i].readable);
writeattr("12.*Shootable:", noun[i].shootable);
writeattr("19.*Win Game:", noun[i].win);
writeattr(" 6.*Wearable:", noun[i].wearable);
writeattr("13. On:", noun[i].on);
writeattr("20.*Global:", noun[i].isglobal);
writeattr(" 7.*Closable:", noun[i].closable);
writeattr("14.*Edible:", noun[i].edible);
writeattr("21.*Proper:", noun[i].proper);
writeln("");
writenum("22. Shots =", noun[i].num_shots);
writenum("23. Points =", noun[i].points);
writenum("24. Weight =", noun[i].weight);
writenum("25. Size =", noun[i].size);
writeprop("26.*Key =", noun[i].key);
writeprop("27. Class =", noun[i].oclass);
writenum("28. Flag =", noun[i].flagnum);
writeln("");
/* writeln(""); */
writeln("29. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
tog(noun[i].pushable); /* tog() macro includes break */
case 2:
tog(noun[i].pullable);
case 3:
tog(noun[i].turnable);
case 4:
tog(noun[i].playable);
case 5:
tog(noun[i].readable);
case 6:
tog(noun[i].wearable);
case 7:
tog(noun[i].closable);
case 8:
tog(noun[i].lockable);
case 9:
tog(noun[i].light);
case 10:
tog(noun[i].plural);
case 11:
tog(noun[i].movable);
case 12:
tog(noun[i].shootable);
case 13:
tog(noun[i].on);
case 14:
tog(noun[i].edible);
case 15:
tog(noun[i].drinkable);
case 16:
tog(noun[i].poisonous);
case 17:
tog(noun[i].open);
case 18:
tog(noun[i].locked);
case 19:
tog(noun[i].win);
case 20:
tog(noun[i].isglobal);
case 21:
tog(noun[i].proper);
case 22:
noun[i].num_shots = readval("Shots =", AGT_NUM);
break;
case 23:
noun[i].points = readval("Points =", AGT_NUM);
break;
case 24:
noun[i].weight = readval("Weight =", AGT_NUM);
break;
case 25:
noun[i].size = readval("Size =", AGT_NUM);
break;
case 26:
noun[i].key = readval("Key =", AGT_ITEM | AGT_NONE);
break;
case 27:
noun[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
break;
case 28:
noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
break;
case 29:
edit_objattr(i + first_noun);
break;
default:
writeln("Invalid field");
}
}
}
static void creat_edit(int i) {
int n;
for (;;) {
agt_clrscr();
writestr("CREATURE ");
print_objid(i + first_creat);
writeln("");
writeln("");
writeprop("Location =", creature[i].location);
writeln("");
attrcol = 0;
attrwidth = 2;
writeattr(" 1. Hostile:", creature[i].hostile);
writeattr(" 4. Global:", creature[i].isglobal);
writeattr(" 2. Grp member:", creature[i].groupmemb);
writeattr(" 5.*Proper:", creature[i].proper);
writegender(" 3.*Gender:", creature[i].gender);
writeln("");
writeln("");
writeprop(" 6.*Weapon = ", creature[i].weapon);
writenum(" 7. Points = ", creature[i].points);
writenum(" 8.*Attack Threshold = ", creature[i].threshold);
writenum(" 9. Attack counter = ", creature[i].counter);
writenum("10.*Attack Time Limit = ", creature[i].timethresh);
writenum("11. Attack timer = ", creature[i].timecounter);
writeprop("12. Class = ", creature[i].oclass);
writenum("13. Flag = ", creature[i].flagnum);
writeln("");
writeln("14. Object properties and attributes.");
writeln("");
writeln("(Fields marked with an * are not saved or restored.)");
writeln("");
writestr("Field to change (0 to exit)? ");
n = read_number();
if (n == 0) return;
switch (n) {
case 1:
tog(creature[i].hostile);
case 2:
tog(creature[i].groupmemb);
case 3:
tog(creature[i].isglobal);
case 4:
tog(creature[i].proper);
case 5:
creature[i].gender = readgender();
break;
case 6:
creature[i].weapon = readval("Weapon =", AGT_ITEM | AGT_NONE);
break;
case 7:
creature[i].points = readval("Points =", AGT_NUM);
break;
case 8:
creature[i].threshold = readval("Threshold =", AGT_NUM);
break;
case 9:
creature[i].counter = readval("Attack counter =", AGT_NUM);
break;
case 10:
creature[i].timethresh = readval("Time limit =", AGT_NUM);
break;
case 11:
creature[i].timecounter = readval("Timer =", AGT_NUM);
break;
case 12:
creature[i].oclass = readval("Class =", AGT_ITEM | AGT_NONE);
break;
case 13:
noun[i].flagnum = readval("Flag Number=", AGT_ROOMFLAG);
break;
case 14:
edit_objattr(i + first_creat);
break;
default:
writeln("Invalid field");
}
}
}
#undef tog
static void obj_edit() {
int n;
for (;;) {
writeln("");
do {
writestr("Enter object number (0 to exit)? ");
n = read_number();
if (n <= 0) return;
} while (!troom(n) && !tnoun(n) && !tcreat(n));
if (troom(n)) room_edit(n - first_room);
else if (tnoun(n)) noun_edit(n - first_noun);
else if (tcreat(n)) creat_edit(n - first_creat);
else writeln("[Not yet implemented]");
}
}
static const char *yesnostr[] = { "No", "Yes" };
static void set_debug_options() {
char buff[80];
int n;
for (;;) {
agt_clrscr();
writeln("DEBUGGING OPTIONS:");
writeln("");
Common::sprintf_s(buff, " 1. Trace metacommands: %s", yesnostr[DEBUG_AGT_CMD]);
writeln(buff);
Common::sprintf_s(buff, " 2. Trace ANY metacommands: %s", yesnostr[debug_any]);
writeln(buff);
Common::sprintf_s(buff, " 3. Trace during disambiguation: %s",
yesnostr[debug_disambig]);
writeln(buff);
writeln("");
writeln("(<2> and <3> are ignored if <1> is not set; option <1> can"
" also be changed from the main debugging menu)");
writeln("");
writestr("Option to toggle (0 to exit): ");
n = read_number();
switch (n) {
case 0:
return;
case 1:
DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
break;
case 2:
debug_any = !debug_any;
break;
case 3:
debug_disambig = !debug_disambig;
break;
default:
writeln("Not a valid option");
}
}
}
void get_debugcmd() {
int n;
for (;;) {
writeln("DEBUGGING COMMANDS");
writeln("");
writeln("1. Move player 8. List Rooms");
writeln("2. Get Noun 9. List Nouns");
writeln("3. Move object 10. List Creatures");
writeln("4. View/Edit object 11. List/Set Flags");
writeln("5. Toggle Trace 12. List/Set Variables");
writeln("6. Set Debug Options 13. List/Set Counters");
writeln("7. Edit User Strings");
writeln("");
writestr("Enter choice (0 to exit): ");
n = read_number();
switch (n) {
case -1:
case 0:
return;
case 1:
d_moveobj(1, -1);
break;
case 2:
d_moveobj(-1, 1);
break;
case 3:
d_moveobj(-1, -1);
break;
case 4:
obj_edit();
break;
case 5:
DEBUG_AGT_CMD = !DEBUG_AGT_CMD;
break;
case 6:
set_debug_options();
break;
case 7:
edit_str();
break;
case 8:
d_listroom();
break;
case 9:
d_listnoun();
break;
case 10:
d_listcreat();
break;
case 11:
var_edit(2);
break;
case 12:
var_edit(0);
break;
case 13:
var_edit(1);
break;
default:
writeln("Not a valid option");
}
writeln("");
};
}
} // End of namespace AGT
} // End of namespace Glk

View File

@@ -0,0 +1,96 @@
/* 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/detection.h"
#include "glk/agt/detection_tables.h"
#include "glk/blorb.h"
#include "common/file.h"
#include "common/md5.h"
#include "engines/game.h"
namespace Glk {
namespace AGT {
void AGTMetaEngine::getSupportedGames(PlainGameList &games) {
for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd)
games.push_back(*pd);
}
const GlkDetectionEntry* AGTMetaEngine::getDetectionEntries() {
return AGT_GAMES;
}
GameDescriptor AGTMetaEngine::findGame(const char *gameId) {
for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd) {
if (!strcmp(gameId, pd->gameId))
return *pd;
}
return GameDescriptor::empty();
}
bool AGTMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
// Loop through the files of the folder
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
// Check for a recognised filename
if (file->isDirectory())
continue;
Common::String filename = file->getName();
if (!filename.hasSuffixIgnoreCase(".d$$") && !filename.hasSuffixIgnoreCase(".agx"))
continue;
Common::File gameFile;
if (!gameFile.open(*file))
continue;
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
size_t filesize = (size_t)gameFile.size();
// Scan through the AGT game list for a match
const GlkDetectionEntry *p = AGT_GAMES;
while (p->_md5 && ((p->_filesize != filesize) || (md5 != p->_md5)))
++p;
if (!p->_gameId) {
const PlainGameDescriptor &desc = AGT_GAME_LIST[0];
gameList.push_back(GlkDetectedGame(desc.gameId, desc.description, filename, md5, filesize));
} else {
// Found a match
PlainGameDescriptor gameDesc = findGame(p->_gameId);
DetectedGame gd("glk", p->_gameId, gameDesc.description, p->_language, Common::kPlatformUnknown, p->_extra);
gd.addExtraEntry("filename", filename);
gameList.push_back(gd);
}
}
return !gameList.empty();
}
void AGTMetaEngine::detectClashes(Common::StringMap &map) {
for (const PlainGameDescriptor *pd = AGT_GAME_LIST; pd->gameId; ++pd) {
if (map.contains(pd->gameId))
error("Duplicate game Id found - %s", pd->gameId);
map[pd->gameId] = "";
}
}
} // End of namespace AGT
} // End of namespace Glk

View File

@@ -0,0 +1,64 @@
/* 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/>.
*
*/
#ifndef GLK_AGT_DETECTION
#define GLK_AGT_DETECTION
#include "common/fs.h"
#include "common/hash-str.h"
#include "engines/game.h"
#include "glk/detection.h"
namespace Glk {
namespace AGT {
class AGTMetaEngine {
public:
/**
* Get a list of supported games
*/
static void getSupportedGames(PlainGameList &games);
/**
* Get the detection entries
*/
static const GlkDetectionEntry* getDetectionEntries();
/**
* Returns a game description for the given game Id, if it's supported
*/
static GameDescriptor findGame(const char *gameId);
/**
* Detect supported games
*/
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
/**
* Check for game Id clashes with other sub-engines
*/
static void detectClashes(Common::StringMap &map);
};
} // End of namespace AGT
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,292 @@
/* 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 "engines/game.h"
#include "common/language.h"
namespace Glk {
namespace AGT {
const PlainGameDescriptor AGT_GAME_LIST[] = {
{ "agt", "AGT IF Game" },
{ "abloodylife", "A Bloody Life" },
{ "alandria", "The Search for Princess Alandria" },
{ "alchemistcastle", "Castle of the Alchemists" },
{ "advalice", "The Adventures of Alice Who Went Through the Looking-Glass" },
{ "apprenticetesting", "Apprentice - The Testing of a Magical Novice" },
{ "sirarthur", "Sir Arthur" },
{ "cercla", "Cercla" },
{ "cardigan1", "Space Aliens Laughed at My Cardigan" },
{ "cardigan2", "Still Laughing at my Cardigan" },
{ "curse", "Curse of ManorLand" },
{ "sanityclause", "Sanity Clause or, Why Santa Didn't Make It to YOUR House that Year" },
{ "cliffdiver1", "Cliff Diver: Investigator for Hire - Case 1" },
{ "cliffdiver2", "Cliff Diver: Investigator for Hire - Case 2" },
{ "cosmoserve", "CosmoServe" },
{ "crusade", "Crusade" },
{ "agtdetective", "Detective" },
{ "dragonschocolate", "Dragons in Chocolate Land" },
{ "disenchanted", "Disenchanted" },
{ "ducksoup", "Duck Soup" },
{ "cavesofdyanty", "Caves of Dyanty" },
{ "destinationearth", "Destination: Earth" },
{ "dudleydilemma", "A Dudley Dilemma" },
{ "80days", "Around the World in Eighty Days" },
{ "easteregghunt", "Heather's Easter Egg Hunt" },
{ "electrabot", "Electrabot" },
{ "elf20", "The Elf's Christmas Adventure" },
{ "elfquest", "Elf Quest" },
{ "eliescape", "Escape from the ELI" },
{ "emailbox", "E-MAILBOX" },
{ "escapeprisonisland", "Escape from Prison Island" },
{ "agtfable", "A Fable" },
{ "firststupidgame", "My First Stupid Game" },
{ "ccfirstadv", "Colossal Cave - The First Adventure" },
{ "ggollek", "Ggollek I : The Dissolution" },
{ "ghosttown_rm", "Ghost Town (by Robert Masenten)" },
{ "giganticsecrets", "Secrets of the Gigantic" },
{ "newenglandgothic", "New England Gothic" },
{ "grailmisadventure", "The Misadventure of the Holy Grail" },
{ "hardestadv", "The World's Hardest Adventure" },
{ "helvera", "Helvera, Mistress of the Park" },
{ "highe", "Highe, the Adventures of Elizabeth(\"El\") Highe" },
{ "sirramichobbs", "Sir Ramic Hobbs and the High Level Gorilla" },
{ "holmescasebook", "The Casebook of Sherlock Holmes" },
{ "hotelnotell", "Hotel Notell" },
{ "house2house", "House 2 House" },
{ "agthugecave", "Adventure in Humongous Cave" },
{ "hurryhurry", "Hurry!Hurry!Hurry!!" },
{ "jackofhartz", "Jack of Hartz" },
{ "jubileeroad", "Jubilee Road" },
{ "killjustin", "Kill Justin" },
{ "klaustrophobia1", "Klaustrophobia - Part I" },
{ "klaustrophobia2", "Klaustrophobia - Part II" },
{ "klaustrophobia3", "Klaustrophobia - Part III" },
{ "klingonrpg", "In the Year 2366, Klingon Role Playing Game" },
{ "deadlylabyrinth", "The Deadly Labyrinth" },
{ "library", "Library - Library of Guilford College" },
{ "lostgold", "Lost Gold" },
{ "lostinspace", "Lost in Space : Dr.Smith Goes Home" },
{ "agtlottery", "Lottery" },
{ "loststonemansion", "Lost Stone Mansion" },
{ "agtpyramids", "The Pyramids of Mars" },
{ "mdthief", "The Multi-Dimensional Thief" },
{ "agtmhpquest", "Quest for the Magic Healing Plant" },
{ "mopandmurder", "Mop and Murder" },
{ "agtmst3k1", "Detective, An Interactive MiSTing (Mystery Science Theater 3000)" },
{ "agtmst3k2", "Mystery Science Theater 3000, Adventure 102" },
{ "spacemule", "Space Mule" },
{ "myopia", "Myopia" },
{ "nmr1", "Adventures in NMR" },
{ "nmr2", "Adventures in NMR II : The Adventure Continues" },
{ "oceana", "Oceana" },
{ "agtodieus", "Odieus's Quest for the Magic Flingshot" },
{ "oklib", "Oklib's Revenge" },
{ "ovanpelt", "Orientation to Van Pelt Library of the University of Pennsylvania" },
{ "peterpatzer", "The Adventures of Peter Patzer" },
{ "blackpearl", "Quest for the Black Pearl" },
{ "battleofphilip", "The Battle of Philip against the Forces of Creation" },
{ "flightintofantasy", "The Pilot or A Flight into Fantasy" },
{ "pork1", "PORK I : The Great Underground Sewer System" },
{ "pork2", "PORK II, The Gizzard of Showbiz" },
{ "starportal", "The Star Portal" },
{ "pastoralpitfalls", "Pastoral Pitfalls" },
{ "personalizedsample", "Personalized Adventure Game Sample" },
{ "lostproperty", "Lost Property" },
{ "gameofrecovery", "The Game of Recovery" },
{ "rerunsagain", "Reruns Again version" },
{ "derring", "Der Ring des Nibelungen" },
{ "sherwoodadv", "Adventures in Sherwood" },
{ "shapeshifteradv", "Shape Shifter Adventure!" },
{ "sirguygallant", "Sir Guy Gallant and the Deadly Warning" },
{ "shadesofgray", "Shades of Gray" },
{ "sonofstagefright", "Son of Stagefright" },
{ "spatent", "The Spatent Obstruction" },
{ "squynchia", "The Squynchia Adventure" },
{ "agtstiffy", "The Incredible Erotic Adventures of Stiffy Makane!" },
{ "storms1", "Storms I" },
{ "susan", "Susan (A Lustful Game)" },
{ "tamoret", "Tamoret" },
{ "tarabithia", "Escape from Tarabithia" },
{ "tarksimmons", "The Adventure of Tark Simmons" },
{ "tarotia", "The Books of Tarotia : Book 1" },
{ "tempest", "The Tempest" },
{ "thegame", "Whatever We Decide To Call This Game" },
{ "therift", "The Rift" },
{ "tja", "The Jeweled Arena" },
{ "toho", "Toho Academy" },
{ "tombpharaohs", "The Tomb of the Ancient Pharaohs" },
{ "tossedintospace", "Tossed into Space : Dr.Schmidt Goes Home" },
{ "timesquared", "TimeSquared" },
{ "folkestone", "Murder at the Folkestone Inn" },
{ "void", "VOID:CORPORATION" },
{ "wanderer1", "Black Wanderer 1 - The Darkest Road" },
{ "wanderer2", "Black Wanderer 2 - The Unborn One" },
{ "wanderer3", "Black Wanderer 3 - Twas a Time of Dread" },
{ "weekendsurvival", "Weekend Survival" },
{ "witchfinder", "Witchfinder" },
{ "agtwizardscastle", "The Wizard's Castle" },
{ "hobbswok", "Sir Ramic Hobbs and the Oriental Wok" },
{ "wraithblaster", "Wraith Blaster" },
{ "journeyintoxanth", "A Journey into Xanth" },
{ "zanfar", "Zanfar" },
// Dutch games
{ "querido", "Querido" },
{ nullptr, nullptr }
};
const GlkDetectionEntry AGT_GAMES[] = {
DT_ENTRY0("abloodylife", "c492e0ae0647d3a4835012ca864b99d5", 157221),
DT_ENTRY0("alandria", "0dcaff32c55dd2c1898da7893500de34", 53946),
DT_ENTRY0("alchemistcastle", "7822dfaf1ae31b3e508e7b0a267d054b", 192051),
DT_ENTRY0("advalice", "0aaafb897b46baa28023bbbaf4091fd8", 23004),
DT_ENTRY0("apprenticetesting", "4e4244649dc1cd39546f3d10dc85acb5", 131868),
DT_ENTRY0("sirarthur", "46956e2d28f6b926fc6831d60f891ffc", 120204),
DT_ENTRY0("cardigan1", "301509b196fd27c87d5d176f895b94ea", 103356),
DT_ENTRY0("cardigan2", "f17a9d5401cb5cb1be4cb2719d0c9d34", 97767),
DT_ENTRY0("cercla", "a56219015b70f507d9a1f74e0a92db1f", 136080),
DT_ENTRY0("curse", "b09a74de6081e4d56e0348c9951623e9", 79139),
DT_ENTRY0("sanityclause", "a7ea1c9ae6200511af71dfcebb5d55ff", 246159),
DT_ENTRY0("cliffdiver1", "14ce6a122a061f2b361e725fe2c0c0e4", 120042),
DT_ENTRY0("cliffdiver2", "9cc68e22a0ba03fe13bd4bfb413e08df", 155682),
DT_ENTRY1("cosmoserve", "Final", "fce21feb3a6dfda1298d3eb3b46ef0b2", 377460),
DT_ENTRY0("cosmoserve", "e677a308c446af4e076a26ef0ca235ad", 365229),
DT_ENTRY0("crusade", "d7df6bc394d225ab023e4f099d982156", 50463),
DT_ENTRY0("agtdetective", "b17f780a90fa4e0e30e5bbf590f78cd5", 17901),
DT_ENTRY0("dragonschocolate", "6cb0714d337ed45ae03e6a54ed60fdc4", 143208),
DT_ENTRY0("disenchanted", "7003a85672bbfa067dc6a28a295a1ad1", 99630),
DT_ENTRY0("ducksoup", "e3c609c2a78e89b03c8cdefa19a50293", 83187),
DT_ENTRY1("dudleydilemma", "1.2", "2ff4de040b7cee9592bc8dc2e020d937", 111294),
DT_ENTRY1("dudleydilemma", "3.0", "4cdea9d3acc19f9a02072517e4bc463d", 190188),
DT_ENTRY0("cavesofdyanty", "267e8a2812d58e140be8582914d9cefb", 40662),
DT_ENTRY0("destinationearth", "d00cfa53e2b3315f0ee6813c064be74f", 12474),
DT_ENTRY0("80days", "0086c0151760c59aa4d9e8ca055de84d", 30294),
DT_ENTRY0("easteregghunt", "6ef9fb84ec755b88f1f7c2cc3c47db2e", 55647),
DT_ENTRY0("electrabot", "1c7096e4a9a0579526e9b5084aa27701", 8748),
DT_ENTRY0("elf20", "0fa1e888a452fec59bb4a5a6ffa43d78", 101088),
DT_ENTRY0("elfquest", "5419ab5d7a19037a5971c7e2de59cee4", 16929),
DT_ENTRY0("eliescape", "8d604abcccccbc0064b7488497f6242d", 72414),
DT_ENTRY0("emailbox", "f90b34f0f2d7dfb3c7f29fbae9897671", 55908),
DT_ENTRY0("escapeprisonisland", "8f6cf9b1f46e968b353bd00a48c2bd6b", 48762),
DT_ENTRY0("agtfable", "9acb005ddd793da7898eda2bbc79a9d3", 15147),
DT_ENTRY0("ccfirstadv", "8a8ff26cd6a396c193d865fa6e37594d", 83754),
DT_ENTRY0("firststupidgame", "859933f151a301f64f88a8101853f432", 21222),
DT_ENTRY0("ggollek", "e02fa5e1ddff57e89231481574218834", 75573),
DT_ENTRY0("ghosttown_rm", "33aa534de04a978c50f8a038a6bec3e7", 35235),
DT_ENTRY0("giganticsecrets", "66d6b6b5bf43149a8ad5578c45ad4731", 21627),
DT_ENTRY0("newenglandgothic", "10898900c3b872282a4298b32e851dfc", 104895),
DT_ENTRY0("grailmisadventure", "f7b0447cc01d1f4629e734952deccf98", 107487),
DT_ENTRY0("hardestadv", "326aaac9509503829e2b350b867c4ca5", 115263),
DT_ENTRY0("helvera", "aa1ba7a1f1726a90eec90b0eb998cce8", 104642),
DT_ENTRY0("highe", "8c08f8e0e215d1293398b0d652578baf", 15471),
DT_ENTRY0("sirramichobbs", "ba008ad6016d8302dd4311dd20ccb4e0", 132597),
DT_ENTRY0("holmescasebook", "391e0bd51cbf8bc4cfffe751a1a659b2", 256446),
DT_ENTRY0("hotelnotell", "0c54347ebbcfe32bbf143a1574cdb445", 111132),
DT_ENTRY0("house2house", "9e5ee1005108afc247063e5f770ab1cc", 78246),
DT_ENTRY0("agthugecave", "0364693bb31fb2e9a45927f9e542b1fa", 260415),
DT_ENTRY0("hurryhurry", "040ca0ed40cb4732b66c2ab9b68bca97", 165564),
DT_ENTRY0("jackofhartz", "74d754d8ce9bb7dca6f70b60c72ee27d", 97038),
DT_ENTRY0("klingonrpg", "93811c560f0c78f470f65dbe62834aa1", 15066),
DT_ENTRY0("deadlylabyrinth", "3a5d3ad2f80fb8c02baf5eb9894eb9b6", 113643),
DT_ENTRY0("library", "f23d106273f6e5fdb50f65d2acd4e4fc", 133407),
DT_ENTRY0("lostgold", "ff08d607b3a1a787b5d9e369264ae7f8", 67959),
DT_ENTRY0("lostinspace", "322c226f26768b6962c2b3b147450410", 49410),
DT_ENTRY0("agtlottery", "7c0890c420d6585e4629f1cc228bf259", 24948),
DT_ENTRY0("loststonemansion", "f0ef6d965533e67b29acb130dd0f1213", 39933),
DT_ENTRY0("jubileeroad", "f24fef5bc936c22fbd84c0929d727cbf", 105543),
DT_ENTRY0("killjustin", "94d50b925733e70cf39079a8816b199c", 65043),
DT_ENTRY0("klaustrophobia1", "cbcc82df28e67d89399139e5f234d8fc", 242838),
DT_ENTRY0("klaustrophobia2", "b535015af4fece71c9f120730cb453dc", 292329),
DT_ENTRY0("klaustrophobia3", "47aad0cb89ebe10e54172db55124b8d1", 366039),
DT_ENTRY0("mdthief", "e62d36630c8a301a5da4192dfd28d650", 243729),
DT_ENTRY0("agtpyramids", "cb2aa53dea87209fee2e300cd5396e4a", 126522),
DT_ENTRY0("mopandmurder", "23c4a7ee63dbfb78871b7040a011cd89", 86913),
DT_ENTRY0("agtmhpquest", "5d657aac27f1dc150d74c50251584af0", 29646),
DT_ENTRY0("agtmst3k1", "53552013cadf6b62a5c8dcbb7f2af4a8", 127737),
DT_ENTRY0("agtmst3k2", "973cf89bf1cea65ebd8df72c6d01354d", 107001),
DT_ENTRY0("spacemule", "96cc0630552bc6a343e022777b40d9fd", 79056),
DT_ENTRY0("myopia", "b3f3d0ae4fe3bf1181fa437c69b90016", 69859),
DT_ENTRY0("nmr1", "c1758cd84fceade19866007f8d7c397f", 49734),
DT_ENTRY0("nmr2", "979ffa08dc3b102b59f6893e4a4dede9", 55485),
DT_ENTRY0("oceana", "63a163d87abf793a5e5c2f98f0d4c469", 178200),
DT_ENTRY0("agtodieus", "aef479600d4fb82f8eedbeda855a9706", 28512),
DT_ENTRY0("oklib", "d833679f11041ab1155b5207aabfc873", 166374),
DT_ENTRY0("ovanpelt", "60a49ce4b7f99968cf92ccef5ad403f7", 53298),
DT_ENTRY0("peterpatzer", "6a1be7e416f66c54b22e1305165fd7ee", 62842),
DT_ENTRY0("blackpearl", "12419db6d6088e66394ecf5f28baa68d", 80109),
DT_ENTRY0("battleofphilip", "8bbfd3d06b9eb4df0565e158e41312d8", 97443),
DT_ENTRY0("flightintofantasy", "063f4f434b64c25f2ca816a564edbe35", 100521),
DT_ENTRY0("pork1", "389deffc77cc58cce1ad8c0c57a5cfa8", 105948),
DT_ENTRY0("pork2", "13911c59cbe70ae877c87aa0ded89e47", 28269),
DT_ENTRY0("starportal", "0bf0f86fdeea607083c22a5cb41c6885", 172935),
DT_ENTRY0("pastoralpitfalls", "c35d440286c6bf67cd6ee1e5947c3483", 206469),
DT_ENTRY0("personalizedsample", "c590a3c5116ee2fa786e8f511ef85c8e", 69174),
DT_ENTRY0("lostproperty", "8acf3d6994a3b39911827d5040e8873a", 30375),
DT_ENTRY0("gameofrecovery", "b497bb0e1e93023a892f0fa54d78a1c0", 108459),
DT_ENTRY0("rerunsagain", "d263341c871a2f52e0052c313bf3e525", 81648),
DT_ENTRY0("derring", "5553e1a6966525da7ab2d874090d3758", 52893),
DT_ENTRY0("sherwoodadv", "270be7ce551c615d4c34bc64acd4c190", 313551),
DT_ENTRY0("shapeshifteradv", "8a45a92074747edf8882ea2eaf6cfa54", 137862),
DT_ENTRY0("sirguygallant", "c4376d121b26bc691b6a43b9f77eb22a", 125698),
DT_ENTRY1("shadesofgray", "Final", "e93ed21cdafc1b998ee2ccab557f0649", 433350),
DT_ENTRY0("shadesofgray", "677753739047deb5ccf72f1b6555c677", 431568),
DT_ENTRY0("sonofstagefright", "9527fa27e910470deac8ffbcb29e2427", 116640),
DT_ENTRY0("spatent", "acc4c60cbb9d0239ab9b1900b239771a", 85455),
DT_ENTRY0("squynchia", "e9e5c99ee87f3b38a9ea8e7fdd1ed79f", 81000),
DT_ENTRY0("agtstiffy", "a7f1902ab7aa9972ca46d5b36d06d2b1", 32805),
DT_ENTRY0("storms1", "8567c2db37c80f015a950ef80d299a0a", 111942),
DT_ENTRY0("susan", "cb71705848aabcac90e7ea9e911ceee9", 15633),
DT_ENTRY0("tamoret", "3de37497ed763a58093e556a963ca14e", 156816),
DT_ENTRY0("tarabithia", "6734a6889d825dae528d2a7efaf6dee2", 83430),
DT_ENTRY0("tarksimmons", "cf6945fc43e8a3062a27fc39e01c3d6e", 116397),
DT_ENTRY0("tarotia", "fbeac90159dc88e82240b4201b6231d5", 61479),
DT_ENTRY0("tempest", "114b5224e7bb8def06a87c3910d7c4f3", 52650),
DT_ENTRY0("thegame", "af6e39aadf8dced6f29d03650125a6d6", 139968),
DT_ENTRY0("therift", "1c30da9b9a55d691226c45b8b53c11c3", 41877),
DT_ENTRY0("tja", "6699e867df8263dd7858d2f6fb84acde", 517185),
DT_ENTRY0("toho", "58a6fdf89b29966774beaca80f505fff", 228744),
DT_ENTRY0("tombpharaohs", "2d10501417f28ee1dc5be8479f6e88a3", 46251),
DT_ENTRY0("tossedintospace", "515f06782c5b11108a719a20e182166c", 49491),
DT_ENTRY0("timesquared", "55e36771d5e1fe184cce8f5be666ff9f", 105300),
DT_ENTRY0("folkestone", "7e949a7376b0a64cee0d9412b0203611", 64557),
DT_ENTRY0("wanderer1", "e1d707c9deaf02a4b28c9041a4009cb6", 53946),
DT_ENTRY0("wanderer2", "89dd16629022c75f3ffc171a6b126da6", 46980),
DT_ENTRY0("wanderer3", "839ab34bce5c82ec6194675f0186b15b", 45765),
DT_ENTRY0("weekendsurvival", "e770c0e75b7257eae9d4677340beca10", 91044),
DT_ENTRY0("witchfinder", "9acecd1803d2e99282970db1ef6ff344", 186300),
DT_ENTRY0("agtwizardscastle", "3adecad94b61babdadfbe20242e86b24", 18792),
DT_ENTRY0("hobbswok", "3178e271e8259a889df99545d6c65362", 198369),
DT_ENTRY0("wraithblaster", "392f507d42c006a30c55a20ec9e75f44", 194643),
DT_ENTRY0("void", "b6818cc6396e1357c3c551bc338c653d", 53784),
DT_ENTRY0("journeyintoxanth", "2b073d48a8a01f91d7bca5db482e3ecd", 147177),
DT_ENTRY0("zanfar", "5fc6914fe02c0235f8a5343db8b6359e", 83106),
// Dutch games
DT_ENTRYL0("querido", Common::NL_NLD, "e52fe3a44d7b511bb362ce08a48435ef", 104166),
DT_END_MARKER
};
} // End of namespace AGT
} // End of namespace Glk

View File

@@ -0,0 +1,316 @@
/* 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"
namespace Glk {
namespace AGT {
void dbgprintf(const char *fmt, ...) {
va_list vp;
char buff[300];
va_start(vp, fmt);
Common::vsprintf_s(buff, fmt, vp);
va_end(vp);
debugout(buff);
}
static void print_msg(descr_ptr dptr) {
int j;
descr_line *txt;
txt = read_descr(dptr.start, dptr.size);
if (txt != nullptr) {
for (j = 0; txt[j] != nullptr; j++) {
dbgprintf("\n");
debugout(txt[j]);
}
}
free_descr(txt);
}
static char *getname(int inum)
/* Name should be 20 chars or less */
{
if (inum == 0) return rstrdup("* 0 *");
return objname(inum);
}
extern integer dobj, iobj, actor;
void print_special_obj(int i)
/* This is called by the disassembler in agtdbg.c */
/* i=0 NOUN, 1 OBJECT, 2 NAME */
{
int dval;
char *s;
switch (i) {
case 0:
dval = dobj;
dbgprintf("NOUN");
break;
case 1:
dval = iobj;
dbgprintf("OBJECT");
break;
case 2:
dval = actor;
dbgprintf("NAME");
break;
default:
dval = 0; /* Silence compiler warnings. */
fatal("INTERNAL ERROR: Invalid *dval* in print_special_obj.");
}
if (dbgflagptr == nullptr)
/* This determines whether we are linked with agtout or agil */
return;
s = getname(dval);
dbgprintf("(%d:%s)", dval, s);
rfree(s);
}
#define printval(str,index,ptr) {dbgprintf("[%s%d",str,index);\
if (ptr==NULL) dbgprintf("]");\
else dbgprintf("=%ld]",(long)ptr[index]);}
int argout(int dtype, int dval, int optype) {
char *s;
if (dtype & AGT_VAR) dtype = AGT_VAR;
if ((optype & 3) == 1) /* variable */
dtype = AGT_VAR;
if (optype & 2) { /* NOUN or OBJECT */
if (dtype >= 64 && dtype != AGT_NUM)
dbgprintf("ILL:");
if (optype == 2)
print_special_obj(0); /* NOUN */
else
print_special_obj(1); /* OBJECT */
return 0;
}
if (!interp_arg)
dbgprintf("%d", dval);
else {
if (dtype < 64) {
if (dval == -1)
print_special_obj(2); /* NAME */
else {
s = getname(dval);
if (dtype & (AGT_ITEM | AGT_CREAT | AGT_SELF | AGT_WORN))
dbgprintf("<%d:%s>", dval, s);
else
dbgprintf("{%d:%s}", dval, s);
rfree(s);
}
} else if ((dtype & AGT_VAR) != 0) {
if (dval == -1)
print_tos();
else
printval("Var", dval, dbgvarptr);
} else switch (dtype) {
case AGT_TIME:
dbgprintf("%2d:%2d", dval / 100, dval % 100);
break;
case AGT_NUM: /* Numeric */
dbgprintf("%d", dval);
break;
case AGT_FLAG: /* Flag */
printval("Flg", dval, dbgflagptr);
break;
case AGT_ROOMFLAG: /* Roomflag */
dbgprintf("RoomFlag%d", dval);
break;
case AGT_QUEST: /* Question */
if (dval <= MaxQuestion && dval >= 1 && question != nullptr) {
dbgprintf("\nQ%d:%s\n", dval, question[dval - 1]);
dbgprintf("[A:%s]", answer[dval - 1]);
} else if (quest_ptr != nullptr) {
dbgprintf("\nQ%d: ", dval);
print_msg(quest_ptr[dval - 1]);
dbgprintf("[A:");
print_msg(ans_ptr[dval - 1]);
}
break;
case AGT_MSG: /* Message */
if (dval > last_message || dval < 1 || msg_ptr == nullptr)
dbgprintf("ILLEGAL MESSAGE");
else {
dbgprintf("(Msg%d)", dval);
if (!dbg_nomsg)
print_msg(msg_ptr[dval - 1]);
}
break;
case AGT_ERR: /* Message */
if (dval > NUM_ERR || dval < 1 || err_ptr == nullptr)
dbgprintf("ILLEGAL MESSAGE");
else {
dbgprintf("(Std%d)", dval);
if (!dbg_nomsg)
print_msg(err_ptr[dval - 1]);
}
break;
case AGT_STR: /* String */
if (dval - 1 >= MAX_USTR || userstr == nullptr)
dbgprintf("ILLEGAL STRING");
else
dbgprintf("\nStr%d:%s", dval, userstr[dval]);
break;
case AGT_CNT: /* Counter */
printval("Cnt", dval, dbgcntptr);
break;
case AGT_DIR: /* Direction */
if (dval >= 1 && dval <= 13)
dbgprintf("%s", exitname[dval - 1]);
else dbgprintf("ILL_DIR(%d)", dval);
break;
case AGT_SUB: /* Subroutine */
dbgprintf("Subroutine %d", dval);
break;
case AGT_PIC: /* Picture */
case AGT_PIX:
dbgprintf("Picture #%d", dval);
break;
case AGT_FONT: /* Font */
dbgprintf("Font #%d", dval);
break;
case AGT_SONG: /* Song */
dbgprintf("Song #%d", dval);
break;
case AGT_OBJFLAG:
dbgprintf("ObjFlag%d", dval);
break;
case AGT_OBJPROP:
dbgprintf("ObjProp%d", dval);
break;
case AGT_ATTR:
if (dval < 0 || dval >= NUM_ATTR)
dbgprintf("UnkownAttr%d", dval);
else
dbgprintf("%s", attrlist[dval].name);
break;
case AGT_PROP:
if (dval < 0 || dval >= NUM_PROP)
dbgprintf("UnknownProp%d", dval);
else
dbgprintf("%s", proplist[dval].name);
break;
case AGT_EXIT:
if (dval >= exitmsg_base)
argout(AGT_MSG, dval - exitmsg_base, 0);
else
argout(AGT_ROOM, dval, 0);
break;
default:
dbgprintf("?+%d", dval);
}
}
return 1;
}
void debug_newline(integer op, rbool first_nl) {
rbool early_nl;
if (!dbg_nomsg) return;
early_nl = (op == 1008 || op == 1027 || op == 1083 || op == 1105
|| (op >= 1126 && op <= 1131));
if (early_nl == first_nl)
debugout("\n");
}
void debug_cmd_out(int ip, integer op, int arg1, int arg2, int optype) {
int j;
const opdef *opdata;
rbool save_dbg_nomsg;
dbgprintf(" %2d:", ip);
save_dbg_nomsg = 0; /* Just to silence compiler warnings. */
opdata = get_opdef(op);
if (opdata == &illegal_def)
dbgprintf("ILLEGAL %d\n", op);
else {
if (op >= END_ACT) dbgprintf("!"); /* "Terminal" Actions */
else if (op <= MAX_COND) dbgprintf("?"); /* Condition */
if (op == 1063) { /* RandomMessage needs special handling */
save_dbg_nomsg = dbg_nomsg;
dbg_nomsg = 1;
}
dbgprintf("%s", opdata->opcode);
for (j = 0; j < opdata->argnum; j++) {
dbgprintf("\t");
argout(j == 0 ? opdata->arg1 : opdata->arg2 , j == 0 ? arg1 : arg2,
optype >> 2);
optype <<= 2;
}
if (op == 1063)
dbg_nomsg = save_dbg_nomsg;
}
debug_newline(op, 1);
}
void debug_head(int i) {
int v, w, a;
v = verb_code(command[i].verbcmd);
if (v >= BASE_VERB && v < BASE_VERB + DUMB_VERB && syntbl[synlist[v]] != 0)
w = syntbl[synlist[v]];
else w = command[i].verbcmd;
if (command[i].actor > 0) {
dbgprintf("CMD %d: ", i);
a = command[i].actor;
} else {
dbgprintf("REDIR: ");
a = -command[i].actor;
}
if (a == 2)
dbgprintf("anybody, ");
else if (a > 2) {
char *name;
name = objname(a);
name[0] = toupper(name[0]);
dbgprintf("%s, ", name);
rfree(name);
}
dbgprintf("%s ", w == 0 ? "any" : dict[w]);
if (command[i].noun_adj != 0)
dbgprintf("%s ", gdict(command[i].noun_adj));
dbgprintf("%s %s ", gdict(command[i].nouncmd),
(ver == 3) ? gdict(command[i].prep) : "->");
if (command[i].obj_adj != 0)
dbgprintf("%s ", gdict(command[i].obj_adj));
dbgprintf("%s\n", gdict(command[i].objcmd));
}
} // End of namespace AGT
} // End of namespace Glk

1386
engines/glk/agt/exec.cpp Normal file

File diff suppressed because it is too large Load Diff

271
engines/glk/agt/exec.h Normal file
View File

@@ -0,0 +1,271 @@
/* 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/>.
*
*/
namespace Glk {
namespace AGT {
#ifndef global /* Don't touch this */
#define global extern
#define global_defined_exec
#endif
/* This contains the decoding of the current instruction */
struct op_rec {
integer op;
int arg1;
int arg2;
int optype;
int argcnt; /* Actual number of argument words present */
const opdef *opdata;
const char *errmsg;
rbool disambig; /* Trigger disambiguation? */
rbool negate; /* NOT? (cond token only) */
rbool failmsg; /* Run only on failure? */
rbool endor; /* End any OR blocks? (action tokens, mainly) */
} ;
/* The following determines if we are doing disambiguation
or actually executing a verb */
global uchar do_disambig; /* 0= execution
1= disambiguating noun
2= disambiguating object */
/* Flags used during turn execution */
global rbool beforecmd; /* Only used by 1.8x games */
global rbool suppress_debug; /* Causes debugging info to _not_ be printed
even if debugging is on; used by disambiguator
and to suppress ANY commands */
global rbool was_metaverb; /* Was the verb that just executed a metaverb? */
/* Metaverbs are commands that should not take game time
to execute: SAVE, RESTORE, RESTART, QUIT, SCRIPT, UNSCRIPT,
NOTIFY, SCORE, etc. */
global integer oldloc; /* Save old location for NO_BLOCK_HOSTILE purposes */
/* This is a hack to pass the subroutine number from exec_token
back to scan_metacommand when a DoSubroutine is done */
global integer subcall_arg;
/* This fixes a bug in the original AGT spec, causing "actor, verb ..."
commands to misfire if there is more than one creature of the same
name. */
global integer *creat_fix;
/* -------------------------------------------------------------------- */
/* Defined in EXEC.C */
/* -------------------------------------------------------------------- */
extern void raw_lineout(const char *s, rbool do_repl,
int context, const char *pword);
extern void msgout(int msgnum, rbool add_nl);
extern void sysmsg(int msgid, const char *s);
extern void alt_sysmsg(int msgid, const char *s, parse_rec *new_dobjrec,
parse_rec *new_iobjrec);
extern void sysmsgd(int msgid, const char *s, parse_rec *new_dobj_rec);
rbool ask_question(int qnum);
extern void increment_turn(void);
/* Warning: the following function rfrees <ans> */
extern rbool match_answer(char *ans, int anum);
extern void look_room(void);
extern void runptr(int i, descr_ptr dp[], const char *msg, int msgid,
parse_rec *nounrec, parse_rec *objrec);
extern int normalize_time(int tnum); /* Convert hhmm so mm<60 */
extern void add_time(int dt);
/* -------------------------------------------------------------------- */
/* Defined in OBJECT.C */
/* -------------------------------------------------------------------- */
extern parse_rec *make_parserec(int obj, parse_rec *rec);
extern parse_rec *copy_parserec(parse_rec *rec);
extern void free_all_parserec(void); /* Freeds doj_rec, iobj_rec, and actor_rec */
extern rbool in_scope(int item);
extern rbool islit(void);
extern rbool it_possess(int item);
extern rbool it_proper(int item);
extern rbool it_isweapon(int item);
extern rbool it_door(int obj, word noun); /* Is obj a door? */
extern rbool is_within(integer obj1, integer obj2, rbool stop_if_closed);
extern integer it_room(int item); /* Returns the room that the item is in */
extern int lightcheck(int parent, int roomlight, rbool active);
/* If active is false, we don't care if the light is actually working. */
#define it_move(a,b) it_reposition(a,b,0)
#define it_destroy(item) it_move(item,0)
#define get_obj(dobj) it_move(dobj,1)
#define drop_obj(dobj) it_move(dobj,loc+first_room)
extern void it_reposition(int item, int newloc, rbool save_pos);
extern void goto_room(int newroom);
extern void it_describe(int dobj);
extern int print_contents(int obj, int ind_lev);
extern void recompute_score(void);
extern int check_fit(int obj1, int obj2);
/* And its possible return values: */
#define FIT_OK 0 /* Fits */
#define FIT_WEIGHT 1 /* Too heavy [*] */
#define FIT_NETWEIGHT 2 /* With other stuff is too heavy [*] */
#define FIT_SIZE 3 /* Too big */
#define FIT_NETSIZE 4 /* With other stuff is too big */
/* [*]-- These can only occur if obj2==1 or for ME/1.5-1.7 */
extern long getprop(int obj, int prop);
extern void setprop(int obj, int prop, long val);
extern rbool getattr(int obj, int prop);
extern void setattr(int obj, int prop, rbool val);
extern rbool matchclass(int obj, int oclass);
/* ---------------------------------------------------------------------- */
/* Define in RUNVERB.C */
/* ---------------------------------------------------------------------- */
/* Verbs actually used elsewhere in th interpreter */
extern void v_inventory(void);
extern void v_look(void);
extern void v_listexit(void);
/* The routine that actually runs the current player command */
extern void exec_verb(void);
/* ---------------------------------------------------------------------- */
/* In METACOMMAND.C */
/* ---------------------------------------------------------------------- */
/* The main routine to search the metacommand list and run the appropriate
meta-commands */
extern int scan_metacommand(integer m_actor, int vcode,
integer m_dobj, word m_prep, integer m_iobj,
int *redir_flag);
/* The type checking routine */
rbool argvalid(int argtype, int arg);
/* ---------------------------------------------------------------------- */
/* In TOKEN.C */
/* ---------------------------------------------------------------------- */
extern int exec_instr(op_rec *oprec); /* Execute instruction */
extern long pop_expr_stack(void); /* Wrapper around routine to access TOS */
/* ---------------------------------------------------------------------- */
/* Defined in DEBUGCMD.C */
/* ---------------------------------------------------------------------- */
extern void get_debugcmd(void); /* Get and execute debugging commands */
/* ------------------------------------------------------------------- */
/* Macros for getting information about items */
/* (mainly used to blackbox the difference between nouns and creatures) */
/* -------------------------------------------------------------------- */
/* A note on object codes:
<0 obj is a 'virtual' object, existing only as the word
dict[-obj], e.g. DOOR, flag nouns, global nouns
0 No object (or any object)
1 Self(i.e. the player)
first_room..last_room Rooms
first_noun..last_noun Nouns
first_creat..last_creat Creatures
1000 Being worn by the player */
/* The following macro loops over the contents of an object */
#define contloop(i,obj) for(i=it_contents(obj);i!=0;i=it_next(i))
#define safecontloop(i,j,obj) for(i=it_contents(obj),j=it_next(i); \
i!=0;i=j,j=it_next(i))
#define cnt_val(c) ((c)==-1 ? 0 : (c))
/* -------------------------------------------------------------------- */
/* These are the macros that should usually be used to determine */
/* information about the objects in the game, unless the object type */
/* is definitely known */
/* ------------------------------------------------------------------- */
#define it_on(item) nounattr(item,on)
#define it_group(item) creatattr(item,groupmemb)
#define it_adj(item) objattr(item,adj)
#define it_pushable(item) nounattr(item,pushable)
#define it_pullable(item) nounattr(item,pullable)
#define it_turnable(item) nounattr(item,turnable)
#define it_playable(item) nounattr(item,playable)
#define it_plur(item) nounattr(item,plural)
#define it_gender(item) creatattr(item,gender)
#define it_pict(item) objattr(item,pict)
#define it_class(item) anyattr(item,oclass)
#define it_next(item) objattr(item,next)
#define it_isglobal(item) objattr(item,isglobal)
#define it_flagnum(item) objattr(item,flagnum)
#define it_seen(item) anyattr(item,seen)
#define it_name(item) objattr2(item,name,(item<0) ? -item : 0)
#define it_open(item) nounattr2(item,open, tcreat(item) || \
(tdoor(item) && !room[loc].locked_door))
/* This checks to make sure the object isn't unmovable. */
/* (As such, all non-nouns automatically pass) */
#define it_canmove(item) (!tnoun(item) || noun[(item)-first_noun].movable)
#ifdef IT_MACRO
#define it_contents(item) objattr2(item,contents,\
roomattr2(item,contents,\
(item==1) ? player_contents : \
(item==1000) ? player_worn : 0))
#define it_lockable(item) nounattr2(item,lockable, (tdoor(item) ? 1 : 0) )
#define it_locked(item,name) nounattr2(item,locked,\
(tdoor(item) && room[loc].locked_door ? \
1 : 0))
#else
extern int it_contents(integer obj);
extern rbool it_lockable(integer obj, word noun);
extern rbool it_locked(integer obj, word noun);
#endif
#ifdef global_defined_exec
#undef global
#undef global_defined_exec
#endif
} // End of namespace AGT
} // End of namespace Glk

View File

@@ -0,0 +1,544 @@
/* 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"
namespace Glk {
namespace AGT {
#ifdef force16
#undef int
#define int short
#endif
/*----------------------------------------------------------------------*/
/* Filetype Data */
/*----------------------------------------------------------------------*/
const char *extname[] = {
"",
DA1, DA2, DA3, DA4, DA5, DA6, DSS,
pHNT, pOPT, pTTL,
pSAV, pSCR, pLOG,
pAGX, pINS, pVOC, pCFG,
pAGT, pDAT, pMSG, pCMD, pSTD, AGTpSTD
};
#ifdef PATH_SEP
static const char *path_sep = PATH_SEP;
#else
static const char *path_sep = nullptr;
#endif
/* This returns the options to use when opening the given file type */
/* rw is true if we are writing, false if we are reading. */
const char *filetype_info(filetype ft, rbool rw) {
if (ft < fTTL) return "rb";
if (ft == fAGX) return rw ? "wb" : "rb";
if (ft == fSAV) return (rw ? "wb" : "rb");
if (ft == fTTL || ft == fINS || ft == fVOC) return "rb";
#ifdef OPEN_AS_TEXT
if (ft >= fCFG) return (open_as_binary ? "rb" : "r");
#else
if (ft >= fCFG) return "rb";
#endif
if (ft == fSCR) {
if (rw)
return (BATCH_MODE || make_test) ? "w" : "a";
else return "r";
}
if (ft == fLOG) return rw ? "w" : "r";
fatal("INTERNAL ERROR: Invalid filetype.");
return nullptr;
}
/* Returns true if ft is a possible extension in general context ft_base */
static rbool compat_ext(filetype ft, filetype ft_base) {
if (ft_base == fNONE || ft_base == fDA1 || ft_base == fAGX) { /* Game file */
return (ft >= fDA1 && ft <= fDSS)
|| ft == fOPT || ft == fTTL
|| (ft >= fAGX && ft <= fCFG);
}
if (ft_base == fSAV || ft_base == fSCR || ft_base == fLOG)
return (ft == ft_base);
if (ft_base == fAGT) { /* Source code */
return (ft >= fAGT && ft <= fCMD)
|| ft == fTTL || ft == fCFG;
}
fatal("INTERNAL ERROR: Invalid file class.");
return 0;
}
/*----------------------------------------------------------------------*/
/* Misc. utilities */
/*----------------------------------------------------------------------*/
char *assemble_filename(const char *path, const char *root,
const char *ext) {
int len1, len2, len3;
char *name;
len1 = len2 = len3 = 0;
if (path != nullptr) len1 = strlen(path);
if (root != nullptr) len2 = strlen(root);
if (ext != nullptr) len3 = strlen(ext);
name = (char *)rmalloc(len1 + len2 + len3 + 1);
if (path != nullptr) memcpy(name, path, len1);
#ifdef PREFIX_EXT
if (ext != NULL) memcpy(name + len1, ext, len3);
if (root != NULL) memcpy(name + len1 + len3, root, len2);
#else
if (root != nullptr) memcpy(name + len1, root, len2);
if (ext != nullptr) memcpy(name + len1 + len2, ext, len3);
#endif
name[len1 + len2 + len3] = 0;
return name;
}
#ifdef PATH_SEP
/* This works for binary files; we don't care about non-binary
files since this only used to search for game files. */
static rbool file_exist(const char *fname) {
return Common::File::exists(fname);
}
#endif
/* This checks to see if c matches any of the characters in matchset */
static rbool smatch(char c, const char *matchset) {
for (; *matchset != 0; matchset++)
if (*matchset == c) return 1;
return 0;
}
/*----------------------------------------------------------------------*/
/* Taking Apart the Filename */
/*----------------------------------------------------------------------*/
static int find_path_sep(const char *name) {
int i;
if (path_sep == nullptr)
return -1;
for (i = strlen(name) - 1; i >= 0; i--)
if (smatch(name[i], path_sep)) break;
return i;
}
/* Checks to see if the filename (which must be path-free)
has an extension. Returns the length of the extensions
and writes the extension type in pft */
static int search_for_ext(const char *name, filetype base_ft,
filetype *pft) {
filetype t;
int xlen, len;
*pft = fNONE;
len = strlen(name);
if (len == 0) return 0;
for (t = (filetype)(fNONE + 1); t <= fSTD; t = (filetype)((int)t + 1))
if (compat_ext(t, base_ft)) {
xlen = strlen(extname[t]);
if (xlen == 0 || xlen > len) continue;
#ifdef PREFIX_EXT
if (strncasecmp(name, extname[t], xlen) == 0)
#else
if (fnamecmp(name + len - xlen, extname[t]) == 0)
#endif
{
*pft = t;
return xlen;
}
}
return 0;
}
/* Extract root filename or extension from
pathless name, given that the extension is of length extlen. */
/* If isext is true, extract the extension. If isext is false,
then extrac the root. */
static char *extract_piece(const char *name, int extlen, rbool isext) {
char *root;
int len, xlen;
rbool first; /* If true, extract from beginning; if false, extract
from end */
len = strlen(name) - extlen;
xlen = extlen;
if (isext) {
int tmp;
tmp = len;
len = xlen;
xlen = tmp;
}
if (len == 0) return nullptr;
root = (char *)rmalloc((len + 1) * sizeof(char));
#ifdef PREFIX_EXT
first = isext ? 1 : 0;
#else
first = isext ? 0 : 1;
#endif
if (first) {
memcpy(root, name, len);
root[len] = 0;
} else {
memcpy(root, name + xlen, len);
root[len] = 0;
}
return root;
}
/* This returns true if "path" is absolute, false otherwise.
This is _very_ platform dependent. */
static rbool absolute_path(char *path) {
#ifdef pathtest
return pathtest(path);
#else
return 1;
#endif
}
/*----------------------------------------------------------------------*/
/* Basic routines for dealing with file contexts */
/*----------------------------------------------------------------------*/
#define FC(x) ((file_context_rec*)(x))
/* formal_name is used to get filenames for diagnostic messages, etc. */
char *formal_name(fc_type fc, filetype ft) {
if (FC(fc)->special) return FC(fc)->gamename;
if (ft == fNONE)
return rstrdup(FC(fc)->shortname);
if (ft == fAGT_STD)
return rstrdup(AGTpSTD);
return assemble_filename("", FC(fc)->shortname, extname[ft]);
}
#ifdef PATH_SEP
static rbool test_file(const char *path, const char *root, const char *ext) {
char *name;
rbool tmp;
name = assemble_filename(path, root, ext);
tmp = file_exist(name);
rfree(name);
return tmp;
}
/* This does a path search for the game files. */
static void fix_path(file_context_rec *fc) {
char **ppath;
if (gamepath == NULL) return;
for (ppath = gamepath; *ppath != NULL; ppath++)
if (test_file(*ppath, fc->shortname, fc->ext)
|| test_file(*ppath, fc->shortname, pAGX)
|| test_file(*ppath, fc->shortname, DA1)) {
fc->path = rstrdup(*ppath);
return;
}
}
#endif
/* This creates a new file context based on gamename. */
/* ft indicates the rough use it will be put towards:
ft=fNONE indicates it's the first pass read, before PATH has been
read in, and so the fc shouldn't be filled out until
fix_file_context() is called.
ft=pDA1 indicates that name refers to the game files.
ft=pAGX indicates the name of the AGX file to be written to.
ft=pSAV,pLOG,pSCR all indicate that name corresponds to the
related type of file. */
fc_type init_file_context(const char *name, filetype ft) {
file_context_rec *fc;
int p, x; /* Path and extension markers */
fc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
fc->special = 0;
fc->gamename = rstrdup(name);
p = find_path_sep(fc->gamename);
if (p < 0)
fc->path = nullptr;
else {
fc->path = (char *)rmalloc((p + 2) * sizeof(char));
memcpy(fc->path, fc->gamename, p + 1);
fc->path[p + 1] = '\0';
}
x = search_for_ext(fc->gamename + p + 1, ft, &fc->ft);
fc->shortname = extract_piece(fc->gamename + p + 1, x, 0);
fc->ext = extract_piece(fc->gamename + p + 1, x, 1);
#ifdef PATH_SEP
if (fc->path == NULL && ft == fDA1)
fix_path(fc);
#endif
return fc;
}
void fix_file_context(fc_type fc, filetype ft) {
#ifdef PATH_SEP
if (FC(fc)->path == NULL && ft == fDA1)
fix_path(FC(fc));
#endif
}
/* This creates new file contexts from old. */
/* This is used to create save/log/script filenames from the game name,
and to create include files in the same directory as the source file. */
fc_type convert_file_context(fc_type fc, filetype ft, const char *name) {
file_context_rec *nfc;
rbool local_ftype; /* Indicates file should be in working directory,
not game directory. */
local_ftype = (ft == fSAV || ft == fSCR || ft == fLOG);
if (BATCH_MODE || make_test) local_ftype = 0;
if (name == nullptr) {
nfc = (file_context_rec *)rmalloc(sizeof(file_context_rec));
nfc->gamename = nullptr;
nfc->path = nullptr;
nfc->shortname = rstrdup(fc->shortname);
nfc->ext = nullptr;
nfc->ft = fNONE;
nfc->special = 0;
} else {
nfc = init_file_context(name, ft);
}
/* If path already defined, then combine paths. */
if (!local_ftype && nfc->path != nullptr && !absolute_path(nfc->path)) {
char *newpath;
newpath = nfc->path;
newpath = assemble_filename(fc->path, nfc->path, "");
rfree(nfc->path);
nfc->path = newpath;
}
/* scripts, save-games and logs should go in the working directory,
not the game directory, so leave nfc->path equal to NULL for them. */
if (!local_ftype && nfc->path == nullptr)
nfc->path = rstrdup(fc->path); /* Put files in game directory */
return nfc;
}
void release_file_context(fc_type *pfc) {
file_context_rec *fc;
fc = FC(*pfc);
rfree(fc->gamename);
rfree(fc->path);
rfree(fc->shortname);
rfree(fc->ext);
rfree(fc);
}
/*----------------------------------------------------------------------*/
/* Routines for Finding Files */
/*----------------------------------------------------------------------*/
static genfile try_open_file(const char *path, const char *root,
const char *ext, const char *how,
rbool nofix) {
char *name = assemble_filename(path, root, ext);
genfile f = fopen(name, how);
rfree(name);
return f;
}
static genfile findread(file_context_rec *fc, filetype ft) {
genfile f;
f = nullptr;
if (ft == fAGT_STD) {
f = try_open_file(fc->path, AGTpSTD, "", filetype_info(ft, 0), 0);
return f;
}
if (ft == fAGX || ft == fNONE) /* Try opening w/o added extension */
f = try_open_file(fc->path, fc->shortname, fc->ext, filetype_info(ft, 0), 0);
if (f == nullptr)
f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 0);
return f;
}
/*----------------------------------------------------------------------*/
/* File IO Routines */
/*----------------------------------------------------------------------*/
genfile readopen(fc_type fc, filetype ft, const char **errstr) {
genfile f;
*errstr = nullptr;
f = findread(fc, ft);
if (f == nullptr) {
*errstr = "Cannot open file";
}
return f;
}
rbool fileexist(fc_type fc, filetype ft) {
genfile f;
if (fc->special) return 0;
f = try_open_file(fc->path, fc->shortname, extname[ft], filetype_info(ft, 0), 1);
if (f != nullptr) { /* File already exists */
readclose(f);
return 1;
}
return 0;
}
genfile writeopen(fc_type fc, filetype ft,
file_id_type *pfileid, const char **errstr) {
char *name;
genfile f;
*errstr = nullptr;
name = nullptr;
{
name = assemble_filename(FC(fc)->path, FC(fc)->shortname, extname[ft]);
f = fopen(name, filetype_info(ft, 1));
}
if (f == nullptr) {
*errstr = "Cannot open file";
}
if (pfileid == nullptr)
rfree(name);
else
*pfileid = name;
return f;
}
rbool filevalid(genfile f, filetype ft) {
return (f != nullptr);
}
void binseek(genfile f, long offset) {
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
assert(rs);
rs->seek(offset);
}
/* This returns the number of bytes read, or 0 if there was an error. */
long varread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
long num;
*errstr = nullptr;
assert(f != nullptr);
num = fread(buff, recsize, recnum, f);
if (num != recnum)
*errstr = "varread";
num = num * recsize;
return num;
}
rbool binread(genfile f, void *buff, long recsize, long recnum, const char **errstr) {
long num;
num = varread(f, buff, recsize, recnum, errstr);
if (num < recsize * recnum && *errstr == nullptr)
*errstr = rstrdup("Unexpected end of file.");
return (*errstr == nullptr);
}
rbool binwrite(genfile f, void *buff, long recsize, long recnum, rbool ferr) {
assert(f != nullptr);
if (fwrite(buff, recsize, recnum, f) != (size_t)recnum) {
if (ferr) fatal("binwrite");
return 0;
}
return 1;
}
void readclose(genfile f) {
assert(f != nullptr);
fclose(f);
}
void writeclose(genfile f, file_id_type fileid) {
assert(f != nullptr);
rfree(fileid);
fclose(f);
}
long binsize(genfile f)
/* Returns the size of a binary file */
{
long pos, leng;
assert(f != nullptr);
pos = ftell(f);
fseek(f, 0, SEEK_END);
leng = ftell(f);
fseek(f, pos, SEEK_SET);
return leng;
}
rbool textrewind(genfile f) {
Common::SeekableReadStream *rs = dynamic_cast<Common::SeekableReadStream *>(f);
assert(rs);
rs->seek(0);
return 0;
}
genfile badfile(filetype ft) {
return nullptr;
}
} // End of namespace AGT
} // End of namespace Glk

1654
engines/glk/agt/gamedata.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

539
engines/glk/agt/interp.h Normal file
View File

@@ -0,0 +1,539 @@
/* 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 "common/file.h"
#ifndef GLK_AGT_INTERP
#define GLK_AGT_INTERP
namespace Glk {
namespace AGT {
/* This file contains variables and data structures used
by the interpreter but not read in from the gamefile.
For the rest of the data structures, see agility.h and
agtdata.c */
#ifndef global
#define uagt_defined_global
#define global extern
#endif
/* -------------------------------------------------------------------- */
/* The following are debugging and diagnostic flags. */
/* They are mainly intended for debugging the interpreter, but */
/* they could concievable be used for debugging games under the */
/* interpreter */
/* -------------------------------------------------------------------- */
global rbool stable_random;
/* --Force random numbers to be repeatable. */
global rbool DEBUG_MEM; /* prints out information on memory allocation */
global rbool debug_parse; /* Prints out long parse diagnostic information
after the sentence has been parse but before
disambiguation */
global rbool DEBUG_EXEC_VERB; /* This prints out a line indicating each command
that exec_verb() is asked to run */
global rbool DEBUG_DISAMBIG; /* Print out dismabiguation debugging info */
global rbool DEBUG_SMSG; /* Print out STANDARD message info */
#define DEBUG_AGT_CMD flag[0] /* This sends metacommand execution information
to either the screen or debugfile, depending on
whether DEBUG_OUT is true or false. */
global rbool debug_disambig, debug_any;
/* These determine if metacommands are traced during disambiguation
or during the scanning of ANY commands */
global rbool DEBUG_OUT; /* True if debugging output is redirected somewhere
other than the screen */
global Common::DumpFile *debugfile; /* Where debugging output should be sent */
#define def_DEBUG_MEM 1 /* parser.c */
/* -------------------------------------------------------------------- */
/* The following are AGT 'purity' flags; they turn off features of */
/* my interpreter that are not fully consistent with the original AGT.*/
/* More are defined in agility.h, and you should look there for general */
/* notes */
/* -------------------------------------------------------------------- */
/* The following are defined (and described) in agil.c */
extern rbool PURE_INPUT, PURE_TONE;
#define PURE_WEAR 1 /* If this is cleared, then things taken off
still stay in the player's inventory.
The support for this isn't quite complete yet
(there are one or two checks that need to be done
but aren't) and so right now this should be 1. */
global rbool PURE_DOT; /* Treats period as a letter-character and not
as punctuation. This should be set automatically
during initialization based on whether any words
in the dictionary have dots. */
#define FORCE_PURE_DOT 0 /* This forces the period to be treated as a letter
even if there are no words in the dictionary
containing periods. The only reason to set
this was if you were concerned that knowledge
about the presence or absence of periods in the
dictionary would give puzzles away. */
#define MEM_MARGIN (16*1024) /* 16K should be enough (SOGGY, the largest AGT
game, uses around 12K) */
#define PICT_SUPPORT 0 /* Graphics not supported */
#define TAB_SIZE 3 /* Number of spaces in a tab */
/* -------------------------------------------------------------------- */
/* Variables and Flags related to Metaverbs */
/* -------------------------------------------------------------------- */
global rbool notify_flag, listexit_flag, menu_mode;
global rbool cmd_saveable; /* set indicates that this command can be repeated
with AGAIN. */
global rbool can_undo; /* Can we UNDO the last turn? */
global uchar *restart_state, *undo_state; /* Store old game states for
RESTART and UNDO */
global char doing_restore; /* Have we been asked to RESTORE? */
global rbool do_look; /* True if we should print a room description */
global rbool do_autoverb; /* True if we should run the autoexec verb
for the current room */
/* The following are used for scripting and logging */
global rbool script_on;
global genfile scriptfile;
global signed char logflag; /* 1=logging, 2=replaying, 0=neither, 3=both */
global int logdelay; /* -1=wait for keypress, >=0 is numerical delay */
global genfile log_in, log_out;
global rbool fast_replay; /* If true, don't print MORE prompts. */
global rbool sound_on; /* Manipulated by music_cmd; can be used by interface
to determine if a tone should be made */
global integer *pictable; /* Used to decode picture numbers */
global fc_type hold_fc; /* Needed to print instructions on demand */
global unsigned short compass_rose; /* Used to pass compass info to
the os layer */
/* -------------------------------------------------------------------- */
/* Game State */
/* -------------------------------------------------------------------- */
global rbool quitflag, deadflag, winflag, endflag;
global rbool first_visit_flag, newlife_flag, room_firstdesc;
global rbool start_of_turn; /* True if running the command on the first
noun in the list */
global rbool end_of_turn; /* True if running command on last noun in
the list. */
global rbool actor_in_scope; /* Used to determine if the actor was in
scope when the command was originally
given */
global integer loc; /* Player's location */
global integer player_contents, player_worn; /* Worn and carried objects*/
global long totwt, totsize; /* Size and wt player is carrying around */
global integer curr_lives; /* Number of lives left. */
global long tscore, old_score; /* Total score */
global long objscore; /* Portion of tscore that comes from the POINTS
field of nouns and creatures. */
global integer turncnt; /* Number of turns that have passed */
global integer curr_time; /* The time in the game; in the format
1243==12:43 */
global rbool *flag; /* AGT Flags */
global short *agt_counter; /* AGT counters */
#ifdef AGT_16BIT
global short *agt_var; /*AGT variables */
#else
global long *agt_var;
#endif
global long agt_number; /* The number entered by the player */
global rbool agt_answer; /* Did the player get the answer to the last question
right? */
global tline l_stat, r_stat; /* Left and right parts of status line */
/* If r_stat is the empty string, l_stat should be
centered to create a Trinity-like status line */
global rbool nomatch_aware; /* Does the game use the nomatch extension
to the metacommand format?
(which allow <none> and ANY to be
distingused) */
global rbool smart_look; /* If true, then LOOK <object> will be converted
to EXAMINE. This is set automatically in agil.c,
based on whether the game file uses
LOOK <object> in any of the metacommands; if it
does, then smart_look is set to 0. */
/* -------------------------------------------------------------------- */
/* Menu data structures */
/* -------------------------------------------------------------------- */
#define MENU_WIDTH 50
typedef char menuentry[MENU_WIDTH];
global int vm_width; /* Width of widest element */
global menuentry *verbmenu;
/* -------------------------------------------------------------------- */
/* Parser Data Structures */
/* This also includes "parser-related" variables like dobj and iobj */
/* -------------------------------------------------------------------- */
/* This extracts the object number from a parse rec */
#define p_obj(objrec) ((objrec) ? (objrec)->obj : 0)
/* The following data structures are used for disambiguation of nouns */
struct parse_rec {
long num; /* Numeric value of object; 0 if object doesn't have one */
int obj; /* Object number; negative values point into the dictionary */
int info; /* Disambiguation info */
/* -1=last record; ignore obj field. */
word noun, adj; /* Used for printing out error messages */
short score; /* Disambiguation score */
}; /* Stores objects that have been found during parse */
/* In an ideal world, the following would all be local variables. */
/* Unfortunately, they're used in too many different places for this
to be practical */
global int vb;
global integer actor, dobj, iobj;
global parse_rec *actor_rec, *dobj_rec, *iobj_rec;
global word prep;
global parse_rec *curr_creat_rec;
/* Creature currently behaving in a hostile way:
used to fill in $c_name$ messages */
global int disambig_score; /* Used to rank objects during disambiguation */
#define DISAMBIG_SUCC 1000 /* Score given to an object that triggers a
built-in verb or an action token */
#define MAXINPUT 200 /* Max number of words input */
global word input[MAXINPUT]; /* 200 words of input should be enough */
global words in_text[MAXINPUT];
/* The corrospoinding strings, for error reporting purposes */
global short ip, ep; /* input pointer and error pointer */
global short ap, vp, np, pp, op; /* Points to first word in actor, verb, noun,
and object resp. */
/* The following needs to be kept consistent with ext_voc[] in
agil.c */
typedef enum {wthe, wmy, wa, wan, wthen, wp, wsc, wand, wc, wits, wall, wundo, wlook, wg,
wpick, wgo, wexits, wtalk, wtake, wdoor, wagain, wbut, wexcept,
wscene, weverything, wlistexit, wlistexits, wclose,
wdverb, wdnoun, wdadjective, wdprep, wdobject, wdname,
wstep, w_any, weither, wboth, weveryone, weverybody,
whe, wshe, wit, wthey, whim, wher, wthem, wis, ware, woops,
wwas, wwere, wscream,
win, wout, winto, wat, wto, wacross, winside, wwith, wnear, wfor,
wof, wbehind, wbeside, won, woff, wunder, wfrom, wthrough,
wtoward, wtowards, wbetween, waround, wupon, wthru,
wby, wover, wup, wdown,
wabout
} wtype;
global word ext_code[wabout + 1]; /* Codes for the above */
global short last_he, last_she, last_it, last_they;
/* Used for pronoun support */
/* -------------------------------------------------------------------- */
/* Noun List Data structures and constants */
/* -------------------------------------------------------------------- */
/* The following are used in noun lists */
#define AND_MARK (-ext_code[wand])
#define ALL_MARK (-ext_code[wall])
#define D_END 50 /* Marks end of disambiguation list */
#define D_AND 51 /* Used to separate multiple objects during disambig */
#define D_NOUN 0 /* Noun match */
#define D_SYN 1 /* Adjective/synonym only match */
#define D_ADJ 2 /* Adj only match */
#define D_FLAG 3 /* Flag noun */
#define D_GLOBAL 4 /* Global noun */
#define D_PIX 5 /* PIX name */
#define D_PRO 6 /* Pronoun */
#define D_ALL 7 /* ALL, or a header to an ALL EXCEPT _ AND _ ... list */
#define D_INTERN 8 /* Internal nouns: DOOR, SCENE */
#define D_NUM 9 /* A number, value is in obj */
#define D_EITHER 10 /* EITHER or ANY, used only to resolve disambiguation */
#define D_MARK 0x80 /* Used as a temporary marker, usually to indicate
this noun is being considered for elimination */
/* -------------------------------------------------------------------- */
/* These are used for text boxes (quotes and the title) */
/* -------------------------------------------------------------------- */
#define TB_TTL 1 /* We're printing the title */
#define TB_BOLD 2 /* Print it bold */
#define TB_BORDER 4 /* Give it a border */
#define TB_CENTER 8 /* Center the text inside */
#define TB_NOCENT 16 /* Don't center the whole box */
/* -------------------------------------------------------------------- */
/* In AGIL.C */
/* -------------------------------------------------------------------- */
extern void print_instructions(fc_type fc);
extern void run_game(fc_type fc);
/* -------------------------------------------------------------------- */
/* In PARSER.C */
/* -------------------------------------------------------------------- */
extern rbool parse(void); /* Returns true unless there is ambiguity */
extern void menu_cmd(void);
/* -------------------------------------------------------------------- */
/* In EXEC.C */
/* -------------------------------------------------------------------- */
/* Legal values for gen_sysmsg context; they indicate who is calling it */
#define MSG_PARSE 0 /* The parser */
#define MSG_MAIN 1 /* The main execution loop */
#define MSG_RUN 2 /* The routines that execute the player's commands */
#define MSG_DESC 3 /* Printing out description. */
extern void gen_sysmsg(int msgid, const char *s, int context, const char *pword);
/* Prints either STANDARD message <msgid> or default msg <s>;
<context> determines what $$ substitutions are meaningful
<parseword> gives the $pword$ substitution for MSG_PARSE messages */
extern void exec(parse_rec *actor, int vnum, parse_rec *lnoun,
word prep, parse_rec *iobj);
extern void set_statline(void);
extern void newroom(void);
extern void print_descr(descr_ptr dp, rbool nl);
extern void quote(int msgnum);
extern void print_score(void);
extern long read_number(void);
/* -------------------------------------------------------------------- */
/* In TOKEN.C */
/* -------------------------------------------------------------------- */
extern void init_stack(void); /* Set up expression stack */
extern void clear_stack(void); /* Set stack back to empty state */
extern void reset_random(void); /* Reset random number generator state */
extern int get_random(int a, int b); /* Return random number from a to b, inclusive */
/* -------------------------------------------------------------------- */
/* In OBJECT.C */
/* -------------------------------------------------------------------- */
extern rbool player_has(int item);
extern rbool visible(int item);
extern rbool genvisible(parse_rec *dobj);
extern int *get_nouns(void); /* Returns list of in scope nouns */
extern void add_object(int loc, int item); /* Adds item to loc's contents list */
extern void tmpobj(parse_rec *objrec);
extern void compute_scope(void); /* Sets scope flags for all of the objects */
extern void compute_seen(void); /* Determine HAS_SEEN flag for nouns and creatures */
extern void init_creat_fix(void);
extern void free_creat_fix(void);
/* ------------------------------------------------------------------- */
/* The following are intended as building blocks to construct macros */
/* to extract information about general objects, regardless of whether */
/* they are nouns, creatures, or virtual nouns with no associated */
/* data structure. */
/* ------------------------------------------------------------------- */
/* nounattr(item,attr) -- returns 0 if not noun.
creatattr(item,attr) -- returns 0 if not creature
objattr(item,attr) -- Returns attribute for noun or creature, 0 otherwise
anyattr(item,attr) -- Returns attribute for noun, creature, or room,
0 otherwise.
*/
#define creatattr2(item,attr,op3) (tcreat(item)? \
creature[(item)-first_creat].attr:\
(op3))
#define creatattr(item,attr) creatattr2(item,attr,0)
#define nounattr2(item,attr,alt) (tnoun(item)? \
noun[(item)-first_noun].attr:(alt))
#define nounattr(item,attr) nounattr2(item,attr,0)
#define objattr(item,attr) nounattr2(item,attr,creatattr(item,attr))
#define objattr2(item,attr,op3) nounattr2(item,attr,creatattr2(item,attr,op3))
#define roomattr2(item,attr,op3) (troom(item)?\
room[(item)-first_room].attr:(op3))
#define anyattr(item,attr) roomattr2(item,attr,objattr(item,attr))
#define it_scratch(item) objattr(item,scratch)
#define it_loc(item) objattr2(item,location,\
(tdoor(item)) ? loc+first_room : 0)
/* -------------------------------------------------------------------- */
/* In RUNVERB.C */
/* -------------------------------------------------------------------- */
extern int check_obj(parse_rec *act, int verbid,
parse_rec *donum, word prep, parse_rec *ionum);
/* -------------------------------------------------------------------- */
/* In AGTDBG.C */
/* -------------------------------------------------------------------- */
extern void debug_cmd_out(int ip, integer op, int arg1, int arg2, int optype);
extern void debug_head(int);
extern void debug_newline(integer op, rbool first_nl);
/* -------------------------------------------------------------------- */
/* In SAVEGAME.C */
/* -------------------------------------------------------------------- */
extern Common::Error savegame(Common::WriteStream *savefile);
extern Common::Error loadgame(Common::SeekableReadStream *loadfile);
extern void init_state_sys(void); /* Must be called before either of the following */
extern uchar *getstate(uchar *gs);
/* Returns malloc'd block containing game state. */
extern void putstate(uchar *gs); /* Restores games state. */
extern void init_vals(void); /* Compute dependent variables
such as totwt, totsize, etc. */
extern void restart_game(void);
/* -------------------------------------------------------------------- */
/* In OS_<whatever>.C */
/* -------------------------------------------------------------------- */
global volatile int screen_width, status_width;
global int screen_height;
global volatile int curr_x;
extern void init_interface();
extern void start_interface(fc_type fc);
extern char *agt_input(int in_type); /* read line, return malloc'd string */
extern char agt_getkey(rbool echo_char);
extern void agt_clrscr(void);
extern void agt_textcolor(int c);
extern void agt_delay(int n); /* n in seconds */
extern int agt_rand(int a, int b); /* Return random number from a to b, inclusive */
extern void agt_newline(void);
extern void agt_puts(const char *s); /* Output string */
extern void agt_statline(const char *s); /* Prints s out on status line */
extern void agt_tone(int hz, int ms);
extern void agt_makebox(int width, int height, unsigned long flags);
extern void agt_qnewline(void);
extern void agt_endbox(void);
extern genfile agt_globalfile(int fid); /* When fid=0, return global config file */
extern rbool agt_option(int optnum, char *optstr[], rbool setflag);
/* These have stubs defined in interface.c that would need to be
commented out if you actually wanted to support these */
extern void fontcmd(int cmd, int font); /* fontlist[font] */
extern void pictcmd(int cmd, int pict); /* pictlist[pict] or pixlist[pict] */
extern int musiccmd(int cmd, int song); /* songlist[song] */
/* -------------------------------------------------------------------- */
/* In INTERFACE.C */
/* -------------------------------------------------------------------- */
/* init_interface() (in os_?????.c) is responsible for initializing these */
global rbool par_fill_on, center_on;
global rbool textbold; /* Is the text currently bold? */
extern void wait_return(void);
extern void agt_waitkey(void);
extern void agt_center(rbool b); /* 1=turn on text centering, 0=turn off */
extern void agt_par(rbool b); /* 1=turn on "paragraph" mode, 0=turn off */
extern char *agt_readline(int in_type); /* Front end for agt_input */
extern char agt_getchar(void); /* Front end for some uses of agt_getkey */
extern void prompt_out(int); /* 1=standard prompt, 2=question prompt */
extern genfile get_user_file(int ft); /* 0=script, 1=save, 2=restore */
extern void set_default_filenames(fc_type fc);
extern void script(uchar); /* 0=turn off, 1=turn on */
extern void logon(void); /* Turn on logging */
extern int close_pfile(genfile f, int ft); /* ft is the same as for get_user_file */
extern void replay(int delay); /* REPLAY */
extern rbool yesno(const char *);
extern void textbox(char *(txt[]), int len, unsigned long flags);
extern void padout(int padleng); /* Outputs padleng spaces */
extern int agt_menu(const char *header, int size, int width, menuentry *menu);
extern fc_type new_game(void);
extern void set_test_mode(fc_type fc);
/* This sets up scripting and replaying for testing mode */
/* These are intended to be called by the os layer */
extern void print_statline(void);
extern void agt_save(void);
extern void agt_restore(void);
extern void agt_restart(void);
extern void agt_quit(void);
extern void agt_newgame(fc_type fc);
/* -------------------------------------------------------------------- */
/* Object manipulation macros */
/* -------------------------------------------------------------------- */
#define objloop(i) for(i=first_noun; i<=maxnoun || i<=maxcreat; \
(i<=maxnoun || i>=first_creat) ? (i++) : (i=first_creat) )
#define nounloop(i) for(i=0;i<=maxnoun-first_noun;i++)
#define creatloop(i) for(i=0;i<=maxcreat-first_creat;i++)
#define tdoor(x) ((x)==-ext_code[wdoor])
#ifdef uagt_defined_global
#undef global
#undef uagt_define_global
#endif
} // End of namespace AGT
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

1063
engines/glk/agt/object.cpp Normal file

File diff suppressed because it is too large Load Diff

5872
engines/glk/agt/os_glk.cpp Normal file

File diff suppressed because it is too large Load Diff

1622
engines/glk/agt/parser.cpp Normal file

File diff suppressed because it is too large Load Diff

1673
engines/glk/agt/runverb.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,428 @@
/* 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"
#include "glk/agt/exec.h"
namespace Glk {
namespace AGT {
#define SAVE_UNDO
#define DEBUG_SAVE_SIZE 0
long state_size;
/*-------------------------------------------------------------------*/
/* INITIALISATION ROUTINES */
/* These initialize all of the values that can be derived from */
/* other data in the game file or that are reset when a game */
/* is restored */
/* See parser.c for the interpreter's main initialisation routines */
void init_vals(void)
/* Compute quantities that can be deduced from existing data */
{
int i;
quitflag = winflag = deadflag = endflag = 0;
cmd_saveable = 0;
last_he = last_she = last_it = 0;
totwt = totsize = 0;
for (i = 0; i <= maxroom - first_room; i++)
room[i].contents = 0;
player_contents = player_worn = 0;
for (i = 0; i <= maxnoun - first_noun; i++) {
if (player_has(i + first_noun)) totwt += noun[i].weight;
if (noun[i].location == 1) totsize += noun[i].size;
noun[i].something_pos_near_noun = 0;
noun[i].contents = noun[i].next = 0;
}
for (i = 0; i <= maxcreat - first_creat; i++)
creature[i].contents = creature[i].next = 0;
for (i = 0; i <= maxnoun - first_noun; i++) {
add_object(noun[i].location, i + first_noun);
if (noun[i].nearby_noun >= first_noun &&
noun[i].nearby_noun <= maxnoun)
noun[noun[i].nearby_noun - first_noun].something_pos_near_noun = 1;
}
for (i = 0; i <= maxcreat - first_creat; i++)
add_object(creature[i].location, i + first_creat);
objscore = 0; /* Will need to recompute this ... */
}
/*-------------------------------------------------------------------*/
/* ROUTINES TO SAVE/RESTORE THE GAME STATE */
/* These are used by RESTART and UNDO as well as SAVE and RESTORE */
/* Game State format: */
/* The first two bytes indicate the length of the block (unsigned).*/
/* The next two bytes indicate the game file somehow (so we don't try to */
/* restore to a different game). */
/* After this comes the game information itself. */
/* All values are still little-endian (that is, LSB first) */
/* These are the macros for writing game information to the state block */
/* There is no difference between signed and unsigned when storing them;
there will be problems when recovering them again. */
#define g(ft,var) {ft,DT_DEFAULT,&var,0}
#define r(ft,str,f) {ft,DT_DEFAULT,NULL,offsetof(str,f)}
#define dptype {FT_DESCPTR,DT_DESCPTR,NULL,0}
static file_info fi_savehead[] = {
g(FT_INT16, loc), g(FT_INT32, tscore), g(FT_INT16, turncnt),
g(FT_BYTE, statusmode),
g(FT_BOOL, first_visit_flag), g(FT_BOOL, newlife_flag),
g(FT_BOOL, room_firstdesc), g(FT_BOOL, verboseflag),
g(FT_BOOL, notify_flag), g(FT_BOOL, listexit_flag),
g(FT_BOOL, menu_mode), g(FT_BOOL, sound_on),
g(FT_BOOL, agt_answer), g(FT_INT32, agt_number),
g(FT_INT16, curr_time), g(FT_INT16, curr_lives),
g(FT_INT16, delta_time),
endrec
};
static file_info fi_saveroom[] = {
dptype,
r(FT_BOOL, room_rec, seen),
r(FT_BOOL, room_rec, locked_door),
r(FT_INT16, room_rec, oclass),
r(FT_INT16, room_rec, points),
r(FT_INT16, room_rec, light),
r(FT_PATHARRAY, room_rec, path),
r(FT_UINT32, room_rec, flag_noun_bits),
endrec
};
static file_info fi_savenoun[] = {
dptype,
r(FT_INT16, noun_rec, location),
r(FT_INT16, noun_rec, nearby_noun),
r(FT_INT16, noun_rec, num_shots),
r(FT_INT16, noun_rec, initdesc),
r(FT_INT16, noun_rec, oclass),
r(FT_INT16, noun_rec, points),
r(FT_INT16, noun_rec, weight),
r(FT_INT16, noun_rec, size),
r(FT_BOOL, noun_rec, on),
r(FT_BOOL, noun_rec, open),
r(FT_BOOL, noun_rec, locked),
r(FT_BOOL, noun_rec, movable),
r(FT_BOOL, noun_rec, seen),
r(FT_WORD, noun_rec, pos_prep),
r(FT_WORD, noun_rec, pos_name),
endrec
};
static file_info fi_savecreat[] = {
dptype,
r(FT_INT16, creat_rec, location),
r(FT_INT16, creat_rec, counter),
r(FT_INT16, creat_rec, timecounter),
r(FT_INT16, creat_rec, initdesc),
r(FT_INT16, creat_rec, oclass),
r(FT_INT16, creat_rec, points),
r(FT_BOOL, creat_rec, groupmemb),
r(FT_BOOL, creat_rec, hostile),
r(FT_BOOL, creat_rec, seen),
endrec
};
static file_info fi_saveustr[] = {
{FT_TLINE, DT_DEFAULT, nullptr, 0},
endrec
};
uchar *getstate(uchar *gs)
/* Returns block containing game state.
If gs!=NULL, uses that space as a buffer;
if gs==NULL, we malloc a new block and return it */
{
rbool new_block; /* True if we allocate a new block */
long bp;
if (gs == nullptr) {
rm_trap = 0; /* Don't exit on out-of-memory condition */
gs = (uchar *)rmalloc(state_size); /* This should be enough. */
rm_trap = 1;
if (gs == nullptr) /* This is why we set rm_trap to 0 before calling rmalloc */
return nullptr;
new_block = 1;
} else new_block = 0;
/* First two bytes reserved for block size, which we don't know yet.*/
gs[4] = game_sig & 0xFF;
gs[5] = (game_sig >> 8) & 0xFF;
tscore -= objscore; /* Only include "permanent" part of score;
objscore we can recompute on RESTORE */
/* Need to setup here */
set_internal_buffer(gs);
fi_saveroom[0].ptr = room_ptr;
fi_savenoun[0].ptr = noun_ptr;
fi_savecreat[0].ptr = creat_ptr;
bp = 6;
bp += write_globalrec(fi_savehead, bp);
bp += write_recblock(flag, FT_BYTE, FLAG_NUM + 1, bp);
bp += write_recblock(agt_counter, FT_INT16, CNT_NUM + 1, bp);
bp += write_recblock(agt_var, FT_INT32, VAR_NUM + 1, bp);
bp += write_recarray(room, sizeof(room_rec), rangefix(maxroom - first_room + 1),
fi_saveroom, bp);
bp += write_recarray(noun, sizeof(noun_rec), rangefix(maxnoun - first_noun + 1),
fi_savenoun, bp);
bp += write_recarray(creature, sizeof(creat_rec),
rangefix(maxcreat - first_creat + 1),
fi_savecreat, bp);
if (userstr != nullptr)
bp += write_recarray(userstr, sizeof(tline), MAX_USTR, fi_saveustr, bp);
if (objflag != nullptr)
bp += write_recblock(objflag, FT_BYTE, objextsize(0), bp);
if (objprop != nullptr)
bp += write_recblock(objprop, FT_INT32, objextsize(1), bp);
set_internal_buffer(nullptr);
gs[0] = bp & 0xFF;
gs[1] = (bp >> 8) & 0xFF;
gs[2] = (bp >> 16) & 0xFF;
gs[3] = (bp >> 24) & 0x7F; /* Don't trust top bit */
if (new_block)
gs = (uchar *)rrealloc(gs, bp);
tscore += objscore;
return gs;
}
void putstate(uchar *gs) { /* Restores games state. */
long size, bp, numrec, i;
size = gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24);
if (size != state_size) {
writeln("Size difference in save files!");
agt_delay(3);
return;
}
if (gs[4] + (((long)gs[5]) << 8) != game_sig) {
writestr("This appears to be a save file for a different game. Is this"
" from an earlier chapter in a multi-part game such as"
" Klaustrophobia");
if (yesno("?"))
skip_descr = 1; /* We don't want to overwrite the descriptions
with the pointers from the save file. */
else {
writestr("Do you want to try using it anyhow (WARNING: This could"
" crash the interpreter)");
if (!(yesno("?"))) {
writeln("Command cancelled!");
agt_delay(3);
return;
}
}
}
/* setup... */
set_internal_buffer(gs);
fi_saveroom[0].ptr = room_ptr;
fi_savenoun[0].ptr = noun_ptr;
fi_savecreat[0].ptr = creat_ptr;
bp = 6;
read_globalrec(fi_savehead, nullptr, bp, 0);
bp += compute_recsize(fi_savehead);
read_recblock(flag, FT_BYTE, FLAG_NUM + 1, bp, 0);
bp += ft_leng[FT_BYTE] * (FLAG_NUM + 1);
read_recblock(agt_counter, FT_INT16, CNT_NUM + 1, bp, 0);
bp += ft_leng[FT_INT16] * (CNT_NUM + 1);
read_recblock(agt_var, FT_INT32, VAR_NUM + 1, bp, 0);
bp += ft_leng[FT_INT32] * (VAR_NUM + 1);
numrec = rangefix(maxroom - first_room + 1);
read_recarray(room, sizeof(room_rec), numrec, fi_saveroom, nullptr, bp, 0);
bp += compute_recsize(fi_saveroom) * numrec;
numrec = rangefix(maxnoun - first_noun + 1);
read_recarray(noun, sizeof(noun_rec), numrec, fi_savenoun, nullptr, bp, 0);
bp += compute_recsize(fi_savenoun) * numrec;
numrec = rangefix(maxcreat - first_creat + 1);
read_recarray(creature, sizeof(creat_rec), numrec, fi_savecreat, nullptr, bp, 0);
bp += compute_recsize(fi_savecreat) * numrec;
if (userstr != nullptr) {
read_recarray(userstr, sizeof(tline), MAX_USTR, fi_saveustr, nullptr, bp, 0);
bp += ft_leng[FT_TLINE] * MAX_USTR;
}
if (objflag != nullptr) {
i = objextsize(0);
read_recblock(objflag, FT_BYTE, i, bp, 0);
bp += ft_leng[FT_BYTE] * i;
}
if (objprop != nullptr) {
i = objextsize(1);
read_recblock(objprop, FT_INT32, i, bp, 0);
bp += ft_leng[FT_INT32] * i;
}
set_internal_buffer(nullptr);
if (skip_descr) /* Need to "fix" position information. This is a hack. */
/* Basically, this sets the position of each object to its default */
/* The problem here is that the usual position info is invalid-- we've
changed games, and hence dictionaries */
for (i = 0; i < maxnoun - first_noun; i++) {
if (noun[i].position != nullptr && noun[i].position[0] != 0)
noun[i].pos_prep = -1;
else noun[i].pos_prep = 0;
}
else /* Rebuild position information */
for (i = 0; i < maxnoun - first_noun; i++)
if (noun[i].pos_prep == -1)
noun[i].position = noun[i].initpos;
else
noun[i].position = nullptr;
init_vals();
skip_descr = 0; /* If we set this to 1, restore it to its original state */
/* Now do some simple consistancy checking on major variables */
if (loc > maxroom || loc < 0 || turncnt < 0 ||
curr_lives < 0 || curr_lives > max_lives) {
error("Error: Save file inconsistent.");
}
}
void init_state_sys(void)
/* Initializes the state saving mechanisms */
/* Mainly it just computes the size of a state block */
{
state_size = compute_recsize(fi_savehead)
+ compute_recsize(fi_saveroom) * rangefix(maxroom - first_room + 1)
+ compute_recsize(fi_savenoun) * rangefix(maxnoun - first_noun + 1)
+ compute_recsize(fi_savecreat) * rangefix(maxcreat - first_creat + 1)
+ ft_leng[FT_BYTE] * (FLAG_NUM + 1)
+ ft_leng[FT_INT16] * (CNT_NUM + 1)
+ ft_leng[FT_INT32] * (VAR_NUM + 1)
+ ft_leng[FT_BYTE] * objextsize(0)
+ ft_leng[FT_INT32] * objextsize(1)
+ 6; /* Six bytes in header */
if (userstr != nullptr) state_size += ft_leng[FT_TLINE] * MAX_USTR;
}
/*-------------------------------------------------------------------*/
/* SAVE FILE ROUTINES */
extern Common::Error savegame(Common::WriteStream *savefile) {
uchar *gs;
long size;
#ifndef UNDO_SAVE
gs = getstate(nullptr);
#else
gs = undo_state;
#endif
if (gs == nullptr) {
writeln("Insufficiant memory to support SAVE.");
return Common::kWritingFailed;
}
if (!filevalid(savefile, fSAV)) {
writeln("That is not a valid save file.");
return Common::kWritingFailed;
}
size = gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24);
bool result = binwrite(savefile, gs, size, 1, 0);
#ifndef UNDO_SAVE
rfree(gs);
#endif
if (!result) {
warning("Error writing save file.");
return Common::kWritingFailed;
} else {
return Common::kNoError;
}
}
/* 1=success, 0=failure */
Common::Error loadgame(Common::SeekableReadStream *loadfile) {
long size;
uchar *gs;
const char *errstr;
if (!filevalid(loadfile, fSAV)) {
warning("Unable to open file.");
return Common::kReadingFailed;
}
size = binsize(loadfile);
if (size == -1) {
warning("Could not access file.");
return Common::kReadingFailed;
}
gs = (uchar *)rmalloc(size);
if (!binread(loadfile, gs, size, 1, &errstr)) {
warning("Error reading file.");
rfree(gs);
return Common::kReadingFailed;
}
if (size != gs[0] + (((long)gs[1]) << 8) + (((long)gs[2]) << 16) + (((long)gs[3]) << 24)) {
if (size == gs[0] + (((long)gs[1]) << 8)) {
/* Old save file format; patch to look like new format */
gs = (uchar *)rrealloc(gs, size + 2);
memmove(gs + 4, gs + 2, size - 2);
gs[2] = gs[3] = 0;
} else {
warning("Save file corrupted or invalid.");
rfree(gs);
return Common::kReadingFailed;
}
}
putstate(gs);
rfree(gs);
set_statline();
look_room();
return Common::kNoError;
}
void restart_game(void) {
putstate(restart_state);
reset_random();
agt_clrscr();
set_statline();
do_look = do_autoverb = 1;
if (intro_ptr.size > 0) {
print_descr(intro_ptr, 1);
wait_return();
agt_clrscr();
}
newroom();
}
} // End of namespace AGT
} // End of namespace Glk

1177
engines/glk/agt/token.cpp Normal file

File diff suppressed because it is too large Load Diff

1475
engines/glk/agt/util.cpp Normal file

File diff suppressed because it is too large Load Diff

208
engines/glk/agt/vars.cpp Normal file
View File

@@ -0,0 +1,208 @@
/* 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 {
uchar DIAG, interp_arg, debug_da1, RAW_CMD_OUT;
int ERR_LEVEL;
rbool agx_file;
rbool have_opt;
rbool skip_descr;
rbool no_auxsyn;
rbool BATCH_MODE, make_test;
rbool have_meta;
rbool debug_mode, freeze_mode, milltime_mode, bold_mode;
uchar score_mode, statusmode;
rbool intro_first;
rbool box_title;
rbool mars_fix;
rbool fix_ascii_flag;
rbool dbg_nomsg;
rbool irun_mode;
rbool verboseflag;
int font_status;
rbool MASTERS_OR;
integer FLAG_NUM, CNT_NUM, VAR_NUM;
integer MAX_USTR;
integer MAX_SUB;
integer DVERB;
integer NUM_ERR;
integer num_rflags, num_nflags, num_cflags;
integer num_rprops, num_nprops, num_cprops;
integer oprop_cnt, oflag_cnt;
integer maxroom, maxnoun, maxcreat;
long MaxQuestion;
integer first_room, first_noun, first_creat, last_obj;
long last_message, last_cmd;
long numglobal;
long maxpict, maxpix, maxfont, maxsong;
long num_prep;
int num_auxcomb;
int num_comb;
integer exitmsg_base;
integer start_room, treas_room, resurrect_room, max_lives;
long max_score;
integer startup_time, delta_time;
int ver, aver;
long game_sig;
int vm_size;
int min_ver;
room_rec *room;
creat_rec *creature;
noun_rec *noun;
cmd_rec *command;
unsigned char *objflag;
long *objprop;
attrdef_rec *attrtable;
propdef_rec *proptable;
vardef_rec *vartable;
flagdef_rec *flagtable;
const char **propstr;
long propstr_size;
tline *userstr;
word *sub_name;
verbentry_rec *verbinfo;
short *verbptr, *verbend;
slist *synlist;
slist *comblist;
word *old_agt_verb;
slist *userprep;
word flag_noun[MAX_FLAG_NOUN], *globalnoun;
word pix_name[MAX_PIX];
filename *pictlist, *pixlist, *fontlist, *songlist;
uchar opt_data[14];
slist *auxsyn;
slist *preplist;
uchar *verbflag;
slist *auxcomb;
#ifdef PATH_SEP
char **gamepath;
#endif
rbool stable_random;
rbool DEBUG_MEM;
rbool debug_parse;
rbool DEBUG_EXEC_VERB;
rbool DEBUG_DISAMBIG;
rbool DEBUG_SMSG;
rbool debug_disambig, debug_any;
rbool DEBUG_OUT;
Common::DumpFile *debugfile;
rbool notify_flag, listexit_flag, menu_mode;
rbool cmd_saveable;
rbool can_undo;
uchar *restart_state, *undo_state;
char doing_restore;
rbool do_look;
rbool do_autoverb;
rbool script_on;
genfile scriptfile;
signed char logflag;
int logdelay;
genfile log_in, log_out;
rbool fast_replay;
rbool sound_on;
integer *pictable;
fc_type hold_fc;
unsigned short compass_rose;
rbool quitflag, deadflag, winflag, endflag;
rbool first_visit_flag, newlife_flag, room_firstdesc;
rbool start_of_turn;
rbool end_of_turn;
rbool actor_in_scope;
integer loc;
integer player_contents, player_worn;
long totwt, totsize;
integer curr_lives;
long tscore, old_score;
long objscore;
integer turncnt;
integer curr_time;
rbool *flag;
short *agt_counter;
#ifdef AGT_16BIT
short *agt_var;
#else
long *agt_var;
#endif
long agt_number;
rbool agt_answer;
tline l_stat, r_stat;
rbool nomatch_aware;
rbool smart_look;
int vm_width;
menuentry *verbmenu;
int vb;
integer actor, dobj, iobj;
parse_rec *actor_rec, *dobj_rec, *iobj_rec;
word prep;
parse_rec *curr_creat_rec;
int disambig_score;
word input[MAXINPUT];
words in_text[MAXINPUT];
short ip, ep;
short ap, vp, np, pp, op;
word ext_code[wabout + 1];
short last_he, last_she, last_it, last_they;
volatile int screen_width, status_width;
int screen_height;
volatile int curr_x;
rbool par_fill_on, center_on;
rbool textbold;
uchar trans_ascii[256];
rbool rm_acct;
long rfree_cnt, ralloc_cnt;
long rm_size, rm_freesize;
words *verblist;
/*-----------------------------------------------------------------*/
uchar do_disambig;
rbool beforecmd;
rbool suppress_debug;
rbool was_metaverb;
integer oldloc;
integer subcall_arg;
integer *creat_fix;
/*-----------------------------------------------------------------*/
rbool *dbgflagptr;
long *dbgvarptr;
short *dbgcntptr;
rbool PURE_DOT;
} // End of namespace AGT
} // End of namespace Glk