Initial commit
This commit is contained in:
664
engines/glk/alan3/exe.cpp
Normal file
664
engines/glk/alan3/exe.cpp
Normal file
@@ -0,0 +1,664 @@
|
||||
/* 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/alan3/exe.h"
|
||||
#include "glk/alan3/actor.h"
|
||||
#include "glk/alan3/alan3.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/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/types.h"
|
||||
#include "glk/alan3/utils.h"
|
||||
#include "glk/alan3/word.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Alan3 {
|
||||
|
||||
// PUBLIC DATA
|
||||
Common::SeekableReadStream *textFile;
|
||||
|
||||
// PUBLIC DATA - formerly method statics
|
||||
bool printFlag; // Printing already?
|
||||
|
||||
/* PRIVATE CONSTANTS */
|
||||
|
||||
#define WIDTH 80
|
||||
|
||||
/*======================================================================*/
|
||||
void print(Aword fpos, Aword len) {
|
||||
char str[2 * WIDTH]; /* String buffer */
|
||||
uint outlen = 0; /* Current output length */
|
||||
int ch = 0;
|
||||
int i;
|
||||
long savfp = 0; /* Temporary saved text file position */
|
||||
bool savedPrintFlag = printFlag;
|
||||
void *info = nullptr; /* 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();
|
||||
|
||||
if (ch == EOFChar)
|
||||
break; // Or end of text?
|
||||
|
||||
str[i] = ch;
|
||||
}
|
||||
str[i] = '\0';
|
||||
|
||||
output(str);
|
||||
}
|
||||
|
||||
/* And restore */
|
||||
printFlag = savedPrintFlag;
|
||||
if (printFlag) {
|
||||
if (header->pack)
|
||||
popDecode(info);
|
||||
else
|
||||
textFile->seek(savfp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void sys(Aword fpos, Aword len) {
|
||||
syserr("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 = nullptr;
|
||||
messageParameters = (Parameter *)ensureParameterArrayAllocated(messageParameters);
|
||||
|
||||
current.location = where(HERO, DIRECT);
|
||||
clearParameterArray(messageParameters);
|
||||
addParameterForString(&messageParameters[0], words);
|
||||
setEndOfArray(&messageParameters[1]);
|
||||
printMessageWithParameters(M_UNDONE, messageParameters);
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void undo(CONTEXT) {
|
||||
forgetGameState();
|
||||
if (anySavedState()) {
|
||||
recallGameState();
|
||||
sayUndoneCommand(recreatePlayerCommand());
|
||||
} else {
|
||||
printMessage(M_NO_UNDO);
|
||||
}
|
||||
|
||||
LONG_JUMP_LABEL("returnUndo")
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void quitGame(CONTEXT) {
|
||||
char buf[80];
|
||||
bool flag;
|
||||
|
||||
current.location = where(HERO, DIRECT);
|
||||
para();
|
||||
while (!g_vm->shouldQuit()) {
|
||||
col = 1;
|
||||
CALL0(g_io->statusLine)
|
||||
printMessage(M_QUITACTION);
|
||||
|
||||
FUNC2(g_io->readLine, flag, buf, 80)
|
||||
if (!flag)
|
||||
CALL1(terminate, 0)
|
||||
|
||||
if (scumm_stricmp(buf, "restart") == 0) {
|
||||
LONG_JUMP_LABEL("restart")
|
||||
|
||||
} else if (scumm_stricmp(buf, "restore") == 0) {
|
||||
g_vm->loadGame();
|
||||
return;
|
||||
|
||||
} else if (scumm_stricmp(buf, "quit") == 0) {
|
||||
CALL1(terminate, 0)
|
||||
|
||||
} else if (scumm_stricmp(buf, "undo") == 0) {
|
||||
if (gameStateChanged) {
|
||||
rememberCommands();
|
||||
rememberGameState();
|
||||
CALL0(undo)
|
||||
} else {
|
||||
if (anySavedState()) {
|
||||
recallGameState();
|
||||
sayUndoneCommand(playerWordsAsCommandString());
|
||||
} else {
|
||||
printMessage(M_NO_UNDO);
|
||||
}
|
||||
|
||||
LONG_JUMP_LABEL("returnUndo")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syserr("Fallthrough in QUIT");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void restartGame(CONTEXT) {
|
||||
Aint previousLocation = current.location;
|
||||
current.location = where(HERO, DIRECT);
|
||||
para();
|
||||
|
||||
bool flag;
|
||||
FUNC1(confirm, flag, M_REALLY)
|
||||
if (flag) {
|
||||
LONG_JUMP_LABEL("restart")
|
||||
}
|
||||
|
||||
current.location = previousLocation;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void cancelEvent(Aword theEvent) {
|
||||
int i;
|
||||
|
||||
for (i = eventQueueTop - 1; i >= 0; i--)
|
||||
if (eventQueue[i].event == (int)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 == nullptr) 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) {
|
||||
uint i;
|
||||
|
||||
if (event == 0) syserr("NULL event");
|
||||
|
||||
cancelEvent(event);
|
||||
/* Check for overflow */
|
||||
if (eventQueue == nullptr || eventQueueTop == eventQueueSize) {
|
||||
increaseEventQueue();
|
||||
assert(eventQueue);
|
||||
}
|
||||
|
||||
/* Bubble this event down */
|
||||
for (i = eventQueueTop; i >= 1 && eventQueue[i - 1].after <= (int)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);
|
||||
size_t ln = strlen(s1) + strlen(s2) + 1;
|
||||
char *result = (char *)allocate(ln);
|
||||
Common::strcpy_s(result, ln, s1);
|
||||
Common::strcat_s(result, ln, s2);
|
||||
return toAptr(result);
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static char *stripCharsFromStringForwards(int count, char *initialString, char **theRest) {
|
||||
int stripPosition;
|
||||
char *strippedString;
|
||||
char *rest;
|
||||
|
||||
if (count > (int)strlen(initialString))
|
||||
stripPosition = strlen(initialString);
|
||||
else
|
||||
stripPosition = count;
|
||||
rest = scumm_strdup(&initialString[stripPosition]);
|
||||
strippedString = scumm_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 > (int)strlen(initialString))
|
||||
stripPosition = 0;
|
||||
else
|
||||
stripPosition = strlen(initialString) - count;
|
||||
strippedString = scumm_strdup(&initialString[stripPosition]);
|
||||
rest = scumm_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[] = " .,?";
|
||||
|
||||
uint i;
|
||||
|
||||
for (i = position; i <= strlen(string) && strchr(separators, string[i]) == nullptr; 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 = scumm_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]) == nullptr; i--)
|
||||
;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
static int countTrailingBlanks(char *string, int position) {
|
||||
int skippedChars, i;
|
||||
skippedChars = 0;
|
||||
|
||||
if (position > (int)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 = scumm_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) {
|
||||
uint 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 empty(CONTEXT, int cnt, int whr) {
|
||||
uint i;
|
||||
|
||||
for (i = 1; i <= header->instanceMax; i++)
|
||||
if (isIn(i, cnt, DIRECT))
|
||||
CALL2(locate, i, whr)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void use(CONTEXT, int actor, int script) {
|
||||
char str[80];
|
||||
StepEntry *step;
|
||||
|
||||
if (!isAActor(actor)) {
|
||||
Common::sprintf_s(str, "Instance is not an Actor (%d).", actor);
|
||||
syserr(str);
|
||||
}
|
||||
|
||||
admin[actor].script = script;
|
||||
admin[actor].step = 0;
|
||||
step = stepOf(actor);
|
||||
if (step != nullptr && step->after != 0) {
|
||||
FUNC1(evaluate, admin[actor].waitCount, step->after)
|
||||
}
|
||||
|
||||
gameStateChanged = TRUE;
|
||||
}
|
||||
|
||||
/*======================================================================*/
|
||||
void stop(int act) {
|
||||
char str[80];
|
||||
|
||||
if (!isAActor(act)) {
|
||||
Common::sprintf_s(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 (g_vm->getRandomNumber(0x7fffffff) / 10) % (to - from + 1) + from;
|
||||
else
|
||||
return (g_vm->getRandomNumber(0x7fffffff) / 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)) != nullptr);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
bool streq(char a[], char b[]) {
|
||||
bool eq;
|
||||
|
||||
strlow(a);
|
||||
strlow(b);
|
||||
|
||||
eq = (strcmp(a, b) == 0);
|
||||
|
||||
return eq;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void startTranscript(void) {
|
||||
if (logFile == nullptr) {
|
||||
Common::String filename = g_vm->getTargetName() + ".log";
|
||||
|
||||
uint fileUsage = transcriptOption ? fileusage_Transcript : fileusage_InputRecord;
|
||||
frefid_t logFileRef = g_vm->glk_fileref_create_by_name(fileUsage, filename.c_str(), 0);
|
||||
logFile = g_vm->glk_stream_open_file(logFileRef, filemode_Write, 0);
|
||||
|
||||
if (logFile == nullptr) {
|
||||
transcriptOption = FALSE;
|
||||
logOption = FALSE;
|
||||
} else {
|
||||
transcriptOption = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*======================================================================*/
|
||||
void stopTranscript(void) {
|
||||
if (logFile != nullptr) {
|
||||
if (transcriptOption || logOption)
|
||||
delete logFile;
|
||||
|
||||
logFile = nullptr;
|
||||
transcriptOption = FALSE;
|
||||
logOption = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Alan3
|
||||
} // End of namespace Glk
|
||||
Reference in New Issue
Block a user