Initial commit
This commit is contained in:
248
engines/glk/tads/tads2/appctx.h
Normal file
248
engines/glk/tads/tads2/appctx.h
Normal 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
|
||||
4332
engines/glk/tads/tads2/built_in.cpp
Normal file
4332
engines/glk/tads/tads2/built_in.cpp
Normal file
File diff suppressed because it is too large
Load Diff
219
engines/glk/tads/tads2/built_in.h
Normal file
219
engines/glk/tads/tads2/built_in.h
Normal 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
|
||||
339
engines/glk/tads/tads2/character_map.cpp
Normal file
339
engines/glk/tads/tads2/character_map.cpp
Normal 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
|
||||
130
engines/glk/tads/tads2/character_map.h
Normal file
130
engines/glk/tads/tads2/character_map.h
Normal 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
|
||||
86
engines/glk/tads/tads2/command_line.cpp
Normal file
86
engines/glk/tads/tads2/command_line.cpp
Normal 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
|
||||
69
engines/glk/tads/tads2/command_line.h
Normal file
69
engines/glk/tads/tads2/command_line.h
Normal 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
|
||||
73
engines/glk/tads/tads2/data.cpp
Normal file
73
engines/glk/tads/tads2/data.cpp
Normal 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
|
||||
58
engines/glk/tads/tads2/data.h
Normal file
58
engines/glk/tads/tads2/data.h
Normal 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
|
||||
522
engines/glk/tads/tads2/debug.cpp
Normal file
522
engines/glk/tads/tads2/debug.cpp
Normal 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
|
||||
589
engines/glk/tads/tads2/debug.h
Normal file
589
engines/glk/tads/tads2/debug.h
Normal 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
|
||||
185
engines/glk/tads/tads2/error.cpp
Normal file
185
engines/glk/tads/tads2/error.cpp
Normal 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
|
||||
389
engines/glk/tads/tads2/error.h
Normal file
389
engines/glk/tads/tads2/error.h
Normal 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
|
||||
205
engines/glk/tads/tads2/error_handling.cpp
Normal file
205
engines/glk/tads/tads2/error_handling.cpp
Normal 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
|
||||
342
engines/glk/tads/tads2/error_handling.h
Normal file
342
engines/glk/tads/tads2/error_handling.h
Normal 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
|
||||
69
engines/glk/tads/tads2/error_message.cpp
Normal file
69
engines/glk/tads/tads2/error_message.cpp
Normal 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
|
||||
3684
engines/glk/tads/tads2/execute_command.cpp
Normal file
3684
engines/glk/tads/tads2/execute_command.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1779
engines/glk/tads/tads2/file_io.cpp
Normal file
1779
engines/glk/tads/tads2/file_io.cpp
Normal file
File diff suppressed because it is too large
Load Diff
135
engines/glk/tads/tads2/file_io.h
Normal file
135
engines/glk/tads/tads2/file_io.h
Normal 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
|
||||
172
engines/glk/tads/tads2/get_string.cpp
Normal file
172
engines/glk/tads/tads2/get_string.cpp
Normal 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
|
||||
139
engines/glk/tads/tads2/lib.h
Normal file
139
engines/glk/tads/tads2/lib.h
Normal 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
|
||||
128
engines/glk/tads/tads2/line_source.h
Normal file
128
engines/glk/tads/tads2/line_source.h
Normal 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
|
||||
1070
engines/glk/tads/tads2/line_source_file.cpp
Normal file
1070
engines/glk/tads/tads2/line_source_file.cpp
Normal file
File diff suppressed because it is too large
Load Diff
169
engines/glk/tads/tads2/line_source_file.h
Normal file
169
engines/glk/tads/tads2/line_source_file.h
Normal 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
|
||||
41
engines/glk/tads/tads2/list.cpp
Normal file
41
engines/glk/tads/tads2/list.cpp
Normal 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
|
||||
46
engines/glk/tads/tads2/list.h
Normal file
46
engines/glk/tads/tads2/list.h
Normal 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
|
||||
196
engines/glk/tads/tads2/ltk.cpp
Normal file
196
engines/glk/tads/tads2/ltk.cpp
Normal 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
|
||||
132
engines/glk/tads/tads2/ltk.h
Normal file
132
engines/glk/tads/tads2/ltk.h
Normal 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
|
||||
1184
engines/glk/tads/tads2/memory_cache.cpp
Normal file
1184
engines/glk/tads/tads2/memory_cache.cpp
Normal file
File diff suppressed because it is too large
Load Diff
402
engines/glk/tads/tads2/memory_cache.h
Normal file
402
engines/glk/tads/tads2/memory_cache.h
Normal 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
|
||||
50
engines/glk/tads/tads2/memory_cache_heap.cpp
Normal file
50
engines/glk/tads/tads2/memory_cache_heap.cpp
Normal 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
|
||||
59
engines/glk/tads/tads2/memory_cache_heap.h
Normal file
59
engines/glk/tads/tads2/memory_cache_heap.h
Normal 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
|
||||
56
engines/glk/tads/tads2/memory_cache_loader.h
Normal file
56
engines/glk/tads/tads2/memory_cache_loader.h
Normal 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
|
||||
301
engines/glk/tads/tads2/memory_cache_swap.cpp
Normal file
301
engines/glk/tads/tads2/memory_cache_swap.cpp
Normal 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
|
||||
121
engines/glk/tads/tads2/memory_cache_swap.h
Normal file
121
engines/glk/tads/tads2/memory_cache_swap.h
Normal 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
|
||||
1075
engines/glk/tads/tads2/object.cpp
Normal file
1075
engines/glk/tads/tads2/object.cpp
Normal file
File diff suppressed because it is too large
Load Diff
395
engines/glk/tads/tads2/object.h
Normal file
395
engines/glk/tads/tads2/object.h
Normal 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
|
||||
217
engines/glk/tads/tads2/opcode.h
Normal file
217
engines/glk/tads/tads2/opcode.h
Normal 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
|
||||
44
engines/glk/tads/tads2/os.cpp
Normal file
44
engines/glk/tads/tads2/os.cpp
Normal 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
404
engines/glk/tads/tads2/os.h
Normal 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
|
||||
3580
engines/glk/tads/tads2/output.cpp
Normal file
3580
engines/glk/tads/tads2/output.cpp
Normal file
File diff suppressed because it is too large
Load Diff
356
engines/glk/tads/tads2/play.cpp
Normal file
356
engines/glk/tads/tads2/play.cpp
Normal 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
|
||||
45
engines/glk/tads/tads2/play.h
Normal file
45
engines/glk/tads/tads2/play.h
Normal 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
|
||||
64
engines/glk/tads/tads2/post_compilation.cpp
Normal file
64
engines/glk/tads/tads2/post_compilation.cpp
Normal 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
|
||||
90
engines/glk/tads/tads2/post_compilation.h
Normal file
90
engines/glk/tads/tads2/post_compilation.h
Normal 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
|
||||
166
engines/glk/tads/tads2/property.h
Normal file
166
engines/glk/tads/tads2/property.h
Normal 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
|
||||
117
engines/glk/tads/tads2/qa_scriptor.cpp
Normal file
117
engines/glk/tads/tads2/qa_scriptor.cpp
Normal 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
|
||||
1589
engines/glk/tads/tads2/regex.cpp
Normal file
1589
engines/glk/tads/tads2/regex.cpp
Normal file
File diff suppressed because it is too large
Load Diff
199
engines/glk/tads/tads2/regex.h
Normal file
199
engines/glk/tads/tads2/regex.h
Normal 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
|
||||
2436
engines/glk/tads/tads2/run.cpp
Normal file
2436
engines/glk/tads/tads2/run.cpp
Normal file
File diff suppressed because it is too large
Load Diff
374
engines/glk/tads/tads2/run.h
Normal file
374
engines/glk/tads/tads2/run.h
Normal 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
|
||||
88
engines/glk/tads/tads2/runstat.cpp
Normal file
88
engines/glk/tads/tads2/runstat.cpp
Normal 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
|
||||
34
engines/glk/tads/tads2/runtime_app.cpp
Normal file
34
engines/glk/tads/tads2/runtime_app.cpp
Normal 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
|
||||
112
engines/glk/tads/tads2/runtime_app.h
Normal file
112
engines/glk/tads/tads2/runtime_app.h
Normal 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
|
||||
913
engines/glk/tads/tads2/runtime_driver.cpp
Normal file
913
engines/glk/tads/tads2/runtime_driver.cpp
Normal 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
|
||||
68
engines/glk/tads/tads2/string_resources.h
Normal file
68
engines/glk/tads/tads2/string_resources.h
Normal 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
|
||||
51
engines/glk/tads/tads2/tads2.cpp
Normal file
51
engines/glk/tads/tads2/tads2.cpp
Normal 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
|
||||
58
engines/glk/tads/tads2/tads2.h
Normal file
58
engines/glk/tads/tads2/tads2.h
Normal 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
|
||||
222
engines/glk/tads/tads2/text_io.h
Normal file
222
engines/glk/tads/tads2/text_io.h
Normal 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
|
||||
1501
engines/glk/tads/tads2/tokenizer.cpp
Normal file
1501
engines/glk/tads/tads2/tokenizer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
473
engines/glk/tads/tads2/tokenizer.h
Normal file
473
engines/glk/tads/tads2/tokenizer.h
Normal 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
|
||||
335
engines/glk/tads/tads2/tokenizer_hash.cpp
Normal file
335
engines/glk/tads/tads2/tokenizer_hash.cpp
Normal 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
|
||||
934
engines/glk/tads/tads2/vocabulary.cpp
Normal file
934
engines/glk/tads/tads2/vocabulary.cpp
Normal 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
|
||||
799
engines/glk/tads/tads2/vocabulary.h
Normal file
799
engines/glk/tads/tads2/vocabulary.h
Normal 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
|
||||
8159
engines/glk/tads/tads2/vocabulary_parser.cpp
Normal file
8159
engines/glk/tads/tads2/vocabulary_parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user