aboutsummaryrefslogtreecommitdiff
path: root/engines/glk/alan3/exe.cpp
diff options
context:
space:
mode:
authorPaul Gilbert2019-06-25 21:18:44 -0700
committerPaul Gilbert2019-07-06 15:27:07 -0700
commitcd7cf4141425e3e77b164d2dbcbae8de1eacb035 (patch)
tree97caacd0a7e26488844ada4e7cd529cae482526a /engines/glk/alan3/exe.cpp
parentcfd66173bad8bb23ba7ba2d531fc5e98872212ce (diff)
downloadscummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.gz
scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.tar.bz2
scummvm-rg350-cd7cf4141425e3e77b164d2dbcbae8de1eacb035.zip
GLK: ALAN3: Initial files commit
Diffstat (limited to 'engines/glk/alan3/exe.cpp')
-rw-r--r--engines/glk/alan3/exe.cpp753
1 files changed, 753 insertions, 0 deletions
diff --git a/engines/glk/alan3/exe.cpp b/engines/glk/alan3/exe.cpp
new file mode 100644
index 0000000000..eb01870e2f
--- /dev/null
+++ b/engines/glk/alan3/exe.cpp
@@ -0,0 +1,753 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/alan3/exe.h"
+#include "glk/alan3/actor.h"
+#include "glk/alan3/alan3.h"
+#include "glk/alan3/args.h"
+#include "glk/alan3/current.h"
+#include "glk/alan3/decode.h"
+#include "glk/alan3/event.h"
+#include "glk/alan3/glkio.h"
+#include "glk/alan3/lists.h"
+#include "glk/alan3/instance.h"
+#include "glk/alan3/inter.h"
+#include "glk/alan3/memory.h"
+#include "glk/alan3/msg.h"
+#include "glk/alan3/output.h"
+#include "glk/alan3/options.h"
+#include "glk/alan3/readline.h"
+#include "glk/alan3/save.h"
+#include "glk/alan3/score.h"
+#include "glk/alan3/state.h"
+#include "glk/alan3/syserr.h"
+#include "glk/alan3/sysdep.h"
+#include "glk/alan3/term.h"
+#include "glk/alan3/types.h"
+#include "glk/alan3/utils.h"
+#include "glk/alan3/word.h"
+#include "common/stream.h"
+#include "common/textconsole.h"
+
+namespace Glk {
+namespace Alan3 {
+
+/* PUBLIC DATA */
+
+Common::SeekableReadStream *textFile;
+
+/* Long jump buffers */
+// TODO move to longjump.c? or error.c, and abstract them into functions?
+//jmp_buf restartLabel; /* Restart long jump return point */
+//jmp_buf returnLabel; /* Error (or undo) long jump return point */
+//jmp_buf forfeitLabel; /* Player forfeit by an empty command */
+
+
+/* PRIVATE CONSTANTS */
+
+#define WIDTH 80
+
+
+/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
+static char logFileName[256] = "";
+
+/*======================================================================*/
+void setStyle(int style)
+{
+#ifdef HAVE_GLK
+ switch (style) {
+ case NORMAL_STYLE: g_vm->glk_set_style(style_Normal); break;
+ case EMPHASIZED_STYLE: g_vm->glk_set_style(style_Emphasized); break;
+ case PREFORMATTED_STYLE: g_vm->glk_set_style(style_Preformatted); break;
+ case ALERT_STYLE: g_vm->glk_set_style(style_Alert); break;
+ case QUOTE_STYLE: g_vm->glk_set_style(style_BlockQuote); break;
+ }
+#endif
+}
+
+/*======================================================================*/
+void print(Aword fpos, Aword len)
+{
+ char str[2*WIDTH]; /* String buffer */
+ int outlen = 0; /* Current output length */
+ int ch = 0;
+ int i;
+ long savfp = 0; /* Temporary saved text file position */
+ static bool printFlag = FALSE; /* Printing already? */
+ bool savedPrintFlag = printFlag;
+ void *info = NULL; /* Saved decoding info */
+
+
+ if (len == 0) return;
+
+ if (isHere(HERO, /*TRUE*/ DIRECT)) { /* Check if the player will see it */
+ if (printFlag) { /* Already printing? */
+ /* Save current text file position and/or decoding info */
+ if (header->pack)
+ info = pushDecode();
+ else
+ savfp = textFile->pos();
+ }
+ printFlag = TRUE; /* We're printing now! */
+
+ /* Position to start of text */
+ textFile->seek(fpos+header->stringOffset);
+
+ if (header->pack)
+ startDecoding();
+ for (outlen = 0; outlen != len; outlen = outlen + strlen(str)) {
+ /* Fill the buffer from the beginning */
+ for (i = 0; i <= WIDTH || (i > WIDTH && ch != ' '); i++) {
+ if (outlen + i == len) /* No more characters? */
+ break;
+ if (header->pack)
+ ch = decodeChar();
+ else
+ ch = textFile->readByte();
+
+ str[i] = ch;
+ if (textFile->pos() >= textFile->size()) /* Or end of text? */
+ break;
+ }
+ str[i] = '\0';
+#if ISO == 0
+ fromIso(str, str);
+#endif
+ output(str);
+ }
+
+ /* And restore */
+ printFlag = savedPrintFlag;
+ if (printFlag) {
+ if (header->pack)
+ popDecode(info);
+ else
+ textFile->seek(savfp);
+ }
+ }
+}
+
+
+/*======================================================================*/
+void sys(Aword fpos, Aword len)
+{
+ ::error("sys calls are unsupported");
+}
+
+
+/*======================================================================*/
+char *getStringFromFile(Aword fpos, Aword len)
+{
+ char *buf = (char *)allocate(len+1);
+ char *bufp = buf;
+
+ /* Position to start of text */
+ textFile->seek(fpos+header->stringOffset);
+
+ if (header->pack)
+ startDecoding();
+ while (len--)
+ if (header->pack)
+ *(bufp++) = decodeChar();
+ else
+ *(bufp++) = textFile->readByte();
+
+ /* Terminate string with zero */
+ *bufp = '\0';
+
+ return buf;
+}
+
+
+
+/*======================================================================*/
+void score(Aword sc)
+{
+ if (sc == 0) {
+ ParameterArray messageParameters = newParameterArray();
+ addParameterForInteger(messageParameters, current.score);
+ addParameterForInteger(messageParameters, header->maximumScore);
+ addParameterForInteger(messageParameters, current.tick);
+ printMessageWithParameters(M_SCORE, messageParameters);
+ freeParameterArray(messageParameters);
+ } else {
+ current.score += scores[sc-1];
+ scores[sc-1] = 0;
+ gameStateChanged = TRUE;
+ }
+}
+
+
+/*======================================================================*/
+void visits(Aword v)
+{
+ current.visits = v;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void sayUndoneCommand(char *words) {
+ static Parameter *messageParameters = NULL;
+ messageParameters = (Parameter *)ensureParameterArrayAllocated(messageParameters);
+
+ current.location = where(HERO, DIRECT);
+ clearParameterArray(messageParameters);
+ addParameterForString(&messageParameters[0], words);
+ setEndOfArray(&messageParameters[1]);
+ printMessageWithParameters(M_UNDONE, messageParameters);
+}
+
+
+/*======================================================================*/
+void undo(void) {
+ forgetGameState();
+ if (anySavedState()) {
+ recallGameState();
+ sayUndoneCommand(recreatePlayerCommand());
+ } else {
+ printMessage(M_NO_UNDO);
+ }
+#ifdef TODO
+ longjmp(returnLabel, UNDO_RETURN);
+#else
+ ::error("TODO: undo longjmp");
+#endif
+}
+
+
+/*======================================================================*/
+void quitGame(void)
+{
+#ifdef TODO
+ char buf[80];
+
+ current.location = where(HERO, DIRECT);
+ para();
+ while (TRUE) {
+ col = 1;
+ statusline();
+ printMessage(M_QUITACTION);
+#ifdef USE_READLINE
+ if (!readline(buf)) terminate(0);
+#else
+ if (gets(buf) == NULL) terminate(0);
+#endif
+ if (strcasecmp(buf, "restart") == 0)
+ longjmp(restartLabel, TRUE);
+ else if (strcasecmp(buf, "restore") == 0) {
+ restore();
+ return;
+ } else if (strcasecmp(buf, "quit") == 0) {
+ terminate(0);
+ } else if (strcasecmp(buf, "undo") == 0) {
+ if (gameStateChanged) {
+ rememberCommands();
+ rememberGameState();
+ undo();
+ } else {
+ if (anySavedState()) {
+ recallGameState();
+ sayUndoneCommand(playerWordsAsCommandString());
+ } else
+ printMessage(M_NO_UNDO);
+ longjmp(returnLabel, UNDO_RETURN);
+ }
+ }
+ }
+#endif
+ syserr("Fallthrough in QUIT");
+}
+
+
+
+/*======================================================================*/
+void restartGame(void)
+{
+ Aint previousLocation = current.location;
+#ifdef TODO
+ current.location = where(HERO, DIRECT);
+ para();
+ if (confirm(M_REALLY)) {
+ longjmp(restartLabel, TRUE);
+ }
+ current.location = previousLocation;
+#lse
+ ::error("TODO: restartGame");
+#endif
+}
+
+
+
+/*======================================================================*/
+void cancelEvent(Aword theEvent)
+{
+ int i;
+
+ for (i = eventQueueTop-1; i>=0; i--)
+ if (eventQueue[i].event == theEvent) {
+ while (i < eventQueueTop-1) {
+ eventQueue[i].event = eventQueue[i+1].event;
+ eventQueue[i].after = eventQueue[i+1].after;
+ eventQueue[i].where = eventQueue[i+1].where;
+ i++;
+ }
+ eventQueueTop--;
+ return;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+static void increaseEventQueue(void)
+{
+ eventQueue = (EventQueueEntry *)realloc(eventQueue, (eventQueueTop+2)*sizeof(EventQueueEntry));
+ if (eventQueue == NULL) syserr("Out of memory in increaseEventQueue()");
+
+ eventQueueSize = eventQueueTop + 2;
+}
+
+
+/*----------------------------------------------------------------------*/
+static void moveEvent(int to, int from) {
+ eventQueue[to].event = eventQueue[from].event;
+ eventQueue[to].after = eventQueue[from].after;
+ eventQueue[to].where = eventQueue[from].where;
+}
+
+
+/*======================================================================*/
+void schedule(Aword event, Aword where, Aword after)
+{
+ int i;
+
+ if (event == 0) syserr("NULL event");
+
+ cancelEvent(event);
+ /* Check for overflow */
+ if (eventQueue == NULL || eventQueueTop == eventQueueSize)
+ increaseEventQueue();
+
+ /* Bubble this event down */
+ for (i = eventQueueTop; i >= 1 && eventQueue[i-1].after <= after; i--) {
+ moveEvent(i, i-1);
+ }
+
+ eventQueue[i].after = after;
+ eventQueue[i].where = where;
+ eventQueue[i].event = event;
+ eventQueueTop++;
+}
+
+
+// TODO Move to string.c?
+/*======================================================================*/
+Aptr concat(Aptr as1, Aptr as2)
+{
+ char *s1 = (char *)fromAptr(as1);
+ char *s2 = (char *)fromAptr(as2);
+ char *result = (char *)allocate(strlen((char*)s1)+strlen((char*)s2)+1);
+ strcpy(result, s1);
+ strcat(result, s2);
+ return toAptr(result);
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripCharsFromStringForwards(int count, char *initialString, char **theRest)
+{
+ int stripPosition;
+ char *strippedString;
+ char *rest;
+
+ if (count > strlen(initialString))
+ stripPosition = strlen(initialString);
+ else
+ stripPosition = count;
+ rest = strdup(&initialString[stripPosition]);
+ strippedString = strdup(initialString);
+ strippedString[stripPosition] = '\0';
+ *theRest = rest;
+ return strippedString;
+}
+
+/*----------------------------------------------------------------------*/
+static char *stripCharsFromStringBackwards(Aint count, char *initialString, char **theRest) {
+ int stripPosition;
+ char *strippedString;
+ char *rest;
+
+ if (count > strlen(initialString))
+ stripPosition = 0;
+ else
+ stripPosition = strlen(initialString)-count;
+ strippedString = strdup(&initialString[stripPosition]);
+ rest = strdup(initialString);
+ rest[stripPosition] = '\0';
+ *theRest = rest;
+ return strippedString;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int countLeadingBlanks(char *string, int position) {
+ static char blanks[] = " ";
+ return strspn(&string[position], blanks);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int skipWordForwards(char *string, int position)
+{
+ char separators[] = " .,?";
+
+ int i;
+
+ for (i = position; i<=strlen(string) && strchr(separators, string[i]) == NULL; i++)
+ ;
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripWordsFromStringForwards(Aint count, char *initialString, char **theRest) {
+ int skippedChars;
+ int position = 0;
+ char *stripped;
+ int i;
+
+ for (i = count; i>0; i--) {
+ /* Ignore any initial blanks */
+ skippedChars = countLeadingBlanks(initialString, position);
+ position += skippedChars;
+ position = skipWordForwards(initialString, position);
+ }
+
+ stripped = (char *)allocate(position+1);
+ strncpy(stripped, initialString, position);
+ stripped[position] = '\0';
+
+ skippedChars = countLeadingBlanks(initialString, position);
+ *theRest = strdup(&initialString[position+skippedChars]);
+
+ return(stripped);
+}
+
+
+/*----------------------------------------------------------------------*/
+static int skipWordBackwards(char *string, int position)
+{
+ char separators[] = " .,?";
+ int i;
+
+ for (i = position; i>0 && strchr(separators, string[i-1]) == NULL; i--)
+ ;
+ return i;
+}
+
+
+/*----------------------------------------------------------------------*/
+static int countTrailingBlanks(char *string, int position) {
+ int skippedChars, i;
+ skippedChars = 0;
+
+ if (position > strlen(string)-1)
+ syserr("position > length in countTrailingBlanks");
+ for (i = position; i >= 0 && string[i] == ' '; i--)
+ skippedChars++;
+ return(skippedChars);
+}
+
+
+/*----------------------------------------------------------------------*/
+static char *stripWordsFromStringBackwards(Aint count, char *initialString, char **theRest) {
+ int skippedChars;
+ char *stripped;
+ int strippedLength;
+ int position = strlen(initialString);
+ int i;
+
+ for (i = count; i>0 && position>0; i--) {
+ position -= 1;
+ /* Ignore trailing blanks */
+ skippedChars = countTrailingBlanks(initialString, position);
+ if (position - skippedChars < 0) break; /* No more words to strip */
+ position -= skippedChars;
+ position = skipWordBackwards(initialString, position);
+ }
+
+ skippedChars = countLeadingBlanks(initialString, 0);
+ strippedLength = strlen(initialString)-position-skippedChars;
+ stripped = (char *)allocate(strippedLength+1);
+ strncpy(stripped, &initialString[position+skippedChars], strippedLength);
+ stripped[strippedLength] = '\0';
+
+ if (position > 0) {
+ skippedChars = countTrailingBlanks(initialString, position-1);
+ position -= skippedChars;
+ }
+ *theRest = strdup(initialString);
+ (*theRest)[position] = '\0';
+ return(stripped);
+}
+
+
+
+/*======================================================================*/
+Aptr strip(bool stripFromBeginningNotEnd, int count, bool stripWordsNotChars, int id, int atr)
+{
+ char *initialString = (char *)fromAptr(getInstanceAttribute(id, atr));
+ char *theStripped;
+ char *theRest;
+
+ if (stripFromBeginningNotEnd) {
+ if (stripWordsNotChars)
+ theStripped = stripWordsFromStringForwards(count, initialString, &theRest);
+ else
+ theStripped = stripCharsFromStringForwards(count, initialString, &theRest);
+ } else {
+ if (stripWordsNotChars)
+ theStripped = stripWordsFromStringBackwards(count, initialString, &theRest);
+ else
+ theStripped = stripCharsFromStringBackwards(count, initialString, &theRest);
+ }
+ setInstanceStringAttribute(id, atr, theRest);
+ return toAptr(theStripped);
+}
+
+
+/*======================================================================*/
+int getContainerMember(int container, int index, bool directly) {
+ Aint i;
+ Aint count = 0;
+
+ for (i = 1; i <= header->instanceMax; i++) {
+ if (isIn(i, container, DIRECT)) {
+ count++;
+ if (count == index)
+ return i;
+ }
+ }
+ apperr("Index not in container in 'containerMember()'");
+ return 0;
+}
+
+
+/***********************************************************************\
+
+ Description Handling
+
+\***********************************************************************/
+
+
+/*======================================================================*/
+void showImage(int image, int align)
+{
+#ifdef HAVE_GLK
+ uint ecode;
+
+ if ((g_vm->glk_gestalt(gestalt_Graphics, 0) == 1) &&
+ (g_vm->glk_gestalt(gestalt_DrawImage, wintype_TextBuffer) == 1)) {
+ g_vm->glk_window_flow_break(glkMainWin);
+ printf("\n");
+ ecode = g_vm->glk_image_draw(glkMainWin, image, imagealign_MarginLeft, 0);
+ (void)ecode;
+ }
+#endif
+}
+
+
+/*======================================================================*/
+void playSound(int sound)
+{
+#ifdef HAVE_GLK
+#ifdef GLK_MODULE_SOUND
+ static schanid_t soundChannel = NULL;
+
+ if (g_vm->glk_gestalt(gestalt_Sound, 0) == 1) {
+ if (soundChannel == NULL)
+ soundChannel = g_vm->glk_schannel_create(0);
+ if (soundChannel != NULL) {
+ g_vm->glk_schannel_stop(soundChannel);
+ (void)g_vm->glk_schannel_play(soundChannel, sound);
+ }
+ }
+#endif
+#endif
+}
+
+
+
+/*======================================================================*/
+void empty(int cnt, int whr)
+{
+ int i;
+
+ for (i = 1; i <= header->instanceMax; i++)
+ if (isIn(i, cnt, DIRECT))
+ locate(i, whr);
+}
+
+
+
+/*======================================================================*/
+void use(int actor, int script)
+{
+ char str[80];
+ StepEntry *step;
+
+ if (!isAActor(actor)) {
+ sprintf(str, "Instance is not an Actor (%d).", actor);
+ syserr(str);
+ }
+
+ admin[actor].script = script;
+ admin[actor].step = 0;
+ step = stepOf(actor);
+ if (step != NULL && step->after != 0) {
+ admin[actor].waitCount = evaluate(step->after);
+ }
+
+ gameStateChanged = TRUE;
+}
+
+/*======================================================================*/
+void stop(int act)
+{
+ char str[80];
+
+ if (!isAActor(act)) {
+ sprintf(str, "Instance is not an Actor (%d).", act);
+ syserr(str);
+ }
+
+ admin[act].script = 0;
+ admin[act].step = 0;
+
+ gameStateChanged = TRUE;
+}
+
+
+
+static int randomValue = 0;
+/*----------------------------------------------------------------------*/
+int randomInteger(int from, int to)
+{
+ if (regressionTestOption) {
+ int ret = from + randomValue;
+ /* Generate them in sequence */
+ if (ret > to) {
+ ret = from;
+ randomValue = 1;
+ } else if (ret == to)
+ randomValue = 0;
+ else
+ randomValue++;
+ return ret;
+ } else {
+ if (to == from)
+ return to;
+ else if (to > from)
+ return (rand()/10)%(to-from+1)+from;
+ else
+ return (rand()/10)%(from-to+1)+to;
+ }
+}
+
+
+
+/*----------------------------------------------------------------------*/
+bool between(int val, int low, int high)
+{
+ if (high > low)
+ return low <= val && val <= high;
+ else
+ return high <= val && val <= low;
+}
+
+
+
+/*======================================================================*/
+bool contains(Aptr string, Aptr substring)
+{
+ bool found;
+
+ strlow((char *)fromAptr(string));
+ strlow((char *)fromAptr(substring));
+
+ found = (strstr((char *)fromAptr(string), (char *)fromAptr(substring)) != 0);
+
+ return found;
+}
+
+
+/*======================================================================*/
+bool streq(char a[], char b[])
+{
+ bool eq;
+
+ strlow(a);
+ strlow(b);
+
+ eq = (strcmp(a, b) == 0);
+
+ return eq;
+}
+
+
+
+/*======================================================================*/
+void startTranscript(void) {
+ time_t tick;
+
+ if (logFile != NULL)
+ return;
+
+ Common::String target = g_vm->getTargetName() + ".log";
+
+ uint fileUsage = transcriptOption ? fileusage_Transcript : fileusage_InputRecord;
+ frefid_t logFileRef = g_vm->glk_fileref_create_by_name(fileUsage, logFileName, 0);
+ logFile = g_vm->glk_stream_open_file(logFileRef, filemode_Write, 0);
+
+ if (logFile == NULL) {
+ transcriptOption = FALSE;
+ logOption = FALSE;
+ } else {
+ transcriptOption = TRUE;
+ }
+}
+
+
+/*======================================================================*/
+void stopTranscript(void) {
+ if (logFile == NULL)
+ return;
+
+ if (transcriptOption|| logOption)
+ delete logFile;
+
+ logFile = NULL;
+ transcriptOption = FALSE;
+ logOption = FALSE;
+}
+
+} // End of namespace Alan3
+} // End of namespace Glk