1507 lines
46 KiB
C++
1507 lines
46 KiB
C++
/*
|
|
* QT AGI Studio :: Copyright (C) 2000 Helen Zommer
|
|
*
|
|
* Almost all of this code was adapted from the Windows AGI Studio
|
|
* developed by Peter Kelly.
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include "logic.h"
|
|
#include "game.h"
|
|
#include "logedit.h"
|
|
#include "menu.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
|
|
extern TStringList InputLines; //temporary -
|
|
//input text from the editor window or file
|
|
|
|
static bool UseTypeChecking = true;
|
|
static int ResPos,LogicSize;
|
|
static TStringList EditLines,IncludeFilenames;
|
|
static string DefineNames[MaxDefines];
|
|
static string DefineValues[MaxDefines];
|
|
static int NumDefines;
|
|
static int RealLineNum[65535], LineFile[65535];
|
|
static int DefineNameLength[MaxDefines];
|
|
static string Messages[MaxMessages];
|
|
static bool MessageExists[MaxMessages];
|
|
|
|
typedef struct{
|
|
string Name;
|
|
int Loc;
|
|
}TLogicLabel;
|
|
static TLogicLabel Labels[MaxLabels+1];
|
|
static int NumLabels;
|
|
|
|
static bool ErrorOccured;
|
|
static int CurLine;
|
|
static string LowerCaseLine,ArgText,LowerCaseArgText;
|
|
static string::size_type LinePos,LineLength,ArgTextLength,ArgTextPos;
|
|
static bool FinishedReading;
|
|
static int CommandNameStartPos;
|
|
static string CommandName;
|
|
static int CommandNum;
|
|
static bool NOTOn;
|
|
|
|
char empty_tmp[] = {0};
|
|
|
|
extern const char EncryptionKey[];
|
|
static int EncryptionStart;
|
|
//*************************************************
|
|
static void WriteByte(byte b)
|
|
{
|
|
|
|
if(ResPos < ResourceData.Size){
|
|
ResourceData.Data[ResPos++]=b;
|
|
if(ResPos > LogicSize)LogicSize=ResPos;
|
|
}
|
|
}
|
|
|
|
|
|
static void WriteByteAtLoc(byte b,int Loc)
|
|
{
|
|
|
|
if(Loc < ResourceData.Size){
|
|
ResourceData.Data[Loc]=b;
|
|
if(Loc > LogicSize)LogicSize= Loc;
|
|
}
|
|
|
|
}
|
|
|
|
static void WriteLSMSWord(short word)
|
|
{
|
|
|
|
WriteByte(word % 256);
|
|
WriteByte(word / 256);
|
|
|
|
}
|
|
//*************************************************
|
|
void Logic::ShowError(int Line, string ErrorMsg)
|
|
{
|
|
|
|
int LineNum = RealLineNum[Line];
|
|
if(LineFile[Line] == 0 || Line > EditLines.num){
|
|
// error is in logic in editor window
|
|
sprintf(tmp,"Line %d: %s\n",RealLineNum[Line],ErrorMsg.c_str());
|
|
}
|
|
else{ //error in include file
|
|
if (LineFile[Line] > IncludeFilenames.num){
|
|
sprintf(tmp,"[unknown include file] Line ???: %s\n",ErrorMsg.c_str());
|
|
}
|
|
else{
|
|
sprintf(tmp,"File %s Line %d: %s\n",IncludeFilenames.at(LineFile[Line]-1).c_str(),LineNum,ErrorMsg.c_str());
|
|
}
|
|
}
|
|
|
|
ErrorList.append(tmp);
|
|
ErrorOccured=true;
|
|
|
|
}
|
|
|
|
//***************************************************
|
|
string Logic::ReadString(string::size_type *pos, string& str)
|
|
//returns string without quotes, starting from pos1
|
|
//pos is set to the 1st char after string
|
|
{
|
|
string::size_type pos1 = *pos;
|
|
string::size_type pos2 = pos1;
|
|
|
|
// printf ("ReadString: str=%s pos=%d\n",str.c_str(),*pos);
|
|
|
|
do{
|
|
pos2 = str.find_first_of("\"",pos2+1);
|
|
if(pos2 == string::npos){
|
|
ShowError(CurLine,"\" required at end of string.");
|
|
printf("string: *%s*\n",str.c_str());
|
|
return "";
|
|
}
|
|
}while(str[pos2-1]=='\\');
|
|
|
|
*pos = pos2+1;
|
|
if(pos2==pos1+1){
|
|
return "";
|
|
}
|
|
|
|
return str.substr(pos1+1,pos2-pos1-1);
|
|
}
|
|
//***************************************************
|
|
int Logic::RemoveComments(TStringList Lines)
|
|
{
|
|
int CommentDepth = 0;
|
|
for(CurLine=0;CurLine<Lines.num;CurLine++){
|
|
string Line = Lines.at(CurLine);
|
|
string NewLine;
|
|
bool InQuotes = false;
|
|
for ( unsigned i=0; i<Line.size(); ++i ){
|
|
if ( !InQuotes ){
|
|
if (CommentDepth==0 && Line[i] == '[')
|
|
break;
|
|
if (i<Line.size()-1){
|
|
if (CommentDepth==0 && Line.substr(i,2) == "//")
|
|
break;
|
|
else if ( Line.substr(i,2) == "/*"){
|
|
++CommentDepth;
|
|
++i;
|
|
continue;
|
|
}
|
|
}
|
|
else if (CommentDepth>0 && Line.substr(i,2) == "*/" ){
|
|
--CommentDepth;
|
|
++i;
|
|
continue;
|
|
}
|
|
}
|
|
if ( CommentDepth == 0 ){
|
|
if(Line[i]=='\"' && (i==0 || Line[i-1] != '\\'))
|
|
InQuotes = !InQuotes;
|
|
NewLine += Line[i];
|
|
}
|
|
}
|
|
Lines.replace(CurLine,NewLine);
|
|
}
|
|
return 0;
|
|
}
|
|
//***************************************************
|
|
int Logic::AddIncludes()
|
|
{
|
|
TStringList IncludeStrings,IncludeLines;
|
|
int CurInputLine,CurIncludeLine;
|
|
string filename;
|
|
int err=0;
|
|
string::size_type pos1,pos2;
|
|
int CurLine;
|
|
char *ptr;
|
|
|
|
IncludeFilenames = TStringList();
|
|
IncludeStrings = TStringList();
|
|
EditLines = TStringList();
|
|
IncludeLines = TStringList();
|
|
CurLine = 0;
|
|
for(CurInputLine = 0;CurInputLine<InputLines.num;CurInputLine++){
|
|
EditLines.add(InputLines.at(CurInputLine));
|
|
CurLine = EditLines.num -1;
|
|
RealLineNum[CurLine] = CurInputLine;
|
|
LineFile[CurLine] = 0;
|
|
#ifdef _WIN32
|
|
if(_strnicmp(InputLines.at(CurInputLine).c_str(),"#include",8)) {
|
|
#else
|
|
if(strncasecmp(InputLines.at(CurInputLine).c_str(),"#include",8)){
|
|
#endif
|
|
continue;
|
|
}
|
|
string str = InputLines.at(CurInputLine).substr(8);
|
|
if(str.length()<4){
|
|
ShowError(CurLine,"Missing include filename !");
|
|
err=1;
|
|
continue;
|
|
}
|
|
if(str[0] != ' '){
|
|
ShowError(CurLine,"' ' expected after #include.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
pos1 = str.find_first_of("\"",1);
|
|
pos2 = str.find_first_of("\"",pos1+1);
|
|
if(pos1 == string::npos || pos2 == string::npos){
|
|
ShowError(CurLine,"Include filenames need quote marks around them.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
filename = str.substr(pos1+1,pos2-pos1-1);
|
|
if(filename.find_first_of("/")!=string::npos){
|
|
ShowError(CurLine,"Only files in the src directory can be included.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
sprintf(tmp,"%s/src/%s",game->dir.c_str(),filename.c_str());
|
|
FILE *fptr = fopen(tmp,"rb");
|
|
if(fptr==NULL){
|
|
sprintf(tmp,"Can't open include file: %s/src/%s",game->dir.c_str(),filename.c_str());
|
|
ShowError(CurLine,tmp);
|
|
err=1;
|
|
continue;
|
|
}
|
|
IncludeLines.lfree();
|
|
|
|
while(fgets(tmp,MAX_TMP,fptr)!=NULL){
|
|
if((ptr=strchr(tmp,0x0a)))*ptr=0;
|
|
if((ptr=strchr(tmp,0x0d)))*ptr=0;
|
|
IncludeLines.add(tmp);
|
|
}
|
|
fclose(fptr);
|
|
if(IncludeLines.num==0)continue;
|
|
IncludeFilenames.add(filename);
|
|
RemoveComments(IncludeLines);
|
|
EditLines.replace(CurLine,empty_tmp);
|
|
for(CurIncludeLine=0;CurIncludeLine<IncludeLines.num;CurIncludeLine++){
|
|
EditLines.add(IncludeLines.at(CurIncludeLine));
|
|
CurLine=EditLines.num-1;
|
|
RealLineNum[CurLine] = CurIncludeLine;
|
|
LineFile[CurLine] = IncludeFilenames.num;
|
|
}
|
|
}
|
|
|
|
IncludeLines.lfree();
|
|
InputLines.lfree();
|
|
return err;
|
|
|
|
}
|
|
|
|
//***************************************************
|
|
int Logic::ReadDefines()
|
|
{
|
|
int err=0,i;
|
|
string::size_type pos1,pos2;
|
|
string ThisDefineName,ThisDefineValue;
|
|
int CurLine;
|
|
|
|
NumDefines = 0;
|
|
for(CurLine = 0;CurLine<EditLines.num;CurLine++){
|
|
#ifdef _WIN32
|
|
if(_strnicmp(EditLines.at(CurLine).c_str(),"#define",7)){
|
|
#else
|
|
if(strncasecmp(EditLines.at(CurLine).c_str(),"#define",7)){
|
|
#endif
|
|
continue;
|
|
}
|
|
string str = EditLines.at(CurLine).substr(7);
|
|
toLower(&str);
|
|
if(str.length()<4){
|
|
ShowError(CurLine,"Missing define name !");
|
|
err=1;
|
|
continue;
|
|
}
|
|
if(str[0] != ' '){
|
|
ShowError(CurLine,"' ' expected after #define.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
if(NumDefines >= MaxDefines){
|
|
ShowError(CurLine,"Too many defines (max " + IntToStr(MaxDefines) + ")");
|
|
err=1;
|
|
continue;
|
|
}
|
|
pos1 = str.find_first_not_of(" ",1);
|
|
pos2 = str.find_first_of(" ",pos1);
|
|
if(pos1 == string::npos||pos2 == string::npos){
|
|
ShowError(CurLine,"Missing define name !");
|
|
err=1;
|
|
continue;
|
|
}
|
|
ThisDefineName = str.substr(pos1,pos2-1);
|
|
if(ThisDefineName.find_first_not_of("qwertyuiopasdfghjklzxcvbnm1234567890._") != string::npos){
|
|
ShowError(CurLine,"Define name can contain only characters from [a-z],'.' and '_'.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
for(i=0;i<NumDefines;i++){
|
|
if(ThisDefineName == DefineNames[i]){
|
|
ShowError(CurLine,ThisDefineName + " already defined !");
|
|
err=1;
|
|
break;
|
|
}
|
|
}
|
|
if(err)continue;
|
|
|
|
for(i=0;i<=NumAGICommands;i++){
|
|
if(ThisDefineName == AGICommand[i].Name){
|
|
ShowError(CurLine,"Define name can not be a command name.");
|
|
err=1;
|
|
break;
|
|
}
|
|
}
|
|
if(err)continue;
|
|
|
|
for(i=1;i<=NumTestCommands;i++){
|
|
if(ThisDefineName == TestCommand[i].Name){
|
|
ShowError(CurLine,"Define name can not be a command name.");
|
|
err=1; break;
|
|
}
|
|
}
|
|
if(err)continue;
|
|
|
|
if(ThisDefineName == "if" || ThisDefineName == "else" || ThisDefineName == "goto"){
|
|
ShowError(CurLine,"Invalid define name (" + ThisDefineName + ")");
|
|
err=1;
|
|
continue;
|
|
}
|
|
|
|
pos1 = str.find_first_not_of(" ",pos2+1);
|
|
if(pos1 == string::npos){
|
|
ShowError(CurLine,"Missing define value !");
|
|
err=1;
|
|
continue;
|
|
}
|
|
if(str[pos1] == '"'){
|
|
ThisDefineValue = "\"" + ReadString(&pos1,str) + "\"";
|
|
if(ErrorOccured)continue;
|
|
if(str.find_first_not_of(" ",pos1) != string::npos){
|
|
ShowError(CurLine,"Nothing allowed on line after define value.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
}
|
|
else{
|
|
pos2 = str.find_first_of(" ",pos1+1);
|
|
if(pos2 == string::npos){
|
|
ThisDefineValue = str.substr(pos1);
|
|
}
|
|
else{
|
|
ThisDefineValue = str.substr(pos1,pos2-pos1);
|
|
if(str.find_first_not_of(" ",pos2) != string::npos){
|
|
ShowError(CurLine,"Nothing allowed on line after define value.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
}
|
|
if(ThisDefineValue.find_first_not_of("qwertyuiopasdfghjklzxcvbnm1234567890._") != string::npos){
|
|
ShowError(CurLine,"Non-string define value can contain only characters from [a-z],'.' and '_'.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
DefineNames[NumDefines]=ThisDefineName;
|
|
DefineValues[NumDefines]=ThisDefineValue;
|
|
DefineNameLength[NumDefines] = ThisDefineName.length();
|
|
NumDefines++;
|
|
EditLines.replace(CurLine,empty_tmp);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
//***************************************************
|
|
int Logic::ReadPredefinedMessages()
|
|
{
|
|
int err=0,i;
|
|
string::size_type pos1;
|
|
int MessageNum;
|
|
|
|
for(i=0;i<MaxMessages;i++){
|
|
Messages[i]="";
|
|
MessageExists[i]=false;
|
|
}
|
|
for(CurLine = 0;CurLine<EditLines.num;CurLine++){
|
|
#ifdef _WIN32
|
|
if(_strnicmp(EditLines.at(CurLine).c_str(),"#message",8)){
|
|
#else
|
|
if(strncasecmp(EditLines.at(CurLine).c_str(),"#message",8)){
|
|
#endif
|
|
continue;
|
|
}
|
|
string str = EditLines.at(CurLine).substr(8);
|
|
if(str[0] != ' '){
|
|
ShowError(CurLine,"' ' expected after #message.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
MessageNum = atoi(str.c_str());
|
|
if(MessageNum==0){
|
|
ShowError(CurLine,"Invalid message number (must be 1-255).");
|
|
err=1;
|
|
continue;
|
|
}
|
|
pos1 = str.find_first_of("\"");
|
|
if(pos1 == string::npos){
|
|
ShowError(CurLine,"\" required at start of string.");
|
|
err=1;
|
|
continue;
|
|
}
|
|
Messages[MessageNum]=ReadString(&pos1,str);
|
|
if(ErrorOccured)continue;
|
|
if(Messages[MessageNum].find_first_not_of(" ",pos1) != string::npos){
|
|
sprintf(tmp,"Nothing allowed on line after message. ");
|
|
ShowError(CurLine,tmp);
|
|
err=1;
|
|
continue;
|
|
}
|
|
MessageExists[MessageNum] =true;
|
|
EditLines.replace(CurLine,empty_tmp);
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
//***************************************************
|
|
int Logic::ReadLabels()
|
|
{
|
|
int err=0,i;
|
|
string::size_type pos1,pos2;
|
|
string LabelName;
|
|
int CurLine;
|
|
|
|
NumLabels = 0;
|
|
for(CurLine = 0;CurLine<EditLines.num;CurLine++){
|
|
string str = EditLines.at(CurLine);
|
|
toLower(&str);
|
|
pos1 = str.find_first_not_of(" ");
|
|
if(pos1 == string::npos)continue;
|
|
pos2 = str.find_first_not_of("qwertyuiopasdfghjklzxcvbnm1234567890._",pos1);
|
|
if(pos2 == string::npos)continue;
|
|
if((pos1 == pos2) || (str[pos2]!=':'))continue;
|
|
LabelName = str.substr(pos1,pos2-pos1);
|
|
for(i=1;i<=NumLabels;i++){
|
|
if(LabelName == Labels[i].Name){
|
|
ShowError(CurLine,"Label "+LabelName+" already defined.");
|
|
err=1;break;
|
|
}
|
|
}
|
|
if(err)continue;
|
|
if(NumLabels > MaxLabels){
|
|
ShowError(CurLine,"Too many labels (max "+IntToStr(MaxLabels)+")");
|
|
err=1;continue;
|
|
}
|
|
if(LabelName == "if" || LabelName == "else" || LabelName == "goto"){
|
|
ShowError(CurLine,"Invalid label name ("+LabelName+")");
|
|
err=1;continue;
|
|
}
|
|
for(i=0;i<NumDefines;i++){
|
|
if((LabelName == DefineNames[i]) || (LabelName+":" == DefineNames[i])){
|
|
ShowError(CurLine,"Can't have a label with the same name a a define.");
|
|
err=1;break;
|
|
}
|
|
}
|
|
if(err)continue;
|
|
NumLabels++;
|
|
Labels[NumLabels].Name = LabelName;
|
|
Labels[NumLabels].Loc = 0;
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
//***************************************************
|
|
void Logic::NextLine()
|
|
{
|
|
|
|
int NumLines = EditLines.num;
|
|
|
|
CurLine++;
|
|
if(CurLine>NumLines){
|
|
FinishedReading = true;
|
|
return;
|
|
}
|
|
do{
|
|
LowerCaseLine = EditLines.at(CurLine);
|
|
if(LowerCaseLine == empty_tmp || (LinePos=LowerCaseLine.find_first_not_of(" ")) == string::npos){
|
|
CurLine++;
|
|
continue;
|
|
}
|
|
//printf("Line %d: %s\n",CurLine,LowerCaseLine.c_str());
|
|
toLower(&LowerCaseLine);
|
|
LineLength = LowerCaseLine.length();
|
|
return;
|
|
}while(CurLine<NumLines);
|
|
FinishedReading = true;
|
|
|
|
}
|
|
//***************************************************
|
|
void Logic::SkipSpaces()
|
|
{
|
|
LinePos=LowerCaseLine.find_first_not_of(" ",LinePos);
|
|
if(LinePos==string::npos)NextLine();
|
|
|
|
}
|
|
//***************************************************
|
|
byte Logic::MessageNum(string TheMessage)
|
|
{
|
|
for(int i=1;i<MaxMessages;i++){
|
|
if(MessageExists[i] && Messages[i] == TheMessage)return i;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
//***************************************************
|
|
byte Logic::AddMessage(string TheMessage)
|
|
{
|
|
// Adds a message to the message list regardles of whether or not it
|
|
// already exists. Returns the number of the added message, or 0 if
|
|
// there are already 255 messages.
|
|
|
|
for(int i=1;i<MaxMessages;i++){
|
|
if(!MessageExists[i]){
|
|
MessageExists[i]=true;
|
|
Messages[i]=TheMessage;
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
//***************************************************
|
|
static string TrimEndWhitespaces( const string& str )
|
|
{
|
|
int i=str.length();
|
|
while( i>0 && (str[i-1]==' ' || str[i-1]=='\t'))
|
|
--i;
|
|
return str.substr(0,i);
|
|
}
|
|
//***************************************************
|
|
string Logic::ReplaceDefine(string InText)
|
|
{
|
|
string str=InText;
|
|
toLower(&str);
|
|
for(int i=0;i<NumDefines;i++){
|
|
if(str == DefineNames[i]){
|
|
return DefineValues[i];
|
|
}
|
|
}
|
|
return InText;
|
|
}
|
|
//***************************************************
|
|
void Logic::ReadArgText()
|
|
// do not use for string - does not take quotes into account
|
|
{
|
|
string::size_type pos1,pos2;
|
|
|
|
SkipSpaces();
|
|
pos1 = pos2 = LinePos;
|
|
|
|
if(LowerCaseLine[pos1]=='"'){
|
|
ArgText = "\""+ReadString(&pos2,LowerCaseLine)+"\"";
|
|
pos2 = LowerCaseLine.find_first_of(",)",pos2);
|
|
}
|
|
else{
|
|
pos2 = LowerCaseLine.find_first_of(",)",pos1);
|
|
}
|
|
if(pos2==string::npos){
|
|
LinePos=LineLength;
|
|
ArgText = ReplaceDefine(TrimEndWhitespaces(
|
|
EditLines.at(CurLine).substr(pos1)));
|
|
}
|
|
else{
|
|
LinePos=pos2;
|
|
ArgText = ReplaceDefine(TrimEndWhitespaces(
|
|
EditLines.at(CurLine).substr(pos1,pos2-pos1)));
|
|
}
|
|
|
|
LowerCaseArgText = ArgText;
|
|
toLower(&LowerCaseArgText);
|
|
ArgTextLength = ArgText.length();
|
|
ArgTextPos=0;
|
|
}
|
|
//***************************************************
|
|
int Logic::ReadArgValue()
|
|
{
|
|
char *ptr;
|
|
string str2 = ArgText.substr(ArgTextPos);
|
|
const char *str = str2.c_str();
|
|
long val = strtol(str,&ptr,10);
|
|
ArgTextPos += (int)(ptr-str);
|
|
if((val ==0 && ptr==str) || val==LONG_MIN||val==LONG_MAX) return -1;
|
|
return val;
|
|
|
|
}
|
|
//***************************************************
|
|
int Logic::Val(string str)
|
|
{
|
|
char *ptr;
|
|
const char *s = str.c_str();
|
|
long val = strtol(s,&ptr,10);
|
|
if(val==LONG_MIN||val==LONG_MAX)return -1;
|
|
if(val==0){
|
|
if(ptr==s)return -1;
|
|
else return val;
|
|
}
|
|
return val;
|
|
|
|
}
|
|
//***************************************************
|
|
void Logic::ReadArgs(bool CommandIsIf, byte CmdNum)
|
|
{
|
|
char *ThisArgTypePrefix;
|
|
bool FinishedReadingSaidArgs=false;
|
|
int ArgValue,NumSaidArgs;
|
|
string ThisWord;
|
|
#define MaxSaidArgs 40
|
|
int SaidArgs[MaxSaidArgs];
|
|
int CurArg;
|
|
CommandStruct ThisCommand;
|
|
string ThisMessage;
|
|
int ThisMessageNum;
|
|
string ThisInvObjectName;
|
|
int i;
|
|
|
|
SkipSpaces();
|
|
if(LinePos >= LineLength || EditLines.at(CurLine)[LinePos] != '('){
|
|
ShowError(CurLine,"'(' expected.");
|
|
return;
|
|
}
|
|
LinePos++;
|
|
if(CmdNum==14 && CommandIsIf){ //said test command
|
|
NumSaidArgs = -1;
|
|
FinishedReadingSaidArgs = false;
|
|
do{
|
|
ReadArgText();
|
|
NumSaidArgs++;
|
|
if(ArgText[0]=='"'){
|
|
ArgValue=0;
|
|
ArgTextPos=0;
|
|
ThisWord=ReadString(&ArgTextPos,ArgText);
|
|
if(ErrorOccured)
|
|
ShowError(CurLine,"\" required at end of word.");
|
|
else{
|
|
//find word group number
|
|
bool found=false;
|
|
for(int k=0;k<wordlist->NumGroups;k++){
|
|
for(int i=0;i<wordlist->WordGroup[k].Words.num;i++){
|
|
if( wordlist->WordGroup[k].Words.at(i) == ThisWord){
|
|
ArgValue = wordlist->WordGroup[k].GroupNum;
|
|
found=true;
|
|
break;
|
|
}
|
|
}
|
|
if(found)break;
|
|
}
|
|
if(!found){
|
|
ShowError(CurLine,"Unknown word "+ThisWord+".");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
else ArgValue = ReadArgValue();
|
|
SaidArgs[NumSaidArgs] = ArgValue;
|
|
if (SaidArgs[NumSaidArgs] < 0 || SaidArgs[NumSaidArgs] > 65535){
|
|
ShowError(CurLine,"Invalid word number for argument " +IntToStr(NumSaidArgs)+ " (must be 0-65535).");
|
|
SaidArgs[NumSaidArgs] = 0;
|
|
}
|
|
if ((LinePos < LineLength) & (LowerCaseLine[LinePos] == ',')){
|
|
if (NumSaidArgs > MaxSaidArgs){
|
|
ShowError(CurLine,"Too many arguments for said command.");
|
|
FinishedReadingSaidArgs = true;
|
|
}
|
|
}
|
|
else if(LinePos < LineLength && LowerCaseLine[LinePos] == ')'){
|
|
FinishedReadingSaidArgs = true;
|
|
}
|
|
else
|
|
ShowError(CurLine,"',' or ')' expected after argument "+IntToStr(NumSaidArgs)+".");
|
|
LinePos++;
|
|
}while(!FinishedReadingSaidArgs||ErrorOccured);
|
|
WriteByte(NumSaidArgs+1);
|
|
for (int i=0;i<=NumSaidArgs;i++){
|
|
WriteByte(SaidArgs[i] % 256);
|
|
WriteByte(SaidArgs[i] / 256);
|
|
}
|
|
}//if said test command
|
|
else{ //other command
|
|
if (CommandIsIf) ThisCommand = TestCommand[CmdNum];
|
|
else ThisCommand = AGICommand[CmdNum];
|
|
for (CurArg = 0;CurArg<ThisCommand.NumArgs;CurArg++){
|
|
SkipSpaces();
|
|
ReadArgText();
|
|
if (ThisCommand.argTypes[CurArg] == atMsg && ArgTextLength >=1 && ArgText[0]=='"'){
|
|
// argument is message and given as string
|
|
ArgTextPos=0;
|
|
ThisMessage = "";
|
|
//splitting the message into lines if it doesn't fit the screen
|
|
do{
|
|
if(ThisMessage != "" && ThisMessage[ThisMessage.length()-1]!=' ')ThisMessage += " ";
|
|
ThisMessage += ReadString(&ArgTextPos,ArgText);
|
|
if(LinePos+1>=LineLength || LowerCaseLine.find_first_not_of(" ",LinePos+1)==string::npos){
|
|
|
|
NextLine();
|
|
SkipSpaces();
|
|
ReadArgText();
|
|
}
|
|
else break;
|
|
}while(true);
|
|
ThisMessageNum = MessageNum(ThisMessage);
|
|
if (ThisMessageNum > 0){
|
|
WriteByte(ThisMessageNum);
|
|
}
|
|
else{
|
|
ThisMessageNum = AddMessage(ThisMessage);
|
|
if (ThisMessageNum == 0)
|
|
ShowError(CurLine,"Too many messages (max 255).");
|
|
else
|
|
WriteByte(ThisMessageNum);
|
|
}
|
|
}//argument is message and given as string
|
|
else if (ThisCommand.argTypes[CurArg] == atIObj && ArgTextLength >= 1 && ArgText[0] == '"'){
|
|
// argument is inventory object and given as string
|
|
ArgTextPos=0;
|
|
ThisInvObjectName= ReadString(&ArgTextPos,ArgText);
|
|
if(ThisInvObjectName == "")ShowError(CurLine,"Object name must be at least one character.");
|
|
else{
|
|
for(i=0;i<objlist->ItemNames.num;i++){
|
|
if(objlist->ItemNames.at(i)==ThisInvObjectName){
|
|
WriteByte(i);
|
|
break;
|
|
}
|
|
}
|
|
if(i>=objlist->ItemNames.num){
|
|
ShowError(CurLine,"Unknown inventory object "+ThisInvObjectName);
|
|
}
|
|
}
|
|
}// argument is inventory object and given as string
|
|
else{ //normal argument
|
|
ThisArgTypePrefix = (char *)ArgTypePrefix[(int)ThisCommand.argTypes[CurArg]];
|
|
if(UseTypeChecking && (strcmp(LowerCaseArgText.substr(0,strlen(ThisArgTypePrefix)).c_str(),ThisArgTypePrefix))){
|
|
ShowError(CurLine,"Invalid or unknown argument type for argument "+IntToStr(CurArg)+" (should be a "+ArgTypeName[(int)ThisCommand.argTypes[CurArg]]+").");
|
|
}
|
|
else{
|
|
if (UseTypeChecking)ArgTextPos+=strlen(ThisArgTypePrefix);
|
|
else
|
|
while (ArgTextPos < ArgTextLength && !(LowerCaseArgText[ArgTextPos] >= 'a' && LowerCaseArgText[ArgTextPos] <= 'z' )) ArgTextPos++;
|
|
ArgValue=ReadArgValue();
|
|
if(ArgValue<0||ArgValue>255)
|
|
ShowError(CurLine,"Invalid or missing value for argument "+IntToStr(CurArg)+" (must be 0-255)");
|
|
else
|
|
WriteByte(ArgValue);
|
|
}
|
|
}//normal argument
|
|
if (CurArg < ThisCommand.NumArgs-1){
|
|
if (ArgTextPos < ArgTextLength)
|
|
ShowError(CurLine,"',' expected after argument "+IntToStr(CurArg)+".");
|
|
else if(LinePos >= LineLength || LowerCaseLine[LinePos] != ',')
|
|
ShowError(CurLine,"',' expected after argument "+IntToStr(CurArg)+".");
|
|
else
|
|
LinePos++;
|
|
}
|
|
else if (ArgTextPos < ArgTextLength){
|
|
ShowError(CurLine,"(1) ')' expected after argument "+IntToStr(CurArg)+".");
|
|
printf("Line %s argtextpos=%d arglen=%d\n",LowerCaseLine.c_str(),(int)ArgTextPos,(int)ArgTextLength);
|
|
}
|
|
}
|
|
SkipSpaces();
|
|
if (LinePos >= LineLength || LowerCaseLine[LinePos] != ')'){
|
|
|
|
if (ThisCommand.NumArgs > 0){
|
|
ShowError(CurLine,"(2) ')' expected after argument "+IntToStr(ThisCommand.NumArgs)+".");
|
|
printf("Line %s argtextpos=%d arglen=%d\n",LowerCaseLine.c_str(),(int)ArgTextPos,(int)ArgTextLength);
|
|
}
|
|
else
|
|
ShowError(CurLine,"')' expected.");
|
|
}
|
|
else
|
|
LinePos++;
|
|
}
|
|
|
|
}
|
|
//***************************************************
|
|
string Logic::ReadText()
|
|
{
|
|
int p = LinePos;
|
|
string::size_type pos = LowerCaseLine.find_first_of("( ,):",LinePos);
|
|
if(pos == string::npos){
|
|
LinePos = LineLength;
|
|
return LowerCaseLine.substr(p);
|
|
}
|
|
else{
|
|
LinePos = pos;
|
|
return LowerCaseLine.substr(p,pos-p);
|
|
}
|
|
|
|
}
|
|
//***************************************************
|
|
string Logic::ReadPlainText()
|
|
{
|
|
int p = LinePos;
|
|
string::size_type pos = LowerCaseLine.find_first_not_of("qwertyuiopasdfghjklzxcvbnm1234567890._",LinePos);
|
|
|
|
if(pos == string::npos){
|
|
LinePos = LineLength;
|
|
return LowerCaseLine.substr(p);
|
|
}
|
|
else{
|
|
LinePos = pos;
|
|
return LowerCaseLine.substr(p,pos-p);
|
|
}
|
|
|
|
}
|
|
//***************************************************
|
|
string Logic::ReadExprText()
|
|
{
|
|
int p = LinePos;
|
|
string::size_type pos = LowerCaseLine.find_first_not_of("=+-*/><!",LinePos);
|
|
if(pos == string::npos){
|
|
LinePos = LineLength;
|
|
return LowerCaseLine.substr(p);
|
|
}
|
|
else{
|
|
LinePos = pos;
|
|
return LowerCaseLine.substr(p,pos-p);
|
|
}
|
|
|
|
}
|
|
|
|
//***************************************************
|
|
void Logic::ReadCommandName()
|
|
{
|
|
|
|
SkipSpaces();
|
|
CommandNameStartPos = LinePos;
|
|
CommandName = ReadText();
|
|
}
|
|
//***************************************************
|
|
byte Logic::FindCommandNum(bool CommandIsIf,string CmdName)
|
|
{
|
|
int i;
|
|
const char *s;
|
|
|
|
s=CmdName.c_str();
|
|
|
|
if (CommandIsIf){
|
|
for(i=1;i<=NumTestCommands;i++){
|
|
if(!strcmp(s,TestCommand[i].Name))return i;
|
|
}
|
|
return 255;
|
|
}
|
|
else{
|
|
for(i=0;i<=NumAGICommands;i++){
|
|
if(!strcmp(s,AGICommand[i].Name))return i;
|
|
}
|
|
return 255;
|
|
}
|
|
|
|
}
|
|
//***************************************************
|
|
bool Logic::AddSpecialIFSyntax()
|
|
{
|
|
int OldLinePos;
|
|
int arg1,arg2;
|
|
bool arg2isvar,AddNOT;
|
|
string ArgText,expr;
|
|
|
|
OldLinePos = LinePos;
|
|
LinePos -= CommandName.length();
|
|
ArgText = ReplaceDefine(ReadPlainText());
|
|
|
|
if(ArgText[0]=='v'){
|
|
if (NOTOn) ShowError(CurLine,"'!' not allowed before var.");
|
|
arg1=Val(ArgText.substr(1));
|
|
if(arg1<0||arg1>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
SkipSpaces();
|
|
expr = ReadExprText();
|
|
SkipSpaces();
|
|
ArgText = ReplaceDefine(ReadPlainText());
|
|
arg2isvar = (ArgText[0]=='v');
|
|
if(arg2isvar)
|
|
arg2=Val(ArgText.substr(1));
|
|
else
|
|
arg2=Val(ArgText);
|
|
if(arg2<0||arg2>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
CommandNum=0;
|
|
AddNOT = false;
|
|
if(expr == "==")CommandNum = 0x01; //equal
|
|
else if(expr == "<")CommandNum = 0x03; //less
|
|
else if(expr == ">")CommandNum = 0x05; //greater
|
|
else if(expr == "!="){ CommandNum = 0x01; AddNOT = true; } //!equal
|
|
else if(expr == ">="){ CommandNum = 0x03; AddNOT = true; } //!less
|
|
else if(expr == "<="){ CommandNum = 0x05; AddNOT = true; } //!greater
|
|
else ShowError(CurLine,"Expression syntax error");
|
|
if(CommandNum>0){
|
|
if (arg2isvar)CommandNum++;
|
|
if (AddNOT) WriteByte(0xFD);
|
|
WriteByte(CommandNum);
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}//if(ArgText[0]=='v')
|
|
else if(ArgText[0]=='f'){
|
|
arg1 = Val(ArgText.substr(1));
|
|
if(arg1<0||arg1>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax..");
|
|
else{
|
|
WriteByte(0x07); // isset
|
|
WriteByte(arg1);
|
|
return true;
|
|
}
|
|
}//if(ArgText[0]=='f')
|
|
else LinePos = OldLinePos;
|
|
return false;
|
|
|
|
|
|
}
|
|
//***************************************************
|
|
bool Logic::AddSpecialSyntax()
|
|
{
|
|
int arg1,arg2,arg3;
|
|
bool arg2isvar=false,arg3isvar=false,arg2isstar=false;
|
|
string ArgText="",expr,expr2;
|
|
int OldLinePos;
|
|
|
|
OldLinePos = LinePos;
|
|
LinePos -= CommandName.length();
|
|
if(CommandName[0]=='*'){
|
|
LinePos++;
|
|
ArgText = "*" + ReplaceDefine(ReadPlainText());
|
|
}
|
|
else ArgText = ReplaceDefine(ReadPlainText());
|
|
|
|
if(ArgText[0]=='v'){
|
|
|
|
arg1 = Val(ArgText.substr(1));
|
|
if(arg1<0 || arg1>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
SkipSpaces();
|
|
expr = ReadExprText();
|
|
if(expr == "++"){
|
|
WriteByte(0x01); // increment
|
|
WriteByte(arg1);
|
|
return true;
|
|
}
|
|
else if (expr == "--"){
|
|
WriteByte(0x02); // decrement
|
|
WriteByte(arg1);
|
|
return true;
|
|
}
|
|
else{
|
|
if(expr[0]=='*'){
|
|
expr = expr.substr(1);
|
|
LinePos++;
|
|
}
|
|
SkipSpaces();
|
|
arg2isstar = false;
|
|
ArgText = ReadPlainText();
|
|
if(ReadPlainText() == "" && LowerCaseLine[LinePos-ArgText.length()]=='*'){
|
|
LinePos++;
|
|
ArgText = "*" + ReplaceDefine(ReadPlainText());
|
|
}
|
|
else ArgText = ReplaceDefine(ArgText);
|
|
|
|
if(ArgText[0] == 'v' && !arg2isstar)arg2isvar=true;
|
|
else if (ArgText.substr(0,2) == "*v" && !arg2isstar) arg2isstar = true;
|
|
|
|
if(arg2isvar)arg2 = Val(ArgText.substr(1));
|
|
else if(arg2isstar)arg2 = Val(ArgText.substr(2));
|
|
else arg2 = Val(ArgText);
|
|
|
|
if(arg2 <0 || arg2 >255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
if(expr == "+=" && !arg2isstar){
|
|
if(arg2isvar)WriteByte(0x06); //addv
|
|
else WriteByte(0x05); //addn
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
else if(expr == "-=" && !arg2isstar){
|
|
if(arg2isvar)WriteByte(0x08); //subv
|
|
else WriteByte(0x07); //subn
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
else if(expr == "*=" && !arg2isstar){
|
|
if(arg2isvar)WriteByte(0xa6); //mul.v
|
|
else WriteByte(0xa5); //mul.n
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
else if(expr == "/=" && !arg2isstar){
|
|
if(arg2isvar)WriteByte(0xa8); //div.v
|
|
else WriteByte(0xa7); //div.n
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
else if(expr == "="){
|
|
if(LinePos < LineLength && EditLines.at(CurLine)[LinePos] == ';'){
|
|
//must be assignn, assignv or rindirect
|
|
if (arg2isvar) WriteByte(0x04); // assignv
|
|
else if (arg2isstar) WriteByte(0x0A); // rindirect
|
|
else WriteByte(0x03); // assignv
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
else if(arg2 != arg1) ShowError(CurLine,"Expression syntax error");
|
|
else{
|
|
SkipSpaces();
|
|
expr2 = ReadExprText();
|
|
SkipSpaces();
|
|
ArgText = ReplaceDefine(ReadPlainText());
|
|
arg3isvar = (ArgText[0]=='v');
|
|
if(arg3isvar)arg3=Val(ArgText.substr(1));
|
|
else arg3 = Val(ArgText);
|
|
if(arg3<0 || arg3>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
if (expr2 == "+"){
|
|
if (arg3isvar) WriteByte(0x06); //addv
|
|
else WriteByte(0x05); //addn
|
|
WriteByte(arg1);
|
|
WriteByte(arg3);
|
|
return true;
|
|
}
|
|
else if (expr2 == "-"){
|
|
if (arg3isvar) WriteByte(0x08); //subv
|
|
else WriteByte(0x07); //subn
|
|
WriteByte(arg1);
|
|
WriteByte(arg3);
|
|
return true;
|
|
}
|
|
else if (expr2 == "*"){
|
|
if (arg3isvar) WriteByte(0xa6); //mul.v
|
|
else WriteByte(0xa5); //mul.n
|
|
WriteByte(arg1);
|
|
WriteByte(arg3);
|
|
return true;
|
|
}
|
|
else if (expr2 == "/"){
|
|
if (arg3isvar) WriteByte(0xa8); //div.v
|
|
else WriteByte(0xa7); //div.n
|
|
WriteByte(arg1);
|
|
WriteByte(arg3);
|
|
return true;
|
|
}
|
|
else ShowError(CurLine,"Expression syntax error");
|
|
}
|
|
}
|
|
}//if(expr == "=")
|
|
else ShowError(CurLine,"Expression syntax error");
|
|
}
|
|
}//if (expr != "--" && expr != "++")
|
|
}//if(arg1<0 || arg1>255)
|
|
}//if(ArgText[0]=='v')
|
|
else if(ArgText.substr(0,2)=="*v"){
|
|
LinePos -= (CommandName.length() -1);
|
|
ArgText = ReplaceDefine(ReadPlainText());
|
|
arg1 = Val(ArgText.substr(1));
|
|
if(arg1<0 || arg1>255)
|
|
ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
SkipSpaces();
|
|
expr = ReadExprText();
|
|
if(expr != "=")ShowError(CurLine,"Expression syntax error");
|
|
else{
|
|
SkipSpaces();
|
|
ArgText = ReplaceDefine(ReadPlainText());
|
|
arg2isvar = (ArgText[0]=='v');
|
|
if(arg2isvar)arg2 = Val(ArgText.substr(1));
|
|
else arg2 = Val(ArgText);
|
|
if(arg2 < 0 || arg2 > 255) ShowError(CurLine,"Invalid number given or error in expression syntax.");
|
|
else{
|
|
if(arg2isvar)WriteByte(0x09); //lindirectv
|
|
else WriteByte(0x0b); //lindirectn
|
|
WriteByte(arg1);
|
|
WriteByte(arg2);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}//if(ArgText.substr(0,2)=="*v")
|
|
else LinePos = OldLinePos;
|
|
return false;
|
|
}
|
|
//***************************************************
|
|
int Logic::LabelNum(string LabelName)
|
|
{
|
|
|
|
for(int i=1;i<=NumLabels;i++){
|
|
if(Labels[i].Name == LabelName)return i;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
//***************************************************
|
|
bool Logic::LabelAtStartOfLine(string LabelName)
|
|
{
|
|
string::size_type pos = LinePos - LabelName.length()-1;
|
|
if(LowerCaseLine.find_first_not_of(" ")<pos){
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
}
|
|
//***************************************************
|
|
void WriteEncByte(byte TheByte)
|
|
{
|
|
WriteByte( TheByte ^ EncryptionKey[(ResPos-EncryptionStart)%11]) ;
|
|
|
|
}
|
|
|
|
void Logic::WriteMessageSection()
|
|
{
|
|
int MessageSectionStart, MessageSectionEnd;
|
|
int MessageLoc[MaxMessages];
|
|
int CurMessage,NumMessages,ThisMessageLength;
|
|
|
|
MessageSectionStart = ResPos;
|
|
NumMessages = 0;
|
|
for(CurMessage=255;CurMessage>=0;CurMessage--){
|
|
if(MessageExists[CurMessage])break;
|
|
}
|
|
NumMessages = CurMessage;
|
|
WriteByte(NumMessages);
|
|
ResPos = MessageSectionStart + 3 + NumMessages*2;
|
|
EncryptionStart = ResPos;
|
|
for (CurMessage = 1;CurMessage<=NumMessages;CurMessage++){
|
|
if(!MessageExists[CurMessage]){
|
|
MessageLoc[CurMessage]=0;
|
|
continue;
|
|
}
|
|
ThisMessageLength = Messages[CurMessage].length();
|
|
MessageLoc[CurMessage] = ResPos - MessageSectionStart - 1;
|
|
for(int i=0;i<ThisMessageLength;i++){
|
|
if(Messages[CurMessage][i]=='\\' && i<ThisMessageLength-1){
|
|
if(Messages[CurMessage][i+1] == 'n'){
|
|
WriteEncByte(0x0a);i++;
|
|
}
|
|
else if(Messages[CurMessage][i+1] == '"'){
|
|
WriteEncByte(0x22);i++;
|
|
}
|
|
else if(Messages[CurMessage][i+1] == '\\'){
|
|
WriteEncByte(0x5c);i++;
|
|
}
|
|
else WriteEncByte(0x5c);
|
|
}
|
|
else WriteEncByte(Messages[CurMessage][i]);
|
|
}
|
|
WriteEncByte(0x00);
|
|
}
|
|
MessageSectionEnd = ResPos - MessageSectionStart - 1;
|
|
ResPos = MessageSectionStart + 1;
|
|
WriteLSMSWord(MessageSectionEnd);
|
|
for (CurMessage = 1;CurMessage<=NumMessages;CurMessage++)
|
|
WriteLSMSWord(MessageLoc[CurMessage]);
|
|
ResPos = 0;
|
|
WriteLSMSWord(MessageSectionStart-2); // in the file, the message section start is relative to start of actual code
|
|
|
|
}
|
|
//***************************************************
|
|
int Logic::CompileCommands()
|
|
{
|
|
int err=0;
|
|
short BlockDepth;
|
|
short BlockStartDataLoc[MaxBlockDepth+1];
|
|
short BlockLength[MaxBlockDepth+1];
|
|
bool BlockIsIf[MaxBlockDepth+1];
|
|
bool InIf,LastCommandWasReturn,InIfBrackets=false,AwaitingNextTestCommand=false,EncounteredLabel;
|
|
int NumCommandsInIfStatement=0,NumCommandsInIfBrackets=0;
|
|
|
|
typedef struct{
|
|
byte LabelNum;
|
|
int DataLoc;
|
|
}TLogicGoto;
|
|
TLogicGoto Gotos[MaxGotos+1];
|
|
short NumGotos,GotoData,CurGoto;
|
|
|
|
memset( BlockIsIf,0,sizeof( BlockIsIf));
|
|
InIf = false;
|
|
BlockDepth = 0;
|
|
NumGotos = 0;
|
|
FinishedReading = false;
|
|
CurLine = -1;
|
|
NextLine();
|
|
if(FinishedReading){
|
|
ShowError(CurLine,"Nothing to compile !");
|
|
return 1;
|
|
}
|
|
do{
|
|
LastCommandWasReturn = false;
|
|
if(!InIf){
|
|
if(LinePos < LineLength && LowerCaseLine[LinePos] == '}'){
|
|
LinePos++;
|
|
if(BlockDepth==0)
|
|
ShowError(CurLine,"'}' not at end of any command blocks.");
|
|
else{
|
|
// if (ResPos == BlockStartDataLoc[BlockDepth] + 2)
|
|
// ShowError(CurLine,"Command block must contain at least one command.");
|
|
BlockLength[BlockDepth] = ResPos-BlockStartDataLoc[BlockDepth]-2;
|
|
WriteByteAtLoc(BlockLength[BlockDepth] & 0xff,BlockStartDataLoc[BlockDepth]);
|
|
WriteByteAtLoc((BlockLength[BlockDepth]>>8)&0xff,BlockStartDataLoc[BlockDepth]+1);
|
|
BlockDepth--;
|
|
SkipSpaces();
|
|
if (LinePos >= LineLength && CurLine < EditLines.num-1)NextLine();
|
|
if(LowerCaseLine.substr(LinePos,4) == "else"){
|
|
LinePos+=4;
|
|
SkipSpaces();
|
|
if(! BlockIsIf[BlockDepth+1])
|
|
ShowError(CurLine,"'else' not allowed after command blocks that start with 'else'.");
|
|
|
|
else if(LinePos >= LineLength || LowerCaseLine[LinePos] != '{')
|
|
ShowError(CurLine,"'{' expected after else.");
|
|
|
|
else{
|
|
LinePos++;
|
|
BlockDepth++;
|
|
BlockLength[BlockDepth] +=3;
|
|
WriteByteAtLoc(BlockLength[BlockDepth]&0xff,BlockStartDataLoc[BlockDepth]);
|
|
WriteByteAtLoc((BlockLength[BlockDepth]>>8)&0xff,BlockStartDataLoc[BlockDepth]+1);
|
|
BlockIsIf[BlockDepth] = true;
|
|
WriteByte(0xfe);
|
|
BlockStartDataLoc[BlockDepth] = ResPos;
|
|
WriteByte(0x00); // block length filled in later.
|
|
WriteByte(0x00);
|
|
}
|
|
}//if(LowerCaseLine.substr(LinePos,4) == "else"
|
|
}//if BlockDepth > 0
|
|
}//if LowerCaseLine[LinePos] == '}'
|
|
else{
|
|
ReadCommandName();
|
|
if(CommandName == "if"){
|
|
WriteByte(0xFF);
|
|
InIf = true;
|
|
SkipSpaces();
|
|
if(LinePos >= LineLength || EditLines.at(CurLine)[LinePos] != '(')
|
|
ShowError(CurLine,"'(' expected at start of if statement.");
|
|
|
|
LinePos++;
|
|
InIfBrackets = false;
|
|
NumCommandsInIfStatement = 0;
|
|
AwaitingNextTestCommand = true;
|
|
}
|
|
else if(CommandName == "else")
|
|
ShowError(CurLine,"'}' required before 'else'.");
|
|
|
|
else if(CommandName == "goto"){
|
|
if(LinePos >= LineLength || LowerCaseLine[LinePos] != '(')
|
|
ShowError(CurLine,"'(' expected.");
|
|
else{
|
|
LinePos++;
|
|
ReadCommandName();
|
|
CommandName = ReplaceDefine(CommandName);
|
|
if (LabelNum(CommandName) == 0)
|
|
ShowError(CurLine,"Unknown label "+CommandName+".");
|
|
else if (NumGotos >= MaxGotos)
|
|
ShowError(CurLine,"Too many labels (max "+IntToStr(MaxLabels)+").");
|
|
else{
|
|
NumGotos++;
|
|
Gotos[NumGotos].LabelNum = LabelNum(CommandName);
|
|
WriteByte(0xFE);
|
|
Gotos[NumGotos].DataLoc = ResPos;
|
|
WriteByte(0x00);
|
|
WriteByte(0x00);
|
|
if(LinePos >= LineLength || LowerCaseLine[LinePos] != ')')
|
|
ShowError(CurLine,"')' expected after label name.");
|
|
|
|
LinePos++;
|
|
if(LinePos >= LineLength || LowerCaseLine[LinePos] != ';')
|
|
ShowError(CurLine,"';' expected after goto command.");
|
|
LinePos++;
|
|
}
|
|
}
|
|
}
|
|
else{
|
|
CommandNum = FindCommandNum(false,CommandName);
|
|
EncounteredLabel = (LabelNum(CommandName) > 0);
|
|
if (EncounteredLabel && LinePos < LineLength && LowerCaseLine[LinePos] == ':')
|
|
LinePos++;
|
|
else EncounteredLabel = false;
|
|
EncounteredLabel = (EncounteredLabel && LabelAtStartOfLine(CommandName));
|
|
if(EncounteredLabel){
|
|
Labels[LabelNum(CommandName)].Loc = ResPos;
|
|
}
|
|
else{
|
|
if (CommandNum == 255){ // not found
|
|
if (!AddSpecialSyntax())
|
|
ShowError(CurLine,"Unknown action command "+EditLines.at(CurLine).substr(CommandNameStartPos,CommandName.length())+".");
|
|
}
|
|
else{
|
|
WriteByte(CommandNum);
|
|
ReadArgs(false,CommandNum);
|
|
if (CommandNum == 0)LastCommandWasReturn = true;
|
|
}
|
|
if (LinePos >= LineLength || EditLines.at(CurLine)[LinePos] != ';') ShowError(CurLine,"';' expected after command.");
|
|
|
|
LinePos++;
|
|
}//if we found a label
|
|
}//command
|
|
}//if LowerCaseLine[LinePos] != '}'
|
|
}//(!InIf)
|
|
if(InIf){
|
|
LastCommandWasReturn = false;
|
|
if (AwaitingNextTestCommand){
|
|
if (LowerCaseLine[LinePos] == '('){
|
|
if (InIfBrackets)ShowError(CurLine,"Brackets too deep in if statement.");
|
|
InIfBrackets = true;
|
|
WriteByte(0xFC);
|
|
NumCommandsInIfBrackets = 0;
|
|
LinePos++;
|
|
}// if LowerCaseLine[LinePos] = '('
|
|
else if (LowerCaseLine[LinePos] == ')'){
|
|
if (NumCommandsInIfStatement == 0)
|
|
ShowError(CurLine,"If statement must contain at least one command.");
|
|
else if(InIfBrackets && (NumCommandsInIfBrackets==0))
|
|
ShowError(CurLine,"Brackets must contain at least one command.");
|
|
else ShowError(CurLine,"Expected statement but found closing bracket.");
|
|
LinePos++;
|
|
}
|
|
else{
|
|
NOTOn = false;
|
|
if (LowerCaseLine[LinePos] == '!'){
|
|
NOTOn = true;
|
|
LinePos++;
|
|
}
|
|
SkipSpaces();
|
|
ReadCommandName();
|
|
CommandNum = FindCommandNum(true,CommandName);
|
|
if (NOTOn) WriteByte(0xFD);
|
|
if (CommandNum == 255){ // not found
|
|
if (!AddSpecialIFSyntax())ShowError(CurLine,"Unknown test command "+EditLines.at(CurLine).substr(CommandNameStartPos,CommandName.length())+".");
|
|
}
|
|
else{
|
|
WriteByte(CommandNum);
|
|
ReadArgs(true,CommandNum);
|
|
}
|
|
NumCommandsInIfStatement++;
|
|
if (InIfBrackets) NumCommandsInIfBrackets++;
|
|
AwaitingNextTestCommand = false;
|
|
}
|
|
} // if AwaitingNextTestCommand
|
|
else if(LinePos < LineLength){
|
|
if(LowerCaseLine[LinePos] == ')'){
|
|
LinePos++;
|
|
if(InIfBrackets){
|
|
if (NumCommandsInIfBrackets == 0)
|
|
ShowError(CurLine,"Brackets must contain at least one command.");
|
|
else InIfBrackets = false;
|
|
WriteByte(0xFC);
|
|
}
|
|
else{
|
|
if (NumCommandsInIfStatement == 0)
|
|
ShowError(CurLine,"If statement must contain at least one command.");
|
|
else{
|
|
SkipSpaces();
|
|
if (LinePos > LineLength || EditLines.at(CurLine)[LinePos] != '{')
|
|
ShowError(CurLine,"'{' expected after if statement.");
|
|
LinePos++;
|
|
WriteByte(0xFF);
|
|
if (BlockDepth > MaxBlockDepth)
|
|
ShowError(CurLine,"Too many nested blocks (max "+IntToStr(MaxBlockDepth)+").");
|
|
else{
|
|
BlockDepth++;
|
|
BlockStartDataLoc[BlockDepth] = ResPos;
|
|
BlockIsIf[BlockDepth] = true;
|
|
WriteByte(0x00); // block length filled in later.
|
|
WriteByte(0x00);
|
|
}
|
|
InIf = false;
|
|
}
|
|
}
|
|
} // else if LowerCaseLine[LinePos] == ')'
|
|
else if (LowerCaseLine[LinePos] == '!'){
|
|
ShowError(CurLine,"'!' can only be placed directly in front of a command.");
|
|
LinePos++;
|
|
}
|
|
else if(LowerCaseLine.substr(LinePos,2) == "&&"){
|
|
if (InIfBrackets)ShowError(CurLine,"'&&' not allowed within brackets.");
|
|
AwaitingNextTestCommand = true;
|
|
LinePos+=2;
|
|
}
|
|
else if(LowerCaseLine.substr(LinePos,2) == "||"){
|
|
if (!InIfBrackets)
|
|
ShowError(CurLine,"Commands to be ORred together must be placed within brackets.");
|
|
AwaitingNextTestCommand = true;
|
|
LinePos+=2;
|
|
}
|
|
else{
|
|
if (InIfBrackets)
|
|
ShowError(CurLine,"Expected '||' or end of if statement.");
|
|
else
|
|
ShowError(CurLine,"Expected '&&' or end of if statement.");
|
|
}
|
|
}// if (not AwaitingNextTestCommand) and (LinePos < LineLength)
|
|
}//if InIf
|
|
SkipSpaces();
|
|
if (ErrorOccured)FinishedReading = true;
|
|
else if (LinePos >= LineLength)NextLine();
|
|
}while(!FinishedReading);
|
|
if (!LastCommandWasReturn)ShowError(CurLine,"return command expected.");
|
|
if(InIf){
|
|
if (AwaitingNextTestCommand){
|
|
if (NumCommandsInIfStatement == 0)ShowError(CurLine,"Expected test command.");
|
|
else ShowError(CurLine,"Expected another test command or end of if statement.");
|
|
}
|
|
else{
|
|
if(InIfBrackets)ShowError(CurLine,"Expected '||' or end of if statement.");
|
|
else ShowError(CurLine,"Expected '&&' or end of if statement.");
|
|
}
|
|
}
|
|
else if (BlockDepth > 0){
|
|
ShowError(CurLine,"'}' expected.");
|
|
}
|
|
for( CurGoto =1;CurGoto<=NumGotos;CurGoto++){
|
|
GotoData = Labels[Gotos[CurGoto].LabelNum].Loc - Gotos[CurGoto].DataLoc - 2;
|
|
WriteByteAtLoc((GotoData&0xff),Gotos[CurGoto].DataLoc);
|
|
WriteByteAtLoc((GotoData>>8)&0xff,Gotos[CurGoto].DataLoc+1);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
//***************************************************
|
|
int Logic::compile()
|
|
{
|
|
int ret,i,j;
|
|
|
|
sprintf(tmp,"%s/words.tok",game->dir.c_str());
|
|
ret = wordlist->read(tmp);
|
|
if(ret)return 1;
|
|
|
|
sprintf(tmp,"%s/object",game->dir.c_str());
|
|
ret = objlist->read(tmp,false);
|
|
if(ret)return 1;
|
|
|
|
objlist->ItemNames.toLower();
|
|
// words already in lower case in file so we don't need to convert them
|
|
for(i=0;i<objlist->ItemNames.num;i++){
|
|
if(objlist->ItemNames.at(i).find_first_of("\"")==string::npos)continue;
|
|
//replace " with \"
|
|
char *ptr=(char *)objlist->ItemNames.at(i).c_str();
|
|
for(j=0;*ptr;ptr++){
|
|
if(*ptr=='"'){
|
|
tmp[j++]='\\';
|
|
tmp[j++]='"';
|
|
}
|
|
else tmp[j++]=*ptr;
|
|
}
|
|
tmp[j]=0;
|
|
objlist->ItemNames.replace(i,tmp);
|
|
}
|
|
|
|
ResourceData.Size = MaxResourceSize;
|
|
LogicSize = 0;
|
|
ResPos = 2;
|
|
ErrorOccured = false;
|
|
NumDefines = 0;
|
|
ErrorList="";
|
|
|
|
if(RemoveComments(InputLines))return 1;
|
|
if(AddIncludes())return 1;
|
|
if(ReadDefines())return 1;
|
|
if(ReadPredefinedMessages())return 1;
|
|
if(ReadLabels())return 1;
|
|
if(CompileCommands())return 1;
|
|
|
|
WriteMessageSection();
|
|
|
|
EditLines.lfree();
|
|
|
|
if(ErrorOccured)return 1;
|
|
// printf("\n************* SUCCESS !!! ***********\n");
|
|
ResourceData.Size = LogicSize;
|
|
return 0;
|
|
|
|
}
|