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

View File

@@ -0,0 +1,248 @@
/* 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_TADS_TADS2_APPCTX
#define GLK_TADS_TADS2_APPCTX
#include "common/scummsys.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* Application container context. The TADS run-time is a subsystem that
* can be invoked from different types of applications; in fact, even
* when only the standard stand-alone run-time is considered, multiple
* application containers must be supported because of differences
* between operating systems. The application container context is an
* optional mechanism that the main application can use to provide
* structured interaction between itself and the TADS run-time subsystem.
*
* The function pointers contained herein are intended to allow the
* run-time subsystem to call the host system to notify it of certain
* events, or obtain optional services from the host system. Any of
* these function pointers can be null, in which case the run-time
* subsystem will skip calling them.
*
* Note that each function has an associated callback context. This
* allows the host system to recover any necessary context information
* when the callback is invoked.
*/
struct appctxdef {
/**
* Get the .GAM file name. The run-time will call this only if it
* can't find a game file to load through some other means first.
* The run-time determines the game file first by looking at the
* command line, then by checking to see if a .GAM file is attached
* to the executable. If none of these checks yields a game, the
* run-time will call this routine to see if the host system wants
* to provide a game. This routine can be implemented on a GUI
* system, for example, to display a dialog prompting the user to
* select a game file to open. A trivial implementation of this
* routine (that merely returns false) is okay.
*
* This routine should return true (any non-zero value) if it
* provides the name of a file to open, false (zero) if not.
*/
int (*get_game_name)(void *appctxdat, char *buf, size_t buflen);
void *get_game_name_ctx;
/**
* Set the .GAM file name. When the run-time determines the name of
* the file it will use to read the game, it calls this routine.
* The host system should note the game filename if it will need to
* access the game file itself (for example, to load resources).
*/
void (*set_game_name)(void *appctxdat, const char *fname);
void *set_game_name_ctx;
/**
* Set the root path for individual resources. By default, we use the
* directory containing the game file, but this can be used to override
* that.
*/
void (*set_res_dir)(void *appctxdat, const char *fname);
void *set_res_dir_ctx;
/**
* Set the resource map address in the game file. If the .GAM
* reader encounters a resource map in the file, it calls this
* routine with the seek offset of the first resource. Each
* resource's address is given as an offset from this point.
*
* fileno is the file number assigned by the host system in
* add_resfile. File number zero is always the .GAM file.
*/
void (*set_resmap_seek)(void *appctxdat, unsigned long seekpos, int fileno);
void *set_resmap_seek_ctx;
/**
* Add a resource entry. The 'ofs' entry is the byte offset of the
* start of the resource, relative to the seek position previously
* set with set_resmap_seek. 'siz' is the size of the resource in
* bytes; the resource is stored as contiguous bytes starting at the
* given offset for the given size. Note that resources may be
* added before the resource map seek position is set, so the host
* system must simply store the resource information for later use.
* The 'fileno' is zero for the .GAM file, or the number assigned by
* the host system in add_resfile for other resource files.
*/
void (*add_resource)(void *appctxdat, unsigned long ofs,
unsigned long siz, const char *nm, size_t nmlen,
int fileno);
void *add_resource_ctx;
/**
* Add a resource link entry. 'fname' and 'fnamelen' give the name of
* a local file containing the resource data; 'resname' and
* 'resnamelen' give the name of the resource as it appears within the
* compiled game file. This creates a link from a .GAM resource name
* to a local filename, where the actual binary resource data reside,
* so that we can retrieve a resource by .GAM resource name without
* actually copying the data into the .GAM file. This is used mostly
* for debugging purposes: it allows the compiler to skip the step of
* copying the resource data into the .GAM file, but still allows the
* game to load resources by .GAM resource name, to create a testing
* environment that's consistent with the full build version (where the
* resources would actually be copied).
*/
void (*add_resource_link)(void *appctxdat,
const char *fname, size_t fnamelen,
const char *resname, size_t resnamelen);
void *add_resource_link_ctx;
/**
* Add a resource path. 'path' is a string giving a directory prefix
* in local system notation.
*
* This adds a directory to the list of directories that we'll search
* when we're looking for an individual resource as an external file
* (such as a ".jpg" image file or ".ogg" sound file). This can be
* called zero or more times; each call adds another directory to
* search after any previous directories. We'll always search the
* default directory first (this is the directory containing the game
* file); then we'll search directories added with this call in the
* order in which the directories were added.
*/
void (*add_res_path)(void *appctxdat, const char *path, size_t len);
void *add_res_path_ctx;
/**
* Find a resource entry. If the resource can be found, this must
* return an osfildef* handle to the resource, with its seek position
* set to the first byte of the resource data, and set *res_size to
* the size in bytes of the resource data in the file. If the
* resource cannot be found, returns null.
*/
osfildef *(*find_resource)(void *appctxdat,
const char *resname, size_t resnamelen,
unsigned long *res_size);
void *find_resource_ctx;
/**
* Add a resource file. The return value is a non-zero file number
* assigned by the host system; we'll use this number in subsequent
* calls to add_resource to add the resources from this file.
*
* After calling this routine to add the file, we'll parse the file
* and add any resources using add_resource.
*/
int (*add_resfile)(void *appctxdat, const char *fname);
void *add_resfile_ctx;
/**
* Determine if a resource exists. Returns true if the resource can
* be loaded, false if not. The resource name is in the standard
* URL-style format.
*/
int (*resfile_exists)(void *appctxdat, const char *res_name,
size_t res_name_len);
void *resfile_exists_ctx;
/**
* Resource file path. If we should look for resource files in a
* different location than the .GAM file, the host system can set
* this to a path that we should use to look for resource files. If
* it's null, we'll look in the directory that contains the .GAM
* file. Note that if the path is provided, it must be set up with
* a trailing path separator character, so that we can directly
* append a name to this path to form a valid fully-qualified
* filename.
*/
const char *ext_res_path;
/**
* File safety level get/set. During initialization, we'll call the
* host system to tell it the file safety level selected by the user on
* the command line; if the host system is saving preference
* information, it should temporarily override its saved preferences
* and use the command line setting (and it may, if appropriate, want
* to save the command line setting as the saved preference setting,
* depending on how it handles preferences). During execution, any
* time the game tries to open a file (using the fopen built-in
* function), we'll call the host system to ask it for the current
* setting, and use this new setting rather than the original command
* line setting.
*
* Refer to bif.c for information on the meanings of the file safety
* levels.
*/
void (*set_io_safety_level)(void *ctx, int read, int write);
void (*get_io_safety_level)(void *ctx, int *read, int *write);
void *io_safety_level_ctx;
/**
* Network safety level get/set. This is analogous to the file safety
* level scheme, but controls access to network resources. There are
* two components to the network safety setting: client and server.
* The client component controls the game's ability to open network
* connections to access information on remote machines, such as
* opening http connections to access web sites. The server component
* controls the game's ability to create servers of its own and accept
* incoming connections. Each component can be set to one of the
* following:
*
*. 0 = no restrictions (least "safety"): all network access granted
*. 1 = 'localhost' access only
*. 2 = no network access
*
* This only applies to the TADS 3 VM. TADS 2 doesn't support any
* network features, so this doesn't apply.
*/
void (*set_net_safety_level)(void *ctx, int client_level, int srv_level);
void (*get_net_safety_level)(void *ctx, int *client_level, int *srv_level);
void *net_safety_level_ctx;
/**
* Name of run-time application for usage messages. If this is
* null, the default run-time application name will be used for
* usage messages.
*/
const char *usage_app_name;
};
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,219 @@
/* 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/>.
*
*/
/* Built-in functions interface
*
* Interface to run-time intrinsic function implementation
*/
#ifndef GLK_TADS_TADS2_BUILT_IN
#define GLK_TADS_TADS2_BUILT_IN
#include "glk/tads/tads2/built_in.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/list.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/text_io.h"
#include "glk/tads/tads2/regex.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward definitions */
struct vocidef;
struct voccxdef;
/* maximum number of file handles available */
#define BIFFILMAX 10
/* file contexts for the built-in file handling functions */
struct biffildef {
osfildef *fp; /* underyling system file handle */
uint flags; /* flags */
#define BIFFIL_F_BINARY 0x01 /* file is binary */
biffildef() : fp(nullptr), flags(0) {}
};
/* built-in execution context */
struct bifcxdef {
errcxdef *bifcxerr; /* error-handling context */
runcxdef *bifcxrun; /* code execution context */
tiocxdef *bifcxtio; /* text I/O context */
long bifcxrnd; /* random number seed */
int bifcxseed1; /* first seed for new generator */
int bifcxseed2; /* second seed for new generator */
int bifcxseed3; /* third seed for new generator */
int bifcxrndset; /* randomize() has been called */
biffildef bifcxfile[BIFFILMAX]; /* file handles for fopen, etc */
int bifcxsafetyr; /* file I/O safety level - read */
int bifcxsafetyw; /* file I/O safety level - write */
const char *bifcxsavext; /* saved game extension (null by default) */
appctxdef *bifcxappctx; /* host application context */
re_context bifcxregex; /* regular expression searching context */
bifcxdef() : bifcxerr(nullptr), bifcxrun(nullptr), bifcxtio(nullptr),
bifcxrnd(0), bifcxseed1(0), bifcxseed2(0), bifcxseed3(0), bifcxrndset(0),
bifcxsafetyr(0), bifcxsafetyw(0), bifcxsavext(nullptr), bifcxappctx(nullptr) {
}
};
/*
* argument list checking routines - can be disabled for faster
* run-time
*/
/* check for proper number of arguments */
/* void bifcntargs(bifcxdef *ctx, int argcnt) */
/* check that next argument has proper type */
/* void bifchkarg(bifcxdef *ctx, dattyp typ); */
#ifdef RUNFAST
# define bifcntargs(ctx, parmcnt, argcnt)
# define bifchkarg(ctx, typ)
#else /* RUNFAST */
# define bifcntargs(ctx, parmcnt, argcnt) \
(parmcnt == argcnt ? DISCARD 0 : \
(runsig(ctx->bifcxrun, ERR_BIFARGC), DISCARD 0))
# define bifchkarg(ctx, typ) \
(runtostyp(ctx->bifcxrun) == typ ? DISCARD 0 : \
(runsig(ctx->bifcxrun, ERR_INVTBIF), DISCARD 0))
#endif /* RUNFAST */
/* determine if one object is a subclass of another */
int bifinh(struct voccxdef *voc, struct vocidef *v, objnum cls);
/* enumerate the built-in functions */
void bifyon(bifcxdef *ctx, int argc); /* yorn - yes or no */
void bifsfs(bifcxdef *ctx, int argc); /* setfuse */
void bifrfs(bifcxdef *ctx, int argc); /* remfuse */
void bifsdm(bifcxdef *ctx, int argc); /* setdaemon */
void bifrdm(bifcxdef *ctx, int argc); /* remdaemon */
void bifinc(bifcxdef *ctx, int argc); /* incturn */
void bifqui(bifcxdef *ctx, int argc); /* quit */
void bifsav(bifcxdef *ctx, int argc); /* save */
void bifrso(bifcxdef *ctx, int argc); /* restore */
void biflog(bifcxdef *ctx, int argc); /* logging */
void bifres(bifcxdef *ctx, int argc); /* restart */
void bifinp(bifcxdef *ctx, int argc); /* input - get line from kb */
void bifnfy(bifcxdef *ctx, int argc); /* notify */
void bifunn(bifcxdef *ctx, int argc); /* unnotify */
void biftrc(bifcxdef *ctx, int argc); /* trace on/off */
void bifsay(bifcxdef *ctx, int argc); /* say */
void bifcar(bifcxdef *ctx, int argc); /* car */
void bifcdr(bifcxdef *ctx, int argc); /* cdr */
void bifcap(bifcxdef *ctx, int argc); /* caps */
void biflen(bifcxdef *ctx, int argc); /* length */
void biffnd(bifcxdef *ctx, int argc); /* find */
void bifsit(bifcxdef *ctx, int argc); /* setit - set current 'it' */
void bifsrn(bifcxdef *ctx, int argc); /* randomize: seed rand */
void bifrnd(bifcxdef *ctx, int argc); /* rand - get a random number */
void bifask(bifcxdef *ctx, int argc); /* askfile */
void bifssc(bifcxdef *ctx, int argc); /* setscore */
void bifsub(bifcxdef *ctx, int argc); /* substr */
void bifcvs(bifcxdef *ctx, int argc); /* cvtstr: convert to string */
void bifcvn(bifcxdef *ctx, int argc); /* cvtnum: convert to number */
void bifupr(bifcxdef *ctx, int argc); /* upper */
void biflwr(bifcxdef *ctx, int argc); /* lower */
void biffob(bifcxdef *ctx, int argc); /* firstobj */
void bifnob(bifcxdef *ctx, int argc); /* nextobj */
void bifsvn(bifcxdef *ctx, int argc); /* setversion */
void bifarg(bifcxdef *ctx, int argc); /* getarg */
void biftyp(bifcxdef *ctx, int argc); /* datatype */
void bifisc(bifcxdef *ctx, int argc); /* isclass */
void bifund(bifcxdef *ctx, int argc); /* undo */
void bifdef(bifcxdef *ctx, int argc); /* defined */
void bifpty(bifcxdef *ctx, int argc); /* proptype */
void bifoph(bifcxdef *ctx, int argc); /* outhide */
void bifgfu(bifcxdef *ctx, int argc); /* getfuse */
void bifruf(bifcxdef *ctx, int argc); /* runfuses */
void bifrud(bifcxdef *ctx, int argc); /* rundaemons */
void biftim(bifcxdef *ctx, int argc); /* gettime */
void bifsct(bifcxdef *ctx, int argc); /* intersect */
void bifink(bifcxdef *ctx, int argc); /* inputkey */
void bifwrd(bifcxdef *ctx, int argc); /* objwords */
void bifadw(bifcxdef *ctx, int argc); /* addword */
void bifdlw(bifcxdef *ctx, int argc); /* delword */
void bifgtw(bifcxdef *ctx, int argc); /* getwords */
void bifnoc(bifcxdef *ctx, int argc); /* nocaps */
void bifskt(bifcxdef *ctx, int argc); /* skipturn */
void bifcls(bifcxdef *ctx, int argc); /* clearscreen */
void bif1sc(bifcxdef *ctx, int argc); /* firstsc */
void bifvin(bifcxdef *ctx, int argc); /* verbinfo */
void bifcapture(bifcxdef *ctx, int argc); /* outcapture */
void biffopen(bifcxdef *ctx, int argc); /* fopen */
void biffclose(bifcxdef *ctx, int argc); /* fclose */
void biffwrite(bifcxdef *ctx, int argc); /* fwrite */
void biffread(bifcxdef *ctx, int argc); /* fread */
void biffseek(bifcxdef *ctx, int argc); /* fseek */
void biffseekeof(bifcxdef *ctx, int argc); /* fseekeof */
void bifftell(bifcxdef *ctx, int argc); /* ftell */
void bifsysinfo(bifcxdef *ctx, int argc); /* systemInfo */
void bifmore(bifcxdef *ctx, int argc); /* morePrompt */
void bifsetme(bifcxdef *ctx, int argc); /* parserSetMe */
void bifgetme(bifcxdef *ctx, int argc); /* parserGetMe */
void bifresearch(bifcxdef *ctx, int argc); /* reSearch */
void bifregroup(bifcxdef *ctx, int argc); /* reGetGroup */
void bifinpevt(bifcxdef *ctx, int argc); /* inputevent */
void bifdelay(bifcxdef *ctx, int argc); /* timeDelay */
void bifsetoutfilter(bifcxdef *ctx, int argc); /* setOutputFilter */
void bifexec(bifcxdef *ctx, int argc); /* execCommand */
void bifgetobj(bifcxdef *ctx, int argc); /* parserGetObj */
void bifparsenl(bifcxdef *ctx, int argc); /* parserParseNounList */
void bifprstok(bifcxdef *ctx, int argc); /* parserTokenize */
void bifprstoktyp(bifcxdef *ctx, int argc); /* parserGetTokTypes */
void bifprsdict(bifcxdef *ctx, int argc); /* parserDictLookup */
void bifprsrslv(bifcxdef *ctx, int argc); /* parserResolveObjects */
void bifprsrplcmd(bifcxdef *ctx, int argc); /* parserReplaceCommand */
void bifexitobj(bifcxdef *ctx, int argc); /* exitobj */
void bifinpdlg(bifcxdef *ctx, int argc); /* inputdialog */
void bifresexists(bifcxdef *ctx, int argc); /* resourceExists */
/*
* TADS/graphic functions - these are present in the text system, but
* don't do anything.
*/
void bifgrp(bifcxdef *ctx, int argc); /* g_readpic: read picture */
void bifgsp(bifcxdef *ctx, int argc); /* g_showpic: show picture */
void bifgsh(bifcxdef *ctx, int argc); /* g_sethot: set hot list */
void bifgin(bifcxdef *ctx, int argc); /* g_inventory */
void bifgco(bifcxdef *ctx, int argc); /* g_compass */
void bifgov(bifcxdef *ctx, int argc); /* g_overlay */
void bifgmd(bifcxdef *ctx, int argc); /* g_mode */
void bifgmu(bifcxdef *ctx, int argc); /* g_music */
void bifgpa(bifcxdef *ctx, int argc); /* g_pause */
void bifgef(bifcxdef *ctx, int argc); /* g_effect */
void bifgsn(bifcxdef *ctx, int argc); /* g_sound */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,339 @@
/* 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/tads/tads2/character_map.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/os.h"
#include "glk/tads/tads2/text_io.h"
#include "glk/tads/os_frob_tads.h"
#include "glk/tads/os_glk.h"
#include "common/algorithm.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* ------------------------------------------------------------------------ */
/*
* Global variables for character mapping tables
*/
unsigned char G_cmap_input[256];
unsigned char G_cmap_output[256];
char G_cmap_id[5];
char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1];
/*
* static variables
*/
/*
* flag: true -> a character set has been explicitly loaded, so we
* should ignore any game character set setting
*/
static int S_cmap_loaded;
/* ------------------------------------------------------------------------ */
/*
* Initialize the default character mappings
*/
void cmap_init_default(void)
{
size_t i;
/* initialize the input table */
for (i = 0 ; i < sizeof(G_cmap_input)/sizeof(G_cmap_input[0]) ; ++i)
G_cmap_input[i] = (unsigned char)i;
/* initialize the output table */
for (i = 0 ; i < sizeof(G_cmap_output)/sizeof(G_cmap_output[0]) ; ++i)
G_cmap_output[i] = (unsigned char)i;
/* we have a null ID */
memset(G_cmap_id, 0, sizeof(G_cmap_id));
/* indicate that it's the default */
Common::strcpy_s(G_cmap_ldesc, "(native/no mapping)");
/* note that we have no character set loaded */
S_cmap_loaded = FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* Internal routine to load a character map from a file
*/
static int cmap_load_internal(char *filename)
{
osfildef *fp;
static char sig1[] = CMAP_SIG_S100;
char buf[256];
uchar lenbuf[2];
size_t len;
int sysblk;
/* if there's no mapping file, use the default mapping */
if (filename == nullptr)
{
/* initialize with the default mapping */
cmap_init_default();
/* return success */
return 0;
}
/* open the file */
fp = osfoprb(filename, OSFTCMAP);
if (fp == nullptr)
return 1;
/* check the signature */
if (osfrb(fp, buf, sizeof(sig1))
|| memcmp(buf, sig1, sizeof(sig1)) != 0)
{
osfcls(fp);
return 2;
}
/* load the ID */
G_cmap_id[4] = '\0';
if (osfrb(fp, G_cmap_id, 4))
{
osfcls(fp);
return 3;
}
/* load the long description */
if (osfrb(fp, lenbuf, 2)
|| (len = osrp2(lenbuf)) > sizeof(G_cmap_ldesc)
|| osfrb(fp, G_cmap_ldesc, len))
{
osfcls(fp);
return 4;
}
/* load the two tables - input, then output */
if (osfrb(fp, G_cmap_input, sizeof(G_cmap_input))
|| osfrb(fp, G_cmap_output, sizeof(G_cmap_output)))
{
osfcls(fp);
return 5;
}
/* read the next section header */
if (osfrb(fp, buf, 4))
{
osfcls(fp);
return 6;
}
/* if it's "SYSI", read the system information string */
if (!memcmp(buf, "SYSI", 4))
{
/* read the length prefix, then the string */
if (osfrb(fp, lenbuf, 2)
|| (len = osrp2(lenbuf)) > sizeof(buf)
|| osfrb(fp, buf, len))
{
osfcls(fp);
return 7;
}
/* we have a system information block */
sysblk = TRUE;
}
else
{
/* there's no system information block */
sysblk = FALSE;
}
/*
* call the OS code, so that it can do any system-dependent
* initialization for the new character mapping
*/
os_advise_load_charmap(G_cmap_id, G_cmap_ldesc, sysblk ? buf : "");
/* read the next section header */
if (sysblk && osfrb(fp, buf, 4))
{
osfcls(fp);
return 8;
}
/* see if we have an entity list */
if (!memcmp(buf, "ENTY", 4))
{
/* read the entities */
for (;;)
{
size_t blen;
unsigned int cval;
char expansion[CMAP_MAX_ENTITY_EXPANSION];
/* read the next item's length and character value */
if (osfrb(fp, buf, 4))
{
osfcls(fp);
return 9;
}
/* decode the values */
blen = osrp2(buf);
cval = osrp2(buf+2);
/* if we've reached the zero marker, we're done */
if (blen == 0 && cval == 0)
break;
/* read the string */
if (blen > CMAP_MAX_ENTITY_EXPANSION
|| osfrb(fp, expansion, blen))
{
osfcls(fp);
return 10;
}
/* tell the output code about the expansion */
tio_set_html_expansion(cval, expansion, blen);
}
}
/*
* ignore anything else we find - if the file format is updated to
* include extra information in the future, and this old code tries
* to load an updated file, we'll just ignore the new information,
* which should always be placed after the "SYSI" block (if present)
* to ensure compatibility with past versions (such as this code)
*/
/* no problems - close the file and return success */
osfcls(fp);
return 0;
}
/* ------------------------------------------------------------------------ */
/*
* Explicitly load a character set from a file. This character set
* mapping will override any implicit character set mapping that we read
* from a game file. This should be called when the player explicitly
* loads a character set (via a command line option or similar action).
*/
int cmap_load(char *filename)
{
int err;
/* try loading the file */
if ((err = cmap_load_internal(filename)) != 0)
return err;
/*
* note that we've explicitly loaded a character set, if they named
* a character set (if not, this simply establishes the default
* setting, so we haven't explicitly loaded anything)
*/
if (filename != nullptr)
S_cmap_loaded = TRUE;
/* success */
return 0;
}
/* ------------------------------------------------------------------------ */
/*
* Explicitly override any game character set and use no character set
* instead.
*/
void cmap_override(void)
{
/* apply the default mapping */
cmap_init_default();
/*
* pretend we have a character map loaded, so that we don't try to
* load another one if the game specifies a character set
*/
S_cmap_loaded = TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Set the game's internal character set. This is called when a game is
* loaded, and the game specifies a character set.
*/
void cmap_set_game_charset(errcxdef *ec,
char *internal_id, char *internal_ldesc,
char *argv0)
{
char filename[OSFNMAX];
/*
* If a character set is already explicitly loaded, ignore the
* game's character set - the player asked us to use a particular
* mapping, so ignore what the game wants. (This will probably
* result in incorrect display of non-ASCII character values, but
* the player is most likely to use this to avoid errors when an
* appropriate mapping file for the game is not available. In this
* case, the player informs us by setting the option that he or she
* knows and accepts that the game will not look exactly right.)
*/
if (S_cmap_loaded)
return;
/*
* ask the operating system to name the mapping file -- this routine
* will determine, if possible, the current native character set,
* and apply a system-specific naming convention to tell us what
* mapping file we should open
*/
os_gen_charmap_filename(filename, internal_id, argv0);
/* try loading the mapping file */
if (cmap_load_internal(filename))
errsig2(ec, ERR_CHRNOFILE,
ERRTSTR, errstr(ec, filename, strlen(filename)),
ERRTSTR, errstr(ec, internal_ldesc, strlen(internal_ldesc)));
/*
* We were successful - the game's internal character set is now
* mapped to the current native character set. Even though we
* loaded an ldesc from the mapping file, forget that and store the
* internal ldesc that the game specified. The reason we do this is
* that it's possible that the player will dynamically switch native
* character sets in the future, at which point we'll need to
* re-load the mapping table, which could raise an error if a
* mapping file for the new character set isn't available. So, we
* may need to provide the same explanation later that we needed to
* provide here. Save the game's character set ldesc for that
* eventuality, since it describes exactly what the *game* wanted.
*/
Common::strcpy_s(G_cmap_ldesc, internal_ldesc);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,130 @@
/* 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_TADS_CHARACTER_MAP
#define GLK_TADS_CHARACTER_MAP
namespace Glk {
namespace TADS {
namespace TADS2 {
struct errcxdef;
/* ------------------------------------------------------------------------ */
/*
* Initialize the default character mappings. If no mapping file is to
* be read, this function will establish identify mappings that leave
* characters untranslated.
*/
void cmap_init_default(void);
/*
* Load a character map file. Returns zero on success, non-zero on
* failure. If filename is null, we'll use the default mapping.
*/
int cmap_load(char *filename);
/*
* Turn off character translation. This overrides any game character
* set that we find and simply uses the default translation.
*/
void cmap_override(void);
/*
* Set the game's internal character set. This should be called when a
* game is loaded, and the game specifies an internal character set. If
* there is no character map file explicitly loaded, we will attempt to
* load a character mapping file that maps this character set to the
* current native character set. Signals an error on failure. This
* routine will succeed (without doing anything) if a character set has
* already been explicitly loaded, since an explicitly-loaded character
* set overrides the automatic character set selection that we attempt
* when loading a game.
*
* argv0 must be provided so that we know where to look for our mapping
* file on systems where mapping files are stored in the same directory
* as the TADS executables.
*/
void cmap_set_game_charset(struct errcxdef *errctx,
char *internal_id, char *internal_ldesc,
char *argv0);
/* ------------------------------------------------------------------------ */
/*
* Mapping macros
*/
/* map a native character (read externally) into an internal character */
#define cmap_n2i(c) (G_cmap_input[(unsigned char)(c)])
/* map an internal character into a native character (for display) */
#define cmap_i2n(c) (G_cmap_output[(unsigned char)(c)])
/* ------------------------------------------------------------------------ */
/*
* Global character mapping tables. The character map is established at
* start-up.
*/
/*
* input-mapping table - for native character 'n', cmap_input[n] yields
* the internal character code
*/
extern unsigned char G_cmap_input[256];
/*
* output-mapping table - for internal character 'n', cmap_output[n]
* yields the output character code
*/
extern unsigned char G_cmap_output[256];
/* the ID of the loaded character set */
extern char G_cmap_id[5];
/* the full name (for display purposes) of the loaded character set */
#define CMAP_LDESC_MAX_LEN 40
extern char G_cmap_ldesc[CMAP_LDESC_MAX_LEN + 1];
/*
* Maximum expansion for an HTML entity mapping
*/
#define CMAP_MAX_ENTITY_EXPANSION 50
/* ------------------------------------------------------------------------ */
/*
* Signatures for character map files. The signature is stored at the
* beginning of the file.
*/
/* single-byte character map version 1.0.0 */
#define CMAP_SIG_S100 "TADS2 charmap S100\n\r\01a"
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,86 @@
/* 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/tads/tads2/command_line.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* get a toggle argument */
int cmdtog(errcxdef *ec, int prv, char *argp, int ofs,
void (*usagefn)(errcxdef *))
{
switch(argp[ofs + 1])
{
case '+':
return(TRUE);
case '-':
return(FALSE);
case '\0':
return(!prv);
default:
/* invalid - display usage if we have a callback for it */
if (usagefn != nullptr)
(*usagefn)(ec);
NOTREACHEDV(int);
return 0;
}
}
/* get an argument to a switch */
char *cmdarg(errcxdef *ec, char ***argpp, int *ip, int argc, int ofs,
void (*usagefn)(errcxdef *))
{
char *ret;
/*
* check to see if the argument is appended directly to the option;
* if not, look at the next string
*/
ret = (**argpp) + ofs + 1;
if (*ret == '\0')
{
/*
* it's not part of this string - get the argument from the next
* string in the vector
*/
++(*ip);
++(*argpp);
ret = (*ip >= argc ? nullptr : **argpp);
}
/*
* if we didn't find the argument, it's an error - display usage if
* we have a valid usage callback
*/
if ((ret == nullptr || *ret == 0) && usagefn != nullptr)
(*usagefn)(ec);
return ret;
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,69 @@
/* 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/>.
*
*/
/* interface to command line option service routines
*/
#ifndef GLK_TADS_TADS2_COMMAND_LINE
#define GLK_TADS_TADS2_COMMAND_LINE
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Get argument to an option. Option can be rammed up against option
* letter(s) with no space, or can be separated by a space. argp is a
* pointer to the pointer to the current position in the argv[] array;
* ip is a pointer to the index in the argv[] array. Both *argpp and
* *ip are incremented if the next word must be read. argc is the total
* number of arguments. ofs gives the number of characters (NOT
* including the '-') in this option flag; most options will have ofs==1
* since they are of the form '-x'. usagefn is a function to call if
* the parsing fails; it is not expected to return, but should signal an
* error instead.
*/
char *cmdarg(errcxdef *ec, char ***argpp, int *ip, int argc,
int ofs, void (*usagefn)(errcxdef*));
/*
* Read a toggle argument. prv is the previous value (prior to this
* switch) of the parameter (TRUE or FALSE). argp is a pointer to the
* current argument word. ofs is the length of this option flag, NOT
* including the '-'; most options have ofs==1 since they are of the
* form '-x'. If the option is followed by '+', the value returned is
* TRUE; if it's followed by '-', the value is FALSE; if followed by
* nothing, the option is the logical inverse of the previous value. If
* it's followed by any other character, we call the usage callback,
* which is not expected to return, but should signal an error.
*/
int cmdtog(struct errcxdef *ec, int prv, char *argp, int ofs,
void (*usagefn)(errcxdef*));
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,73 @@
/* 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/tads/tads2/data.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/os_glk.h"
#include "common/algorithm.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* return size of a data value */
uint datsiz(dattyp typ, const void *val)
{
switch(typ)
{
case DAT_NUMBER:
return(4); /* numbers are in 4-byte lsb-first format */
case DAT_OBJECT:
return(2); /* object numbers are in 2-byte lsb-first format */
case DAT_SSTRING:
case DAT_DSTRING:
case DAT_LIST:
return(osrp2((const char *)val));
case DAT_NIL:
case DAT_TRUE:
return(0);
case DAT_PROPNUM:
case DAT_SYN:
case DAT_FNADDR:
case DAT_REDIR:
return(2);
case DAT_TPL:
/* template is counted array of 10-byte entries, plus length byte */
return(1 + ((*(const uchar *)val) * VOCTPLSIZ));
case DAT_TPL2:
return(1 + ((*(const uchar *)val) * VOCTPL2SIZ));
default:
return(0);
}
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,58 @@
/* 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_TADS_TADS2_DATA
#define GLK_TADS_TADS2_DATA
#include "common/scummsys.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
enum DataType {
DAT_NUMBER = 1,
DAT_OBJECT = 2,
DAT_SSTRING = 3,
DAT_BASEPTR = 4,
DAT_NIL = 5, ///< nil, as in FALSE or empty list
DAT_CODE = 6,
DAT_LIST = 7,
DAT_TRUE = 8, ///< inverse of nil
DAT_DSTRING = 9,
DAT_FNADDR = 10, ///< a function address
DAT_TPL = 11, ///< template list pointer
DAT_PROPNUM = 13, ///< a property number
DAT_DEMAND = 14, ///< special flag: use callback to set on use
DAT_SYN = 15, ///< synonym to indicated property value
DAT_REDIR = 16, ///< redirection to different object
DAT_TPL2 = 17 ///< new-style template
};
typedef int dattyp;
/* determine the size of a piece of data */
uint datsiz(dattyp typ, const void *valptr);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,522 @@
/* 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/tads/tads2/debug.h"
#include "glk/tads/tads2/list.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* add a string to the history buffer */
void dbgaddhist(dbgcxdef *ctx, char *buf, int l)
{
char *p;
int dell;
if (ctx->dbgcxhstf + l + 1 >= ctx->dbgcxhstl)
{
/* delete first lines until we have enough space */
for (dell = 0, p = ctx->dbgcxhstp ; *p || dell < l ; ++p, ++dell) ;
if (*p) ++p;
memmove(ctx->dbgcxhstp, ctx->dbgcxhstp + dell,
(size_t)(ctx->dbgcxhstf - dell));
ctx->dbgcxhstf -= dell;
}
memcpy(ctx->dbgcxhstp + ctx->dbgcxhstf, buf, (size_t)l);
ctx->dbgcxhstf += l;
}
/* callback for dbgent - saves history line to a char buffer */
static void dbgent1(void *ctx0, const char *str, int strl)
{
char **ctx = (char **)ctx0;
memcpy(*ctx, str, (size_t)strl);
*ctx += strl;
}
void dbgent(dbgcxdef *ctx, runsdef *bp, objnum self, objnum target,
prpnum prop, int binum, int argc)
{
dbgfdef *p;
++(ctx->dbgcxdep); /* increment actual depth */
if (ctx->dbgcxfcn == DBGMAXFRAME)
{
--(ctx->dbgcxfcn); /* back to top frame */
memmove(ctx->dbgcxfrm, ctx->dbgcxfrm + 1,
(size_t)((DBGMAXFRAME - 1) * sizeof(dbgfdef)));
}
p = &ctx->dbgcxfrm[ctx->dbgcxfcn];
++(ctx->dbgcxfcn); /* increment frame pointer */
p->dbgfbp = bp;
p->dbgfself = self;
p->dbgftarg = target;
p->dbgfprop = prop;
p->dbgfbif = binum;
p->dbgfargc = argc;
p->dbgffr = 0; /* no frame has yet been recorded */
p->dbgflin = 0;
/* save call history */
if (ctx->dbgcxflg & DBGCXFTRC)
{
char buf[128];
char *tmp;
int l;
tmp = buf;
dbgstktr(ctx, dbgent1, &tmp, -1, TRUE, FALSE);
if ((l = (tmp - buf)) > 0 && buf[l-1] == '\n') --l;
buf[l++] = '\0';
dbgaddhist(ctx, buf, l);
}
}
void dbglv(dbgcxdef *ctx, int exittype)
{
--(ctx->dbgcxdep); /* decrement actual depth */
if (ctx->dbgcxfcn) --(ctx->dbgcxfcn); /* decrement frame pointer */
/*
* if we're in STEP OUT/OVER mode, and the target context is level
* 0, and we're now at level 0, it means that we are stepping out of
* a routine called directly by the system and the debugger is
* supposed to break when that happens -- return to single-stepping
* mode so that we break into the debugger the next time the system
* calls a method
*/
if ((ctx->dbgcxflg & DBGCXFSS) != 0
&& (ctx->dbgcxflg & DBGCXFSO) != 0
&& ctx->dbgcxsof == 0 && ctx->dbgcxdep == 0)
{
/*
* stepping out/over at level 0 - go to normal single-step mode
* (clear the out/over flag)
*/
ctx->dbgcxflg &= ~DBGCXFSO;
}
/* record exit in call history if appropriate */
if (ctx->dbgcxflg & DBGCXFTRC)
{
char buf[128];
char *p;
switch(exittype)
{
case DBGEXVAL:
if (ctx->dbgcxfcn > 1)
{
memset(buf, ' ', (size_t)(ctx->dbgcxfcn - 1));
dbgaddhist(ctx, buf, (int)ctx->dbgcxfcn - 1);
}
memcpy(buf, " => ", (size_t)4);
p = buf + 4;
dbgpval(ctx, ctx->dbgcxrun->runcxsp - 1,
dbgent1, &p, TRUE);
*p++ = '\0';
dbgaddhist(ctx, buf, (int)(p - buf));
break;
case DBGEXPASS:
memcpy(buf, " [pass]", (size_t)8);
dbgaddhist(ctx, buf, 8);
break;
}
}
}
/* get a symbol name; returns length of name */
int dbgnam(dbgcxdef *ctx, char *outbuf, int typ, int val)
{
toksdef sym;
if (!ctx->dbgcxtab)
{
memcpy(outbuf, "<NO SYMBOL TABLE>", (size_t)17);
return(17);
}
if (tokthfind((toktdef *)ctx->dbgcxtab, typ, val, &sym))
{
memcpy(outbuf, sym.toksnam, (size_t)sym.tokslen);
return(sym.tokslen);
}
else if (typ == TOKSTOBJ)
{
if ((mcmon)val == MCMONINV)
{
memcpy(outbuf, "<invalid object>", 16);
return 16;
}
else
{
Common::sprintf_s(outbuf, TOKNAMMAX + 1, "<object#%u>", val);
return strlen(outbuf);
}
}
else
{
memcpy(outbuf, "<UNKNOWN>", (size_t)9);
return(9);
}
}
/* send a buffer value (as from a list) to ui callback for display */
static void dbgpbval(dbgcxdef *ctx, dattyp typ, const uchar *val,
void (*dispfn)(void *, const char *, int),
void *dispctx)
{
char buf[TOKNAMMAX + 1];
const char *p = buf;
uint len;
switch(typ)
{
case DAT_NUMBER:
Common::sprintf_s(buf, "%ld", (long)osrp4s(val));
len = strlen(buf);
break;
case DAT_OBJECT:
len = dbgnam(ctx, buf, TOKSTOBJ, osrp2(val));
break;
case DAT_SSTRING:
len = osrp2(val) - 2;
p = (const char *)val + 2;
break;
case DAT_NIL:
p = "nil";
len = 3;
break;
case DAT_LIST:
(*dispfn)(dispctx, "[", 1);
len = osrp2(val) - 2;
p = (const char *)val + 2;
while (len)
{
dbgpbval(ctx, (dattyp)*p, (const uchar *)(p + 1), dispfn, dispctx);
lstadv((uchar **)const_cast<char **>(&p), &len);
if (len) (*dispfn)(dispctx, " ", 1);
}
(*dispfn)(dispctx, "]", 1);
len = 0;
break;
case DAT_TRUE:
p = "true";
len = 4;
break;
case DAT_FNADDR:
(*dispfn)(dispctx, "&", 1);
len = dbgnam(ctx, buf, TOKSTFUNC, osrp2(val));
break;
case DAT_PROPNUM:
(*dispfn)(dispctx, "&", 1);
len = dbgnam(ctx, buf, TOKSTPROP, osrp2(val));
break;
default:
p = "[unknown type]";
len = 14;
break;
}
if (typ == DAT_SSTRING) (*dispfn)(dispctx, "'", 1);
if (len) (*dispfn)(dispctx, p, len);
if (typ == DAT_SSTRING) (*dispfn)(dispctx, "'", 1);
}
/* send a value to ui callback for display */
void dbgpval(dbgcxdef *ctx, runsdef *val,
void (*dispfn)(void *ctx, const char *str, int strl),
void *dispctx,
int showtype)
{
uchar buf[TOKNAMMAX + 1];
uint len;
const uchar *p = buf;
const char *typ = nullptr;
switch(val->runstyp)
{
case DAT_NUMBER:
Common::sprintf_s(buf, "%ld", val->runsv.runsvnum);
len = strlen((char *)buf);
typ = "number";
break;
case DAT_OBJECT:
len = dbgnam(ctx, (char *)buf, TOKSTOBJ, val->runsv.runsvobj);
typ = "object";
break;
case DAT_SSTRING:
len = osrp2(val->runsv.runsvstr) - 2;
p = val->runsv.runsvstr + 2;
typ = "string";
break;
case DAT_NIL:
p = (const uchar *)"nil";
len = 3;
break;
case DAT_LIST: {
if (showtype) (*dispfn)(dispctx, "list: ", 6);
(*dispfn)(dispctx, "[", 1);
len = osrp2(val->runsv.runsvstr) - 2;
uchar *up = val->runsv.runsvstr + 2;
while (len)
{
dbgpbval(ctx, (dattyp)*up, (const uchar *)(up + 1), dispfn, dispctx);
lstadv(&up, &len);
if (len) (*dispfn)(dispctx, " ", 1);
}
(*dispfn)(dispctx, "]", 1);
len = 0;
p = up;
break;
}
case DAT_TRUE:
p = (const uchar *)"true";
len = 4;
break;
case DAT_FNADDR:
len = dbgnam(ctx, (char *)buf, TOKSTFUNC, val->runsv.runsvobj);
typ = "function pointer";
break;
case DAT_PROPNUM:
len = dbgnam(ctx, (char *)buf, TOKSTPROP, val->runsv.runsvprp);
typ = "property pointer";
break;
default:
p = (const uchar *)"[unknown type]";
len = 14;
break;
}
/* show the type prefix if desired, or add a quote if it's a string */
if (typ && showtype)
{
/* show the type prefix */
(*dispfn)(dispctx, typ, (int)strlen(typ));
(*dispfn)(dispctx, ": ", 2);
}
else if (val->runstyp == DAT_SSTRING)
{
/* it's a string, and we're not showing a type - add a quote */
(*dispfn)(dispctx, "'", 1);
}
/*
* if possible, null-terminate the buffer - do this only if the
* length is actually within the buffer, which won't be the case if
* the text comes from someplace outside the buffer (which is the
* case if it's a string, for example)
*/
if (len < sizeof(buf))
buf[len] = '\0';
/* display a "&" prefix if it's an address of some kind */
if (val->runstyp == DAT_PROPNUM || val->runstyp == DAT_FNADDR)
(*dispfn)(dispctx, "&", 1);
/* display the text */
if (len != 0)
(*dispfn)(dispctx, (const char *)p, len);
/* add a closing quote if it's a string and we showed an open quote */
if (val->runstyp == DAT_SSTRING && !(typ && showtype))
(*dispfn)(dispctx, "'", 1);
}
void dbgstktr(dbgcxdef *ctx,
void (*dispfn)(void *ctx, const char *str, int strl),
void *dispctx,
int level, int toponly, int include_markers)
{
dbgfdef *f;
int i;
int j;
int k;
char buf[128];
char *p;
char c;
for (i = ctx->dbgcxfcn, j = ctx->dbgcxdep, f = &ctx->dbgcxfrm[i-1]
; i ; --f, --j, --i)
{
p = buf;
if (toponly)
{
int v = (i < 50 ? i : 50);
if (v > 1)
{
memset(buf, ' ', (size_t)(v - 1));
dbgaddhist(ctx, buf, v-1);
}
}
else if (include_markers)
{
c = (i == level + 1 ? '*' : ' ');
Common::sprintf_s(buf, "%3d%c ", j, c);
p += 4;
}
if (f->dbgftarg == MCMONINV)
p += dbgnam(ctx, p, TOKSTBIFN, f->dbgfbif);
else
p += dbgnam(ctx, p,
(f->dbgfself == MCMONINV ? TOKSTFUNC : TOKSTOBJ),
(int)f->dbgftarg);
if (f->dbgfself != MCMONINV && f->dbgfself != f->dbgftarg)
{
memcpy(p, "<self=", (size_t)6);
p += 6;
p += dbgnam(ctx, p, TOKSTOBJ, (int)f->dbgfself);
*p++ = '>';
}
if (f->dbgfprop)
{
*p++ = '.';
p += dbgnam(ctx, p, TOKSTPROP, (int)f->dbgfprop);
}
/* display what we have so far */
*p++ = '\0';
(*dispfn)(dispctx, buf, (int)strlen(buf));
/* display arguments if there are any */
if (f->dbgfself == MCMONINV || f->dbgfargc != 0)
{
(*dispfn)(dispctx, "(", 1);
for (k = 0 ; k < f->dbgfargc ; ++k)
{
dbgpval(ctx, f->dbgfbp - k - 2, dispfn, dispctx, FALSE);
if (k + 1 < f->dbgfargc) (*dispfn)(dispctx, ", ", 2);
}
(*dispfn)(dispctx, ")", 1);
}
/* send out a newline, then move on to next frame */
(*dispfn)(dispctx, "\n", 1);
/* we're done if doing one function only */
if (toponly) break;
}
}
static void dbgdsdisp(void *ctx, const char *buf, int bufl)
{
if (buf[0] == '\n')
tioflush((tiocxdef *)ctx);
else
tioputslen((tiocxdef *)ctx, (const char *)buf, bufl);
}
/* dump the stack */
void dbgds(dbgcxdef *ctx)
{
/* don't do stack dumps if we're running from the debugger command line */
if (ctx->dbgcxflg & DBGCXFIND) return;
tioflush(ctx->dbgcxtio);
tioshow(ctx->dbgcxtio);
dbgstktr(ctx, dbgdsdisp, ctx->dbgcxtio, -1, FALSE, TRUE);
tioflush(ctx->dbgcxtio);
ctx->dbgcxfcn = ctx->dbgcxdep = 0;
}
/* get information about the currently executing source line */
void dbglget(dbgcxdef *ctx, uchar *buf)
{
dbglgetlvl(ctx, buf, 0);
}
/*
* Get information about a source line at a particular stack level into
* the buffer; leaves out frame info. level 0 is the currently
* executing line, 1 is the first enclosing level, and so on.
*/
int dbglgetlvl(dbgcxdef *ctx, uchar *buf, int level)
{
uchar *linrec;
uchar *obj;
dbgfdef *fr;
/* make sure the level is valid */
if (level > ctx->dbgcxfcn - 1)
return 1;
/* get the frame at the given level */
fr = &ctx->dbgcxfrm[ctx->dbgcxfcn - level - 1];
/* if we're in an intrinsic, go to enclosing frame */
if (fr->dbgftarg == MCMONINV) --fr;
/* make sure we've encountered an OPCLINE in this frame */
if (fr->dbgflin == 0)
return 1;
/* we need to read from the target object - lock it */
obj = mcmlck(ctx->dbgcxmem, (mcmon)fr->dbgftarg);
linrec = obj + fr->dbgflin;
memcpy(buf, linrec + 3, (size_t)(*linrec - 3));
/* no longer need the target object locked */
mcmunlck(ctx->dbgcxmem, (mcmon)fr->dbgftarg);
/* success */
return 0;
}
/* tell the line source the location of the current line being compiled */
void dbgclin(tokcxdef *tokctx, objnum objn, uint ofs)
{
uchar buf[4];
/* package the information and send it to the line source */
oswp2(buf, objn);
oswp2(buf + 2, ofs);
lincmpinf(tokctx->tokcxlin, buf);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,589 @@
/* 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_TADS_TADS2_DEBUG
#define GLK_TADS_TADS2_DEBUG
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward declarations */
struct bifcxdef;
struct toksdef;
struct toktdef;
struct tokcxdef;
/* stack frame record */
struct dbgfdef
{
struct runsdef *dbgfbp; /* base pointer of frame */
objnum dbgfself; /* 'self' object (MCMONINV for functions) */
objnum dbgftarg; /* actual target object */
prpnum dbgfprop; /* property being evalutated */
int dbgfargc; /* number of arguments */
int dbgfbif; /* set to built-in function number if in built-in */
uint dbgffr; /* offset in object of local frame symbol table */
uint dbgflin; /* OPCLINE operand of latest line */
};
typedef struct dbgfdef dbgfdef;
/* max number of frames to store in debug frame memory */
#define DBGMAXFRAME 100
/* maximum number of breakpoints set concurrently */
#define DBGBPMAX 50
/* breakpoint structure */
struct dbgbpdef
{
objnum dbgbpself; /* the "self" object for the breakpoint */
objnum dbgbptarg; /* actual target object for the breakpoint */
uint dbgbpofs; /* offset in object of the breakpoint */
uint dbgbpflg; /* breakpoint flags */
#define DBGBPFUSED 0x01 /* breakpoint has been set */
#define DBGBPFNAME 0x02 /* name of address has been stored */
#define DBGBPFCOND 0x04 /* breakpoint has a condition attached */
#define DBGBPFDISA 0x08 /* breakpoint is disabled */
#define DBGBPFCONDNAME 0x10 /* condition name string has been stored */
uint dbgbpnam; /* offset of address name within dbgcxnam buffer */
uint dbgbpcondnam; /* offset of condition string within buffer */
objnum dbgbpcond; /* object containing compiled condition for bp */
};
typedef struct dbgbpdef dbgbpdef;
/* maximum number of watch expressions set concurrently */
#define DBGWXMAX 30
/* watch expression structure */
struct dbgwxdef
{
objnum dbgwxobj; /* object containing compiled expression */
objnum dbgwxself; /* 'self' for the expression */
uint dbgwxnam; /* offset of expression text within dbgcxnam buffer */
uint dbgwxflg; /* flags for this watch expression slot */
#define DBGWXFUSED 0x01 /* watch slot is in use */
#define DBGWXFNAME 0x02 /* name of watch has been stored */
};
typedef struct dbgwxdef dbgwxdef;
/* amount of space for bp names (original address strings from user) */
#define DBGCXNAMSIZ 2048
/* debug context */
struct dbgcxdef
{
struct tiocxdef *dbgcxtio; /* text i/o context */
struct tokthdef *dbgcxtab; /* symbol table */
struct mcmcxdef *dbgcxmem; /* memory cache manager context */
struct errcxdef *dbgcxerr; /* error handling context */
struct lindef *dbgcxlin; /* chain of line sources */
int dbgcxfcn; /* number of frames in use */
int dbgcxdep; /* actual depth (if overflow frame buffer) */
int dbgcxfid; /* source file serial number */
dbgfdef dbgcxfrm[DBGMAXFRAME]; /* stack frames */
int dbgcxflg; /* flags for debug session */
#define DBGCXFSS 0x01 /* single-stepping source lines */
#define DBGCXFSO 0x02 /* stepping over a function/method call */
#define DBGCXFOK 0x04 /* debugger is linked in */
#define DBGCXFIND 0x08 /* in debugger - suppress stack trace on err */
#define DBGCXFGBP 0x10 /* global breakpoints in effect */
#define DBGCXFTRC 0x20 /* call tracing activated */
#define DBGCXFLIN2 0x40 /* new-style line records (line numbers) */
int dbgcxsof; /* frame depth at step-over time */
dbgbpdef dbgcxbp[DBGBPMAX]; /* breakpoints */
dbgwxdef dbgcxwx[DBGWXMAX]; /* watch expressions */
struct prscxdef *dbgcxprs; /* parsing context */
struct runcxdef *dbgcxrun; /* execution context */
uint dbgcxnamf; /* next free byte of dbgcxnam buffer */
uint dbgcxnams; /* size of dbgcxnam buffer */
char *dbgcxnam; /* space for bp address names */
char *dbgcxhstp; /* call history buffer */
uint dbgcxhstl; /* history buffer length */
uint dbgcxhstf; /* offset of next free byte of history */
/*
* This member is for the use of the user interface code. If the
* user interface implementation needs to store additional context,
* it can allocate a structure of its own (it should probably do
* this in dbguini()) and store a pointer to that structure here.
* Since the user interface entrypoints always have the debugger
* context passed as a parameter, the user interface code can
* recover its extra context information by following this pointer
* and casting it to its private structure type. The TADS code
* won't do anything with this pointer except initialize it to null
* when initializing the debugger context.
*/
void *dbgcxui;
};
typedef struct dbgcxdef dbgcxdef;
/* ======================================================================== */
/*
* Compiler interface. These routines are called by the compiler to
* inform the debug record generator about important events as
* compilation proceeds.
*/
/*
* Tell the current line source that we're compiling an executable
* line, and tell it the object number and offset of the code within the
* object.
*/
void dbgclin(struct tokcxdef *tokctx, objnum objn, uint ofs);
/* size of information given to line source via lincmpinf method */
#define DBGLINFSIZ 4
/* ======================================================================== */
/*
* Run-time interface. These routines are called by the run-time
* system to apprise the debugger of important events during execution.
*/
/*
* Determine if the debugger is present. Returns true if so, false if
* not. This should return false for any stand-alone version of the
* executable that isn't linked with the debugger. If this returns
* true, dbgucmd() must not have a trivial implementation -- dbgucmd()
* must at least let the user quit out of the game.
*
* This can be switched at either link time or compile time. If DBG_OFF
* is defined, we'll force this to return false; otherwise, we'll let
* the program define the appropriate implementation through the linker.
*/
#ifdef DBG_OFF
#define dbgpresent() (false)
#else
int dbgpresent();
#endif
/* add a debug tracing record */
/* void dbgenter(dbgcxdef *ctx, runsdef *bp, objnum self, objnum target,
prpnum prop, int binum, int argc); */
/* tell debugger where the current line's local frame table is located */
/* void dbgframe(dbgcxdef *ctx, uint ofsfr, ofslin); */
/*
* Single-step interrupt: the run-time has reached a new source line.
* ofs is the offset from the start of the object of the line record,
* and p is the current execution pointer. *p can be changed upon
* return, in which case the run-time will continue from the new
* position; however, the new address must be within the same function
* or method as it was originally.
*/
/* void dbgssi(dbgcxdef *ctx, uint ofs, int instr,
int err, uchar *noreg *p); */
/* pop debug trace level */
/* void dbgleave(dbgcxdef *ctx, int exittype); */
#define DBGEXRET 0 /* return with no value */
#define DBGEXVAL 1 /* return with a value */
#define DBGEXPASS 2 /* use 'pass' to exit a function */
/* dump the stack into text output */
/* void dbgdump(dbgcxdef *ctx); */
/* reset debug stack (throw away entire contents) */
/* void dbgrst(dbgcxdef *ctx); */
/* activate debugger if possible; returns TRUE if no debugger is present */
int dbgstart(dbgcxdef *ctx);
/* add a string to the history buffer */
void dbgaddhist(dbgcxdef *ctx, char *buf, int bufl);
/*
* Find a base pointer, given the object+offset of the frame. If the
* frame is not active, this routine signals ERR_INACTFR; otherwise, the
* bp value for the frame is returned.
*/
struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs);
/* ======================================================================== */
/*
* User Interface Support routines. These routines are called by the
* user interface layer to get information from the debugger and perform
* debugging operations.
*/
/* get a symbol name; returns length of name */
int dbgnam(dbgcxdef *ctx, char *outbuf, int typ, int val);
/*
* Get information about current line. It is assumed that the caller
* knows the size of the line information .
*/
void dbglget(dbgcxdef *ctx, uchar *buf);
/*
* Get information about a line in an enclosing stack frame. Level 0 is
* the current line, level 1 is the first enclosing frame, and so on.
* Returns 0 on success, non-zero if the frame level is invalid.
*/
int dbglgetlvl(dbgcxdef *ctx, uchar *buf, int level);
/*
* Set a breakpoint by symbolic address: "function" or
* "object.property". The string may contain whitespace characters
* around each symbol; it must be null-terminated. If an error occurs,
* the error number is returned. bpnum returns with the breakpoint
* number if err == 0. If the condition string is given (and is not an
* empty string), the condition is compiled in the scope of the
* breakpoint and attached as the breakpoint condition.
*/
int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum);
/*
* Set a breakpoint at an object + offset location. If 'toggle' is
* true, and there's already a breakpoint at the given location, we'll
* clear the breakpoint; in this case, *did_set will return false to
* indicate that an existing breakpoint was cleared rather than a new
* breakpoint created. *did_set will return true if a new breakpoint
* was set.
*/
int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
uint ofs, int *bpnum, char *bpname, int toggle,
char *condition, int *did_set);
/*
* Set a breakpoint at an object + offset location, optionally with a
* condition, using an existing breakpoint slot. If the slot is already
* in use, we'll return an error.
*/
int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
uint ofs, char *bpname, int toggle, char *cond,
int *did_set);
/*
* Determine if there's a breakpoint at a given code location. Fills in
* *bpnum with the breakpoint identifier and returns true if a
* breakpoint is found at the given location; returns false if there are
* no breakpoints matching the description.
*/
int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum);
/*
* Determine if the given breakpoint is enabled
*/
int dbgisbpena(dbgcxdef *ctx, int bpnum);
/*
* Delete a breakpoint by breakpoint number (as returned from
* dbgbpset). Returns error number, or 0 for success.
*/
int dbgbpdel(dbgcxdef *ctx, int bpnum);
/* disable or enable a breakpoint, by breakpoint number; returns error num */
int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable);
/*
* Set a new condition for the given breakpoint. Replaces any existing
* condition. If an error occurs, we'll leave the old condition as it
* was and return a non-zero error code; on success, we'll update the
* condition and return zero.
*/
int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond);
/* list breakpoints, using user callback to do display */
void dbgbplist(dbgcxdef *ctx,
void (*dispfn)(void *ctx, const char *str, int len),
void *dispctx);
/* enumerate breakpoints */
void dbgbpenum(dbgcxdef *ctx,
void (*cbfunc)(void *cbctx, int bpnum, const char *desc,
const char *cond, int disabled), void *cbctx);
/* call callback with lindef data for each breakpoint currently set */
void dbgbpeach(dbgcxdef *ctx,
void (*fn)(void *, int, uchar *, uint),
void *fnctx);
/*
* Get information on a specific breakpoint. Returns zero on success,
* non-zero on failure.
*/
int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
char *condbuf, size_t condbuflen);
/*
* Evaluate an expression (a text string to be parsed) at a particular
* stack context level; returns error number. Invokes the callback
* function repeatedly to display the value string, and ends the display
* with a newline. If showtype is true, we'll include a type name
* prefix, otherwise we'll simply display the value.
*/
int dbgeval(dbgcxdef *ctx, char *expr,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype);
/*
* Evaluate an expression, extended version. For aggregate values
* (objects, lists), we'll invoke a callback function for each value
* contained by the aggregate value, passing the callback the name and
* relationship of the subitem. The relationship is simply the operator
* that should be used to join the parent expression and the subitem
* name to form the full subitem expression; for objects, it's ".", and
* for lists it's null (because for lists the subitem names will include
* brackets). 'speculative' is passed to dbgcompile; see the comments
* there for information on the purpose of this flag.
*/
int dbgevalext(dbgcxdef *ctx, char *expr,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype, dattyp *dat,
void (*aggcb)(void *aggctx, const char *subname,
int subnamelen, const char *relationship),
void *aggctx, int speculative);
/*
* enumerate local variables at a given stack context level by calling
* the given function once for each local variable
*/
void dbgenumlcl(dbgcxdef *ctx, int level,
void (*func)(void *ctx, const char *lclnam, size_t lclnamlen),
void *cbctx);
/*
* Compile an expression in a given frame context. Returns an error
* number. Allocates a new object to contain the compiled code, and
* returns the object number in *objn; the caller is responsible for
* freeing the object when done with it.
*
* If 'speculative' is set to true, we'll prohibit the expression from
* making any assignments or calling any methods or functions. This
* mode can be used to try compiling an expression that the user could
* conceivably be interested in but has not expressly evaluated; for
* example, this can be used to implement "tooltip evaluation," where
* the debugger automatically shows a little pop-up window with the
* expression under the mouse cursor if the mouse cursor is left
* hovering over some text for a few moments. In such cases, since the
* user hasn't explicitly requested evaluation, it would be bad to make
* any changes to game state, hence the prohibition of assignments or
* calls.
*/
int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
int speculative);
/* display a stack traceback through a user callback */
void dbgstktr(dbgcxdef *ctx,
void (*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int toponly, int include_markers);
/* format a display of where execution is stopped into a buffer */
void dbgwhere(dbgcxdef *ctx, char *buf);
/* set a watch expression; returns error or 0 for success */
int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level);
/* delete a watch expression */
int dbgwxdel(dbgcxdef *ctx, int wxnum);
/* update all watch expressions */
void dbgwxupd(dbgcxdef *ctx,
void (*dispfn)(void *dispctx, const char *txt, int len),
void *dispctx);
/* switch to a new active lindef */
void dbgswitch(struct lindef **linp, struct lindef *newlin);
/* ======================================================================== */
/*
* User Interface Routines. The routines are called by the debugger
* to perform user interaction.
*/
/*
* Debugger user interface initialization, phase one. TADS calls this
* routine during startup, before reading the .GAM file, to let the user
* interface perform any initialization it requires before the .GAM file
* is loaded.
*/
void dbguini(dbgcxdef *ctx, const char *game_filename);
/*
* Debugger user interface initialization, phase two. TADS calls this
* routine during startup, after read the .GAM file. The debugger user
* interface code can perform any required initialization that depends
* on the .GAM file having been read.
*/
void dbguini2(dbgcxdef *ctx);
/*
* Determine if the debugger can resume from a run-time error. This
* reflects the capabilities of the user interface of the debugger. In
* particular, if the UI provides a way to change the instruction
* pointer, then the debugger can resume from an error, since the user
* can always move past the run-time error and continue execution. If
* the UI doesn't let the user change the instruction pointer, resuming
* from an error won't work, since the program will keep hitting the
* same error and re-entering the debugger. If this returns false, the
* run-time will trap to the debugger on an error, but will simply abort
* the current command when the debugger returns. If this returns true,
* the run-time will trap to the debugger on an error with the
* instruction pointer set back to the start of the line containing the
* error, and will thus re-try the same line of code when the debugger
* returns, unless the debugger explicitly moves the instruction pointer
* before returning.
*/
int dbgu_err_resume(dbgcxdef *ctx);
/*
* Find a source file. origname is the name of the source file as it
* appears in the game's debugging information; this routine should
* figure out where the file actually is, and put the fully-qualified
* path to the file in fullname. The debugger calls this after it
* exhausts all of its other methods of finding a source file (such as
* searching the include path).
*
* Return true if the source file should be considered valid, false if
* not. Most implementations will simply return true if the file was
* found, false if not; however, this approach will cause the debugger
* to terminate with an error at start-up if the user hasn't set up the
* debugger's include path correctly before running the debugger. Some
* implementations, in particular GUI implementations, may wish to wait
* to find a file until the file is actually needed, rather than pester
* the user with file search dialogs repeatedly at start-up.
*
* must_find_file specifies how to respond if we can't find the file.
* If must_find_file is true, we should always return false if we can't
* find the file. If must_find_file is false, however, we can
* optionally return true even if we can't find the file. Doing so
* indicates that the debugger UI will defer locating the file until it
* is actually needed.
*
* If this routine returns true without actually finding the file, it
* should set fullname[0] to '\0' to indicate that fullname doesn't
* contain a valid filename.
*/
int dbgu_find_src(const char *origname, int origlen,
char *fullname, size_t full_len, int must_find_file);
/*
* Debugger user interface main command loop. If err is non-zero, the
* debugger was entered because a run-time error occurred; otherwise, if
* bphit is non-zero, it's the number of the breakpoint that was
* encountered; otherwise, the debugger was entered through a
* single-step of some kind. exec_ofs is the byte offset within the
* target object of the next instruction to be executed. This can be
* changed upon return, in which case execution will continue from the
* new offset, but the offset must be within the same method of the same
* object (or within the same function) as it was upon entry.
*/
void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs);
/*
* Debugger UI - quitting game. The runtime calls this routine just
* before the play loop is about to terminate after the game code has
* called the "quit" built-in function. If the debugger wants, it can
* take control here (just as with dbgucmd()) for as long as it wants.
* If the debugger wants to restart the game, it should call bifrst().
* If this routine returns without signalling a RUN_RESTART error, TADS
* will terminate. If a RUN_RESTART error is signalled, TADS will
* resume the play loop.
*/
void dbguquitting(dbgcxdef *ctx);
/*
* debugger user interface termination - this routine is called when the
* debugger is about to terminate, so that the user interface can close
* itself down (close windows, release memory, etc)
*/
void dbguterm(dbgcxdef *ctx);
/*
* Debugger user interface: display an error. This is called mainly so
* that the debugger can display an error using special output
* formatting if the error occurs while debugging.
*/
void dbguerr(dbgcxdef *ctx, int errnum, char *msg);
/* turn hidden output tracing on/off */
void trchid(void);
void trcsho(void);
/* ======================================================================== */
/*
* optional debugger macros - these compile to nothing when compiling a
* version for use without the debugger
*/
#ifdef DBG_OFF
#define dbgenter(ctx, bp, self, target, prop, binum, argc)
#define dbgleave(ctx, exittype) ((void)0)
#define dbgdump(ctx) ((void)0)
#define dbgrst(ctx) ((void)0)
#define dbgframe(ctx, frofs, linofs)
#define dbgssi(ctx, ofs, instr, err, p) ((void)0)
#else /* DBG_OFF */
#define dbgenter(ctx, bp, self, target, prop, binum, argc) \
dbgent(ctx, bp, self, target, prop, binum, argc)
#define dbgleave(ctx, exittype) dbglv(ctx, exittype)
#define dbgdump(ctx) dbgds(ctx)
#define dbgrst(ctx) ((ctx)->dbgcxfcn = (ctx)->dbgcxdep = 0)
#define dbgframe(ctx, frofs, linofs) \
(((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgffr = (frofs)), \
((ctx)->dbgcxfrm[(ctx)->dbgcxfcn - 1].dbgflin = (linofs)))
#define dbgssi(ctx, ofs, instr, err, p) dbgss(ctx, ofs, instr, err, p)
#endif /* DBG_OFF */
/* ======================================================================== */
/* private internal routines */
void dbgent(dbgcxdef *ctx, struct runsdef *bp, objnum self, objnum target,
prpnum prop, int binum, int argc);
void dbglv(dbgcxdef *ctx, int exittype);
void dbgds(dbgcxdef *ctx);
void dbgss(dbgcxdef *ctx, uint ofs, int instr, int err, uchar *noreg *p);
void dbgpval(struct dbgcxdef *ctx, struct runsdef *val,
void (*dispfn)(void *, const char *, int),
void *dispctx, int showtype);
int dbgtabsea(struct toktdef *tab, char *name, int namel, int hash,
struct toksdef *ret);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,185 @@
/* 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/tads/tads2/error.h"
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/tads2/tokenizer.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* format an error message, sprintf-style, using an erradef array */
int errfmt(char *outbuf, int outbufl, const char *fmt, int argc, const erradef *argv) {
int outlen = 0;
int argi = 0;
int len = 0;
char buf[20];
const char *p = nullptr;
char fmtchar;
while (*fmt)
{
switch(*fmt)
{
case '\\':
++fmt;
len = 1;
switch(*fmt)
{
case '\0':
--fmt;
break;
case '\n':
p = "\n";
break;
case '\t':
p = "\t";
break;
default:
p = fmt;
break;
}
break;
case '%':
++fmt;
fmtchar = *fmt;
if (argi >= argc) fmtchar = 1; /* too many - ignore it */
switch(fmtchar)
{
case '\0':
--fmt;
break;
case '%':
p = "%";
len = 1;
break;
case 'd':
Common::sprintf_s(buf, "%d", argv[argi].erraint);
len = strlen(buf);
p = buf;
break;
case 's':
p = argv[argi].errastr;
len = strlen(p);
break;
case 't':
{
int i;
static struct
{
int tokid;
const char *toknam;
} toklist[] =
{
{ TOKTSEM, "semicolon" },
{ TOKTCOLON, "colon" },
{ TOKTFUNCTION, "\"function\"" },
{ TOKTCOMMA, "comma" },
{ TOKTLBRACE, "left brace ('{')" },
{ TOKTRPAR, "right paren (')')" },
{ TOKTRBRACK, "right square bracket (']')" },
{ TOKTWHILE, "\"while\"" },
{ TOKTLPAR, "left paren ('(')" },
{ TOKTEQ, "'='" },
{ 0, (const char *)nullptr }
};
for (i = 0 ; toklist[i].toknam ; ++i)
{
if (toklist[i].tokid == argv[argi].erraint)
{
p = toklist[i].toknam;
break;
}
}
if (!toklist[i].toknam)
p = "<unknown token>";
len = strlen(p);
break;
}
default:
p = "";
len = 0;
--argi;
break;
}
++argi;
break;
default:
p = fmt;
len = 1;
break;
}
/* copy output that was set up above */
if (len)
{
if (outbufl >= len)
{
memcpy(outbuf, p, (size_t)len);
outbufl -= len;
outbuf += len;
}
else if (outbufl)
{
memcpy(outbuf, p, (size_t)outbufl);
outbufl = 0;
}
outlen += len;
}
++fmt;
}
if (outbufl) *outbuf++ = '\0';
return(outlen);
}
#ifdef ERR_NO_MACRO
/* base error signal function */
void errsign(errcxdef *ctx, int e) {
ctx->errcxofs = 0;
#if 0
longjmp(ctx->errcxptr->errbuf, e);
#else
error("errsign");
#endif
}
/* log an error: base function */
void errlogn(errcxdef *ctx, int err) {
error("errlogn");
}
#endif /* ERR_NO_MACRO */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,389 @@
/* 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/>.
*
*/
/* Error handling
* This package defines a set of macros that allows code to raise and
* handle exceptions.A macro is provided which signals an error, which
* does a non - local goto to the innermost enclosing exception handler.
* A set of macros sets up exception handling code.
*
* To catch exceptions that occur inside a block of code(i.e., in the
* code or in any subroutines called by the code), begin the block with
* ERRBEGIN.At the end of the protected code, place the exception
* handler, which starts with ERRCATCH.At the end of the exception
* handler, place ERREND.If no exception occurs, execution goes
* through the protected code, then resumes at the code following
* the ERREND.
*
* The exception handler can signal another error, which will cause
* the next enclosing frame to catch the error.Alternatively, if
* the exception handler doesn't signal an error or return, execution
* continues at the code following the ERREND.Exceptions that are
* signalled during exception handling will be caught by the next
* enclosing frame, unless the exception handler code is itself
* protected by another ERRBEGIN - ERREND block.
*
* To signal an error, use errsig().
*
* To use a string argument in a signalled error, cover the string
* with errstr(ctx, str, len); for example:
*
* errsig1(ctx, ERR_XYZ, ERRTSTR, errstr(ctx, buf, strlen(buf)));
*
* This copies the string into a buffer that is unaffected by
* stack resetting during error signalling.
*/
#ifndef GLK_TADS_TADS2_ERROR
#define GLK_TADS_TADS2_ERROR
#include "glk/tads/tads2/lib.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* for compatility with old facility-free mechanism, signal with
* facility "TADS"
*/
#define errsig(ctx, err) errsigf(ctx, "TADS", err)
#define errsig1(c, e, t, a) errsigf1(c,"TADS",e,t,a)
#define errsig2(c, e, t1, a1, t2, a2) errsigf2(c,"TADS",e,t1,a1,t2,a2)
#define errlog(c, e) errlogf(c, "TADS", e)
#define errlog1(c, e, t, a) errlogf1(c,"TADS",e,t,a)
#define errlog2(c, e, t1, a1, t2, a2) errlogf2(c,"TADS",e,t1,a1,t2,a2)
/*---------------------------- TADS Error Codes ----------------------------*/
/* memory/cache manager errors */
#define ERR_NOMEM 1 /* out of memory */
#define ERR_FSEEK 2 /* error seeking in file */
#define ERR_FREAD 3 /* error reading from file */
#define ERR_NOPAGE 4 /* no more page slots */
#define ERR_REALCK 5 /* attempting to reallocate a locked object */
#define ERR_SWAPBIG 6 /* swapfile limit reached - out of virtual memory */
#define ERR_FWRITE 7 /* error writing file */
#define ERR_SWAPPG 8 /* exceeded swap page table limit */
#define ERR_CLIUSE 9 /* requested client object number already in use */
#define ERR_CLIFULL 10 /* client mapping table is full */
#define ERR_NOMEM1 11 /* swapping/garbage collection failed to find enuf */
#define ERR_NOMEM2 12 /* no memory to resize (expand) an object */
#define ERR_OPSWAP 13 /* unable to open swap file */
#define ERR_NOHDR 14 /* can't get a new object header */
#define ERR_NOLOAD 15 /* mcm cannot find object to load (internal error) */
#define ERR_LCKFRE 16 /* attempting to free a locked object (internal) */
#define ERR_INVOBJ 17 /* invalid object */
#define ERR_BIGOBJ 18 /* object too big - exceeds memory allocation limit */
/* lexical analysis errors */
#define ERR_INVTOK 100 /* invalid token */
#define ERR_STREOF 101 /* end of file while scanning string */
#define ERR_TRUNC 102 /* symbol too long - truncated */
#define ERR_NOLCLSY 103 /* no space in local symbol table */
#define ERR_PRPDIR 104 /* invalid preprocessor (#) directive */
#define ERR_INCNOFN 105 /* no filename in #include directive */
#define ERR_INCSYN 106 /* invalid #include syntax */
#define ERR_INCSEAR 107 /* can't find included file */
#define ERR_INCMTCH 108 /* no matching delimiter in #include filename */
#define ERR_MANYSYM 109 /* out of space for symbol table */
#define ERR_LONGLIN 110 /* line too long */
#define ERR_INCRPT 111 /* header file already included - ignored */
#define ERR_PRAGMA 112 /* unknown pragma (ignored) */
#define ERR_BADPELSE 113 /* unexpected #else */
#define ERR_BADENDIF 114 /* unexpected #endif */
#define ERR_BADELIF 115 /* unexpected #elif */
#define ERR_MANYPIF 116 /* #if nesting too deep */
#define ERR_DEFREDEF 117 /* #define symbol already defined */
#define ERR_PUNDEF 118 /* #undef symbol not defined */
#define ERR_NOENDIF 119 /* missing #endif */
#define ERR_MACNEST 120 /* macros nested too deeply */
#define ERR_BADISDEF 121 /* invalid argument for defined() operator */
#define ERR_PIF_NA 122 /* #if is not implemented */
#define ERR_PELIF_NA 123 /* #elif is not implemented */
#define ERR_P_ERROR 124 /* error directive: %s */
#define ERR_LONG_FILE_MACRO 125 /* __FILE__ expansion too long */
#define ERR_LONG_LINE_MACRO 126 /* __LINE__ expansion too long */
/* undo errors */
#define ERR_UNDOVF 200 /* operation is too big for undo log */
#define ERR_NOUNDO 201 /* no more undo information */
#define ERR_ICUNDO 202 /* incomplete undo (no previous savepoint) */
/* parser errors */
#define ERR_REQTOK 300 /* expected token (arg 1) - found something else */
#define ERR_REQSYM 301 /* expected symbol */
#define ERR_REQPRP 302 /* expected a property name */
#define ERR_REQOPN 303 /* expected operand */
#define ERR_REQARG 304 /* expected comma or closing paren (arg list) */
#define ERR_NONODE 305 /* no space for new parse node */
#define ERR_REQOBJ 306 /* epxected object name */
#define ERR_REQEXT 307 /* redefining symbol as external function */
#define ERR_REQFCN 308 /* redefining symbol as function */
#define ERR_NOCLASS 309 /* can't use CLASS with function/external function */
#define ERR_REQUNO 310 /* required unary operator */
#define ERR_REQBIN 311 /* required binary operator */
#define ERR_INVBIN 312 /* invalid binary operator */
#define ERR_INVASI 313 /* invalid assignment */
#define ERR_REQVAR 314 /* required variable name */
#define ERR_LCLSYN 315 /* required comma or semicolon in local list */
#define ERR_REQRBR 316 /* required right brace (eof before end of group) */
#define ERR_BADBRK 317 /* 'break' without 'while' */
#define ERR_BADCNT 318 /* 'continue' without 'while' */
#define ERR_BADELS 319 /* 'else' without 'if' */
#define ERR_WEQASI 320 /* warning: possible use of '=' where ':=' intended */
#define ERR_EOF 321 /* unexpected end of file */
#define ERR_SYNTAX 322 /* general syntax error */
#define ERR_INVOP 323 /* invalid operand type */
#define ERR_NOMEMLC 324 /* no memory for new local symbol table */
#define ERR_NOMEMAR 325 /* no memory for argument symbol table */
#define ERR_FREDEF 326 /* redefining a function which is already defined */
#define ERR_NOSW 327 /* 'case' or 'default' and not in switch block */
#define ERR_SWRQCN 328 /* constant required in switch case value */
#define ERR_REQLBL 329 /* label required for 'goto' */
#define ERR_NOGOTO 330 /* 'goto' label never defined */
#define ERR_MANYSC 331 /* too many superclasses for object */
#define ERR_OREDEF 332 /* redefining symbol as object */
#define ERR_PREDEF 333 /* property being redefined in object */
#define ERR_BADPVL 334 /* invalid property value */
#define ERR_BADVOC 335 /* bad vocabulary property value */
#define ERR_BADTPL 336 /* bad template property value (need sstring) */
#define ERR_LONGTPL 337 /* template base property name too long */
#define ERR_MANYTPL 338 /* too many templates (internal compiler limit) */
#define ERR_BADCMPD 339 /* bad value for compound word (sstring required) */
#define ERR_BADFMT 340 /* bad value for format string (sstring needed) */
#define ERR_BADSYN 341 /* invalid value for synonym (sstring required) */
#define ERR_UNDFSYM 342 /* undefined symbol */
#define ERR_BADSPEC 343 /* bad special word */
#define ERR_NOSELF 344 /* "self" not valid in this context */
#define ERR_STREND 345 /* warning: possible unterminated string */
#define ERR_MODRPLX 346 /* modify/replace not allowed with external func */
#define ERR_MODFCN 347 /* modify not allowed with function */
#define ERR_MODFWD 348 /* modify/replace not allowed with forward func */
#define ERR_MODOBJ 349 /* modify can only be used with a defined object */
#define ERR_RPLSPEC 350 /* warning - replacing specialWords */
#define ERR_SPECNIL 351 /* nil only allowed with modify specialWords */
#define ERR_BADLCL 353 /* 'local' statement must precede executable code */
#define ERR_IMPPROP 354 /* implied verifier '%s' is not a property */
#define ERR_BADTPLF 355 /* invalid command template flag */
#define ERR_NOTPLFLG 356 /* flags are not allowed with old file format */
#define ERR_AMBIGBIN 357 /* warning: operator '%s' could be binary */
#define ERR_PIA 358 /* warning: possibly incorrect assignment */
#define ERR_BADSPECEXPR 359 /* invalid speculation evaluation */
/* code generation errors */
#define ERR_OBJOVF 400 /* object cannot grow any bigger - code too big */
#define ERR_NOLBL 401 /* no more temporary labels/fixups */
#define ERR_LBNOSET 402 /* (internal error) label never set */
#define ERR_INVLSTE 403 /* invalid datatype for list element */
#define ERR_MANYDBG 404 /* too many debugger line records (internal limit) */
/* vocabulary setup errors */
#define ERR_VOCINUS 450 /* vocabulary being redefined for object */
#define ERR_VOCMNPG 451 /* too many vocwdef pages (internal limit) */
#define ERR_VOCREVB 452 /* redefining same verb */
#define ERR_VOCREVB2 453 /* redefining same verb - two arguments */
/* set-up errors */
#define ERR_LOCNOBJ 500 /* location of object %s is not an object */
#define ERR_CNTNLST 501 /* contents of object %s is not list */
#define ERR_SUPOVF 502 /* overflow trying to build contents list */
#define ERR_RQOBJNF 503 /* required object %s not found */
#define ERR_WRNONF 504 /* warning - object %s not found */
#define ERR_MANYBIF 505 /* too many built-in functions (internal error) */
/* fio errors */
#define ERR_OPWGAM 600 /* unable to open game for writing */
#define ERR_WRTGAM 601 /* error writing to game file */
#define ERR_FIOMSC 602 /* too many sc's for writing in fiowrt */
#define ERR_UNDEFF 603 /* undefined function */
#define ERR_UNDEFO 604 /* undefined object */
#define ERR_UNDEF 605 /* undefined symbols found */
#define ERR_OPRGAM 606 /* unable to open game for reading */
#define ERR_RDGAM 607 /* error reading game file */
#define ERR_BADHDR 608 /* file has invalid header - not TADS game file */
#define ERR_UNKRSC 609 /* unknown resource type in .gam file */
#define ERR_UNKOTYP 610 /* unknown object type in OBJ resource */
#define ERR_BADVSN 611 /* file saved by different incompatible version */
#define ERR_LDGAM 612 /* error loading object on demand */
#define ERR_LDBIG 613 /* object too big for load region (prob. internal) */
#define ERR_UNXEXT 614 /* did not expect external function */
#define ERR_WRTVSN 615 /* compiler cannot write the requested version */
#define ERR_VNOCTAB 616 /* format version cannot be used with -ctab */
#define ERR_BADHDRRSC 617 /* invalid resource file header in file %s */
#define ERR_RDRSC 618 /* error reading resource file "xxx" */
/* character mapping errors */
#define ERR_CHRNOFILE 700 /* unable to load character mapping file */
/* user interrupt */
#define ERR_USRINT 990 /* user requested cancel of current operation */
/* run-time errors */
#define ERR_STKOVF 1001 /* stack overflow */
#define ERR_HPOVF 1002 /* heap overflow */
#define ERR_REQNUM 1003 /* numeric value required */
#define ERR_STKUND 1004 /* stack underflow */
#define ERR_REQLOG 1005 /* logical value required */
#define ERR_INVCMP 1006 /* invalid datatypes for magnitude comparison */
#define ERR_REQSTR 1007 /* string value required */
#define ERR_INVADD 1008 /* invalid datatypes for '+' operator */
#define ERR_INVSUB 1009 /* invalid datatypes for binary '-' operator */
#define ERR_REQVOB 1010 /* require object value */
#define ERR_REQVFN 1011 /* required function pointer */
#define ERR_REQVPR 1012 /* required property number value */
/* non-error conditions: run-time EXIT, ABORT, ASKIO, ASKDO */
#define ERR_RUNEXIT 1013 /* 'exit' statement executed */
#define ERR_RUNABRT 1014 /* 'abort' statement executed */
#define ERR_RUNASKD 1015 /* 'askdo' statement executed */
#define ERR_RUNASKI 1016 /* 'askio' executed; int arg 1 is prep */
#define ERR_RUNQUIT 1017 /* 'quit' executed */
#define ERR_RUNRESTART 1018 /* 'reset' executed */
#define ERR_RUNEXITOBJ 1019 /* 'exitobj' executed */
#define ERR_REQVLS 1020 /* list value required */
#define ERR_LOWINX 1021 /* index value too low (must be >= 1) */
#define ERR_HIGHINX 1022 /* index value too high (must be <= length(list)) */
#define ERR_INVTBIF 1023 /* invalid type for built-in function */
#define ERR_INVVBIF 1024 /* invalid value for built-in function */
#define ERR_BIFARGC 1025 /* wrong number of arguments to built-in */
#define ERR_ARGC 1026 /* wrong number of arguments to user function */
#define ERR_FUSEVAL 1027 /* string/list not allowed for fuse/daemon arg */
#define ERR_BADSETF 1028 /* internal error in setfuse/setdaemon/notify */
#define ERR_MANYFUS 1029 /* too many fuses */
#define ERR_MANYDMN 1030 /* too many daemons */
#define ERR_MANYNFY 1031 /* too many notifiers */
#define ERR_NOFUSE 1032 /* fuse not found in remfuse */
#define ERR_NODMN 1033 /* daemon not found in remdaemon */
#define ERR_NONFY 1034 /* notifier not found in unnotify */
#define ERR_BADREMF 1035 /* internal error in remfuse/remdaemon/unnotify */
#define ERR_DMDLOOP 1036 /* load-on-demand loop: property not being set */
#define ERR_UNDFOBJ 1037 /* undefined object in vocabulary tree */
#define ERR_BIFCSTR 1038 /* c-string conversion overflows buffer */
#define ERR_INVOPC 1039 /* invalid opcode */
#define ERR_RUNNOBJ 1040 /* runtime error: property taken of non-object */
#define ERR_EXTLOAD 1041 /* unable to load external function "%s" */
#define ERR_EXTRUN 1042 /* error executing external function "%s" */
#define ERR_CIRCSYN 1043 /* circular synonym */
#define ERR_DIVZERO 1044 /* divide by zero */
#define ERR_BADDEL 1045 /* can only delete objects created with "new" */
#define ERR_BADNEWSC 1046 /* superclass for "new" cannot be a new object */
#define ERR_VOCSTK 1047 /* insufficient space in parser stack */
#define ERR_BADFILE 1048 /* invalid file handle */
#define ERR_RUNEXITPRECMD 1049 /* exited from preCommand */
/* run-time parser errors */
#define ERR_PRS_SENT_UNK 1200 /* sentence structure not recognized */
#define ERR_PRS_VERDO_FAIL 1201 /* verDoVerb failed */
#define ERR_PRS_VERIO_FAIL 1202 /* verIoVerb failed */
#define ERR_PRS_NO_VERDO 1203 /* no verDoVerb for direct object */
#define ERR_PRS_NO_VERIO 1204 /* no verIoVerb for direct object */
#define ERR_PRS_VAL_DO_FAIL 1205 /* direct object validation failed */
#define ERR_PRS_VAL_IO_FAIL 1206 /* indirect object validation failed */
/* compiler/runtime/debugger driver errors */
#define ERR_USAGE 1500 /* invalid usage */
#define ERR_OPNINP 1501 /* error opening input file */
#define ERR_NODBG 1502 /* game not compiled for debugging */
#define ERR_ERRFIL 1503 /* unable to open error capture file */
#define ERR_PRSCXSIZ 1504 /* parse pool + local size too large */
#define ERR_STKSIZE 1505 /* stack size too large */
#define ERR_OPNSTRFIL 1506 /* error opening string capture file */
#define ERR_INVCMAP 1507 /* invalid character map file */
/* debugger errors */
#define ERR_BPSYM 2000 /* symbol not found for breakpoint */
#define ERR_BPPROP 2002 /* breakpoint symbol is not a property */
#define ERR_BPFUNC 2003 /* breakpoint symbol is not a function */
#define ERR_BPNOPRP 2004 /* property is not defined for object */
#define ERR_BPPRPNC 2005 /* property is not code */
#define ERR_BPSET 2006 /* breakpoint already set at this location */
#define ERR_BPNOTLN 2007 /* breakpoint is not at a line (OPCLINE instr) */
#define ERR_MANYBP 2008 /* too many breakpoints */
#define ERR_BPNSET 2009 /* breakpoint to be deleted was not set */
#define ERR_DBGMNSY 2010 /* too many symbols in debug expression (int lim) */
#define ERR_NOSOURC 2011 /* unable to find source file %s */
#define ERR_WTCHLCL 2012 /* illegal to assign to local in watch expr */
#define ERR_INACTFR 2013 /* inactive frame (expression value not available) */
#define ERR_MANYWX 2014 /* too many watch expressions */
#define ERR_WXNSET 2015 /* watchpoint not set */
#define ERR_EXTRTXT 2016 /* extraneous text at end of command */
#define ERR_BPOBJ 2017 /* breakpoint symbol is not an object */
#define ERR_DBGINACT 2018 /* debugger is not active */
#define ERR_BPINUSE 2019 /* breakpoint is already used */
#define ERR_RTBADSPECEXPR 2020 /* invalid speculative expression */
#define ERR_NEEDLIN2 2021 /* -ds2 information not found - must recompile */
/* usage error messages */
#define ERR_TCUS1 3000 /* first tc usage message */
#define ERR_TCUSL 3024 /* last tc usage message */
#define ERR_TCTGUS1 3030 /* first tc toggle message */
#define ERR_TCTGUSL 3032
#define ERR_TCZUS1 3040 /* first tc -Z suboptions usage message */
#define ERR_TCZUSL 3041
#define ERR_TC1US1 3050 /* first tc -1 suboptions usage message */
#define ERR_TC1USL 3058
#define ERR_TCMUS1 3070 /* first tc -m suboptions usage message */
#define ERR_TCMUSL 3076
#define ERR_TCVUS1 3080 /* first -v suboption usage message */
#define ERR_TCVUSL 3082
#define ERR_TRUSPARM 3099
#define ERR_TRUS1 3100 /* first tr usage message */
#define ERR_TRUSL 3117
#define ERR_TRUSFT1 3118 /* first tr "footer" message */
#define ERR_TRUSFTL 3119 /* last tr "footer" message */
#define ERR_TRSUS1 3150 /* first tr -s suboptions usage message */
#define ERR_TRSUSL 3157
#define ERR_TDBUSPARM 3199
#define ERR_TDBUS1 3200 /* first tdb usage message */
#define ERR_TDBUSL 3214 /* last tdb usage message */
/* TR 16-bit MSDOS-specific usage messages */
#define ERR_TRUS_DOS_1 3300
#define ERR_TRUS_DOS_L 3300
/* TR 32-bit MSDOS console mode usage messages */
#define ERR_TRUS_DOS32_1 3310
#define ERR_TRUS_DOS32_L 3312
/* TADS/Graphic errors */
#define ERR_GNOFIL 4001 /* can't find graphics file %s */
#define ERR_GNORM 4002 /* can't find room %s */
#define ERR_GNOOBJ 4003 /* can't find hot spot object %s */
#define ERR_GNOICN 4004 /* can't find icon object %s */
/*
* Special error flag - this is returned from execmd() when preparseCmd
* returns a command list. This indicates to voc1cmd that it should try
* the command over again, using the words in the new list.
*/
#define ERR_PREPRSCMDREDO 30000 /* preparseCmd returned a list */
#define ERR_PREPRSCMDCAN 30001 /* preparseCmd returned 'nil' to cancel */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,205 @@
/* 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/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* format an error message, sprintf-style, using an erradef array */
int errfmt(char *outbuf, int outbufl, char *fmt, int argc, erradef *argv)
{
int outlen = 0;
int argi = 0;
int len;
char buf[20];
const char *p = nullptr;
char fmtchar;
while (*fmt != '\0' && outbufl > 1)
{
switch(*fmt)
{
case '\\':
++fmt;
len = 1;
switch(*fmt)
{
case '\0':
--fmt;
break;
case '\n':
p = "\n";
break;
case '\t':
p = "\t";
break;
default:
p = fmt;
break;
}
break;
case '%':
++fmt;
fmtchar = *fmt;
if (argi >= argc) fmtchar = 1; /* too many - ignore it */
switch(fmtchar)
{
case '\0':
--fmt;
len = 0;
break;
case '%':
p = "%";
len = 1;
break;
case 'd':
Common::sprintf_s(buf, "%d", argv[argi].erraint);
len = strlen(buf);
p = buf;
break;
case 'u':
Common::sprintf_s(buf, "%u", argv[argi].erraint);
len = strlen(buf);
p = buf;
break;
case 's':
p = argv[argi].errastr;
len = strlen(p);
break;
default:
p = "";
len = 0;
--argi;
break;
}
++argi;
break;
default:
p = fmt;
len = 1;
break;
}
/* copy output that was set up above */
if (len != 0)
{
if (outbufl >= len)
{
memcpy(outbuf, p, (size_t)len);
outbufl -= len;
outbuf += len;
}
else if (outbufl > 1)
{
memcpy(outbuf, p, (size_t)outbufl - 1);
outbufl = 1;
}
outlen += len;
}
++fmt;
}
/* add a null terminator */
if (outbufl != 0)
*outbuf++ = '\0';
/* return the length */
return outlen;
}
#if defined(DEBUG) && !defined(ERR_NO_MACRO)
#error longjmp unsupported in ScummVM
#endif /* DEBUG */
#ifdef ERR_NO_MACRO
/* base error signal function */
void errsign(errcxdef *ctx, int e, const char *facility)
{
strncpy(ctx->errcxptr->errfac, facility, ERRFACMAX);
ctx->errcxptr->errfac[ERRFACMAX] = '\0';
ctx->errcxofs = 0;
#if 0
longjmp(ctx->errcxptr->errbuf, e);
#else
error("Error - %s", facility);
#endif
}
/* signal an error with no arguments */
void errsigf(errcxdef *ctx, const char *facility, int e)
{
errargc(ctx, 0);
errsign(ctx, e, facility);
}
/* enter a string argument */
char *errstr(errcxdef *ctx, const char *str, int len)
{
char *ret = &ctx->errcxbuf[ctx->errcxofs];
memcpy(ret, str, (size_t)len);
ret[len] = '\0';
ctx->errcxofs += len + 1;
return(ret);
}
/* resignal current error */
void errrse1(errcxdef *ctx, errdef *fr)
{
errargc(ctx, fr->erraac);
memcpy(ctx->errcxptr->erraav, fr->erraav,
(size_t)(fr->erraac * sizeof(erradef)));
errsign(ctx, fr->errcode, fr->errfac);
}
/* log an error: base function */
void errlogn(errcxdef *ctx, int err, const char *facility)
{
ctx->errcxofs = 0;
(*ctx->errcxlog)(ctx->errcxlgc, facility, err, ctx->errcxptr->erraac,
ctx->errcxptr->erraav);
}
/* log an error with no arguments */
void errlogf(errcxdef *ctx, const char *facility, int err)
{
errargc(ctx, 0);
errlogn(ctx, err, facility);
}
#endif /* ERR_NO_MACRO */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,342 @@
/* 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/>.
*
*/
/* Library error handling definitions
* All of the functions and macros in here are named ERRxxx because
* this file was based on the TADS err.h, which used the ERRxxx naming
* convention, and it would be a lot of trouble to change.
*
* This package defines a set of macros that allows code to raise and
* handle exceptions. A macro is provided which signals an error, which
* does a non-local goto to the innermost enclosing exception handler.
* A set of macros sets up exception handling code.
*
* To catch exceptions that occur inside a block of code (i.e., in the
* code or in any subroutines called by the code), begin the block with
* ERRBEGIN. At the end of the protected code, place the exception
* handler, which starts with ERRCATCH. At the end of the exception
* handler, place ERREND. If no exception occurs, execution goes
* through the protected code, then resumes at the code following
* the ERREND.
*
* The exception handler can signal another error, which will cause
* the next enclosing frame to catch the error. Alternatively, if
* the exception handler doesn't signal an error or return, execution
* continues at the code following the ERREND. Exceptions that are
* signalled during exception handling will be caught by the next
* enclosing frame, unless the exception handler code is itself
* protected by another ERRBEGIN-ERREND block.
*
* To signal an error, use errsig().
*
* To use a string argument in a signalled error, cover the string
* with errstr(ctx, str, len); for example:
*
* errsig1(ctx, ERR_XYZ, ERRTSTR, errstr(ctx, buf, strlen(buf)));
*
* This copies the string into a buffer that is unaffected by
* stack resetting during error signalling.
*/
#ifndef GLK_TADS_TADS2_ERROR_HANDLING
#define GLK_TADS_TADS2_ERROR_HANDLING
#include "glk/tads/tads2/lib.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* Maximum length of a facility identifier
*/
#define ERRFACMAX 6
union erradef {
int erraint; /* integer argument */
const char *errastr; /* text string argument */
};
struct errdef {
struct errdef *errprv; /* previous error frame */
int errcode; /* error code of exception being handled */
char errfac[ERRFACMAX+1]; /* facility of current error */
erradef erraav[10]; /* parameters for error */
int erraac; /* count of parameters in argc */
};
#define ERRBUFSIZ 512
/**
* Seek location record for an error message by number
*/
struct errmfdef {
uint errmfnum; /* error number */
ulong errmfseek; /* seek location of this message */
};
typedef struct errmfdef errmfdef;
struct errcxdef {
errdef *errcxptr; /* current error frame */
void (*errcxlog)(void *, const char *fac, int err, int argc, erradef *);
/* error logging callback function */
void *errcxlgc; /* context for error logging callback */
int errcxofs; /* offset in argument buffer */
char errcxbuf[ERRBUFSIZ]; /* space for argument strings */
osfildef *errcxfp; /* message file, if one is being used */
errmfdef *errcxseek; /* seek locations of messages in file */
uint errcxsksz; /* size of errcxseek array */
ulong errcxbase; /* offset in physical file of logical error file */
struct appctxdef *errcxappctx; /* host application context */
};
/**
* Begin protected code
*/
#define ERRBEGIN(ctx) \
{ \
errdef fr_; \
if (1 /*(fr_.errcode = setjmp(fr_.errbuf)) == 0 */) \
{ \
fr_.errprv = (ctx)->errcxptr; \
(ctx)->errcxptr = &fr_;
/**
* End protected code, begin error handler
*/
#define ERRCATCH(ctx, e) \
assert(1==1 && (ctx)->errcxptr != fr_.errprv); \
(ctx)->errcxptr = fr_.errprv; \
} \
else \
{ \
assert(2==2 && (ctx)->errcxptr != fr_.errprv); \
(e) = fr_.errcode; \
(ctx)->errcxptr = fr_.errprv;
#define ERRCATCH_ERRCODE_UNUSED(ctx) \
assert(1==1 && (ctx)->errcxptr != fr_.errprv); \
(ctx)->errcxptr = fr_.errprv; \
} \
else \
{ \
assert(2==2 && (ctx)->errcxptr != fr_.errprv); \
(ctx)->errcxptr = fr_.errprv;
/* retrieve argument (int, string) in current error frame */
#define errargint(argnum) (fr_.erraav[argnum].erraint)
#define errargstr(argnum) (fr_.erraav[argnum].errastr)
#define ERREND(ctx) \
} \
}
/* end protected code, begin cleanup (no handling; just cleaning up) */
#define ERRCLEAN(ctx) \
assert((ctx)->errcxptr != fr_.errprv); \
(ctx)->errcxptr = fr_.errprv; \
} \
else \
{ \
assert((ctx)->errcxptr != fr_.errprv); \
(ctx)->errcxptr = fr_.errprv;
#define ERRENDCLN(ctx) \
errrse(ctx); \
} \
}
/* argument types for errors with arguments */
#define ERRTINT erraint
#define ERRTSTR errastr
/* set argument count in error frame */
#define errargc(ctx,cnt) ((ctx)->errcxptr->erraac=(cnt))
/* enter string argument; returns pointer to argument used in errargv */
#ifdef ERR_NO_MACRO
char *errstr(errcxdef *ctx, const char *str, int len);
#else /* ERR_NO_MACRO */
#define errstr(ctx,str,len) \
((memcpy(&(ctx)->errcxbuf[(ctx)->errcxofs],str,(size_t)len), \
(ctx)->errcxofs += (len), \
(ctx)->errcxbuf[(ctx)->errcxofs++] = '\0'), \
&(ctx)->errcxbuf[(ctx)->errcxofs-(len)-1])
#endif /* ERR_NO_MACRO */
/* set argument in error frame argument vector */
#define errargv(ctx,index,typ,arg) \
((ctx)->errcxptr->erraav[index].typ=(arg))
/* signal an error with argument count already set */
#ifdef ERR_NO_MACRO
void errsign(errcxdef *ctx, int e, const char *facility);
#else /* ERR_NO_MACRO */
# ifdef DEBUG
void errjmp(jump_buf buf, int e);
# define errsign(ctx, e, fac) \
(strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
(ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
(ctx)->errcxofs=0, errjmp((ctx)->errcxptr->errbuf, e))
# else /* DEBUG */
# define errsign(ctx, e, fac) \
(strncpy((ctx)->errcxptr->errfac, fac, ERRFACMAX),\
(ctx)->errcxptr->errfac[ERRFACMAX]='\0',\
(ctx)->errcxofs=0, longjmp((ctx)->errcxptr->errbuf, e))
# endif /* DEBUG */
#endif /* ERR_NO_MACRO */
/* signal an error with no arguments */
#ifdef ERR_NO_MACRO
void errsigf(errcxdef *ctx, const char *facility, int err);
#else /* ERR_NO_MACRO */
#define errsigf(ctx, fac, e) (errargc(ctx,0),errsign(ctx,e,fac))
#endif /* ERR_NO_MACRO */
/* signal an error with one argument */
#define errsigf1(ctx, fac, e, typ1, arg1) \
(errargv(ctx,0,typ1,arg1),errargc(ctx,1),errsign(ctx,e,fac))
/* signal an error with two arguments */
#define errsigf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
(errargv(ctx,0,typ1,arg1), errargv(ctx,1,typ2,arg2), \
errargc(ctx,2), errsign(ctx,e,fac))
/* resignal the current error - only usable within exception handlers */
#ifdef ERR_NO_MACRO
void errrse1(errcxdef *ctx, errdef *fr);
# define errrse(ctx) errrse1(ctx, &fr_)
#else /* ERR_NO_MACRO */
/* void errrse(errcxdef *ctx); */
# define errrse(ctx) \
(errargc(ctx, fr_.erraac),\
memcpy((ctx)->errcxptr->erraav, fr_.erraav, \
(size_t)(fr_.erraac*sizeof(erradef))),\
errsign(ctx, fr_.errcode, fr_.errfac))
#endif /* ERR_NO_MACRO */
/*
* For use in an error handler (ERRCATCH..ERREND) only: Copy the
* parameters from the error currently being handled to the enclosing
* frame. This is useful when "keeping" an error being handled - i.e.,
* the arguments will continue to be used outside of the
* ERRCATCH..ERREND code.
*/
/* void errkeepargs(errcxdef *ctx); */
#define errkeepargs(ctx) errcopyargs(ctx, &fr_)
/*
* copy the parameters for an error from another frame into the current
* frame - this can be used when we want to be able to display an error
* that occurred in an inner frame within code that is protected by a
* new enclosing error frame
*/
/* void errcopyargs(errcxdef *ctx, errdef *fr); */
#define errcopyargs(ctx, fr) \
(errargc((ctx), (fr)->erraac), \
memcpy((ctx)->errcxptr->erraav, (fr)->erraav, \
(size_t)((fr)->erraac*sizeof(erradef))))
/* log error that's been caught, using arguments already caught */
#define errclog(ctx) \
((*(ctx)->errcxlog)((ctx)->errcxlgc,fr_.errfac,fr_.errcode,\
fr_.erraac,fr_.erraav))
/* log an error that's been set up but not signalled yet */
#define errprelog(ctx, err) \
((*(ctx)->errcxlog)((ctx)->errcxlgc,(ctx)->errcxptr->errfac,\
err,(ctx)->errcxptr->erraac,\
(ctx)->errcxptr->erraav))
/* log an error (no signalling, just reporting) */
#ifdef ERR_NO_MACRO
void errlogn(errcxdef *ctx, int err, const char *facility);
#else /* ERR_NO_MACRO */
#define errlogn(ctx,err,fac) \
((ctx)->errcxofs=0,\
(*(ctx)->errcxlog)((ctx)->errcxlgc,fac,err,(ctx)->errcxptr->erraac,\
(ctx)->errcxptr->erraav))
#endif /* ERR_NO_MACRO */
/* log an error with no arguments */
#ifdef ERR_NO_MACRO
void errlogf(errcxdef *ctx, const char *facility, int err);
#else /* ERR_NO_MACRO */
/* void errlogf(errcxdef *ctx, char *facility, int err); */
#define errlogf(ctx,fac,err) (errargc(ctx,0),errlogn(ctx,err,fac))
#endif /* ERR_NO_MACRO */
/* log an error with one argument */
#define errlogf1(ctx, fac, e, typ1, arg1) \
(errargv(ctx,0,typ1,arg1),errargc(ctx,1),errlogn(ctx,e,fac))
/* log an error with two arguments */
#define errlogf2(ctx, fac, e, typ1, arg1, typ2, arg2) \
(errargv(ctx,0,typ1,arg1),errargv(ctx,1,typ2,arg2),\
errargc(ctx,2),errlogn(ctx,e,fac))
/*
* Format an error message, sprintf-style, using arguments in an
* erradef array (which is passed to the error-logging callback).
* Returns the length of the output string, even if the actual
* output string was truncated because the outbuf was too short.
* (If called with outbufl == 0, nothing will be written out, but
* the size of the buffer needed, minus the terminating null byte,
* will be computed and returned.)
*/
int errfmt(char *outbuf, int outbufl, char *fmt, int argc,
erradef *argv);
/* get the text of an error */
void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err);
/* initialize error subsystem, opening error message file if necessary */
void errini(errcxdef *ctx, osfildef *fp);
/* allocate and initialize error context, free error context */
errcxdef *lerini();
void lerfre(errcxdef *ctx);
/* error message structure - number + text */
struct errmdef {
uint errmerr; /* error number */
char *errmtxt; /* text of error message */
};
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,69 @@
/* 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/tads/tads2/error_handling.h"
#include "glk/tads/tads2/ltk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
errcxdef *lerini() {
errcxdef *errcx; /* error context */
// allocate an error context
if (!(errcx = (errcxdef *)ltk_suballoc(sizeof(errcxdef)))) {
// failure
return((errcxdef *)nullptr);
}
// initialize the error context
errcx->errcxfp = (osfildef *)nullptr; /* no error file handle */
errcx->errcxofs = 0; /* no offset in argument buffer */
errcx->errcxlog = ltk_errlog; /* error logging routine */
errcx->errcxlgc = errcx; /* error logging context */
// return the new context
return errcx;
}
void errini(errcxdef *ctx, char *arg0) {
VARUSED(ctx);
VARUSED(arg0);
}
void errini(errcxdef *ctx, osfildef *fp) {
VARUSED(ctx);
VARUSED(fp);
}
void lerfre(errcxdef *errcx) {
// free the context
ltk_subfree(errcx);
}
void errmsg(errcxdef *ctx, char *outbuf, uint outbufl, uint err) {
Common::sprintf_s(outbuf, outbufl, "Error #%d occurred.", err);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,135 @@
/* 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/>.
*
*/
/*
* file i/o interface
*/
#ifndef GLK_TADS_TADS2_FILE_IO
#define GLK_TADS_TADS2_FILE_IO
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/memory_cache_loader.h"
#include "glk/tads/tads2/object.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward declarations */
struct voccxdef;
struct tokpdef;
struct tokthdef;
struct tokcxdef;
/* load-on-demand context (passed in by mcm in load callback) */
struct fiolcxdef {
osfildef *fiolcxfp; /* file pointer of load file */
errcxdef *fiolcxerr; /* error handling context */
ulong fiolcxst; /* starting offset in file */
uint fiolcxflg; /* flags from original load file */
uint fiolcxseed; /* fioxor seed */
uint fiolcxinc; /* fioxor increment */
};
/* write game to binary file */
void fiowrt(struct mcmcxdef *mctx, voccxdef *vctx,
struct tokcxdef *tokctx, struct tokthdef *tab,
uchar *fmts, uint fmtl, char *fname, uint flags, objnum preinit,
int extc, uint prpcnt, char *filever);
/* flag values for use with fiowrt */
#define FIOFSYM 0x01 /* include symbol table in output file */
#define FIOFLIN 0x02 /* include source file tracking information */
#define FIOFPRE 0x04 /* preinit needs to be run after reading game */
#define FIOFCRYPT 0x08 /* "encrypt" objects prior to writing them */
#define FIOFBIN 0x10 /* writing precompiled header */
#define FIOFFAST 0x20 /* fast-load records are in file */
#define FIOFCASE 0x40 /* case folding was turned on in original compile */
#define FIOFLIN2 0x80 /* new-style line records */
/* read game from binary file; sets up loader callback context */
void fiord(mcmcxdef *mctx, voccxdef *vctx, tokcxdef *tctx, const char *fname,
const char *exename, fiolcxdef *setupctx, objnum *preinit, uint *flagp,
tokpdef *path, uchar **fmtsp, uint *fmtlp, uint *pcntptr, int flags,
appctxdef *appctx, char *argv0);
/* shut down load-on-demand subsystem, close load file */
void fiorcls(fiolcxdef *ctx);
/* loader callback - load an object on demand */
void OS_LOADDS fioldobj(void *ctx, mclhd handle, uchar *ptr, ushort siz);
/*
* Save a game - returns TRUE on failure. We'll save the file to
* 'fname'. 'game_fname' is the name of the game file; if this is not
* null, we'll save it to the saved game file so that the player can
* later start the game by specifying only the saved game file to the
* run-time. 'game_fname' can be null, in which case we'll omit the
* game file information.
*/
int fiosav(voccxdef *vctx, char *fname, char *game_fname);
/*
* fiorso() result codes
*/
#define FIORSO_SUCCESS 0 /* success */
#define FIORSO_FILE_NOT_FOUND 1 /* file not found */
#define FIORSO_NOT_SAVE_FILE 2 /* not a saved game file */
#define FIORSO_BAD_FMT_VSN 3 /* incompatible file format version */
#define FIORSO_BAD_GAME_VSN 4 /* file saved by another game or version */
#define FIORSO_READ_ERROR 5 /* error reading from the file */
#define FIORSO_NO_PARAM_FILE 6 /* no parameter file (for restore(nil)) */
/* restore a game - returns TRUE on failure */
int fiorso(voccxdef *vctx, char *fname);
/*
* Look in a saved game file to determine if it has information on which
* GAM file created it. If the GAM file information is available, this
* routine returns true and stores the game file name in the given
* buffer; if the information isn't available, we'll return false.
*/
int fiorso_getgame(char *saved_file, char *buf, size_t buflen);
/* encrypt/decrypt an object */
void fioxor(uchar *p, uint siz, uint seed, uint inc);
/* strings stored in binary game file for identification and validation */
/* file header string */
#define FIOFILHDR "TADS2 bin\012\015\032"
/* resource file header string */
#define FIOFILHDRRSC "TADS2 rsc\012\015\032"
/* CURRENT file format version string */
#define FIOVSNHDR "v2.2.0"
/* other file format versions that can be READ by this version */
#define FIOVSNHDR2 "v2.0.0"
#define FIOVSNHDR3 "v2.0.1"
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,172 @@
/* 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/tads/tads2/character_map.h"
#include "glk/tads/tads2/text_io.h"
#include "glk/tads/tads2/os.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Global variable with the current command logging file. If this is
* not null, we'll log each command that we read to this file.
*/
osfildef *cmdfile;
/*
* External global with the current script input file. If this is
* non-null, we'll read commands from this file rather than from the
* keyboard.
*/
extern osfildef *scrfp;
/*
* External global indicating script echo status. If we're reading from
* a script input file (i.e., scrfp is non-null), and this variable is
* true, it indicates that we're in "quiet" mode reading the script, so
* we will not echo commands that we read from the script file to the
* display.
*/
extern int scrquiet;
/*
* getstring reads a string from the keyboard, doing all necessary
* output flushing. Prompting is to be done by the caller. This
* routine should be called instead of os_gets.
*/
int getstring(const char *prompt, char *buf, int bufl)
{
char *result;
int savemoremode;
int retval = 0;
/* show prompt if one was given and flush output */
savemoremode = setmore(0);
if (prompt != nullptr)
{
/* display the prompt text */
outformat(prompt);
/* make sure it shows up in the log file as well */
out_logfile_print(prompt, FALSE);
}
outflushn(0);
outreset();
/* read from the command input file if we have one */
if (scrfp != nullptr)
{
int quiet = scrquiet;
/* try reading from command input file */
if ((result = qasgets(buf, bufl)) == nullptr)
{
/*
* End of command input file; return to reading the
* keyboard. If we didn't already show the prompt, show it
* now.
*
* Note that qasgets() will have closed the script file
* before returning eof, so we won't directly read the
* command here but instead handle it later when we check to
* see if we need to read from the keyboard.
*/
if (quiet && prompt != nullptr)
outformat(prompt);
outflushn(0);
outreset();
/*
* Guarantee that moremode is turned back on. (moremode can
* be turned off for one of two reasons: we're printing the
* prompt, or we're reading from a script with no pauses.
* In either case, moremode should be turned back on at this
* point. -CDN)
*/
savemoremode = 1;
/* turn off NONSTOP mode now that we're done with the script */
os_nonstop_mode(FALSE);
}
/* success */
retval = 0;
}
/* if we don't have a script file, read from the keyboard */
if (scrfp == nullptr)
{
/* update the status line */
runstat();
/* read a line from the keyboard */
result = (char *)os_gets((uchar *)buf, bufl);
/*
* if the result is null, we're at eof, so return a non-zero
* value; otherwise, we successfully read a command, so return
* zero
*/
retval = (result == nullptr);
}
/* restore the original "more" mode */
setmore(savemoremode);
/* check the result */
if (retval != 0)
{
/* we got an error reading the command - return the error */
return retval;
}
else
{
char *p;
/*
* we got a command, or at least a partial command (if we timed
* out, we may still have a partial line in the buffer) - write
* the input line to the log and/or command files, as
* appropriate
*/
out_logfile_print(buf, TRUE);
if (cmdfile != nullptr)
{
os_fprintz(cmdfile, ">");
os_fprintz(cmdfile, buf);
os_fprintz(cmdfile, "\n");
}
/* translate the input to the internal character set */
for (p = buf ; *p != '\0' ; ++p)
*p = cmap_n2i(*p);
/* success */
return retval;
}
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,139 @@
/* 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_TADS_TADS2_LIB
#define GLK_TADS_TADS2_LIB
#include "common/scummsys.h"
#include "common/algorithm.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* short-hand for various types */
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;
typedef byte ub1;
typedef signed char sb1;
typedef char b1;
typedef unsigned int ub2;
typedef signed int sb2;
typedef int b2;
typedef unsigned long ub4;
typedef signed long sb4;
typedef long b4;
typedef int eword;
/* maximum/minimum portable values for various types */
#define UB4MAXVAL 0xffffffffUL
#define UB2MAXVAL 0xffffU
#define UB1MAXVAL 0xffU
#define SB4MAXVAL 0x7fffffffL
#define SB2MAXVAL 0x7fff
#define SB1MAXVAL 0x7f
#define SB4MINVAL (-(0x7fffffff)-1)
#define SB2MINVAL (-(0x7fff)-1)
#define SB1MINVAL (-(0x7f)-1)
/* clear a struture */
#define CLRSTRUCT(x) memset(&(x), 0, (size_t)sizeof(x))
#define CPSTRUCT(dst,src) memcpy(&(dst), &(src), (size_t)sizeof(dst))
#if defined(TRUE)
#undef TRUE
#endif
#if defined(FALSE)
#undef FALSE
#endif
/* TRUE and FALSE */
#define TRUE true
#define FALSE false
/* bitwise operations */
#define bit(va, bt) ((va) & (bt))
#define bis(va, bt) ((va) |= (bt))
#define bic(va, bt) ((va) &= ~(bt))
/*
* noreg/NOREG - was used for variables changed in error-protected code that
* are used in error handling code. However, since ScummVM doesn't support
* setjmp for portability reasons, the following can be left as blank defines
*/
# define noreg
# define NOREG(arglist)
/*
* Linting directives. You can define these before including this file
* if you have a fussy compiler.
*/
#ifdef LINT
# ifndef NOTREACHED
# define NOTREACHED return
# endif
# ifndef NOTREACHEDV
# define NOTREACHEDV(t) return((t)0)
# endif
# ifndef VARUSED
# define VARUSED(v) varused(v)
# endif
void varused();
#else /* LINT */
# ifndef NOTREACHED
# define NOTREACHED
# endif
# ifndef NOTREACHEDV
# define NOTREACHEDV(t)
# endif
# ifndef VARUSED
# define VARUSED(v)
# endif
#endif /* LINT */
/* conditionally compile code if debugging is enabled */
#ifdef DEBUG
# define IF_DEBUG(x) x
#else /* DEBUG */
# define IF_DEBUG(x)
#endif /* DEBUG */
#ifndef offsetof
# define offsetof(s_name, m_name) (size_t)&(((s_name *)0)->m_name)
#endif /* offsetof */
/*
* Define our own version of isspace(), so that we don't try to interpret
* anything outside of the normal ASCII set as spaces.
*/
#define t_isspace(c) \
(((unsigned char)(c)) <= 127 && Common::isSpace((unsigned char)(c)))
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,128 @@
/* 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/>.
*
*/
/* Line source definitions
*
* A line source is a mechanism for reading source text. The tokenizer
* reads its input from a line source. This is the basic class
* definition; individual line sources will define the functions and
* class data needed.
*/
#ifndef GLK_TADS_TADS2_LINE_SOURCE
#define GLK_TADS_TADS2_LINE_SOURCE
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* Line source superclass structure
*/
struct lindef {
int (*lingetp)(lindef *lin); /* get next line */
void (*linclsp)(lindef *lin); /* close line source */
void (*linppos)(lindef *lin, char *buf, uint buflen);
/* write printable rep of position to buf (for error reporting) */
void (*linglop)(lindef *lin, uchar *buf);
/* generate a line record for an OPCLINE instruction */
int (*linwrtp)(lindef *lin, osfildef *fp);
/* write line source information to binary file; TRUE ==> error */
void (*lincmpp)(lindef *lin, uchar *buf);
/* give location of compiled code for current line to line source */
void (*linactp)(lindef *lin); /* activate for dbg */
void (*lindisp)(lindef *lin); /* disactivate */
void (*lintellp)(lindef *lin, uchar *pos); /* get position */
void (*linseekp)(lindef *lin, uchar *pos); /* seek */
int (*linreadp)(lindef *lin, uchar *buf, uint siz); /* fread */
void (*linpaddp)(lindef *lin, uchar *pos, long delta);
/* add an offset to a position value */
int (*linqtopp)(lindef *lin, uchar *pos); /* at top? */
int (*lingetsp)(lindef *lin, uchar *buf, uint siz); /* get line */
void (*linnamp)(lindef *lin, char *buf); /* get source name */
void (*linfindp)(lindef *lin, char *buf, objnum *objp,
uint *ofsp); /* find nearest line record */
void (*lingotop)(lindef *lin, int where); /* seek global */
long (*linofsp)(lindef *lin); /* byte offset in line source */
void (*linrenp)(lindef *lin, objnum oldnum, objnum newnum);
/* renumber an object (for "modify") */
void (*lindelp)(lindef *lin, objnum objn);
/* delete an object (for "replace") */
ulong (*linlnump)(lindef *lin); /* get the current line number */
#define LINGOTOP OSFSK_SET /* go to top of line source */
#define LINGOEND OSFSK_END /* go to end of line source */
lindef *linpar; /* parent of current line source */
lindef *linnxt; /* next line in line source chain */
int linid; /* serial number of line source (for debugger) */
char *linbuf; /* pointer to current line */
ushort linflg; /* flags */
#define LINFEOF 0x01 /* line source is at end of file */
#define LINFMORE 0x02 /* there's more to the line than linlen */
#define LINFDBG 0x04 /* debug record already generated for line */
#define LINFNOINC 0x08 /* ignore # directives from this line source */
#define LINFCMODE 0x10 /* line source is parsed in C-mode */
ushort linlen; /* length of the line */
ushort linlln; /* length of line record generated by lingloc */
};
/**
* Maximum allowed value for linlln, in bytes. This allows subsystems
* that need to maintain local copies of seek locations to know how big
* an area to allocate for them.
*/
#define LINLLNMAX 20
/* macros to cover calls to functions */
#define linget(lin) ((*((lindef *)(lin))->lingetp)((lindef *)(lin)))
#define lincls(lin) ((*((lindef *)(lin))->linclsp)((lindef *)(lin)))
#define linppos(lin, buf, buflen) \
((*((lindef *)(lin))->linppos)((lindef *)(lin), buf, buflen))
#define linglop(lin, buf) ((*((lindef *)(lin))->linglop)(lin, buf))
#define linwrt(lin, fp) ((*((lindef *)(lin))->linwrtp)(lin, fp))
#define lincmpinf(lin, buf) ((*((lindef *)(lin))->lincmpp)(lin, buf))
#define linactiv(lin) ((*((lindef *)(lin))->linactp)(lin))
#define lindisact(lin) ((*((lindef *)(lin))->lindisp)(lin))
#define lintell(lin, pos) ((*((lindef *)(lin))->lintellp)(lin, pos))
#define linseek(lin, pos) ((*((lindef *)(lin))->linseekp)(lin, pos))
#define linread(lin, buf, siz) ((*((lindef *)(lin))->linreadp)(lin, buf, siz))
#define linpadd(lin, pos, delta) \
((*((lindef *)(lin))->linpaddp)(lin, pos, delta))
#define linqtop(lin, pos) ((*((lindef *)(lin))->linqtopp)(lin, pos))
#define lingets(lin, buf, siz) ((*((lindef *)(lin))->lingetsp)(lin, buf, siz))
#define linnam(lin, buf) ((*((lindef *)(lin))->linnamp)(lin, buf))
#define linlnum(lin) ((*((lindef *)(lin))->linlnump)(lin))
#define linfind(lin, buf, objp, ofsp) \
((*((lindef *)(lin))->linfindp)(lin, buf, objp, ofsp))
#define lingoto(lin, where) ((*((lindef *)(lin))->lingotop)(lin, where))
#define linofs(lin) ((*((lindef *)(lin))->linofsp)(lin))
#define linrenum(lin, oldnum, newnum) \
((*((lindef *)(lin))->linrenp)(lin, oldnum, newnum))
#define lindelnum(lin, objn) ((*((lindef *)(lin))->lindelp)(lin, objn))
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/* 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_TADS_TADS2_LINE_SOURCE_FILE
#define GLK_TADS_TADS2_LINE_SOURCE_FILE
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/debug.h"
#include "glk/tads/tads2/line_source.h"
#include "glk/tads/tads2/object.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
struct tokpdef;
/* maximum number of pages of debugging records we can keep */
#define LINFPGMAX 128
/*
* executable line information structure: this record relates one
* executable line to the object containing the p-code, and the offset
* in the object of the p-code for the start of the line
*/
struct linfinfo {
/*
* OPCLINE data (file seek position or line number, depending on how
* the game was compiled: -ds -> file seek offset, -ds2 -> line
* number)
*/
ulong fpos;
/* object number */
objnum objn;
/* offset from start of code */
uint ofs;
};
/*
* file line source
*/
struct linfdef {
lindef linflin; /* superclass data */
osfildef *linffp; /* file pointer for this line source */
char linfbuf[100]; /* buffer for the line contents */
int linfbufnxt; /* offset in buffer of start of next line */
int linfnxtlen; /* length of data after linfbufnxt */
ulong linfnum; /* current line number */
ulong linfseek; /* seek position of current line */
mcmcxdef *linfmem; /* memory manager context */
mcmon linfpg[LINFPGMAX]; /* pages for debugging records */
ulong linfcrec; /* number of debugger records written so far */
char linfnam[1]; /* name of file being read */
};
/* initialize a file line source, opening the file for the line source */
linfdef *linfini(mcmcxdef *mctx, errcxdef *errctx, const char *filename,
int flen, tokpdef *path, int must_find_file,
int new_line_records);
/* initialize a pre-allocated linfdef, skipping debugger page setup */
void linfini2(mcmcxdef *mctx, linfdef *linf,
const char *filename, int flen, osfildef *fp, int new_line_records);
/* get next line from line source */
int linfget(lindef *lin);
/* generate printable rep of current position in source (for errors) */
void linfppos(lindef *lin, char *buf, uint bufl);
/* close line source */
void linfcls(lindef *lin);
/* generate source-line debug instruction operand */
void linfglop(lindef *lin, uchar *buf);
/* generate new-style source-line debug instructino operand */
void linfglop2(lindef *lin, uchar *buf);
/* save line source to binary (.gam) file */
int linfwrt(lindef *lin, osfildef *fp);
/* load a file-line-source from binary (.gam) file */
int linfload(osfildef *fp, dbgcxdef *dbgctx, errcxdef *ec,
tokpdef *path);
/* add a debugger line record for the current line */
void linfcmp(lindef *lin, uchar *buf);
/* find nearest line record to a file seek location */
void linffind(lindef *lin, char *buf, objnum *objp, uint *ofsp);
/* activate line source for debugging */
void linfact(lindef *lin);
/* disactivate line source */
void linfdis(lindef *lin);
/* get current seek position */
void linftell(lindef *lin, uchar *pos);
/* seek */
void linfseek(lindef *lin, uchar *pos);
/* read */
int linfread(lindef *lin, uchar *buf, uint siz);
/* add a signed delta to a seek position */
void linfpadd(lindef *lin, uchar *pos, long delta);
/* query whether at top of file */
int linfqtop(lindef *lin, uchar *pos);
/* read one line at current seek position */
int linfgets(lindef *lin, uchar *buf, uint bufsiz);
/* get name of line source */
void linfnam(lindef *lin, char *buf);
/* get the current line number */
ulong linflnum(lindef *lin);
/* go to top or bottom */
void linfgoto(lindef *lin, int where);
/* return the current offset in the line source */
long linfofs(lindef *lin);
/* renumber an object */
void linfren(lindef *lin, objnum oldnum, objnum newnum);
/* delete an object */
void linfdelnum(lindef *lin, objnum objn);
/* copy line records to an array of linfinfo structures */
void linf_copy_linerecs(linfdef *linf, linfinfo *info);
/* debugging echo */
#ifdef DEBUG
# define LINFDEBUG(x) x
#else /* DEBUG */
# define LINFDEBUG(x)
#endif /* DEBUG */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,41 @@
/* 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/tads/tads2/list.h"
#include "glk/tads/tads2/data.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
void lstadv(uchar **lstp, uint *sizp)
{
uint siz;
siz = datsiz(**lstp, (*lstp) + 1) + 1;
assert(siz <= *sizp);
*lstp += siz;
*sizp -= siz;
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,46 @@
/* 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/>.
*
*/
/* List definitions
*
* A TADS run-time list is essentially a packed counted array.
* The first thing in a list is a ushort, which specifies the
* number of elements in the list. The list elements are then
* packed into the list immediately following.
*/
#ifndef GLK_TADS_TADS2_LIST
#define GLK_TADS_TADS2_LIST
#include "glk/tads/tads2/lib.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* advance a list pointer/size pair to the next element of a list */
void lstadv(uchar **lstp, uint *sizp);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,196 @@
/* 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/tads/tads2/ltk.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*--------------------------------- ltkini ---------------------------------*/
/*
* ltkini - allocate and INItialize toolkit context.
*/
void ltkini(unsigned short heapsiz) {
}
/*--------------------------------- ltkfre ---------------------------------*/
/*
* ltkfre - FREe toolkit context.
*/
void ltkfre() {
}
/*------------------------------ ltk_suballoc ------------------------------*/
/*
* ltk_suballoc - SUB ALLOCate memory from heap segment.
*/
void *ltk_suballoc(size_t siz) {
return ltk_alloc(siz);
}
/*---------------------------- ltk_sigsuballoc -----------------------------*/
/*
* ltk_sigsuballoc - allocate from heap, signal failure.
*/
void *ltk_sigsuballoc(errcxdef *errcx, size_t siz) {
void *ptr; /* ptr to allocated memory */
/* allocate the memory */
if (!(ptr = ltk_suballoc(siz)))
{
/* signal an error */
errsigf(errcx, "LTK", 0);
}
/* return the memory */
return(ptr);
}
/*------------------------------ ltk_subfree -------------------------------*/
/*
* ltk_subfree - FREe memory allocated by ltk_suballoc
*/
void ltk_subfree(void *ptr) {
free(ptr);
}
/*------------------------------- ltk_alloc --------------------------------*/
/*
* ltk_alloc - Allocate a block of memory
*/
void *ltk_alloc(size_t siz) {
byte *data = (byte *)malloc(siz);
Common::fill(data, data + siz, 0);
return data;
}
/*------------------------------ ltk_realloc ------------------------------*/
void *ltk_realloc(void *ptr, size_t siz) {
return realloc(ptr, siz);
}
/*------------------------------ ltk_sigalloc ------------------------------*/
/*
* ltk_sigalloc - allocate permanent global memory, and signal on failure.
*/
void *ltk_sigalloc(errcxdef *errcx, size_t siz) {
void *ptr; /* pointer to allocated memory */
if (!(ptr = ltk_alloc(siz))) {
/* signal error */
errsigf(errcx, "LTK", 0);
}
/* return a ptr to the allocated memory */
return ptr;
}
/*-------------------------------- ltk_free --------------------------------*/
/*
* ltk_free - free a block of memory allocated by ltk_alloc. This
* takes the memory handle stashed just behind the allocation, and frees
* the block of memory.
*/
void ltk_free(void *mem) {
free(mem);
}
/*------------------------------- ltk_errlog -------------------------------*/
/*
* ltk_errlog - ERRor LOGging function. Logs an error from the LER
* system.
*/
void ltk_errlog(void *ctx, const char *fac, int errCode, int argc, erradef *argv) {
char buf[128]; /* formatted error buffer */
char msg[128]; /* message buffer */
/* $$$ filter out error #504 $$$ */
if (errCode == 504) return;
/* get the error message into msg */
errmsg((errcxdef *)ctx, msg, sizeof(msg), errCode);
/* format the error message */
errfmt(buf, (int)sizeof(buf), msg, argc, argv);
/* display a dialog box containing the error message */
ltk_dlg("Error", buf);
}
/*-------------------------------- ltk_dlg ---------------------------------*/
/*
* ltk_dlg - DiaLog. Puts the given message in a dialog box.
*/
void ltk_dlg(const char *title, const char *msg, ...) {
va_list argp; /* printf args */
char inbuf[80]; /* input buffer */
char outbuf[160]; /* allow inbuf to double in size */
/* clip the input message, if necessary */
strncpy(inbuf, msg, sizeof(inbuf));
inbuf[sizeof(inbuf) - 1] = '\0';
/* get the printf args, build the message, and display it */
va_start(argp, msg);
Common::vsprintf_s(outbuf, inbuf, argp);
/* display the message */
error("%s", outbuf);
}
/*-------------------------------- ltk_beep --------------------------------*/
/*
* ltk_beep - BEEP the PC's speaker.
*/
void ltk_beep() {
}
/*------------------------------ ltk_beg_wait ------------------------------*/
/*
* ltk_beg_wait - Put up hourglass prompt.
*/
void ltk_beg_wait() {
}
/*------------------------------ ltk_end_wait ------------------------------*/
/*
* ltk_end_wait - Put up normal prompt.
*/
void ltk_end_wait() {
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,132 @@
/* 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/>.
*
*/
/* Library porting Tool Kit
*
* These are generic definitions which should be applicable to any system.
*/
#ifndef GLK_TADS_TADS2_LTK
#define GLK_TADS_TADS2_LTK
#include "glk/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* ltkini - allocate and INItialize ltk context. 'heapsiz' is the
* requested size for the local heap. Returns 0 if the request cannot be
* satisfied.
*/
extern void ltkini(unsigned short heapsiz);
/*
* ltkfre - FREe ltk context.
*/
extern void ltkfre();
/*
* ltk_dlg - DiaLoG. Present user with informational dialog message.
* 'title' specifies the title to use in the dialog box, 'msg' is the
* text message, which may contain printf-style formatting.
* printf-style arguments must be passed in also, if the message
* requires them.
*/
extern void ltk_dlg(const char *title, const char *msg, ...);
/*
* ltk_errlog - Error logging function for LER routines.
*/
extern void ltk_errlog(void *ctx, const char *fac, int errCode, int agrc, erradef *argv);
/*
* ltk_alloc - ALLOCate permanent global memory. Returns 0 if the
* request cannot be satisfied.
*/
extern void *ltk_alloc(size_t siz);
/* ltk_realloc - reallocate memory; analogous to realloc() */
extern void *ltk_realloc(void *ptr, size_t siz);
/*
* ltk_sigalloc - ALLOCate permanent global memory, signals error on
* failure.
*/
extern void *ltk_sigalloc(struct errcxdef *errcx, size_t siz);
/*
* ltk_free - FREE memory allocated using ltk_alloc.
*/
extern void ltk_free(void *ptr);
/*
* ltk_suballoc - SUB-ALLOCate memory from user heap. Returns 0 if the
* request cannot be satisfied.
*/
extern void *ltk_suballoc(size_t siz);
/*
* ltk_sigsuballoc - SUB-ALLOCate memory from user heap, signals error
* on failure.
*/
extern void *ltk_sigsuballoc(struct errcxdef *errcx, size_t siz);
/*
* ltk_subfree - SUBsegment FREE. Frees memory allocated by
* ltk_suballoc.
*/
extern void ltk_subfree(void *ptr);
/*
* ltk_beep - BEEP the user.
*/
extern void ltk_beep(void);
/*
* ltk_beg_wait - signal that the user needs to wait.
*/
extern void ltk_beg_wait(void);
/*
* ltk_end_wait - end the waiting period .
*/
extern void ltk_end_wait(void);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,402 @@
/* 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/>.
*
*/
/*
* Memory cache manager
*/
#ifndef GLK_TADS_TADS2_MEMORY_CACHE
#define GLK_TADS_TADS2_MEMORY_CACHE
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/memory_cache_loader.h"
#include "glk/tads/tads2/memory_cache_swap.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* if the os layer doesn't want long mcm macros, we don't either */
#ifdef OS_MCM_NO_MACRO
# define MCM_NO_MACRO
#endif
/*
* mcmon - cache object number. Each object in the cache is referenced
* by an object number, which is a number of this type.
*/
typedef ushort mcmon;
/* invalid object number - used to indicate N/A or no object */
#define MCMONINV ((mcmon)~0)
/* invalid page number */
#define MCMPINV ((uint)~0)
union mcmodefloc {
mcsseg mcmolocs; /* swap segment handle */
mclhd mcmolocl; /* load file object handle */
};
/*
* mcmodef - cache object definition. Each cache object is managed by
* this structure. Pointers for a doubly-linked list are provided;
* these are used to maintain a recent-use list for objects in memory,
* and to maintain a free list for cache object entries not in use. A
* set of flag bits is provided, as is the size of the object and its
* location (which is a memory pointer for objects in memory, a swap
* file handle for swapped-out objects, or a load-file handle for
* objects that are unloaded).
*/
struct mcmodef {
uchar *mcmoptr; /* memory pointer (if object is present) */
mcmodefloc mcmoloc; /* object's external location */
#define mcmoswh mcmoloc.mcmolocs
#define mcmoldh mcmoloc.mcmolocl
mcmon mcmonxt; /* next object in this list */
mcmon mcmoprv; /* previous object in this list */
ushort mcmoflg; /* flags for this cache object */
#define MCMOFDIRTY 0x01 /* object has been written */
#define MCMOFNODISC 0x02 /* not in load file (can't be discarded) */
#define MCMOFLOCK 0x04 /* object is locked */
#define MCMOFPRES 0x08 /* object is present in memory */
#define MCMOFLRU 0x10 /* object is in LRU chain */
#define MCMOFPAGE 0x20 /* object is a cache manager page */
#define MCMOFNOSWAP 0x40 /* object cannot be swapped out */
#define MCMOFFREE 0x80 /* entry refers to a free block of memory */
#define MCMOFREVRT 0x100 /* call revert callback upon loading */
uchar mcmolcnt; /* lock count */
ushort mcmosiz; /* size of the object */
};
/* heap header - allocate one of these in each heap */
struct mcmhdef {
mcmhdef *mcmhnxt; /* next heap in this chain */
};
/* GLOBAL cache manager context: tracks cache manager state */
struct mcmcx1def {
mcmodef **mcmcxtab; /* page table for the cache */
errcxdef *mcmcxerr; /* error handling context */
mcmhdef *mcmcxhpch; /* heap chain pointer */
mcscxdef mcmcxswc; /* swap manager context */
mclcxdef mcmcxldc; /* loader context */
ulong mcmcxmax; /* maximum amount of actual heap we can ever alloc */
mcmon mcmcxlru; /* least recently used object still in memory */
mcmon mcmcxmru; /* most recently used object */
mcmon mcmcxfre; /* head of free list */
mcmon mcmcxunu; /* head of unused list */
ushort mcmcxpage; /* last page table slot used */
ushort mcmcxpgmx; /* maximum number of pages we can allocate */
void (*mcmcxcsw)(mcmcx1def *, mcmon, mcsseg, mcsseg);
/* change swap handle in object to new swap handle */
};
/* CLIENT cache manager context: used by client to request mcm services */
struct mcmcxdef {
mcmcx1def *mcmcxgl; /* global cache manager context */
uint mcmcxflg; /* flags */
uint mcmcxmsz; /* maximum size of mapping table */
void (*mcmcxldf)(void *ctx, mclhd handle, uchar *ptr,
ushort siz); /* callback to load objects */
void *mcmcxldc; /* context for load callback */
void (*mcmcxrvf)(void *ctx, mcmon objn); /* revert object */
void *mcmcxrvc; /* context for revert callback */
mcmon *mcmcxmtb[1]; /* mapping table */
};
/* context flags */
#define MCMCXF_NO_PRP_DEL 0x0001 /* PRPFDEL is invalid in this game file */
/* convert from a client object number to a global object number */
/* mcmon mcmc2g(mcmcxdef *ctx, mcmon objn); */
#define mcmc2g(ctx, objn) ((ctx)->mcmcxmtb[(objn)>>8][(objn)&255])
/*
* FREE LIST: this is a list, headed by context->mcmcxfre and chained
* forward and back by mcmonxt and mcmoprv, consisting of free memory
* blocks. These refer to blocks in the heap that are not used by any
* client objects.
*/
/*
* UNUSED LIST: this is a list, headed by context->mcmcxunu and chained
* forward by mcmonxt (not back, because it's never necessary to take
* anything out of the list except at the head, nor to search the list
* backwards), of unused cache object entries. These entries are not
* associated with any client object or with any heap memory. This list
* is used to get a new cache object header, and deleted cache objects
* are placed onto this list.
*/
/*
* LRU LIST: this is a list of in-memory blocks in ascending order of
* recency of use by the client. Each time a client unlocks a block,
* the block is moved to the most recent position in the list (the end
* of the list). To make it fast to add a new object, we keep a pointer
* to the end of the list as well as to the beginning. The start of the
* list is at context->mcmcxlru, and is the least recently unlocked
* block still in memory. The end of the list is at context->mcmcxmru,
* and is the most recently unlocked block.
*/
/*
* initialize the cache manager, returning a context for cache manager
* operations; a null pointer is returned if insufficient heap memory is
* available for initialization. The 'max' argument specifies the
* maximum amount of actual low-level heap memory that the cache manager
* can ever allocate on behalf of this context (of course, it can
* overcommit the heap through swapping). If 'max' is less than the
* size of a single heap allocation, it is adjusted upwards to that
* minimum.
*/
mcmcx1def *mcmini(ulong max, uint pages, ulong swapsize,
osfildef *swapfp, char *swapfilename, errcxdef *errctx);
/* terminate the cache manager - frees the structure and all cache memory */
void mcmterm(mcmcx1def *ctx);
/* allocate a client context */
mcmcxdef *mcmcini(mcmcx1def *globalctx, uint pages,
void (*loadfn)(void *, mclhd, uchar *, ushort),
void *loadctx,
void (*revertfn)(void *, mcmon), void *revertctx);
/* terminate a client context - frees the structure memory */
void mcmcterm(mcmcxdef *ctx);
/*
* Lock a cache object, bringing it into memory if necessary. Returns
* a pointer to the memory containing the object. A null pointer is
* returned in case of error. The object remains fixed in memory at the
* returned location until unlocked. Locks are stacked; that is, if
* an object is locked twice in succession, it needs to be unlocked
* twice in succession before it is actually unlocked.
*/
#ifdef MCM_NO_MACRO
uchar *mcmlck(mcmcxdef *ctx, mcmon objnum);
#else /* MCM_NO_MACRO */
/* uchar *mcmlck(mcmcxdef *ctx, mcmon objnum); */
#define mcmlck(ctx,num) \
((mcmobje(ctx,num)->mcmoflg & MCMOFPRES ) ? \
((mcmobje(ctx,num)->mcmoflg|=MCMOFLOCK), \
++(mcmobje(ctx,num)->mcmolcnt), mcmobje(ctx,num)->mcmoptr) \
: mcmload(ctx,num))
#endif /* MCM_NO_MACRO */
/*
* Unlock a cache object, allowing it to be moved and swapped.
* Unlocking an object moves it to the end (i.e., most recently used)
* position on the LRU chain, making it the least favorable to swap out
* or discard. This happens at unlock time (rather than lock time)
* because presumably the client has been using the object the entire
* time it was locked. For this reason, and to keep memory unfragmented
* as much as possible, objects should not be kept locked except when
* actually in use. Note that locks nest; if an object is locked three
* times without an intervening unlock, it must be unlocked three times
* in a row. An object can be unlocked even if it's not locked; doing
* so has no effect.
*/
#ifdef MCM_NO_MACRO
void mcmunlck(mcmcxdef *ctx, mcmon objnum);
#else /* MCM_NO_MACRO */
/* void mcmunlck(mcmcxdef *ctx, mcmon objnum); */
#define mcmunlck(ctx,obj) \
((mcmobje(ctx,obj)->mcmoflg & MCMOFLOCK) ? \
(--(mcmobje(ctx,obj)->mcmolcnt) ? (void)0 : \
((mcmobje(ctx,obj)->mcmoflg&=(~MCMOFLOCK)), \
mcmuse((ctx)->mcmcxgl,mcmc2g(ctx,obj)))) : (void)0)
#endif /* MCM_NO_MACRO */
/*
* Allocate a new cache object. The new object is locked upon return.
* A pointer to the memory for the new object is returned, and
* the object number is returned at *nump. A null pointer is returned
* if the object cannot be allocated.
*/
/* uchar *mcmalo(mcmcxdef *ctx, ushort siz, mcmon *nump); */
#define mcmalo(ctx, siz, nump) mcmalo0(ctx, siz, nump, MCMONINV, FALSE)
/*
* Reserve space for an object, giving it a particular client object
* number. This doesn't actually allocate any space for the object, but
* just sets it up so that it can be loaded by the client when it's
* needed.
*/
void mcmrsrv(mcmcxdef *ctx, ushort siz, mcmon clinum, mclhd loadhd);
/*
* Allocate a new cache object, and associate it with a particular
* client object number. An error is signalled if the client object
* number is already in use.
*/
/* uchar *mcmalonum(mcmcxdef *ctx, ushort siz, mcmon num); */
#define mcmalonum(ctx, siz, num) mcmalo0(ctx, siz, (mcmon *)0, num, FALSE)
/*
* Reallocate an existing object. The object's size is increased
* or reduced according to newsize. The object is locked if it is
* not already, and the address of the object's memory is returned.
* Note that the object can move when reallocated, even if it was
* locked before the call.
*/
uchar *mcmrealo(mcmcxdef *ctx, mcmon objnum, ushort newsize);
/*
* Touch a cache object (rendering it dirty). When an object is
* written, the client must touch it to ensure that the version in
* memory is not discarded. The cache manager attempts to optimize
* activity by not writing objects that can be reconstructed from the
* load or swap file. Touching the object informs the cache manager
* that the object is different from any version it has in a swap or
* load file.
*/
/* void mcmtch(mcmcxdef *ctx, mcmon objnum); */
#define mcmtch(ctx,obj) \
(mcmobje(ctx,obj)->mcmoflg |= MCMOFDIRTY)
/* was: (mcmobje(ctx,obj)->mcmoflg |= (MCMOFDIRTY | MCMOFNODISC)) */
/* get size of a cache manager object - object need not be locked */
/* ushort mcmobjsiz(mcmcxdef *ctx, mcmon objn); */
#define mcmobjsiz(ctx, objn) (mcmobje(ctx, objn)->mcmosiz)
/* determine if object has ever been touched */
/* int mcmobjdirty(mcmcxdef *ctx, mcmon objn); */
#define mcmobjdirty(ctx, objn) \
(mcmobje(ctx, objn)->mcmoflg & (MCMOFDIRTY | MCMOFNODISC))
/* get object's memory pointer - object must be locked for valid result */
/* uchar *mcmobjptr(mcmcxdef *ctx, mcmon objn); */
#define mcmobjptr(ctx, objn) (mcmobje(ctx, objn)->mcmoptr)
/*
* Free an object. The memory occupied by the object is discarded, and
* the object may no longer be referenced.
*/
void mcmfre(mcmcxdef *ctx, mcmon obj);
/*
* "Revert" an object - convert it back to original state. This
* routine just invokes a client callback to do the actual reversion
* work. The callback is called immediately if the object is already
* present in memory, but is deferred until the object is loaded/swapped
* in if the object is not in memory.
*/
/* void mcmrevert(mcmcxdef *ctx, mcmon objn); */
#define mcmrevert(ctx, objn) \
((mcmobje(ctx, objn)->mcmoflg & MCMOFPRES) ? \
((*(ctx)->mcmcxrvf)((ctx)->mcmcxrvc, objn), DISCARD 0) \
: DISCARD (mcmobje(ctx, objn)->mcmoflg |= MCMOFREVRT))
/* get current size of object cache */
ulong mcmcsiz(mcmcxdef *ctx);
/* change an object's swap handle (used by swapper) */
/* void mcmcsw(mcmcx1def *ctx, ushort objn, mcsseg swapn, mcsseg oldswn); */
#define mcmcsw(ctx, objn, swapn, oldswapn) \
((*(ctx)->mcmcxcsw)(ctx, objn, swapn, oldswapn))
/* ------------------------------- PRIVATE ------------------------------- */
/* Unlock an object by its global handle */
#ifdef MCM_NO_MACRO
void mcmgunlck(mcmcx1def *ctx, mcmon objnum);
#else /* MCM_NO_MACRO */
/* void mcmgunlck(mcmcx1def *ctx, mcmon objnum); */
#define mcmgunlck(ctx,obj) \
((mcmgobje(ctx,obj)->mcmoflg & MCMOFLOCK) ? \
(--(mcmgobje(ctx,obj)->mcmolcnt) ? (void)0 : \
((mcmgobje(ctx,obj)->mcmoflg&=(~MCMOFLOCK)), mcmuse(ctx,obj))) : \
(void)0)
#endif /* MCM_NO_MACRO */
/* real memory allocator; clients use cover macros */
uchar *mcmalo0(mcmcxdef *ctx, ushort siz, mcmon *nump, mcmon clinum,
int noclitrans);
/* free an object by global object number */
void mcmgfre(mcmcx1def *ctx, mcmon obj);
/* "use" an object (move to most-recent position in LRU chain) */
void mcmuse(mcmcx1def *ctx, mcmon n);
/*
* Load or swap in a cache object which is currently unloaded, locking
* it before returning. Returns a pointer to the memory containing
* the object, or a null pointer in case of error. The object
* remains fixed in memory at the returned location until unlocked.
*/
uchar *mcmload(mcmcxdef *ctx, mcmon objnum);
/*
* Size of each chunk of memory we'll request from the heap manager.
* To cut down on wasted memory from the heap manager, we'll always make
* our requests in this large size, then sub-allocate out of these
* chunks.
*/
#define MCMCHUNK 32768
/*
* number of cache entries in a page - make this a power of 2 to keep
* the arithmetic to find a cache object entry simple
*/
#define MCMPAGECNT 256
/*
* size of a page, in bytes
*/
#define MCMPAGESIZE (MCMPAGECNT * sizeof(mcmodef))
/*
* When allocating memory, and we find a free block satisfying the
* request, we will split the free block if doing so would result in
* enough space in the second block. MCMSPLIT specifies the minimum
* size left over that will allow a split to occur.
*/
#define MCMSPLIT 64
/* get an object cache entyr given a GLOBAL object number */
#define mcmgobje(ctx,num) (&((ctx)->mcmcxtab[(num)>>8][(num)&255]))
/* get an object cache entry given a CLIENT object number */
/* mcmodef *mcmobje(mcmcxdef *ctx, mcmon objnum) */
#define mcmobje(ctx,num) mcmgobje((ctx)->mcmcxgl,mcmc2g(ctx,num))
/* allocate a block that will be locked for its entire lifetime */
void *mcmptralo(mcmcxdef *ctx, ushort siz);
/* free a block allocated with mcmptralo */
void mcmptrfre(mcmcxdef *ctx, void *block);
/* change an object's swap handle */
void mcmcswf(mcmcx1def *ctx, mcmon objn, mcsseg swapn, mcsseg oldswapn);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,50 @@
/* 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/tads/tads2/memory_cache_heap.h"
#include "glk/tads/tads2/error.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* global to keep track of all allocations */
IF_DEBUG(ulong mchtotmem;)
uchar *mchalo(errcxdef *ctx, size_t siz, const char *comment) {
uchar *ret;
VARUSED(comment);
IF_DEBUG(mchtotmem += siz;)
ret = (uchar *)osmalloc(siz);
if (ret)
return(ret);
else {
errsig(ctx, ERR_NOMEM);
NOTREACHEDV(uchar *);
return nullptr;
}
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,59 @@
/* 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/>.
*
*/
/*
* Memory cache heap manager
*
* This is the low-level heap manager, which maintains a list of non-relocatable,
* non-swappable blocks of memory. The cache manager uses the heap manager for
* its basic storage needs.
*/
#ifndef GLK_TADS_TADS2_MEMORY_CACHE_HEAP
#define GLK_TADS_TADS2_MEMORY_CACHE_HEAP
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* Allocate a block of memory; returns pointer to the block.
* An out-of-memory error is signalled if insufficient memory
* is available. The comment is for debugging purposes only.
*/
uchar *mchalo(errcxdef *ctx, size_t siz, const char *comment);
/* allocate a structure */
#define MCHNEW(errctx, typ, comment) \
((typ *)mchalo(errctx, sizeof(typ), comment))
/* free a block of memory */
/* void mchfre(uchar *ptr); */
#define mchfre(ptr) (osfree(ptr))
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,56 @@
/* 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/>.
*
*/
/*
* Memory cache swap manager
*
* The cache swap manager provides swap file services to the memory
* cache manager. The cache manager calls the swap manager to write
* objects to the swap file and read in previously swapped-out objects.
*/
#ifndef GLK_TADS_TADS2_MEMORY_CACHE_LOADER
#define GLK_TADS_TADS2_MEMORY_CACHE_LOADER
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* Loader context
*/
struct mclcxdef {
errcxdef *mclcxerr; /* error handling context */
};
/**
* Loader handle
*/
typedef ulong mclhd; /* essentially a seek address */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,301 @@
/* 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/tads/tads2/memory_cache_swap.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/memory_cache.h"
#include "glk/tads/tads2/memory_cache_heap.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* initialize swapper: allocate memory for swap page table */
void mcsini(mcscxdef *ctx, mcmcx1def *gmemctx, ulong maxsiz,
osfildef *fp, char *swapfilename, errcxdef *errctx) {
uchar *p;
ctx->mcscxtab = (mcsdsdef **)nullptr; /* anticipate failure */
/* allocate space from the low-level heap for page table and one page */
p = mchalo(errctx, ((MCSPAGETAB * sizeof(mcsdsdef *)) + (MCSPAGECNT * sizeof(mcsdsdef))), "mcsini");
/* set up the context with pointers to this chunk */
ctx->mcscxtab = (mcsdsdef **)p;
memset(p, 0, (size_t)(MCSPAGETAB * sizeof(mcsdsdef *)));
p += MCSPAGETAB * sizeof(mcsdsdef *);
ctx->mcscxtab[0] = (mcsdsdef *)p;
/* set up the rest of the context */
ctx->mcscxtop = (ulong)0;
ctx->mcscxmax = maxsiz;
ctx->mcscxmsg = 0;
ctx->mcscxfp = fp;
ctx->mcscxerr = errctx;
ctx->mcscxmem = gmemctx;
/*
* store the swap filename - make a copy so that the caller doesn't
* have to retain the original copy (in case it's on the stack)
*/
if (swapfilename != nullptr) {
size_t ln = strlen(swapfilename) + 1;
ctx->mcscxfname = (char *)mchalo(errctx, ln, "mcsini");
Common::strcpy_s(ctx->mcscxfname, ln, swapfilename);
} else {
ctx->mcscxfname = nullptr;
}
}
/* close the swapper */
void mcsclose(mcscxdef *ctx) {
if (ctx->mcscxtab)
mchfre(ctx->mcscxtab);
}
/*
* Attempt to compact the swap file when it grows too big. The segment
* descriptors are always allocated in increasing seek location within
* the swap file. To compress the file, make each descriptor's
* allocated size equal its used size for each in-use segment, and leave
* free segments at their allocated sizes.
*/
static void mcscompact(mcscxdef *ctx) {
char buf[512];
ulong max;
mcsseg cur_in;
mcsseg cur_out;
mcsdsdef *desc_in;
mcsdsdef *desc_out;
uint siz;
uint rdsiz;
ulong ptr_in;
ulong ptr_out;
max = 0; /* start at offset zero within file */
for (cur_in = cur_out = 0; cur_in < ctx->mcscxmsg; ++cur_in) {
desc_in = mcsdsc(ctx, cur_in);
/*
* If the present descriptor's address is wrong, and the swap
* segment is in use, move the swap segment. If it's not in
* use, we don't need to move it, because we're going to throw
* away the segment entirely.
*/
if (desc_in->mcsdsptr != max && (desc_in->mcsdsflg & MCSDSFINUSE)) {
/* ptr_in is the old location, ptr_out is the new location */
ptr_in = desc_in->mcsdsptr;
ptr_out = max;
/* copy through our buffer */
for (siz = desc_in->mcsdsosz; siz; siz -= rdsiz) {
/* size is whole buffer, or last piece if smaller */
rdsiz = (siz > sizeof(buf) ? sizeof(buf) : siz);
/* seek to old location and get the piece */
osfseek(ctx->mcscxfp, ptr_in, OSFSK_SET);
(void)osfrb(ctx->mcscxfp, buf, (size_t)rdsiz);
/* seek to new location and write the piece */
osfseek(ctx->mcscxfp, ptr_out, OSFSK_SET);
(void)osfwb(ctx->mcscxfp, buf, (size_t)rdsiz);
/* adjust the pointers by the size copied */
ptr_in += rdsiz;
ptr_out += rdsiz;
}
}
/* adjust object descriptor to reflect new location */
desc_in->mcsdsptr = max;
/*
* Make current object's size exact if it's in use. If it's
* not in use, delete the segment altogether.
*/
if (desc_in->mcsdsflg & MCSDSFINUSE) {
desc_in->mcsdssiz = desc_in->mcsdsosz;
max += desc_in->mcsdssiz;
/* copy descriptor to correct position to close any holes */
if (cur_out != cur_in) {
desc_out = mcsdsc(ctx, cur_out);
OSCPYSTRUCT(*desc_out, *desc_in);
/* we need to renumber the corresponding object as well */
mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
cur_out, cur_in);
}
/* we actually wrote this one, so move output pointer */
++cur_out;
} else {
/*
* We need to renumber the corresponding object so that it
* knows there is no swap segment for it any more.
*/
mcmcsw(ctx->mcscxmem, (mcmon)desc_in->mcsdsobj,
MCSSEGINV, cur_in);
}
}
/*
* Adjust the top of the file for our new size, and add the savings
* into the available space counter. Also, adjust the total handle
* count to reflect any descriptors that we've deleted.
*/
ctx->mcscxmax += (ctx->mcscxtop - max);
ctx->mcscxtop = max;
ctx->mcscxmsg = cur_out;
}
/* swap an object out to the swap file */
mcsseg mcsout(mcscxdef *ctx, uint objid, uchar *ptr, ushort siz,
mcsseg oldseg, int dirty) {
mcsdsdef *desc;
mcsdsdef **pagep;
uint i;
uint j;
mcsseg min;
mcsseg cur;
ushort minsiz = 0;
IF_DEBUG(debug(10, "<< mcsout: objid=%d, ptr=%lx, siz=%u, oldseg=%u >>\n",
objid, (unsigned long)ptr, siz, oldseg));
/* see if old segment can be reused */
if (oldseg != MCSSEGINV) {
desc = mcsdsc(ctx, oldseg);
if (!(desc->mcsdsflg & MCSDSFINUSE) /* if old seg is not in use */
&& desc->mcsdsobj == objid /* and it has same object */
&& desc->mcsdssiz >= siz /* and it's still big enough */
&& !dirty) /* and the object in memory hasn't been changed */
{
/* we can reuse the old segment without rewriting it */
desc->mcsdsflg |= MCSDSFINUSE; /* mark segment as in use */
return (oldseg);
}
}
/* look for the smallest unused segment big enough for this object */
for (cur = 0, min = MCSSEGINV, i = 0, pagep = ctx->mcscxtab; cur < ctx->mcscxmsg && i < MCSPAGETAB && *pagep; ++pagep, ++i) {
for (j = 0, desc = *pagep; cur < ctx->mcscxmsg && j < MCSPAGECNT; ++desc, ++j, ++cur) {
if (!(desc->mcsdsflg & MCSDSFINUSE) && desc->mcsdssiz >= siz && (min == MCSSEGINV || desc->mcsdssiz < minsiz)) {
min = cur;
minsiz = desc->mcsdssiz;
if (minsiz == siz)
break; /* exact match - we're done */
}
}
/* quit if we found an exact match */
if (min != MCSSEGINV && minsiz == siz)
break;
}
/* if we found nothing, allocate a new segment if possible */
if (min == MCSSEGINV) {
if (siz > ctx->mcscxmax) {
/* swap file is too big; compact it and try again */
mcscompact(ctx);
if (siz > ctx->mcscxmax)
errsig(ctx->mcscxerr, ERR_SWAPBIG);
}
min = ctx->mcscxmsg;
if (!ctx->mcscxtab[min >> 8]) /* haven't allocate page yet? */
{
ctx->mcscxtab[min >> 8] =
(mcsdsdef *)mchalo(ctx->mcscxerr,
(MCSPAGECNT * sizeof(mcsdsdef)),
"mcsout");
}
/* set up new descriptor */
desc = mcsdsc(ctx, min);
desc->mcsdsptr = ctx->mcscxtop;
desc->mcsdssiz = siz;
desc->mcsdsobj = objid;
/* write out the segment */
mcswrt(ctx, desc, ptr, siz);
desc->mcsdsflg = MCSDSFINUSE;
/* update context information to account for new segment */
ctx->mcscxtop += siz; /* add to top seek offset in file */
ctx->mcscxmax -= siz; /* take size out of quota */
ctx->mcscxmsg++; /* increment last segment allocated */
return (min);
} else {
desc = mcsdsc(ctx, min);
desc->mcsdsobj = objid;
mcswrt(ctx, desc, ptr, siz);
desc->mcsdsflg |= MCSDSFINUSE;
return (min);
}
}
void mcsin(mcscxdef *ctx, mcsseg seg, uchar *ptr, ushort siz) {
mcsdsdef *desc = mcsdsc(ctx, seg);
IF_DEBUG(debug(10, "<< mcsin: seg=%u, ptr=%lx, siz=%d, objid=%u >>\n",
seg, (unsigned long)ptr, siz, desc->mcsdsobj));
assert(seg < ctx->mcscxmsg);
/* can only swap in as much as we wrote */
if (desc->mcsdsosz < siz)
siz = desc->mcsdsosz;
/* seek to and read the segment */
if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
errsig(ctx->mcscxerr, ERR_FSEEK);
if (osfrb(ctx->mcscxfp, ptr, (size_t)siz))
errsig(ctx->mcscxerr, ERR_FREAD);
desc->mcsdsflg &= ~MCSDSFINUSE; /* segment no longer in use */
}
void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl) {
int tries;
desc->mcsdsosz = bufl;
for (tries = 0; tries < 2; ++tries) {
/* attempt to write the object to the swap file */
if (osfseek(ctx->mcscxfp, desc->mcsdsptr, OSFSK_SET))
errsig(ctx->mcscxerr, ERR_FSEEK);
if (!osfwb(ctx->mcscxfp, buf, (size_t)bufl))
return;
/* couldn't write it; compact the swap file */
mcscompact(ctx);
}
/* couldn't write to swap file, even after compacting it */
errsig(ctx->mcscxerr, ERR_FWRITE);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,121 @@
/* 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/>.
*
*/
/*
* Memory cache swap manager
*
* The cache swap manager provides swap file services to the memory
* cache manager. The cache manager calls the swap manager to write
* objects to the swap file and read in previously swapped-out objects.
*/
#ifndef GLK_TADS_TADS2_MEMORY_CACHE_SWAP
#define GLK_TADS_TADS2_MEMORY_CACHE_SWAP
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* Forward declarations */
struct mcmcx1def;
/**
* swap segment descriptor
*/
struct mcsdsdef {
ulong mcsdsptr; /* seek pointer in swap file */
ushort mcsdssiz; /* size of this swap segment */
ushort mcsdsosz; /* size of object written to segment */
uint mcsdsobj; /* client object ID */
ushort mcsdsflg; /* flags */
#define MCSDSFINUSE 0x01 /* segment is in use */
};
/**
* mcsseg - swap segment handle. All swap-file segments are addressed
* through this handle type.
*/
typedef ushort mcsseg;
/**
* Swap manager context
*/
struct mcscxdef {
osfildef *mcscxfp; /* swap file handle */
char *mcscxfname; /* name of swap file */
errcxdef *mcscxerr; /* error handling context */
ulong mcscxtop; /* top of swap file allocated so far */
ulong mcscxmax; /* maximum size of swap file we're allowed */
mcsdsdef **mcscxtab; /* swap descriptor page table */
mcsseg mcscxmsg; /* maximum segment allocated so far */
mcmcx1def *mcscxmem; /* memory manager context */
};
#define MCSSEGINV ((mcsseg)~0) /* invalid segment ID - error indicator */
/* initialize swapper - returns 0 for success, other for error */
void mcsini(struct mcscxdef *ctx, struct mcmcx1def *gmemctx, ulong maxsiz,
osfildef *fp, char *swapfilename, struct errcxdef *errctx);
/* close swapper (release memory areas) */
void mcsclose(struct mcscxdef *ctx);
/**
* Swap an object out. The caller specifies the location and size of
* the object, as well as a unique handle (arbitrary, up to the caller;
* the only requirement is that it be unique among all caller objects
* and always the same for a particular caller's object) and the
* previous swap handle if the object ever had one. If the object is
* not dirty (it hasn't been written since being swapped in), and the
* swap manager hasn't reused the swap slot, the swap manager doesn't
* need to write the memory, since it already has a copy on disk;
* instead, it can just mark the slot as back in use. If the caller
* doesn't wish to take advantage of this optimization, always pass in
* dirty == TRUE, which will force a write regardless of the object ID.
*/
mcsseg mcsout(struct mcscxdef *ctx, uint objid, uchar *objptr,
ushort objsize, mcsseg oldswapseg, int dirty);
/* Swap an object in */
void mcsin(struct mcscxdef *ctx, mcsseg swapseg, uchar *objptr, ushort size);
/* number of page pointers in page table (max number of pages) */
#define MCSPAGETAB 256
/* number of swap descriptors in a page */
#define MCSPAGECNT 256
/* find swap descriptor corresponding to swap segment number */
#define mcsdsc(ctx,seg) (&(ctx)->mcscxtab[(seg)>>8][(seg)&255])
/* write out a swap segment */
void mcswrt(mcscxdef *ctx, mcsdsdef *desc, uchar *buf, ushort bufl);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/* 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_TADS_TADS2_OBJECT
#define GLK_TADS_TADS2_OBJECT
#include "glk/tads/tads.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/memory_cache.h"
#include "glk/tads/tads2/property.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* object number
*/
typedef ushort objnum;
/**
* For non-class objects, we'll leave some space free in the object so
* that a few properties can be added without having to resize the
* object. Class objects will probably never have anything added, so
* there's no need for extra space.
*/
#define OBJEXTRA 64
# define OBJFCLS 0x01 /* object is a class */
/**
* The object structure is actually laid out portably, using unaligned
* 2-byte arrays, stored least significant byte first, for each ushort
* (including the objnum array for the superclasses). The actual
* entries are at these offsets on all machines:
*
* objws 0
* objflg 2
* objnsc 4
* objnprop 6
* objfree 8
* objrst 10
* objstat 12
* objsc[0] 14
* objsc[1] 16
* etc
*
* If the OBJFINDEX flag is set, the object has a property index.
* The index occurs after the last superclass (so it's where the
* property data would go if there were no index), and the property
* data follows. Each index entry consists of a pair of two-byte
* entries: the first is the property number, and the second is
* its offset within the object. For performance reasons, an index
* is only built on a class object -- whenever a property is changed
* within an object, the entire index must be rebuilt, because the
* locations of many properties within the object can be changed by
* a single property change in the object. The index is ordered by
* property number, so it can be searched using a binary search.
* Furthermore, "ignored" properties are excluded from the index;
* only the active instance of a particular property is stored.
* The index must be maintained by all routines that can change
* property information: setp, delp, revert, etc.
*
* Preceding the index table is a two-byte entry that gives the
* offset of the properties. Since the properties immediately
* follow the index, this can be used to deduce how large a space
* is available for the index itself.
*/
typedef uchar objdef;
#define OBJDEFSIZ 14 /* "sizeof(objdef)" - size of object header w/o sc's */
/* object flags */
#define OBJFCLASS 1 /* object is a class */
#define OBJFINDEX 2 /* object has a property index */
#define OBJFMOD 4 /* object has been modified by a newer definition */
/* undo context */
struct objucxdef {
mcmcxdef *objucxmem; /* cache manager context */
errcxdef *objucxerr; /* error context */
ushort objucxsiz; /* size of the undo buffer */
ushort objucxhead; /* head (position of next write) */
ushort objucxtail; /* tail (position of oldest record) */
ushort objucxprv; /* previous head pointer */
ushort objucxtop; /* highest head value written */
void (*objucxcun)(void *ctx, uchar *data);
/* apply a client undo record */
ushort (*objucxcsz)(void *ctx, uchar *data);
/* get size of a client undo record */
void *objucxccx; /* client undo context */
uchar objucxbuf[1]; /* undo buffer */
};
/*
* Undo records are kept in a circular buffer allocated as part of an
* undo context. Offsets within the buffer are kept for the head, tail,
* and previous head records. The head always points to the byte at
* which the next undo record will be written. The previous head points
* to the most recently written undo record; it contains a back link to
* the undo record before that, and so forth back through the entire
* chain. (These reverse links are necessary because undo records vary
* in size depending on the data contained within.) The tail points to
* the oldest undo record that's still in the buffer. Conceptually, the
* head is always "above" the tail in the buffer; since the buffer is
* circular, the tail may have a higher address, but this just means
* that the buffer wraps around at the top. When the head bumps into
* the tail (i.e., the head address is physically below or equal to the
* tail address, and the head is then advanced so that its address
* becomes higher than the tail's), the tail is advanced by discarding
* as many of the least recent undo records as necessary to make room
* for the new head position. When the head and the previous head point
* to the same place, we have no undo records in the buffer.
*/
/**
* The first byte of an undo record specifies what action is to be
* undone. If a property was added, it is undone merely by deleting the
* property. If a property was changed, it is undone by setting the
* property back to its old value. An additional special flag indicates
* a "savepoint." Normally, all changes back to a savepoint will be
* undone.
*/
#define OBJUADD 1 /* a property was added (undo by deleting) */
#define OBJUCHG 2 /* a property was changed (change back to old value) */
#define OBJUSAV 3 /* savepoint marker (no property information) */
#define OBJUOVR 4 /* override original property (set orig to IGNORE) */
#define OBJUCLI 5 /* client undo record (any client data) */
/*
* After the control byte (OBJUxxx), the object number, property
* number, datatype, and data value will follow; some or all of these
* may be omitted, depending on the control byte.
*/
/* get object flags */
#define objflg(o) ((ushort)osrp2(((char *)(o)) + 2))
/* get object flags */
#define objsflg(o, val) oswp2(((char *)(o)) + 2, val)
/* given an object pointer, get a pointer to the first prpdef */
/* prpdef *objprp(objdef *objptr); */
#define objprp(o) ((prpdef *)(objsc(o) + 2*objnsc(o)))
/* given an object pointer, get number of properties in the prpdef */
/* int objnprop(objdef *objptr); */
#define objnprop(o) ((ushort)osrp2(((char *)(o)) + 6))
/* set number of properties */
/* void objsnp(objdef *objptr, int newnum); */
#define objsnp(o,n) oswp2(((char *)(o)) + 6, n)
/* given an object pointer, get offset of free space */
/* int objfree(objdef *objptr); */
#define objfree(o) ((ushort)osrp2(((char *)(o)) + 8))
/* set free space pointer */
/* void objsfree(objdef *objptr, int newfree); */
#define objsfree(o,n) oswp2(((char *)(o)) + 8, n)
/* get number of static properties */
/* ushort objstat(objdef *objptr); */
#define objstat(o) ((ushort)osrp2(((char *)(o)) + 10))
/* set number of static properties */
/* void objsetst(objdef *objptr, int newstat); */
#define objsetst(o,n) oswp2(((char *)(o)) + 10, n)
/* get reset size (size of static properties) */
/* ushort objrst(objdef *objptr); */
#define objrst(o) ((ushort)osrp2(((char *)(o)) + 12))
/* set reset size */
/* void objsetrst(objdef *objptr, uint newrst); */
#define objsetrst(o,n) oswp2(((char *)(o)) + 12, n)
/* given an object pointer, get first superclass pointer */
/* uchar *objsc(objdef *objptr); */
#define objsc(o) (((uchar *)(o)) + OBJDEFSIZ)
/* given an object pointer, get number of superclasses */
/* int objnsc(objdef *objptr); */
#define objnsc(o) ((ushort)osrp2(((char *)(o)) + 4))
/* set number of superclasses */
/* void objsnsc(objdef *objptr, int num); */
#define objsnsc(o,n) oswp2(((char *)(o)) + 4, n)
/* given a prpdef, get the next prpdef */
/* prpdef *objpnxt(prpdef *p); */
#define objpnxt(p) \
((prpdef *)(((uchar *)(p)) + PRPHDRSIZ + prpsize(p)))
/* get pointer to free prpdef */
/* prpdef *objpfre(objdef *objptr); */
#define objpfre(o) ((prpdef *)(((uchar *)(o)) + objfree(o)))
/* given a prpdef and an object pointer, compute the prpdef offset */
/* uint objpofs(objdef *objptr, prpdef *propptr); */
#define objpofs(o,p) ((uint)((p) ? (((uchar *)(p)) - ((uchar *)(o))) : 0))
/* given an object pointer and a property offset, get prpdef pointer */
/* prpdef *objofsp(objdef *objptr, uint propofs); */
#define objofsp(o,ofs) ((prpdef *)((ofs) ? (((uchar *)(o)) + (ofs)) : 0))
/*
* Get the first superclass of an object. If it doesn't have any
* superclasses, return invalid.
*/
objnum objget1sc(mcmcxdef *ctx, objnum objn);
/*
* Get an object's property WITHOUT INHERITANCE. If the object has the
* indicated property set, the byte OFFSET of the prpdef within the
* object is returned. The offset will remain valid until any type of
* operation that sets a property in the object (such as objdelp,
* objsetp, or an undo operation). An offset of zero means that the
* property was not set in the object.
*/
uint objgetp(mcmcxdef *ctx, objnum objn, prpnum prop,
dattyp *typptr);
/*
* Get the *ending* offset of the given property's value, without any
* inheritance. Returns the byte offset one past the end of the
* property's data.
*/
uint objgetp_end(mcmcxdef *ctx, objnum objn, prpnum prop);
/*
* Get a property of an object, either from the object or from a
* superclass (inherited). If the inh flag is TRUE, we do not look
* at all in the object itself, but restrict our search to inherited
* properties only. We return the byte OFFSET of the prpdef within
* the object in which the prpdef is found; the superclass object
* itself is NOT locked upon return, but we will NOT unlock the
* object passed in. If the offset is zero, the property was not
* found. The offset returned is valid until any operation that
* sets a property in the object (such as objdelp, objsetp, or an
* undo operation).
*/
uint objgetap(mcmcxdef *ctx, noreg objnum objn, prpnum prop,
objnum *orn, int inh);
/*
* expand an object by a requested amount, returning a pointer to the
* object's new location if it must be moved. The object will be
* unlocked and relocked by this call. On return, the actual amount
* of space ADDED to the object will be returned.
*/
objdef *objexp(mcmcxdef *ctx, objnum obj, ushort *siz);
/*
* Set an object's property, deleting the original value of the
* property if it existed. If an undo context is provided, write an
* undo record for the change; if the undo context pointer is null, no
* undo information is retained.
*/
void objsetp(mcmcxdef *ctx, objnum obj, prpnum prop,
dattyp typ, const void *val, objucxdef *undoctx);
/*
* Delete a property. If mark_only is true, we'll only mark the
* property as deleted without actually reclaiming its space; this is
* necessary when removing a code property (type DAT_CODE) any time
* other code properties may follow, because p-code is not entirely
* self-relative and thus can't always be relocated within an object.
*/
void objdelp(mcmcxdef *mctx, objnum objn, prpnum prop, int mark_only);
/*
* Set up for emitting code into an object. Writes a property header
* of type 'code', and returns the offset of the next free byte in the
* object. Call objendemt when done. The datatype argument is
* provided so that list generation can be done through the same
* mechanism, since parser lists must be converted to run-time
* lists via the code generator.
*/
uint objemt(mcmcxdef *ctx, objnum objn, prpnum prop, dattyp typ);
/* done emitting code into property, finish setting object info */
void objendemt(mcmcxdef *ctx, objnum objn, prpnum prop, uint endofs);
/*
* Determine if undo records should be kept. Undo records should be
* kept only if a savepoint is present in the undo log. If no savepoint
* is present, adding undo records would be useless, since it will not
* be possible to apply the undo information.
*/
int objuok(objucxdef *undoctx);
/*
* Reserve space in an undo buffer, deleting old records as needed.
* Returns a pointer to the reserved space.
*/
uchar *objures(objucxdef *undoctx, uchar cmd, ushort siz);
/* advance the tail pointer in an undo buffer over the record it points to */
void objutadv(objucxdef *undoctx);
/* apply one undo record, and remove it from undo list */
void obj1undo(mcmcxdef *mctx, objucxdef *undoctx);
/*
* Undo back to the most recent savepoint. If there is no savepoint in
* the undo list, NOTHING will be undone. This prevents reaching an
* inconsistent state in which some, but not all, of the operations
* between two savepoints are undone: either all operations between two
* savepoints will be undone, or none will.
*/
void objundo(mcmcxdef *mctx, objucxdef *undoctx);
/* set an undo savepoint */
void objusav(objucxdef *undoctx);
/* initialize undo context */
objucxdef *objuini(mcmcxdef *memctx, ushort undosiz,
void (*undocb)(void *ctx, uchar *data),
ushort (*sizecb)(void *ctx, uchar *data),
void *callctx);
/* free the undo context - releases memory allocated by objuini() */
void objuterm(objucxdef *undoctx);
/* discard all undo context (for times such as restarting) */
void objulose(objucxdef *undoctx);
/*
* Allocate and initialize a new object. The caller specifies the
* number of superclasses to be allocated, and the amount of space (in
* bytes) for the object's property data. The caller must fill in the
* superclass array. Upon return, the object is allocated and locked,
* and is initialized with no properties. A pointer to the object's
* memory is returned, and *objnptr receives the object number.
*/
objdef *objnew(mcmcxdef *mctx, int sccnt, ushort propspace,
objnum *objnptr, int classflg);
/* initialize an already allocated object */
void objini(mcmcxdef *mctx, int sccnt, objnum objn, int classflg);
/*
* Add space for additional superclasses to an object. The object can
* already have some properties set (if it doesn't, it can just be
* reinitialized).
*/
void objaddsc(mcmcxdef *mctx, int sccnt, objnum objn);
/*
* Delete an object's properties and superclasses. The 'mindel'
* parameter specifies the minimum property number to be deleted.
* Properties below this are considered "system" properties that are not
* to be deleted. This could be used by a development environment to
* store the source for an object as a special system property in the
* object; when the object is recompiled, all of the object's properties
* and superclasses must be deleted except the source property, which is
* retained even after recompilation.
*/
void objclr(mcmcxdef *mctx, objnum objn, prpnum mindel);
/* Build or rebuild an object's property index */
void objindx(mcmcxdef *mctx, objnum objn);
/* set up just-compiled object: mark static part and original properties */
void objcomp(mcmcxdef *mctx, objnum objn, int for_debug);
/* revert an object to original post-compilation state */
void objrevert(void *mctx, mcmon objn);
/* reset 'ignore' flags for a newly reconstructed object */
void objsetign(mcmcxdef *mctx, objnum objn);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,217 @@
/* 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_TADS_TADS2_OPCODE
#define GLK_TADS_TADS2_OPCODE
/*
* Opcode definitions
*
* Lifted largely from the old TADS, since the basic run - time interpreter's
* operation is essentially the same.
*/
#include "glk/tads/tads.h"
#include "glk/tads/tads2/data.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
#define OPCPUSHNUM 1 /* push a constant numeric value */
#define OPCPUSHOBJ 2 /* push an object */
#define OPCNEG 3 /* unary negation */
#define OPCNOT 4 /* logical negation */
#define OPCADD 5 /* addition/list concatenation */
#define OPCSUB 6 /* subtraction/list difference */
#define OPCMUL 7 /* multiplication */
#define OPCDIV 8 /* division */
#define OPCAND 9 /* logical AND */
#define OPCOR 10 /* logical OR */
#define OPCEQ 11 /* equality */
#define OPCNE 12 /* inequality */
#define OPCGT 13 /* greater than */
#define OPCGE 14 /* greater or equal */
#define OPCLT 15 /* less than */
#define OPCLE 16 /* less or equal */
#define OPCCALL 17 /* call a function */
#define OPCGETP 18 /* get property */
#define OPCGETPDATA 19 /* get a property, allowing only data values */
#define OPCGETLCL 20 /* get a local variable's value */
#define OPCPTRGETPDATA 21 /* get property via pointer; only allow data */
#define OPCRETURN 22 /* return without a value */
#define OPCRETVAL 23 /* return a value */
#define OPCENTER 24 /* enter a function */
#define OPCDISCARD 25 /* discard top of stack */
#define OPCJMP 26 /* unconditional jump */
#define OPCJF 27 /* jump if false */
#define OPCPUSHSELF 28 /* push current object */
#define OPCSAY 29 /* implicit printout for doublequote strings */
#define OPCBUILTIN 30 /* call a built-in function */
#define OPCPUSHSTR 31 /* push a string */
#define OPCPUSHLST 32 /* push a list */
#define OPCPUSHNIL 33 /* push the NIL value */
#define OPCPUSHTRUE 34 /* push the TRUE value */
#define OPCPUSHFN 35 /* push the address of a function */
#define OPCGETPSELFDATA 36 /* push property of self; only allow data */
#define OPCPTRCALL 38 /* call function pointed to by top of stack */
#define OPCPTRINH 39 /* inherit pointer to property (stack=prop) */
#define OPCPTRGETP 40 /* get property by pointer (stack=obj,prop) */
#define OPCPASS 41 /* pass to inherited handler */
#define OPCEXIT 42 /* exit turn, but continue with fuses/daemons */
#define OPCABORT 43 /* abort turn, skipping fuses/daemons */
#define OPCASKDO 44 /* ask for a direct object */
#define OPCASKIO 45 /* ask for indirect object and set preposition */
/* explicit superclass inheritance opcodes */
#define OPCEXPINH 46 /* "inherited <superclass>.<property>" */
#define OPCEXPINHPTR 47 /* "inherited <superclass>.<prop-pointer>" */
/*
* Special opcodes for peephole optimization. These are essentially
* pairs of operations that occur frequently so have been collapsed into
* a single instruction.
*/
#define OPCCALLD 48 /* call function and discard value */
#define OPCGETPD 49 /* evaluate property and discard any value */
#define OPCBUILTIND 50 /* call built-in function and discard value */
#define OPCJE 51 /* jump if equal */
#define OPCJNE 52 /* jump if not equal */
#define OPCJGT 53 /* jump if greater than */
#define OPCJGE 54 /* jump if greater or equal */
#define OPCJLT 55 /* jump if less than */
#define OPCJLE 56 /* jump if less or equal */
#define OPCJNAND 57 /* jump if not AND */
#define OPCJNOR 58 /* jump if not OR */
#define OPCJT 59 /* jump if true */
#define OPCGETPSELF 60 /* get property of the 'self' object */
#define OPCGETPSLFD 61 /* get property of 'self' and discard result */
#define OPCGETPOBJ 62 /* get property of a given object */
/* note: differs from GETP in that object is */
/* encoded into the instruction */
#define OPCGETPOBJD 63 /* get property of an object and discard result */
#define OPCINDEX 64 /* get an indexed entry from a list */
#define OPCPUSHPN 67 /* push a property number */
#define OPCJST 68 /* jump and save top-of-stack if true */
#define OPCJSF 69 /* jump and save top-of-stack if false */
#define OPCJMPD 70 /* discard stack and then jump unconditionally */
#define OPCINHERIT 71 /* inherit a property from superclass */
#define OPCCALLEXT 72 /* call external function */
#define OPCDBGRET 73 /* return to debugger (no stack frame leaving) */
#define OPCCONS 74 /* construct list from top two stack elements */
#define OPCSWITCH 75 /* switch statement */
#define OPCARGC 76 /* get argument count */
#define OPCCHKARGC 77 /* check actual arguments against formal count */
#define OPCLINE 78 /* line record */
#define OPCFRAME 79 /* local variable frame record */
#define OPCBP 80 /* breakpoint - replaces an OPCLINE instruction */
#define OPCGETDBLCL 81 /* get debugger local */
#define OPCGETPPTRSELF 82 /* get property pointer from self */
#define OPCMOD 83 /* modulo */
#define OPCBAND 84 /* binary AND */
#define OPCBOR 85 /* binary OR */
#define OPCXOR 86 /* binary XOR */
#define OPCBNOT 87 /* binary negation */
#define OPCSHL 88 /* bit shift left */
#define OPCSHR 89 /* bit shift right */
#define OPCNEW 90 /* create new object */
#define OPCDELETE 91 /* delete object */
/* ----- opcodes 192 and above are reserved for assignment operations ----- */
/*
ASSIGNMENT OPERATIONS
When (opcode & 0xc0 == 0xc0), we have an assignment operation.
(Note that this means that opcodes from 0xc0 up are all reserved
for assignment operations.) The low six bits of the opcode
specify exactly what kind of operation is to be performed:
bits 0-1: specifies destination type:
00 2-byte operand is local number
01 2-byte operand is property to set in obj at tos
10 tos is index, [sp-1] is list to be indexed and set
11 tos is property pointer, [sp-1] is object
bits 2-4: specifies assignment operation:
000 := (direct assignment)
001 += (add tos to destination)
010 -= (subtract tos from destination)
011 *= (multiply destination by tos)
100 /= (divide destination by tos)
101 ++ (increment tos)
110 -- (decrement tos)
111 *reserved*
bit 5: specifies what to do with value computed by assignment
0 leave on stack (implies pre increment/decrement)
1 discard (implies post increment/decrement)
*/
#define OPCASI_MASK 0xc0 /* assignment instruction */
#define OPCASIDEST_MASK 0x03 /* mask to get destination field */
#define OPCASILCL 0x00 /* assign to a local */
#define OPCASIPRP 0x01 /* assign to an object.property */
#define OPCASIIND 0x02 /* assign to an element of a list */
#define OPCASIPRPPTR 0x03 /* assign property via pointer */
#define OPCASITYP_MASK 0x1c /* mask to get assignment type field */
#define OPCASIDIR 0x00 /* direct assignment */
#define OPCASIADD 0x04 /* assign and add */
#define OPCASISUB 0x08 /* assign and subtract */
#define OPCASIMUL 0x0c /* assign and multiply */
#define OPCASIDIV 0x10 /* assign and divide */
#define OPCASIINC 0x14 /* increment */
#define OPCASIDEC 0x18 /* decrement */
#define OPCASIEXT 0x1c /* other - extension flag */
/* extended assignment flags - next byte when OPCASIEXT is used */
#define OPCASIMOD 1 /* modulo and assign */
#define OPCASIBAND 2 /* binary AND and assign */
#define OPCASIBOR 3 /* binary OR and assign */
#define OPCASIXOR 4 /* binary XOR and assign */
#define OPCASISHL 5 /* shift left and assign */
#define OPCASISHR 6 /* shift right and assign */
#define OPCASIPRE_MASK 0x20 /* mask for pre/post field */
#define OPCASIPOST 0x00 /* increment after push */
#define OPCASIPRE 0x20 /* increment before push */
/* some composite opcodes for convenience */
#define OPCSETLCL (OPCASI_MASK | OPCASILCL | OPCASIDIR)
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,44 @@
/* 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/tads/tads2/os.h"
#include "common/system.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
osfildef *oserrop(const char *arg0) {
char buf[128];
if (!os_locate("tadserr.msg", 11, arg0, buf, sizeof(buf)))
return((osfildef *)nullptr);
return(osfoprb(buf, OSFTERRS));
}
long os_get_sys_clock_ms() {
return g_system->getMillis();
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

404
engines/glk/tads/tads2/os.h Normal file
View File

@@ -0,0 +1,404 @@
/* 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/>.
*
*/
/* Portable interfaces to OS-specific functions
*
* This file defines interfaces to certain functions that must be called
* from portable code, but which must have system-specific implementations.
*/
#ifndef GLK_TADS_TADS2_OS
#define GLK_TADS_TADS2_OS
#include "common/system.h"
#include "glk/tads/os_frob_tads.h"
#include "glk/tads/os_glk.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/appctx.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* ------------------------------------------------------------------------ */
/*
* A note on character sets:
*
* Except where noted, all character strings passed to and from the
* osxxx functions defined herein use the local operating system
* representation. On a Windows machine localized to Eastern Europe,
* for example, the character strings passed to and from the osxxx
* functions would use single-byte characters in the Windows code page
* 1250 representation.
*
* Callers that use multiple character sets must implement mappings to
* and from the local character set when calling the osxxx functions.
* The osxxx implementations are thus free to ignore any issues related
* to character set conversion or mapping.
*
* The osxxx implementations are specifically not permitted to use
* double-byte Unicode as the native character set, nor any other
* character set where a null byte could appear as part of a non-null
* character. In particular, callers may assume that null-terminated
* strings passed to and from the osxxx functions contain no embedded
* null bytes. Multi-byte character sets (i.e., character sets with
* mixed single-byte and double-byte characters) may be used as long as
* a null byte is never part of any multi-byte character, since this
* would guarantee that a null byte could always be taken as a null
* character without knowledge of the encoding or context.
*/
/* ------------------------------------------------------------------------ */
/*
* "Far" Pointers. Most platforms can ignore this. For platforms with
* mixed-mode addressing models, where pointers of different sizes can
* be used within a single program and hence some pointers require
* qualification to indicate that they use a non-default addressing
* model, the keyword OSFAR should be defined to the appropriate
* compiler-specific extension keyword.
*
* If you don't know what I'm talking about here, you should just ignore
* it, because your platform probably doesn't have anything this
* sinister. As of this writing, this applies only to MS-DOS, and then
* only to 16-bit implementations that must interact with other 16-bit
* programs via dynamic linking or other mechanisms.
*/
/* ------------------------------------------------------------------------ */
/*
* ANSI C99 exact-size integer types.
*
* C99 defines a set of integer types with exact bit sizes, named intXX_t
* for a signed integer with XX bits, and uintXX_t for unsigned. XX can be
* 8, 16, 32, or 64. TADS uses the 16- and 32-bit sizes, so each platform
* is responsible for defining the following types:
*
*. int16_t - a signed integer type storing EXACTLY 16 bits
*. uint16_t - an unsigned integer type storing EXACTLY 16 bits
*. int32_t - a signed integer type storing EXACTLY 32 bits
*. uint32_t - an unsigned integer type storing EXACTLY 32 bits
*
* Many modern compilers provide definitions for these types via the
* standard header stdint.h. Where stdint.h is provided, the platform code
* can merely #include <stdint.h>.
*
* For compilers where stdint.h isn't available, you must provide suitable
* typedefs. Note that the types must be defined with the exact bit sizes
* specified; it's not sufficient to use a bigger type, because we depend
* in some cases on overflow and sign extension behavior at the specific
* bit size.
*/
/* ------------------------------------------------------------------------ */
/*
* Thread-local storage (TLS).
*
* When TADS is compiled with threading support, it requires some variables
* to be "thread-local". This means that the variables have global scope
* (so they're not stored in "auto" variables on the stack), but each
* thread has a private copy of each such variable.
*
* Nearly all systems that support threads also support thread-local
* storage. Like threading support itself, though, TLS support is at
* present implemented only in non-portable OS APIs rather than standard C
* language features. TLS is a requirement if TADS is compiled with
* threading, but it's not needed for non-threaded builds. TADS only
* requires threading at present (version 3.1) for its network features;
* since these features are optional, systems that don't have threading and
* TLS support will simply need to disable the network features, which will
* allow all of the threading and TLS definitions in osifc to be omitted.
*
* There appear to be two common styles of TLS programming models. The
* first provides non-standard compiler syntax for declarative creation of
* thread-local variables. The Microsoft (on Windows) and Gnu compilers
* (on Linux and Unix) do this: they provide custom storage class modifiers
* for declaring thread locals (__declspec(thread) for MSVC, __thread for
* gcc). Compilers that support declarative thread locals handle the
* implementation details through code generation, so the program merely
* needs to add the special TLS storage class qualifier to an otherwise
* ordinary global variable declaration, and then can access the thread
* local as though it were an ordinary global.
*
* The second programming model is via explicit OS API calls to create,
* initialize, and access thread locals. pthreads provides such an API, as
* does Win32. In fact, when you use the declarative syntax with MSVC or
* gcc, the compiler generates the appropriate API calls, but the details
* are transparent to the program; in contrast, when using pthreads
* directly, the program must actively call the relevant APIs.
*
* It's probably the case that every system that has compiler-level support
* for declarative thread local creation also has procedural APIs, so the
* simplest way to abstract the platform differences would be to do
* everything in terms of APIs. However, it seems likely that compilers
* with declarative syntax might be able to generate more efficient code,
* since optimizers always benefit from declarative information. So we'd
* like to use declarative syntax whenever it's available, but fall back on
* explicit API calls when it's not. So our programming model is a union
* of the two styles:
*
* 1. For each thread local, declare the thread local:
*. OS_DECL_TLS(char *, my_local);
*
* 2. At main program startup (for the main thread only), initialize each
* thread local:
*. os_tls_create(my_local);
*
* 3. Never get or set the value of a thread local directly; instead, use
* the get/set functions:
*. char *x = os_tls_get(char *, my_local);
*. os_tls_set(my_local, "hello");
*
* One key feature of this implementation is that each thread local is
* stored as a (void *) value. We do it this way to allow a simple direct
* mapping to the pthreads APIs, since that's going to be the most common
* non-declarative implementation. This means that a thread local variable
* can contain any pointer type, but *only* a pointer type. The standard
* pattern for dealing with anything more ocmplex is the same as in
* pthreads: gather up the data into a structure, malloc() an instance of
* that structure at entry to each thread (including the main thread), and
* os_tls_set() the variable to contain a pointer to that structure. From
* then on, use os_tls_set(my_struct *, my_local)->member to access the
* member variables in the structure. And finally, each thread must delete
* the structure at thread exit.
*/
/*
*
* Declare a thread local.
*
* - For compilers that support declarative TLS variables, the local OS
* headers should use the compiler support by #defining OS_DECL_TLS to the
* appropriate local declarative keyword.
*
* - For systems without declarative TLS support but with TLS APIs, the
* global declared by this macro actually stores the slot ID (what pthreads
* calls the "key") for the variable. This macro should therefore expand
* to a declaration of the appropriate API type for a slot ID; for example,
* on pthreads, #define OS_DECL_TLS(t, v) pthread_key_t v.
*
* - For builds with no thread support, simply #define this to declare the
* variable as an ordinary global: #define OS_DECL_TLS(t, v) t v.
*/
/* #define OS_DECL_TLS(typ, varname) __thread typ varname */
/*
* For API-based systems without declarative support in the compiler, the
* main program startup code must explicitly create a slot for each thread-
* local variable by calling os_tls_create(). The API returns a slot ID,
* which is shared among threads and therefore can be stored in an ordinary
* global variable. OS_DECL_TLS will have declared the global variable
* name in this case as an ordinary global of the slot ID type. The
* os_tls_create() macro should therefore expand to a call to the slot
* creation API, storing the new slot ID in the global.
*
* Correspondingly, before the main thread exits, it should delete each
* slot it created, b calling os_tls_delete().
*
* For declarative systems, there's no action required here, so these
* macros can be defined to empty.
*/
/* #define os_tls_create(varname) pthread_key_create(&varname, NULL) */
/* #define os_tls_delete(varname) pthread_key_delete(varname) */
/*
* On API-based systems, each access to get or set the thread local
* requires an API call, using the slot ID stored in the actual global to
* get the per-thread instance of the variable's storage.
*. #define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
*. #define os_tls_set(varname, val) pthread_setspecific(varname, val)
*
* On declarative systems, the global variable itself is the thread local,
* so get/set can be implemented as direct access to the variable.
*. #define os_tls_get(typ, varname) varname
*. #define os_tls_set(varname, val) varname = (val)
*/
/*
* Common TLS definitions - declarative thread locals
*
* For systems with declarative TLS support in the compiler, the OS header
* can #define OS_DECLARATIVE_TLS to pick up suitable definitions for the
* os_tls_xxx() macros. The OS header must separately define OS_DECL_TLS
* as appropriate for the local system.
*/
#ifdef OS_DECLARATIVE_TLS
#define os_tls_create(varname)
#define os_tls_delete(varname)
#define os_tls_get(typ, varname) varname
#define os_tls_set(varname, val) varname = (val)
#endif
/*
* Common TLS definitions - pthreads
*
* For pthreads systems without declarative TLS support in the compiler,
* the OS header can simply #define OS_PTHREAD_TLS to pick up the standard
* definitions below.
*/
#ifdef OS_PTHREAD_TLS
#include <pthread.h>
#define OS_DECL_TLS(typ, varname) pthread_key_t varname
#define os_tls_create(varname) pthread_key_create(&varname, NULL)
#define os_tls_delete(varname) pthread_key_delete(varname)
#define os_tls_get(typ, varname) ((typ)pthread_getspecific(varname))
#define os_tls_set(varname, val) pthread_setspecific(varname, val)
#endif
/* ------------------------------------------------------------------------ */
/*
* OS main entrypoint
*/
int os0main(int oargc, char **oargv,
int (*mainfn)(int, char **, char *),
const char *before, const char *config);
/*
* new-style OS main entrypoint - takes an application container context
*/
int os0main2(int oargc, char **oargv,
int (*mainfn)(int, char **, appctxdef *, char *),
const char *before, const char *config,
appctxdef *appctx);
/* open the error message file for reading */
osfildef *oserrop(const char *arg0);
/* ------------------------------------------------------------------------ */
/*
* Special "switch" statement optimization flags. These definitions are
* OPTIONAL - if a platform doesn't provide these definitions, suitable
* code that's fully portable will be used.
*
* On some compilers, the performance of a "switch" statement can be
* improved by fully populating the switch with all possible "case"
* values. When the compiler can safely assume that every possible "case"
* value is specifically called out in the switch, the compiler can
* generate somewhat faster code by omitting any range check for the
* controlling expression of the switch: a range check is unnecessary
* because the compiler knows that the value can never be outside the
* "case" table.
*
* This type of optimization doesn't apply to all compilers. When a given
* platform's compiler can be coerced to produce faster "switch"
* statements, though, there might be some benefit in defining these
* symbols according to local platform rules.
*
* First, #define OS_FILL_OUT_CASE_TABLES if you want this type of switch
* optimization at all. This symbol is merely a flag, so it doesn't need
* a value - #defining it is enough to activate the special code. If you
* don't define this symbol, then the code that cares about this will
* simply generate ordinary switches, leaving holes in the case table and
* using "default:" to cover them. If the platform's osxxx.h header does
* #define OS_FILL_OUT_CASE_TABLES, then the portable code will know to
* fill out case tables with all possible alternatives where it's possible
* and potentially beneficial to do so.
*
* Second, if you #define OS_FILL_OUT_CASE_TABLES, you MUST ALSO #define
* OS_IMPOSSIBLE_DEFAULT_CASE. The value for this symbol must be some
* code to insert into a "switch" statement at the point where a
* "default:" case would normally go. On some compilers, special
* extensions allow the program to explicitly indicate within a switch
* that all possible cases are covered, so that the compiler doesn't have
* to be relied upon to infer this for itself (which would be impossible
* for it to do in many cases anyway). You can use an empty definition
* for this symbol if your compiler doesn't have any special construct for
* indicating a fully-populated case table.
*
* Note that *most* switch statements in portable code won't care one way
* or the other about these definitions. There's a time/space trade-off
* in fully populating a switch's case table, so only the most
* time-critical code will bother trying.
*/
/* ------------------------------------------------------------------------ */
/*
* TADS 2 swapping configuration. Define OS_DEFAULT_SWAP_ENABLED to 0
* if you want swapping turned off, 1 if you want it turned on. If we
* haven't defined a default swapping mode yet, turn swapping on by
* default.
*/
#ifndef OS_DEFAULT_SWAP_ENABLED
# define OS_DEFAULT_SWAP_ENABLED 1
#endif
/*
* If the system "long description" (for the banner) isn't defined, make
* it the same as the platform ID string.
*/
#ifndef OS_SYSTEM_LDESC
# define OS_SYSTEM_LDESC OS_SYSTEM_NAME
#endif
/*
* TADS 2 Usage Lines
*
* If the "usage" lines (i.e., the descriptive lines of text describing
* how to run the various programs) haven't been otherwise defined,
* define defaults for them here. Some platforms use names other than
* tc, tr, and tdb for the tools (for example, on Unix they're usually
* tadsc, tadsr, and tadsdb), so the usage lines should be adjusted
* accordingly; simply define them earlier than this point (in this file
* or in one of the files included by this file, such as osunixt.h) for
* non-default text.
*/
#ifndef OS_TC_USAGE
# define OS_TC_USAGE "usage: tc [options] file"
#endif
#ifndef OS_TR_USAGE
# define OS_TR_USAGE "usage: tr [options] file"
#endif
#ifndef OS_TDB_USAGE
# define OS_TDB_USAGE "usage: tdb [options] file"
#endif
/*
* Ports can define a special TDB startup message, which is displayed
* after the copyright/version banner. If it's not defined at this
* point, define it to an empty string.
*/
#ifndef OS_TDB_STARTUP_MSG
# define OS_TDB_STARTUP_MSG ""
#endif
/*
* If a system patch sub-level isn't defined, define it here as zero.
* The patch sub-level is used on some systems where a number of ports
* are derived from a base port (for example, a large number of ports
* are based on the generic Unix port). For platforms like the Mac,
* where the porting work applies only to that one platform, this
* sub-level isn't meaningful.
*/
#ifndef OS_SYSTEM_PATCHSUBLVL
# define OS_SYSTEM_PATCHSUBLVL "0"
#endif
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,356 @@
/* 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/tads/tads2/play.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/file_io.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
void plygo(runcxdef *run, voccxdef *voc, tiocxdef *tio, objnum preinit, char *restore_fname) {
int err;
errcxdef *ec = run->runcxerr;
char filbuf[128];
int first_time;
int noreg inited = FALSE;
NOREG((&inited));
first_time = TRUE;
/*
* Write out the special <?T2> HTML sequence, in case we're on an HTML
* system. This tells the HTML parser to use the parsing rules for
* TADS 2 callers.
*/
outformat("\\H+<?T2>\\H-");
startover:
if (!inited)
{
/* use Me as the format-string actor for preinit and init */
tiosetactor(voc->voccxtio, voc->voccxme);
/*
* Run preinit, if it hasn't been run yet. Note that we only
* do this the first time through. If we come back here via the
* restart function, preinit will already have been run in the
* restart function itself, so we don't need to run it again.
*/
if (first_time)
{
/* make a note that we've been through here once already */
first_time = FALSE;
/* remember the preinit function for later use in restarting */
voc->voccxpreinit = preinit;
/* run the preinit() function */
ERRBEGIN(ec)
{
/* reset the interpreter */
runrst(run);
/* reset the parser */
voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
/* run preinit */
if (preinit != MCMONINV)
runfn(run, preinit, 0);
}
ERRCATCH(ec, err)
{
/* if they restarted, go back and start over */
if (err == ERR_RUNRESTART)
goto startover;
/* resignal the error */
errrse(ec);
}
ERREND(ec);
}
/*
* Run the "init" function. Do NOT run init if we're restoring
* a game directly from the command line AND there's an
* initRestore function defined.
*/
if (restore_fname == nullptr || voc->voccxinitrestore == MCMONINV)
{
ERRBEGIN(ec)
{
/* reset the interpreter */
runrst(run);
/* reset the parser */
voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
/* run init */
runfn(run, (objnum)voc->voccxini, 0);
}
ERRCATCH(ec, err)
{
/* if they restarted, go back and start over */
if (err == ERR_RUNRESTART)
goto startover;
/* resignal the error */
errrse(ec);
}
ERREND(ec);
}
}
/* next time through, we'll need to run init again */
inited = FALSE;
/*
* check for startup parameter file to restore - if there's a
* system-specific parameter file specified, pretend that it was
* specified as the restore file
*/
if (os_paramfile(filbuf))
restore_fname = filbuf;
/* check for a file to restore */
if (restore_fname != nullptr)
{
/*
* Check to see if the game file supports the initRestore
* function. If so, call it to restore the game. If not,
* restore the game directly.
*/
if (voc->voccxinitrestore != MCMONINV)
{
char restore_buf[OSFNMAX*2];
char *src;
char *dst;
/* convert any backslashes to double backslashes */
for (src = restore_fname, dst = restore_buf ;
*src != '\0' && dst + 2 < restore_buf + sizeof(restore_buf) ;
++src)
{
switch(*src)
{
case '\\':
/* it's a backslash - double it */
*dst++ = '\\';
*dst++ = '\\';
break;
default:
/* copy the character as-is */
*dst++ = *src;
}
}
/*
* all the game's initRestore function with the name of
* saved game file to restore as the argument
*/
/* reset the interpreter */
runrst(run);
/* reset the parser */
voc_stk_ini(voc, (uint)VOC_STACK_SIZE);
/* push the game file name and run initRestore */
runpstr(run, restore_buf, dst - restore_buf, 0);
runfn(run, (objnum)voc->voccxinitrestore, 1);
}
else
{
/* restore the game */
os_printz("\n\n[Restoring saved game]\n\n");
err = fiorso(voc, restore_fname);
if (err)
{
char buf[60 + OSFNMAX];
Common::sprintf_s(buf, "\n\nError: unable to restore file \"%s\"\n\n",
restore_fname);
os_printz(buf);
}
}
/* forget the saved game name, in case we restore */
restore_fname = nullptr;
}
/* clear out the redo command buffer */
voc->voccxredobuf[0] = '\0';
/* read and execute commands */
for (;;)
{
char buf[128];
err = 0;
ERRBEGIN(ec)
/* read a new command if there's nothing to redo */
if (!voc->voccxredo)
{
/* reset hidden output so we're showing output */
tioshow(tio);
tioflush(tio);
/* clear the interpreter stack */
runrst(run);
/* read a command */
vocread(voc, MCMONINV, MCMONINV, buf, (int)sizeof(buf), 0);
/* special qa checking */
if (buf[0] == '@')
{
int quiet = FALSE;
char *p;
p = buf + 1;
if (*p == '@')
{
/* turn off MORE mode */
setmore(0);
/* set NONSTOP mode in the OS layer */
os_nonstop_mode(TRUE);
/* skip the extra '@' */
++p;
}
else if (*p == '!')
{
quiet = TRUE;
++p;
}
while (*p != '\0' && t_isspace(*p)) ++p;
if (*p != '\0')
{
/* open the named file */
qasopn(p, quiet);
}
else
{
char fname[256];
/* no file was named - ask the user to select a file */
if (tio_askfile("Read script file:", fname, sizeof(fname),
OS_AFP_OPEN, OSFTCMD) == 0)
qasopn(fname, quiet);
}
goto end_loop;
}
}
/*
* If there's redo in the redo buffer, use it now. If the
* buffer is empty and the redo flag is set, we'll just
* re-execute whatever's in our internal buffer.
*/
if (voc->voccxredo && voc->voccxredobuf[0] != '\0')
{
/* copy the redo buffer into our internal buffer */
Common::strcpy_s(buf, voc->voccxredobuf);
/* we've consumed it now, so clear it out */
voc->voccxredobuf[0] = '\0';
}
/* we've now consumed the redo */
voc->voccxredo = FALSE;
/* clear any pending break that's queued up */
(void)os_break();
/* execute the command */
(void)voccmd(voc, buf, (uint)sizeof(buf));
end_loop:
ERRCATCH(ec, err)
{
if (err != ERR_RUNQUIT
&& err != ERR_RUNRESTART
&& !(err == ERR_RUNABRT && voc->voccxredo))
errclog(ec);
}
ERREND(ec);
/* on interrupt, undo last command (which was partially executed) */
if (err == ERR_USRINT && voc->voccxundo)
{
ERRBEGIN(ec)
objundo(voc->voccxmem, voc->voccxundo);
ERRCATCH(ec, err)
if (err != ERR_NOUNDO && err != ERR_ICUNDO)
errrse(ec);
ERREND(ec)
}
/* if they want to quit, we're done */
if (err == ERR_RUNQUIT)
break;
else if (err == ERR_RUNRESTART)
goto startover;
}
/*
* If we're quitting, give the debugger one last chance at taking
* control. If it just returns, we can go ahead and terminate, but
* if it wants it can restart the game by calling bifrst() as
* normal.
*/
ERRBEGIN(ec)
{
/* clear anything in the debugger stack trace */
run->runcxdbg->dbgcxfcn = 0;
run->runcxdbg->dbgcxdep = 0;
/* tell the debugger the game has exited */
dbguquitting(run->runcxdbg);
}
ERRCATCH(ec, err)
{
switch(err)
{
case ERR_RUNRESTART:
/* they restarted the game - re-enter the play loop */
goto startover;
case ERR_RUNQUIT:
/* quitting - proceed to return as normal */
break;
default:
/* resignal any other error */
errrse(ec);
}
}
ERREND(ec);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,45 @@
/* 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/>.
*
*/
/*
* Implementation of main gameplay loop
*/
#ifndef GLK_TADS_TADS2_PLAY
#define GLK_TADS_TADS2_PLAY
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/tads2/tokenizer.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
void plygo(runcxdef *run, voccxdef *voc,
tiocxdef *tio, objnum preinit, char *restore_fname);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

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/>.
*
*/
#include "glk/tads/tads2/post_compilation.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/os.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
void supcont(void *ctx, objnum obj, prpnum prp) {
// No implementation
}
void supivoc(supcxdef *ctx) {
// No implementation
}
void supfind(errcxdef *ctx, tokthdef *tab, voccxdef *voc,
objnum *preinit, int warnlevel, int casefold) {
// No implementation
}
void suprsrv(supcxdef *sup, void(*bif[])(struct bifcxdef *, int),
toktdef *tab, int fncntmax, int v1compat, char *new_do, int casefold) {
// No implementation
}
void supbif(supcxdef *sup, void(*bif[])(struct bifcxdef *, int), int bifsiz) {
// No implementation
}
void sup_log_undefobj(mcmcxdef *mctx, errcxdef *ec, int err,
char *sym_name, int sym_name_len, objnum objn) {
// No implementation
}
void supivoc1(supcxdef *sup, voccxdef *ctx, vocidef *v, objnum target,
int inh_from_obj, int flags) {
// No implementation
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,90 @@
/* 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/>.
*
*/
/* definitions for post-compilation setup
*/
#ifndef GLK_TADS_TADS2_POST_COMPILATION
#define GLK_TADS_TADS2_POST_COMPILATION
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/tokenizer.h"
#include "glk/tads/tads2/vocabulary.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* setup context */
struct supcxdef {
errcxdef *supcxerr;
mcmcxdef *supcxmem; /* memory manager client context */
voccxdef *supcxvoc; /* player command parsing context */
tokthdef *supcxtab; /* top-level symbol table */
runcxdef *supcxrun; /* execution context */
uchar *supcxbuf; /* space for building a list */
ushort supcxlen; /* size of buffer */
};
/* set up contents list for one object for demand-on-load */
extern void supcont(void *ctx, objnum obj, prpnum prp);
/* set up inherited vocabulary (called before executing game) */
extern void supivoc(supcxdef *ctx);
/* find required objects/functions */
extern void supfind(errcxdef *ctx, tokthdef *tab, voccxdef *voc,
objnum *preinit, int warnlevel, int casefold);
/* set up reserved words */
extern void suprsrv(supcxdef *sup, void (*bif[])(struct bifcxdef *, int),
toktdef *tab, int fncntmax, int v1compat, char *new_do,
int casefold);
/* set up built-in functions without symbol table (for run-time) */
extern void supbif(supcxdef *sup, void (*bif[])(struct bifcxdef *, int),
int bifsiz);
/* log an undefined-object error */
extern void sup_log_undefobj(mcmcxdef *mctx, errcxdef *ec, int err,
char *sym_name, int sym_name_len, objnum objn);
/* set up inherited vocabulary for a particular object */
extern void supivoc1(supcxdef *sup, voccxdef *ctx, vocidef *v, objnum target,
int inh_from_obj, int flags);
/* get name of an object out of symbol table */
extern void supgnam(char *buf, tokthdef *tab, objnum objn);
/* table of built-in functions */
struct supbidef {
char *supbinam; /* name of function */
void (*supbifn)(struct bifcxdef *, int); /* C routine to call */
};
/* external definition for special token table */
//extern tokldef supsctab[];
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,166 @@
/* 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/>.
*
*/
/* property definitions
*
* A property structure must be binary-portable, because properties are
* stored in objects, which must be binary-portable. Hence, the internal
* structure of a property header is not a C structure, but a portable
* sequence of bytes. Multi-byte quantities are stored in Intel format.
*
* property number - 2 bytes
* property datatype - 1 byte
* property size - 2 bytes
* property flags - 1 byte
*
* This header is followed immediately by the property value. For
* convenience, a set of macros is defined to provide access to the
* fields of a property header.
*/
#ifndef GLK_TADS_TADS2_PROPERTY
#define GLK_TADS_TADS2_PROPERTY
#include "glk/tads/tads.h"
#include "glk/tads/tads2/data.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* a property number, used to look up all properties */
typedef ushort prpnum;
typedef uchar prpdef; /* prpdef is just an array of bytes */
#define PRPHDRSIZ 6 /* "sizeof(prpdef)" - size of property header */
/* Macros to provide access to property header entries */
#define prpprop(p) osrp2((uchar *)(p))
#define prptype(p) (*(((uchar *)(p)) + 2))
#define prpsize(p) osrp2((((uchar *)(p)) + 3))
#define prpflg(p) (*(((uchar *)(p)) + 5))
#define prpvalp(p) (((uchar *)(p)) + 6)
#define prpsetprop(p,n) oswp2((uchar *)(p), n)
#define prpsetsize(p,s) oswp2((((uchar *)(p)) + 3), s)
/* property flag values */
#define PRPFORG 0x01 /* property is original startup value */
#define PRPFIGN 0x02 /* ignore this prop (has been changed) */
#define PRPFDEL 0x04 /* property has been permanently deleted */
/**
* Invalid property number - this number will never be used as an actual
* property, so it can be used to signify the lack of a valid property
*/
#define PRP_INVALID 0
/* certain property types are special, and are reserved here */
#define PRP_DOACTION 1 /* doAction property */
/* vocabulary properties - keep these contiguous, and must start at 2 */
#define PRP_VERB 2 /* verb vocabulary property */
#define PRP_NOUN 3 /* noun vocabulary property */
#define PRP_ADJ 4 /* adjective vocabulary property */
#define PRP_PREP 5 /* preposition vocabulary property */
#define PRP_ARTICLE 6 /* article vocabulary property */
#define PRP_PLURAL 7 /* plural vocabulary property */
/* determine if a property is a vocab property */
/* int prpisvoc(prpnum p); */
#define prpisvoc(p) ((p) >= PRP_VERB && (p) <= PRP_PLURAL)
/* more properties... */
#define PRP_SDESC 8
#define PRP_THEDESC 9
#define PRP_DODEFAULT 10
#define PRP_IODEFAULT 11
#define PRP_IOACTION 12
#define PRP_LOCATION 13
#define PRP_VALUE 14
#define PRP_ROOMACTION 15
#define PRP_ACTORACTION 16
#define PRP_CONTENTS 17
#define PRP_TPL 18 /* special built-in TEMPLATE structure */
#define PRP_PREPDEFAULT 19
#define PRP_VERACTOR 20
#define PRP_VALIDDO 21
#define PRP_VALIDIO 22
#define PRP_LOOKAROUND 23
#define PRP_ROOMCHECK 24
#define PRP_STATUSLINE 25
#define PRP_LOCOK 26
#define PRP_ISVIS 27
#define PRP_NOREACH 28
#define PRP_ISHIM 29
#define PRP_ISHER 30
#define PRP_ACTION 31 /* action method */
#define PRP_VALDOLIST 32 /* validDoList */
#define PRP_VALIOLIST 33 /* validIoList */
#define PRP_IOBJGEN 34 /* iobjGen */
#define PRP_DOBJGEN 35 /* dobjGen */
#define PRP_NILPREP 36 /* nilPrep */
#define PRP_REJECTMDO 37 /* rejectMultiDobj */
#define PRP_MOVEINTO 38 /* moveInto */
#define PRP_CONSTRUCT 39 /* construct */
#define PRP_DESTRUCT 40 /* destruct */
#define PRP_VALIDACTOR 41 /* validActor */
#define PRP_PREFACTOR 42 /* preferredActor */
#define PRP_ISEQUIV 43 /* isEquivalent */
#define PRP_ADESC 44
#define PRP_MULTISDESC 45
#define PRP_TPL2 46 /* new-style built-in TEMPLATE structure */
#define PRP_ANYVALUE 47 /* anyvalue(n) - value to use for '#' with ANY */
#define PRP_NEWNUMOBJ 48 /* newnumbered(n) - create new numbered object */
#define PRP_UNKNOWN 49 /* internal property for unknown words */
#define PRP_PARSEUNKNOWNDOBJ 50 /* parseUnknownDobj */
#define PRP_PARSEUNKNOWNIOBJ 51 /* parseUnknownIobj */
#define PRP_DOBJCHECK 52 /* dobjCheck */
#define PRP_IOBJCHECK 53 /* iobjCheck */
#define PRP_VERBACTION 54 /* verbAction */
#define PRP_DISAMBIGDO 55 /* disambigDobj */
#define PRP_DISAMBIGIO 56 /* disambigIobj */
#define PRP_PREFIXDESC 57 /* prefixdesc */
#define PRP_ISTHEM 58 /* isThem */
/* properties used by TADS/Graphic */
#define PRP_GP_PIC 100 /* gp_picture */
#define PRP_GP_NAME 101 /* gp_name */
#define PRP_GP_DEFVERB 102 /* gp_defverb */
#define PRP_GP_ACTIVE 103 /* gp_active */
#define PRP_GP_HOTLIST 104 /* gp_hotlist */
#define PRP_GP_ICON 105 /* gp_icon */
#define PRP_GP_DEFVERB2 106 /* gp_defverb2 */
#define PRP_GP_DEFPREP 107 /* gp_defprep */
#define PRP_GP_HOTID 108 /* gp_hotid */
#define PRP_GP_OVERLAY 109 /* gp_overlay */
#define PRP_GP_HOTX 110 /* gp_hotx */
#define PRP_GP_HOTY 111 /* gp_hoty */
/* highest reserved property number - must match last one above */
#define PRP_LASTRSV 111
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,117 @@
/* 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/tads/tads2/lib.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Globals for the script reader
*/
osfildef *scrfp = (osfildef *)nullptr; /* script file */
int scrquiet = 0; /* flag: true ==> script is NOT shown as read */
int qasopn(char *scrnam, int quiet) {
if (scrfp) return 1; /* already reading from script */
if ((scrfp = osfoprt(scrnam, OSFTCMD)) == nullptr) return 1;
scrquiet = quiet;
return 0;
}
void qasclose() {
/* only close the script file if there's one open */
if (scrfp)
{
osfcls(scrfp);
scrfp = nullptr; /* no more script file */
scrquiet = 0;
}
}
char *qasgets(char *buf, int bufl) {
/* shouldn't be here at all if there's no script file */
if (scrfp == nullptr)
return nullptr;
/* update status line */
runstat();
/* keep going until we find something we like */
for (;;)
{
int c;
/*
* Read the next character of input. If it's not a newline,
* there's more on the same line, so read the rest and see what
* to do.
*/
c = osfgetc(scrfp);
if (c != '\n' && c != '\r')
{
/* read the rest of the line */
if (!osfgets(buf, bufl, scrfp))
{
/* end of file: close the script and return eof */
qasclose();
return nullptr;
}
/* if the line started with '>', strip '\n' and return line */
if (c == '>')
{
int l;
/* remove the trailing newline */
if ((l = strlen(buf)) > 0
&& (buf[l-1] == '\n' || buf[l-1] == '\r'))
buf[l-1] = 0;
/*
* if we're not in quiet mode, echo the command to the
* display
*/
if (!scrquiet)
outformat(buf);
/* flush the current line without adding any blank lines */
outflushn(1);
/* return the command */
return buf;
}
} else if (c == EOF) {
/* end of file - close the script and return eof */
qasclose();
return nullptr;
}
}
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,199 @@
/* 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_TADS_TADS2_REGEX
#define GLK_TADS_TADS2_REGEX
#include "common/array.h"
#include "glk/tads/tads2/error_handling.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* state ID */
typedef int re_state_id;
/* invalid state ID - used to mark null machines */
#define RE_STATE_INVALID ((re_state_id)-1)
/* first valid state ID */
#define RE_STATE_FIRST_VALID ((re_state_id)0)
/* ------------------------------------------------------------------------ */
/*
* Group register structure. Each register keeps track of the starting
* and ending offset of the group's text.
*/
typedef struct _re_group_register
{
const char *start_ofs;
const char *end_ofs;
_re_group_register() : start_ofs(nullptr), end_ofs(nullptr) {}
void clear() {
start_ofs = nullptr;
end_ofs = nullptr;
}
} re_group_register;
/* number of group registers we keep */
#define RE_GROUP_REG_CNT 10
/* ------------------------------------------------------------------------ */
/*
* Denormalized state transition tuple. Each tuple represents the
* complete set of transitions out of a particular state. A particular
* state can have one character transition, or two epsilon transitions.
* Note that we don't need to store the state ID in the tuple, because
* the state ID is the index of the tuple in an array of state tuples.
*/
typedef struct
{
/* the character we must match to transition to the target state */
char ch;
/* the target states */
re_state_id next_state_1;
re_state_id next_state_2;
/* character range match table, if used */
unsigned char *char_range;
/* flags */
unsigned char flags;
} re_tuple;
/*
* Tuple flags
*/
/* this state is the start of a group - the 'ch' value is the group ID */
#define RE_STATE_GROUP_BEGIN 0x02
/* this state is the end of a group - 'ch' is the group ID */
#define RE_STATE_GROUP_END 0x04
/* ------------------------------------------------------------------------ */
/*
* Regular expression compilation context structure. This tracks the
* state of the compilation and stores the resources associated with the
* compiled expression.
*/
typedef struct _re_context
{
/* error context */
errcxdef *errctx;
/* next available state ID */
re_state_id next_state;
/*
* The array of transition tuples. We'll allocate this array and
* expand it as necessary.
*/
re_tuple *tuple_arr;
/* number of transition tuples allocated in the array */
int tuples_alloc;
/* current group ID */
int cur_group;
/* group registers */
re_group_register regs[RE_GROUP_REG_CNT];
/*
* Buffer for retaining a copy of the last string we scanned. We
* retain our own copy of each string, and point the group registers
* into this copy rather than the caller's original string -- this
* ensures that the group registers remain valid even after the
* caller has deallocated the original string.
*/
char *strbuf;
/* length of the string currently in the buffer */
size_t curlen;
/* size of the buffer allocated to strbuf */
size_t strbufsiz;
_re_context() : errctx(nullptr), next_state(0), tuple_arr(nullptr), tuples_alloc(0), cur_group(0), strbuf(nullptr), curlen(0), strbufsiz(0) {}
} re_context;
/* ------------------------------------------------------------------------ */
/*
* Status codes
*/
typedef enum
{
/* success */
RE_STATUS_SUCCESS = 0,
/* compilation error - group nesting too deep */
RE_STATUS_GROUP_NESTING_TOO_DEEP
} re_status_t;
/* ------------------------------------------------------------------------ */
/*
* Initialize the context. The memory for the context structure itself
* must be allocated and maintained by the caller.
*/
void re_init(re_context *ctx, errcxdef *errctx);
/*
* Delete the context - frees structures associated with the context.
* Does NOT free the memory used by the context structure itself.
*/
void re_delete(re_context *ctx);
/*
* Compile an expression and search for a match within the given string.
* Returns the offset of the match, or -1 if no match was found.
*/
int re_compile_and_search(re_context *ctx,
const char *pattern, size_t patlen,
const char *searchstr, size_t searchlen,
int *result_len);
/*
* Compile an expression and check for a match. Returns the length of
* the match if we found a match, -1 if we found no match. This is not
* a search function; we merely match the leading substring of the given
* string to the given pattern.
*/
int re_compile_and_match(re_context *ctx,
const char *pattern, size_t patlen,
const char *searchstr, size_t searchlen);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,374 @@
/* 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/>.
*
*/
/* Definitions for code execution
*
* The preprocessor symbol RUNFAST can be defined if run - time checking
* of stack overflow, stack underflow, and other unusual but potentially
* dangerous conditions is to be turned off.This will result in somewhat
* faster run-time performance, but run - time errors could be disastrous.
*/
#ifndef GLK_TADS_TADS2_RUN
#define GLK_TADS_TADS2_RUN
#include "common/scummsys.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/debug.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/memory_cache.h"
#include "glk/tads/tads2/memory_cache_swap.h"
#include "glk/tads/tads2/opcode.h"
#include "glk/tads/tads2/property.h"
#include "glk/tads/tads2/text_io.h"
#include "glk/tads/tads2/tokenizer.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward declarations */
struct bifcxdef;
/* stack element - the stack is an array of these structures */
struct runsdef {
uchar runstyp; /* type of element */
union {
long runsvnum; /* numeric value */
objnum runsvobj; /* object value */
prpnum runsvprp; /* property number value */
uchar *runsvstr; /* string/list value */
} runsv;
runsdef() : runstyp(0) {
runsv.runsvnum = 0;
}
};
/* external function control structure */
struct runxdef {
char runxnam[TOKNAMMAX + 1]; /* name of external function */
int (*runxptr)(void *); /* pointer to memory containing code */
};
/* external function context structure - passed to user exits */
struct runuxdef {
struct runcxdef osfar_t *runuxctx; /* run-time context */
struct runufdef osfar_t *runuxvec; /* vector of functions */
int runuxargc; /* count of arguments to function */
};
/* external function callback vector */
struct runufdef {
int (osfar_t *runuftyp)(runuxdef *); /* type of top of stack */
long (osfar_t *runufnpo)(runuxdef *); /* pop a number */
uchar *(osfar_t *runufspo)(runuxdef *); /* pop a string */
void (osfar_t *runufdsc)(runuxdef *); /* discard item at top of stack */
void (osfar_t *runufnpu)(runuxdef *, long); /* push a number */
void (osfar_t *runufspu)(runuxdef *, uchar *); /* push alloc'd string */
void (osfar_t *runufcspu)(runuxdef *, char *); /* push a C-string */
uchar *(osfar_t *runufsal)(runuxdef *, int); /* allocate a new string */
void (osfar_t *runuflpu)(runuxdef *, int);/* push DAT_TRUE or DAT_NIL */
};
/* execution context */
struct runcxdef {
errcxdef *runcxerr; /* error management context */
mcmcxdef *runcxmem; /* cache manager context for object references */
runsdef *runcxstk; /* base of interpreter stack */
runsdef *runcxstop; /* top of stack */
runsdef *runcxsp; /* current stack pointer (stack grows upwards) */
runsdef *runcxbp; /* base pointer */
uchar *runcxheap; /* run-time variable-length object heap */
uchar *runcxhp; /* current heap pointer */
uchar *runcxhtop; /* top of heap */
objucxdef *runcxundo; /* undo context */
tiocxdef *runcxtio; /* text I/O context */
void *runcxbcx; /* context for built-in callback functions */
void (**runcxbi)(struct bifcxdef *ctx, int argc);
/* built-in functions */
struct dbgcxdef *runcxdbg; /* debugger context */
struct voccxdef *runcxvoc; /* player command parser context */
void (*runcxdmd)(void *ctx, objnum obj, prpnum prp);
/* demand-loader callback function */
void *runcxdmc; /* demand-loader callback context */
runxdef *runcxext; /* external function array */
int runcxexc; /* count of external functions */
uint runcxlofs; /* offset of last line record encountered */
char *runcxgamename; /* name of the .GAM file */
char *runcxgamepath; /* absolute directory path of .GAM file */
};
/* execute a function, given the function object number */
void runfn(runcxdef *ctx, noreg objnum objn, int argc);
/*
* Execute p-code given a pointer to the code. p is the actual pointer
* to the first byte of code to be executed. self is the object to be
* used for the special 'self' pseudo-object, and target is the object
* whose data are actually being executed. targprop is the property being
* executed; 0 is used for functions.
*/
void runexe(runcxdef *ctx, uchar *p, objnum self, objnum target,
prpnum targprop, int argc);
/* push a value onto the stack */
void runpush(runcxdef *ctx, dattyp typ, runsdef *val);
/* push a value onto the stack that's already in the heap */
void runrepush(runcxdef *ctx, runsdef *val);
/* push a number onto the stack */
void runpnum(runcxdef *ctx, long val);
/* push an object onto the stack */
void runpobj(runcxdef *ctx, objnum obj);
/* push nil */
void runpnil(runcxdef *ctx);
/* push a value onto the stack from a buffer (propdef, list) */
void runpbuf(runcxdef *ctx, int typ, void *val);
/* push a counted-length string onto the stack */
void runpstr(runcxdef *ctx, const char *str, int len, int sav);
/*
* Push a C-style string onto the stack, converting escape codes. If
* the character contains backslashes, newline, or tab characters, we'll
* convert these characters to their escaped equivalent.
*/
void runpushcstr(runcxdef *ctx, const char *str, size_t len, int sav);
/*
* Push a property onto the stack. codepp is a pointer to the caller's
* code pointer, which will be updated if necessary; callobj and
* callofsp are the object and starting offset within the object of the
* code being executed by the caller, which are needed to update
* *codepp. Property 0 is used if a function is being executed. obj
* and prop are the object and property number whose value is to be
* pushed. If 'inh' is TRUE, it means that only a property inherited
* by 'obj' is to be considered; this is used for "pass"/"inherited"
* operations, with the current target object given as 'obj'.
*/
void runpprop(runcxdef *ctx, uchar *noreg *codepp, objnum callobj,
prpnum callprop, noreg objnum obj, prpnum prop, int inh,
int argc, objnum self);
/* top level runpprop, when caller is not executing in an object */
/* void runppr(runcxdef *ctx, objnum obj, prpnum prp, int argc); */
#define runppr(ctx, obj, prp, argc) \
runpprop(ctx, (uchar **)0, (objnum)0, (prpnum)0, obj, prp, FALSE, argc, obj)
/* discard top element on stack */
/* void rundisc(runcxdef *ctx); */
#define rundisc(ctx) (runstkund(ctx), (--((ctx)->runcxsp)))
/* pop the top element on the stack */
/* void runpop(runcxdef *ctx, runsdef *val); */
#define runpop(ctx, v) \
(runstkund(ctx), memcpy(v, (--((ctx)->runcxsp)), (size_t)sizeof(runsdef)))
/* pop a numeric value, signalling an error if not a number */
/* long runpopnum(runcxdef *ctx); */
#define runpopnum(ctx) \
(runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_NUMBER ? \
(runsig(ctx,ERR_REQNUM), (long)0) : \
((ctx)->runcxsp->runsv.runsvnum)))
/* pop an object, signalling an error if not an object */
/* objnum runpopobj(runcxdef *ctx); */
#define runpopobj(ctx) \
(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_OBJECT ? \
(runsig(ctx,ERR_REQVOB), (objnum)0) : \
((ctx)->runcxsp->runsv.runsvobj))
/* pop an object or nil - returns MCMONINV if the value is nil */
#define runpopobjnil(ctx) \
(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp==DAT_OBJECT ? \
((ctx)->runcxsp->runsv.runsvobj) : \
((ctx)->runcxsp->runstyp==DAT_NIL ? MCMONINV : \
(runsig(ctx,ERR_REQVOB), (objnum)0)))
/* pop a list, signalling an error if not a list */
/* uchar *runpoplst(runcxdef *ctx); */
#define runpoplst(ctx) \
(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_LIST ? \
(runsig(ctx,ERR_REQVLS), (uchar *)0) : \
(uchar *)((ctx)->runcxsp->runsv.runsvstr))
/* pop a property number, signalling an error if not a property number */
/* prpnum runpopprp(runcxdef *ctx); */
#define runpopprp(ctx) \
(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_PROPNUM ? \
(runsig(ctx,ERR_REQVPR), (prpnum)0) : \
((ctx)->runcxsp->runsv.runsvprp))
/* pop function pointer */
/* objnum runpopfn(runcxdef *ctx); */
#define runpopfn(ctx) \
((objnum)(runstkund(ctx), ((--(ctx)->runcxsp))->runstyp!=DAT_FNADDR ? \
(runsig(ctx,ERR_REQVFN), (objnum)0) : \
((ctx)->runcxsp->runsv.runsvobj)))
/* pop a string value */
/* char *runpopstr(runcxdef *ctx); */
#define runpopstr(ctx) \
(runstkund(ctx), ((--((ctx)->runcxsp))->runstyp!=DAT_SSTRING ? \
(runsig(ctx,ERR_REQSTR), (uchar *)0) : \
((ctx)->runcxsp->runsv.runsvstr)))
/* pop a logical value - TRUE for DAT_TRUE, FALSE for DAT_NIL */
/* int runpoplog(runcxdef *ctx); */
#define runpoplog(ctx) \
((--((ctx)->runcxsp))->runstyp==DAT_TRUE ? TRUE : \
(ctx)->runcxsp->runstyp==DAT_NIL ? FALSE : \
(runsig(ctx, ERR_REQLOG), 0))
/* get type of top of stack */
/* int runtostyp(runcxdef *ctx); */
#define runtostyp(ctx) (((ctx)->runcxsp - 1)->runstyp)
/* determine if top of stack is logical value (returns TRUE if so) */
/* int runtoslog(runcxdef *ctx); */
#define runtoslog(ctx) \
(runtostyp(ctx) == DAT_TRUE || runtostyp(ctx) == DAT_NIL)
/* convert C logical to TADS logical (TRUE->DAT_TRUE, FALSE->DAT_NIL) */
/* int runclog(int log); */
#define runclog(l) ((l) ? DAT_TRUE : DAT_NIL)
/* compare magnitudes of numbers/strings on top of stack; strcmp-like value */
int runmcmp(runcxdef *ctx);
/* TRUE if items at top of stack are equal, FALSE otherwise */
int runeq(runcxdef *ctx);
/* check for stack underflow */
/* void runstkund(runcxdef *ctx); */
/* check for stack overflow */
/* void runstkovf(runcxdef *ctx); */
/*
* Check to ensure we have enough arguments to pass to a function or method
* call - this simply ensures we have enough data in the current frame.
* This is important because the called function will be able to write to
* our frame. If we don't have enough arguments, we'll push enough 'nil'
* values to meet the need.
*/
#define runcheckargc(ctx, nargc) \
while ((ctx)->runcxsp - (ctx)->runcxbp < *(nargc)) \
runpnil(ctx)
#ifdef RUNFAST
# define runstkovf(ctx) (DISCARD 0)
# define runstkund(ctx) (DISCARD 0)
#else /* RUNFAST */
# define runstkovf(ctx) \
((ctx)->runcxsp >= (ctx)->runcxstop ? (runsig(ctx, ERR_STKOVF), \
DISCARD 0) : DISCARD 0)
# define runstkund(ctx) \
((ctx)->runcxsp == (ctx)->runcxstk ? runsig(ctx, ERR_STKUND), \
DISCARD 0 : DISCARD 0)
#endif /* RUNFAST */
/* reserve space in heap, collecting garbage if necessary */
/* void runhres(runcxdef *ctx, uint siz, uint below); */
#define runhres(ctx, siz, below) \
((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
(runhcmp(ctx, siz, below, (runsdef *)0, (runsdef *)0, (runsdef *)0),\
DISCARD 0))
/* reserve space, with various amounts of saving */
#define runhres1(ctx, siz, below, val1) \
((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
(runhcmp(ctx, siz, below, val1, (runsdef *)0, (runsdef *)0), DISCARD 0))
#define runhres2(ctx, siz, below, val1, val2) \
((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
(runhcmp(ctx, siz, below, val1, val2, (runsdef *)0), DISCARD 0))
#define runhres3(ctx, siz, below, val1, val2, val3) \
((uint)((ctx)->runcxhtop - (ctx)->runcxhp) > (uint)(siz) ? DISCARD 0 : \
(runhcmp(ctx, siz, below, val1, val2, val3), DISCARD 0))
/* garbage collect heap, making sure 'siz' bytes are available afterwards */
void runhcmp(runcxdef *ctx, uint siz, uint below,
runsdef *val1, runsdef *val2, runsdef *val3);
/* determine size of a data item */
int runsiz(runsdef *item);
/* find a sublist within a list, returning pointer to sublist or NULL */
uchar *runfind(uchar *list, runsdef *item);
/* add two runsdef values, returning result in *val */
void runadd(runcxdef *ctx, runsdef *val, runsdef *val2, uint below);
/*
* subtract val2 from val, returning result in *val; return TRUE if
* value changed, FALSE otherwise (this is returned when subtracting
* something from a list that isn't in the list)
*/
int runsub(runcxdef *ctx, runsdef *val, runsdef *val2, uint below);
/* restore code pointer from object.property + offset */
uchar *runcprst(runcxdef *ctx, uint ofs, objnum obj, prpnum prop);
/* leave a stack frame, removing arguments */
/* void runleave(runcxdef *ctx, uint parms); */
#define runleave(ctx, parms) \
(((ctx)->runcxsp = (ctx)->runcxbp), \
((ctx)->runcxbp = (runsdef *)((--((ctx)->runcxsp))->runsv.runsvstr)), \
((ctx)->runcxsp -= (parms)))
/* reset run-time: throw away entire stack and heap */
/* void runrst(runcxdef *ctx); */
#define runrst(ctx) (((ctx)->runcxsp = (ctx)->runcxstk), \
((ctx)->runcxhp = (ctx)->runcxheap), \
dbgrst(ctx->runcxdbg))
/* set up runtime status line display */
void runistat(struct voccxdef *vctx, struct runcxdef *rctx,
struct tiocxdef *tctx);
/* signal a run-time error - allows debugger trapping */
void runsign(runcxdef *ctx, int err);
/* sign a run-time error with zero arguments */
#define runsig(ctx, err) (errargc((ctx)->runcxerr,0),runsign(ctx,err))
/* signal a run-time error with one argument */
#define runsig1(ctx, err, typ, arg) \
(errargv((ctx)->runcxerr,0,typ,arg),errargc((ctx)->runcxerr,1),\
runsign(ctx,err))
/* draw status line */
void runstat();
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,88 @@
/* 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/tads/tads2/error.h"
#include "glk/tads/tads2/os.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/tokenizer.h"
#include "glk/tads/tads2/vocabulary.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
static runcxdef *runctx;
static voccxdef *vocctx;
static tiocxdef *tioctx;
void runstat(void)
{
objnum locobj;
int savemoremode;
/* get the location of the Me object */
runppr(runctx, vocctx->voccxme, PRP_LOCATION, 0);
/* if that's no an object, there's nothing we can do */
if (runtostyp(runctx) != DAT_OBJECT)
{
rundisc(runctx);
return;
}
/* get Me.location */
locobj = runpopobj(runctx);
/* flush any pending output */
outflushn(0);
/* switch to output display mode 1 (status line) */
os_status(1);
/* turn off MORE mode */
savemoremode = setmore(0);
/* call the statusLine method of the current room */
runppr(runctx, locobj, PRP_STATUSLINE, 0);
/* if we're in the status line, make sure the line gets flushed */
if (os_get_status() != 0)
tioputs(tioctx, "\\n");
outflushn(0);
/* restore the previous MORE mode */
setmore(savemoremode);
/* switch to output display mode 0 (main text area) */
os_status(0);
}
void runistat(voccxdef *vctx, runcxdef *rctx, tiocxdef *tctx)
{
runctx = rctx;
vocctx = vctx;
tioctx = tctx;
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,34 @@
/* 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 TADS {
namespace TADS2 {
char G_tads_oem_app_name[] = "GlkTADS";
char G_tads_oem_display_mode[] = "text-only";
char G_tads_oem_dbg_name[] = "tdb";
char G_tads_oem_author[] = "Maintained by ScummVM\n";
int G_tads_oem_copyright_prefix = true;
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,112 @@
/* 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_TADS_TADS2_RUNTIME_APP
#define GLK_TADS_TADS2_RUNTIME_APP
#include "glk/tads/tads2/appctx.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward-declare structure types */
struct runcxdef;
/*
* Run-time version number
*/
#define TADS_RUNTIME_VERSION "2.5.17"
extern char G_tads_oem_app_name[];
extern char G_tads_oem_display_mode[];
extern char G_tads_oem_dbg_name[];
extern char G_tads_oem_author[];
extern int G_tads_oem_copyright_prefix;
/*
* Main run-time subsystem entrypoint. Runs the game specified in the
* argument vector; does not return until the game terminates. The
* application container context is optional; pass null if no context is
* required.
*/
int trdmain(int argc, char **argv, appctxdef *appctx, const char *save_ext);
/*
* Main debugger subsystem entrypoint. Works like trdmain(), but starts
* the game under the debugger.
*/
int tddmain(int argc, char **argv, appctxdef *appctx, const char *save_ext);
/*
* close and delete the swap file
*/
void trd_close_swapfile(struct runcxdef *runctx);
/*
* Define default memory sizes if no one else has.
*/
#ifndef TRD_HEAPSIZ
# define TRD_HEAPSIZ 4096
#endif
#ifndef TRD_STKSIZ
# define TRD_STKSIZ 200
#endif
#ifndef TRD_UNDOSIZ
# define TRD_UNDOSIZ (16 * 1024)
#endif
#ifndef TDD_HEAPSIZ
# define TDD_HEAPSIZ 4096
#endif
#ifndef TDD_STKSIZ
# define TDD_STKSIZ 200
#endif
#ifndef TDD_UNDOSIZ
# define TDD_UNDOSIZ (16 * 1024)
#endif
#ifndef TDD_POOLSIZ
# define TDD_POOLSIZ (2 * 1024)
#endif
#ifndef TDD_LCLSIZ
# define TDD_LCLSIZ 0
#endif
/*
* If the OS headers haven't defined any system-specific option usage
* messages, set up a dummy list. The usage display routine will show
* messages starting from the lower number up to and including the higher
* number; by default we'll make the ending number lower than the starting
* number so that we don't display any messages at all.
*/
#ifndef ERR_TRUS_OS_FIRST
# define ERR_TRUS_OS_FIRST 100
# define ERR_TRUS_OS_LAST 99
#endif
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,913 @@
/* 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/tads/tads2/built_in.h"
#include "glk/tads/tads2/character_map.h"
#include "glk/tads/tads2/command_line.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/file_io.h"
#include "glk/tads/tads2/memory_cache_heap.h"
#include "glk/tads/tads2/os.h"
#include "glk/tads/tads2/play.h"
#include "glk/tads/tads2/post_compilation.h"
#include "glk/tads/tads2/run.h"
#include "glk/tads/tads2/runtime_app.h"
#include "glk/tads/tads2/tokenizer.h"
#include "glk/tads/tads2/tads2.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/os_frob_tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* dummy setup function */
void supgnam(char *buf, tokthdef *tab, objnum sc)
{
Common::strcpy_s(buf, TOKNAMMAX + 1, "???");
}
/* dummy file read functions */
void tok_read_defines(tokcxdef *tctx, osfildef *fp, errcxdef *ec)
{
errsig(ec, ERR_UNKRSC);
}
/* dummy debugger functions */
int dbgbpset(dbgcxdef *ctx, char *addr, int *bpnum) { return 0; }
int dbgbpat(dbgcxdef *ctx, objnum objn, objnum self,
uint ofs, int *bpnum, char *bpname, int toggle,
char *condition, int *did_set) { return 0; }
int dbgbpatid(dbgcxdef *ctx, int bpnum, objnum target, objnum self,
uint ofs, char *bpname, int toggle, char *cond,
int *did_set) { return 0; }
int dbgisbp(dbgcxdef *ctx, objnum target, objnum self, uint ofs, int *bpnum) { return 0; }
int dbgisbpena(dbgcxdef *ctx, int bpnum) { return 0; }
int dbgbpdel(dbgcxdef *ctx, int bpnum) { return 0; }
int dbgbpdis(dbgcxdef *ctx, int bpnum, int disable) { return 0; }
int dbgbpsetcond(dbgcxdef *ctx, int bpnum, char *cond) { return 0; }
void dbgbplist(dbgcxdef *ctx, void(*dispfn)(void *ctx, const char *str, int len), void *dispctx) {}
void dbgbpenum(dbgcxdef *ctx, void(*cbfunc)(void *cbctx, int bpnum, const char *desc,
const char *cond, int disabled), void *cbctx) {}
void dbgbpeach(dbgcxdef *ctx, void(*fn)(void *, int, uchar *, uint), void *fnctx) {}
int dbgbpgetinfo(dbgcxdef *ctx, int bpnum, char *descbuf, size_t descbuflen,
char *condbuf, size_t condbuflen) { return 0; }
int dbgeval(dbgcxdef *ctx, char *expr,
void(*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype) { return 0; }
int dbgevalext(dbgcxdef *ctx, char *expr,
void(*dispfn)(void *dispctx, const char *str, int strl),
void *dispctx, int level, int showtype, dattyp *dat,
void(*aggcb)(void *aggctx, const char *subname,
int subnamelen, const char *relationship),
void *aggctx, int speculative) { return 0 ;}
void dbgenumlcl(dbgcxdef *ctx, int level,
void(*func)(void *ctx, const char *lclnam, size_t lclnamlen),
void *cbctx) {}
int dbgcompile(dbgcxdef *ctx, char *expr, dbgfdef *fr, objnum *objn,
int speculative) { return 0; }
void dbgwhere(dbgcxdef *ctx, char *buf) {}
int dbgwxset(dbgcxdef *ctx, char *expr, int *wxnum, int level) { return 0; }
int dbgwxdel(dbgcxdef *ctx, int wxnum) { return 0; }
void dbgwxupd(dbgcxdef *ctx,
void(*dispfn)(void *dispctx, const char *txt, int len),
void *dispctx) {}
void dbgswitch(struct lindef **linp, struct lindef *newlin) {}
void dbguini(dbgcxdef *ctx, const char *game_filename) {}
void dbguini2(dbgcxdef *ctx) {}
int dbgu_err_resume(dbgcxdef *ctx) { return 0; }
int dbgu_find_src(const char *origname, int origlen,
char *fullname, size_t full_len, int must_find_file) { return 0; }
void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs) {}
void dbguquitting(dbgcxdef *ctx) {}
void dbguterm(dbgcxdef *ctx) {}
void dbguerr(dbgcxdef *ctx, int errnum, char *msg) {}
void trchid(void) {}
void trcsho(void) {}
struct runsdef *dbgfrfind(dbgcxdef *ctx, objnum frobj, uint frofs)
{
VARUSED(frobj);
VARUSED(frofs);
errsig(ctx->dbgcxerr, ERR_INACTFR);
return nullptr;
}
void dbgss(struct dbgcxdef *ctx, uint ofs, int instr, int err,
uchar *noreg *p)
{
VARUSED(ctx);
VARUSED(ofs);
VARUSED(instr);
VARUSED(err);
VARUSED(p);
}
int dbgstart(struct dbgcxdef *ctx)
{
VARUSED(ctx);
return TRUE;
}
/* printf-style formatting */
static void trdptf(const char *fmt, ...)
{
char buf[256];
va_list va;
/* format the string */
va_start(va, fmt);
Common::vsprintf_s(buf, fmt, va);
va_end(va);
/* print the formatted buffer */
os_printz(buf);
}
/*
* display a range of usage messages
*/
static void trdusage_show_range(errcxdef *ec, int msg_first, int msg_last)
{
int i;
char buf[128];
for (i = msg_first ; i <= msg_last ; ++i)
{
errmsg(ec, buf, (uint)sizeof(buf), i);
trdptf("%s\n", buf);
}
}
/*
* display a range of usage messages, then throw the usage error
*/
static void trdusage_range(errcxdef *ec, int msg_first, int msg_last)
{
/* show the message range */
trdusage_show_range(ec, msg_first, msg_last);
/* signal the usage error */
errsig(ec, ERR_USAGE);
}
/*
* display general run-time usage information
*/
static void trdusage(errcxdef *ec)
{
int first;
/*
* if we have an app display name, display it instead of the
* hard-coded text in the message identifying the app
*/
first = ERR_TRUS1;
if (ec->errcxappctx != nullptr && ec->errcxappctx->usage_app_name != nullptr)
{
char buf[128];
char buf2[128];
erradef argv[1];
/* get the parameterized usage message */
errmsg(ec, buf, (uint)sizeof(buf), ERR_TRUSPARM);
/* format in the application name */
argv[0].errastr = ec->errcxappctx->usage_app_name;
errfmt(buf2, (int)sizeof(buf2), buf, 1, argv);
/* display it */
trdptf("%s\n", buf2);
/* start at the next message */
++first;
}
/* display the main option list messages */
trdusage_show_range(ec, first, ERR_TRUSL);
/* display the OS-specific option messages, if any */
trdusage_show_range(ec, ERR_TRUS_OS_FIRST, ERR_TRUS_OS_LAST);
/* display the usage footer messages */
trdusage_range(ec, ERR_TRUSFT1, ERR_TRUSFTL);
}
/*
* display -s suboptions
*/
static void trdusage_s(errcxdef *ec)
{
trdusage_range(ec, ERR_TRSUS1, ERR_TRSUSL);
}
static void trdmain1(errcxdef *ec, int argc, char *argv[],
appctxdef *appctx, const char *save_ext)
{
osfildef *swapfp = (osfildef *)nullptr;
runcxdef runctx;
bifcxdef bifctx;
voccxdef vocctx;
void (*bif[100])(struct bifcxdef *, int);
mcmcxdef *mctx = nullptr;
mcmcx1def *globalctx = nullptr;
dbgcxdef dbg;
supcxdef supctx;
char *swapname = nullptr;
char swapbuf[OSFNMAX];
char **argp;
char *arg;
char *infile;
char infile_abs[OSFNMAX]; /* fully-qualified input file name */
char infile_path[OSFNMAX]; /* absolute path to input file */
const char *exefile; /* try with executable file if no infile */
ulong swapsize = 0xffffffffL; /* allow unlimited swap space */
int swapena = OS_DEFAULT_SWAP_ENABLED; /* swapping enabled? */
int i;
int pause = FALSE; /* pause after finishing game */
fiolcxdef fiolctx;
noreg int loadopen = FALSE;
char inbuf[OSFNMAX];
ulong cachelimit = 0xffffffff;
ushort undosiz = TRD_UNDOSIZ; /* default undo context size 16k */
objucxdef *undoptr = nullptr;
uint flags; /* flags used to write the file we're reading */
objnum preinit; /* preinit object, if we need to execute it */
uint heapsiz = TRD_HEAPSIZ;
uint stksiz = TRD_STKSIZ;
runsdef *mystack;
uchar *myheap;
extern osfildef *cmdfile; /* hacky v1 qa interface - command log fp */
extern osfildef *logfp; /* hacky v1 qa interface - output log fp */
int preload = FALSE; /* TRUE => preload all objects */
ulong totsize;
extern voccxdef *main_voc_ctx;
int safety_read, safety_write; /* file I/O safety level */
char *restore_file = nullptr; /* .SAV file to restore */
char *charmap = nullptr; /* character map file */
int charmap_none; /* explicitly do not use a character set */
int doublespace = TRUE; /* formatter double-space setting */
NOREG((&loadopen))
/* initialize the output formatter */
out_init();
/* set safety level to 2 by default - read any/write current dir only */
safety_read = safety_write = 2;
/* no -ctab- yet */
charmap_none = FALSE;
/* parse arguments */
for (i = 1, argp = argv + 1 ; i < argc ; ++argp, ++i)
{
arg = *argp;
if (*arg == '-')
{
switch(*(arg+1))
{
case 'c':
if (!strcmp(arg+1, "ctab"))
{
/* get the character mapping table */
charmap = cmdarg(ec, &argp, &i, argc, 4, trdusage);
}
else if (!strcmp(arg+1, "ctab-"))
{
/* use the default mapping */
charmap_none = TRUE;
}
else
trdusage(ec);
break;
case 'r':
/* restore a game */
restore_file = cmdarg(ec, &argp, &i, argc, 1, trdusage);
break;
case 'i':
qasopn(cmdarg(ec, &argp, &i, argc, 1, trdusage), TRUE);
break;
case 'o':
cmdfile = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
OSFTCMD);
break;
case 'l':
logfp = osfopwt(cmdarg(ec, &argp, &i, argc, 1, trdusage),
OSFTCMD);
break;
case 'p':
if (!scumm_stricmp(arg, "-plain"))
{
os_plain();
break;
}
pause = cmdtog(ec, pause, arg, 1, trdusage);
break;
case 'd':
if (!scumm_strnicmp(arg, "-double", 7))
{
/* get the argument value */
doublespace = cmdtog(ec, doublespace, arg, 6, trdusage);
/* set the double-space mode in the formatter */
out_set_doublespace(doublespace);
break;
}
break;
case 's':
{
char *p;
/* get the option */
p = cmdarg(ec, &argp, &i, argc, 1, trdusage);
/* if they're asking for help, display detailed usage */
if (*p == '?')
trdusage_s(ec);
/* get the safety level from the argument */
safety_read = *p - '0';
safety_write = (*(p+1) != '\0' ? *(p+1) - '0' :
safety_read);
/* range-check the values */
if (safety_read < 0 || safety_read > 4
|| safety_write < 0 || safety_write > 4)
trdusage_s(ec);
/* tell the host system about the setting */
if (appctx != nullptr && appctx->set_io_safety_level != nullptr)
(*appctx->set_io_safety_level)
(appctx->io_safety_level_ctx,
safety_read, safety_write);
}
break;
case 'm':
switch(*(arg + 2))
{
case 's':
stksiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
break;
case 'h':
heapsiz = atoi(cmdarg(ec, &argp, &i, argc, 2, trdusage));
break;
default:
cachelimit = atol(cmdarg(ec, &argp, &i, argc, 1,
trdusage));
break;
}
break;
case 't':
/* swap file options: -tf file, -ts size, -t- (no swap) */
switch(*(arg+2))
{
case 'f':
swapname = cmdarg(ec, &argp, &i, argc, 2, trdusage);
break;
case 's':
swapsize = atol(cmdarg(ec, &argp, &i, argc, 2, trdusage));
break;
case 'p':
preload = cmdtog(ec, preload, arg, 2, trdusage);
break;
default:
swapena = cmdtog(ec, swapena, arg, 1, trdusage);
break;
}
break;
case 'u':
undosiz = atoi(cmdarg(ec, &argp, &i, argc, 1, trdusage));
break;
default:
trdusage(ec);
}
}
else break;
}
/* presume we won't take the .gam from the application executable */
exefile = nullptr;
/* get input name argument, and make sure it's the last argument */
if (i == argc)
{
osfildef *fp;
ulong curpos;
ulong endpos;
int use_exe;
/*
* There's no input name argument, so we need to find the game
* to play some other way. First, check to see if we have a
* game to restore, and if so whether it has the .GAM name
* encoded into it. Next, look to see if there's a game
* attached to the executable file; if so, use it. If not, see
* if the host system wants to provide a name through its
* callback.
*/
/* presume we won't find a game attached to the executable file */
infile = nullptr;
use_exe = FALSE;
/*
* see if we have a saved game to restore, and it specifies the
* GAM file that saved it
*/
if (restore_file != nullptr)
{
/* try getting the game name from the restore file */
if (fiorso_getgame(restore_file, inbuf, sizeof(inbuf)))
{
/* got it - use this file */
infile = inbuf;
}
}
/*
* it that didn't work, try to read from os-dependent part of
* program being executed
*/
if (infile == nullptr)
{
/* try opening the executable file */
exefile = (argv && argv[0] ? argv[0] : "TRX");
fp = os_exeseek(exefile, "TGAM");
if (fp != nullptr)
{
/* see if there's a game file attached to the executable */
curpos = osfpos(fp);
osfseek(fp, 0L, OSFSK_END);
endpos = osfpos(fp);
osfcls(fp);
/* if we found it, use it */
if (endpos != curpos)
use_exe = TRUE;
}
}
/*
* if we didn't find a game in the executable, try the host
* system callback
*/
if (infile == nullptr && !use_exe)
{
/*
* ask the host system callback what to do - if we don't
* have a host system callback, or the callback
*/
if (appctx != nullptr && appctx->get_game_name != nullptr)
{
/* call the host system callback */
if ((*appctx->get_game_name)(appctx->get_game_name_ctx,
inbuf, sizeof(inbuf)))
{
/* the host system provided a name - use it */
infile = inbuf;
}
else
{
/*
* the host didn't provide a name - simply display a
* message indicating that no game file has been
* chosen, and return
*/
trdptf("\n");
trdptf("(No game has been selected.)\n");
return;
}
}
else
{
/*
* we've run out of ways to get a filename - give the
* user the usage message and quit
*/
trdusage(ec);
}
}
}
else
{
infile = *argp;
if (i + 1 != argc)
trdusage(ec);
#ifndef OS_HATES_EXTENSIONS
/*
* If original name exists, use it; otherwise, try adding .GAM.
* Note that this code is ifdef'd so that platforms that don't
* use filename extensions in the manner conventional for DOS
* and Unix won't use this code.
*/
if (osfacc(infile))
{
Common::strcpy_s(inbuf, infile);
os_defext(inbuf, "gam");
infile = inbuf;
}
#endif /* !defined(OS_HATES_EXTENSIONS) */
}
/* open up the swap file */
if (swapena && swapsize)
{
swapfp = os_create_tempfile(swapname, swapbuf);
if (swapname == nullptr) swapname = swapbuf;
if (swapfp == nullptr) errsig(ec, ERR_OPSWAP);
}
/* load the character map */
if (charmap_none)
cmap_override();
else if (cmap_load(charmap))
errsig(ec, ERR_INVCMAP);
ERRBEGIN(ec)
/* initialize cache manager context */
globalctx = mcmini(cachelimit, 128, swapsize, swapfp, swapname, ec);
mctx = mcmcini(globalctx, 128, fioldobj, &fiolctx,
objrevert, (void *)nullptr);
mctx->mcmcxrvc = mctx;
/* set up an undo context */
if (undosiz)
undoptr = objuini(mctx, undosiz, vocdundo, vocdusz, &vocctx);
else
undoptr = (objucxdef *)nullptr;
/* set up vocabulary context */
vocini(&vocctx, ec, mctx, &runctx, undoptr, 100, 100, 200);
/*
* save a pointer to the voc context globally, so that certain
* external routines (such as Unix-style signal handlers) can reach
* it
*/
main_voc_ctx = &vocctx;
/* allocate stack and heap */
totsize = (ulong)stksiz * (ulong)sizeof(runsdef);
if (totsize != (size_t)totsize)
errsig1(ec, ERR_STKSIZE, ERRTINT, (uint)(65535/sizeof(runsdef)));
mystack = (runsdef *)mchalo(ec, (size_t)totsize, "runtime stack");
myheap = mchalo(ec, heapsiz, "runtime heap");
/* get the absolute path for the input file */
if (infile != nullptr)
os_get_abs_filename(infile_abs, sizeof(infile_abs), infile);
else if (exefile != nullptr)
os_get_abs_filename(infile_abs, sizeof(infile_abs), exefile);
else
infile_abs[0] = '\0';
os_get_path_name(infile_path, sizeof(infile_path), infile_abs);
/* set up execution context */
runctx.runcxerr = ec;
runctx.runcxmem = mctx;
runctx.runcxstk = mystack;
runctx.runcxstop = &mystack[stksiz];
runctx.runcxsp = mystack;
runctx.runcxbp = mystack;
runctx.runcxheap = myheap;
runctx.runcxhp = myheap;
runctx.runcxhtop = &myheap[heapsiz];
runctx.runcxundo = undoptr;
runctx.runcxbcx = &bifctx;
runctx.runcxbi = bif;
runctx.runcxtio = (tiocxdef *)nullptr;
runctx.runcxdbg = &dbg;
runctx.runcxvoc = &vocctx;
runctx.runcxdmd = supcont;
runctx.runcxdmc = &supctx;
runctx.runcxext = nullptr;
runctx.runcxgamename = infile;
runctx.runcxgamepath = infile_path;
/* set up setup context */
supctx.supcxerr = ec;
supctx.supcxmem = mctx;
supctx.supcxtab = (tokthdef *)nullptr;
supctx.supcxbuf = (uchar *)nullptr;
supctx.supcxlen = 0;
supctx.supcxvoc = &vocctx;
supctx.supcxrun = &runctx;
/* set up debug context */
dbg.dbgcxtio = (tiocxdef *)nullptr;
dbg.dbgcxmem = mctx;
dbg.dbgcxerr = ec;
dbg.dbgcxtab = (tokthdef *)nullptr;
dbg.dbgcxfcn = 0;
dbg.dbgcxdep = 0;
dbg.dbgcxflg = 0;
dbg.dbgcxlin = (lindef *)nullptr; /* no line sources yet */
/* set up built-in function context */
bifctx.bifcxerr = ec;
bifctx.bifcxrun = &runctx;
bifctx.bifcxtio = (tiocxdef *)nullptr;
bifctx.bifcxrnd = 0;
bifctx.bifcxrndset = FALSE;
bifctx.bifcxappctx = appctx;
bifctx.bifcxsafetyr = safety_read;
bifctx.bifcxsafetyw = safety_write;
bifctx.bifcxsavext = save_ext;
/* initialize the regular expression parser context */
re_init(&bifctx.bifcxregex, ec);
/* add the built-in functions, keywords, etc */
supbif(&supctx, bif, (int)(sizeof(bif)/sizeof(bif[0])));
/* set up status line hack */
runistat(&vocctx, &runctx, (tiocxdef *)nullptr);
/* turn on the "busy" cursor before loading */
os_csr_busy(TRUE);
/* read the game from the binary file */
fiord(mctx, &vocctx, (struct tokcxdef *)nullptr,
infile, exefile, &fiolctx, &preinit, &flags,
(struct tokpdef *)nullptr, (uchar **)nullptr, (uint *)nullptr, (uint *)nullptr,
(preload ? 2 : 0), appctx, argv[0]);
loadopen = TRUE;
/* turn off the "busy" cursor */
os_csr_busy(FALSE);
/* play the game */
plygo(&runctx, &vocctx, (tiocxdef *)nullptr, preinit, restore_file);
/* close load file */
fiorcls(&fiolctx);
if (pause)
{
trdptf("[press a key to exit]");
os_waitc();
trdptf("\n");
}
/* close and delete swapfile, if one was opened */
trd_close_swapfile(&runctx);
/* make sure the script file is closed, if we have one */
qasclose();
ERRCLEAN(ec)
/* close and delete swapfile, if one was opened */
trd_close_swapfile(&runctx);
/* close the load file if one was opened */
if (loadopen)
fiorcls(&fiolctx);
/* vocctx is going out of scope - forget the global reference to it */
main_voc_ctx = nullptr;
/* delete the voc context */
vocterm(&vocctx);
/* delete the undo context */
if (undoptr != nullptr)
objuterm(undoptr);
/* release the object cache structures */
if (mctx != nullptr)
mcmcterm(mctx);
if (globalctx != nullptr)
mcmterm(globalctx);
ERRENDCLN(ec)
/* vocctx is going out of scope - forget the global reference to it */
main_voc_ctx = nullptr;
/* delete the voc contxt */
vocterm(&vocctx);
/* delete the undo context */
if (undoptr != nullptr)
objuterm(undoptr);
/* release the object cache structures */
mcmcterm(mctx);
mcmterm(globalctx);
}
/*
* If the OS configuration so desires, use a less technical format for
* run-time error messages by leaving out the numeric error code. Note
* that we'll only do this if the error messages are linked directly
* into the run-time, since we need the numeric code as a last resort
* when the error message may not be present.
*/
#ifdef OS_SKIP_ERROR_CODES
# ifdef ERR_LINK_MESSAGES
# define TRDLOGERR_PREFIX "\n[An error has occurred within TADS: "
# endif
#endif
/*
* If we didn't define a different error prefix format, use the default
* format with the numeric error code.
*/
#ifndef TRDLOGERR_PREFIX
# define TRDLOGERR_PREFIX "\n[%s-%d: "
#endif
/* log an error */
static void trdlogerr(void *ctx0, const char *fac, int err, int argc, erradef *argv) {
errcxdef *ctx = (errcxdef *)ctx0;
char buf[256];
char msg[256];
/* display the prefix message to the console and log file */
Common::sprintf_s(buf, TRDLOGERR_PREFIX, fac, err);
trdptf("%s", buf);
out_logfile_print(buf, FALSE);
/* display the error message text to the console and log file */
errmsg(ctx, msg, (uint)sizeof(msg), err);
errfmt(buf, (int)sizeof(buf), msg, argc, argv);
trdptf("%s]\n", buf);
out_logfile_print(buf, FALSE);
out_logfile_print("]", TRUE);
}
/*
* close and delete the swap file
*/
void trd_close_swapfile(runcxdef *runctx)
{
extern voccxdef *main_voc_ctx;
mcmcxdef *mctx;
mcmcx1def *globalctx;
mcscxdef *mcsctx;
/* if no run context was supplied, find it from the main voc context */
if (runctx == nullptr)
{
/* if there is no main voc context, we're out of luck */
if (main_voc_ctx == nullptr)
return;
/* get the run context */
runctx = main_voc_ctx->voccxrun;
}
/* get the other relevant contexts */
mctx = runctx->runcxmem;
globalctx = mctx->mcmcxgl;
mcsctx = &globalctx->mcmcxswc;
/* if we have a swap file open, close it */
if (mcsctx->mcscxfp != nullptr)
{
/* close the file */
osfcls(mcsctx->mcscxfp);
/* forget about the file, so we don't try to close it again */
mcsctx->mcscxfp = (osfildef *)nullptr;
}
/* if we have a filename, delete the file */
if (mcsctx->mcscxfname != nullptr)
{
/* delete the file */
osfdel_temp(mcsctx->mcscxfname);
/* forget the filename, so we don't try to delete the file again */
mchfre(mcsctx->mcscxfname);
mcsctx->mcscxfname = nullptr;
}
}
/* main - called by os main after setting up arguments */
int trdmain(int argc, char *argv[], appctxdef *appctx, const char *save_ext)
{
errcxdef errctx;
int err;
osfildef *fp;
errctx.errcxlog = trdlogerr;
errctx.errcxlgc = &errctx;
errctx.errcxfp = (osfildef *)nullptr;
errctx.errcxofs = 0;
errctx.errcxappctx = appctx;
fp = oserrop(argv[0]);
errini(&errctx, fp);
/* copyright-date-string */
#ifndef NO_T2_COPYRIGHT_NOTICE
trdptf("%s - A %s TADS %s Interpreter.\n",
G_tads_oem_app_name, G_tads_oem_display_mode,
TADS_RUNTIME_VERSION);
trdptf("%sopyright (c) 1993, 2012 by Michael J. Roberts.\n",
G_tads_oem_copyright_prefix ? "TADS c" : "C");
trdptf("%s\n", G_tads_oem_author);
#endif
ERRBEGIN(&errctx)
trdmain1(&errctx, argc, argv, appctx, save_ext);
ERRCATCH(&errctx, err)
/*
* log the error, unless it's usage (in which case we logged it
* already) or we're simply quitting the game
*/
if (err != ERR_USAGE && err != ERR_RUNQUIT)
errclog(&errctx);
/* close the error file */
if (errctx.errcxfp != nullptr)
osfcls(errctx.errcxfp);
/* pause before exiting if the OS desires it */
os_expause();
/* return failure unless we're simply quitting the game */
return (err == ERR_RUNQUIT ? OSEXSUCC : OSEXFAIL);
ERREND(&errctx)
/* close the error file if we opened it */
if (errctx.errcxfp != nullptr)
osfcls(errctx.errcxfp);
/* successful completion */
return(OSEXSUCC);
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,68 @@
/* 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_TADS_TADS2_STRING_RESOURCES
#define GLK_TADS_TADS2_STRING_RESOURCES
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Dialog buttons. These provide the text for standard buttons in
* dialogs created with os_input_dialog().
*
* These labels can use "&" to indicate a shortcut letter, per the
* normal os_input_dialog() interface; for example, if the Yes button
* label is "&Yes", the button has the shortcut letter "Y".
*
* The text of these buttons may vary by system, since these should
* conform to local conventions where there are local conventions. In
* addition, of course, these strings will vary by language.
*/
/* OK and Cancel buttons */
#define RESID_BTN_OK 1
#define RESID_BTN_CANCEL 2
/* "Yes" and "No" buttons */
#define RESID_BTN_YES 3
#define RESID_BTN_NO 4
/*
* Reply strings for the yorn() built-in function. These strings are
* regular expressions as matched by the regex.h functions. For
* English, for example, the "yes" string would be "[Yy].*" and the "no"
* string would be "[Nn].*". For German, it might be desirable to
* accept both "Ja" and "Yes", so the "Yes" string might be "[JjYy].*".
*
* It's not necessary in these patterns to consider leading spaces,
* since the yorn() function will skip any leading spaces before
* performing the pattern match.
*/
#define RESID_YORN_YES 5
#define RESID_YORN_NO 6
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,51 @@
/* 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/tads/tads2/tads2.h"
#include "glk/tads/tads2/appctx.h"
#include "glk/tads/tads2/runtime_app.h"
#include "glk/tads/tads2/os.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
TADS2::TADS2(OSystem *syst, const GlkGameDescription &gameDesc) : TADS(syst, gameDesc) {
}
void TADS2::runGame() {
// Initialize the OS layer
os_init(nullptr, nullptr, nullptr, nullptr, 0);
os_instbrk(true);
char name[255];
Common::strcpy_s(name, getFilename().c_str());
char *argv[2] = { nullptr, name };
trdmain(2, argv, nullptr, ".sav");
os_instbrk(false);
os_uninit();
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,58 @@
/* 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_TADS_TADS2
#define GLK_TADS_TADS2
#include "glk/tads/tads.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/**
* TADS 2 game interpreter
*/
class TADS2 : public TADS {
public:
/**
* Constructor
*/
TADS2(OSystem *syst, const GlkGameDescription &gameDesc);
/**
* Execute the game
*/
void runGame() override;
/**
* Returns the running interpreter type
*/
InterpreterType getInterpreterType() const override { return INTERPRETER_TADS2; }
};
//typedef TADS2 appctxdef;
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,222 @@
/* 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_TADS_TADS2_TEXT_IO
#define GLK_TADS_TADS2_TEXT_IO
/*
* Text I/O interface
*
* Formatted text input and output interface definition
*/
#include "glk/tads/tads.h"
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/tads2/run.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* forward decls */
struct runcxdef;
/**
* Text i/o context
*/
struct tiocxdef {
errcxdef *tiocxerr; /* error handling context */
};
/**
* Initialize the output formatter subsystem. This must be called once
* at startup.
*/
void out_init();
/* redirect all tioxxx routines to TADS v1.x outxxx equivalents */
#define tioflushn(ctx, nl) outflushn(nl)
#define tioflush(ctx) outflush()
#define tioblank(ctx) outblank()
#define tioreset(ctx) outreset()
#define tiogets(ctx, prompt, str, siz) getstring(prompt, str, siz)
#define tioputs(ctx, str) outformat(str)
#define tioputslen(ctx, str, len) outformatlen(str, len)
#define tiocaps(ctx) outcaps()
#define tionocaps(ctx) outnocaps()
#define tioshow(ctx) outshow()
#define tiohide(ctx) outhide()
#define tioscore(ctx, s1, s2) os_score(s1, s2)
#define tiostrsc(ctx, s) os_strsc(s)
/* set up format strings in output subsystem */
void tiosetfmt(tiocxdef *ctx, runcxdef *rctx, uchar *fmtbase,
uint fmtlen);
/* tell tio subsystem the current actor */
void tiosetactor(tiocxdef *ctx, objnum actor);
/* get the current tio subsystem actor */
objnum tiogetactor(tiocxdef *ctx);
/* turn output capture on/off */
void tiocapture(tiocxdef *tioctx, mcmcxdef *memctx, int flag);
/* get the capture object handle */
mcmon tiogetcapture(tiocxdef *ctx);
/* get the amount of text captured */
uint tiocapturesize(tiocxdef *ctx);
/* format a length-prefixed (runtime-style) string to the display */
void outfmt(tiocxdef *ctx, uchar *txt);
/* format a null-terminated (C-style) string to the display */
int outformat(const char *s);
/* format a counted-length string, which may not be null-terminated */
int outformatlen(const char *s, uint len);
/* flush output, with specified newline mode */
void outflushn(int nl);
/* flush output */
void outflush(void);
/* reset output state */
void outreset(void);
/*
* Get a string from the keyboard. Returns non-zero if an error occurs
* (in particular, if no more input is available from the keyboard),
* zero on success.
*/
int getstring(const char *prompt, char *buf, int bufl);
/* set capitalize-next-character mode on/off */
void outcaps(void);
void outnocaps(void);
/* open/close output log file */
int tiologopn(tiocxdef *ctx, char *fn);
int tiologcls(tiocxdef *ctx);
/*
* Write text explicitly to the log file. This can be used to add
* special text (such as prompt text) that would normally be suppressed
* from the log file. When more mode is turned off, we don't
* automatically copy text to the log file; any text that the caller
* knows should be in the log file during times when more mode is turned
* off can be explicitly added with this function.
*
* If nl is true, we'll add a newline at the end of this text. The
* caller should not include any newlines in the text being displayed
* here.
*/
void out_logfile_print(const char *txt, int nl);
/*
* Check output status. Indicate whether output is currently hidden,
* and whether any hidden output has occurred.
*/
void outstat(int *hidden, int *output_occurred);
/* hide/show output */
void outhide(void);
int outshow(void);
/* set the flag to indicate that output has occurred */
void outsethidden(void);
/* write a blank line */
void outblank(void);
/* start/end watchpoint evaluation */
void outwx(int flag);
/* clear all captured output */
void tioclrcapture(tiocxdef *tioctx);
/*
* clear captured output back to a given point -- this can be used to
* remove captured output in an inner capture from an enclosing capture
*/
void tiopopcapture(tiocxdef *tioctx, uint orig_size);
/* turn MORE mode on or off */
int setmore(int state);
/* explicitly activate the "MORE" prompt */
void out_more_prompt();
/*
* QA controller functions
*/
int qasopn(char *scrnam, int quiet);
void qasclose(void);
char *qasgets(char *buf, int bufl);
/*
* Set an HTML entity expansion. This is called during initialization
* when we read a character mapping table that includes HTML entity
* expansions. The HTML run-time uses its own expansion mechanism, so
* it will ignore this information. The standard character-mode TADS
* run-time, however, uses this information to map HTML entities to the
* local character set.
*/
void tio_set_html_expansion(unsigned int html_char_val,
const char *expansion, size_t expansion_len);
/* check for HTML mode - returns true if an "\H+" sequence is active */
int tio_is_html_mode();
/* set the user output filter function */
void out_set_filter(objnum filter_fn);
/* set the double-space mode */
void out_set_doublespace(int dbl);
/*
* Ask for a filename, using a system-defined dialog (via os_askfile) if
* possible. Uses the same interface as os_askfile(), which we will
* call directly for graphical implementations. We'll use formatted
* text for text-only implementations.
*/
int tio_askfile(const char *prompt, char *reply, int replen, int prompt_type, os_filetype_t file_type);
/*
* Display a dialog, using a system-defined dialog (via os_input_dialog)
* if possible. Uses the same interface as os_input_dialog(), which we
* will call directly for graphical implementations. We'll use
* formatted text for text-only implementations.
*/
int tio_input_dialog(int icon_id, const char *prompt, int standard_button_set,
const char **buttons, int button_count,
int default_index, int cancel_index);
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,473 @@
/* 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_TADS_TADS2_TOKENIZER
#define GLK_TADS_TADS2_TOKENIZER
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/error_handling.h"
#include "glk/tads/tads2/line_source.h"
#include "glk/tads/tads2/line_source_file.h"
#include "glk/tads/tads2/memory_cache.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* number of entries in hash table - must be power of 2 */
#define TOKHASHSIZE 256
/* symbol types */
#define TOKSTUNK 0 /* unknown symbol, not yet defined */
#define TOKSTFUNC 1 /* function; value is object number */
#define TOKSTOBJ 2 /* object; value is object number */
#define TOKSTPROP 3 /* property; value is property number */
#define TOKSTLOCAL 4 /* a local variable or formal parameter */
#define TOKSTSELF 5 /* the pseudo-object "self" */
#define TOKSTBIFN 6 /* a built-in function */
#define TOKSTFWDOBJ 7 /* forward-referenced object */
#define TOKSTFWDFN 8 /* forward-referenced object */
#define TOKSTINHERIT 9 /* the pseudo-object "inherited" */
#define TOKSTEXTERN 10 /* an external function */
#define TOKSTKW 11 /* keyword; value is token number */
#define TOKSTLABEL 12 /* statement label */
#define TOKSTARGC 13 /* 'argcount' pseudo-variable */
#define TOKSTPROPSPEC 14 /* speculative evaluation property */
/* token types */
#define TOKTEOF 1
/* binary operators - keep these together (see prsbopl[] in prs.c) */
#define TOKTPLUS 2
#define TOKTMINUS 3
#define TOKTDIV 4
#define TOKTTIMES 5
#define TOKTNOT 6 /* ! or "not" */
#define TOKTEQ 7
#define TOKTNE 8
#define TOKTGT 9
#define TOKTGE 10
#define TOKTLT 11
#define TOKTLE 12
#define TOKTMOD 13
#define TOKTBAND 14
#define TOKTBOR 15
#define TOKTXOR 16
#define TOKTSHL 17
#define TOKTSHR 18
#define TOKTTILDE 30
/*
* special 'dot' replacement for speculative evaluation mode -- this is
* strictly for marking parse tree nodes, and has the same meaning in a
* parse tree node as a regular TOKTDOT, but generates code that can't
* call methods
*/
#define TOKTDOTSPEC 31
/* special node marker for explicit superclass inheritance nodes */
#define TOKTEXPINH 32
#define TOKTLPAR 50 /* ( */
#define TOKTRPAR 51 /* ) */
#define TOKTCOLON 52
#define TOKTDSTRING 53 /* string in double quotes */
#define TOKTSSTRING 54 /* string in single quotes */
#define TOKTNUMBER 55
#define TOKTSYMBOL 56
#define TOKTINVALID 57 /* invalid lexical token */
#define TOKTLBRACK 58 /* [ */
#define TOKTRBRACK 59 /* ] */
#define TOKTLBRACE 60 /* { */
#define TOKTRBRACE 61 /* } */
#define TOKTSEM 62 /* ; */
#define TOKTCOMMA 63
#define TOKTDOT 64 /* . */
#define TOKTOR 65 /* | or "if" */
#define TOKTAND 66 /* & or "and" */
#define TOKTIF 67 /* keywords */
#define TOKTELSE 68
#define TOKTWHILE 69
#define TOKTFUNCTION 70
#define TOKTRETURN 71
#define TOKTLOCAL 72
#define TOKTOBJECT 73
#define TOKTBREAK 74
#define TOKTCONTINUE 75
#define TOKTLIST 76 /* a list */
#define TOKTNIL 77
#define TOKTTRUE 78
#define TOKTPASS 79
#define TOKTCLASS 80
#define TOKTEXIT 81
#define TOKTABORT 82
#define TOKTASKDO 83
#define TOKTASKIO 84
#define TOKTPOUND 85 /* # */
#define TOKTQUESTION 86 /* ? */
#define TOKTCOMPOUND 87
#define TOKTIOSYN 88
#define TOKTDOSYN 89
#define TOKTEXTERN 90
#define TOKTFORMAT 91
#define TOKTDO 92
#define TOKTFOR 93
#define TOKTNEW 94
#define TOKTDELETE 95
/* assignment operators - keep these together */
#define TOKTINC 150 /* ++ */
#define TOKTPOSTINC 151 /* MUST BE TOKTINC + 1 */
#define TOKTDEC 152 /* -- */
#define TOKTPOSTDEC 153 /* MUST BE TOKTDEC + 1 */
#define TOKTPLEQ 154 /* += */
#define TOKTMINEQ 155 /* -= */
#define TOKTDIVEQ 156 /* /= */
#define TOKTTIMEQ 157 /* *= */
#define TOKTASSIGN 158 /* simple assignment */
#define TOKTMODEQ 159 /* %= (mod and assign) operator */
#define TOKTBANDEQ 160 /* &= */
#define TOKTBOREQ 161 /* |= */
#define TOKTXOREQ 162 /* ^= (xor and assign) */
#define TOKTSHLEQ 163 /* <<= (shift left and assign) */
#define TOKTSHREQ 164 /* >>= (shift right and assign */
#define TOKTSWITCH 200
#define TOKTCASE 201
#define TOKTDEFAULT 202
#define TOKTGOTO 203
#define TOKTELLIPSIS 204 /* ... */
#define TOKTSPECIAL 205 /* "specialWords" */
#define TOKTREPLACE 206 /* replace */
#define TOKTMODIFY 207 /* modify */
#define TOKTEQEQ 208 /* the '==' operator */
#define TOKTPOINTER 209 /* the -> operator */
/* the longest a symbol name can be */
#define TOKNAMMAX 39
/* symbol table entry */
struct toksdef {
uchar tokstyp; /* type of the symbol */
uchar tokshsh; /* hash value of symbol */
ushort toksval; /* value of the symbol (depends on type) */
ushort toksfr; /* frame offset of symbol (for debugger) */
uchar tokslen; /* length of the symbol's name */
char toksnam[TOKNAMMAX]; /* name of symbol */
};
/* symbol table entry without 'name' portion - for allocation purposes */
struct toks1def {
uchar tokstyp;
uchar tokshsh;
ushort toksval;
ushort toksfr;
uchar tokslen;
char toksnam[1];
};
/* generic symbol table object - other symbol tables are subclasses */
struct toktdef {
void (*toktfadd)(toktdef *tab, char *name, int namel, int typ,
int val, int hash); /* add symbol */
int (*toktfsea)(toktdef *tab, char *name, int namel, int hash,
toksdef *ret); /* search symbol table */
void (*toktfset)(toktdef *tab, toksdef *sym);
/* update val & typ of symbol to those in *sym */
void (*toktfeach)(toktdef *tab,
void (*fn)(void *ctx, toksdef *sym),
void *fnctx); /* call fn for each sym */
toktdef *toktnxt; /* next symbol table to be searched */
errcxdef *tokterr; /* error handling context */
};
/* maximum number of pools (TOKTSIZE bytes each) for symbols */
#define TOKPOOLMAX 128
/* pointer to a symbol in a hashed symbol table */
struct tokthpdef {
mcmon tokthpobj; /* cache manager object number of page */
uint tokthpofs; /* offset within page of this symbol */
};
/* extended symbol entry in a hashed symbol table */
struct tokshdef {
tokthpdef tokshnxt; /* pointer to next symbol in the table */
toksdef tokshsc; /* superclass - normal symbol entry */
};
/* hashing symbol table (subclass of generic symbol table) */
struct tokthdef {
toktdef tokthsc; /* generic symbol table superclass data */
mcmcxdef *tokthmem; /* memory manager context */
tokthpdef tokthhsh[TOKHASHSIZE]; /* hash table */
uint tokthpcnt; /* number of memory pools for toksdef's */
mcmon tokthpool[TOKPOOLMAX]; /* memory pools for toksdef's */
uint tokthfinal[TOKPOOLMAX]; /* actual sizes of these pools */
uchar *tokthcpool; /* current pool pointer */
ushort tokthsize; /* remaining size of top memory pool */
ushort tokthofs; /* allocation offset in top memory pool */
};
/* size of toksdef pools to allocate for hashed symbol tables */
#define TOKTHSIZE 4096
/*
* Linear cache-object-embedded symbol table. This type of symbol
* table is used for frame parameter/local variable lists. It is best
* for small tables, because it isn't broken up into hash buckets, so it
* is searched linearly. As a result, it's small enough to be embedded
* in code.
*/
struct toktldef {
toktdef toktlsc; /* generic symbol table superclass data */
uchar *toktlptr; /* base of linear symbol table */
uchar *toktlnxt; /* next free byte in table */
uint toktlcnt; /* number of objects in the table */
uint toktlsiz; /* bytes remaining in the table */
};
struct tokdef {
int toktyp; /* type of the token */
int toklen; /* length of token text, if a symbolic token */
long tokval; /* numeric value, if applicable */
ushort tokofs;
uint tokhash; /* token hash value, if a symbolic token */
char toknam[TOKNAMMAX+1]; /* text of token, if a symbolic token */
toksdef toksym; /* symbol from table matching token */
};
/* special character sequence */
#define TOKSCMAX 3 /* maximum length of a special char sequence */
struct tokscdef {
tokscdef *tokscnxt; /* next sequence with same first character */
int toksctyp; /* token type corresponding to sequence */
int toksclen; /* length of the sequence */
char tokscstr[TOKSCMAX+1]; /* the sequence itself */
};
/*
* Compare a special character sequence - for efficiency, define
* something special for the maximum length available (TOKSCMAX).
* Note that the first character will always be equal, or the
* string wouldn't even get to the point of being tested by this
* macro.
*/
#if TOKSCMAX == 3
# define toksceq(str1, str2, len1, len2) \
((len2) >= (len1) \
&& ((len1) == 1 \
|| ((str1)[1] == (str2)[1] \
&& ((len1) == 2 \
|| (str1)[2] == (str2)[2]))))
#endif /* TOKSCMAX == 3 */
#ifndef toksceq
# define toksceq(str1, str2, len) (!memcmp(str1, str2, (size_t)(len)))
#endif /* toksceq */
/* special character sequence list table entry */
struct tokldef {
int tokltyp; /* token type corresponding to sequence */
char toklstr[TOKSCMAX+1]; /* the text of the sequence */
};
/* include path structure */
struct tokpdef {
tokpdef *tokpnxt; /* next path in list */
int tokplen; /* length of directory name */
char tokpdir[1]; /* directory to search */
};
/* #define symbol structure */
struct tokdfdef {
tokdfdef *nxt; /* next symbol in the same hash chain */
char *nm; /* name of the symbol */
int len; /* length of the symbol */
int explen; /* length of the expansion */
char expan[1]; /* expansion buffer */
};
/* #define hash table information */
#define TOKDFHSHSIZ 64
#define TOKDFHSHMASK 63
/* maximum #if nesting */
#define TOKIFNEST 64
/* #if state */
#define TOKIF_IF_YES 1 /* processing a true #if/#ifdef block */
#define TOKIF_IF_NO 2 /* processing a false #if/#ifdef block */
#define TOKIF_ELSE_YES 3 /* processing a true #else part */
#define TOKIF_ELSE_NO 4 /* processing a false #else part */
/* maximum macro expansion nesting */
#define TOKMACNEST 20
/* lexical analysis context */
struct tokcxdef {
errcxdef *tokcxerr; /* error handling context */
mcmcxdef *tokcxmem; /* cache manager context */
struct dbgcxdef *tokcxdbg; /* debugger context */
lindef *tokcxlin; /* line source */
tokpdef *tokcxinc; /* head of include path list */
toktdef *tokcxstab; /* current head of symbol table chain */
void *tokcxscx; /* context for string storage callback functions */
ushort (*tokcxsst)(void *ctx);
/* start storing a string; return offset of string's storage */
void (*tokcxsad)(void *ctx, const char *str, ushort len);
/* add characters to a string */
void (*tokcxsend)(void *ctx); /* finish storing string */
const char *tokcxmsav[TOKMACNEST]; /* saved positions for macro expansion */
ushort tokcxmsvl[TOKMACNEST]; /* saved lengths for macro expansion */
int tokcxmlvl; /* macro nesting level */
int tokcxflg; /* flags */
# define TOKCXFINMAC 0x01 /* doing <<expr>> macro expansion */
# define TOKCXCASEFOLD 0x02 /* fold upper and lower case */
# define TOKCXFCMODE 0x04 /* parse using C operators */
# define TOKCXF_EMBED_PAREN_PRE 0x08 /* embedded expr - did '(' */
# define TOKCXF_EMBED_PAREN_AFT 0x10 /* embedded expr - must do ')' */
# define TOKCXFLIN2 0x20 /* new-style line records */
tokdef tokcxcur; /* current token */
char *tokcxbuf; /* buffer for long lines */
ushort tokcxbsz; /* size of long line buffer */
const char *tokcxptr; /* pointer into line source */
ushort tokcxlen; /* length of text in buffer */
uchar tokcxinx[256]; /* special character indices */
tokdfdef *tokcxdf[TOKDFHSHSIZ]; /* hash table for #define symbols */
int tokcxifcnt; /* number of #endif's we expect to find */
char tokcxif[TOKIFNEST]; /* #if state for each nesting level */
int tokcxifcur; /* current #if state, obeying nesting */
linfdef *tokcxhdr; /* list of previously included headers */
tokscdef *tokcxsc[1]; /* special character table */
};
/* allocate and initialize a lexical analysis context */
tokcxdef *tokcxini(errcxdef *errctx, mcmcxdef *mctx, tokldef *sctab);
/* add an include path to a token handling context */
void tokaddinc(tokcxdef *ctx, char *path, int pathlen);
/* compute the hash value of a string */
uint tokhsh(char *nam);
/*
* Fold case of a token if we're in case-insensitive mode. This should
* be called any time a token is constructed artificially; it need not
* be used the token is read through the tokenizer, because the
* tokenizer will always adjust a token as needed before returning it.
*/
void tok_case_fold(tokcxdef *ctx, tokdef *tok);
/* initialize a hashed symbol table */
void tokthini(errcxdef *errctx, mcmcxdef *memctx, toktdef *toktab1);
/* add a symbol to a hashed symbol table */
void tokthadd(toktdef *toktab, char *name, int namel,
int typ, int val, int hash);
/* update a symbol in a hashed symbol table */
void tokthset(toktdef *toktab, toksdef *sym);
/* search a hashed symbol table for a symbol */
int tokthsea(toktdef *tab, char *name, int namel, int hash,
toksdef *ret);
/* call a function for each symbol in a hashed symbol table */
void toktheach(toktdef *tab, void (*cb)(void *ctx, toksdef *sym),
void *ctx);
/* find a symbol given type and value */
int tokthfind(toktdef *tab, int typ, uint val, toksdef *sym);
/* initialize a linear symbol table */
void toktlini(errcxdef *errctx, toktldef *toktab,
uchar *mem, uint siz);
/* add a symbol to a linear symbol table */
void toktladd(toktdef *toktab, char *name, int namel,
int typ, int val, int hash);
/* search a linear symbol table */
int toktlsea(toktdef *tab, char *name, int namel, int hash,
toksdef *ret);
/* update a symbol in a linear symbol table */
void toktlset(toktdef *toktab, toksdef *sym);
/* call a function for each symbol in a local symbol table */
void toktleach(toktdef *tab, void (*cb)(void *ctx, toksdef *sym),
void *ctx);
/* delete all symbols from a linear table */
void toktldel(toktldef *tab);
/* get next token, removing it from input stream */
int toknext(tokcxdef *ctx);
/* general function to get/peek at next token */
int tokget1(tokcxdef *ctx, tokdef *tok, int consume);
/* add a symbol to the #define symbol table */
void tok_add_define(tokcxdef *ctx, const char *sym, int len,
const char *expan, int explen);
/*
* add a symbol to the #define symbol table, folding case if we're
* operating in case-insensitive mode
*/
void tok_add_define_cvtcase(tokcxdef *ctx, const char *sym, int len,
const char *expan, int explen);
/* add a symbol to the #define symbol table as a number */
void tok_add_define_num_cvtcase(tokcxdef *ctx, const char *sym, int len, int num);
/* undefine a #define symbol */
void tok_del_define(tokcxdef *ctx, char *sym, int len);
/* read/write preprocessor symbols from/to a file */
void tok_read_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec);
/* write preprocessor state to a file */
void tok_write_defines(tokcxdef *ctx, osfildef *fp, errcxdef *ec);
/* determine if a char is a valid non-initial character in a symbol name */
#define TOKISSYM(c) \
(Common::isAlpha((uchar)(c)) || Common::isDigit((uchar)(c)) || (c)=='_' || (c)=='$')
/* numeric conversion and checking macros */
#define TOKISHEX(c) \
(Common::isDigit((uchar)(c))||((c)>='a'&&(c)<='f')||((c)>='A'&&(c)<='F'))
#define TOKISOCT(c) \
(Common::isDigit((uchar)(c))&&!((c)=='8'||(c)=='9'))
#define TOKHEX2INT(c) \
(Common::isDigit((uchar)c)?(c)-'0':((c)>='a'?(c)-'a'+10:(c)-'A'+10))
#define TOKOCT2INT(c) ((c)-'0')
#define TOKDEC2INT(c) ((c)-'0')
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

View File

@@ -0,0 +1,335 @@
/* 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/>.
*
*/
/* hashed symbol table manipulation functions
*
* Implements hashed symbol tables. A hashed symbol table stores
* a table of pointers to linked lists of symbols; each entry in
* the table corresponds to a hash value, allowing a large table
* to be searched for a symbol rapidly.
*
* Notes: Separated from tokenizer.cpp to allow the run-time to link the hashed
* symbol table functions without needing to link the rest of the
* lexical analysis subsystem.
*/
#include "glk/tads/tads2/tokenizer.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/* compute a hash value */
uint tokhsh(char *nam)
{
uint hash = 0;
while (*nam) hash = ((hash + *nam++) & (TOKHASHSIZE - 1));
return(hash);
}
/* for allocation - size of tokshdef without name portion */
struct toksh1def
{
tokthpdef tokshnxt;
toks1def tokshsc;
};
typedef struct toksh1def toksh1def;
/* initialize a hashed symbol table */
void tokthini(errcxdef *errctx, mcmcxdef *memctx, toktdef *toktab1)
{
tokthdef *toktab = (tokthdef *)toktab1; /* convert to correct type */
int i;
CLRSTRUCT(*toktab);
toktab->tokthsc.toktfadd = tokthadd; /* set add-symbol method */
toktab->tokthsc.toktfsea = tokthsea; /* set search-table method */
toktab->tokthsc.toktfset = tokthset; /* update symbol */
toktab->tokthsc.toktfeach = toktheach; /* call fn for all symbols */
toktab->tokthsc.tokterr = errctx; /* set error handling context */
toktab->tokthmem = memctx; /* memory manager context */
toktab->tokthcpool = mcmalo(memctx, (ushort)TOKTHSIZE,
&toktab->tokthpool[0]);
toktab->tokthpcnt = 0;
toktab->tokthsize = TOKTHSIZE;
/* set hash table entries to point to nothing (MCMONINV) */
for (i = 0 ; i < TOKHASHSIZE ; ++i)
toktab->tokthhsh[i].tokthpobj = MCMONINV;
}
/* add a symbol to a hashed symbol table */
void tokthadd(toktdef *toktab1, char *name, int namel,
int typ, int val, int hash)
{
int siz = sizeof(toksh1def) + namel;
toksdef *sym;
tokshdef *symh;
tokthdef *toktab = (tokthdef *)toktab1;
if (toktab->tokthsize < siz)
{
mcmcxdef *mctx = toktab->tokthmem;
/* insufficient space in current pool; add a new pool */
if (toktab->tokthpcnt >= TOKPOOLMAX)
errsig(toktab->tokthsc.tokterr, ERR_MANYSYM);
/* unlock current pool page, and note its final size */
mcmunlck(mctx, toktab->tokthpool[toktab->tokthpcnt]);
toktab->tokthfinal[toktab->tokthpcnt] = toktab->tokthofs;
/* allocate a new pool page, and leave it locked */
toktab->tokthcpool = mcmalo(mctx, (ushort)TOKTHSIZE,
&toktab->tokthpool[++(toktab->tokthpcnt)]);
toktab->tokthsize = TOKTHSIZE;
toktab->tokthofs = 0;
}
symh = (tokshdef *)(toktab->tokthcpool + toktab->tokthofs);
sym = &symh->tokshsc;
/* link into list for this hash value */
OSCPYSTRUCT(symh->tokshnxt, toktab->tokthhsh[hash]);
toktab->tokthhsh[hash].tokthpobj = toktab->tokthpool[toktab->tokthpcnt];
toktab->tokthhsh[hash].tokthpofs = toktab->tokthofs;
/* fill in rest of toksdef */
sym->toksval = val;
sym->tokslen = namel;
sym->tokstyp = typ;
sym->tokshsh = hash;
sym->toksfr = 0;
memcpy(sym->toksnam, name, (size_t)namel);
/* update free pool pointer */
siz = osrndsz(siz);
toktab->tokthofs += siz;
if (siz > toktab->tokthsize) toktab->tokthsize = 0;
else toktab->tokthsize -= siz;
}
/*
* Scan a hash chain, calling a callback for each entry. If the
* callback returns TRUE for any symbol, we stop there, and return TRUE.
* If the callback returns FALSE for a symbol, we keep going. If the
* callback returns FALSE for all symbols, we return FALSE.
*/
static int tokthscan(tokthdef *tab, uint hash,
int (*cb)(void *, toksdef *, mcmon),
void *cbctx)
{
tokshdef *symh;
toksdef *sym;
tokthpdef p;
tokthpdef nxt;
uchar *pg = nullptr;
mcmcxdef *mctx = tab->tokthmem;
mcmon curobj;
/* get first object, and lock its page if there is one */
OSCPYSTRUCT(p, tab->tokthhsh[hash]);
if ((curobj = p.tokthpobj) != MCMONINV)
pg = mcmlck(mctx, curobj);
/* look for a match using the callback */
for ( ; p.tokthpobj != MCMONINV ; OSCPYSTRUCT(p, nxt))
{
symh = (tokshdef *)(pg + p.tokthpofs);
sym = &symh->tokshsc;
OSCPYSTRUCT(nxt, symh->tokshnxt);
/* check for a match; copy to return buffer if found */
if ((*cb)(cbctx, sym, p.tokthpobj))
{
mcmunlck(mctx, p.tokthpobj);
return(TRUE);
}
/* if the next page is different from this one, get new lock */
if (nxt.tokthpobj != curobj && nxt.tokthpobj != MCMONINV)
{
mcmunlck(mctx, curobj);
curobj = nxt.tokthpobj;
pg = mcmlck(mctx, curobj);
}
}
/* unlock last object, if we had a lock at all */
if (curobj != MCMONINV) mcmunlck(mctx, curobj);
return(FALSE);
}
struct tokseadef
{
char *tokseanam;
toksdef tokseasym;
toksdef *toksearet;
mcmcxdef *tokseamctx;
};
typedef struct tokseadef tokseadef;
/* search callback */
static int tokthsea1(void *ctx0, toksdef *sym, mcmon objn)
{
tokseadef *ctx = (tokseadef *)ctx0;
VARUSED(objn);
if (sym->tokslen == ctx->tokseasym.tokslen &&
!memcmp(sym->toksnam, ctx->tokseanam, ctx->tokseasym.tokslen))
{
memcpy(ctx->toksearet, sym,
(size_t)(sizeof(toks1def) + ctx->tokseasym.tokslen));
return(TRUE);
}
else
return(FALSE);
}
/* search a hashed symbol table for a symbol */
int tokthsea(toktdef *tab1, char *name, int namel, int hash, toksdef *ret)
{
tokseadef ctx;
ctx.tokseanam = name;
ctx.tokseasym.tokslen = namel;
ctx.toksearet = ret;
return(tokthscan((tokthdef *)tab1, hash, tokthsea1, &ctx));
}
/* callback for tokthset */
static int tokthset1(void *ctx0, toksdef *sym, mcmon objn)
{
tokseadef *ctx = (tokseadef *)ctx0;
if (sym->tokslen == ctx->tokseasym.tokslen
&& !memcmp(sym->toksnam, ctx->tokseasym.toksnam,
ctx->tokseasym.tokslen))
{
sym->toksval = ctx->tokseasym.toksval;
sym->tokstyp = ctx->tokseasym.tokstyp;
/* touch object, since it's been changed */
mcmtch(ctx->tokseamctx, objn);
return(TRUE);
}
else
return(FALSE);
}
/* update a symbol in a hashed symbol table */
void tokthset(toktdef *tab1, toksdef *newsym)
{
tokseadef ctx;
tokthdef *tab = (tokthdef *)tab1;
OSCPYSTRUCT(ctx.tokseasym, *newsym);
ctx.tokseamctx = tab->tokthmem;
tokthscan((tokthdef *)tab1, newsym->tokshsh, tokthset1, &ctx);
}
/* callback for tokthfind */
static int tokthfind1(void *ctx0, toksdef *sym, mcmon objn)
{
tokseadef *ctx = (tokseadef *)ctx0;
VARUSED(objn);
if (sym->toksval == ctx->tokseasym.toksval
&& sym->tokstyp == ctx->tokseasym.tokstyp)
{
memcpy(ctx->toksearet, sym,
(size_t)(sizeof(toks1def) + sym->tokslen));
return(TRUE);
}
else
return(FALSE);
}
/* find a symbol of a particular type and value */
int tokthfind(toktdef *tab1, int typ, uint val, toksdef *ret)
{
tokseadef ctx;
int i;
ctx.tokseasym.tokstyp = typ;
ctx.tokseasym.toksval = val;
ctx.toksearet = ret;
for (i = 0 ; i < TOKHASHSIZE ; ++i)
{
if (tokthscan((tokthdef *)tab1, i, tokthfind1, &ctx))
return(TRUE);
}
return(FALSE);
}
/* call a callback for each function in a hashed symbol table */
void toktheach(toktdef *tab1,
void (*cb)(void *, toksdef *), void *ctx)
{
tokthdef *tab = (tokthdef *)tab1;
uchar *p;
uint max;
uint ofs;
tokshdef *symh;
toksdef *sym;
uint siz;
uint i;
for (i = 0 ; i <= tab->tokthpcnt ; ++i)
{
/* lock the current page */
p = mcmlck(tab->tokthmem, tab->tokthpool[i]);
ERRBEGIN(tab1->tokterr)
max = (i == tab->tokthpcnt ? tab->tokthofs : tab->tokthfinal[i]);
for (ofs = 0 ; ofs < max ; )
{
/* get this symbol */
symh = (tokshdef *)(p + ofs);
sym = &symh->tokshsc;
/* call the user callback */
(*cb)(ctx, sym);
/* advance to the next symbol on this page */
siz = sizeof(toksh1def) + sym->tokslen;
ofs += osrndsz(siz);
}
ERRCLEAN(tab1->tokterr)
mcmunlck(tab->tokthmem, tab->tokthpool[i]);
ERRENDCLN(tab1->tokterr)
/* done with current page; unlock it */
mcmunlck(tab->tokthmem, tab->tokthpool[i]);
}
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,934 @@
/* 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/tads/tads2/run.h"
#include "glk/tads/tads2/vocabulary.h"
#include "glk/tads/tads2/error.h"
#include "glk/tads/tads2/memory_cache_heap.h"
#include "glk/tads/os_glk.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Main vocabulary context. This can be saved globally if desired, so
* that routines that don't have any other access to it (such as
* Unix-style signal handlers) can reach it.
*/
voccxdef *main_voc_ctx = nullptr;
#ifdef VOCW_IN_CACHE
vocwdef *vocwget(voccxdef *ctx, uint idx)
{
uint pg;
if (idx == VOCCXW_NONE)
return 0;
/* get the page we need */
pg = idx/VOCWPGSIZ;
/* if it's not locked, lock it */
if (pg != ctx->voccxwplck)
{
/* unlock the old page */
if (ctx->voccxwplck != MCMONINV)
mcmunlck(ctx->voccxmem, ctx->voccxwp[ctx->voccxwplck]);
/* lock the new page */
ctx->voccxwpgptr = (vocwdef *)mcmlck(ctx->voccxmem,
ctx->voccxwp[pg]);
ctx->voccxwplck = pg;
}
/* return the entry on that page */
return ctx->voccxwpgptr + (idx % VOCWPGSIZ);
}
#endif /*VOCW_IN_CACHE */
/* hash value is based on first 6 characters only to allow match-in-6 */
uint vochsh(const uchar *t, int len)
{
uint ret = 0;
if (len > 6) len = 6;
for ( ; len ; --len, ++t)
ret = (ret + (uint)(vocisupper(*t) ? tolower(*t) : *t))
& (VOCHASHSIZ - 1);
return(ret);
}
/* copy vocabulary word, and convert to lower case */
static void voccpy(uchar *dst, const uchar *src, int len)
{
for ( ; len ; --len, ++dst, ++src)
*dst = vocisupper(*src) ? tolower(*src) : *src;
}
/* allocate and set up a new vocwdef record, linking into a vocdef's list */
static void vocwset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
int classflg)
{
vocwdef *vw;
uint inx;
vocwdef *vw2;
/*
* look through the vocdef list to see if there's an existing entry
* with the DELETED marker -- if so, simply undelete it
*/
for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
inx = vw->vocwnxt, vw = vocwget(ctx, inx))
{
/* if this entry was deleted, and otherwise matches, undelete it */
if ((vw->vocwflg & VOCFDEL)
&& vw->vocwobj == objn && vw->vocwtyp == p)
{
/*
* Remove the deleted flag. We will otherwise leave the
* flags unchanged, since the VOCFDEL flag applies only to
* statically allocated objects, and hence the original
* flags should take precedence over any run-time flags.
*/
vw->vocwflg &= ~VOCFDEL;
/* we're done */
return;
}
}
/* make sure the word+object+type record isn't already defined */
for (inx = v->vocwlst, vw = vocwget(ctx, inx) ; vw ;
inx = vw->vocwnxt, vw = vocwget(ctx, inx))
{
if (vw->vocwobj == objn && vw->vocwtyp == p
&& (vw->vocwflg & VOCFCLASS) == (classflg & VOCFCLASS))
{
/* it matches - don't add a redundant record */
return;
}
}
/* look in the free list for an available vocwdef */
if (ctx->voccxwfre != VOCCXW_NONE)
{
inx = ctx->voccxwfre;
vw = vocwget(ctx, inx); /* get the free vocwdef */
ctx->voccxwfre = vw->vocwnxt; /* unlink from free list */
}
else
{
/* allocate another page of vocwdef's if necssary */
if ((ctx->voccxwalocnt % VOCWPGSIZ) == 0)
{
int pg = ctx->voccxwalocnt / VOCWPGSIZ;
/* make sure we haven't exceeded the available page count */
if (pg >= VOCWPGMAX) errsig(ctx->voccxerr, ERR_VOCMNPG);
/* allocate on the new page */
#ifdef VOCW_IN_CACHE
mcmalo(ctx->voccxmem, (ushort)(VOCWPGSIZ * sizeof(vocwdef)),
&ctx->voccxwp[pg]);
mcmunlck(ctx->voccxmem, ctx->voccxwp[pg]);
#else
ctx->voccxwp[pg] =
(vocwdef *)mchalo(ctx->voccxerr,
(VOCWPGSIZ * sizeof(vocwdef)),
"vocwset");
#endif
}
/* get the next entry, and increment count of used entries */
inx = ctx->voccxwalocnt++;
vw = vocwget(ctx, inx);
}
/* link the new vocwdef into the vocdef's relation list */
vw->vocwnxt = v->vocwlst;
v->vocwlst = inx;
/* set up the new vocwdef */
vw->vocwtyp = (uchar)p;
vw->vocwobj = objn;
vw->vocwflg = classflg;
/*
* Scan the list and make sure we're not adding a redundant verb.
* Don't bother with the warning if this is a class.
*/
if (p == PRP_VERB && (ctx->voccxflg & VOCCXFVWARN)
&& (vw->vocwflg & VOCFCLASS) == 0)
{
for (vw2 = vocwget(ctx, v->vocwlst) ; vw2 ;
vw2 = vocwget(ctx, vw2->vocwnxt))
{
/*
* if this is a different object, and it's not a class, and
* it's defined as a verb, warn about it
*/
if (vw2 != vw
&& (vw2->vocwflg & VOCFCLASS) == 0
&& vw2->vocwtyp == PRP_VERB)
{
if (v->vocln2 != 0)
errlog2(ctx->voccxerr, ERR_VOCREVB,
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt, v->voclen),
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt + v->voclen, v->vocln2));
else
errlog1(ctx->voccxerr, ERR_VOCREVB,
ERRTSTR,
errstr(ctx->voccxerr,
(char *)v->voctxt, v->voclen));
break;
}
}
}
}
/* set up a vocdef record, and link into hash table */
static void vocset(voccxdef *ctx, vocdef *v, prpnum p, objnum objn,
int classflg, uchar *wrdtxt, int len,
uchar *wrd2, int len2)
{
uint hshval = vochsh(wrdtxt, len);
v->vocnxt = ctx->voccxhsh[hshval];
ctx->voccxhsh[hshval] = v;
v->voclen = len;
v->vocln2 = len2;
voccpy(v->voctxt, wrdtxt, len);
if (wrd2) voccpy(v->voctxt + len, wrd2, len2);
/* allocate and initialize a vocwdef for the object */
vocwset(ctx, v, p, objn, classflg);
}
/* internal addword - already parsed into two words and have lengths */
void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg,
uchar *wrdtxt, int len, uchar *wrd2, int len2)
{
vocdef *v;
vocdef *prv;
uint need;
uint hshval;
/* if the word is null, ignore it entirely */
if (len == 0 && len2 == 0)
return;
/* look for a vocdef entry with the same word text */
hshval = vochsh(wrdtxt, len);
for (v = ctx->voccxhsh[hshval] ; v ; v = v->vocnxt)
{
/* if it matches on both words, use this entry */
if (v->voclen == len && !memcmp(wrdtxt, v->voctxt, (size_t)len)
&& ((!wrd2 && v->vocln2 == 0)
|| (v->vocln2 == len2 &&
!memcmp(wrd2, v->voctxt + len, (size_t)len2))))
{
vocwset(ctx, v, p, objn, classflg);
return;
}
}
/* look for a free vocdef entry of the same size */
for (prv = (vocdef *)nullptr, v = ctx->voccxfre ; v ; prv = v, v = v->vocnxt)
if (v->voclen == len + len2) break;
if (v)
{
/* we found something - unlink from free list */
if (prv) prv->vocnxt = v->vocnxt;
else ctx->voccxfre = v->vocnxt;
/* reuse the entry */
v->vocwlst = VOCCXW_NONE;
vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
return;
}
/* didn't find an existing vocdef; allocate a new one */
need = sizeof(vocdef) + len + len2 - 1;
if (ctx->voccxrem < need)
{
/* not enough space in current page; allocate a new one */
ctx->voccxpool = mchalo(ctx->voccxerr, VOCPGSIZ, "vocadd2");
ctx->voccxrem = VOCPGSIZ;
}
/* use top of current pool, and update pool pointer and size */
v = (vocdef *)ctx->voccxpool;
need = osrndsz(need);
ctx->voccxpool += need;
if (ctx->voccxrem > need) ctx->voccxrem -= need;
else ctx->voccxrem = 0;
/* set up new vocdef */
v->vocwlst = VOCCXW_NONE;
vocset(ctx, v, p, objn, classflg, wrdtxt, len, wrd2, len2);
}
static void voc_parse_words(char **wrdtxt, int *len, char **wrd2, int *len2)
{
/* get length and pointer to actual text */
*len = osrp2(*wrdtxt) - 2;
*wrdtxt += 2;
/* see if there's a second word - look for a space */
for (*wrd2 = *wrdtxt, *len2 = *len ; *len2 && !vocisspace(**wrd2) ;
++*wrd2, --*len2) ;
if (*len2)
{
*len -= *len2;
while (*len2 && vocisspace(**wrd2)) ++*wrd2, --*len2;
}
else
{
/* no space ==> no second word */
*wrd2 = (char *)nullptr;
}
}
void vocadd(voccxdef *ctx, prpnum p, objnum objn, int classflg, char *wrdtxt)
{
int len;
char *wrd2;
int len2;
voc_parse_words(&wrdtxt, &len, &wrd2, &len2);
vocadd2(ctx, p, objn, classflg, (uchar *)wrdtxt, len, (uchar *)wrd2, len2);
}
/* make sure we have a page table entry for an object, allocating one if not */
void vocialo(voccxdef *ctx, objnum obj)
{
if (!ctx->voccxinh[obj >> 8])
{
ctx->voccxinh[obj >> 8] =
(vocidef **)mchalo(ctx->voccxerr,
(256 * sizeof(vocidef *)), "vocialo");
memset(ctx->voccxinh[obj >> 8], 0, (size_t)(256 * sizeof(vocidef *)));
}
}
/* add an inheritance/location record */
void vociadd(voccxdef *ctx, objnum obj, objnum loc,
int numsc, objnum *sc, int flags)
{
vocidef *v;
vocidef *min;
vocidef *prv;
vocidef *minprv = nullptr;
/* make sure we have a page table entry for this object */
vocialo(ctx, obj);
/* look in free list for an entry that's big enough */
for (prv = (vocidef *)nullptr, min = (vocidef *)nullptr, v = ctx->voccxifr ; v ;
prv = v, v = v->vocinxt)
{
if (v->vocinsc == numsc)
{
min = v;
minprv = prv;
break;
}
else if (v->vocinsc > numsc)
{
if (!min || v->vocinsc < min->vocinsc)
{
min = v;
minprv = prv;
}
}
}
if (!min)
{
uint need;
/* nothing in free list; allocate a new entry */
need = osrndsz(sizeof(vocidef) + (numsc - 1)*sizeof(objnum));
if (ctx->voccxilst + need >= VOCISIZ)
{
/* nothing left on current page; allocate a new page */
ctx->voccxip[++(ctx->voccxiplst)] =
mchalo(ctx->voccxerr, VOCISIZ, "vociadd");
ctx->voccxilst = 0;
}
/* allocate space out of current page */
v = (vocidef *)(ctx->voccxip[ctx->voccxiplst] + ctx->voccxilst);
ctx->voccxilst += need;
}
else
{
/* unlink from chain and use */
v = min;
if (minprv)
minprv->vocinxt = v->vocinxt;
else
ctx->voccxifr = v->vocinxt;
}
/* set up the entry */
if (vocinh(ctx, obj) != (vocidef *)nullptr) errsig(ctx->voccxerr, ERR_VOCINUS);
v->vociloc = loc;
v->vociilc = MCMONINV;
v->vociflg = (flags & ~VOCIFXLAT);
v->vocinsc = numsc;
if (numsc)
{
if (flags & VOCIFXLAT)
{
int i;
for (i = 0 ; i < numsc ; ++i)
v->vocisc[i] = osrp2(&sc[i]);
}
else
memcpy(v->vocisc, sc, (size_t)(numsc * sizeof(objnum)));
}
vocinh(ctx, obj) = v; /* set page table entry */
}
/* revert all objects to original state, using inheritance records */
void vocrevert(voccxdef *vctx)
{
vocidef ***vpg;
vocidef **v;
int i;
int j;
objnum obj;
/*
* Go through the inheritance records. Delete each dynamically
* allocated object, and revert each static object to its original
* load state.
*/
for (vpg = vctx->voccxinh, i = 0 ; i < VOCINHMAX ; ++vpg, ++i)
{
if (!*vpg) continue;
for (v = *vpg, obj = (i << 8), j = 0 ; j < 256 ; ++v, ++obj, ++j)
{
if (*v)
{
/* if the object was dynamically allocated, delete it */
if ((*v)->vociflg & VOCIFNEW)
{
/* delete vocabulary and inheritance data for the object */
vocidel(vctx, obj);
vocdel(vctx, obj);
/* delete the object */
mcmfre(vctx->voccxmem, (mcmon)obj);
}
else
{
/* revert the object */
mcmrevert(vctx->voccxmem, (mcmon)obj);
}
}
}
}
/*
* Revert the vocabulary list: delete all newly added words, and
* undelete all original words marked as deleted.
*/
vocdel1(vctx, MCMONINV, (char *)nullptr, 0, TRUE, TRUE, FALSE);
}
/* initialize voc context */
void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx,
runcxdef *runctx, objucxdef *undoctx,
int fuses, int daemons, int notifiers)
{
CLRSTRUCT(*vocctx);
vocctx->voccxerr = errctx;
vocctx->voccxiplst = (uint)-1;
vocctx->voccxilst = VOCISIZ;
vocctx->voccxmem = memctx;
vocctx->voccxrun = runctx;
vocctx->voccxundo = undoctx;
vocctx->voccxme =
vocctx->voccxme_init =
vocctx->voccxvtk =
vocctx->voccxstr =
vocctx->voccxnum =
vocctx->voccxit =
vocctx->voccxhim =
vocctx->voccxprd =
vocctx->voccxpre =
vocctx->voccxpre2 =
vocctx->voccxppc =
vocctx->voccxlsv =
vocctx->voccxpreinit =
vocctx->voccxper =
vocctx->voccxprom =
vocctx->voccxpostprom =
vocctx->voccxpdis =
vocctx->voccxper2 =
vocctx->voccxperp =
vocctx->voccxpdef =
vocctx->voccxpdef2 =
vocctx->voccxpask =
vocctx->voccxpask2 =
vocctx->voccxpask3 =
vocctx->voccxinitrestore =
vocctx->voccxpuv =
vocctx->voccxpnp =
vocctx->voccxpostact =
vocctx->voccxendcmd =
vocctx->voccxher = MCMONINV;
vocctx->voccxthc = 0;
#ifdef VOCW_IN_CACHE
vocctx->voccxwplck = MCMONINV;
#endif
vocctx->voccxactor = MCMONINV;
vocctx->voccxverb = MCMONINV;
vocctx->voccxprep = MCMONINV;
vocctx->voccxdobj = nullptr;
vocctx->voccxiobj = nullptr;
vocctx->voccxunknown = 0;
vocctx->voccxlastunk = 0;
vocctx->voc_stk_ptr = nullptr;
vocctx->voc_stk_cur = nullptr;
vocctx->voc_stk_end = nullptr;
/* allocate fuses, daemons, notifiers */
vocinialo(vocctx, &vocctx->voccxfus, (vocctx->voccxfuc = fuses));
vocinialo(vocctx, &vocctx->voccxdmn, (vocctx->voccxdmc = daemons));
vocinialo(vocctx, &vocctx->voccxalm, (vocctx->voccxalc = notifiers));
/* no entries in vocwdef free list yet */
vocctx->voccxwfre = VOCCXW_NONE;
}
/* uninitialize the voc context */
void vocterm(voccxdef *ctx)
{
/* delete the fuses, daemons, and notifiers */
voctermfree(ctx->voccxfus);
voctermfree(ctx->voccxdmn);
voctermfree(ctx->voccxalm);
/* delete the private stack */
if (ctx->voc_stk_ptr != nullptr)
mchfre(ctx->voc_stk_ptr);
}
/* clean up the vocab context */
void voctermfree(vocddef *what)
{
if (what != nullptr)
mchfre(what);
}
/*
* Iterate through all words for a particular object, calling a
* function with each vocwdef found. If objn == MCMONINV, we'll call
* the callback for every word.
*/
void voc_iterate(voccxdef *ctx, objnum objn,
void (*fn)(void *, vocdef *, vocwdef *), void *fnctx)
{
int i;
vocdef *v;
vocdef **vp;
vocwdef *vw;
uint idx;
/* go through each hash value looking for matching words */
for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
{
/* go through all words in this hash chain */
for (v = *vp ; v ; v = v->vocnxt)
{
/* go through each object relation for this word */
for (idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
idx = vw->vocwnxt, vw = vocwget(ctx, idx))
{
/*
* if this word is for this object, call the callback
*/
if (objn == MCMONINV || vw->vocwobj == objn)
(*fn)(fnctx, v, vw);
}
}
}
}
/* callback context for voc_count */
struct voc_count_ctx
{
int cnt;
int siz;
prpnum prp;
};
/* callback for voc_count */
static void voc_count_cb(void *ctx0, vocdef *voc, vocwdef *vocw)
{
struct voc_count_ctx *ctx = (struct voc_count_ctx *)ctx0;
VARUSED(vocw);
/*
* If it matches the property (or we want all properties), count
* it. Don't count deleted objects.
*/
if ((ctx->prp == 0 || ctx->prp == vocw->vocwtyp)
&& !(vocw->vocwflg & VOCFDEL))
{
/* count the word */
ctx->cnt++;
/* count the size */
ctx->siz += voc->voclen + voc->vocln2;
}
}
/*
* Get the number and size of words defined for an object. The size
* returns the total byte count from all the words involved. Do not
* include deleted words in the count.
*/
void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz)
{
struct voc_count_ctx fnctx;
/* set up the context with zero initial counts */
fnctx.cnt = 0;
fnctx.siz = 0;
fnctx.prp = prp;
/* iterate over all words for the object with our callback */
voc_iterate(ctx, objn, voc_count_cb, &fnctx);
/* return the data */
if (cnt) *cnt = fnctx.cnt;
if (siz) *siz = fnctx.siz;
}
/*
* Delete a particular word associated with an object, or all words if
* the word pointer is null. If the word isn't marked as added at
* runtime (i.e., the VOCFNEW flag is not set for the word), the word is
* simply marked as deleted, rather than being actually deleted.
* However, if the 'really_delete' flag is set, the word is actually
* deleted. If the 'revert' flag is true, this routine deletes _every_
* dynamically created word, and undeletes all dynamically deleted words
* that were in the original vocabulary.
*/
void vocdel1(voccxdef *ctx, objnum objn, char *wrd1, prpnum prp,
int really_delete, int revert, int keep_undo)
{
int i;
vocdef *v;
vocdef *prv;
vocdef *nxt;
vocdef **vp;
vocwdef *vw;
vocwdef *prvw;
vocwdef *nxtw;
uint nxtidx;
uint idx;
int deleted_vocdef;
char *wrd2 = nullptr;
int len1 = 0, len2 = 0;
int do_del;
char *orgwrd;
/* parse the word if provided */
orgwrd = wrd1;
if (wrd1)
voc_parse_words(&wrd1, &len1, &wrd2, &len2);
/* go through each hash value looking for matching words */
for (i = VOCHASHSIZ, vp = ctx->voccxhsh ; i ; ++vp, --i)
{
/* go through all words in this hash chain */
for (prv = (vocdef *)nullptr, v = *vp ; v ; v = nxt)
{
/* remember next word in hash chain */
nxt = v->vocnxt;
/* if this word doesn't match, skip it */
if (wrd1)
{
/* compare the first word */
if (v->voclen != len1
|| memicmp((char *)v->voctxt, wrd1, (size_t)len1))
{
prv = v;
continue;
}
/* if there's a second word, compare it as well */
if (wrd2 && (v->vocln2 != len2
|| memicmp((char *)v->voctxt + len1,
wrd2, (size_t)len2)))
{
prv = v;
continue;
}
}
/* presume we're not going to delete this vocdef */
deleted_vocdef = FALSE;
/* go through all object relations for this word */
for (prvw = nullptr, idx = v->vocwlst, vw = vocwget(ctx, idx) ; vw ;
vw = nxtw, idx = nxtidx)
{
/* remember next word in relation list */
nxtidx = vw->vocwnxt;
nxtw = vocwget(ctx, nxtidx);
/*
* figure out whether to delete this word, based on the
* caller's specified operating mode
*/
if (revert)
{
/* if reverting, delete all new words */
do_del = (vw->vocwflg & VOCFNEW);
/* also, remove the DELETED flag if present */
vw->vocwflg &= ~VOCFDEL;
}
else
{
/*
* delete the word if the object number matches,
* AND either we're not searching for a specific
* vocabulary word (in which case wrd1 will be null)
* or the word matches the vocabulary word we're
* seeking (in which case the part of speech must
* match)
*/
do_del = (vw->vocwobj == objn
&& (wrd1 == nullptr || vw->vocwtyp == prp));
/*
* if we're not in really_delete mode, and the word
* matches and it wasn't dynamically added at
* run-time, simply mark it as deleted -- this will
* allow it to be undeleted if the game is reverted
* to RESTART conditions
*/
if (do_del && !really_delete && !(vw->vocwflg & VOCFNEW))
{
/* geneate undo for the operation */
if (keep_undo && orgwrd)
vocdusave_delwrd(ctx, objn, prp,
vw->vocwflg, orgwrd);
/* just mark the word for deletion, but keep vw */
vw->vocwflg |= VOCFDEL;
do_del = FALSE;
}
}
/* now delete the structure if we decided we should */
if (do_del)
{
/* geneate undo for the operation */
if (keep_undo && orgwrd)
vocdusave_delwrd(ctx, objn, prp, vw->vocwflg, orgwrd);
/* unlink this vocwdef from the vocdef's list */
if (prvw)
prvw->vocwnxt = vw->vocwnxt;
else
v->vocwlst = vw->vocwnxt;
/* link the vocwdef into the vocwdef free list */
vw->vocwnxt = ctx->voccxwfre;
ctx->voccxwfre = idx;
/*
* if there's nothing left in the vocdef's list,
* delete the entire vocdef as well
*/
if (v->vocwlst == VOCCXW_NONE)
{
if (prv) prv->vocnxt = v->vocnxt;
else *vp = v->vocnxt;
/* link into free chain */
v->vocnxt = ctx->voccxfre;
ctx->voccxfre = v;
/* note that it's been deleted */
deleted_vocdef = TRUE;
}
}
else
{
/* we're not deleting the word, so move prvw forward */
prvw = vw;
}
}
/* if we didn't delete this vocdef, move prv forward onto it */
if (!deleted_vocdef)
prv = v;
}
}
}
/* delete all vocabulary for an object */
void vocdel(voccxdef *ctx, objnum objn)
{
vocdel1(ctx, objn, (char *)nullptr, (prpnum)0, TRUE, FALSE, FALSE);
}
/* delete object inheritance records for a particular object */
void vocidel(voccxdef *ctx, objnum obj)
{
vocidef *v;
/* get entry out of page table, and clear page table slot */
v = vocinh(ctx, obj);
vocinh(ctx, obj) = (vocidef *)nullptr;
/* link into free list */
if (v)
{
v->vocinxt = ctx->voccxifr;
ctx->voccxifr = v;
}
}
/*
* Find template matching a verb+object+prep combination; return TRUE
* if a suitable template is found, FALSE otherwise.
*/
int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep,
uchar *tplout, int *newstyle)
{
uchar *tplptr;
uchar *thistpl;
int found;
int tplcnt;
uint tplofs;
objnum verb;
/* look for a new-style template first */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, FALSE);
if (tplofs)
{
/* flag the presence of the new-style template */
*newstyle = TRUE;
}
else
{
/* no new-style template - look for old version */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, FALSE);
*newstyle = FALSE;
}
/* inherit templates until we run out of them */
for (;;)
{
/* if we found something already, use it */
if (tplofs)
{
size_t siz;
/* figure the size of this template style */
siz = (*newstyle ? VOCTPL2SIZ : VOCTPLSIZ);
/* lock the verb object, and get the property value pointer */
tplptr = mcmlck(ctx->voccxmem, verb);
thistpl = prpvalp(tplptr + tplofs);
/* first byte is number of templates in array */
tplcnt = *thistpl++;
/* look for a template that matches the preposition object */
for (found = FALSE ; tplcnt ; thistpl += siz, --tplcnt)
{
if (voctplpr(thistpl) == prep)
{
found = TRUE;
break;
}
}
/* unlock the object and return the value if we found one */
mcmunlck(ctx->voccxmem, verb);
if (found)
{
memcpy(tplout, thistpl, siz);
return(TRUE);
}
}
/* try inheriting a template (new-style, then old-style) */
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL2, &verb, TRUE);
if (tplofs)
*newstyle = TRUE;
else
{
tplofs = objgetap(ctx->voccxmem, verb_in, PRP_TPL, &verb, TRUE);
*newstyle = FALSE;
}
/* return not-found if we couldn't inherit it */
if (!tplofs)
return FALSE;
/* use the newly found verb */
verb_in = verb;
}
}
/*
* Set the "Me" object
*/
void voc_set_me(voccxdef *ctx, objnum new_me)
{
/* save undo for the change */
vocdusave_me(ctx, ctx->voccxme);
/* set the new "Me" object */
ctx->voccxme = new_me;
}
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk

View File

@@ -0,0 +1,799 @@
/* 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/>.
*
*/
/*
* Defines TADS vocabulary (player command parser) functionality
*/
#ifndef GLK_TADS_TADS2_VOCABULARY
#define GLK_TADS_TADS2_VOCABULARY
#include "common/util.h"
#include "glk/tads/tads2/lib.h"
#include "glk/tads/tads2/object.h"
#include "glk/tads/tads2/property.h"
#include "glk/tads/tads2/run.h"
namespace Glk {
namespace TADS {
namespace TADS2 {
/*
* Cover macro for parser errors. Any parser error should be covered
* with this macro for documentation and search purposes. (The macro
* doesn't do anything - this is just something to search for when we're
* trying to enumerate parser error codes.)
*/
#define VOCERR(errcode) errcode
/* maximum number of objects matching an ambiguous word */
#define VOCMAXAMBIG 200
/* size of input buffer */
#define VOCBUFSIZ 128
/*
* Vocabulary relation structure - this structure relates a vocabulary
* word to an object and part of speech. A list of these structures is
* attached to each vocabulary word structure to provide the word's
* meanings.
*/
struct vocwdef {
uint vocwnxt; /* index of next vocwdef attached to the same word */
objnum vocwobj; /* object associated with the word */
uchar vocwtyp; /* property associated with the word (part of speech) */
uchar vocwflg; /* flags for the word */
#define VOCFCLASS 1 /* word is for a class object */
#define VOCFINH 2 /* word is inherited from a superclass */
#define VOCFNEW 4 /* word was added at run-time */
#define VOCFDEL 8 /* word has been deleted */
};
/* vocabulary word structure */
struct vocdef {
vocdef *vocnxt; /* next word at same hash value */
uchar voclen; /* length of the word */
uchar vocln2; /* length of second word (0 if no second word) */
uint vocwlst; /* head of list of vocwdef's attached to the word */
uchar voctxt[1]; /* text of the word */
};
/* vocabulary inheritance cell */
struct vocidef {
uchar vocinsc; /* # of superclasses (gives size of record) */
union {
struct {
uchar vociusflg; /* flags for entry */
#define VOCIFCLASS 1 /* entry refers to a class object (loc records only) */
#define VOCIFVOC 2 /* entry has vocabulary words defined */
#define VOCIFXLAT 4 /* superclasses must be translated from portable fmt */
#define VOCIFLOCNIL 8 /* location is explicitly set to nil */
#define VOCIFNEW 16 /* object was allocated at run-time with "new" */
objnum vociusloc; /* location of the object */
objnum vociusilc; /* inherited location */
objnum vociussc[1]; /* array of superclasses */
} vocius;
vocidef *vociunxt;
} vociu;
#define vociflg vociu.vocius.vociusflg
#define vociloc vociu.vocius.vociusloc
#define vociilc vociu.vocius.vociusilc
#define vocisc vociu.vocius.vociussc
#define vocinxt vociu.vociunxt
};
/* size of a page in a vocabulary pool */
#define VOCPGSIZ 8192
/* number of bytes in an inheritance cell page */
#define VOCISIZ 8192
/* maximum number of inheritance pages */
#define VOCIPGMAX 32
/* maximum number of inheritance pages (256 objects per page) */
#define VOCINHMAX 128
/* size of vocabulary hash table */
#define VOCHASHSIZ 256
/* size of a template structure */
#define VOCTPLSIZ 10
/* new-style template structure */
#define VOCTPL2SIZ 16
/*
* vocwdef's are fixed in size. They're allocated in a set of arrays
* (the voccxwp member of the voc context has the list of arrays). Each
* array is of a fixed number of vocwdef entries; a maximum number of
* vocwdef arrays is possible.
*/
#define VOCWPGSIZ 2000 /* number of vocwdef's per array */
#define VOCWPGMAX 16 /* maximum number of vocwdef arrays */
/*
* To find a vocwdef entry given its index, divide the index by the
* number of entries per array to find the array number, and use the
* remainder to find the index within that array.
*/
/*#define VOCW_IN_CACHE*/
#ifdef VOCW_IN_CACHE
vocwdef *vocwget(struct voccxdef *ctx, uint idx);
#else
#define vocwget(ctx, idx) \
((idx) == VOCCXW_NONE ? (vocwdef *)0 : \
((ctx)->voccxwp[(idx)/VOCWPGSIZ] + ((idx) % VOCWPGSIZ)))
#endif
/*
* Special values for vocdtim - these values indicate that the daemon
* does not have a normal turn-based expiration time.
*/
#define VOCDTIM_EACH_TURN 0xffff /* the daemon fires every turn */
/* daemon/fuse/alarm slot */
struct vocddef {
objnum vocdfn; /* object number of function to be invoked */
runsdef vocdarg; /* argument for daemon/fuse function */
prpnum vocdprp; /* property number (used only for alarms) */
uint vocdtim; /* time for fuses/alarms (0xffff -> each-turn alarm) */
};
/* vocabulary object list entry */
struct vocoldef {
objnum vocolobj; /* object matching the word */
const char *vocolfst; /* first word in cmd[] that identified object */
const char *vocollst; /* last word in cmd[] that identified object */
char *vocolhlst; /* hypothetical last word, if we trimmed a prep */
int vocolflg; /* special flags (ALL, etc) */
};
/* vocabulary context */
struct voccxdef {
errcxdef *voccxerr; /* error handling context */
tiocxdef *voccxtio; /* text i/o context */
runcxdef *voccxrun; /* execution context */
mcmcxdef *voccxmem; /* memory manager context */
objucxdef *voccxundo; /* undo context */
uchar *voccxpool; /* next free byte in vocdef pool */
vocdef *voccxfre; /* head of vocdef free list */
char *voccxcpp; /* pointer to compound word area */
int voccxcpl; /* length of compound word area */
char *voccxspp; /* pointer to special word area */
int voccxspl; /* length of special word area */
uint voccxrem; /* number of bytes remaining in vocdef pool */
vocidef **voccxinh[VOCINHMAX]; /* vocidef page table: 256 per page */
uchar *voccxip[VOCIPGMAX]; /* inheritance cell pool */
vocidef *voccxifr; /* head of inheritance cell free list */
uint voccxiplst; /* last inheritance cell page allocated */
uint voccxilst; /* next unused byte in last inheritance page */
int voccxredo; /* flag: redo command in buffer */
/*
* redo buffer - if voccxredo is set, and this buffer is not empty,
* we'll redo the command in this buffer rather than the one in our
* internal stack buffer
*/
char voccxredobuf[VOCBUFSIZ];
/*
* "again" buffer - when we save the last command for repeating via
* the "again" command, we'll save the direct and indirect object
* words here, so that they can be recovered if "again" is used
*/
char voccxagainbuf[VOCBUFSIZ];
vocdef *voccxhsh[VOCHASHSIZ]; /* hash table */
#ifdef VOCW_IN_CACHE
mcmon voccxwp[VOCWPGMAX]; /* list of pages of vocab records */
mcmon voccxwplck; /* locked page of vocab records */
vocwdef *voccxwpgptr; /* pointer to currently locked page */
#else
vocwdef *voccxwp[VOCWPGMAX]; /* vocabulary word pool */
#endif
uint voccxwalocnt; /* number of vocwdef's used so far */
uint voccxwfre; /* index of first vocwdef in free list */
#define VOCCXW_NONE ((uint)(-1)) /* index value indicating end of list */
vocddef *voccxdmn; /* array of daemon slots */
uint voccxdmc; /* number of slots in daemon array */
vocddef *voccxfus; /* array of fuse slots */
uint voccxfuc; /* number of slots in fuse array */
vocddef *voccxalm; /* array of alarm slots */
uint voccxalc; /* number of slots in alarm array */
char voccxtim[26]; /* game's timestamp (asctime value) */
objnum voccxvtk; /* object number of "take" deepverb */
objnum voccxme; /* object number of "Me" actor */
objnum voccxme_init; /* initial setting of "Me" */
objnum voccxstr; /* object number of "strObj" */
objnum voccxnum; /* object number of "numObj" */
objnum voccxit; /* last "it" value */
objnum voccxhim; /* last "him" value */
objnum voccxher; /* last "her" value */
objnum voccxthc; /* count of items in "them" list */
objnum voccxthm[VOCMAXAMBIG]; /* list of items in "them" */
objnum voccxprd; /* "pardon" function object number */
objnum voccxpre; /* "preparse" function object number */
objnum voccxppc; /* "preparseCmd" function object number */
objnum voccxpre2; /* "preparseExt" function object number */
objnum voccxvag; /* "again" verb object */
objnum voccxini; /* "init" function */
objnum voccxper; /* "parseError" function object number */
objnum voccxprom; /* "cmdPrompt" function object number */
objnum voccxpostprom; /* "cmdPostPrompt" function object number */
objnum voccxpdis; /* parseDisambig function */
objnum voccxper2; /* parseError2 function */
objnum voccxperp; /* parseErrorParam function */
objnum voccxpdef; /* parseDefault function */
objnum voccxpdef2; /* parseDefaultExt function */
objnum voccxpask; /* parseAskobj function */
objnum voccxpask2; /* parseAskobjActor function */
objnum voccxpask3; /* parseAskobjIndirect function */
objnum voccxinitrestore; /* "initRestore" function object number */
objnum voccxpuv; /* parseUnknownVerb function object number */
objnum voccxpnp; /* parseNounPhrase function object number */
objnum voccxpostact; /* postAction function object number */
objnum voccxprecmd; /* preCommand function object number */
objnum voccxendcmd; /* endCommand function object number */
/* current command word list values */
vocoldef *voccxdobj; /* current direct object word list */
vocoldef *voccxiobj; /* current indirect object word list */
/* current command objects */
objnum voccxactor; /* current actor */
objnum voccxverb; /* current command deepverb */
objnum voccxprep; /* current command preposition */
/* previous command values - used by "again" */
objnum voccxlsa; /* previous actor */
objnum voccxlsv; /* previous verb */
vocoldef voccxlsd; /* previous direct object */
vocoldef voccxlsi; /* previous indirect object */
objnum voccxlsp; /* preposition */
int voccxlssty; /* style (new/old) of last template */
uchar voccxlst[VOCTPL2SIZ]; /* template */
objnum voccxpreinit; /* preinit function */
/* special flags */
uchar voccxflg;
#define VOCCXFCLEAR 1 /* ignore remainder of command line (restore) */
#define VOCCXFVWARN 2 /* generate redundant verb warnings */
#define VOCCXFDBG 4 /* debug mode: show parsing information */
#define VOCCXAGAINDEL 8 /* "again" lost due to object deletion */
/* number of remaining unresolved unknown words in the command */
int voccxunknown;
/* total number of unresolved words in the last command */
int voccxlastunk;
/* parser stack area */
uchar *voc_stk_ptr;
uchar *voc_stk_cur;
uchar *voc_stk_end;
};
/* allocate and push a list, returning a pointer to the list's memory */
uchar *voc_push_list_siz(voccxdef *ctx, uint lstsiz);
/* push a list of objects from a vocoldef array */
void voc_push_vocoldef_list(voccxdef *ctx, vocoldef *objlist, int cnt);
/* push a list of objects from an objnum array */
void voc_push_objlist(voccxdef *ctx, objnum objlist[], int cnt);
/* change the player character ("Me") object */
void voc_set_me(voccxdef *ctx, objnum new_me);
/* add a vocabulary word */
void vocadd(voccxdef *ctx, prpnum p, objnum objn,
int classflag, char *wrdval);
/* internal addword - must already be split into two words and lengths */
void vocadd2(voccxdef *ctx, prpnum p, objnum objn, int classflg,
uchar *wrd1, int len1, uchar *wrd2, int len2);
/* delete vocabulary for a given object */
void vocdel(voccxdef *ctx, objnum objn);
/* lower-level vocabulary deletion routine */
void vocdel1(voccxdef *ctx, objnum objn, char *wrd, prpnum prp,
int really_delete, int revert, int keep_undo);
/* delete all inherited vocabulary */
void vocdelinh(voccxdef *ctx);
/* allocate space for an inheritance record if needed */
void vocialo(voccxdef *ctx, objnum obj);
/* add an inheritance/location record */
void vociadd(voccxdef *ctx, objnum obj, objnum loc,
int numsc, objnum *sc, int flags);
/* delete inheritance records for an object */
void vocidel(voccxdef *ctx, objnum chi);
/* renumber an object's inheritance records - used for 'modify' */
void vociren(voccxdef *ctx, objnum oldnum, objnum newnum);
/* caller-provided context structure for vocffw/vocfnw searches */
struct vocseadef {
vocdef *v;
vocwdef *vw;
const uchar *wrd1;
int len1;
const uchar *wrd2;
int len2;
};
/* find first word matching a given word */
vocwdef *vocffw(voccxdef *ctx, const char *wrd, int len, const char *wrd2, int len2,
int p, vocseadef *search_ctx);
/* find next word */
vocwdef *vocfnw(voccxdef *voccx, vocseadef *search_ctx);
/* read a line of input text */
int vocread(voccxdef *ctx, objnum actor, objnum verb,
char *buf, int bufl, int type);
#define VOCREAD_OK 0
#define VOCREAD_REDO 1
/* compute size of a vocoldef list */
int voclistlen(vocoldef *lst);
/* tokenize an input buffer */
int voctok(voccxdef *ctx, char *cmd, char *outbuf,
char **wrd, int lower, int cvt_ones, int show_errors);
/* get types for a word list */
int vocgtyp(voccxdef *ctx, char **cmd, int *types, char *orgbuf, size_t orgbuflen);
/* execute a player command */
int voccmd(voccxdef *ctx, char *cmd, uint cmdlen);
/* disambiguator */
int vocdisambig(voccxdef *ctx, vocoldef *outlist, vocoldef *inlist,
prpnum defprop, prpnum accprop, prpnum verprop,
char *cmd[], objnum otherobj, objnum cmdActor,
objnum cmdVerb, objnum cmdPrep, char *cmdbuf,
size_t cmdlen, int silent);
/* display a multiple-object prefix */
void voc_multi_prefix(voccxdef *ctx, objnum objn,
int show_prefix, int multi_flags,
int cur_index, int count);
/* low-level executor */
int execmd(voccxdef *ctx, objnum actor, objnum prep,
char *vverb, char *vprep, vocoldef *dolist, vocoldef *iolist,
char **cmd, int *typelist, char *cmdbuf, size_t cmdlen,
int wrdcnt, uchar **preparse_list, int *next_start);
/* recursive command execution */
int execmd_recurs(voccxdef *ctx, objnum actor, objnum verb,
objnum dobj, objnum prep, objnum iobj,
int validate_dobj, int validate_iobj);
/* try running preparseCmd user function */
int try_preparse_cmd(voccxdef *ctx, char **cmd, int wrdcnt,
uchar **preparse_list);
/*
* Handle an unknown verb or sentence structure. We'll call this when
* we encounter a sentence where we don't know the verb word, or we
* don't know the combination of verb and verb preposition, or we don't
* recognize the sentence structure (for example, an indirect object is
* present, but we don't have a template defined using an indirect
* object for the verb).
*
* 'wrdcnt' is the number of words in the cmd[] array. If wrdcnt is
* zero, we'll automatically count the array entries, with the end of
* the array indicated by a null pointer entry.
*
* If do_fuses is true, we'll execute the fuses and daemons if the
* function exists and doesn't throw an ABORT error, or any other
* run-time error other than EXIT.
*
* This function calls the game-defined function parseUnknownVerb, if it
* exists. If the function doesn't exist, we'll simply display the
* given error message, using the normal parseError mechanism. The
* function should use "abort" or "exit" if it wants to cancel further
* processing of the command.
*
* We'll return true if the function exists and executes successfully,
* in which case normal processing should continue with any remaining
* command on the command line. We'll return false if the function
* doesn't exist or throws an error other than EXIT, in which case the
* remainder of the command should be aborted.
*/
int try_unknown_verb(voccxdef *ctx, objnum actor,
char **cmd, int *typelist, int wrdcnt, int *next_start,
int do_fuses, int err, const char *msg, ...);
/* find a template */
int voctplfnd(voccxdef *ctx, objnum verb_in, objnum prep,
uchar *tplout, int *newstyle);
/* build a printable name for an object from the words in a command list */
void voc_make_obj_name(voccxdef *ctx, char *namebuf, char *cmd[],
int firstwrd, int lastwrd);
void voc_make_obj_name_from_list(voccxdef *ctx, char *namebuf,
char *cmd[], const char *firstwrd, const char *lastwrd);
/*
* check noun - determines whether the next set of words is a valid noun
* phrase. No complaint is issued if not; this check is generally made
* to figure out what type of sentence we're dealing with. This is
* simple; we just call vocgobj() with the complaint flag turned off.
*/
/* int vocchknoun(voccxdef *ctx, char **cmd, int *typelist, int cur,
int *next, vocoldef *nounlist, int chkact); */
#define vocchknoun(ctx, cmd, typelist, cur, next, nounlist, chkact) \
vocgobj(ctx, cmd, typelist, cur, next, FALSE, nounlist, TRUE, chkact, 0)
#define vocchknoun2(ctx, cmd, typlst, cur, next, nounlist, chkact, nomatch) \
vocgobj(ctx, cmd, typlst, cur, next, FALSE, nounlist, TRUE, chkact, nomatch)
/*
* get noun - reads an object list. We simply call vocgobj() with the
* complaint and multiple-noun flags turned on.
*/
/* int vocgetnoun(voccxdef *ctx, char **cmd, int *typelist, int cur,
int *next, vocoldef *nounlist); */
#define vocgetnoun(ctx, cmd, typelist, cur, next, nounlist) \
vocgobj(ctx, cmd, typelist, cur, next, TRUE, nounlist, TRUE, FALSE, 0)
/* get object */
int vocgobj(voccxdef *ctx, char **cmd, int *typelist, int cur,
int *next, int complain, vocoldef *nounlist,
int multi, int chkact, int *nomatch);
/* tokenize a string - TADS program code interface */
void voc_parse_tok(voccxdef *ctx);
/* get token types - TADS program code interface */
void voc_parse_types(voccxdef *ctx);
/* get objects matching all of the given words - TADS program code interface */
void voc_parse_dict_lookup(voccxdef *ctx);
/* parse a noun list - TADS program code interface */
void voc_parse_np(voccxdef *ctx);
/* disambiguate a noun list - TADS program code interface */
void voc_parse_disambig(voccxdef *ctx);
/* replace the current command - TADS program code interface */
void voc_parse_replace_cmd(voccxdef *ctx);
/* check access to an object */
int vocchkaccess(voccxdef *ctx, objnum obj, prpnum verprop,
int seqno, objnum actor, objnum verb);
/* check to see if an object is visible */
int vocchkvis(voccxdef *ctx, objnum obj, objnum cmdActor);
/* display an appropriate message for an unreachable object */
void vocnoreach(voccxdef *ctx, objnum *list1, int cnt,
objnum actor, objnum verb, objnum prep, prpnum defprop,
int show_multi_prefix, int multi_flags,
int multi_base_index, int multi_total_count);
/* set {numObj | strObj}.value, as appropriate */
void vocsetobj(voccxdef *ctx, objnum obj, dattyp typ, const void *val,
vocoldef *inobj, vocoldef *outobj);
/* macros to read values out of templates */
#define voctplpr(tpl) ((objnum)osrp2(((uchar *)tpl))) /* preposition */
#define voctplvi(tpl) ((prpnum)osrp2(((uchar *)tpl) + 2)) /* verIoVerb */
#define voctplio(tpl) ((prpnum)osrp2(((uchar *)tpl) + 4)) /* ioVerb */
#define voctplvd(tpl) ((prpnum)osrp2(((uchar *)tpl) + 6)) /* verDoVerb */
#define voctpldo(tpl) ((prpnum)osrp2(((uchar *)tpl) + 8)) /* doVerb */
#define voctplflg(tpl) (*(((uchar *)tpl) + 10)) /* flags */
/* flag values for the voctplflg */
#define VOCTPLFLG_DOBJ_FIRST 0x01 /* disambiguate direct object first */
/* word type flags */
#define VOCT_ARTICLE 1
#define VOCT_ADJ 2
#define VOCT_NOUN 4
#define VOCT_PREP 8
#define VOCT_VERB 16
#define VOCT_SPEC 32 /* special words - "of", ",", ".", etc. */
#define VOCT_PLURAL 64
#define VOCT_UNKNOWN 128 /* word is unknown */
/* special type flags */
#define VOCS_ALL 1 /* "all" */
#define VOCS_EXCEPT 2 /* "except" */
#define VOCS_IT 4 /* "it" */
#define VOCS_THEM 8 /* "them" */
#define VOCS_NUM 16 /* a number */
#define VOCS_COUNT 32 /* a number being used as a count */
#define VOCS_PLURAL 64 /* plural */
#define VOCS_ANY 128 /* "any" */
#define VOCS_HIM 256 /* "him" */
#define VOCS_HER 512 /* "her" */
#define VOCS_STR 1024 /* a quoted string */
#define VOCS_UNKNOWN 2048 /* noun phrase contains an unknown word */
#define VOCS_ENDADJ 4096 /* word matched adjective at end of phrase */
#define VOCS_TRUNC 8192 /* truncated match - word is leading substring */
#define VOCS_TRIMPREP 16384 /* trimmed prep phrase: assumed it was for verb */
/* special internally-defined one-character word flags */
#define VOCW_AND ','
#define VOCW_THEN '.'
#define VOCW_OF 'O'
#define VOCW_ALL 'A'
#define VOCW_BOTH 'B'
#define VOCW_IT 'I'
#define VOCW_HIM 'M'
#define VOCW_ONE 'N'
#define VOCW_ONES 'P'
#define VOCW_HER 'R'
#define VOCW_THEM 'T'
#define VOCW_BUT 'X'
#define VOCW_ANY 'Y'
/* structure for special internal word table */
struct vocspdef {
const char *vocspin;
char vocspout;
};
/* check if a word is a special word - true if word is given special word */
/* int vocspec(char *wordptr, int speccode); */
#define vocspec(w, s) (*(w) == (s))
/*
* Set a fuse/daemon/notifier.
*/
void vocsetfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop,
uint tm, runsdef *val, int err);
/* remove a fuse/daemon/notifier */
void vocremfd(voccxdef *ctx, vocddef *what, objnum func, prpnum prop,
runsdef *val, int err);
/* count a turn (down all fuse/notifier timers) */
void vocturn(voccxdef *ctx, int turncnt, int do_fuses);
/* initialize voc context */
void vocini(voccxdef *vocctx, errcxdef *errctx, mcmcxdef *memctx,
runcxdef *runctx, objucxdef *undoctx, int fuses,
int daemons, int notifiers);
/* clean up the voc context - frees memory allocated by vocini() */
void vocterm(voccxdef *vocctx);
/* allocate/free fuse/daemon/notifier array for voc ctx initialization */
void vocinialo(voccxdef *ctx, vocddef **what, int cnt);
void voctermfree(vocddef *what);
/* get a vocidef given an object number */
/* vocidef *vocinh(voccxdef *ctx, objnum obj); */
#define vocinh(ctx, obj) ((ctx)->voccxinh[(obj) >> 8][(obj) & 255])
/* revert all objects back to original state, using inheritance records */
void vocrevert(voccxdef *ctx);
/* clear all fuses/daemons/notifiers (useful for restarting) */
void vocdmnclr(voccxdef *ctx);
/* display a parser error message */
void vocerr(voccxdef *ctx, int err, const char *f, ...);
/*
* display a parser informational error message - this will display the
* message whether or not we're suppressing messages due to unknown
* words, and should be used when providing information, such as objects
* we're assuming by default
*/
void vocerr_info(voccxdef *ctx, int err, const char *f, ...);
/* client undo callback - undoes a daemon/fuse/notifier */
void vocdundo(void *ctx, uchar *data);
/* client undo size figuring callback - return size of client undo record */
ushort OS_LOADDS vocdusz(void *ctx, uchar *data);
/* save undo for object creation */
void vocdusave_newobj(voccxdef *ctx, objnum objn);
/* save undo for adding a word */
void vocdusave_addwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags,
char *wrd);
/* save undo for deleting a word */
void vocdusave_delwrd(voccxdef *ctx, objnum objn, prpnum typ, int flags,
char *wrd);
/* save undo for object deletion */
void vocdusave_delobj(voccxdef *ctx, objnum objn);
/* save undo for changing the "Me" object */
void vocdusave_me(voccxdef *ctx, objnum old_me);
/* compute vocabulary word hash value */
uint vochsh(const uchar *t, int len);
/* TADS versions of isalpha, isspace, isdigit, etc */
#define vocisupper(c) ((uchar)(c) <= 127 && Common::isUpper((uchar)(c)))
#define vocislower(c) ((uchar)(c) <= 127 && Common::isLower((uchar)(c)))
#define vocisalpha(c) ((uchar)(c) > 127 || Common::isAlpha((uchar)(c)))
#define vocisspace(c) ((uchar)(c) <= 127 && Common::isSpace((uchar)(c)))
#define vocisdigit(c) ((uchar)(c) <= 127 && Common::isDigit((uchar)(c)))
/*
* Undo types for voc subsystem
*/
#define VOC_UNDO_DAEMON 1 /* fuse/daemon status change */
#define VOC_UNDO_NEWOBJ 2 /* object creation */
#define VOC_UNDO_DELOBJ 3 /* object deletion */
#define VOC_UNDO_ADDVOC 4 /* add vocabulary to an object */
#define VOC_UNDO_DELVOC 5 /* delete vocabulary from an object */
#define VOC_UNDO_SETME 6 /* set the "Me" object */
/*
* Our own stack. We need to allocate some fairly large structures
* (for the disambiguation lists, mostly) in a stack-like fashion, and
* we don't want to consume vast quantities of the real stack, because
* some machines have relatively restrictive limitations on stack usage.
* To provide some elbow room, we'll use a stack-like structure of our
* own: we'll allocate out of this structure as needed, and whenever we
* leave a C stack frame, we'll also leave our own stack frame.
*/
/* re-initialize the stack, allocating space for it if needed */
void voc_stk_ini(voccxdef *ctx, uint siz);
/* enter a stack frame, marking our current position */
#define voc_enter(ctx, marker) (*(marker) = (ctx)->voc_stk_cur)
/* leave a stack frame, restoring the entry position */
#define voc_leave(ctx, marker) ((ctx)->voc_stk_cur = marker)
/* return a value */
#define VOC_RETVAL(ctx, marker, retval) \
voc_leave(ctx, marker); return retval
/* allocate space from the stack */
void *voc_stk_alo(voccxdef *ctx, uint siz);
/* allocation cover macros */
#define VOC_STK_ARRAY(ctx, typ, var, cnt) \
(var = (typ *)voc_stk_alo(ctx, (uint)((cnt) * sizeof(typ))))
#define VOC_MAX_ARRAY(ctx, typ, var) \
VOC_STK_ARRAY(ctx, typ, var, VOCMAXAMBIG)
/*
* Stack size for the vocab stack. We'll scale our stack needs based
* on the size of the vocoldef structure, since this is the most common
* item to be allocated on the vocab stack. We'll also scale based on
* the defined VOCMAXAMBIG parameter, since it is the number of elements
* usually allocated. The actual amount of space needed depends on how
* the functions in vocab.c and execmd.c work, so this parameter may
* need to be adjusted for changes to the player command parser.
*/
#define VOC_STACK_SIZE (16 * VOCMAXAMBIG * sizeof(vocoldef))
/*
* Execute all fuses and daemons, then execute the endCommand user
* function. Returns zero on success, or ERR_ABORT if 'abort' was
* thrown during execution. This is a convenient cover single function
* to do all end-of-turn processing; this calls exefuse() and exedaem()
* as needed, trapping any 'abort' or 'exit' errors that occur.
*
* If 'do_fuses' is true, we'll run fuses and daemons. Otherwise,
*/
int exe_fuses_and_daemons(voccxdef *ctx, int err, int do_fuses,
objnum actor, objnum verb,
vocoldef *dobj_list, int do_cnt,
objnum prep, objnum iobj);
/*
* Execute any pending fuses. Return TRUE if any fuses were executed,
* FALSE otherwise.
*/
int exefuse(voccxdef *ctx, int do_run);
/*
* Execute daemons
*/
void exedaem(voccxdef *ctx);
/*
* Get the number and size of words defined for an object. The size
* returns the total byte count from all the words involved. Do not
* include deleted words in the count.
*/
void voc_count(voccxdef *ctx, objnum objn, prpnum prp, int *cnt, int *siz);
/*
* Iterate through all words for a particular object, calling a
* function with each vocwdef found. If objn == MCMONINV, we'll call
* the callback for every word.
*/
void voc_iterate(voccxdef *ctx, objnum objn,
void (*fn)(void *, vocdef *, vocwdef *), void *fnctx);
/* ------------------------------------------------------------------------ */
/*
* disambiguation status codes - used for disambigDobj and disambigIobj
* methods in the deepverb
*/
/* continue with disambiguation process (using possibly updated list) */
#define VOC_DISAMBIG_CONT 1
/* done - the list is fully resolved; return with (possibly updated) list */
#define VOC_DISAMBIG_DONE 2
/* error - abort the command */
#define VOC_DISAMBIG_ERROR 3
/* parse string returned in second element of list as interactive response */
#define VOC_DISAMBIG_PARSE_RESP 4
/* already asked for an interactive response, but didn't read it yet */
#define VOC_DISAMBIG_PROMPTED 5
/* ------------------------------------------------------------------------ */
/*
* parseNounPhrase status codes
*/
/* parse error occurred */
#define VOC_PNP_ERROR 1
/* use built-in default parser */
#define VOC_PNP_DEFAULT 2
/* successful parse */
#define VOC_PNP_SUCCESS 3
/* ------------------------------------------------------------------------ */
/*
* parserResolveObjects usage codes
*/
#define VOC_PRO_RESOLVE_DOBJ 1 /* direct object */
#define VOC_PRO_RESOLVE_IOBJ 2 /* indirect object */
#define VOC_PRO_RESOLVE_ACTOR 3 /* actor */
} // End of namespace TADS2
} // End of namespace TADS
} // End of namespace Glk
#endif

File diff suppressed because it is too large Load Diff