From 56f3ff15d80af92cd26670f8693aeb64ff94a29a Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Fri, 24 Feb 2006 05:03:04 +0000 Subject: [PATCH] Initial commit for ACC. SVN r8 (trunk) --- Makefile | 144 ++ acc.c | 283 ++++ acc.dsp | 186 +++ acc.dsw | 33 + common.h | 110 ++ error.c | 418 ++++++ error.h | 159 ++ misc.c | 355 +++++ misc.h | 48 + parse.c | 4027 ++++++++++++++++++++++++++++++++++++++++++++++++++ parse.h | 41 + pcode.c | 1422 ++++++++++++++++++ pcode.h | 389 +++++ strlist.c | 428 ++++++ strlist.h | 38 + symbol.c | 470 ++++++ symbol.h | 127 ++ token.c | 1374 +++++++++++++++++ token.h | 159 ++ zcommon.acs | 15 + zdefs.acs | 466 ++++++ zspecial.acs | 187 +++ zwvars.acs | 8 + 23 files changed, 10887 insertions(+) create mode 100644 Makefile create mode 100644 acc.c create mode 100644 acc.dsp create mode 100644 acc.dsw create mode 100644 common.h create mode 100644 error.c create mode 100644 error.h create mode 100644 misc.c create mode 100644 misc.h create mode 100644 parse.c create mode 100644 parse.h create mode 100644 pcode.c create mode 100644 pcode.h create mode 100644 strlist.c create mode 100644 strlist.h create mode 100644 symbol.c create mode 100644 symbol.h create mode 100644 token.c create mode 100644 token.h create mode 100644 zcommon.acs create mode 100644 zdefs.acs create mode 100644 zspecial.acs create mode 100644 zwvars.acs diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..223d6dc --- /dev/null +++ b/Makefile @@ -0,0 +1,144 @@ +# get the target for the compiler +target = $(strip $(shell $(CC) -dumpmachine)) + +# DOS and Windows executables should have the .exe extension. +# Other operating systems should be extension-less. + +ifeq ($(findstring mingw32,$(target)),mingw32) +EXENAME = acc.exe +else +ifeq ($(findstring djgpp,$(target)),djgpp) +EXENAME = acc.exe +else +EXENAME = acc +endif +endif + +CFLAGS = -Os -Wall -W -march=pentium -mtune=athlon-4 -fomit-frame-pointer +LDFLAGS = -s +VERNUM = 136 + +OBJS = \ + acc.o \ + error.o \ + misc.o \ + parse.o \ + pcode.o \ + strlist.o \ + symbol.o \ + token.o + +SRCS = \ + acc.c \ + error.c \ + misc.c \ + parse.c \ + pcode.c \ + strlist.c \ + symbol.c \ + token.c \ + common.h \ + error.h \ + misc.h \ + parse.h \ + pcode.h \ + strlist.h \ + symbol.h \ + token.h \ + Makefile \ + acc.dsp \ + acc.dsw + +ACS = \ + zcommon.acs \ + zdefs.acs \ + zspecial.acs \ + zwvars.acs + +$(EXENAME) : $(OBJS) + $(CC) $(OBJS) -o $(EXENAME) $(LDFLAGS) + +acc.o: acc.c \ + common.h \ + error.h \ + misc.h \ + parse.h \ + pcode.h \ + strlist.h \ + symbol.h \ + token.h \ + + +error.o: error.c \ + common.h \ + error.h \ + misc.h \ + token.h \ + + +misc.o: misc.c \ + common.h \ + error.h \ + misc.h \ + + +parse.o: parse.c \ + common.h \ + error.h \ + misc.h \ + parse.h \ + pcode.h \ + strlist.h \ + symbol.h \ + token.h \ + + +pcode.o: pcode.c \ + common.h \ + error.h \ + misc.h \ + pcode.h \ + strlist.h \ + + +strlist.o: strlist.c \ + common.h \ + error.h \ + misc.h \ + pcode.h \ + strlist.h \ + + +symbol.o: symbol.c \ + common.h \ + error.h \ + misc.h \ + pcode.h \ + symbol.h \ + parse.h \ + + +token.o: token.c \ + common.h \ + error.h \ + misc.h \ + pcode.h \ + symbol.h \ + token.h \ + parse.h \ + + +clean: + rm -f $(OBJS) $(EXENAME) + +# These targets can only be made with MinGW's make and not DJGPP's, because +# they use Win32 tools. + +zipsrc: $(SRCS) + kzip /y acc$(VERNUM)-src.zip $(SRCS) "End User License ACC Source Code.doc" + +zipbin: $(EXENAME) $(ACS) + kzip /y acc$(VERNUM).zip $(EXENAME) $(ACS) + +zipwbin: Release/acc.exe $(ACS) + kzip /y acc$(VERNUM)win.zip Release/acc.exe $(ACS) diff --git a/acc.c b/acc.c new file mode 100644 index 0000000..099a414 --- /dev/null +++ b/acc.c @@ -0,0 +1,283 @@ + +//************************************************************************** +//** +//** acc.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include "common.h" +#include "token.h" +#include "error.h" +#include "symbol.h" +#include "misc.h" +#include "pcode.h" +#include "parse.h" +#include "strlist.h" + +// MACROS ------------------------------------------------------------------ + +#define VERSION_TEXT "1.36" +#define COPYRIGHT_YEARS_TEXT "1995" + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void Init(void); +static void DisplayBanner(void); +static void DisplayUsage(void); +static void OpenDebugFile(char *name); +static void ProcessArgs(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +boolean acs_BigEndianHost; +boolean acs_VerboseMode; +boolean acs_DebugMode; +FILE *acs_DebugFile; +char acs_SourceFileName[MAX_FILE_NAME_LENGTH]; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int ArgCount; +static char **ArgVector; +static char ObjectFileName[MAX_FILE_NAME_LENGTH]; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// main +// +//========================================================================== + +int main(int argc, char **argv) +{ + int i; + + ArgCount = argc; + ArgVector = argv; + DisplayBanner(); + Init(); + TK_OpenSource(acs_SourceFileName); + PC_OpenObject(ObjectFileName, DEFAULT_OBJECT_SIZE, 0); + PA_Parse(); + PC_CloseObject(); + TK_CloseSource(); + + MS_Message(MSG_NORMAL, "\n\"%s\":\n %d line%s (%d included)\n", + acs_SourceFileName, tk_Line, tk_Line == 1 ? "" : "s", + tk_IncludedLines); + MS_Message(MSG_NORMAL, " %d function%s\n %d script%s\n", + pc_FunctionCount, pc_FunctionCount == 1 ? "" : "s", + pa_ScriptCount, pa_ScriptCount == 1 ? "" : "s"); + for (i = 0; pa_TypedScriptCounts[i].TypeName; i++) + { + if (pa_TypedScriptCounts[i].TypeCount > 0) + { + MS_Message(MSG_NORMAL, "%5d %s\n", + pa_TypedScriptCounts[i].TypeCount, + pa_TypedScriptCounts[i].TypeName); + } + } + MS_Message(MSG_NORMAL, " %d global variable%s\n" + " %d world variable%s\n" + " %d map variable%s\n" + " %d global array%s\n" + " %d world array%s\n", + pa_GlobalVarCount, pa_GlobalVarCount == 1 ? "" : "s", + pa_WorldVarCount, pa_WorldVarCount == 1 ? "" : "s", + pa_MapVarCount, pa_MapVarCount == 1 ? "" : "s", + pa_GlobalArrayCount, pa_GlobalArrayCount == 1 ? "" : "s", + pa_WorldArrayCount, pa_WorldArrayCount == 1 ? "" : "s" + ); + MS_Message(MSG_NORMAL, " object \"%s\": %d bytes\n", + ObjectFileName, pc_Address); + ERR_RemoveErrorFile(); + return 0; +} + +//========================================================================== +// +// DisplayBanner +// +//========================================================================== + +static void DisplayBanner(void) +{ + fprintf(stderr, "\nOriginal ACC Version 1.10 by Ben Gokey\n"); + fprintf(stderr, "Copyright (c) "COPYRIGHT_YEARS_TEXT + " Raven Software, Corp.\n\n"); + fprintf(stderr, "This is version "VERSION_TEXT" ("__DATE__")\n"); + fprintf(stderr, "This software is not supported by Raven Software or Activision\n"); + fprintf(stderr, "ZDoom changes and language extensions by Randy Heit\n"); + fprintf(stderr, "Further changes by Brad Carney\n"); + fprintf(stderr, "Even more changes by James Bentler\n"); + fprintf(stderr, "Error reporting improvements and limit expansion by Ty Halderman\n"); +} + +//========================================================================== +// +// Init +// +//========================================================================== + +static void Init(void) +{ + short endianTest = 1; + + if (*(char *)&endianTest) + acs_BigEndianHost = NO; + else + acs_BigEndianHost = YES; + acs_VerboseMode = YES; + acs_DebugMode = NO; + acs_DebugFile = NULL; + TK_Init(); + SY_Init(); + STR_Init(); + ProcessArgs(); + MS_Message(MSG_NORMAL, "Host byte order: %s endian\n", + acs_BigEndianHost ? "BIG" : "LITTLE"); +} + +//========================================================================== +// +// ProcessArgs +// +//========================================================================== + +static void ProcessArgs(void) +{ + int i; + int count; + char *text; + char option; + + count = 0; + for(i = 1; i < ArgCount; i++) + { + text = ArgVector[i]; + if(*text == '-') + { + text++; + if(*text == 0) + { + DisplayUsage(); + } + option = toupper(*text++); + switch(option) + { + case 'D': + acs_DebugMode = YES; + acs_VerboseMode = YES; + if(*text != 0) + { + OpenDebugFile(text); + } + break; + case 'H': + pc_NoShrink = TRUE; + pc_HexenCase = TRUE; + break; + default: + DisplayUsage(); + break; + } + continue; + } + count++; + switch(count) + { + case 1: + strcpy(acs_SourceFileName, text); + MS_SuggestFileExt(acs_SourceFileName, ".acs"); + break; + case 2: + strcpy(ObjectFileName, text); + MS_SuggestFileExt(ObjectFileName, ".o"); + break; + default: + DisplayUsage(); + break; + } + } + if(count == 0) + { + DisplayUsage(); + } + if(count == 1) + { + strcpy(ObjectFileName, acs_SourceFileName); + MS_StripFileExt(ObjectFileName); + MS_SuggestFileExt(ObjectFileName, ".o"); + } +} + +//========================================================================== +// +// DisplayUsage +// +//========================================================================== + +static void DisplayUsage(void) +{ + puts("\nUsage: ACC [options] source[.acs] [object[.o]]\n"); + puts("-d[file] Output debugging information"); + puts("-h Create pcode compatible with Hexen and old ZDooms"); + exit(1); +} + +//========================================================================== +// +// OpenDebugFile +// +//========================================================================== + +static void OpenDebugFile(char *name) +{ + if((acs_DebugFile = fopen(name, "w")) == NULL) + { + ERR_Exit(ERR_CANT_OPEN_DBGFILE, NO, "File: \"%s\".", name); + } +} + +//========================================================================== +// +// OptionExists +// +//========================================================================== + +/* +static boolean OptionExists(char *name) +{ + int i; + char *arg; + + for(i = 1; i < ArgCount; i++) + { + arg = ArgVector[i]; + if(*arg == '-') + { + arg++; + if(MS_StrCmp(name, arg) == 0) + { + return YES; + } + } + } + return NO; +} +*/ diff --git a/acc.dsp b/acc.dsp new file mode 100644 index 0000000..a6e72c6 --- /dev/null +++ b/acc.dsp @@ -0,0 +1,186 @@ +# Microsoft Developer Studio Project File - Name="acc" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=acc - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "acc.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "acc.mak" CFG="acc - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "acc - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "acc - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "Perforce Project" +# PROP Scc_LocalPath "." +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "acc - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /opt:nowin98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "acc - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "acc - Win32 Release" +# Name "acc - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\Acc.c +# End Source File +# Begin Source File + +SOURCE=.\Error.c +# End Source File +# Begin Source File + +SOURCE=.\Misc.c +# End Source File +# Begin Source File + +SOURCE=.\Parse.c +# End Source File +# Begin Source File + +SOURCE=.\Pcode.c +# End Source File +# Begin Source File + +SOURCE=.\Strlist.c +# End Source File +# Begin Source File + +SOURCE=.\Symbol.c +# End Source File +# Begin Source File + +SOURCE=.\Token.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\Common.h +# End Source File +# Begin Source File + +SOURCE=.\Error.h +# End Source File +# Begin Source File + +SOURCE=.\Misc.h +# End Source File +# Begin Source File + +SOURCE=.\Parse.h +# End Source File +# Begin Source File + +SOURCE=.\Pcode.h +# End Source File +# Begin Source File + +SOURCE=.\Strlist.h +# End Source File +# Begin Source File + +SOURCE=.\Symbol.h +# End Source File +# Begin Source File + +SOURCE=.\Token.h +# End Source File +# End Group +# Begin Group "ACS Headers" + +# PROP Default_Filter "acs" +# Begin Source File + +SOURCE=.\zcommon.acs +# End Source File +# Begin Source File + +SOURCE=.\zdefs.acs +# End Source File +# Begin Source File + +SOURCE=.\zspecial.acs +# End Source File +# Begin Source File + +SOURCE=.\zwvars.acs +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# Begin Source File + +SOURCE=.\Makefile +# End Source File +# End Target +# End Project diff --git a/acc.dsw b/acc.dsw new file mode 100644 index 0000000..882eab5 --- /dev/null +++ b/acc.dsw @@ -0,0 +1,33 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "acc"=.\acc.dsp - Package Owner=<4> + +Package=<5> +{{{ + begin source code control + Perforce Project + . + end source code control +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/common.h b/common.h new file mode 100644 index 0000000..822e7b3 --- /dev/null +++ b/common.h @@ -0,0 +1,110 @@ + +//************************************************************************** +//** +//** common.h +//** +//************************************************************************** + +#ifndef __COMMON_H__ +#define __COMMON_H__ + +// HEADER FILES ------------------------------------------------------------ + +// MACROS ------------------------------------------------------------------ + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a))) +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef YES +#define YES 1 +#endif +#ifndef NO +#define NO 0 +#endif +// Increased limits - Ty 03jan2000 +// 32 is okay +#define MAX_IDENTIFIER_LENGTH 32 +// 256 long quoted string is okay +#define MAX_QUOTED_LENGTH 256 +// 512 max file name is okay in DOS/Win +#define MAX_FILE_NAME_LENGTH 512 +// Was 64 +#define MAX_SCRIPT_COUNT 1000 +// Was 32 +#define MAX_MAP_VARIABLES 128 +// Left alone--there's something in the docs about this... +// [RH] Bumped up to 20 for fun. +#define MAX_SCRIPT_VARIABLES 20 +// Was 64 +#define MAX_WORLD_VARIABLES 256 +// [RH] New +#define MAX_GLOBAL_VARIABLES 64 +// Was 128 +#define MAX_STRINGS 32768 +// Don't know what this is +#define DEFAULT_OBJECT_SIZE 65536 +// Added Ty 07Jan2000 for error details +#define MAX_STATEMENT_LENGTH 4096 + +#define MAX_LANGUAGES 256 + +#define MAX_FUNCTION_COUNT 256 + +#define MAX_IMPORTS 256 + +// Maximum number of translations that can be used +#define MAX_TRANSLATIONS 32 + +enum +{ + STRLIST_PICS, + STRLIST_FUNCTIONS, + STRLIST_MAPVARS, + + NUM_STRLISTS +}; + +// These are just defs and have not been messed with +#define ASCII_SPACE 32 +#define ASCII_QUOTE 34 +#define ASCII_UNDERSCORE 95 +#define EOF_CHARACTER 127 +#ifdef __NeXT__ +#define DIRECTORY_DELIMITER "/" +#define DIRECTORY_DELIMITER_CHAR ('/') +#else +#define DIRECTORY_DELIMITER "\\" +#define DIRECTORY_DELIMITER_CHAR ('\\') +#endif + + + +#define MAKE4CC(a,b,c,d) ((a)|((b)<<8)|((c)<<16)|((d)<<24)) + +// TYPES ------------------------------------------------------------------- + +typedef unsigned long boolean; +typedef unsigned char byte; +typedef signed char S_BYTE; +typedef unsigned char U_BYTE; +typedef signed short S_WORD; +typedef unsigned short U_WORD; +typedef signed long S_LONG; +typedef unsigned long U_LONG; + +enum ImportModes +{ + IMPORT_None, + IMPORT_Importing, + IMPORT_Exporting +}; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +#endif diff --git a/error.c b/error.c new file mode 100644 index 0000000..29efae0 --- /dev/null +++ b/error.c @@ -0,0 +1,418 @@ + +//************************************************************************** +//** +//** error.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include "common.h" +#include "error.h" +#include "token.h" +#include "misc.h" + +// MACROS ------------------------------------------------------------------ + +#define ERROR_FILE_NAME "acs.err" + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + ERRINFO_GCC, + ERRINFO_VCC +} errorInfo_e; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static char *ErrorText(error_t error); +static char *ErrorFileName(void); +static void eprintf(const char *fmt, ...); +static void veprintf(const char *fmt, va_list args); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern char acs_SourceFileName[MAX_FILE_NAME_LENGTH]; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static struct +{ + error_t number; + char *name; +} ErrorNames[] = +{ + { ERR_MISSING_SEMICOLON, "Missing semicolon." }, + { ERR_MISSING_LPAREN, "Missing '('." }, + { ERR_MISSING_RPAREN, "Missing ')'." }, + { ERR_MISSING_LBRACE, "Missing '{'." }, + { ERR_MISSING_SCRIPT_NUMBER, "Missing script number." }, + { ERR_IDENTIFIER_TOO_LONG, "Identifier too long." }, + { ERR_STRING_TOO_LONG, "String too long." }, + { ERR_FILE_NAME_TOO_LONG, "File name too long.\nFile: \"%s\"" }, + { ERR_BAD_CHARACTER, "Bad character in script text." }, + { ERR_BAD_CHARACTER_CONSTANT, "Bad character constant in script text." }, + { ERR_ALLOC_PCODE_BUFFER, "Failed to allocate PCODE buffer." }, + { ERR_PCODE_BUFFER_OVERFLOW, "PCODE buffer overflow." }, + { ERR_TOO_MANY_SCRIPTS, "Too many scripts." }, + { ERR_TOO_MANY_FUNCTIONS, "Too many functions." }, + { ERR_SAVE_OBJECT_FAILED, "Couldn't save object file." }, + { ERR_MISSING_LPAREN_SCR, "Missing '(' in script definition." }, + { ERR_INVALID_IDENTIFIER, "Invalid identifier." }, + { ERR_REDEFINED_IDENTIFIER, "%s : Redefined identifier." }, + { ERR_MISSING_COMMA, "Missing comma." }, + { ERR_BAD_VAR_TYPE, "Invalid variable type." }, + { ERR_BAD_RETURN_TYPE, "Invalid return type." }, + { ERR_TOO_MANY_SCRIPT_ARGS, "Too many script arguments." }, + { ERR_MISSING_LBRACE_SCR, "Missing opening '{' in script definition." }, + { ERR_MISSING_RBRACE_SCR, "Missing closing '}' in script definition." }, + { ERR_TOO_MANY_MAP_VARS, "Too many map variables." }, + { ERR_TOO_MANY_SCRIPT_VARS, "Too many script variables." }, + { ERR_TOO_MANY_FUNCTION_VARS, "Too many function variables." }, + { ERR_MISSING_WVAR_INDEX, "Missing index in world variable declaration." }, + { ERR_MISSING_GVAR_INDEX, "Missing index in global variable declaration." }, + { ERR_BAD_WVAR_INDEX, "World variable index out of range." }, + { ERR_MISSING_WVAR_COLON, "Missing colon in world variable declaration." }, + { ERR_MISSING_GVAR_COLON, "Missing colon in global variable declaration." }, + { ERR_MISSING_SPEC_VAL, "Missing value in special declaration." }, + { ERR_MISSING_SPEC_COLON, "Missing colon in special declaration." }, + { ERR_MISSING_SPEC_ARGC, "Missing argument count in special declaration." }, + { ERR_CANT_READ_FILE, "Couldn't read file.\nFile: \"%s\"" }, + { ERR_CANT_OPEN_FILE, "Couldn't open file.\nFile: \"%s\"" }, + { ERR_CANT_OPEN_DBGFILE, "Couldn't open debug file." }, + { ERR_INVALID_DIRECTIVE, "Invalid directive." }, + { ERR_BAD_DEFINE, "Non-numeric constant found in #define." }, + { ERR_INCL_NESTING_TOO_DEEP, "Include nesting too deep.\nUnable to include file \"%s\"." }, + { ERR_STRING_LIT_NOT_FOUND, "String literal not found." }, + { ERR_INVALID_DECLARATOR, "Invalid declarator." }, + { ERR_BAD_LSPEC_ARG_COUNT, "Incorrect number of special arguments." }, + { ERR_BAD_ARG_COUNT, "Incorrect number of arguments." }, + { ERR_UNKNOWN_IDENTIFIER, "%s : Identifier has not been declared." }, + { ERR_MISSING_COLON, "Missing colon." }, + { ERR_BAD_EXPR, "Syntax error in expression." }, + { ERR_BAD_CONST_EXPR, "Syntax error in constant expression." }, + { ERR_NO_DIRECT_VER, "Internal function has no direct version." }, + { ERR_ILLEGAL_EXPR_IDENT, "%s : Illegal identifier in expression." }, + { ERR_EXPR_FUNC_NO_RET_VAL, "Function call in expression has no return value." }, + { ERR_MISSING_ASSIGN_OP, "Missing assignment operator." }, + { ERR_INCDEC_OP_ON_NON_VAR, "'++' or '--' used on a non-variable." }, + { ERR_MISSING_RBRACE, "Missing '}' at end of compound statement." }, + { ERR_INVALID_STATEMENT, "Invalid statement." }, + { ERR_BAD_DO_STATEMENT, "Do statement not followed by 'while' or 'until'." }, + { ERR_BAD_SCRIPT_DECL, "Bad script declaration." }, + { ERR_CASE_OVERFLOW, "Internal Error: Case stack overflow." }, + { ERR_BREAK_OVERFLOW, "Internal Error: Break stack overflow." }, + { ERR_CONTINUE_OVERFLOW, "Internal Error: Continue stack overflow." }, + { ERR_STATEMENT_OVERFLOW, "Internal Error: Statement overflow." }, + { ERR_MISPLACED_BREAK, "Misplaced BREAK statement." }, + { ERR_MISPLACED_CONTINUE, "Misplaced CONTINUE statement." }, + { ERR_CASE_NOT_IN_SWITCH, "CASE must appear in switch statement." }, + { ERR_DEFAULT_NOT_IN_SWITCH, "DEFAULT must appear in switch statement." }, + { ERR_MULTIPLE_DEFAULT, "Only 1 DEFAULT per switch allowed." }, + { ERR_EXPR_STACK_OVERFLOW, "Expression stack overflow." }, + { ERR_EXPR_STACK_EMPTY, "Tried to POP empty expression stack." }, + { ERR_UNKNOWN_CONST_EXPR_PCD, "Unknown PCD in constant expression." }, + { ERR_BAD_RADIX_CONSTANT, "Radix out of range in integer constant." }, + { ERR_BAD_ASSIGNMENT, "Syntax error in multiple assignment statement." }, + { ERR_OUT_OF_MEMORY, "Out of memory." }, + { ERR_TOO_MANY_STRINGS, "Too many strings. Current max is %d" }, + { ERR_UNKNOWN_PRTYPE, "Unknown cast type in print statement." }, + { ERR_SCRIPT_OUT_OF_RANGE, "Script number must be between 1 and 999." }, + { ERR_MISSING_PARAM, "Missing required argument." }, + { ERR_SCRIPT_ALREADY_DEFINED, "Script already has a body." }, + { ERR_FUNCTION_ALREADY_DEFINED, "Function already has a body." }, + { ERR_PARM_MUST_BE_VAR, "Parameter must be a variable." }, + { ERR_LANGCODE_SIZE, "Language code must be 2 or 3 characters long." }, + { ERR_MISSING_LBRACE_LOC, "Missing opening '{' in localization definition." }, + { ERR_MISSING_RBRACE_LOC, "Missing closing '}' in localization definition." }, + { ERR_MISSING_LOCALIZED, "Missing localized string." }, + { ERR_BAD_LANGCODE, "Language code must be all letters." }, + { ERR_MISSING_LANGCODE, "Missing language code in localization definiton." }, + { ERR_MISSING_FONT_NAME, "Missing font name." }, + { ERR_MISSING_LBRACE_FONTS, "Missing opening '{' in font list." }, + { ERR_MISSING_RBRACE_FONTS, "Missing closing '}' in font list." }, + { ERR_NOCOMPACT_NOT_HERE, "#nocompact must appear before any scripts." }, + { ERR_MISSING_ASSIGN, "Missing '='." }, + { ERR_PREVIOUS_NOT_VOID, "Previous use of function expected a return value." }, + { ERR_MUST_RETURN_A_VALUE, "Function must return a value." }, + { ERR_MUST_NOT_RETURN_A_VALUE, "Void functions cannot return a value." }, + { ERR_SUSPEND_IN_FUNCTION, "Suspend cannot be used inside a function." }, + { ERR_TERMINATE_IN_FUNCTION, "Terminate cannot be used inside a function." }, + { ERR_RESTART_IN_FUNCTION, "Restart cannot be used inside a function." }, + { ERR_RETURN_OUTSIDE_FUNCTION, "Return can only be used inside a function." }, + { ERR_FUNC_ARGUMENT_COUNT, "Function %s should have %d argument%s." }, + { ERR_EOF, "Unexpected end of file." }, + { ERR_UNDEFINED_FUNC, "Function %s is used but not defined." }, + { ERR_TOO_MANY_ARRAY_DIMS, "Too many array dimensions." }, + { ERR_MISSING_LBRACKET, "Missing '['." }, + { ERR_MISSING_RBRACKET, "Missing ']'." }, + { ERR_ZERO_DIMENSION, "Arrays cannot have a dimension of zero." }, + { ERR_TOO_MANY_DIM_USED, "%s only has %d dimensions." }, + { ERR_ARRAY_MAPVAR_ONLY, "Only map variables can be arrays." }, + { ERR_NOT_AN_ARRAY, "%s is not an array." }, + { ERR_MISSING_LBRACE_ARR, "Missing opening '{' in array initializer." }, + { ERR_MISSING_RBRACE_ARR, "Missing closing '}' in array initializer." }, + { ERR_LATENT_IN_FUNC, "Latent functions cannot be used inside functions." }, + { ERR_LOCAL_VAR_SHADOWED, "A global identifier already has this name." }, + { ERR_MULTIPLE_IMPORTS, "You can only #import one file." }, + { ERR_IMPORT_IN_EXPORT, "You cannot #import from inside an imported file." }, + { ERR_EXPORTER_NOT_FLAGGED, "A file that you #import must have an #imported line." }, + { ERR_TOO_MANY_IMPORTS, "Too many files imported." }, + { ERR_NO_NEED_ARRAY_SIZE, "Only map arrays need a size." }, + { ERR_NO_MULTIDIMENSIONS, "Only map arrays can have more than one dimension." }, + { ERR_NEED_ARRAY_SIZE, "Missing array size." }, + { ERR_DISCONNECT_NEEDS_1_ARG, "Disconnect scripts must have 1 argument." }, + { ERR_UNCLOSED_WITH_ARGS, "Most special scripts must not have arguments." }, + { ERR_NOT_A_CHAR_ARRAY, "%s has %d dimensions. Use %d subscripts to get a char array." }, + { ERR_NONE, NULL } +}; + +static FILE *ErrorFile; +static int ErrorCount; +static errorInfo_e ErrorFormat; +static char *ErrorSourceName; +static int ErrorSourceLine; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// ERR_ErrorAt +// +//========================================================================== + +void ERR_ErrorAt(char *source, int line) +{ + ErrorSourceName = source; + ErrorSourceLine = line; +} + +//========================================================================== +// +// ERR_Error +// +//========================================================================== + +void ERR_Error(error_t error, boolean info, ...) +{ + va_list args; + va_start(args, info); + ERR_ErrorV(error, info, args); + va_end(args); +} + +//========================================================================== +// +// ERR_Exit +// +//========================================================================== + +void ERR_Exit(error_t error, boolean info, ...) +{ + va_list args; + va_start(args, info); + ERR_ErrorV(error, info, args); + va_end(args); + ERR_Finish(); +} + +//========================================================================== +// +// ERR_Finish +// +//========================================================================== + +void ERR_Finish(void) +{ + if(ErrorFile) + { + fclose(ErrorFile); + ErrorFile = NULL; + } + if(ErrorCount) + { + exit(1); + } +} + +//========================================================================== +// +// ShowError +// +//========================================================================== + +void ERR_ErrorV(error_t error, boolean info, va_list args) +{ + char *text; + boolean showLine = NO; + static boolean showedInfo = NO; + + if(!ErrorFile) + { + ErrorFile = fopen(ErrorFileName(), "w"); + } + if(ErrorCount == 0) + { + fprintf(stderr, "\n**** ERROR ****\n"); + } + else if(ErrorCount == 100) + { + eprintf("More than 100 errors. Can't continue.\n"); + ERR_Finish(); + } + ErrorCount++; + if(info == YES) + { + char *source; + int line; + + if(ErrorSourceName) + { + source = ErrorSourceName; + line = ErrorSourceLine; + ErrorSourceName = NULL; + } + else + { + source = tk_SourceName; + line = tk_Line; + showLine = YES; + } + if(showedInfo == NO) + { // Output info compatible with older ACCs + // for editors that expect it. + showedInfo = YES; + eprintf("Line %d in file \"%s\" ...\n", line, source); + } + if(ErrorFormat == ERRINFO_GCC) + { + eprintf("%s:%d: ", source, line); + } + else + { + eprintf("%s(%d) : ", source, line); + if(error != ERR_NONE) + { + eprintf("error %04d: ", error); + } + } + } + if(error != ERR_NONE) + { + text = ErrorText(error); + if(text != NULL) + { + veprintf(text, args); + } + eprintf("\n"); + if(showLine) + { + // deal with master source line and position indicator - Ty 07jan2000 + MasterSourceLine[MasterSourcePos] = '\0'; // pre-incremented already + eprintf("> %s\n", MasterSourceLine); // the string + eprintf(">%*s\n", MasterSourcePos, "^"); // pointer to error + } + } +#if 0 + else + { + va_list args2; + va_start(va_arg(args,char*), args2); + veprintf(va_arg(args,char*), args2); + va_end(args2); + } +#endif +} + +//========================================================================== +// +// ERR_RemoveErrorFile +// +//========================================================================== + +void ERR_RemoveErrorFile(void) +{ + remove(ErrorFileName()); +} + +//========================================================================== +// +// ERR_ErrorFileName +// +//========================================================================== + +static char *ErrorFileName(void) +{ + static char errFileName[MAX_FILE_NAME_LENGTH]; + + strcpy(errFileName, acs_SourceFileName); + if(MS_StripFilename(errFileName) == NO) + { + strcpy(errFileName, ERROR_FILE_NAME); + } + else + { + strcat(errFileName, DIRECTORY_DELIMITER ERROR_FILE_NAME); + } + return errFileName; +} + +//========================================================================== +// +// ErrorText +// +//========================================================================== + +static char *ErrorText(error_t error) +{ + int i; + + for(i = 0; ErrorNames[i].number != ERR_NONE; i++) + { + if(error == ErrorNames[i].number) + { + return ErrorNames[i].name; + } + } + return NULL; +} + +//========================================================================== +// +// eprintf +// +//========================================================================== + +static void eprintf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + veprintf(fmt, args); + va_end(args); +} + +//========================================================================== +// +// veprintf +// +//========================================================================== + +static void veprintf(const char *fmt, va_list args) +{ + vfprintf(stderr, fmt, args); + if(ErrorFile) + { + vfprintf(ErrorFile, fmt, args); + } +} diff --git a/error.h b/error.h new file mode 100644 index 0000000..cc44061 --- /dev/null +++ b/error.h @@ -0,0 +1,159 @@ + +//************************************************************************** +//** +//** error.h +//** +//************************************************************************** + +#ifndef __ERROR_H__ +#define __ERROR_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "common.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + ERR_NONE = 0, + ERR_NO_SYMBOL_MEM = 10, + ERR_IDENTIFIER_TOO_LONG, + ERR_STRING_TOO_LONG, + ERR_FILE_NAME_TOO_LONG, + ERR_MISSING_LPAREN, + ERR_MISSING_RPAREN, + ERR_MISSING_SEMICOLON, + ERR_MISSING_SCRIPT_NUMBER, + ERR_ALLOC_PCODE_BUFFER, + ERR_PCODE_BUFFER_OVERFLOW, + ERR_TOO_MANY_SCRIPTS, + ERR_TOO_MANY_FUNCTIONS, + ERR_SAVE_OBJECT_FAILED, + ERR_MISSING_LPAREN_SCR, + ERR_INVALID_IDENTIFIER, + ERR_REDEFINED_IDENTIFIER, + ERR_MISSING_COMMA, + ERR_BAD_VAR_TYPE, + ERR_BAD_RETURN_TYPE, + ERR_TOO_MANY_SCRIPT_ARGS, + ERR_MISSING_LBRACE_SCR, + ERR_MISSING_RBRACE_SCR, + ERR_TOO_MANY_MAP_VARS, + ERR_MISSING_WVAR_INDEX, + ERR_MISSING_GVAR_INDEX, + ERR_BAD_WVAR_INDEX, + ERR_MISSING_WVAR_COLON, + ERR_MISSING_GVAR_COLON, + ERR_MISSING_SPEC_VAL, + ERR_MISSING_SPEC_COLON, + ERR_MISSING_SPEC_ARGC, + ERR_CANT_READ_FILE, + ERR_CANT_OPEN_FILE, + ERR_CANT_OPEN_DBGFILE, + ERR_INVALID_DIRECTIVE, + ERR_BAD_DEFINE, + ERR_INCL_NESTING_TOO_DEEP, + ERR_STRING_LIT_NOT_FOUND, + ERR_TOO_MANY_SCRIPT_VARS, + ERR_TOO_MANY_FUNCTION_VARS, + ERR_INVALID_DECLARATOR, + ERR_BAD_LSPEC_ARG_COUNT, + ERR_BAD_ARG_COUNT, + ERR_UNKNOWN_IDENTIFIER, + ERR_MISSING_COLON, + ERR_BAD_EXPR, + ERR_BAD_CONST_EXPR, + ERR_NO_DIRECT_VER, + ERR_ILLEGAL_EXPR_IDENT, + ERR_EXPR_FUNC_NO_RET_VAL, + ERR_MISSING_ASSIGN_OP, + ERR_INCDEC_OP_ON_NON_VAR, + ERR_MISSING_RBRACE, + ERR_INVALID_STATEMENT, + ERR_BAD_DO_STATEMENT, + ERR_BAD_SCRIPT_DECL, + ERR_CASE_OVERFLOW, + ERR_BREAK_OVERFLOW, + ERR_CONTINUE_OVERFLOW, + ERR_STATEMENT_OVERFLOW, + ERR_MISPLACED_BREAK, + ERR_MISPLACED_CONTINUE, + ERR_CASE_NOT_IN_SWITCH, + ERR_DEFAULT_NOT_IN_SWITCH, + ERR_MULTIPLE_DEFAULT, + ERR_EXPR_STACK_OVERFLOW, + ERR_EXPR_STACK_EMPTY, + ERR_UNKNOWN_CONST_EXPR_PCD, + ERR_BAD_RADIX_CONSTANT, + ERR_BAD_ASSIGNMENT, + ERR_OUT_OF_MEMORY, + ERR_TOO_MANY_STRINGS, + ERR_UNKNOWN_PRTYPE, + ERR_BAD_CHARACTER, + ERR_SCRIPT_OUT_OF_RANGE, + ERR_MISSING_PARAM, + ERR_SCRIPT_ALREADY_DEFINED, + ERR_FUNCTION_ALREADY_DEFINED, + ERR_PARM_MUST_BE_VAR, + ERR_LANGCODE_SIZE, + ERR_MISSING_LBRACE_LOC, + ERR_MISSING_RBRACE_LOC, + ERR_MISSING_LOCALIZED, + ERR_BAD_LANGCODE, + ERR_MISSING_LANGCODE, + ERR_MISSING_FONT_NAME, + ERR_MISSING_LBRACE_FONTS, + ERR_MISSING_RBRACE_FONTS, + ERR_NOCOMPACT_NOT_HERE, + ERR_MISSING_ASSIGN, + ERR_MUST_RETURN_A_VALUE, + ERR_MUST_NOT_RETURN_A_VALUE, + ERR_SUSPEND_IN_FUNCTION, + ERR_TERMINATE_IN_FUNCTION, + ERR_RESTART_IN_FUNCTION, + ERR_RETURN_OUTSIDE_FUNCTION, + ERR_PREVIOUS_NOT_VOID, + ERR_MISSING_LBRACE, + ERR_FUNC_ARGUMENT_COUNT, + ERR_UNDEFINED_FUNC, + ERR_TOO_MANY_ARRAY_DIMS, + ERR_MISSING_LBRACKET, + ERR_MISSING_RBRACKET, + ERR_ZERO_DIMENSION, + ERR_TOO_MANY_DIM_USED, + ERR_EOF, + ERR_ARRAY_MAPVAR_ONLY, + ERR_NOT_AN_ARRAY, + ERR_MISSING_LBRACE_ARR, + ERR_MISSING_RBRACE_ARR, + ERR_LATENT_IN_FUNC, + ERR_LOCAL_VAR_SHADOWED, + ERR_BAD_CHARACTER_CONSTANT, + ERR_MULTIPLE_IMPORTS, + ERR_IMPORT_IN_EXPORT, + ERR_EXPORTER_NOT_FLAGGED, + ERR_TOO_MANY_IMPORTS, + ERR_NO_NEED_ARRAY_SIZE, + ERR_NO_MULTIDIMENSIONS, + ERR_NEED_ARRAY_SIZE, + ERR_DISCONNECT_NEEDS_1_ARG, + ERR_UNCLOSED_WITH_ARGS, + ERR_NOT_A_CHAR_ARRAY +} error_t; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void ERR_ErrorAt(char *sourceName, int sourceLine); +void ERR_Error(error_t error, boolean info, ...); +void ERR_ErrorV(error_t error, boolean info, va_list args); +void ERR_Finish(void); +void ERR_Exit(error_t error, boolean info, ...); +void ERR_RemoveErrorFile(void); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +#endif diff --git a/misc.c b/misc.c new file mode 100644 index 0000000..61008f6 --- /dev/null +++ b/misc.c @@ -0,0 +1,355 @@ + +//************************************************************************** +//** +//** misc.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#ifdef __NeXT__ +#include +#else +#include +#include +#ifndef unix +#include +#endif +#endif +#ifdef __GNUC__ +#include +#include +#endif +#ifdef _WIN32 +#include +#include +#endif +#include +#include +#include +#include +#include +#include "common.h" +#include "misc.h" +#include "error.h" + +// MACROS ------------------------------------------------------------------ + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +// TYPES ------------------------------------------------------------------- + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +extern boolean acs_BigEndianHost; +extern boolean acs_VerboseMode; +extern boolean acs_DebugMode; +extern FILE *acs_DebugFile; + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// MS_Alloc +// +//========================================================================== + +void *MS_Alloc(size_t size, error_t error) +{ + void *mem; + + if((mem = malloc(size)) == NULL) + { + ERR_Exit(error, NO); + } + return mem; +} + +//========================================================================== +// +// MS_Realloc +// +//========================================================================== + +void *MS_Realloc(void *base, size_t size, error_t error) +{ + void *mem; + + if((mem = realloc(base, size)) == NULL) + { + ERR_Exit(error, NO); + } + return mem; +} + +//========================================================================== +// +// MS_LittleUWORD +// +// Converts a host U_WORD (2 bytes) to little endian byte order. +// +//========================================================================== + +U_WORD MS_LittleUWORD(U_WORD val) +{ + if(acs_BigEndianHost == NO) + { + return val; + } + return ((val&255)<<8)+((val>>8)&255); +} + +//========================================================================== +// +// MS_LittleULONG +// +// Converts a host U_LONG (4 bytes) to little endian byte order. +// +//========================================================================== + +U_LONG MS_LittleULONG(U_LONG val) +{ + if(acs_BigEndianHost == NO) + { + return val; + } + return ((val&255)<<24)+(((val>>8)&255)<<16)+(((val>>16)&255)<<8) + +((val>>24)&255); +} + +//========================================================================== +// +// MS_LoadFile +// +//========================================================================== + +int MS_LoadFile(char *name, char **buffer) +{ + int handle; + int size; + int count; + char *addr; + struct stat fileInfo; + + if(strlen(name) >= MAX_FILE_NAME_LENGTH) + { + ERR_Exit(ERR_FILE_NAME_TOO_LONG, NO, name); + } + if((handle = open(name, O_RDONLY|O_BINARY, 0666)) == -1) + { + ERR_Exit(ERR_CANT_OPEN_FILE, NO, name); + } + if(fstat(handle, &fileInfo) == -1) + { + ERR_Exit(ERR_CANT_READ_FILE, NO, name); + } + size = fileInfo.st_size; + if((addr = malloc(size)) == NULL) + { + ERR_Exit(ERR_NONE, NO, "Couldn't malloc %d bytes for " + "file \"%s\".", size, name); + } + count = read(handle, addr, size); + close(handle); + if(count < size) + { + ERR_Exit(ERR_CANT_READ_FILE, NO, name); + } + *buffer = addr; + return size; +} + +//========================================================================== +// +// MS_SaveFile +// +//========================================================================== + +boolean MS_SaveFile(char *name, void *buffer, int length) +{ + int handle; + int count; + + handle = open(name, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0666); + if(handle == -1) + { + return FALSE; + } + count = write(handle, buffer, length); + close(handle); + if(count < length) + { + return FALSE; + } + return TRUE; +} + +//========================================================================== +// +// MS_StrCmp +// +//========================================================================== + +int MS_StrCmp(char *s1, char *s2) +{ + for(; tolower(*s1) == tolower(*s2); s1++, s2++) + { + if(*s1 == '\0') + { + return 0; + } + } + return tolower(*s1)-tolower(*s2); +} + +//========================================================================== +// +// MS_StrLwr +// +//========================================================================== + +char *MS_StrLwr(char *string) +{ + char *c; + + c = string; + while(*c) + { + *c = tolower(*c); + c++; + } + return string; +} + +//========================================================================== +// +// MS_StrUpr +// +//========================================================================== + +char *MS_StrUpr(char *string) +{ + char *c; + + c = string; + while(*c) + { + *c = toupper(*c); + c++; + } + return string; +} + +//========================================================================== +// +// MS_SuggestFileExt +// +//========================================================================== + +void MS_SuggestFileExt(char *base, char *extension) +{ + char *search; + + search = base+strlen(base)-1; + while(*search != ASCII_SLASH && *search != ASCII_BACKSLASH + && search != base) + { + if(*search-- == '.') + { + return; + } + } + strcat(base, extension); +} + +//========================================================================== +// +// MS_StripFileExt +// +//========================================================================== + +void MS_StripFileExt(char *name) +{ + char *search; + + search = name+strlen(name)-1; + while(*search != ASCII_SLASH && *search != ASCII_BACKSLASH + && search != name) + { + if(*search == '.') + { + *search = '\0'; + return; + } + search--; + } +} + +//========================================================================== +// +// MS_StripFilename +// +//========================================================================== + +boolean MS_StripFilename(char *name) +{ + char *c; + + c = name+strlen(name); + do + { + if(--c == name) + { // No directory delimiter + return NO; + } + } while(*c != DIRECTORY_DELIMITER_CHAR); + *c = 0; + return YES; +} + +//========================================================================== +// +// MS_Message +// +//========================================================================== + +void MS_Message(msg_t type, char *text, ...) +{ + FILE *fp; + va_list argPtr; + + if(type == MSG_VERBOSE && acs_VerboseMode == NO) + { + return; + } + fp = stdout; + if(type == MSG_DEBUG) + { + if(acs_DebugMode == NO) + { + return; + } + if(acs_DebugFile != NULL) + { + fp = acs_DebugFile; + } + } + if(text) + { + va_start(argPtr, text); + vfprintf(fp, text, argPtr); + va_end(argPtr); + } +} diff --git a/misc.h b/misc.h new file mode 100644 index 0000000..17312da --- /dev/null +++ b/misc.h @@ -0,0 +1,48 @@ + +//************************************************************************** +//** +//** misc.h +//** +//************************************************************************** + +#ifndef __MISC_H__ +#define __MISC_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "error.h" + +// MACROS ------------------------------------------------------------------ + +#define ASCII_SLASH 47 +#define ASCII_BACKSLASH 92 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + MSG_NORMAL, + MSG_VERBOSE, + MSG_DEBUG +} msg_t; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void *MS_Alloc(size_t size, error_t error); +void *MS_Realloc(void *base, size_t size, error_t error); +U_WORD MS_LittleUWORD(U_WORD val); +U_LONG MS_LittleULONG(U_LONG val); +int MS_LoadFile(char *name, char **buffer); +boolean MS_SaveFile(char *name, void *buffer, int length); +int MS_StrCmp(char *s1, char *s2); +char *MS_StrLwr(char *string); +char *MS_StrUpr(char *string); +void MS_SuggestFileExt(char *base, char *extension); +void MS_StripFileExt(char *name); +boolean MS_StripFilename(char *path); +void MS_Message(msg_t type, char *text, ...); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +#endif diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..adf7408 --- /dev/null +++ b/parse.c @@ -0,0 +1,4027 @@ + +//************************************************************************** +//** +//** parse.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "parse.h" +#include "symbol.h" +#include "pcode.h" +#include "token.h" +#include "error.h" +#include "misc.h" +#include "strlist.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_STATEMENT_DEPTH 128 +#define MAX_BREAK 128 +#define MAX_CONTINUE 128 +#define MAX_CASE 128 +#define EXPR_STACK_DEPTH 64 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + STMT_SCRIPT, + STMT_IF, + STMT_ELSE, + STMT_DO, + STMT_WHILEUNTIL, + STMT_SWITCH, + STMT_FOR +} statement_t; + +typedef struct +{ + int level; + int addressPtr; +} breakInfo_t; + +typedef struct +{ + int level; + int addressPtr; +} continueInfo_t; + +typedef struct +{ + int level; + int value; + boolean isDefault; + int address; +} caseInfo_t; + +typedef struct prefunc_s +{ + struct prefunc_s *next; + symbolNode_t *sym; + int address; + int argcount; + int line; + char *source; +} prefunc_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void CountScript(int type); +static void Outside(void); +static void OuterScript(void); +static void OuterFunction(void); +static void OuterMapVar(boolean local); +static void OuterWorldVar(boolean isGlobal); +static void OuterSpecialDef(void); +static void OuterDefine(boolean force); +static void OuterInclude(void); +static void OuterImport(void); +static boolean ProcessStatement(statement_t owner); +static void LeadingCompoundStatement(statement_t owner); +static void LeadingVarDeclare(void); +static void LeadingLineSpecial(boolean executewait); +static void LeadingIdentifier(void); +static void BuildPrintString(void); +static void PrintCharArray(void); +static void LeadingPrint(void); +static void LeadingHudMessage(void); +static void LeadingVarAssign(symbolNode_t *sym); +static pcd_t GetAssignPCD(tokenType_t token, symbolType_t symbol); +static void LeadingInternFunc(symbolNode_t *sym); +static void LeadingScriptFunc(symbolNode_t *sym); +static void LeadingSuspend(void); +static void LeadingTerminate(void); +static void LeadingRestart(void); +static void LeadingReturn(void); +static void LeadingIf(void); +static void LeadingFor(void); +static void LeadingWhileUntil(void); +static void LeadingDo(void); +static void LeadingSwitch(void); +static void LeadingCase(void); +static void LeadingDefault(void); +static void LeadingBreak(void); +static void LeadingContinue(void); +static void LeadingCreateTranslation(void); +static void LeadingIncDec(int token); +static void PushCase(int value, boolean isDefault); +static caseInfo_t *GetCaseInfo(void); +static int CaseInfoCmp(const void *a, const void *b); +static boolean DefaultInCurrent(void); +static void PushBreak(void); +static void WriteBreaks(void); +static boolean BreakAncestor(void); +static void PushContinue(void); +static void WriteContinues(int address); +static boolean ContinueAncestor(void); +static void ProcessInternFunc(symbolNode_t *sym); +static void ProcessScriptFunc(symbolNode_t *sym, boolean discardReturn); +static void EvalExpression(void); +static void ExprLevX(int level); +static void ExprLevA(void); +static void ExprFactor(void); +static void ConstExprFactor(void); +static void SendExprCommand(pcd_t pcd); +static void PushExStk(int value); +static int PopExStk(void); +static pcd_t TokenToPCD(tokenType_t token); +static pcd_t GetPushVarPCD(symbolType_t symType); +static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol); +static int EvalConstExpression(void); +static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices); +static void InitializeArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size); +static symbolNode_t *DemandSymbol(char *name); +static symbolNode_t *SpeculateSymbol(char *name, boolean hasReturn); +static symbolNode_t *SpeculateFunction(const char *name, boolean hasReturn); +static void UnspeculateFunction(symbolNode_t *sym); +static void AddScriptFuncRef(symbolNode_t *sym, int address, int argcount); +static void CheckForUndefinedFunctions(void); +static void SkipBraceBlock(int depth); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int pa_ScriptCount; +struct ScriptTypes *pa_TypedScriptCounts; +int pa_MapVarCount; +int pa_WorldVarCount; +int pa_GlobalVarCount; +int pa_WorldArrayCount; +int pa_GlobalArrayCount; +enum ImportModes ImportMode = IMPORT_None; +boolean ExporterFlagged; +boolean pa_ConstExprIsString; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static int ScriptVarCount; +static statement_t StatementHistory[MAX_STATEMENT_DEPTH]; +static int StatementIndex; +static breakInfo_t BreakInfo[MAX_BREAK]; +static int BreakIndex; +static continueInfo_t ContinueInfo[MAX_CONTINUE]; +static int ContinueIndex; +static caseInfo_t CaseInfo[MAX_CASE]; +static int CaseIndex; +static int StatementLevel; +static int ExprStack[EXPR_STACK_DEPTH]; +static int ExprStackIndex; +static boolean ConstantExpression; +static symbolNode_t *InsideFunction; +static prefunc_t *FillinFunctions; +static prefunc_t **FillinFunctionsLatest = &FillinFunctions; +static boolean ArrayHasStrings; + +static int AdjustStmtLevel[] = +{ + 0, // STMT_SCRIPT + 0, // STMT_IF + 0, // STMT_ELSE + 1, // STMT_DO + 1, // STMT_WHILEUNTIL + 1, // STMT_SWITCH + 1 // STMT_FOR +}; + +static boolean IsBreakRoot[] = +{ + NO, // STMT_SCRIPT + NO, // STMT_IF + NO, // STMT_ELSE + YES, // STMT_DO + YES, // STMT_WHILEUNTIL + YES, // STMT_SWITCH + YES // STMT_FOR +}; + +static boolean IsContinueRoot[] = +{ + NO, // STMT_SCRIPT + NO, // STMT_IF + NO, // STMT_ELSE + YES, // STMT_DO + YES, // STMT_WHILEUNTIL + NO, // STMT_SWITCH + YES // STMT_FOR +}; + +static tokenType_t LevAOps[] = +{ + TK_ORLOGICAL, + TK_NONE +}; + +static tokenType_t LevBOps[] = +{ + TK_ANDLOGICAL, + TK_NONE +}; + +static tokenType_t LevCOps[] = +{ + TK_ORBITWISE, + TK_NONE +}; + +static tokenType_t LevDOps[] = +{ + TK_EORBITWISE, + TK_NONE +}; + +static tokenType_t LevEOps[] = +{ + TK_ANDBITWISE, + TK_NONE +}; + +static tokenType_t LevFOps[] = +{ + TK_EQ, + TK_NE, + TK_NONE +}; + +static tokenType_t LevGOps[] = +{ + TK_LT, + TK_LE, + TK_GT, + TK_GE, + TK_NONE +}; + +static tokenType_t LevHOps[] = +{ + TK_LSHIFT, + TK_RSHIFT, + TK_NONE +}; + +static tokenType_t LevIOps[] = +{ + TK_PLUS, + TK_MINUS, + TK_NONE +}; + +static tokenType_t LevJOps[] = +{ + TK_ASTERISK, + TK_SLASH, + TK_PERCENT, + TK_NONE +}; + +static tokenType_t *OpsList[] = +{ + LevAOps, + LevBOps, + LevCOps, + LevDOps, + LevEOps, + LevFOps, + LevGOps, + LevHOps, + LevIOps, + LevJOps, + NULL +}; + +static tokenType_t AssignOps[] = +{ + TK_ASSIGN, + TK_ADDASSIGN, + TK_SUBASSIGN, + TK_MULASSIGN, + TK_DIVASSIGN, + TK_MODASSIGN, + TK_NONE +}; + +static struct ScriptTypes ScriptCounts[] = +{ + { "closed", 0, 0 }, + { "open", OPEN_SCRIPTS_BASE, 0 }, + { "respawn", RESPAWN_SCRIPTS_BASE, 0 }, + { "death", DEATH_SCRIPTS_BASE, 0 }, + { "enter", ENTER_SCRIPTS_BASE, 0 }, + { "pickup", PICKUP_SCRIPTS_BASE, 0 }, + { "bluereturn", BLUE_RETURN_SCRIPTS_BASE, 0 }, + { "redreturn", RED_RETURN_SCRIPTS_BASE, 0 }, + { "whitereturn", WHITE_RETURN_SCRIPTS_BASE, 0 }, + { "lightning", LIGHTNING_SCRIPTS_BASE, 0 }, + { "disconnect", DISCONNECT_SCRIPTS_BASE, 0 }, + { "unloading", UNLOADING_SCRIPTS_BASE, 0 }, + { NULL, -1, 0 } +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// PA_Parse +// +//========================================================================== + +void PA_Parse(void) +{ + int i; + + pa_ScriptCount = 0; + pa_TypedScriptCounts = ScriptCounts; + for (i = 0; ScriptCounts[i].TypeName != NULL; i++) + { + ScriptCounts[i].TypeCount = 0; + } + pa_MapVarCount = 0; + pa_WorldVarCount = 0; + pa_GlobalVarCount = 0; + pa_WorldArrayCount = 0; + pa_GlobalArrayCount = 0; + TK_NextToken(); + Outside(); + CheckForUndefinedFunctions(); + ERR_Finish(); +} + +//========================================================================== +// +// CountScript +// +//========================================================================== + +static void CountScript(int type) +{ + int i; + + for (i = 0; ScriptCounts[i].TypeName != NULL; i++) + { + if (ScriptCounts[i].TypeBase == type) + { + if (type != 0) + { + MS_Message(MSG_DEBUG, "Script type: %s\n", + ScriptCounts[i].TypeName); + } + ScriptCounts[i].TypeCount++; + return; + } + } + return; +} + +//========================================================================== +// +// Outside +// +//========================================================================== + +static void Outside(void) +{ + boolean done; + + done = NO; + while(done == NO) + { + switch(tk_Token) + { + case TK_EOF: + + done = YES; + break; + case TK_SCRIPT: + OuterScript(); + break; + case TK_FUNCTION: + OuterFunction(); + break; + case TK_INT: + case TK_STR: + case TK_BOOL: + OuterMapVar(NO); + break; + case TK_WORLD: + OuterWorldVar(NO); + break; + case TK_GLOBAL: + OuterWorldVar(YES); + break; + case TK_SPECIAL: + OuterSpecialDef(); + break; + case TK_NUMBERSIGN: + TK_NextToken(); + switch(tk_Token) + { + case TK_DEFINE: + OuterDefine(NO); + break; + case TK_LIBDEFINE: + OuterDefine(YES); + break; + case TK_INCLUDE: + OuterInclude(); + break; + case TK_NOCOMPACT: + if(ImportMode != IMPORT_Importing) + { + if(pc_Address != 8) + { + ERR_Error(ERR_NOCOMPACT_NOT_HERE, YES); + } + MS_Message(MSG_DEBUG, "Forcing NoShrink\n"); + pc_NoShrink = TRUE; + } + TK_NextToken(); + break; + case TK_WADAUTHOR: + if(ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "Will write WadAuthor-compatible object\n"); + MS_Message(MSG_NORMAL, "You don't need to use #wadauthor anymore.\n"); + pc_WadAuthor = TRUE; + } + TK_NextToken(); + break; + case TK_NOWADAUTHOR: + if(ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "Will write WadAuthor-incompatible object\n"); + pc_WadAuthor = FALSE; + } + TK_NextToken(); + break; + case TK_ENCRYPTSTRINGS: + if(ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "Strings will be encrypted\n"); + pc_EncryptStrings = TRUE; + } + TK_NextToken(); + break; + case TK_IMPORT: + OuterImport(); + break; + case TK_LIBRARY: + TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND); + if(ImportMode == IMPORT_None) + { + MS_Message(MSG_DEBUG, "Allocations modified for exporting\n"); + ImportMode = IMPORT_Exporting; + } + else if(ImportMode == IMPORT_Importing) + { + PC_AddImport(tk_String); + ExporterFlagged = YES; + } + TK_NextToken(); + break; + default: + ERR_Error(ERR_INVALID_DIRECTIVE, YES); + TK_SkipLine(); + break; + } + break; + default: + ERR_Exit(ERR_INVALID_DECLARATOR, YES, NULL); + break; + } + } +} + +//========================================================================== +// +// OuterScript +// +//========================================================================== + +static void OuterScript(void) +{ + int scriptNumber; + symbolNode_t *sym; + int scriptType; + + MS_Message(MSG_DEBUG, "---- OuterScript ----\n"); + BreakIndex = 0; + CaseIndex = 0; + StatementLevel = 0; + ScriptVarCount = 0; + SY_FreeLocals(); + TK_NextToken(); + + if(ImportMode == IMPORT_Importing) + { + // When importing, the script number is not recorded, because + // it might be a #define that is not included by the main .acs + // file, so processing it would generate a syntax error. + SkipBraceBlock(0); + TK_NextToken(); + return; + } + + // [RH] If you want to use script 0, it must be written as <<0>>. + // This is to avoid using it accidentally, since ZDoom uses script + // 0 to implement many of the Strife-specific line specials. + + if(tk_Token == TK_LSHIFT) + { + TK_NextTokenMustBe(TK_NUMBER, ERR_SCRIPT_OUT_OF_RANGE); + if(tk_Number != 0) + { + ERR_Exit(ERR_SCRIPT_OUT_OF_RANGE, YES, NULL); + } + TK_NextTokenMustBe(TK_RSHIFT, ERR_SCRIPT_OUT_OF_RANGE); + TK_NextToken(); + scriptNumber = 0; + } + else + { + scriptNumber = EvalConstExpression(); + if(scriptNumber < 1 || scriptNumber > 999) + { + TK_Undo(); + ERR_Error(ERR_SCRIPT_OUT_OF_RANGE, YES, NULL); + SkipBraceBlock(0); + TK_NextToken(); + return; + } + } + MS_Message(MSG_DEBUG, "Script number: %d\n", scriptNumber); + scriptType = 0; + if(tk_Token == TK_LPAREN) + { + if(TK_NextToken() == TK_VOID) + { + TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + } + else + { + TK_Undo(); + do + { + TK_NextTokenMustBe(TK_INT, ERR_BAD_VAR_TYPE); + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + if(ScriptVarCount == 3) + { + ERR_Error(ERR_TOO_MANY_SCRIPT_ARGS, YES); + } + if(SY_FindLocal(tk_String) != NULL) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + } + else if(ScriptVarCount < 3) + { + sym = SY_InsertLocal(tk_String, SY_SCRIPTVAR); + sym->info.var.index = ScriptVarCount; + ScriptVarCount++; + } + TK_NextToken(); + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + } + TK_NextToken(); + switch(tk_Token) + { + case TK_DISCONNECT: + scriptType = DISCONNECT_SCRIPTS_BASE; + if(ScriptVarCount != 1) + { + ERR_Error(ERR_DISCONNECT_NEEDS_1_ARG, YES); + } + break; + + case TK_OPEN: + case TK_RESPAWN: + case TK_DEATH: + case TK_ENTER: + case TK_PICKUP: + case TK_BLUERETURN: + case TK_REDRETURN: + case TK_WHITERETURN: + case TK_LIGHTNING: + case TK_UNLOADING: + ERR_Error(ERR_UNCLOSED_WITH_ARGS, YES); + break; + + default: + TK_Undo(); + } + MS_Message(MSG_DEBUG, "Script type: %s (%d %s)\n", + scriptType == 0 ? "closed" : "disconnect", + ScriptVarCount, ScriptVarCount == 1 ? "arg" : "args"); + } + else switch (tk_Token) + { + case TK_OPEN: + scriptType = OPEN_SCRIPTS_BASE; + break; + + case TK_RESPAWN: // [BC] + scriptType = RESPAWN_SCRIPTS_BASE; + break; + + case TK_DEATH: // [BC] + scriptType = DEATH_SCRIPTS_BASE; + break; + + case TK_ENTER: // [BC] + scriptType = ENTER_SCRIPTS_BASE; + break; + + case TK_PICKUP: // [BC] + scriptType = PICKUP_SCRIPTS_BASE; + break; + + case TK_BLUERETURN: // [BC] + scriptType = BLUE_RETURN_SCRIPTS_BASE; + break; + + case TK_REDRETURN: // [BC] + scriptType = RED_RETURN_SCRIPTS_BASE; + break; + + case TK_WHITERETURN: // [BC] + scriptType = WHITE_RETURN_SCRIPTS_BASE; + break; + + case TK_LIGHTNING: + scriptType = LIGHTNING_SCRIPTS_BASE; + break; + + case TK_UNLOADING: + scriptType = UNLOADING_SCRIPTS_BASE; + break; + + case TK_DISCONNECT: + scriptType = DISCONNECT_SCRIPTS_BASE; + ERR_Error (ERR_DISCONNECT_NEEDS_1_ARG, YES); + break; + + default: + ERR_Error(ERR_BAD_SCRIPT_DECL, YES); + SkipBraceBlock(0); + TK_NextToken(); + return; + } + TK_NextToken(); + if(tk_Token == TK_NET) + { + scriptNumber += NET_SCRIPT_FLAG; + TK_NextToken(); + } + CountScript(scriptType); + PC_AddScript(scriptNumber + scriptType, ScriptVarCount); + pc_LastAppendedCommand = PCD_NOP; + if(ProcessStatement(STMT_SCRIPT) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + if(pc_LastAppendedCommand != PCD_TERMINATE) + { + PC_AppendCmd(PCD_TERMINATE); + } + PC_SetScriptVarCount(scriptNumber + scriptType, ScriptVarCount); + pa_ScriptCount++; +} + +//========================================================================== +// +// OuterFunction +// +//========================================================================== + +static void OuterFunction(void) +{ + enum ImportModes importing; + boolean hasReturn; + symbolNode_t *sym; + int defLine; + + MS_Message(MSG_DEBUG, "---- OuterFunction ----\n"); + importing = ImportMode; + BreakIndex = 0; + CaseIndex = 0; + StatementLevel = 0; + ScriptVarCount = 0; + SY_FreeLocals(); + TK_NextToken(); + if(tk_Token != TK_STR && tk_Token != TK_INT && + tk_Token != TK_VOID && tk_Token != TK_BOOL) + { + ERR_Error(ERR_BAD_RETURN_TYPE, YES); + tk_Token = TK_VOID; + } + hasReturn = tk_Token != TK_VOID; + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + sym = SY_FindGlobal(tk_String); + if(sym != NULL) + { + if(sym->type != SY_SCRIPTFUNC) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + SkipBraceBlock(0); + TK_NextToken(); + return; + } + if(!sym->info.scriptFunc.predefined) + { + ERR_Error(ERR_FUNCTION_ALREADY_DEFINED, YES); + ERR_ErrorAt(sym->info.scriptFunc.sourceName, sym->info.scriptFunc.sourceLine); + ERR_Error(ERR_NONE, YES, "Previous definition was here."); + SkipBraceBlock(0); + TK_NextToken(); + return; + } + if(sym->info.scriptFunc.hasReturnValue && !hasReturn) + { + ERR_Error(ERR_PREVIOUS_NOT_VOID, YES); + ERR_ErrorAt(sym->info.scriptFunc.sourceName, sym->info.scriptFunc.sourceLine); + ERR_Error(ERR_NONE, YES, "Previous use was here."); + } + } + else + { + sym = SY_InsertGlobal(tk_String, SY_SCRIPTFUNC); + sym->info.scriptFunc.address = (importing == IMPORT_Importing ? 0 : pc_Address); + sym->info.scriptFunc.predefined = NO; + } + defLine = tk_Line; + + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + if(TK_NextToken() == TK_VOID) + { + TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + } + else + { + TK_Undo(); + do + { + symbolType_t type; + symbolNode_t *local; + + TK_NextToken(); +/* if(tk_Token != TK_INT && tk_Token != TK_STR && tk_Token != TK_BOOL) + { + ERR_Error(ERR_BAD_VAR_TYPE, YES); + tk_Token = TK_INT; + } +*/ if(tk_Token == TK_INT || tk_Token == TK_BOOL) + { + if(TK_NextToken() == TK_LBRACKET) + { + TK_NextTokenMustBe(TK_RBRACKET, ERR_BAD_VAR_TYPE); + type = SY_SCRIPTALIAS; + } + else + { + TK_Undo(); + type = SY_SCRIPTVAR; + } + } + else if(tk_Token == TK_STR) + { + type = SY_SCRIPTVAR; + } + else + { + type = SY_SCRIPTVAR; + ERR_Error(ERR_BAD_VAR_TYPE, YES); + tk_Token = TK_INT; + } + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + if(SY_FindLocal(tk_String) != NULL) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + } + else + { + local = SY_InsertLocal(tk_String, type); + local->info.var.index = ScriptVarCount; + ScriptVarCount++; + } + TK_NextToken(); + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + } + + sym->info.scriptFunc.sourceLine = defLine; + sym->info.scriptFunc.sourceName = tk_SourceName; + sym->info.scriptFunc.argCount = ScriptVarCount; + sym->info.scriptFunc.address = (importing == IMPORT_Importing) ? 0 : pc_Address; + sym->info.scriptFunc.hasReturnValue = hasReturn; + + if(importing == IMPORT_Importing) + { + SkipBraceBlock(0); + TK_NextToken(); + sym->info.scriptFunc.predefined = NO; + sym->info.scriptFunc.varCount = ScriptVarCount; + return; + } + + TK_NextToken(); + InsideFunction = sym; + pc_LastAppendedCommand = PCD_NOP; + + // If we just call ProcessStatement(STMT_SCRIPT), and this function + // needs to return a value but the last pcode output was not a return, + // then the line number given in the error can be confusing because it + // is beyond the end of the function. To avoid this, we process the + // compound statement ourself and check if it returned something + // before checking for the '}'. If a return is required, then the error + // line will be shown as the one that contains the '}' (if present). + + TK_TokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE); + TK_NextToken(); + do ; while(ProcessStatement(STMT_SCRIPT) == YES); + + if(pc_LastAppendedCommand != PCD_RETURNVOID && + pc_LastAppendedCommand != PCD_RETURNVAL) + { + if(hasReturn) + { + TK_Undo(); + ERR_Error(ERR_MUST_RETURN_A_VALUE, YES, NULL); + } + PC_AppendCmd(PCD_RETURNVOID); + } + + TK_TokenMustBe(TK_RBRACE, ERR_INVALID_STATEMENT); + TK_NextToken(); + + sym->info.scriptFunc.predefined = NO; + sym->info.scriptFunc.varCount = ScriptVarCount - + sym->info.scriptFunc.argCount; + PC_AddFunction(sym); + UnspeculateFunction(sym); + InsideFunction = NULL; +} + +//========================================================================== +// +// OuterMapVar +// +//========================================================================== + +static void OuterMapVar(boolean local) +{ + symbolNode_t *sym = NULL; + int index; + + MS_Message(MSG_DEBUG, "---- %s ----\n", local ? "LeadingStaticVarDeclare" : "OuterMapVar"); + do + { + if(pa_MapVarCount >= MAX_MAP_VARIABLES) + { + ERR_Error(ERR_TOO_MANY_MAP_VARS, YES); + index = MAX_MAP_VARIABLES; + } + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + sym = local ? SY_FindLocal(tk_String) + : SY_FindGlobal(tk_String); + if(sym != NULL) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + index = MAX_MAP_VARIABLES; + } + else + { + sym = local ? SY_InsertLocal(tk_String, SY_MAPVAR) + : SY_InsertGlobal(tk_String, SY_MAPVAR); + if(ImportMode == IMPORT_Importing) + { + sym->info.var.index = index = 0; + } + else + { + sym->info.var.index = index = pa_MapVarCount; + if (!local) + { // Local variables are not exported + PC_NameMapVariable(index, sym); + } + pa_MapVarCount++; + } + } + TK_NextToken(); + if(tk_Token == TK_ASSIGN) + { + if(ImportMode != IMPORT_Importing) + { + TK_NextToken(); + PC_PutMapVariable (index, EvalConstExpression()); + } + else + { + // When importing, skip the initializer, because we don't care. + do + { + TK_NextToken(); + } while(tk_Token != TK_COMMA && tk_Token != TK_SEMICOLON); + } + } + else if(tk_Token == TK_LBRACKET) + { + int size = 0; + int ndim = 0; + int dims[MAX_ARRAY_DIMS]; + + memset(dims, 0, sizeof(dims)); + + while(tk_Token == TK_LBRACKET) + { + if(ndim == MAX_ARRAY_DIMS) + { + ERR_Error(ERR_TOO_MANY_ARRAY_DIMS, YES); + do + { + TK_NextToken(); + } while(tk_Token != TK_COMMA && tk_Token != TK_SEMICOLON); + break; + } + TK_NextToken(); + if (tk_Token == TK_RBRACKET) + { + ERR_Error(ERR_NEED_ARRAY_SIZE, YES); + } + else + { + dims[ndim] = EvalConstExpression(); + if(dims[ndim] == 0) + { + ERR_Error(ERR_ZERO_DIMENSION, YES); + dims[ndim] = 1; + } + if(ndim == 0) + { + size = dims[ndim]; + } + else + { + size *= dims[ndim]; + } + } + ndim++; + TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET); + TK_NextToken(); + } + if(sym != NULL) + { + if(ImportMode != IMPORT_Importing) + { + PC_AddArray(index, size); + } + MS_Message(MSG_DEBUG, "%s changed to an array of size %d\n", sym->name, size); + sym->type = SY_MAPARRAY; + sym->info.array.index = index; + sym->info.array.ndim = ndim; + sym->info.array.size = size; + if(ndim > 0) + { + int i; + + sym->info.array.dimensions[ndim-1] = 1; + for(i = ndim - 2; i >= 0; --i) + { + sym->info.array.dimensions[i] = + sym->info.array.dimensions[i+1] * dims[i+1]; + } + } + if(tk_Token == TK_ASSIGN) + { + InitializeArray(sym, dims, size); + } + } + } + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// OuterWorldVar +// +//========================================================================== + +static void OuterWorldVar(boolean isGlobal) +{ + int index; + symbolNode_t *sym; + + MS_Message(MSG_DEBUG, "---- Outer%sVar ----\n", isGlobal ? "Global" : "World"); + if(TK_NextToken() != TK_INT) + { + if(tk_Token != TK_BOOL) + { + TK_TokenMustBe(TK_STR, ERR_BAD_VAR_TYPE); + } + } + do + { + TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_WVAR_INDEX); + if(tk_Number >= (isGlobal ? MAX_GLOBAL_VARIABLES : MAX_WORLD_VARIABLES)) + { + ERR_Error(ERR_BAD_WVAR_INDEX+isGlobal, YES); + index = 0; + } + else + { + index = tk_Number; + } + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_WVAR_COLON+isGlobal); + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + if(SY_FindGlobal(tk_String) != NULL) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + } + else + { + TK_NextToken(); + if(tk_Token == TK_LBRACKET) + { + TK_NextToken (); + if(tk_Token != TK_RBRACKET) + { + ERR_Error(ERR_NO_NEED_ARRAY_SIZE, YES); + TK_SkipPast(TK_RBRACKET); + } + else + { + TK_NextToken(); + } + if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_NO_MULTIDIMENSIONS, YES); + do + { + TK_SkipPast(TK_RBRACKET); + } + while(tk_Token == TK_LBRACKET); + } + sym = SY_InsertGlobal(tk_String, isGlobal ? SY_GLOBALARRAY : SY_WORLDARRAY); + sym->info.var.index = index; + if (isGlobal) + pa_GlobalArrayCount++; + else + pa_WorldArrayCount++; + } + else + { + sym = SY_InsertGlobal(tk_String, isGlobal ? SY_GLOBALVAR : SY_WORLDVAR); + sym->info.var.index = index; + if (isGlobal) + pa_GlobalVarCount++; + else + pa_WorldVarCount++; + } + } + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// OuterSpecialDef +// +//========================================================================== + +static void OuterSpecialDef(void) +{ + int special; + symbolNode_t *sym; + + MS_Message(MSG_DEBUG, "---- OuterSpecialDef ----\n"); + if(ImportMode == IMPORT_Importing) + { + // No need to process special definitions when importing. + TK_SkipPast(TK_SEMICOLON); + } + else + { + do + { + TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_VAL); + special = tk_Number; + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_SPEC_COLON); + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + sym = SY_InsertGlobalUnique(tk_String, SY_SPECIAL); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_ARGC); + sym->info.special.value = special; + sym->info.special.argCount = tk_Number | (tk_Number << 16); + TK_NextToken(); + if(tk_Token == TK_COMMA) + { // Get maximum arg count + TK_NextTokenMustBe(TK_NUMBER, ERR_MISSING_SPEC_ARGC); + sym->info.special.argCount = + (sym->info.special.argCount & 0xffff) | (tk_Number << 16); + } + else + { + TK_Undo (); + } + TK_NextTokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + TK_NextToken(); + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); + } +} + +//========================================================================== +// +// OuterDefine +// +//========================================================================== + +static void OuterDefine(boolean force) +{ + int value; + symbolNode_t *sym; + + // Don't define inside an import + if(ImportMode != IMPORT_Importing || force) + { + MS_Message(MSG_DEBUG, "---- OuterDefine %s----\n", + force ? "(forced) " : ""); + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); + sym = SY_InsertGlobalUnique(tk_String, SY_CONSTANT); + TK_NextToken(); + value = EvalConstExpression(); + MS_Message(MSG_DEBUG, "Constant value: %d\n", value); + sym->info.constant.value = value; + } + else + { + TK_NextToken(); + TK_NextToken(); + EvalConstExpression(); + } +} + +//========================================================================== +// +// OuterInclude +// +//========================================================================== + +static void OuterInclude(void) +{ + // Don't include inside an import + if(ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "---- OuterInclude ----\n"); + TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND); + TK_Include(tk_String); + } + else + { + TK_NextToken(); + } + TK_NextToken(); +} + +//========================================================================== +// +// OuterImport +// +//========================================================================== + +static void OuterImport(void) +{ + + MS_Message(MSG_DEBUG, "---- OuterImport ----\n"); + if(ImportMode == IMPORT_Importing) + { + // Don't import inside an import + TK_NextToken(); + } + else + { + MS_Message(MSG_DEBUG, "Importing a file\n"); + TK_NextTokenMustBe(TK_STRING, ERR_STRING_LIT_NOT_FOUND); + TK_Import(tk_String, ImportMode); + } + TK_NextToken(); +} + +//========================================================================== +// +// ProcessStatement +// +//========================================================================== + +static boolean ProcessStatement(statement_t owner) +{ + if(StatementIndex == MAX_STATEMENT_DEPTH) + { + ERR_Exit(ERR_STATEMENT_OVERFLOW, YES); + } + StatementHistory[StatementIndex++] = owner; + switch(tk_Token) + { + case TK_INT: + case TK_STR: + case TK_BOOL: + case TK_STATIC: + LeadingVarDeclare(); + break; + case TK_LINESPECIAL: + LeadingLineSpecial(NO); + break; + case TK_ACSEXECUTEWAIT: + tk_SpecialArgCount = 1 | (5<<16); + tk_SpecialValue = 80; + LeadingLineSpecial(YES); + break; + case TK_RESTART: + LeadingRestart(); + break; + case TK_SUSPEND: + LeadingSuspend(); + break; + case TK_TERMINATE: + LeadingTerminate(); + break; + case TK_RETURN: + LeadingReturn(); + break; + case TK_IDENTIFIER: + LeadingIdentifier(); + break; + case TK_PRINT: + case TK_PRINTBOLD: + case TK_LOG: + LeadingPrint(); + break; + case TK_HUDMESSAGE: + case TK_HUDMESSAGEBOLD: + LeadingHudMessage(); + break; + case TK_IF: + LeadingIf(); + break; + case TK_FOR: + LeadingFor(); + break; + case TK_WHILE: + case TK_UNTIL: + LeadingWhileUntil(); + break; + case TK_DO: + LeadingDo(); + break; + case TK_SWITCH: + LeadingSwitch(); + break; + case TK_CASE: + if(owner != STMT_SWITCH) + { + ERR_Error(ERR_CASE_NOT_IN_SWITCH, YES); + TK_SkipPast(TK_COLON); + } + else + { + LeadingCase(); + } + break; + case TK_DEFAULT: + if(owner != STMT_SWITCH) + { + ERR_Error(ERR_DEFAULT_NOT_IN_SWITCH, YES); + TK_SkipPast(TK_COLON); + } + else if(DefaultInCurrent() == YES) + { + ERR_Error(ERR_MULTIPLE_DEFAULT, YES); + TK_SkipPast(TK_COLON); + } + else + { + LeadingDefault(); + } + break; + case TK_BREAK: + if(BreakAncestor() == NO) + { + ERR_Error(ERR_MISPLACED_BREAK, YES); + TK_SkipPast(TK_SEMICOLON); + } + else + { + LeadingBreak(); + } + break; + case TK_CONTINUE: + if(ContinueAncestor() == NO) + { + ERR_Error(ERR_MISPLACED_CONTINUE, YES); + TK_SkipPast(TK_SEMICOLON); + } + else + { + LeadingContinue(); + } + break; + case TK_CREATETRANSLATION: + LeadingCreateTranslation(); + break; + case TK_LBRACE: + LeadingCompoundStatement(owner); + break; + case TK_SEMICOLON: + TK_NextToken(); + break; + case TK_INC: + case TK_DEC: + LeadingIncDec(tk_Token); + break; + default: + StatementIndex--; + return NO; + break; + } + StatementIndex--; + return YES; +} + +//========================================================================== +// +// LeadingCompoundStatement +// +//========================================================================== + +static void LeadingCompoundStatement(statement_t owner) +{ + StatementLevel += AdjustStmtLevel[owner]; + TK_NextToken(); // Eat the TK_LBRACE + do ; while(ProcessStatement(owner) == YES); + TK_TokenMustBe(TK_RBRACE, ERR_INVALID_STATEMENT); + TK_NextToken(); + StatementLevel -= AdjustStmtLevel[owner]; +} + +//========================================================================== +// +// LeadingVarDeclare +// +//========================================================================== + +static void LeadingVarDeclare(void) +{ + symbolNode_t *sym = NULL; + + if(tk_Token == TK_STATIC) + { + TK_NextToken(); + if(tk_Token != TK_INT && tk_Token != TK_STR && tk_Token != TK_BOOL) + { + ERR_Error(ERR_BAD_VAR_TYPE, YES); + TK_Undo(); + } + OuterMapVar(YES); + return; + } + + MS_Message(MSG_DEBUG, "---- LeadingVarDeclare ----\n"); + do + { + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INVALID_IDENTIFIER); +#if 0 + if(ScriptVarCount == MAX_SCRIPT_VARIABLES) + { + ERR_Error(InsideFunction + ? ERR_TOO_MANY_FUNCTION_VARS + : ERR_TOO_MANY_SCRIPT_VARS, + YES, NULL); + ScriptVarCount++; + } + else +#endif + if(SY_FindLocal(tk_String) != NULL) + { // Redefined + ERR_Error(ERR_REDEFINED_IDENTIFIER, YES, tk_String); + } + else + { + sym = SY_InsertLocal(tk_String, SY_SCRIPTVAR); + sym->info.var.index = ScriptVarCount; + ScriptVarCount++; + } + TK_NextToken(); + if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_ARRAY_MAPVAR_ONLY, YES); + do ; while(TK_NextToken() != TK_COMMA && tk_Token != TK_SEMICOLON); + } + else if(tk_Token == TK_ASSIGN) + { + TK_NextToken(); + EvalExpression(); + if(sym != NULL) + { + PC_AppendCmd(PCD_ASSIGNSCRIPTVAR); + PC_AppendShrink(sym->info.var.index); + } + } + } while(tk_Token == TK_COMMA); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingLineSpecial +// +//========================================================================== + +static void LeadingLineSpecial(boolean executewait) +{ + int i; + int argCount; + int argCountMin; + int argCountMax; + int argSave[8]; + U_BYTE specialValue; + boolean direct; + + MS_Message(MSG_DEBUG, "---- LeadingLineSpecial ----\n"); + argCountMin = tk_SpecialArgCount & 0xffff; + argCountMax = tk_SpecialArgCount >> 16; + specialValue = tk_SpecialValue; + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + i = 0; + if(argCountMax > 0) + { + if(TK_NextToken() == TK_CONST) + { + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON); + direct = YES; + } + else + { + TK_Undo(); + direct = NO; + } + do + { + if(i == argCountMax) + { + ERR_Error(ERR_BAD_LSPEC_ARG_COUNT, YES); + i = argCountMax+1; + } + TK_NextToken(); + if(direct == YES) + { + argSave[i] = EvalConstExpression(); + } + else + { + EvalExpression(); + if (i == 0 && executewait) + { + PC_AppendCmd(PCD_DUP); + } + } + if(i < argCountMax) + { + i++; + } + } while(tk_Token == TK_COMMA); + if(i < argCountMin) + { + ERR_Error(ERR_BAD_LSPEC_ARG_COUNT, YES); + TK_SkipPast(TK_SEMICOLON); + return; + } + argCount = i; + } + else + { + // [RH] I added some zero-argument specials without realizing that + // ACS won't allow for less than one, so fake them as one-argument + // specials with a parameter of 0. + argCount = 1; + direct = YES; + argSave[0] = 0; + TK_NextToken (); + } + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + if(direct == NO) + { + PC_AppendCmd(PCD_LSPEC1+(argCount-1)); + if(pc_NoShrink) + { + PC_AppendLong(specialValue); + } + else + { + PC_AppendByte(specialValue); + } + if(executewait) + { + PC_AppendCmd(PCD_SCRIPTWAIT); + } + } + else + { + boolean uselongform; + + if(pc_NoShrink) + { + PC_AppendCmd(PCD_LSPEC1DIRECT+(argCount-1)); + PC_AppendLong(specialValue); + uselongform = YES; + } + else + { + uselongform = NO; + for (i = 0; i < argCount; i++) + { + if ((unsigned int)argSave[i] > 255) + { + uselongform = YES; + break; + } + } + PC_AppendCmd((argCount-1)+(uselongform?PCD_LSPEC1DIRECT:PCD_LSPEC1DIRECTB)); + PC_AppendByte(specialValue); + } + if (uselongform) + { + for (i = 0; i < argCount; i++) + { + PC_AppendLong(argSave[i]); + } + } + else + { + for (i = 0; i < argCount; i++) + { + PC_AppendByte((U_BYTE)argSave[i]); + } + } + if(executewait) + { + PC_AppendCmd(PCD_SCRIPTWAITDIRECT); + PC_AppendLong(argSave[0]); + } + } + TK_NextToken(); +} + +//========================================================================== +// +// LeadingIdentifier +// +//========================================================================== + +static void LeadingIdentifier(void) +{ + symbolNode_t *sym; + + sym = SpeculateSymbol(tk_String, NO); + switch(sym->type) + { + case SY_MAPARRAY: + case SY_SCRIPTVAR: + case SY_SCRIPTALIAS: + case SY_MAPVAR: + case SY_WORLDVAR: + case SY_GLOBALVAR: + case SY_WORLDARRAY: + case SY_GLOBALARRAY: + LeadingVarAssign(sym); + break; + case SY_INTERNFUNC: + LeadingInternFunc(sym); + break; + case SY_SCRIPTFUNC: + LeadingScriptFunc(sym); + break; + default: + break; + } +} + +//========================================================================== +// +// LeadingInternFunc +// +//========================================================================== + +static void LeadingInternFunc(symbolNode_t *sym) +{ + if(InsideFunction && sym->info.internFunc.latent) + { + ERR_Error(ERR_LATENT_IN_FUNC, YES); + } + ProcessInternFunc(sym); + if(sym->info.internFunc.hasReturnValue == YES) + { + PC_AppendCmd(PCD_DROP); + } + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// ProcessInternFunc +// +//========================================================================== + +static void ProcessInternFunc(symbolNode_t *sym) +{ + int i; + int argCount; + int optMask; + int outMask; + boolean direct; + boolean specialDirect; + int argSave[8]; + + MS_Message(MSG_DEBUG, "---- ProcessInternFunc ----\n"); + argCount = sym->info.internFunc.argCount; + optMask = sym->info.internFunc.optMask; + outMask = sym->info.internFunc.outMask; + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + if(TK_NextToken() == TK_CONST) + { + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON); + if(sym->info.internFunc.directCommand == PCD_NOP) + { + ERR_Error(ERR_NO_DIRECT_VER, YES, NULL); + direct = NO; + specialDirect = NO; + } + else + { + direct = YES; + if (pc_NoShrink || argCount > 2 || + (sym->info.internFunc.directCommand != PCD_DELAYDIRECT && + sym->info.internFunc.directCommand != PCD_RANDOMDIRECT)) + { + specialDirect = NO; + PC_AppendCmd(sym->info.internFunc.directCommand); + } + else + { + specialDirect = YES; + } + } + TK_NextToken(); + } + else + { + direct = NO; + specialDirect = NO; // keep GCC quiet + } + i = 0; + if(argCount > 0) + { + if(tk_Token == TK_RPAREN) + { + ERR_Error(ERR_MISSING_PARAM, YES); + } + else + { + TK_Undo(); // Adjust for first expression + do + { + if(i == argCount) + { + ERR_Error(ERR_BAD_ARG_COUNT, YES); + TK_SkipTo(TK_SEMICOLON); + TK_Undo(); + return; + } + TK_NextToken(); + if(direct == YES) + { + if (tk_Token != TK_COMMA) + { + if (specialDirect) + { + argSave[i] = EvalConstExpression(); + } + else + { + PC_AppendLong(EvalConstExpression()); + } + } + else + { + if (optMask & 1) + { + if (specialDirect) + { + argSave[i] = 0; + } + else + { + PC_AppendLong(0); + } + } + else + { + ERR_Error(ERR_MISSING_PARAM, YES); + } + } + } + else + { + if (tk_Token != TK_COMMA) + { + if (!(outMask & 1)) + { + EvalExpression(); + } + else if (tk_Token != TK_IDENTIFIER) + { + ERR_Error (ERR_PARM_MUST_BE_VAR, YES); + do + { + TK_NextToken(); + } while (tk_Token != TK_COMMA && tk_Token != TK_RPAREN); + } + else + { + symbolNode_t *sym = DemandSymbol (tk_String); + PC_AppendCmd (PCD_PUSHNUMBER); + switch (sym->type) + { + case SY_SCRIPTVAR: + PC_AppendLong(sym->info.var.index | OUTVAR_SCRIPT_SPEC); + break; + case SY_MAPVAR: + PC_AppendLong(sym->info.var.index | OUTVAR_MAP_SPEC); + break; + case SY_WORLDVAR: + PC_AppendLong(sym->info.var.index | OUTVAR_WORLD_SPEC); + break; + case SY_GLOBALVAR: + PC_AppendLong(sym->info.var.index | OUTVAR_GLOBAL_SPEC); + break; + default: + ERR_Error (ERR_PARM_MUST_BE_VAR, YES); + do + { + TK_NextToken(); + } while (tk_Token != TK_COMMA && tk_Token != TK_RPAREN); + break; + } + TK_NextToken (); + } + } + else + { + if (optMask & 1) + { + PC_AppendPushVal(0); + } + else + { + ERR_Error(ERR_MISSING_PARAM, YES); + } + } + } + i++; + optMask >>= 1; + outMask >>= 1; + } while(tk_Token == TK_COMMA); + } + } + while (i < argCount && (optMask & 1)) + { + if (direct == YES) + { + if (specialDirect) + { + argSave[i] = 0; + } + else + { + PC_AppendLong(0); + } + } + else + { + PC_AppendPushVal(0); + } + i++; + optMask >>= 1; + } + if(i != argCount && i > 0) + { + ERR_Error(ERR_BAD_ARG_COUNT, YES); + } + TK_TokenMustBe(TK_RPAREN, argCount > 0 ? ERR_MISSING_RPAREN : ERR_BAD_ARG_COUNT); + if(direct == NO) + { + PC_AppendCmd(sym->info.internFunc.stackCommand); + } + else if (specialDirect) + { + boolean uselongform = NO; + pcd_t shortpcd; + + switch (sym->info.internFunc.directCommand) + { + case PCD_DELAYDIRECT: + shortpcd = PCD_DELAYDIRECTB; + break; + case PCD_RANDOMDIRECT: + shortpcd = PCD_RANDOMDIRECTB; + break; + default: + uselongform = YES; + shortpcd = PCD_NOP; + break; + } + + if (!uselongform) + { + for (i = 0; i < argCount; i++) + { + if ((unsigned int)argSave[i] > 255) + { + uselongform = YES; + break; + } + } + } + + if (uselongform) + { + PC_AppendCmd(sym->info.internFunc.directCommand); + for (i = 0; i < argCount; i++) + { + PC_AppendLong (argSave[i]); + } + } + else + { + PC_AppendCmd (shortpcd); + for (i = 0; i < argCount; i++) + { + PC_AppendByte ((U_BYTE)argSave[i]); + } + } + } + TK_NextToken(); +} + +//========================================================================== +// +// LeadingScriptFunc +// +//========================================================================== + +static void LeadingScriptFunc(symbolNode_t *sym) +{ + ProcessScriptFunc(sym, YES); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + + +//========================================================================== +// +// ProcessScriptFunc +// +//========================================================================== + +static void ProcessScriptFunc(symbolNode_t *sym, boolean discardReturn) +{ + int i; + int argCount; + + MS_Message(MSG_DEBUG, "---- ProcessScriptFunc ----\n"); + argCount = sym->info.scriptFunc.argCount; + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + i = 0; + if(argCount == 0) + { + TK_NextTokenMustBe(TK_RPAREN, ERR_BAD_ARG_COUNT); + } + else if(argCount > 0) + { + TK_NextToken(); + if(tk_Token == TK_RPAREN) + { + ERR_Error(ERR_BAD_ARG_COUNT, YES); + TK_SkipTo(TK_SEMICOLON); + return; + } + TK_Undo(); + do + { + if(i == argCount) + { + ERR_Error(ERR_BAD_ARG_COUNT, YES); + TK_SkipTo(TK_SEMICOLON); + return; + } + TK_NextToken(); + if (tk_Token != TK_COMMA) + { + EvalExpression(); + } + else + { + ERR_Error(ERR_MISSING_PARAM, YES); + TK_SkipTo(TK_SEMICOLON); + return; + } + i++; + } while(tk_Token == TK_COMMA); + } + if(argCount < 0) + { // Function has not been defined yet, so assume arg count is correct + TK_NextToken(); + while (tk_Token != TK_RPAREN) + { + EvalExpression(); + i++; + if (tk_Token == TK_COMMA) + { + TK_NextToken(); + } + else if (tk_Token != TK_RPAREN) + { + ERR_Error(ERR_MISSING_PARAM, YES); + TK_SkipTo(TK_SEMICOLON); + return; + } + } + } + else if(i != argCount) + { + ERR_Error(ERR_BAD_ARG_COUNT, YES); + TK_SkipTo(TK_SEMICOLON); + return; + } + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + PC_AppendCmd(discardReturn ? PCD_CALLDISCARD : PCD_CALL); + if(sym->info.scriptFunc.predefined && ImportMode != IMPORT_Importing) + { + AddScriptFuncRef(sym, pc_Address, i); + } + if (pc_NoShrink) + { + PC_AppendLong(sym->info.scriptFunc.funcNumber); + } + else + { + PC_AppendByte((U_BYTE)sym->info.scriptFunc.funcNumber); + } + TK_NextToken(); +} + +//========================================================================== +// +// BuildPrintString +// +//========================================================================== + +static void BuildPrintString(void) +{ + pcd_t printCmd; + + do + { + switch(TK_NextCharacter()) + { + case 'a': // character array support [JB] + PrintCharArray(); + continue; + case 's': // string + printCmd = PCD_PRINTSTRING; + break; + case 'l': // [RH] localized string + printCmd = PCD_PRINTLOCALIZED; + break; + case 'i': // integer + case 'd': // decimal + printCmd = PCD_PRINTNUMBER; + break; + case 'c': // character + printCmd = PCD_PRINTCHARACTER; + break; + case 'n': // [BC] name + printCmd = PCD_PRINTNAME; + break; + case 'f': // [RH] fixed point + printCmd = PCD_PRINTFIXED; + break; + default: + printCmd = PCD_PRINTSTRING; + ERR_Error(ERR_UNKNOWN_PRTYPE, YES); + break; + } + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextToken(); + EvalExpression(); + PC_AppendCmd(printCmd); + } while(tk_Token == TK_COMMA); +} + +//========================================================================== +// +// PrintCharArray // JB +// +//========================================================================== + +static void PrintCharArray(void) +{ + symbolNode_t *sym; + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextToken(); + sym = SpeculateSymbol(tk_String, NO); + if((sym->type != SY_MAPARRAY) && (sym->type != SY_WORLDARRAY) + && (sym->type != SY_GLOBALARRAY)) + { + ERR_Error(ERR_NOT_AN_ARRAY, YES); + } + TK_NextToken(); + if(sym->info.array.ndim > 1) + { + ParseArrayIndices(sym, sym->info.array.ndim-1); + } + else + { + PC_AppendPushVal(0); + } + + PC_AppendPushVal(sym->info.array.index); + if(sym->type == SY_MAPARRAY) + { + PC_AppendCmd(PCD_PRINTMAPCHARARRAY); + } + else if(sym->type == SY_WORLDARRAY) + { + PC_AppendCmd(PCD_PRINTWORLDCHARARRAY); + } + else // if(sym->type == SY_GLOBALARRAY) + { + PC_AppendCmd(PCD_PRINTGLOBALCHARARRAY); + } +} + +//========================================================================== +// +// LeadingPrint +// +//========================================================================== + +static void LeadingPrint(void) +{ + tokenType_t stmtToken; + + MS_Message(MSG_DEBUG, "---- LeadingPrint ----\n"); + stmtToken = tk_Token; // Will be TK_PRINT or TK_PRINTBOLD or TK_LOG + PC_AppendCmd(PCD_BEGINPRINT); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + BuildPrintString(); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + if(stmtToken == TK_PRINT) + { + PC_AppendCmd(PCD_ENDPRINT); + } + else if(stmtToken == TK_PRINTBOLD) + { + PC_AppendCmd(PCD_ENDPRINTBOLD); + } + else + { + PC_AppendCmd(PCD_ENDLOG); + } + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingHudMessage +// +// hudmessage(str text; int type, int id, int color, fixed x, fixed y, fixed holdtime, ...) +// +//========================================================================== + +static void LeadingHudMessage(void) +{ + tokenType_t stmtToken; + int i; + + MS_Message(MSG_DEBUG, "---- LeadingHudMessage ----\n"); + stmtToken = tk_Token; // Will be TK_HUDMESSAGE or TK_HUDMESSAGEBOLD + PC_AppendCmd(PCD_BEGINPRINT); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + BuildPrintString(); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_PARAM); + PC_AppendCmd(PCD_MOREHUDMESSAGE); + for (i = 6; i > 0; i--) + { + TK_NextToken(); + EvalExpression(); + if (i > 1) + TK_TokenMustBe(TK_COMMA, ERR_MISSING_PARAM); + } + if (tk_Token == TK_COMMA) + { // HUD message has optional parameters + PC_AppendCmd(PCD_OPTHUDMESSAGE); + do + { + TK_NextToken(); + EvalExpression(); + } while (tk_Token == TK_COMMA); + } + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + PC_AppendCmd(stmtToken == TK_HUDMESSAGE ? + PCD_ENDHUDMESSAGE : PCD_ENDHUDMESSAGEBOLD); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingCreateTranslation +// +// Simple grammar: +// +// tranlationstmt: CreateTranslation ( exp opt_args ) ; +// opt_args: /* empty: just reset the translation */ | , arglist +// arglist: arg | arglist arg +// arg: range = replacement +// range: exp : exp +// replacement: palrep | colorrep +// palrep: exp : exp +// colorrep: [exp,exp,exp]:[exp,exp,exp] +//========================================================================== + +static void LeadingCreateTranslation(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingCreateTranslation ----\n"); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextToken(); + EvalExpression(); + PC_AppendCmd(PCD_STARTTRANSLATION); + while (tk_Token == TK_COMMA) + { + TK_NextToken(); + EvalExpression(); // Get first palette entry in range + TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextToken(); + EvalExpression(); // Get second palette entry in range + TK_TokenMustBe(TK_ASSIGN, ERR_MISSING_ASSIGN); + + TK_NextToken(); + if(tk_Token == TK_LBRACKET) + { // Replacement is color range + int i, j; + + TK_NextToken(); + + for(j = 2; j != 0; --j) + { + for(i = 3; i != 0; --i) + { + EvalExpression(); + if(i != 1) + { + TK_TokenMustBe(TK_COMMA, ERR_MISSING_COMMA); + } + else + { + TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET); + } + TK_NextToken(); + } + if(j == 2) + { + TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextTokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET); + TK_NextToken(); + } + } + PC_AppendCmd(PCD_TRANSLATIONRANGE2); + } + else + { // Replacement is palette range + EvalExpression(); + TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextToken(); + EvalExpression(); + PC_AppendCmd(PCD_TRANSLATIONRANGE1); + } + } + PC_AppendCmd(PCD_ENDTRANSLATION); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingIf +// +//========================================================================== + +static void LeadingIf(void) +{ + int jumpAddrPtr1; + int jumpAddrPtr2; + + MS_Message(MSG_DEBUG, "---- LeadingIf ----\n"); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextToken(); + EvalExpression(); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + PC_AppendCmd(PCD_IFNOTGOTO); + jumpAddrPtr1 = pc_Address; + PC_SkipLong(); + TK_NextToken(); + if(ProcessStatement(STMT_IF) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + if(tk_Token == TK_ELSE) + { + PC_AppendCmd(PCD_GOTO); + jumpAddrPtr2 = pc_Address; + PC_SkipLong(); + PC_WriteLong(pc_Address, jumpAddrPtr1); + TK_NextToken(); + if(ProcessStatement(STMT_ELSE) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + PC_WriteLong(pc_Address, jumpAddrPtr2); + } + else + { + PC_WriteLong(pc_Address, jumpAddrPtr1); + } +} + +//========================================================================== +// +// LeadingFor +// +//========================================================================== + +static void LeadingFor(void) +{ + int exprAddr; + int incAddr; + int ifgotoAddr; + int gotoAddr; + + MS_Message(MSG_DEBUG, "---- LeadingFor ----\n"); + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextToken(); + if(ProcessStatement(STMT_FOR) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + exprAddr = pc_Address; + EvalExpression(); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); + PC_AppendCmd(PCD_IFGOTO); + ifgotoAddr = pc_Address; + PC_SkipLong(); + PC_AppendCmd(PCD_GOTO); + gotoAddr = pc_Address; + PC_SkipLong(); + incAddr = pc_Address; + forSemicolonHack = TRUE; + if(ProcessStatement(STMT_FOR) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + forSemicolonHack = FALSE; + PC_AppendCmd(PCD_GOTO); + PC_AppendLong(exprAddr); + PC_WriteLong(pc_Address,ifgotoAddr); + if(ProcessStatement(STMT_FOR) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + PC_AppendCmd(PCD_GOTO); + PC_AppendLong(incAddr); + WriteContinues(incAddr); + WriteBreaks(); + PC_WriteLong(pc_Address,gotoAddr); +} + +//========================================================================== +// +// LeadingWhileUntil +// +//========================================================================== + +static void LeadingWhileUntil(void) +{ + tokenType_t stmtToken; + int topAddr; + int outAddrPtr; + + MS_Message(MSG_DEBUG, "---- LeadingWhileUntil ----\n"); + stmtToken = tk_Token; + topAddr = pc_Address; + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextToken(); + EvalExpression(); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + PC_AppendCmd(stmtToken == TK_WHILE ? PCD_IFNOTGOTO : PCD_IFGOTO); + outAddrPtr = pc_Address; + PC_SkipLong(); + TK_NextToken(); + if(ProcessStatement(STMT_WHILEUNTIL) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES); + } + PC_AppendCmd(PCD_GOTO); + PC_AppendLong(topAddr); + + PC_WriteLong(pc_Address, outAddrPtr); + + WriteContinues(topAddr); + WriteBreaks(); +} + +//========================================================================== +// +// LeadingDo +// +//========================================================================== + +static void LeadingDo(void) +{ + int topAddr; + int exprAddr; + tokenType_t stmtToken; + + MS_Message(MSG_DEBUG, "---- LeadingDo ----\n"); + topAddr = pc_Address; + TK_NextToken(); + if(ProcessStatement(STMT_DO) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES, NULL); + } + if(tk_Token != TK_WHILE && tk_Token != TK_UNTIL) + { + ERR_Error(ERR_BAD_DO_STATEMENT, YES, NULL); + TK_SkipPast(TK_SEMICOLON); + return; + } + stmtToken = tk_Token; + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + exprAddr = pc_Address; + TK_NextToken(); + EvalExpression(); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(stmtToken == TK_WHILE ? PCD_IFGOTO : PCD_IFNOTGOTO); + PC_AppendLong(topAddr); + WriteContinues(exprAddr); + WriteBreaks(); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingSwitch +// +//========================================================================== + +static void LeadingSwitch(void) +{ + int switcherAddrPtr; + int outAddrPtr; + caseInfo_t *cInfo; + int defaultAddress; + + MS_Message(MSG_DEBUG, "---- LeadingSwitch ----\n"); + + TK_NextTokenMustBe(TK_LPAREN, ERR_MISSING_LPAREN); + TK_NextToken(); + EvalExpression(); + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + + PC_AppendCmd(PCD_GOTO); + switcherAddrPtr = pc_Address; + PC_SkipLong(); + + TK_NextToken(); + if(ProcessStatement(STMT_SWITCH) == NO) + { + ERR_Error(ERR_INVALID_STATEMENT, YES, NULL); + } + + PC_AppendCmd(PCD_GOTO); + outAddrPtr = pc_Address; + PC_SkipLong(); + + PC_WriteLong(pc_Address, switcherAddrPtr); + defaultAddress = 0; + + if(pc_HexenCase) + { + while((cInfo = GetCaseInfo()) != NULL) + { + if(cInfo->isDefault == YES) + { + defaultAddress = cInfo->address; + continue; + } + PC_AppendCmd(PCD_CASEGOTO); + PC_AppendLong(cInfo->value); + PC_AppendLong(cInfo->address); + } + } + else if(CaseIndex != 0) + { + caseInfo_t *maxCase = &CaseInfo[CaseIndex]; + caseInfo_t *minCase = maxCase; + + // [RH] Sort cases so that the VM can handle them with + // a quick binary search. + while((cInfo = GetCaseInfo()) != NULL) + { + minCase = cInfo; + } + qsort(minCase, maxCase - minCase, sizeof(caseInfo_t), CaseInfoCmp); + if(minCase->isDefault == YES) + { + defaultAddress = minCase->address; + minCase++; + } + if (minCase < maxCase) + { + PC_AppendCmd(PCD_CASEGOTOSORTED); + if(pc_Address%4 != 0) + { // Align to a 4-byte boundary + U_LONG pad = 0; + PC_Append((void *)&pad, 4-(pc_Address%4)); + } + PC_AppendLong(maxCase - minCase); + for(; minCase < maxCase; ++minCase) + { + PC_AppendLong(minCase->value); + PC_AppendLong(minCase->address); + } + } + } + PC_AppendCmd(PCD_DROP); + + if(defaultAddress != 0) + { + PC_AppendCmd(PCD_GOTO); + PC_AppendLong(defaultAddress); + } + + PC_WriteLong(pc_Address, outAddrPtr); + + WriteBreaks(); +} + +//========================================================================== +// +// LeadingCase +// +//========================================================================== + +static void LeadingCase(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingCase ----\n"); + TK_NextToken(); + PushCase(EvalConstExpression(), NO); + TK_TokenMustBe(TK_COLON, ERR_MISSING_COLON); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingDefault +// +//========================================================================== + +static void LeadingDefault(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingDefault ----\n"); + TK_NextTokenMustBe(TK_COLON, ERR_MISSING_COLON); + PushCase(0, YES); + TK_NextToken(); +} + +//========================================================================== +// +// PushCase +// +//========================================================================== + +static void PushCase(int value, boolean isDefault) +{ + if(CaseIndex == MAX_CASE) + { + ERR_Exit(ERR_CASE_OVERFLOW, YES); + } + CaseInfo[CaseIndex].level = StatementLevel; + CaseInfo[CaseIndex].value = value; + CaseInfo[CaseIndex].isDefault = isDefault; + CaseInfo[CaseIndex].address = pc_Address; + CaseIndex++; +} + +//========================================================================== +// +// GetCaseInfo +// +//========================================================================== + +static caseInfo_t *GetCaseInfo(void) +{ + if(CaseIndex == 0) + { + return NULL; + } + if(CaseInfo[CaseIndex-1].level > StatementLevel) + { + return &CaseInfo[--CaseIndex]; + } + return NULL; +} + +//========================================================================== +// +// CaseInfoCmp +// +//========================================================================== + +static int CaseInfoCmp (const void *a, const void *b) +{ + const caseInfo_t *ca = (const caseInfo_t *)a; + const caseInfo_t *cb = (const caseInfo_t *)b; + + // The default case always gets moved to the front. + if(ca->isDefault) + { + return -1; + } + if(cb->isDefault) + { + return 1; + } + return ca->value - cb->value; +} + +//========================================================================== +// +// DefaultInCurrent +// +//========================================================================== + +static boolean DefaultInCurrent(void) +{ + int i; + + for(i = 0; i < CaseIndex; i++) + { + if(CaseInfo[i].isDefault == YES + && CaseInfo[i].level == StatementLevel) + { + return YES; + } + } + return NO; +} + +//========================================================================== +// +// LeadingBreak +// +//========================================================================== + +static void LeadingBreak(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingBreak ----\n"); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_GOTO); + PushBreak(); + PC_SkipLong(); + TK_NextToken(); +} + +//========================================================================== +// +// PushBreak +// +//========================================================================== + +static void PushBreak(void) +{ + if(BreakIndex == MAX_CASE) + { + ERR_Exit(ERR_BREAK_OVERFLOW, YES); + } + BreakInfo[BreakIndex].level = StatementLevel; + BreakInfo[BreakIndex].addressPtr = pc_Address; + BreakIndex++; +} + +//========================================================================== +// +// WriteBreaks +// +//========================================================================== + +static void WriteBreaks(void) +{ + while(BreakIndex && BreakInfo[BreakIndex-1].level > StatementLevel) + { + PC_WriteLong(pc_Address, BreakInfo[--BreakIndex].addressPtr); + } +} + +//========================================================================== +// +// BreakAncestor +// +// Returns YES if the current statement history contains a break root +// statement. +// +//========================================================================== + +static boolean BreakAncestor(void) +{ + int i; + + for(i = 0; i < StatementIndex; i++) + { + if(IsBreakRoot[StatementHistory[i]]) + { + return YES; + } + } + return NO; +} + +//========================================================================== +// +// LeadingContinue +// +//========================================================================== + +static void LeadingContinue(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingContinue ----\n"); + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_GOTO); + PushContinue(); + PC_SkipLong(); + TK_NextToken(); +} + +//========================================================================== +// +// PushContinue +// +//========================================================================== + +static void PushContinue(void) +{ + if(ContinueIndex == MAX_CONTINUE) + { + ERR_Exit(ERR_CONTINUE_OVERFLOW, YES); + } + ContinueInfo[ContinueIndex].level = StatementLevel; + ContinueInfo[ContinueIndex].addressPtr = pc_Address; + ContinueIndex++; +} + +//========================================================================== +// +// WriteContinues +// +//========================================================================== + +static void WriteContinues(int address) +{ + if(ContinueIndex == 0) + { + return; + } + while(ContinueInfo[ContinueIndex-1].level > StatementLevel) + { + PC_WriteLong(address, ContinueInfo[--ContinueIndex].addressPtr); + } +} + +//========================================================================== +// +// ContinueAncestor +// +//========================================================================== + +static boolean ContinueAncestor(void) +{ + int i; + + for(i = 0; i < StatementIndex; i++) + { + if(IsContinueRoot[StatementHistory[i]]) + { + return YES; + } + } + return NO; +} + +//========================================================================== +// +// LeadingIncDec +// +//========================================================================== + +static void LeadingIncDec(int token) +{ + symbolNode_t *sym; + + MS_Message(MSG_DEBUG, "---- LeadingIncDec ----\n"); + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INCDEC_OP_ON_NON_VAR); + sym = DemandSymbol(tk_String); + if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR + && sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR + && sym->type != SY_MAPARRAY && sym->type != SY_GLOBALARRAY + && sym->type != SY_WORLDARRAY && sym->type != SY_SCRIPTALIAS) + { + ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES); + TK_SkipPast(TK_SEMICOLON); + return; + } + TK_NextToken(); + if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY + || sym->type == SY_GLOBALARRAY) + { + ParseArrayIndices(sym, sym->info.array.ndim); + } + else if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name); + while(tk_Token == TK_LBRACKET) + { + TK_SkipPast(TK_RBRACKET); + } + } + PC_AppendCmd(GetIncDecPCD(token, sym->type)); + PC_AppendShrink(sym->info.var.index); + if(forSemicolonHack) + { + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + } + else + { + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + } + TK_NextToken(); +} + +//========================================================================== +// +// LeadingVarAssign +// +//========================================================================== + +static void LeadingVarAssign(symbolNode_t *sym) +{ + boolean done; + tokenType_t assignToken; + + MS_Message(MSG_DEBUG, "---- LeadingVarAssign ----\n"); + done = NO; + do + { + TK_NextToken(); // Fetch assignment operator + if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY + || sym->type == SY_GLOBALARRAY) + { + ParseArrayIndices(sym, sym->info.array.ndim); + } + else if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name); + while(tk_Token == TK_LBRACKET) + { + TK_SkipPast(TK_RBRACKET); + } + } + if(tk_Token == TK_INC || tk_Token == TK_DEC) + { // Postfix increment or decrement + PC_AppendCmd(GetIncDecPCD(tk_Token, sym->type)); + if (pc_NoShrink) + { + PC_AppendLong(sym->info.var.index); + } + else + { + PC_AppendByte(sym->info.var.index); + } + TK_NextToken(); + } + else + { // Normal operator + if(TK_Member(AssignOps) == NO) + { + ERR_Error(ERR_MISSING_ASSIGN_OP, YES); + TK_SkipPast(TK_SEMICOLON); + return; + } + assignToken = tk_Token; + TK_NextToken(); + EvalExpression(); + PC_AppendCmd(GetAssignPCD(assignToken, sym->type)); + PC_AppendShrink(sym->info.var.index); + } + if(tk_Token == TK_COMMA) + { + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_BAD_ASSIGNMENT); + sym = DemandSymbol(tk_String); + if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR + && sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR + && sym->type != SY_WORLDARRAY && sym->type != SY_GLOBALARRAY + && sym->type != SY_SCRIPTALIAS) + { + ERR_Error(ERR_BAD_ASSIGNMENT, YES); + TK_SkipPast(TK_SEMICOLON); + return; + } + } + else + { + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + TK_NextToken(); + done = YES; + } + } while(done == NO); +} + +//========================================================================== +// +// GetAssignPCD +// +//========================================================================== + +static pcd_t GetAssignPCD(tokenType_t token, symbolType_t symbol) +{ + size_t i, j; + static tokenType_t tokenLookup[] = + { + TK_ASSIGN, TK_ADDASSIGN, TK_SUBASSIGN, + TK_MULASSIGN, TK_DIVASSIGN, TK_MODASSIGN + }; + static symbolType_t symbolLookup[] = + { + SY_SCRIPTVAR, SY_MAPVAR, SY_WORLDVAR, SY_GLOBALVAR, SY_MAPARRAY, + SY_WORLDARRAY, SY_GLOBALARRAY + }; + static pcd_t assignmentLookup[6][7] = + { + { PCD_ASSIGNSCRIPTVAR, PCD_ASSIGNMAPVAR, PCD_ASSIGNWORLDVAR, PCD_ASSIGNGLOBALVAR, PCD_ASSIGNMAPARRAY, PCD_ASSIGNWORLDARRAY, PCD_ASSIGNGLOBALARRAY }, + { PCD_ADDSCRIPTVAR, PCD_ADDMAPVAR, PCD_ADDWORLDVAR, PCD_ADDGLOBALVAR, PCD_ADDMAPARRAY, PCD_ADDWORLDARRAY, PCD_ADDGLOBALARRAY }, + { PCD_SUBSCRIPTVAR, PCD_SUBMAPVAR, PCD_SUBWORLDVAR, PCD_SUBGLOBALVAR, PCD_SUBMAPARRAY, PCD_SUBWORLDARRAY, PCD_SUBGLOBALARRAY }, + { PCD_MULSCRIPTVAR, PCD_MULMAPVAR, PCD_MULWORLDVAR, PCD_MULGLOBALVAR, PCD_MULMAPARRAY, PCD_MULWORLDARRAY, PCD_MULGLOBALARRAY }, + { PCD_DIVSCRIPTVAR, PCD_DIVMAPVAR, PCD_DIVWORLDVAR, PCD_DIVGLOBALVAR, PCD_DIVMAPARRAY, PCD_DIVWORLDARRAY, PCD_DIVGLOBALARRAY }, + { PCD_MODSCRIPTVAR, PCD_MODMAPVAR, PCD_MODWORLDVAR, PCD_MODGLOBALVAR, PCD_MODMAPARRAY, PCD_MODWORLDARRAY, PCD_MODGLOBALARRAY } + }; + + for(i = 0; i < ARRAY_SIZE(tokenLookup); ++i) + { + if(tokenLookup[i] == token) + { + for(j = 0; j < ARRAY_SIZE(symbolLookup); ++j) + { + if (symbolLookup[j] == symbol) + { + return assignmentLookup[i][j]; + } + } + break; + } + } + return PCD_NOP; +} + +//========================================================================== +// +// LeadingSuspend +// +//========================================================================== + +static void LeadingSuspend(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingSuspend ----\n"); + if(InsideFunction) + { + ERR_Error(ERR_SUSPEND_IN_FUNCTION, YES); + } + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_SUSPEND); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingTerminate +// +//========================================================================== + +static void LeadingTerminate(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingTerminate ----\n"); + if(InsideFunction) + { + ERR_Error(ERR_TERMINATE_IN_FUNCTION, YES); + } + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_TERMINATE); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingRestart +// +//========================================================================== + +static void LeadingRestart(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingRestart ----\n"); + if(InsideFunction) + { + ERR_Error(ERR_RESTART_IN_FUNCTION, YES); + } + TK_NextTokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_RESTART); + TK_NextToken(); +} + +//========================================================================== +// +// LeadingReturn +// +//========================================================================== + +static void LeadingReturn(void) +{ + MS_Message(MSG_DEBUG, "---- LeadingReturn ----\n"); + if(!InsideFunction) + { + ERR_Error(ERR_RETURN_OUTSIDE_FUNCTION, YES); + while (TK_NextToken () != TK_SEMICOLON) + { + if (tk_Token == TK_EOF) + break; + } + } + else + { + TK_NextToken(); + if(tk_Token == TK_SEMICOLON) + { + if(InsideFunction->info.scriptFunc.hasReturnValue) + { + ERR_Error(ERR_MUST_RETURN_A_VALUE, YES); + } + PC_AppendCmd(PCD_RETURNVOID); + } + else + { + if(!InsideFunction->info.scriptFunc.hasReturnValue) + { + ERR_Error(ERR_MUST_NOT_RETURN_A_VALUE, YES); + } + EvalExpression(); + TK_TokenMustBe(TK_SEMICOLON, ERR_MISSING_SEMICOLON); + PC_AppendCmd(PCD_RETURNVAL); + } + TK_NextToken(); + } +} + +//========================================================================== +// +// EvalConstExpression +// +//========================================================================== + +static int EvalConstExpression(void) +{ + pa_ConstExprIsString = NO; // Used by PC_PutMapVariable + ExprStackIndex = 0; + ConstantExpression = YES; + ExprLevA(); + if(ExprStackIndex != 1) + { + ERR_Error(ERR_BAD_CONST_EXPR, YES, NULL); + ExprStack[0] = 0; + ExprStackIndex = 1; + } + return PopExStk(); +} + +//========================================================================== +// +// EvalExpression +// +// [RH] Rewrote all the ExprLevA - ExprLevJ functions in favor of a single +// table-driven parser function. +// +//========================================================================== + +static void EvalExpression(void) +{ + ConstantExpression = NO; + ExprLevA(); +} + +static void ExprLevA(void) +{ + ExprLevX(0); +} + +// Operator precedence levels: +// Operator: || +// Operator: && +// Operator: | +// Operator: ^ +// Operator: & +// Operators: == != +// Operators: < <= > >= +// Operators: << >> +// Operators: + - +// Operators: * / % + +static void ExprLevX(int level) +{ + if(OpsList[level] == NULL) + { + boolean unaryMinus; + + unaryMinus = FALSE; + if(tk_Token == TK_MINUS) + { + unaryMinus = TRUE; + TK_NextToken(); + } + if(ConstantExpression == YES) + { + ConstExprFactor(); + } + else + { + ExprFactor(); + } + if(unaryMinus == TRUE) + { + SendExprCommand(PCD_UNARYMINUS); + } + } + else + { + ExprLevX(level + 1); + while(TK_Member(OpsList[level])) + { + tokenType_t token = tk_Token; + TK_NextToken(); + ExprLevX(level + 1); + SendExprCommand(TokenToPCD(token)); + } + } +} + +static void ExprLineSpecial(void) +{ + U_BYTE specialValue = tk_SpecialValue; + int argCountMin = tk_SpecialArgCount & 0xffff; + int argCountMax = tk_SpecialArgCount >> 16; + + // There are two ways to use a special in an expression: + // 1. The special name by itself returns the special's number. + // 2. The special followed by parameters actually executes the special. + TK_NextToken(); + if(tk_Token != TK_LPAREN) + { + PC_AppendPushVal(tk_SpecialValue); + } + else + { + int argCount = 0; + + TK_NextToken(); + if(tk_Token != TK_RPAREN) + { + TK_Undo(); + do + { + TK_NextToken(); + EvalExpression(); + argCount++; + } while(tk_Token == TK_COMMA); + } + if(argCount < argCountMin || argCount > argCountMax) + { + ERR_Error(ERR_BAD_LSPEC_ARG_COUNT, YES); + return; + } + for(; argCount < 5; ++argCount) + { + PC_AppendPushVal(0); + } + TK_TokenMustBe(TK_RPAREN, ERR_MISSING_RPAREN); + TK_NextToken(); + PC_AppendCmd(PCD_LSPEC5RESULT); + if(pc_NoShrink) + { + PC_AppendLong(specialValue); + } + else + { + PC_AppendByte(specialValue); + } + } +} + +static void ExprFactor(void) +{ + symbolNode_t *sym; + tokenType_t opToken; + + switch(tk_Token) + { + case TK_STRING: + if (ImportMode != IMPORT_Importing) + { + tk_Number = STR_Find(tk_String); + PC_AppendPushVal(tk_Number); + if (ImportMode == IMPORT_Exporting) + { + // The VM identifies strings by storing a library ID in the + // high word of the string index. The library ID is not + // known until the script actually gets loaded into the game, + // so we need to use this p-code to tack the ID of the + // currently running library to the index of the string that + // just got pushed onto the stack. + // + // Simply assuming that a string is from the current library + // is not good enough, because they can be passed around + // between libraries and the map's behavior. Thus, we need + // to know which object file a particular string belongs to. + // + // A specific example: + // A map's behavior calls a function in a library and passes + // a string: + // + // LibFunc ("The library will do something with this string."); + // + // The library function needs to know that the string originated + // outside the library. Similarly, if a library function returns + // a string, the caller needs to know that the string did not + // originate from the same object file. + // + // And that's why strings have library IDs tacked onto them. + // The map's main behavior (i.e. an object that is not a library) + // always uses library ID 0 to identify its strings, so its + // strings don't need to be tagged. + PC_AppendCmd(PCD_TAGSTRING); + } + } + TK_NextToken(); + break; + case TK_NUMBER: + PC_AppendPushVal(tk_Number); + TK_NextToken(); + break; + case TK_LPAREN: + TK_NextToken(); + ExprLevA(); + if(tk_Token != TK_RPAREN) + { + ERR_Error(ERR_BAD_EXPR, YES, NULL); + TK_SkipPast(TK_RPAREN); + } + else + { + TK_NextToken(); + } + break; + case TK_NOT: + TK_NextToken(); + ExprFactor(); + PC_AppendCmd(PCD_NEGATELOGICAL); + break; + case TK_INC: + case TK_DEC: + opToken = tk_Token; + TK_NextTokenMustBe(TK_IDENTIFIER, ERR_INCDEC_OP_ON_NON_VAR); + sym = DemandSymbol(tk_String); + if(sym->type != SY_SCRIPTVAR && sym->type != SY_MAPVAR + && sym->type != SY_WORLDVAR && sym->type != SY_GLOBALVAR + && sym->type != SY_MAPARRAY && sym->type != SY_WORLDARRAY + && sym->type != SY_GLOBALARRAY) + { + ERR_Error(ERR_INCDEC_OP_ON_NON_VAR, YES); + } + else + { + TK_NextToken(); + if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY + || sym->type == SY_GLOBALARRAY) + { + ParseArrayIndices(sym, sym->info.array.ndim); + PC_AppendCmd(PCD_DUP); + } + else if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name); + while(tk_Token == TK_LBRACKET) + { + TK_SkipPast(TK_RBRACKET); + } + } + PC_AppendCmd(GetIncDecPCD(opToken, sym->type)); + PC_AppendShrink(sym->info.var.index); + PC_AppendCmd(GetPushVarPCD(sym->type)); + PC_AppendShrink(sym->info.var.index); + } + break; + case TK_IDENTIFIER: + sym = SpeculateSymbol(tk_String, YES); + switch(sym->type) + { + case SY_SCRIPTALIAS: + // FIXME + break; + case SY_MAPARRAY: + case SY_WORLDARRAY: + case SY_GLOBALARRAY: + TK_NextToken(); + ParseArrayIndices(sym, sym->info.array.ndim); + // fallthrough + case SY_SCRIPTVAR: + case SY_MAPVAR: + case SY_WORLDVAR: + case SY_GLOBALVAR: + if(sym->type != SY_MAPARRAY && sym->type != SY_WORLDARRAY + && sym->type != SY_GLOBALARRAY) + { + TK_NextToken(); + if(tk_Token == TK_LBRACKET) + { + ERR_Error(ERR_NOT_AN_ARRAY, YES, sym->name); + while(tk_Token == TK_LBRACKET) + { + TK_SkipPast(TK_RBRACKET); + } + } + } + if((tk_Token == TK_INC || tk_Token == TK_DEC) + && (sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY + || sym->type == SY_GLOBALARRAY)) + { + PC_AppendCmd(PCD_DUP); + } + PC_AppendCmd(GetPushVarPCD(sym->type)); + PC_AppendShrink(sym->info.var.index); + if(tk_Token == TK_INC || tk_Token == TK_DEC) + { + if(sym->type == SY_MAPARRAY || sym->type == SY_WORLDARRAY + || sym->type == SY_GLOBALARRAY) + { + PC_AppendCmd(PCD_SWAP); + } + PC_AppendCmd(GetIncDecPCD(tk_Token, sym->type)); + PC_AppendShrink(sym->info.var.index); + TK_NextToken(); + } + break; + case SY_INTERNFUNC: + if(sym->info.internFunc.hasReturnValue == NO) + { + ERR_Error(ERR_EXPR_FUNC_NO_RET_VAL, YES); + } + ProcessInternFunc(sym); + break; + case SY_SCRIPTFUNC: + if(sym->info.scriptFunc.hasReturnValue == NO) + { + ERR_Error(ERR_EXPR_FUNC_NO_RET_VAL, YES); + } + ProcessScriptFunc(sym, NO); + break; + default: + ERR_Error(ERR_ILLEGAL_EXPR_IDENT, YES, tk_String); + TK_NextToken(); + break; + } + break; + case TK_LINESPECIAL: + ExprLineSpecial(); + break; + default: + ERR_Error(ERR_BAD_EXPR, YES); + TK_NextToken(); + break; + } +} + +static void ConstExprFactor(void) +{ + switch(tk_Token) + { + case TK_STRING: + if (ImportMode != IMPORT_Importing) + { + int strnum = STR_Find(tk_String); + if (ImportMode == IMPORT_Exporting) + { + pa_ConstExprIsString = YES; + } + PushExStk(strnum); + } + else + { + // Importing, so it doesn't matter + PushExStk(0); + } + TK_NextToken(); + break; + case TK_NUMBER: + PushExStk(tk_Number); + TK_NextToken(); + break; + case TK_LPAREN: + TK_NextToken(); + ExprLevA(); + if(tk_Token != TK_RPAREN) + { + ERR_Error(ERR_BAD_CONST_EXPR, YES); + TK_SkipPast(TK_RPAREN); + } + else + { + TK_NextToken(); + } + break; + case TK_NOT: + TK_NextToken(); + ConstExprFactor(); + SendExprCommand(PCD_NEGATELOGICAL); + break; + default: + ERR_Error(ERR_BAD_CONST_EXPR, YES); + PushExStk(0); + while(tk_Token != TK_COMMA && + tk_Token != TK_SEMICOLON && + tk_Token != TK_RPAREN) + { + TK_NextToken(); + } + break; + } +} + +//========================================================================== +// +// SendExprCommand +// +//========================================================================== + +static void SendExprCommand(pcd_t pcd) +{ + int operand2; + + if(ConstantExpression == NO) + { + PC_AppendCmd(pcd); + return; + } + switch(pcd) + { + case PCD_ADD: + PushExStk(PopExStk()+PopExStk()); + break; + case PCD_SUBTRACT: + operand2 = PopExStk(); + PushExStk(PopExStk()-operand2); + break; + case PCD_MULTIPLY: + PushExStk(PopExStk()*PopExStk()); + break; + case PCD_DIVIDE: + operand2 = PopExStk(); + PushExStk(PopExStk()/operand2); + break; + case PCD_MODULUS: + operand2 = PopExStk(); + PushExStk(PopExStk()%operand2); + break; + case PCD_EQ: + PushExStk(PopExStk() == PopExStk()); + break; + case PCD_NE: + PushExStk(PopExStk() != PopExStk()); + break; + case PCD_LT: + operand2 = PopExStk(); + PushExStk(PopExStk() >= operand2); + break; + case PCD_GT: + operand2 = PopExStk(); + PushExStk(PopExStk() <= operand2); + break; + case PCD_LE: + operand2 = PopExStk(); + PushExStk(PopExStk() > operand2); + break; + case PCD_GE: + operand2 = PopExStk(); + PushExStk(PopExStk() < operand2); + break; + case PCD_ANDLOGICAL: + PushExStk(PopExStk() && PopExStk()); + break; + case PCD_ORLOGICAL: + PushExStk(PopExStk() || PopExStk()); + break; + case PCD_ANDBITWISE: + PushExStk(PopExStk()&PopExStk()); + break; + case PCD_ORBITWISE: + PushExStk(PopExStk()|PopExStk()); + break; + case PCD_EORBITWISE: + PushExStk(PopExStk()^PopExStk()); + break; + case PCD_NEGATELOGICAL: + PushExStk(!PopExStk()); + break; + case PCD_LSHIFT: + operand2 = PopExStk(); + PushExStk(PopExStk()<>operand2); + break; + case PCD_UNARYMINUS: + PushExStk(-PopExStk()); + break; + default: + ERR_Exit(ERR_UNKNOWN_CONST_EXPR_PCD, YES); + break; + } +} + +//========================================================================== +// +// PushExStk +// +//========================================================================== + +static void PushExStk(int value) +{ + if(ExprStackIndex == EXPR_STACK_DEPTH) + { + ERR_Exit(ERR_EXPR_STACK_OVERFLOW, YES); + } + ExprStack[ExprStackIndex++] = value; +} + +//========================================================================== +// +// PopExStk +// +//========================================================================== + +static int PopExStk(void) +{ + if(ExprStackIndex < 1) + { + ERR_Error(ERR_EXPR_STACK_EMPTY, YES); + return 0; + } + return ExprStack[--ExprStackIndex]; +} + +//========================================================================== +// +// TokenToPCD +// +//========================================================================== + +static pcd_t TokenToPCD(tokenType_t token) +{ + int i; + static struct + { + tokenType_t token; + pcd_t pcd; + } operatorLookup[] = + { + { TK_ORLOGICAL, PCD_ORLOGICAL }, + { TK_ANDLOGICAL, PCD_ANDLOGICAL }, + { TK_ORBITWISE, PCD_ORBITWISE }, + { TK_EORBITWISE, PCD_EORBITWISE }, + { TK_ANDBITWISE, PCD_ANDBITWISE }, + { TK_EQ, PCD_EQ }, + { TK_NE, PCD_NE }, + { TK_LT, PCD_LT }, + { TK_LE, PCD_LE }, + { TK_GT, PCD_GT }, + { TK_GE, PCD_GE }, + { TK_LSHIFT, PCD_LSHIFT }, + { TK_RSHIFT, PCD_RSHIFT }, + { TK_PLUS, PCD_ADD }, + { TK_MINUS, PCD_SUBTRACT }, + { TK_ASTERISK, PCD_MULTIPLY }, + { TK_SLASH, PCD_DIVIDE }, + { TK_PERCENT, PCD_MODULUS }, + { TK_NONE, PCD_NOP } + }; + + for(i = 0; operatorLookup[i].token != TK_NONE; i++) + { + if(operatorLookup[i].token == token) + { + return operatorLookup[i].pcd; + } + } + return PCD_NOP; +} + +//========================================================================== +// +// GetPushVarPCD +// +//========================================================================== + +static pcd_t GetPushVarPCD(symbolType_t symType) +{ + switch(symType) + { + case SY_SCRIPTVAR: + return PCD_PUSHSCRIPTVAR; + case SY_MAPVAR: + return PCD_PUSHMAPVAR; + case SY_WORLDVAR: + return PCD_PUSHWORLDVAR; + case SY_GLOBALVAR: + return PCD_PUSHGLOBALVAR; + case SY_MAPARRAY: + return PCD_PUSHMAPARRAY; + case SY_WORLDARRAY: + return PCD_PUSHWORLDARRAY; + case SY_GLOBALARRAY: + return PCD_PUSHGLOBALARRAY; + default: + break; + } + return PCD_NOP; +} + +//========================================================================== +// +// GetIncDecPCD +// +//========================================================================== + +static pcd_t GetIncDecPCD(tokenType_t token, symbolType_t symbol) +{ + int i; + static struct + { + tokenType_t token; + symbolType_t symbol; + pcd_t pcd; + } incDecLookup[] = + { + { TK_INC, SY_SCRIPTVAR, PCD_INCSCRIPTVAR }, + { TK_INC, SY_MAPVAR, PCD_INCMAPVAR }, + { TK_INC, SY_WORLDVAR, PCD_INCWORLDVAR }, + { TK_INC, SY_GLOBALVAR, PCD_INCGLOBALVAR }, + { TK_INC, SY_MAPARRAY, PCD_INCMAPARRAY }, + { TK_INC, SY_WORLDARRAY, PCD_INCWORLDARRAY }, + { TK_INC, SY_GLOBALARRAY, PCD_INCGLOBALARRAY }, + + { TK_DEC, SY_SCRIPTVAR, PCD_DECSCRIPTVAR }, + { TK_DEC, SY_MAPVAR, PCD_DECMAPVAR }, + { TK_DEC, SY_WORLDVAR, PCD_DECWORLDVAR }, + { TK_DEC, SY_GLOBALVAR, PCD_DECGLOBALVAR }, + { TK_DEC, SY_MAPARRAY, PCD_DECMAPARRAY }, + { TK_DEC, SY_WORLDARRAY, PCD_DECWORLDARRAY }, + { TK_DEC, SY_GLOBALARRAY, PCD_DECGLOBALARRAY }, + + { TK_NONE, SY_DUMMY, PCD_NOP } + }; + + for(i = 0; incDecLookup[i].token != TK_NONE; i++) + { + if(incDecLookup[i].token == token + && incDecLookup[i].symbol == symbol) + { + return incDecLookup[i].pcd; + } + } + return PCD_NOP; +} + +//========================================================================== +// +// ParseArrayIndices +// +//========================================================================== + +static void ParseArrayIndices(symbolNode_t *sym, int requiredIndices) +{ + boolean warned = NO; + int i; + + if(requiredIndices > 0) + { + TK_TokenMustBe(TK_LBRACKET, ERR_MISSING_LBRACKET); + } + i = 0; + while(tk_Token == TK_LBRACKET) + { + TK_NextToken(); + if((sym->type == SY_MAPARRAY && i == requiredIndices) || + (sym->type != SY_MAPARRAY && i > 0)) + { + if (!warned) + { + warned = YES; + if(sym->info.array.ndim == requiredIndices) + { + ERR_Error(ERR_TOO_MANY_DIM_USED, YES, + sym->name, sym->info.array.ndim); + } + else + { + ERR_Error(ERR_NOT_A_CHAR_ARRAY, YES, sym->name, + sym->info.array.ndim, requiredIndices); + } + } + } + if(i > 0) + { + PC_AppendPushVal(sym->info.array.dimensions[i-1]); + PC_AppendCmd(PCD_MULTIPLY); + } + EvalExpression(); + if(i > 0) + { + PC_AppendCmd(PCD_ADD); + } + i++; + TK_TokenMustBe(TK_RBRACKET, ERR_MISSING_RBRACKET); + TK_NextToken(); + } + // if there were unspecified indices, multiply the offset by their sizes [JB] + if(requiredIndices < sym->info.array.ndim) + { + int i, mult = 1; + for(i = 0; i < sym->info.array.ndim - requiredIndices; ++i) + { + mult *= sym->info.array.dimensions[sym->info.array.ndim - 2 - i]; + } + if(mult > 1) + { + PC_AppendPushVal(mult); + PC_AppendCmd(PCD_MULTIPLY); + } + } +} + +static int *ProcessArrayLevel(int level, int *entry, int ndim, + int dims[MAX_ARRAY_DIMS], int muls[MAX_ARRAY_DIMS], char *name) +{ + int i; + + for(i = 0; i < dims[level-1]; ++i) + { + if(tk_Token == TK_COMMA) + { + entry += muls[level-1]; + TK_NextToken(); + } + else if(tk_Token == TK_RBRACE) + { + TK_NextToken(); + if(level > 1) + { + return entry + muls[level-2] - i; + } + else + { + return entry + (muls[0]*dims[0]) - i; + } + } + else + { + if(level == ndim) + { + if(tk_Token == TK_LBRACE) + { + ERR_Error(ERR_TOO_MANY_DIM_USED, YES, name, ndim); + SkipBraceBlock(0); + TK_NextToken(); + entry++; + } + else + { + *entry++ = EvalConstExpression(); + ArrayHasStrings |= pa_ConstExprIsString; + } + } + else + { + TK_TokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR); + TK_NextToken(); + entry = ProcessArrayLevel(level+1, entry, ndim, dims, muls, name); + } + if(i < dims[level-1]-1) + { + if(tk_Token != TK_RBRACE) + { + TK_TokenMustBe(TK_COMMA, ERR_MISSING_COMMA); + TK_NextToken(); + } + } + else + { + if(tk_Token != TK_COMMA) + { + TK_TokenMustBe(TK_RBRACE, ERR_MISSING_RBRACE_ARR); + } + else + { + TK_NextToken(); + } + } + } + } + TK_TokenMustBe(TK_RBRACE, ERR_MISSING_RBRACE_ARR); + TK_NextToken(); + return entry; +} + +//========================================================================== +// +// InitializeArray +// +//========================================================================== + +static void InitializeArray(symbolNode_t *sym, int dims[MAX_ARRAY_DIMS], int size) +{ + static int *entries = NULL; + static int lastsize = -1; + + if(lastsize < size) + { + entries = MS_Realloc(entries, sizeof(int)*size, ERR_OUT_OF_MEMORY); + lastsize = size; + } + memset(entries, 0, sizeof(int)*size); + + TK_NextTokenMustBe(TK_LBRACE, ERR_MISSING_LBRACE_ARR); + TK_NextToken(); + ArrayHasStrings = NO; + ProcessArrayLevel(1, entries, sym->info.array.ndim, dims, + sym->info.array.dimensions, sym->name); + if(ImportMode != IMPORT_Importing) + { + PC_InitArray(sym->info.array.index, entries, ArrayHasStrings); + } +} + +//========================================================================== +// +// DemandSymbol +// +//========================================================================== + +static symbolNode_t *DemandSymbol(char *name) +{ + symbolNode_t *sym; + + if((sym = SY_Find(name)) == NULL) + { + ERR_Exit(ERR_UNKNOWN_IDENTIFIER, YES, name); + } + return sym; +} + +//========================================================================== +// +// SpeculateSymbol +// +//========================================================================== + +static symbolNode_t *SpeculateSymbol(char *name, boolean hasReturn) +{ + symbolNode_t *sym; + + sym = SY_Find(name); + if(sym == NULL) + { + char name[MAX_IDENTIFIER_LENGTH]; + + strcpy (name, tk_String); + TK_NextToken(); + if(tk_Token == TK_LPAREN) + { // Looks like a function call + sym = SpeculateFunction(name, hasReturn); + TK_Undo(); + } + else + { + ERR_Exit(ERR_UNKNOWN_IDENTIFIER, YES, name); + } + } + return sym; +} + +//========================================================================== +// +// SpeculateFunction +// +// Add a temporary symbol for a function that is used before it is defined. +// +//========================================================================== + +static symbolNode_t *SpeculateFunction(const char *name, boolean hasReturn) +{ + symbolNode_t *sym; + + MS_Message(MSG_DEBUG, "---- SpeculateFunction %s ----\n", name); + sym = SY_InsertGlobal(tk_String, SY_SCRIPTFUNC); + sym->info.scriptFunc.predefined = YES; + sym->info.scriptFunc.hasReturnValue = hasReturn; + sym->info.scriptFunc.sourceLine = tk_Line; + sym->info.scriptFunc.sourceName = tk_SourceName; + sym->info.scriptFunc.argCount = -1; + sym->info.scriptFunc.funcNumber = 0; + return sym; +} + +//========================================================================== +// +// UnspeculateFunction +// +// Fills in function calls that were made before this function was defined. +// +//========================================================================== + +static void UnspeculateFunction(symbolNode_t *sym) +{ + prefunc_t *fillin; + prefunc_t **prev; + + prev = &FillinFunctions; + fillin = FillinFunctions; + while(fillin != NULL) + { + prefunc_t *next = fillin->next; + if(fillin->sym == sym) + { + if(fillin->argcount != sym->info.scriptFunc.argCount) + { + ERR_ErrorAt(fillin->source, fillin->line); + ERR_Error(ERR_FUNC_ARGUMENT_COUNT, YES, sym->name, + sym->info.scriptFunc.argCount, + sym->info.scriptFunc.argCount == 1 ? "" : "s"); + } + + if(pc_NoShrink) + { + PC_WriteLong(sym->info.scriptFunc.funcNumber, fillin->address); + } + else + { + PC_WriteByte((U_BYTE)sym->info.scriptFunc.funcNumber, fillin->address); + } + if(FillinFunctionsLatest == &fillin->next) + { + FillinFunctionsLatest = prev; + } + free (fillin); + *prev = next; + } + else + { + prev = &fillin->next; + } + fillin = next; + } +} + +//========================================================================== +// +// AddScriptFuncRef +// +//========================================================================== + +static void AddScriptFuncRef(symbolNode_t *sym, int address, int argcount) +{ + prefunc_t *fillin = MS_Alloc(sizeof(*fillin), ERR_OUT_OF_MEMORY); + fillin->next = NULL; + fillin->sym = sym; + fillin->address = address; + fillin->argcount = argcount; + fillin->line = tk_Line; + fillin->source = tk_SourceName; + *FillinFunctionsLatest = fillin; + FillinFunctionsLatest = &fillin->next; +} + +//========================================================================== +// +// Check for undefined functions +// +//========================================================================== + +static void CheckForUndefinedFunctions(void) +{ + prefunc_t *fillin = FillinFunctions; + + while(fillin != NULL) + { + ERR_ErrorAt(fillin->source, fillin->line); + ERR_Error(ERR_UNDEFINED_FUNC, YES, fillin->sym->name); + fillin = fillin->next; + } +} + +//========================================================================== +// +// SkipBraceBlock +// +// If depth is 0, it scans for the first { and then starts going from there. +// At exit, the terminating } is left as the current token. +// +//========================================================================== + +void SkipBraceBlock(int depth) +{ + if (depth == 0) + { + // Find first { + while(tk_Token != TK_LBRACE) + { + if(tk_Token == TK_EOF) + { + ERR_Exit(ERR_EOF, NO); + } + TK_NextToken(); + } + depth = 1; + } + // Match it with a } + do + { + TK_NextToken(); + if(tk_Token == TK_EOF) + { + ERR_Exit(ERR_EOF, NO); + } + else if (tk_Token == TK_LBRACE) + { + depth++; + } + else if (tk_Token == TK_RBRACE) + { + depth--; + } + } while (depth > 0); +} diff --git a/parse.h b/parse.h new file mode 100644 index 0000000..23294de --- /dev/null +++ b/parse.h @@ -0,0 +1,41 @@ + +//************************************************************************** +//** +//** parse.h +//** +//************************************************************************** + +#ifndef __PARSE_H__ +#define __PARSE_H__ + +// HEADER FILES ------------------------------------------------------------ + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +struct ScriptTypes +{ + const char *TypeName; + int TypeBase; + int TypeCount; +}; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void PA_Parse(void); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +extern int pa_ScriptCount; +extern struct ScriptTypes *pa_TypedScriptCounts; +extern int pa_MapVarCount; +extern int pa_WorldVarCount; +extern int pa_GlobalVarCount; +extern int pa_WorldArrayCount; +extern int pa_GlobalArrayCount; +extern enum ImportModes ImportMode; +extern boolean ExporterFlagged; +extern boolean pa_ConstExprIsString; + +#endif diff --git a/pcode.c b/pcode.c new file mode 100644 index 0000000..cf4f346 --- /dev/null +++ b/pcode.c @@ -0,0 +1,1422 @@ + +//************************************************************************** +//** +//** pcode.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "pcode.h" +#include "common.h" +#include "error.h" +#include "misc.h" +#include "strlist.h" +#include "token.h" +#include "symbol.h" +#include "parse.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +typedef struct scriptInfo_s +{ + U_WORD number; + U_BYTE type; + U_BYTE argCount; + U_WORD varCount; + U_WORD flags; + int address; + int srcLine; + boolean imported; +} scriptInfo_t; + +typedef struct functionInfo_s +{ + U_BYTE hasReturnValue; + U_BYTE argCount; + U_BYTE localCount; + int address; + int name; +} functionInfo_t; + +typedef struct mapVarInfo_s +{ + int initializer; + boolean isString; + char *name; + boolean imported; +} mapVarInfo_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static void GrowBuffer(void); +static void Append(void *buffer, size_t size); +static void Write(void *buffer, size_t size, int address); +static void Skip(size_t size); +static void CloseOld(void); +static void CloseNew(void); +static void CreateDummyScripts(void); +static void RecordDummyScripts(void); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int pc_Address; +byte *pc_Buffer; +byte *pc_BufferPtr; +int pc_ScriptCount; +int pc_FunctionCount; +boolean pc_NoShrink; +boolean pc_HexenCase; +boolean pc_WadAuthor = TRUE; +boolean pc_EncryptStrings; +int pc_LastAppendedCommand; +int pc_DummyAddress; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static size_t BufferSize; +static boolean ObjectOpened = NO; +static scriptInfo_t ScriptInfo[MAX_SCRIPT_COUNT]; +static functionInfo_t FunctionInfo[MAX_FUNCTION_COUNT]; +static int ArraySizes[MAX_MAP_VARIABLES]; +static int *ArrayInits[MAX_MAP_VARIABLES]; +static boolean ArrayOfStrings[MAX_MAP_VARIABLES]; +static int NumArrays; +static mapVarInfo_t MapVariables[MAX_MAP_VARIABLES]; +static boolean MapVariablesInit = NO; +static char ObjectName[MAX_FILE_NAME_LENGTH]; +static int ObjectFlags; +static int PushByteAddr; +static char Imports[MAX_IMPORTS][9]; +static int NumImports; +static boolean HaveScriptFlags; + +static char *PCDNames[PCODE_COMMAND_COUNT] = +{ + "PCD_NOP", + "PCD_TERMINATE", + "PCD_SUSPEND", + "PCD_PUSHNUMBER", + "PCD_LSPEC1", + "PCD_LSPEC2", + "PCD_LSPEC3", + "PCD_LSPEC4", + "PCD_LSPEC5", + "PCD_LSPEC1DIRECT", + "PCD_LSPEC2DIRECT", + "PCD_LSPEC3DIRECT", + "PCD_LSPEC4DIRECT", + "PCD_LSPEC5DIRECT", + "PCD_ADD", + "PCD_SUBTRACT", + "PCD_MULTIPLY", + "PCD_DIVIDE", + "PCD_MODULUS", + "PCD_EQ", + "PCD_NE", + "PCD_LT", + "PCD_GT", + "PCD_LE", + "PCD_GE", + "PCD_ASSIGNSCRIPTVAR", + "PCD_ASSIGNMAPVAR", + "PCD_ASSIGNWORLDVAR", + "PCD_PUSHSCRIPTVAR", + "PCD_PUSHMAPVAR", + "PCD_PUSHWORLDVAR", + "PCD_ADDSCRIPTVAR", + "PCD_ADDMAPVAR", + "PCD_ADDWORLDVAR", + "PCD_SUBSCRIPTVAR", + "PCD_SUBMAPVAR", + "PCD_SUBWORLDVAR", + "PCD_MULSCRIPTVAR", + "PCD_MULMAPVAR", + "PCD_MULWORLDVAR", + "PCD_DIVSCRIPTVAR", + "PCD_DIVMAPVAR", + "PCD_DIVWORLDVAR", + "PCD_MODSCRIPTVAR", + "PCD_MODMAPVAR", + "PCD_MODWORLDVAR", + "PCD_INCSCRIPTVAR", + "PCD_INCMAPVAR", + "PCD_INCWORLDVAR", + "PCD_DECSCRIPTVAR", + "PCD_DECMAPVAR", + "PCD_DECWORLDVAR", + "PCD_GOTO", + "PCD_IFGOTO", + "PCD_DROP", + "PCD_DELAY", + "PCD_DELAYDIRECT", + "PCD_RANDOM", + "PCD_RANDOMDIRECT", + "PCD_THINGCOUNT", + "PCD_THINGCOUNTDIRECT", + "PCD_TAGWAIT", + "PCD_TAGWAITDIRECT", + "PCD_POLYWAIT", + "PCD_POLYWAITDIRECT", + "PCD_CHANGEFLOOR", + "PCD_CHANGEFLOORDIRECT", + "PCD_CHANGECEILING", + "PCD_CHANGECEILINGDIRECT", + "PCD_RESTART", + "PCD_ANDLOGICAL", + "PCD_ORLOGICAL", + "PCD_ANDBITWISE", + "PCD_ORBITWISE", + "PCD_EORBITWISE", + "PCD_NEGATELOGICAL", + "PCD_LSHIFT", + "PCD_RSHIFT", + "PCD_UNARYMINUS", + "PCD_IFNOTGOTO", + "PCD_LINESIDE", + "PCD_SCRIPTWAIT", + "PCD_SCRIPTWAITDIRECT", + "PCD_CLEARLINESPECIAL", + "PCD_CASEGOTO", + "PCD_BEGINPRINT", + "PCD_ENDPRINT", + "PCD_PRINTSTRING", + "PCD_PRINTNUMBER", + "PCD_PRINTCHARACTER", + "PCD_PLAYERCOUNT", + "PCD_GAMETYPE", + "PCD_GAMESKILL", + "PCD_TIMER", + "PCD_SECTORSOUND", + "PCD_AMBIENTSOUND", + "PCD_SOUNDSEQUENCE", + "PCD_SETLINETEXTURE", + "PCD_SETLINEBLOCKING", + "PCD_SETLINESPECIAL", + "PCD_THINGSOUND", + "PCD_ENDPRINTBOLD", +// [RH] End of Hexen p-codes + "PCD_ACTIVATORSOUND", + "PCD_LOCALAMBIENTSOUND", + "PCD_SETLINEMONSTERBLOCKING", +// [BC] Start of new pcodes + "PCD_PLAYERBLUESKULL", + "PCD_PLAYERREDSKULL", + "PCD_PLAYERYELLOWSKULL", + "PCD_PLAYERMASTERSKULL", + "PCD_PLAYERBLUECARD", + "PCD_PLAYERREDCARD", + "PCD_PLAYERYELLOWCARD", + "PCD_PLAYERMASTERCARD", + "PCD_PLAYERBLACKSKULL", + "PCD_PLAYERSILVERSKULL", + "PCD_PLAYERGOLDSKULL", + "PCD_PLAYERBLACKCARD", + "PCD_PLAYERSILVERCARD", + "PCD_PLAYERONTEAM", + "PCD_PLAYERTEAM", + "PCD_PLAYERHEALTH", + "PCD_PLAYERARMORPOINTS", + "PCD_PLAYERFRAGS", + "PCD_PLAYEREXPERT", + "PCD_BLUETEAMCOUNT", + "PCD_REDTEAMCOUNT", + "PCD_BLUETEAMSCORE", + "PCD_REDTEAMSCORE", + "PCD_ISONEFLAGCTF", + "PCD_LSPEC6", + "PCD_LSPEC6DIRECT", + "PCD_PRINTNAME", + "PCD_MUSICCHANGE", + "PCD_CONSOLECOMMANDDIRECT", + "PCD_CONSOLECOMMAND", + "PCD_SINGLEPLAYER", +// [RH] End of Skull Tag p-codes + "PCD_FIXEDMUL", + "PCD_FIXEDDIV", + "PCD_SETGRAVITY", + "PCD_SETGRAVITYDIRECT", + "PCD_SETAIRCONTROL", + "PCD_SETAIRCONTROLDIRECT", + "PCD_CLEARINVENTORY", + "PCD_GIVEINVENTORY", + "PCD_GIVEINVENTORYDIRECT", + "PCD_TAKEINVENTORY", + "PCD_TAKEINVENTORYDIRECT", + "PCD_CHECKINVENTORY", + "PCD_CHECKINVENTORYDIRECT", + "PCD_SPAWN", + "PCD_SPAWNDIRECT", + "PCD_SPAWNSPOT", + "PCD_SPAWNSPOTDIRECT", + "PCD_SETMUSIC", + "PCD_SETMUSICDIRECT", + "PCD_LOCALSETMUSIC", + "PCD_LOCALSETMUSICDIRECT", + "PCD_PRINTFIXED", + "PCD_PRINTLOCALIZED", + "PCD_MOREHUDMESSAGE", + "PCD_OPTHUDMESSAGE", + "PCD_ENDHUDMESSAGE", + "PCD_ENDHUDMESSAGEBOLD", + "PCD_SETSTYLE", + "PCD_SETSTYLEDIRECT", + "PCD_SETFONT", + "PCD_SETFONTDIRECT", + "PCD_PUSHBYTE", + "PCD_LSPEC1DIRECTB", + "PCD_LSPEC2DIRECTB", + "PCD_LSPEC3DIRECTB", + "PCD_LSPEC4DIRECTB", + "PCD_LSPEC5DIRECTB", + "PCD_DELAYDIRECTB", + "PCD_RANDOMDIRECTB", + "PCD_PUSHBYTES", + "PCD_PUSH2BYTES", + "PCD_PUSH3BYTES", + "PCD_PUSH4BYTES", + "PCD_PUSH5BYTES", + "PCD_SETTHINGSPECIAL", + "PCD_ASSIGNGLOBALVAR", + "PCD_PUSHGLOBALVAR", + "PCD_ADDGLOBALVAR", + "PCD_SUBGLOBALVAR", + "PCD_MULGLOBALVAR", + "PCD_DIVGLOBALVAR", + "PCD_MODGLOBALVAR", + "PCD_INCGLOBALVAR", + "PCD_DECGLOBALVAR", + "PCD_FADETO", + "PCD_FADERANGE", + "PCD_CANCELFADE", + "PCD_PLAYMOVIE", + "PCD_SETFLOORTRIGGER", + "PCD_SETCEILINGTRIGGER", + "PCD_GETACTORX", + "PCD_GETACTORY", + "PCD_GETACTORZ", + "PCD_STARTTRANSLATION", + "PCD_TRANSLATIONRANGE1", + "PCD_TRANSLATIONRANGE2", + "PCD_ENDTRANSLATION", + "PCD_CALL", + "PCD_CALLDISCARD", + "PCD_RETURNVOID", + "PCD_RETURNVAL", + "PCD_PUSHMAPARRAY", + "PCD_ASSIGNMAPARRAY", + "PCD_ADDMAPARRAY", + "PCD_SUBMAPARRAY", + "PCD_MULMAPARRAY", + "PCD_DIVMAPARRAY", + "PCD_MODMAPARRAY", + "PCD_INCMAPARRAY", + "PCD_DECMAPARRAY", + "PCD_DUP", + "PCD_SWAP", + "PCD_WRITETOINI", + "PCD_GETFROMINI", + "PCD_SIN", + "PCD_COS", + "PCD_VECTORANGLE", + "PCD_CHECKWEAPON", + "PCD_SETWEAPON", + "PCD_TAGSTRING", + "PCD_PUSHWORLDARRAY", + "PCD_ASSIGNWORLDARRAY", + "PCD_ADDWORLDARRAY", + "PCD_SUBWORLDARRAY", + "PCD_MULWORLDARRAY", + "PCD_DIVWORLDARRAY", + "PCD_MODWORLDARRAY", + "PCD_INCWORLDARRAY", + "PCD_DECWORLDARRAY", + "PCD_PUSHGLOBALARRAY", + "PCD_ASSIGNGLOBALARRAY", + "PCD_ADDGLOBALARRAY", + "PCD_SUBGLOBALARRAY", + "PCD_MULGLOBALARRAY", + "PCD_DIVGLOBALARRAY", + "PCD_MODGLOBALARRAY", + "PCD_INCGLOBALARRAY", + "PCD_DECGLOBALARRAY", + "PCD_SETMARINEWEAPON", + "PCD_SETACTORPROPERTY", + "PCD_GETACTORPROPERTY", + "PCD_PLAYERNUMBER", + "PCD_ACTIVATORTID", + "PCD_SETMARINESPRITE", + "PCD_GETSCREENWIDTH", + "PCD_GETSCREENHEIGHT", + "PCD_THING_PROJECTILE2", + "PCD_STRLEN", + "PCD_SETHUDSIZE", + "PCD_GETCVAR", + "PCD_CASEGOTOSORTED", + "PCD_SETRESULTVALUE", + "PCD_GETLINEROWOFFSET", + "PCD_GETACTORFLOORZ", + "PCD_GETACTORANGLE", + "PCD_GETSECTORFLOORZ", + "PCD_GETSECTORCEILINGZ", + "PCD_LSPEC5RESULT", + "PCD_GETSIGILPIECES", + "PCD_GELEVELINFO", + "PCD_CHANGESKY", + "PCD_PLAYERINGAME", + "PCD_PLAYERISBOT", + "PCD_SETCAMERATOTEXTURE", + "PCD_ENDLOG", + "PCD_GETAMMOCAPACITY", + "PCD_SETAMMOCAPACITY", +// [JB] start of new pcodes + "PCD_PRINTMAPCHARARRAY", + "PCD_PRINTWORLDCHARARRAY", + "PCD_PRINTGLOBALCHARARRAY", +// [JB] end of new pcodes + "PCD_SETACTORANGLE", + "PCD_GRABINPUT", + "PCD_SETMOUSEPOINTER", + "PCD_MOVEMOUSEPOINTER" +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// PC_OpenObject +// +//========================================================================== + +void PC_OpenObject(char *name, size_t size, int flags) +{ + if(ObjectOpened == YES) + { + PC_CloseObject(); + } + if(strlen(name) >= MAX_FILE_NAME_LENGTH) + { + ERR_Exit(ERR_FILE_NAME_TOO_LONG, NO, name); + } + strcpy(ObjectName, name); + pc_Buffer = MS_Alloc(size, ERR_ALLOC_PCODE_BUFFER); + pc_BufferPtr = pc_Buffer; + pc_Address = 0; + ObjectFlags = flags; + BufferSize = size; + pc_ScriptCount = 0; + ObjectOpened = YES; + PC_AppendString("ACS"); + PC_SkipLong(); // Script table offset +} + +//========================================================================== +// +// PC_CloseObject +// +//========================================================================== + +void PC_CloseObject(void) +{ + MS_Message(MSG_DEBUG, "---- PC_CloseObject ----\n"); + while (pc_Address & 3) + { + PC_AppendByte (0); + } + if(!pc_NoShrink || (NumLanguages > 1) || (NumStringLists > 0) || + (pc_FunctionCount > 0) || MapVariablesInit || NumArrays != 0 || + pc_EncryptStrings || NumImports != 0 || HaveScriptFlags) + { + CloseNew(); + } + else + { + CloseOld(); + } + if(MS_SaveFile(ObjectName, pc_Buffer, pc_Address) == FALSE) + { + ERR_Exit(ERR_SAVE_OBJECT_FAILED, NO); + } +} + +//========================================================================== +// +// CloseOld +// +//========================================================================== + +static void CloseOld(void) +{ + int i; + + STR_WriteStrings(); + PC_WriteLong((U_LONG)pc_Address, 4); + PC_AppendLong((U_LONG)pc_ScriptCount); + for(i = 0; i < pc_ScriptCount; ++i) + { + scriptInfo_t *info = &ScriptInfo[i]; + MS_Message(MSG_DEBUG, "Script %d, address = %d, arg count = %d\n", + info->number, info->address, info->argCount); + PC_AppendLong((U_LONG)(info->number + info->type * 1000)); + PC_AppendLong((U_LONG)info->address); + PC_AppendLong((U_LONG)info->argCount); + } + STR_WriteList(); +} + +//========================================================================== +// +// CloseNew +// +// Creates a new-format ACS file. For programs that don't know any better, +// this will look just like an old ACS file with no scripts or strings but +// with some extra junk in the middle. Both WadAuthor and DeePsea will not +// accept ACS files that do not have the ACS\0 header. Worse, WadAuthor +// will hang if the file begins with ACS\0 but does not look like something +// that might have been created with Raven's ACC. Thus, the chunks live in +// the string block, and there are two 0 dwords at the end of the file. +// +//========================================================================== + +static void CloseNew(void) +{ + int i, j, count; + int chunkStart; + + if(pc_WadAuthor) + { + CreateDummyScripts(); + } + + chunkStart = pc_Address; + + // Only write out those scripts that this acs file actually provides. + for(i = j = 0; i < pc_ScriptCount; ++i) + { + if(!ScriptInfo[i].imported) + { + ++j; + } + } + if(j > 0) + { + PC_Append("SPTR", 4); + PC_AppendLong(j * 8); + for(i = 0; i < pc_ScriptCount; i++) + { + scriptInfo_t *info = &ScriptInfo[i]; + if(!info->imported) + { + MS_Message(MSG_DEBUG, "Script %d, address = %d, arg count = %d\n", + info->number, info->address, info->argCount); + PC_AppendWord(info->number); + PC_AppendByte(info->type); + PC_AppendByte(info->argCount); + PC_AppendLong((U_LONG)info->address); + } + } + } + + // If any scripts have more than the maximum number of arguments, output them. + for(i = j = 0; i < pc_ScriptCount; ++i) + { + if(!ScriptInfo[i].imported && ScriptInfo[i].varCount > MAX_SCRIPT_VARIABLES) + { + ++j; + } + } + if(j > 0) + { + PC_Append("SVCT", 4); + PC_AppendLong(j * 4); + for(i = 0; i < pc_ScriptCount; ++i) + { + scriptInfo_t *info = &ScriptInfo[i]; + if(!info->imported && info->varCount > MAX_SCRIPT_VARIABLES) + { + MS_Message(MSG_DEBUG, "Script %d, var count = %d\n", + info->number, info->varCount); + PC_AppendWord(info->number); + PC_AppendWord(info->varCount); + } + } + } + + // Write script flags in a separate chunk, so older ZDooms don't get confused + for(i = j = 0; i < pc_ScriptCount; ++i) + { + if(!ScriptInfo[i].imported && ScriptInfo[i].flags != 0) + { + ++j; + } + } + if (j > 0) + { + PC_Append("SFLG", 4); + PC_AppendLong(j * 4); + for(i = 0; i < pc_ScriptCount; ++i) + { + scriptInfo_t *info = &ScriptInfo[i]; + if(!info->imported && info->flags != 0) + { + PC_AppendWord(info->number); + PC_AppendWord(info->flags); + } + } + } + + if(pc_FunctionCount > 0) + { + PC_Append("FUNC", 4); + PC_AppendLong(pc_FunctionCount * 8); + for(i = 0; i < pc_FunctionCount; ++i) + { + functionInfo_t *info = &FunctionInfo[i]; + MS_Message(MSG_DEBUG, "Function %d:%s, address = %d, arg count = %d, var count = %d\n", + i, STR_GetString(STRLIST_FUNCTIONS, info->name), + info->address, info->argCount, info->localCount); + PC_AppendByte(info->argCount); + PC_AppendByte(info->localCount); + PC_AppendByte((U_BYTE)(info->hasReturnValue?1:0)); + PC_AppendByte(0); + PC_AppendLong((U_LONG)info->address); + } + STR_WriteListChunk(STRLIST_FUNCTIONS, MAKE4CC('F','N','A','M'), NO); + } + + if(NumLanguages > 1) + { + for(i = 0; i < NumLanguages; i++) + { + STR_WriteChunk(i, pc_EncryptStrings); + } + } + else if(STR_ListSize(0) > 0) + { + STR_WriteChunk(0, pc_EncryptStrings); + } + + STR_WriteListChunk(STRLIST_PICS, MAKE4CC('P','I','C','S'), NO); + if(MapVariablesInit) + { + int j; + + for(i = 0; i < pa_MapVarCount; ++i) + { + if(MapVariables[i].initializer != 0) + break; + } + for(j = pa_MapVarCount-1; j > i; --j) + { + if(MapVariables[j].initializer != 0) + break; + } + ++j; + + if (i < j) + { + PC_Append("MINI", 4); + PC_AppendLong((j-i)*4+4); + PC_AppendLong(i); // First map var defined + for(; i < j; ++i) + { + PC_AppendLong(MapVariables[i].initializer); + } + } + } + + // If this is a library, record which map variables are + // initialized with strings. + if(ImportMode == IMPORT_Exporting) + { + count = 0; + + for(i = 0; i < pa_MapVarCount; ++i) + { + if(MapVariables[i].isString) + { + ++count; + } + } + if(count > 0) + { + PC_Append("MSTR", 4); + PC_AppendLong(count*4); + for(i = 0; i < pa_MapVarCount; ++i) + { + if(MapVariables[i].isString) + { + PC_AppendLong(i); + } + } + } + + // Now do the same thing for arrays. + for(count = 0, i = 0; i < pa_MapVarCount; ++i) + { + if(ArrayOfStrings[i]) + { + ++count; + } + } + if(count > 0) + { + PC_Append("ASTR", 4); + PC_AppendLong(count*4); + for(i = 0; i < pa_MapVarCount; ++i) + { + if(ArrayOfStrings[i]) + { + PC_AppendLong(i); + } + } + } + } + + // Publicize the names of map variables in a library. + if(ImportMode == IMPORT_Exporting) + { + for(i = 0; i < pa_MapVarCount; ++i) + { + if(!MapVariables[i].imported) + { + STR_AppendToList(STRLIST_MAPVARS, MapVariables[i].name); + } + else + { + STR_AppendToList(STRLIST_MAPVARS, NULL); + } + } + STR_WriteListChunk(STRLIST_MAPVARS, MAKE4CC('M','E','X','P'), NO); + } + + // Record the names of imported map variables + count = 0; + for(i = 0; i < pa_MapVarCount; ++i) + { + if(MapVariables[i].imported && !ArraySizes[i]) + { + count += 5 + strlen(MapVariables[i].name); + } + } + if(count > 0) + { + PC_Append("MIMP", 4); + PC_AppendLong(count); + for(i = 0; i < pa_MapVarCount; ++i) + { + if(MapVariables[i].imported && !ArraySizes[i]) + { + PC_AppendLong(i); + PC_AppendString(MapVariables[i].name); + } + } + } + + if(NumArrays) + { + int count; + + // Arrays defined here + for(count = 0, i = 0; i < pa_MapVarCount; ++i) + { + if(ArraySizes[i] && !MapVariables[i].imported) + { + ++count; + } + } + if(count) + { + PC_Append("ARAY", 4); + PC_AppendLong(count*8); + for(i = 0; i < pa_MapVarCount; ++i) + { + if(ArraySizes[i] && !MapVariables[i].imported) + { + PC_AppendLong(i); + PC_AppendLong(ArraySizes[i]); + } + } + for(i = 0; i < pa_MapVarCount; ++i) + { + if(ArrayInits[i]) + { + int j; + + PC_Append("AINI", 4); + PC_AppendLong(ArraySizes[i]*4+4); + PC_AppendLong((U_LONG)i); + for(j = 0; j < ArraySizes[i]; ++j) + { + PC_AppendLong((U_LONG)ArrayInits[i][j]); + } + } + } + } + + // Arrays imported from elsewhere + for(count = 0, j = i = 0; i < pa_MapVarCount; ++i) + { + if(ArraySizes[i] && MapVariables[i].imported) + { + count += 9 + strlen(MapVariables[i].name); + ++j; + } + } + if(count) + { + PC_Append("AIMP", 4); + PC_AppendLong(count+4); + PC_AppendLong(j); + for(i = 0; i < pa_MapVarCount; ++i) + { + if(ArraySizes[i] && MapVariables[i].imported) + { + PC_AppendLong(i); + PC_AppendLong(ArraySizes[i]); + PC_AppendString(MapVariables[i].name); + } + } + } + } + + // Add a dummy chunk to indicate if this object is a library. + if(ImportMode == IMPORT_Exporting) + { + PC_Append("ALIB", 4); + PC_AppendLong(0); + } + + // Record libraries imported by this object. + if(NumImports > 0) + { + count = 0; + for(i = 0; i < NumImports; ++i) + { + count += strlen(Imports[i]) + 1; + } + if(count > 0) + { + PC_Append("LOAD", 4); + PC_AppendLong(count); + for(i = 0; i < NumImports; ++i) + { + PC_AppendString(Imports[i]); + } + } + } + + PC_AppendLong((U_LONG)chunkStart); + if(pc_NoShrink) + { + PC_Append("ACSE", 4); + } + else + { + PC_Append("ACSe", 4); + } + PC_WriteLong((U_LONG)pc_Address, 4); + + // WadAuthor compatibility when creating a library is pointless, because + // that editor does not know anything about libraries and will never + // find their scripts ever. + if(pc_WadAuthor && ImportMode != IMPORT_Exporting) + { + RecordDummyScripts(); + } + else + { + PC_AppendLong(0); + } + PC_AppendLong(0); +} + +//========================================================================== +// +// CreateDummyScripts +// +//========================================================================== + +static void CreateDummyScripts(void) +{ + int i; + + MS_Message(MSG_DEBUG, "Creating dummy scripts to make WadAuthor happy.\n"); + if(pc_Address%4 != 0) + { // Need to align + U_LONG pad = 0; + PC_Append((void *)&pad, 4-(pc_Address%4)); + } + pc_DummyAddress = pc_Address; + for(i = 0; i < pc_ScriptCount; ++i) + { + if(!ScriptInfo[i].imported) + { + PC_AppendCmd(PCD_TERMINATE); + if(!pc_NoShrink) + { + PC_AppendCmd(PCD_NOP); + PC_AppendCmd(PCD_NOP); + PC_AppendCmd(PCD_NOP); + } + } + } +} + +//========================================================================== +// +// RecordDummyScripts +// +//========================================================================== + +static void RecordDummyScripts(void) +{ + int i, count; + + for(i = count = 0; i < pc_ScriptCount; ++i) + { + if(!ScriptInfo[i].imported) + { + ++count; + } + } + PC_AppendLong((U_LONG)count); + for(i = 0; i < pc_ScriptCount; ++i) + { + scriptInfo_t *info = &ScriptInfo[i]; + if(!info->imported) + { + MS_Message(MSG_DEBUG, "Dummy script %d, address = %d, arg count = %d\n", + info->number, info->address, info->argCount); + PC_AppendLong((U_LONG)info->number); + PC_AppendLong((U_LONG)pc_DummyAddress + i*4); + PC_AppendLong((U_LONG)info->argCount); + } + } +} + +//========================================================================== +// +// GrowBuffer +// +//========================================================================== + +void GrowBuffer(void) +{ + ptrdiff_t buffpos = pc_BufferPtr - pc_Buffer; + + BufferSize *= 2; + pc_Buffer = MS_Realloc(pc_Buffer, BufferSize, ERR_PCODE_BUFFER_OVERFLOW); + pc_BufferPtr = pc_Buffer + buffpos; +} + +//========================================================================== +// +// PC_Append functions +// +//========================================================================== + +static void Append(void *buffer, size_t size) +{ + if (ImportMode != IMPORT_Importing) + { + if(pc_Address+size > BufferSize) + { + GrowBuffer (); + } + memcpy(pc_BufferPtr, buffer, size); + pc_BufferPtr += size; + pc_Address += size; + } +} + +void PC_Append(void *buffer, size_t size) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "AD> %06d = (%d bytes)\n", pc_Address, size); + Append(buffer, size); + } +} + +void PC_AppendByte(U_BYTE val) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "AB> %06d = %d\n", pc_Address, val); + Append(&val, sizeof(U_BYTE)); + } +} + +void PC_AppendWord(U_WORD val) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "AW> %06d = %d\n", pc_Address, val); + val = MS_LittleUWORD(val); + Append(&val, sizeof(U_WORD)); + } +} + +void PC_AppendLong(U_LONG val) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "AL> %06d = %d\n", pc_Address, val); + val = MS_LittleULONG(val); + Append(&val, sizeof(U_LONG)); + } +} + +void PC_AppendString(char *string) +{ + if (ImportMode != IMPORT_Importing) + { + int length; + + length = strlen(string)+1; + MS_Message(MSG_DEBUG, "AS> %06d = \"%s\" (%d bytes)\n", + pc_Address, string, length); + Append(string, length); + } +} + +void PC_AppendCmd(pcd_t command) +{ + if (ImportMode != IMPORT_Importing) + { + pc_LastAppendedCommand = command; + if (pc_NoShrink) + { + MS_Message(MSG_DEBUG, "AC> %06d = #%d:%s\n", pc_Address, + command, PCDNames[command]); + command = MS_LittleULONG(command); + Append(&command, sizeof(U_LONG)); + } + else + { + U_BYTE cmd; + if (command != PCD_PUSHBYTE && PushByteAddr) + { // Maybe shrink a PCD_PUSHBYTE sequence into PCD_PUSHBYTES + int runlen = (pc_Address - PushByteAddr) / 2; + int i; + + if (runlen > 5) + { + pc_Buffer[PushByteAddr] = PCD_PUSHBYTES; + for (i = 0; i < runlen; i++) + { + pc_Buffer[PushByteAddr+i+2] = pc_Buffer[PushByteAddr+i*2+1]; + } + pc_Buffer[PushByteAddr+1] = runlen; + pc_Address = PushByteAddr + runlen + 2; + pc_BufferPtr = pc_Buffer + pc_Address; + MS_Message (MSG_DEBUG, "AC> Last %d PCD_PUSHBYTEs changed to #%d:PCD_PUSHBYTES\n", + runlen, PCD_PUSHBYTES); + } + else if (runlen > 1) + { + pc_Buffer[PushByteAddr] = PCD_PUSH2BYTES + runlen - 2; + for (i = 1; i < runlen; i++) + { + pc_Buffer[PushByteAddr+1+i] = pc_Buffer[PushByteAddr+1+i*2]; + } + pc_Address = PushByteAddr + runlen + 1; + pc_BufferPtr = pc_Buffer + pc_Address; + MS_Message (MSG_DEBUG, "AC> Last %d PCD_PUSHBYTEs changed to #%d:PCD_PUSH%dBYTES\n", + runlen, PCD_PUSH2BYTES+runlen-2, runlen); + } + PushByteAddr = 0; + } + else if (command == PCD_PUSHBYTE && PushByteAddr == 0) + { // Remember the first PCD_PUSHBYTE, in case there are more + PushByteAddr = pc_Address; + } + MS_Message(MSG_DEBUG, "AC> %06d = #%d:%s\n", pc_Address, + command, PCDNames[command]); + + if (command < 256-16) + { + cmd = command; + Append(&cmd, sizeof(U_BYTE)); + } + else + { + // Room for expansion: The top 16 pcodes in the [0,255] + // range select a set of pcodes, and the next byte is + // the pcode in that set. + cmd = ((command - (256-16)) >> 8) + (256-16); + Append(&cmd, sizeof(U_BYTE)); + cmd = (command - (256-16)) & 255; + Append(&cmd, sizeof(U_BYTE)); + } + } + } +} + +//========================================================================== +// +// PC_AppendShrink +// +//========================================================================== + +void PC_AppendShrink(U_BYTE val) +{ + if(pc_NoShrink) + { + PC_AppendLong(val); + } + else + { + PC_AppendByte(val); + } +} + +//========================================================================== +// +// PC_AppendPushVal +// +//========================================================================== + +void PC_AppendPushVal(U_LONG val) +{ + if(pc_NoShrink || val > 255) + { + PC_AppendCmd(PCD_PUSHNUMBER); + PC_AppendLong(val); + } + else + { + PC_AppendCmd(PCD_PUSHBYTE); + PC_AppendByte((U_BYTE)val); + } +} + +//========================================================================== +// +// PC_Write functions +// +//========================================================================== + +static void Write(void *buffer, size_t size, int address) +{ + if (ImportMode != IMPORT_Importing) + { + if(address+size > BufferSize) + { + GrowBuffer(); + } + memcpy(pc_Buffer+address, buffer, size); + } +} + +void PC_Write(void *buffer, size_t size, int address) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "WD> %06d = (%d bytes)\n", address, size); + Write(buffer, size, address); + } +} + +void PC_WriteByte(U_BYTE val, int address) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "WB> %06d = %d\n", address, val); + Write(&val, sizeof(U_BYTE), address); + } +} + +/* +void PC_WriteWord(U_WORD val, int address) +{ + MS_Message(MSG_DEBUG, "WW> %06d = %d\n", address, val); + val = MS_LittleUWORD(val); + Write(&val, sizeof(U_WORD), address); +} +*/ + +void PC_WriteLong(U_LONG val, int address) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "WL> %06d = %d\n", address, val); + val = MS_LittleULONG(val); + Write(&val, sizeof(U_LONG), address); + } + pc_LastAppendedCommand = PCD_NOP; +} + +void PC_WriteString(char *string, int address) +{ + if (ImportMode != IMPORT_Importing) + { + int length; + + length = strlen(string)+1; + MS_Message(MSG_DEBUG, "WS> %06d = \"%s\" (%d bytes)\n", + address, string, length); + Write(string, length, address); + } +} + +void PC_WriteCmd(pcd_t command, int address) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "WC> %06d = #%d:%s\n", address, + command, PCDNames[command]); + command = MS_LittleULONG(command); + Write(&command, sizeof(U_LONG), address); + } +} + +//========================================================================== +// +// PC_Skip functions +// +//========================================================================== + +static void Skip(size_t size) +{ + if (ImportMode != IMPORT_Importing) + { + if(pc_Address+size > BufferSize) + { + GrowBuffer(); + } + pc_BufferPtr += size; + pc_Address += size; + } +} + +void PC_Skip(size_t size) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "SD> %06d (skip %d bytes)\n", pc_Address, size); + Skip(size); + } +} + +/* +void PC_SkipByte(void) +{ + MS_Message(MSG_DEBUG, "SB> %06d (skip byte)\n", pc_Address); + Skip(sizeof(U_BYTE)); +} +*/ + +/* +void PC_SkipWord(void) +{ + MS_Message(MSG_DEBUG, "SW> %06d (skip word)\n", pc_Address); + Skip(sizeof(U_WORD)); +} +*/ + +void PC_SkipLong(void) +{ + if (ImportMode != IMPORT_Importing) + { + MS_Message(MSG_DEBUG, "SL> %06d (skip long)\n", pc_Address); + Skip(sizeof(U_LONG)); + } +} + +//========================================================================== +// +// PC_PutMapVariable +// +//========================================================================== + +void PC_PutMapVariable(int index, int value) +{ + if(index < MAX_MAP_VARIABLES) + { + MapVariables[index].isString = pa_ConstExprIsString; + MapVariables[index].initializer = value; + MapVariablesInit = YES; + } +} + +//========================================================================== +// +// PC_NameMapVariable +// +//========================================================================== + +void PC_NameMapVariable(int index, symbolNode_t *sym) +{ + if(index < MAX_MAP_VARIABLES) + { + MapVariables[index].name = sym->name; + MapVariables[index].imported = sym->imported; + } +} + +//========================================================================== +// +// PC_AddScript +// +//========================================================================== + +void PC_AddScript(int inNumber, int argCount) +{ + scriptInfo_t *script; + int i; + U_BYTE type; + U_WORD flags; + U_WORD number; + + type = (inNumber & 65535) / 1000; + number = (inNumber & 65535) % 1000; + flags = (inNumber >> 16) & 65535; + + if (flags != 0) + { + HaveScriptFlags = YES; + } + + for (i = 0; i < pc_ScriptCount; i++) + { + if (ScriptInfo[i].number == number) + { + ERR_Error(ERR_SCRIPT_ALREADY_DEFINED, YES); + } + } + if(pc_ScriptCount == MAX_SCRIPT_COUNT) + { + ERR_Error(ERR_TOO_MANY_SCRIPTS, YES); + } + else + { + script = &ScriptInfo[pc_ScriptCount]; + script->number = number; + script->type = type; + script->address = (ImportMode == IMPORT_Importing) ? 0 : pc_Address; + script->argCount = argCount; + script->flags = flags; + script->srcLine = tk_Line; + script->imported = (ImportMode == IMPORT_Importing) ? YES : NO; + pc_ScriptCount++; + } +} + +//========================================================================== +// +// PC_SetScriptVarCount +// +// Sets the number of local variables used by a script, including +// arguments. +// +//========================================================================== + +void PC_SetScriptVarCount(int inNumber, int varCount) +{ + int i; + U_BYTE type; + U_WORD number; + + type = (inNumber & 65535) / 1000; + number = (inNumber & 65535) % 1000; + + for(i = 0; i < pc_ScriptCount; i++) + { + if(ScriptInfo[i].number == number) + { + ScriptInfo[i].varCount = varCount; + break; + } + } +} + +//========================================================================== +// +// PC_AddFunction +// +//========================================================================== + +void PC_AddFunction(symbolNode_t *sym) +{ + functionInfo_t *function; + + if(pc_FunctionCount == MAX_FUNCTION_COUNT) + { + ERR_Error(ERR_TOO_MANY_FUNCTIONS, YES, NULL); + } + function = &FunctionInfo[pc_FunctionCount]; + function->hasReturnValue = (U_BYTE)sym->info.scriptFunc.hasReturnValue; + function->argCount = (U_BYTE)sym->info.scriptFunc.argCount; + function->localCount = (U_BYTE)sym->info.scriptFunc.varCount; + function->name = STR_AppendToList (STRLIST_FUNCTIONS, sym->name); + function->address = sym->info.scriptFunc.address; + sym->info.scriptFunc.funcNumber = pc_FunctionCount; + pc_FunctionCount++; +} + +//========================================================================== +// +// PC_AddArray +// +//========================================================================== + +void PC_AddArray(int index, int size) +{ + NumArrays++; + ArraySizes[index] = size; +} + +//========================================================================== +// +// PC_InitArray +// +//========================================================================== + +void PC_InitArray(int index, int *entries, boolean hasStrings) +{ + int i; + + // If the array is just initialized to zeros, then we don't need to + // remember the initializer. + for(i = 0; i < ArraySizes[index]; ++i) + { + if(entries[i] != 0) + { + break; + } + } + if(i < ArraySizes[index]) + { + ArrayInits[index] = MS_Alloc(ArraySizes[index]*sizeof(int), ERR_OUT_OF_MEMORY); + memcpy(ArrayInits[index], entries, ArraySizes[index]*sizeof(int)); + } + ArrayOfStrings[index] = hasStrings; +} + +//========================================================================== +// +// PC_AddImport +// +//========================================================================== + +int PC_AddImport(char *name) +{ + if (NumImports >= MAX_IMPORTS) + { + ERR_Exit(ERR_TOO_MANY_IMPORTS, YES); + } + strncpy(Imports[NumImports], name, 8); + return NumImports++; +} diff --git a/pcode.h b/pcode.h new file mode 100644 index 0000000..b87ebd4 --- /dev/null +++ b/pcode.h @@ -0,0 +1,389 @@ + +//************************************************************************** +//** +//** pcode.h +//** +//************************************************************************** + +#ifndef __PCODE_H__ +#define __PCODE_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include +#include "common.h" + +// MACROS ------------------------------------------------------------------ + +// Values added to script number to indicate its type +enum +{ + OPEN_SCRIPTS_BASE = 1000, + RESPAWN_SCRIPTS_BASE = 2000, // [BC] + DEATH_SCRIPTS_BASE = 3000, // [BC] + ENTER_SCRIPTS_BASE = 4000, // [BC] + PICKUP_SCRIPTS_BASE = 5000, // [BC] + BLUE_RETURN_SCRIPTS_BASE = 6000, // [BC] + RED_RETURN_SCRIPTS_BASE = 7000, // [BC] + WHITE_RETURN_SCRIPTS_BASE = 8000, // [BC] + LIGHTNING_SCRIPTS_BASE = 12000, + UNLOADING_SCRIPTS_BASE = 13000, + DISCONNECT_SCRIPTS_BASE = 14000 +}; + +// Values added to script number to indicate flags (requires new-style .o) +enum +{ + NET_SCRIPT_FLAG = 0x00010000 +}; + +// Or'ed with variable index when passing variables of type "out" +// An idea that was never realized. +enum +{ + OUTVAR_SCRIPT_SPEC = 0x40000000, + OUTVAR_MAP_SPEC = 0x80000000, + OUTVAR_WORLD_SPEC = 0xc0000000, + OUTVAR_GLOBAL_SPEC = 0x00000000 +}; + +// TYPES ------------------------------------------------------------------- + +struct symbolNode_s; // Defined in symbol.h + +typedef enum +{ + PCD_NOP, + PCD_TERMINATE, + PCD_SUSPEND, + PCD_PUSHNUMBER, + PCD_LSPEC1, + PCD_LSPEC2, + PCD_LSPEC3, + PCD_LSPEC4, + PCD_LSPEC5, + PCD_LSPEC1DIRECT, + PCD_LSPEC2DIRECT, + PCD_LSPEC3DIRECT, + PCD_LSPEC4DIRECT, + PCD_LSPEC5DIRECT, + PCD_ADD, + PCD_SUBTRACT, + PCD_MULTIPLY, + PCD_DIVIDE, + PCD_MODULUS, + PCD_EQ, + PCD_NE, + PCD_LT, + PCD_GT, + PCD_LE, + PCD_GE, + PCD_ASSIGNSCRIPTVAR, + PCD_ASSIGNMAPVAR, + PCD_ASSIGNWORLDVAR, + PCD_PUSHSCRIPTVAR, + PCD_PUSHMAPVAR, + PCD_PUSHWORLDVAR, + PCD_ADDSCRIPTVAR, + PCD_ADDMAPVAR, + PCD_ADDWORLDVAR, + PCD_SUBSCRIPTVAR, + PCD_SUBMAPVAR, + PCD_SUBWORLDVAR, + PCD_MULSCRIPTVAR, + PCD_MULMAPVAR, + PCD_MULWORLDVAR, + PCD_DIVSCRIPTVAR, + PCD_DIVMAPVAR, + PCD_DIVWORLDVAR, + PCD_MODSCRIPTVAR, + PCD_MODMAPVAR, + PCD_MODWORLDVAR, + PCD_INCSCRIPTVAR, + PCD_INCMAPVAR, + PCD_INCWORLDVAR, + PCD_DECSCRIPTVAR, + PCD_DECMAPVAR, + PCD_DECWORLDVAR, + PCD_GOTO, + PCD_IFGOTO, + PCD_DROP, + PCD_DELAY, + PCD_DELAYDIRECT, + PCD_RANDOM, + PCD_RANDOMDIRECT, + PCD_THINGCOUNT, + PCD_THINGCOUNTDIRECT, + PCD_TAGWAIT, + PCD_TAGWAITDIRECT, + PCD_POLYWAIT, + PCD_POLYWAITDIRECT, + PCD_CHANGEFLOOR, + PCD_CHANGEFLOORDIRECT, + PCD_CHANGECEILING, + PCD_CHANGECEILINGDIRECT, + PCD_RESTART, + PCD_ANDLOGICAL, + PCD_ORLOGICAL, + PCD_ANDBITWISE, + PCD_ORBITWISE, + PCD_EORBITWISE, + PCD_NEGATELOGICAL, + PCD_LSHIFT, + PCD_RSHIFT, + PCD_UNARYMINUS, + PCD_IFNOTGOTO, + PCD_LINESIDE, + PCD_SCRIPTWAIT, + PCD_SCRIPTWAITDIRECT, + PCD_CLEARLINESPECIAL, + PCD_CASEGOTO, + PCD_BEGINPRINT, + PCD_ENDPRINT, + PCD_PRINTSTRING, + PCD_PRINTNUMBER, + PCD_PRINTCHARACTER, + PCD_PLAYERCOUNT, + PCD_GAMETYPE, + PCD_GAMESKILL, + PCD_TIMER, + PCD_SECTORSOUND, + PCD_AMBIENTSOUND, + PCD_SOUNDSEQUENCE, + PCD_SETLINETEXTURE, + PCD_SETLINEBLOCKING, + PCD_SETLINESPECIAL, + PCD_THINGSOUND, + PCD_ENDPRINTBOLD, +// [RH] End of Hexen p-codes + PCD_ACTIVATORSOUND, + PCD_LOCALAMBIENTSOUND, + PCD_SETLINEMONSTERBLOCKING, +// [BC] Start of new pcodes + PCD_PLAYERBLUESKULL, + PCD_PLAYERREDSKULL, + PCD_PLAYERYELLOWSKULL, + PCD_PLAYERMASTERSKULL, + PCD_PLAYERBLUECARD, + PCD_PLAYERREDCARD, + PCD_PLAYERYELLOWCARD, + PCD_PLAYERMASTERCARD, + PCD_PLAYERBLACKSKULL, + PCD_PLAYERSILVERSKULL, + PCD_PLAYERGOLDSKULL, + PCD_PLAYERBLACKCARD, + PCD_PLAYERSILVERCARD, + PCD_PLAYERONTEAM, + PCD_PLAYERTEAM, + PCD_PLAYERHEALTH, + PCD_PLAYERARMORPOINTS, + PCD_PLAYERFRAGS, + PCD_PLAYEREXPERT, + PCD_BLUETEAMCOUNT, + PCD_REDTEAMCOUNT, + PCD_BLUETEAMSCORE, + PCD_REDTEAMSCORE, + PCD_ISONEFLAGCTF, + PCD_LSPEC6, // [RH] LSPEC6 is never actually used. + PCD_LSPEC6DIRECT, // Should these be removed? + PCD_PRINTNAME, + PCD_MUSICCHANGE, + PCD_CONSOLECOMMANDDIRECT, + PCD_CONSOLECOMMAND, + PCD_SINGLEPLAYER, +// [RH] End of Skull Tag p-codes + PCD_FIXEDMUL, + PCD_FIXEDDIV, + PCD_SETGRAVITY, + PCD_SETGRAVITYDIRECT, + PCD_SETAIRCONTROL, + PCD_SETAIRCONTROLDIRECT, + PCD_CLEARINVENTORY, + PCD_GIVEINVENTORY, + PCD_GIVEINVENTORYDIRECT, + PCD_TAKEINVENTORY, + PCD_TAKEINVENTORYDIRECT, + PCD_CHECKINVENTORY, + PCD_CHECKINVENTORYDIRECT, + PCD_SPAWN, + PCD_SPAWNDIRECT, + PCD_SPAWNSPOT, + PCD_SPAWNSPOTDIRECT, + PCD_SETMUSIC, + PCD_SETMUSICDIRECT, + PCD_LOCALSETMUSIC, + PCD_LOCALSETMUSICDIRECT, + PCD_PRINTFIXED, + PCD_PRINTLOCALIZED, + PCD_MOREHUDMESSAGE, + PCD_OPTHUDMESSAGE, + PCD_ENDHUDMESSAGE, + PCD_ENDHUDMESSAGEBOLD, + PCD_SETSTYLE, + PCD_SETSTYLEDIRECT, + PCD_SETFONT, + PCD_SETFONTDIRECT, + PCD_PUSHBYTE, // Valid in compact-script mode only + PCD_LSPEC1DIRECTB, // " + PCD_LSPEC2DIRECTB, // " + PCD_LSPEC3DIRECTB, // " + PCD_LSPEC4DIRECTB, // " + PCD_LSPEC5DIRECTB, // " + PCD_DELAYDIRECTB, // " + PCD_RANDOMDIRECTB, // " + PCD_PUSHBYTES, // " + PCD_PUSH2BYTES, // " + PCD_PUSH3BYTES, // " + PCD_PUSH4BYTES, // " + PCD_PUSH5BYTES, // " + PCD_SETTHINGSPECIAL, + PCD_ASSIGNGLOBALVAR, + PCD_PUSHGLOBALVAR, + PCD_ADDGLOBALVAR, + PCD_SUBGLOBALVAR, + PCD_MULGLOBALVAR, + PCD_DIVGLOBALVAR, + PCD_MODGLOBALVAR, + PCD_INCGLOBALVAR, + PCD_DECGLOBALVAR, + PCD_FADETO, + PCD_FADERANGE, + PCD_CANCELFADE, + PCD_PLAYMOVIE, + PCD_SETFLOORTRIGGER, + PCD_SETCEILINGTRIGGER, + PCD_GETACTORX, + PCD_GETACTORY, + PCD_GETACTORZ, + PCD_STARTTRANSLATION, + PCD_TRANSLATIONRANGE1, + PCD_TRANSLATIONRANGE2, + PCD_ENDTRANSLATION, + PCD_CALL, + PCD_CALLDISCARD, + PCD_RETURNVOID, + PCD_RETURNVAL, + PCD_PUSHMAPARRAY, + PCD_ASSIGNMAPARRAY, + PCD_ADDMAPARRAY, + PCD_SUBMAPARRAY, + PCD_MULMAPARRAY, + PCD_DIVMAPARRAY, + PCD_MODMAPARRAY, + PCD_INCMAPARRAY, + PCD_DECMAPARRAY, + PCD_DUP, + PCD_SWAP, + PCD_WRITETOINI, + PCD_GETFROMINI, + PCD_SIN, + PCD_COS, + PCD_VECTORANGLE, + PCD_CHECKWEAPON, + PCD_SETWEAPON, + PCD_TAGSTRING, + PCD_PUSHWORLDARRAY, + PCD_ASSIGNWORLDARRAY, + PCD_ADDWORLDARRAY, + PCD_SUBWORLDARRAY, + PCD_MULWORLDARRAY, + PCD_DIVWORLDARRAY, + PCD_MODWORLDARRAY, + PCD_INCWORLDARRAY, + PCD_DECWORLDARRAY, + PCD_PUSHGLOBALARRAY, + PCD_ASSIGNGLOBALARRAY, + PCD_ADDGLOBALARRAY, + PCD_SUBGLOBALARRAY, + PCD_MULGLOBALARRAY, + PCD_DIVGLOBALARRAY, + PCD_MODGLOBALARRAY, + PCD_INCGLOBALARRAY, + PCD_DECGLOBALARRAY, + PCD_SETMARINEWEAPON, + PCD_SETACTORPROPERTY, + PCD_GETACTORPROPERTY, + PCD_PLAYERNUMBER, + PCD_ACTIVATORTID, + PCD_SETMARINESPRITE, + PCD_GETSCREENWIDTH, + PCD_GETSCREENHEIGHT, + PCD_THING_PROJECTILE2, + PCD_STRLEN, + PCD_SETHUDSIZE, + PCD_GETCVAR, + PCD_CASEGOTOSORTED, + PCD_SETRESULTVALUE, + PCD_GETLINEROWOFFSET, + PCD_GETACTORFLOORZ, + PCD_GETACTORANGLE, + PCD_GETSECTORFLOORZ, + PCD_GETSECTORCEILINGZ, + PCD_LSPEC5RESULT, + PCD_GETSIGILPIECES, + PCD_GETLEVELINFO, + PCD_CHANGESKY, + PCD_PLAYERINGAME, + PCD_PLAYERISBOT, + PCD_SETCAMERATOTEXTURE, + PCD_ENDLOG, + PCD_GETAMMOCAPACITY, + PCD_SETAMMOCAPACITY, +// [JB] start of new pcodes + PCD_PRINTMAPCHARARRAY, + PCD_PRINTWORLDCHARARRAY, + PCD_PRINTGLOBALCHARARRAY, +// [JB] end of new pcodes + PCD_SETACTORANGLE, + PCD_GRABINPUT, + PCD_SETMOUSEPOINTER, + PCD_MOVEMOUSEPOINTER, + + PCODE_COMMAND_COUNT +} pcd_t; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void PC_OpenObject(char *name, size_t size, int flags); +void PC_CloseObject(void); +void PC_Append(void *buffer, size_t size); +void PC_AppendByte(U_BYTE val); +void PC_AppendWord(U_WORD val); +void PC_AppendLong(U_LONG val); +void PC_AppendString(char *string); +void PC_AppendCmd(pcd_t command); +void PC_AppendPushVal(U_LONG val); +void PC_AppendShrink(U_BYTE val); +void PC_Write(void *buffer, size_t size, int address); +void PC_WriteByte(U_BYTE val, int address); +//void PC_WriteWord(U_WORD val, int address); +void PC_WriteLong(U_LONG val, int address); +void PC_WriteString(char *string, int address); +void PC_WriteCmd(pcd_t command, int address); +void PC_Skip(size_t size); +//void PC_SkipByte(void); +//void PC_SkipWord(void); +void PC_SkipLong(void); +void PC_AddScript(int number, int argCount); +void PC_SetScriptVarCount(int number, int varCount); +void PC_AddFunction(struct symbolNode_s *sym); +void PC_PutMapVariable(int index, int value); +void PC_NameMapVariable(int index, struct symbolNode_s *sym); +void PC_AddArray(int index, int size); +void PC_InitArray(int index, int *entries, boolean hasStrings); +int PC_AddImport(char *name); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +extern int pc_Address; +extern byte *pc_Buffer; +extern byte *pc_BufferPtr; +extern int pc_ScriptCount; +extern int pc_FunctionCount; +extern boolean pc_NoShrink; +extern boolean pc_HexenCase; +extern boolean pc_WadAuthor; +extern boolean pc_EncryptStrings; +extern int pc_LastAppendedCommand; + +#endif diff --git a/strlist.c b/strlist.c new file mode 100644 index 0000000..386cd5e --- /dev/null +++ b/strlist.c @@ -0,0 +1,428 @@ + +//************************************************************************** +//** +//** strlist.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "common.h" +#include "strlist.h" +#include "error.h" +#include "misc.h" +#include "pcode.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + char *name; + int address; +} stringInfo_t; + +typedef struct +{ + int stringCount; + stringInfo_t strings[MAX_STRINGS]; +} stringList_t; + +typedef struct +{ + char name[4]; + stringList_t list; +} languageInfo_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static int STR_PutStringInSomeList(stringList_t *list, int index, char *name); +static int STR_FindInSomeList(stringList_t *list, char *name); +static void DumpStrings(stringList_t *list, int lenadr, boolean quad, boolean crypt); +static void Encrypt(void *data, int key, int len); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +int NumLanguages, NumStringLists; + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static languageInfo_t *LanguageInfo[MAX_LANGUAGES]; +static stringList_t *StringLists[NUM_STRLISTS]; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// STR_Init +// +//========================================================================== + +void STR_Init(void) +{ + NumLanguages = NumStringLists = 0; + STR_FindLanguage(NULL); // Default language is always number 0 +} + +//========================================================================== +// +// STR_FindLanguage +// +//========================================================================== + +int STR_FindLanguage(char *name) +{ + int i; + + if (name == NULL) + { + if (NumLanguages > 0) + { + return 0; + } + i = 0; + } + else + { + for(i = 0; i < NumLanguages; i++) + { + if (strcmp (name, LanguageInfo[i]->name) == 0) + { + return i; + } + } + } + if(i == NumLanguages) + { + LanguageInfo[i] = MS_Alloc(sizeof(languageInfo_t), ERR_OUT_OF_MEMORY); + memset(LanguageInfo[i]->name, 0, 4); + if(name != NULL) + { + strncpy(LanguageInfo[i]->name, name, 3); + } + LanguageInfo[i]->list.stringCount = 0; + NumLanguages++; + } + return i; +} + +//========================================================================== +// +// STR_Find +// +//========================================================================== + +int STR_Find(char *name) +{ + return STR_FindInLanguage(0, name); +} + +//========================================================================== +// +// STR_FindInLanguage +// +//========================================================================== + +int STR_FindInLanguage(int language, char *name) +{ + return STR_FindInSomeList (&LanguageInfo[language]->list, name); +} + +//========================================================================== +// +// STR_FindInList +// +//========================================================================== + +int STR_FindInList(int list, char *name) +{ + if (StringLists[list] == NULL) + { + StringLists[list] = MS_Alloc(sizeof(stringList_t), ERR_OUT_OF_MEMORY); + StringLists[list]->stringCount = 0; + NumStringLists++; + } + return STR_FindInSomeList (StringLists[list], name); +} + +//========================================================================== +// +// STR_FindInSomeList +// +//========================================================================== + +static int STR_FindInSomeList(stringList_t *list, char *name) +{ + int i; + + for(i = 0; i < list->stringCount; i++) + { + if(strcmp(list->strings[i].name, name) == 0) + { + return i; + } + } + // Add to list + return STR_PutStringInSomeList(list, i, name); +} + +//========================================================================== +// +// STR_GetString +// +//========================================================================== + +const char *STR_GetString(int list, int index) +{ + if (StringLists[list] == NULL) + { + return NULL; + } + if (index < 0 || index >= StringLists[list]->stringCount) + { + return NULL; + } + return StringLists[list]->strings[index].name; +} + +//========================================================================== +// +// STR_AppendToList +// +//========================================================================== + +int STR_AppendToList(int list, char *name) +{ + if (StringLists[list] == NULL) + { + StringLists[list] = MS_Alloc(sizeof(stringList_t), ERR_OUT_OF_MEMORY); + StringLists[list]->stringCount = 0; + NumStringLists++; + } + return STR_PutStringInSomeList(StringLists[list], StringLists[list]->stringCount, name); +} + +//========================================================================== +// +// STR_PutStringInSomeList +// +//========================================================================== + +static int STR_PutStringInSomeList(stringList_t *list, int index, char *name) +{ + int i; + + if(index >= MAX_STRINGS) + { + ERR_Error(ERR_TOO_MANY_STRINGS, YES, MAX_STRINGS); + return 0; + } + MS_Message(MSG_DEBUG, "Adding string %d:\n \"%s\"\n", + list->stringCount, name); + if(index >= list->stringCount) + { + for(i = list->stringCount; i <= index; i++) + { + list->strings[i].name = NULL; + } + list->stringCount = index + 1; + } + if(list->strings[index].name != NULL) + { + free(list->strings[index].name); + } + if(name != NULL) + { + list->strings[index].name = MS_Alloc(strlen(name)+1, ERR_OUT_OF_MEMORY); + strcpy(list->strings[index].name, name); + } + else + { + list->strings[index].name = NULL; + } + return index; +} + +//========================================================================== +// +// STR_ListSize +// +//========================================================================== + +int STR_ListSize(int list) +{ + return LanguageInfo[list]->list.stringCount; +} + +//========================================================================== +// +// STR_WriteStrings +// +// Writes all the strings to the p-code buffer. +// +//========================================================================== + +void STR_WriteStrings(void) +{ + int i; + U_LONG pad; + + MS_Message(MSG_DEBUG, "---- STR_WriteStrings ----\n"); + for(i = 0; i < LanguageInfo[0]->list.stringCount; i++) + { + LanguageInfo[0]->list.strings[i].address = pc_Address; + PC_AppendString(LanguageInfo[0]->list.strings[i].name); + } + if(pc_Address%4 != 0) + { // Need to align + pad = 0; + PC_Append((void *)&pad, 4-(pc_Address%4)); + } +} + +//========================================================================== +// +// STR_WriteList +// +//========================================================================== + +void STR_WriteList(void) +{ + int i; + + MS_Message(MSG_DEBUG, "---- STR_WriteList ----\n"); + PC_AppendLong((U_LONG)LanguageInfo[0]->list.stringCount); + for(i = 0; i < LanguageInfo[0]->list.stringCount; i++) + { + PC_AppendLong((U_LONG)LanguageInfo[0]->list.strings[i].address); + } +} + +//========================================================================== +// +// STR_WriteChunk +// +//========================================================================== + +void STR_WriteChunk(int language, boolean encrypt) +{ + languageInfo_t *lang = LanguageInfo[language]; + int lenadr; + + MS_Message(MSG_DEBUG, "---- STR_WriteChunk %d ----\n", language); + PC_Append(encrypt ? "STRE" : "STRL", 4); + lenadr = pc_Address; + PC_SkipLong(); + PC_Append(&lang->name, 4); + PC_AppendLong(lang->list.stringCount); + PC_AppendLong(0); // Used in-game for stringing lists together + + DumpStrings (&lang->list, lenadr, NO, encrypt); +} + +//========================================================================== +// +// STR_WriteListChunk +// +//========================================================================== + +void STR_WriteListChunk(int list, int id, boolean quad) +{ + int lenadr; + + if (StringLists[list] != NULL && StringLists[list]->stringCount > 0) + { + MS_Message(MSG_DEBUG, "---- STR_WriteListChunk %d %c%c%c%c----\n", list, + id&255, (id>>8)&255, (id>>16)&255, (id>>24)&255); + PC_AppendLong((U_LONG)id); + lenadr = pc_Address; + PC_SkipLong(); + PC_AppendLong(StringLists[list]->stringCount); + if (quad && pc_Address%8 != 0) + { // If writing quadword indices, align the indices to an + // 8-byte boundary. + U_LONG pad = 0; + PC_Append (&pad, 4); + } + DumpStrings(StringLists[list], lenadr, quad, NO); + } +} + +//========================================================================== +// +// DumpStrings +// +//========================================================================== + +static void DumpStrings(stringList_t *list, int lenadr, boolean quad, boolean crypt) +{ + int i, ofs, startofs; + + startofs = ofs = pc_Address - lenadr - 4 + list->stringCount*(quad?8:4); + + for(i = 0; i < list->stringCount; i++) + { + if (list->strings[i].name != NULL) + { + PC_AppendLong((U_LONG)ofs); + ofs += strlen(list->strings[i].name) + 1; + } + else + { + PC_AppendLong(0); + } + if (quad) + { + PC_AppendLong(0); + } + } + + ofs = startofs; + + for(i = 0; i < list->stringCount; i++) + { + if(list->strings[i].name != NULL) + { + int stringlen = strlen(list->strings[i].name) + 1; + if(crypt) + { + int cryptkey = ofs*157135; + + Encrypt(list->strings[i].name, cryptkey, stringlen); + PC_Append(list->strings[i].name, stringlen); + ofs += stringlen; + Encrypt(list->strings[i].name, cryptkey, stringlen); + } + else + { + PC_AppendString(list->strings[i].name); + } + } + } + if(pc_Address%4 != 0) + { // Need to align + U_LONG pad = 0; + PC_Append((void *)&pad, 4-(pc_Address%4)); + } + + PC_WriteLong(pc_Address - lenadr - 4, lenadr); +} + +static void Encrypt(void *data, int key, int len) +{ + int p = (byte)key, i; + + for(i = 0; i < len; ++i) + { + ((byte *)data)[i] ^= (byte)(p+(i>>1)); + } +} diff --git a/strlist.h b/strlist.h new file mode 100644 index 0000000..63a5b8c --- /dev/null +++ b/strlist.h @@ -0,0 +1,38 @@ + +//************************************************************************** +//** +//** strlist.h +//** +//************************************************************************** + +#ifndef __STRLIST_H__ +#define __STRLIST_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include "common.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void STR_Init(void); +int STR_Find(char *name); +void STR_WriteStrings(void); +void STR_WriteList(void); +int STR_FindLanguage(char *name); +int STR_FindInLanguage(int language, char *name); +int STR_FindInList(int list, char *name); +int STR_AppendToList(int list, char *name); +const char *STR_GetString(int list, int index); +void STR_WriteChunk(int language, boolean encrypt); +void STR_WriteListChunk(int list, int id, boolean quad); +int STR_ListSize(int list); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +extern int NumLanguages, NumStringLists; + +#endif diff --git a/symbol.c b/symbol.c new file mode 100644 index 0000000..a238232 --- /dev/null +++ b/symbol.c @@ -0,0 +1,470 @@ + +//************************************************************************** +//** +//** symbol.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#include +#include +#include "common.h" +#include "symbol.h" +#include "misc.h" +#include "parse.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +typedef struct +{ + char *name; + pcd_t directCommand; + pcd_t stackCommand; + int argCount; + int optMask; + int outMask; + boolean hasReturnValue; + boolean latent; +} internFuncDef_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static symbolNode_t *Find(char *name, symbolNode_t *root); +static symbolNode_t *Insert(char *name, symbolType_t type, + symbolNode_t **root); +static void FreeNodes(symbolNode_t *root); +static void ClearShared(symbolNode_t *root); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static symbolNode_t *LocalRoot; +static symbolNode_t *GlobalRoot; + +static internFuncDef_t InternalFunctions[] = +{ + { "tagwait", PCD_TAGWAITDIRECT, PCD_TAGWAIT, 1, 0, 0, NO, YES }, + { "polywait", PCD_POLYWAITDIRECT, PCD_POLYWAIT, 1, 0, 0, NO, YES }, + { "scriptwait", PCD_SCRIPTWAITDIRECT, PCD_SCRIPTWAIT, 1, 0, 0, NO, YES}, + { "delay", PCD_DELAYDIRECT, PCD_DELAY, 1, 0, 0, NO, YES }, + { "random", PCD_RANDOMDIRECT, PCD_RANDOM, 2, 0, 0, YES, NO }, + { "thingcount", PCD_THINGCOUNTDIRECT, PCD_THINGCOUNT, 2, 0, 0, YES, NO }, + { "changefloor", PCD_CHANGEFLOORDIRECT, PCD_CHANGEFLOOR, 2, 0, 0, NO, NO }, + { "changeceiling", PCD_CHANGECEILINGDIRECT, PCD_CHANGECEILING, 2, 0, 0, NO, NO }, + { "lineside", PCD_NOP, PCD_LINESIDE, 0, 0, 0, YES, NO }, + { "clearlinespecial", PCD_NOP, PCD_CLEARLINESPECIAL, 0, 0, 0, NO, NO }, + { "playercount", PCD_NOP, PCD_PLAYERCOUNT, 0, 0, 0, YES, NO }, + { "gametype", PCD_NOP, PCD_GAMETYPE, 0, 0, 0, YES, NO }, + { "gameskill", PCD_NOP, PCD_GAMESKILL, 0, 0, 0, YES, NO }, + { "timer", PCD_NOP, PCD_TIMER, 0, 0, 0, YES, NO }, + { "sectorsound", PCD_NOP, PCD_SECTORSOUND, 2, 0, 0, NO, NO }, + { "ambientsound", PCD_NOP, PCD_AMBIENTSOUND, 2, 0, 0, NO, NO }, + { "soundsequence", PCD_NOP, PCD_SOUNDSEQUENCE, 1, 0, 0, NO, NO }, + { "setlinetexture", PCD_NOP, PCD_SETLINETEXTURE, 4, 0, 0, NO, NO }, + { "setlineblocking", PCD_NOP, PCD_SETLINEBLOCKING, 2, 0, 0, NO, NO }, + { "setlinespecial", PCD_NOP, PCD_SETLINESPECIAL, 7, 4|8|16|32|64, 0, NO, NO }, + { "thingsound", PCD_NOP, PCD_THINGSOUND, 3, 0, 0, NO, NO }, + { "activatorsound", PCD_NOP, PCD_ACTIVATORSOUND, 2, 0, 0, NO, NO }, + { "localambientsound", PCD_NOP, PCD_LOCALAMBIENTSOUND, 2, 0, 0, NO, NO }, + { "setlinemonsterblocking", PCD_NOP, PCD_SETLINEMONSTERBLOCKING, 2, 0, 0, NO, NO }, + { "fixedmul", PCD_NOP, PCD_FIXEDMUL, 2, 0, 0, YES, NO }, + { "fixeddiv", PCD_NOP, PCD_FIXEDDIV, 2, 0, 0, YES, NO }, +// [BC] Start of new pcodes + { "playerblueskull", PCD_NOP, PCD_PLAYERBLUESKULL, 0, 0, 0, YES, NO }, + { "playerredskull", PCD_NOP, PCD_PLAYERREDSKULL, 0, 0, 0, YES, NO }, + { "playeryellowskull", PCD_NOP, PCD_PLAYERYELLOWSKULL, 0, 0, 0, YES, NO }, + { "playerbluecard", PCD_NOP, PCD_PLAYERBLUECARD, 0, 0, 0, YES, NO }, + { "playerredcard", PCD_NOP, PCD_PLAYERREDCARD, 0, 0, 0, YES, NO }, + { "playeryellowcard", PCD_NOP, PCD_PLAYERYELLOWCARD, 0, 0, 0, YES, NO }, + { "playeronteam", PCD_NOP, PCD_PLAYERONTEAM, 0, 0, 0, YES, NO }, + { "playerteam", PCD_NOP, PCD_PLAYERTEAM, 0, 0, 0, YES, NO }, + { "playerfrags", PCD_NOP, PCD_PLAYERFRAGS, 0, 0, 0, YES, NO }, + { "playerhealth", PCD_NOP, PCD_PLAYERHEALTH, 0, 0, 0, YES, NO }, + { "playerarmorpoints", PCD_NOP, PCD_PLAYERARMORPOINTS, 0, 0, 0, YES, NO }, + { "playerexpert", PCD_NOP, PCD_PLAYEREXPERT, 0, 0, 0, YES, NO }, + { "bluecount", PCD_NOP, PCD_BLUETEAMCOUNT, 0, 0, 0, YES, NO }, + { "redcount", PCD_NOP, PCD_REDTEAMCOUNT, 0, 0, 0, YES, NO }, + { "bluescore", PCD_NOP, PCD_BLUETEAMSCORE, 0, 0, 0, YES, NO }, + { "redscore", PCD_NOP, PCD_REDTEAMSCORE, 0, 0, 0, YES, NO }, + { "isoneflagctf", PCD_NOP, PCD_ISONEFLAGCTF, 0, 0, 0, YES, NO }, + { "music_change", PCD_NOP, PCD_MUSICCHANGE, 2, 0, 0, NO, NO }, + { "consolecommand", PCD_CONSOLECOMMANDDIRECT, PCD_CONSOLECOMMAND, 3, 2|4, 0, NO, NO }, + { "singleplayer", PCD_NOP, PCD_SINGLEPLAYER, 0, 0, 0, YES, NO }, +// [RH] end of Skull Tag functions + { "setgravity", PCD_SETGRAVITYDIRECT, PCD_SETGRAVITY, 1, 0, 0, NO, NO }, + { "setaircontrol", PCD_SETAIRCONTROLDIRECT, PCD_SETAIRCONTROL, 1, 0, 0, NO, NO }, + { "clearinventory", PCD_NOP, PCD_CLEARINVENTORY, 0, 0, 0, NO, NO }, + { "giveinventory", PCD_GIVEINVENTORYDIRECT, PCD_GIVEINVENTORY, 2, 0, 0, NO, NO }, + { "takeinventory", PCD_TAKEINVENTORYDIRECT, PCD_TAKEINVENTORY, 2, 0, 0, NO, NO }, + { "checkinventory", PCD_CHECKINVENTORYDIRECT, PCD_CHECKINVENTORY, 1, 0, 0, YES, NO }, + { "spawn", PCD_SPAWNDIRECT, PCD_SPAWN, 6, 16|32, 0, YES, NO }, + { "spawnspot", PCD_SPAWNSPOTDIRECT, PCD_SPAWNSPOT, 4, 4|8, 0, YES, NO }, + { "setmusic", PCD_SETMUSICDIRECT, PCD_SETMUSIC, 3, 2|4, 0, NO, NO }, + { "localsetmusic", PCD_LOCALSETMUSICDIRECT, PCD_LOCALSETMUSIC, 3, 2|4, 0, NO, NO }, + { "setstyle", PCD_SETSTYLEDIRECT, PCD_SETSTYLE, 1, 0, 0, NO, NO }, + { "setfont", PCD_SETFONTDIRECT, PCD_SETFONT, 1, 0, 0, NO, NO }, + { "setthingspecial", PCD_NOP, PCD_SETTHINGSPECIAL, 7, 4|8|16|32|64, 0, NO, NO }, + { "fadeto", PCD_NOP, PCD_FADETO, 5, 0, 0, NO, NO }, + { "faderange", PCD_NOP, PCD_FADERANGE, 9, 0, 0, NO, NO }, + { "cancelfade", PCD_NOP, PCD_CANCELFADE, 0, 0, 0, NO, NO }, + { "playmovie", PCD_NOP, PCD_PLAYMOVIE, 1, 0, 0, YES, NO }, + { "setfloortrigger", PCD_NOP, PCD_SETFLOORTRIGGER, 8, 8|16|32|64|128, 0, NO, NO }, + { "setceilingtrigger", PCD_NOP, PCD_SETCEILINGTRIGGER, 8, 8|16|32|64|128, 0, NO, NO }, + { "getactorx", PCD_NOP, PCD_GETACTORX, 1, 0, 0, YES, NO }, + { "getactory", PCD_NOP, PCD_GETACTORY, 1, 0, 0, YES, NO }, + { "getactorz", PCD_NOP, PCD_GETACTORZ, 1, 0, 0, YES, NO }, + { "getactorfloorz", PCD_NOP, PCD_GETACTORFLOORZ, 1, 0, 0, YES, NO }, + { "getactorangle", PCD_NOP, PCD_GETACTORANGLE, 1, 0, 0, YES, NO }, + { "writetoini", PCD_NOP, PCD_WRITETOINI, 3, 0, 0, NO, NO }, + { "getfromini", PCD_NOP, PCD_GETFROMINI, 3, 0, 0, YES, NO }, + { "sin", PCD_NOP, PCD_SIN, 1, 0, 0, YES, NO }, + { "cos", PCD_NOP, PCD_COS, 1, 0, 0, YES, NO }, + { "vectorangle", PCD_NOP, PCD_VECTORANGLE, 2, 0, 0, YES, NO }, + { "checkweapon", PCD_NOP, PCD_CHECKWEAPON, 1, 0, 0, YES, NO }, + { "setweapon", PCD_NOP, PCD_SETWEAPON, 1, 0, 0, YES, NO }, + { "setmarineweapon", PCD_NOP, PCD_SETMARINEWEAPON, 2, 0, 0, NO, NO }, + { "setactorproperty", PCD_NOP, PCD_SETACTORPROPERTY, 3, 0, 0, NO, NO }, + { "getactorproperty", PCD_NOP, PCD_GETACTORPROPERTY, 2, 0, 0, YES, NO }, + { "playernumber", PCD_NOP, PCD_PLAYERNUMBER, 0, 0, 0, YES, NO }, + { "activatortid", PCD_NOP, PCD_ACTIVATORTID, 0, 0, 0, YES, NO }, + { "setmarinesprite", PCD_NOP, PCD_SETMARINESPRITE, 2, 0, 0, NO, NO }, + { "getscreenwidth", PCD_NOP, PCD_GETSCREENWIDTH, 0, 0, 0, YES, NO }, + { "getscreenheight", PCD_NOP, PCD_GETSCREENHEIGHT, 0, 0, 0, YES, NO }, + { "thing_projectile2", PCD_NOP, PCD_THING_PROJECTILE2, 7, 0, 0, NO, NO }, + { "strlen", PCD_NOP, PCD_STRLEN, 1, 0, 0, YES, NO }, + { "sethudsize", PCD_NOP, PCD_SETHUDSIZE, 3, 0, 0, NO, NO }, + { "getcvar", PCD_NOP, PCD_GETCVAR, 1, 0, 0, YES, NO }, + { "setresultvalue", PCD_NOP, PCD_SETRESULTVALUE, 1, 0, 0, NO, NO }, + { "getlinerowoffset", PCD_NOP, PCD_GETLINEROWOFFSET, 0, 0, 0, YES, NO }, + { "getsectorfloorz", PCD_NOP, PCD_GETSECTORFLOORZ, 3, 0, 0, YES, NO }, + { "getsectorceilingz", PCD_NOP, PCD_GETSECTORCEILINGZ, 3, 0, 0, YES, NO }, + { "getsigilpieces", PCD_NOP, PCD_GETSIGILPIECES, 0, 0, 0, YES, NO }, + { "getlevelinfo", PCD_NOP, PCD_GETLEVELINFO, 1, 0, 0, YES, NO }, + { "changesky", PCD_NOP, PCD_CHANGESKY, 2, 0, 0, NO, NO }, + { "playeringame", PCD_NOP, PCD_PLAYERINGAME, 1, 0, 0, YES, NO }, + { "playerisbot", PCD_NOP, PCD_PLAYERISBOT, 1, 0, 0, YES, NO }, + { "setcameratotexture", PCD_NOP, PCD_SETCAMERATOTEXTURE, 3, 0, 0, NO, NO }, + { "grabinput", PCD_NOP, PCD_GRABINPUT, 2, 0, 0, NO, NO }, + { "setmousepointer", PCD_NOP, PCD_SETMOUSEPOINTER, 3, 0, 0, NO, NO }, + { "movemousepointer", PCD_NOP, PCD_MOVEMOUSEPOINTER, 2, 0, 0, NO, NO }, + { "getammocapacity", PCD_NOP, PCD_GETAMMOCAPACITY, 1, 0, 0, YES, NO }, + { "setammocapacity", PCD_NOP, PCD_SETAMMOCAPACITY, 2, 0, 0, NO, NO }, + { "setactorangle", PCD_NOP, PCD_SETACTORANGLE, 2, 0, 0, NO, NO }, + + { NULL, PCD_NOP, PCD_NOP, 0, 0, 0, NO, NO } +}; + +static char *SymbolTypeNames[] = +{ + "SY_DUMMY", + "SY_LABEL", + "SY_SCRIPTVAR", + "SY_SCRIPTALIAS", + "SY_MAPVAR", + "SY_WORLDVAR", + "SY_GLOBALVAR", + "SY_MAPARRAY", + "SY_WORLDARRAY", + "SY_GLOBALARRAY", + "SY_SPECIAL", + "SY_CONSTANT", + "SY_INTERNFUNC", + "SY_SCRIPTFUNC" +}; + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// SY_Init +// +//========================================================================== + +void SY_Init(void) +{ + symbolNode_t *sym; + internFuncDef_t *def; + + LocalRoot = NULL; + GlobalRoot = NULL; + for(def = InternalFunctions; def->name != NULL; def++) + { + sym = SY_InsertGlobal(def->name, SY_INTERNFUNC); + sym->info.internFunc.directCommand = def->directCommand; + sym->info.internFunc.stackCommand = def->stackCommand; + sym->info.internFunc.argCount = def->argCount; + sym->info.internFunc.optMask = def->optMask; + sym->info.internFunc.outMask = def->outMask; + sym->info.internFunc.hasReturnValue = def->hasReturnValue; + sym->info.internFunc.latent = def->latent; + } +} + +//========================================================================== +// +// SY_Find +// +//========================================================================== + +symbolNode_t *SY_Find(char *name) +{ + symbolNode_t *node; + + if((node = SY_FindGlobal(name)) == NULL) + { + return SY_FindLocal(name); + } + return node; +} + +//========================================================================== +// +// SY_FindGlobal +// +//========================================================================== + +symbolNode_t *SY_FindGlobal(char *name) +{ + symbolNode_t *sym = Find(name, GlobalRoot); + if(sym != NULL && sym->unused) + { + MS_Message(MSG_DEBUG, "Symbol %s marked as used.\n", name); + sym->unused = NO; + if(sym->type == SY_SCRIPTFUNC) + { + PC_AddFunction(sym); + } + else if(sym->type == SY_MAPVAR) + { + if(pa_MapVarCount >= MAX_MAP_VARIABLES) + { + ERR_Error(ERR_TOO_MANY_MAP_VARS, YES); + } + else + { + sym->info.var.index = pa_MapVarCount++; + PC_NameMapVariable(sym->info.var.index, sym); + } + } + else if(sym->type == SY_MAPARRAY) + { + if(pa_MapVarCount >= MAX_MAP_VARIABLES) + { + ERR_Error(ERR_TOO_MANY_MAP_VARS, YES); + } + else + { + sym->info.array.index = pa_MapVarCount++; + PC_NameMapVariable(sym->info.array.index, sym); + if(sym->type == SY_MAPARRAY) + { + PC_AddArray(sym->info.array.index, sym->info.array.size); + } + } + } + } + return sym; +} + +//========================================================================== +// +// SY_Findlocal +// +//========================================================================== + +symbolNode_t *SY_FindLocal(char *name) +{ + return Find(name, LocalRoot); +} + +//========================================================================== +// +// Find +// +//========================================================================== + +static symbolNode_t *Find(char *name, symbolNode_t *root) +{ + int compare; + symbolNode_t *node; + + node = root; + while(node != NULL) + { + compare = strcmp(name, node->name); + if(compare == 0) + { + if(node->type != SY_DUMMY) + { + return node; + } + else + { + return NULL; + } + } + node = compare < 0 ? node->left : node->right; + } + return NULL; +} + +//========================================================================== +// +// SY_InsertLocal +// +//========================================================================== + +symbolNode_t *SY_InsertLocal(char *name, symbolType_t type) +{ + if(Find(name, GlobalRoot)) + { + ERR_Error(ERR_LOCAL_VAR_SHADOWED, YES); + } + MS_Message(MSG_DEBUG, "Inserting local identifier: %s (%s)\n", + name, SymbolTypeNames[type]); + return Insert(name, type, &LocalRoot); +} + +//========================================================================== +// +// SY_InsertGlobal +// +//========================================================================== + +symbolNode_t *SY_InsertGlobal(char *name, symbolType_t type) +{ + MS_Message(MSG_DEBUG, "Inserting global identifier: %s (%s)\n", + name, SymbolTypeNames[type]); + return Insert(name, type, &GlobalRoot); +} + +//========================================================================== +// +// SY_InsertGlobalUnique +// +//========================================================================== + +symbolNode_t *SY_InsertGlobalUnique(char *name, symbolType_t type) +{ + if(SY_FindGlobal(name) != NULL) + { // Redefined + ERR_Exit(ERR_REDEFINED_IDENTIFIER, YES, name); + } + return SY_InsertGlobal(name, type); +} + +//========================================================================== +// +// Insert +// +//========================================================================== + +static symbolNode_t *Insert(char *name, symbolType_t type, + symbolNode_t **root) +{ + int compare; + symbolNode_t *newNode; + symbolNode_t *node; + + newNode = MS_Alloc(sizeof(symbolNode_t), ERR_NO_SYMBOL_MEM); + newNode->name = MS_Alloc(strlen(name)+1, ERR_NO_SYMBOL_MEM); + strcpy(newNode->name, name); + newNode->left = newNode->right = NULL; + newNode->type = type; + newNode->unused = NO; + newNode->imported = ImportMode == IMPORT_Importing; + while((node = *root) != NULL) + { + compare = strcmp(name, node->name); + root = compare < 0 ? &(node->left) : &(node->right); + } + *root = newNode; + return(newNode); +} + +//========================================================================== +// +// SY_FreeLocals +// +//========================================================================== + +void SY_FreeLocals(void) +{ + MS_Message(MSG_DEBUG, "Freeing local identifiers\n"); + FreeNodes(LocalRoot); + LocalRoot = NULL; +} + +//========================================================================== +// +// SY_FreeGlobals +// +//========================================================================== + +void SY_FreeGlobals(void) +{ + MS_Message(MSG_DEBUG, "Freeing global identifiers\n"); + FreeNodes(GlobalRoot); + GlobalRoot = NULL; +} + +//========================================================================== +// +// FreeNodes +// +//========================================================================== + +static void FreeNodes(symbolNode_t *root) +{ + if(root == NULL) + { + return; + } + FreeNodes(root->left); + FreeNodes(root->right); + free(root->name); + free(root); +} + +//========================================================================== +// +// SY_ClearShared +// +//========================================================================== + +void SY_ClearShared(void) +{ + MS_Message(MSG_DEBUG, "Marking library exports as unused\n"); + ClearShared(GlobalRoot); +} + +//========================================================================== +// +// ClearShared +// +//========================================================================== + +static void ClearShared(symbolNode_t *root) +{ + while(root != NULL) + { + if( root->type == SY_SCRIPTFUNC || + root->type == SY_MAPVAR || + root->type == SY_MAPARRAY) + { + root->unused = YES; + } + ClearShared(root->left); + root = root->right; + } +} diff --git a/symbol.h b/symbol.h new file mode 100644 index 0000000..1fc786d --- /dev/null +++ b/symbol.h @@ -0,0 +1,127 @@ + +//************************************************************************** +//** +//** symbol.h +//** +//************************************************************************** + +#ifndef __SYMBOL_H__ +#define __SYMBOL_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include "common.h" +#include "pcode.h" + +// MACROS ------------------------------------------------------------------ + +#define MAX_ARRAY_DIMS 8 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + SY_DUMMY, + SY_LABEL, + SY_SCRIPTVAR, + SY_SCRIPTALIAS, + SY_MAPVAR, + SY_WORLDVAR, + SY_GLOBALVAR, + SY_MAPARRAY, + SY_WORLDARRAY, + SY_GLOBALARRAY, + SY_SPECIAL, + SY_CONSTANT, + SY_INTERNFUNC, + SY_SCRIPTFUNC +} symbolType_t; + +typedef struct +{ + U_BYTE index; +} symVar_t; + +typedef struct +{ + U_BYTE index; + int dimensions[MAX_ARRAY_DIMS]; + int ndim; + int size; +} symArray_t; + +typedef struct +{ + int address; +} symLabel_t; + +typedef struct +{ + U_BYTE value; + int argCount; +} symSpecial_t; + +typedef struct +{ + int value; +} symConstant_t; + +typedef struct +{ + pcd_t directCommand; + pcd_t stackCommand; + int argCount; + int optMask; + int outMask; + boolean hasReturnValue; + boolean latent; +} symInternFunc_t; + +typedef struct +{ + int address; + int argCount; + int varCount; + int funcNumber; + boolean hasReturnValue; + int sourceLine; + char *sourceName; + boolean predefined; +} symScriptFunc_t; + +typedef struct symbolNode_s +{ + struct symbolNode_s *left; + struct symbolNode_s *right; + char *name; + symbolType_t type; + boolean unused; + boolean imported; + union + { + symVar_t var; + symArray_t array; + symLabel_t label; + symSpecial_t special; + symConstant_t constant; + symInternFunc_t internFunc; + symScriptFunc_t scriptFunc; + } info; +} symbolNode_t; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void SY_Init(void); +symbolNode_t *SY_Find(char *name); +symbolNode_t *SY_FindLocal(char *name); +symbolNode_t *SY_FindGlobal(char *name); +symbolNode_t *SY_InsertLocal(char *name, symbolType_t type); +symbolNode_t *SY_InsertGlobal(char *name, symbolType_t type); +symbolNode_t *SY_InsertGlobalUnique(char *name, symbolType_t type); +void SY_FreeLocals(void); +void SY_FreeGlobals(void); +void SY_ClearShared(void); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +#endif diff --git a/token.c b/token.c new file mode 100644 index 0000000..282df06 --- /dev/null +++ b/token.c @@ -0,0 +1,1374 @@ + +//************************************************************************** +//** +//** token.c +//** +//************************************************************************** + +// HEADER FILES ------------------------------------------------------------ + +#ifdef __NeXT__ +#include +#else +#ifndef unix +#include +#endif +#include +#include +#endif +#include +#include +#include +#include "common.h" +#include "token.h" +#include "error.h" +#include "misc.h" +#include "symbol.h" +#include "parse.h" + +// MACROS ------------------------------------------------------------------ + +#define NON_HEX_DIGIT 255 +#define MAX_NESTED_SOURCES 16 + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + CHR_EOF, + CHR_LETTER, + CHR_NUMBER, + CHR_QUOTE, + CHR_SPECIAL +} chr_t; + +typedef struct +{ + char *name; + char *start; + char *end; + char *position; + int line; + boolean incLineNumber; + boolean imported; + enum ImportModes prevMode; + char lastChar; +} nestInfo_t; + +// EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +// PRIVATE FUNCTION PROTOTYPES --------------------------------------------- + +static int SortKeywords(const void *a, const void *b); +static void MakeIncludePath(char *sourceName); +static int PopNestedSource(enum ImportModes *prevMode); +static void ProcessLetterToken(void); +static void ProcessNumberToken(void); +static void EvalFixedConstant(int whole); +static void EvalHexConstant(void); +static void EvalRadixConstant(void); +static int DigitValue(char digit, int radix); +static void ProcessQuoteToken(void); +static void ProcessSpecialToken(void); +static boolean CheckForKeyword(void); +static boolean CheckForLineSpecial(void); +static boolean CheckForConstant(void); +static void NextChr(void); +static void SkipComment(void); +static void SkipCPPComment(void); +static void BumpMasterSourceLine(char Chr, boolean clear); // master line - Ty 07jan2000 +static char *AddFileName(const char *name); +static int OctalChar(); + +// EXTERNAL DATA DECLARATIONS ---------------------------------------------- + +// PUBLIC DATA DEFINITIONS ------------------------------------------------- + +tokenType_t tk_Token; +int tk_Line; +int tk_Number; +char *tk_String; +U_BYTE tk_SpecialValue; +int tk_SpecialArgCount; +char *tk_SourceName; +int tk_IncludedLines; +boolean forSemicolonHack; +char MasterSourceLine[MAX_STATEMENT_LENGTH+1]; // master line - Ty 07jan2000 +int MasterSourcePos; // master position - Ty 07jan2000 +int PrevMasterSourcePos; // previous master position - RH 09feb2000 +boolean ClearMasterSourceLine; // master clear flag - Ty 07jan2000 + +// PRIVATE DATA DEFINITIONS ------------------------------------------------ + +static char Chr; +static char *FileStart; +static char *FilePtr; +static char *FileEnd; +static boolean SourceOpen; +static char ASCIIToChrCode[256]; +static byte ASCIIToHexDigit[256]; +static char TokenStringBuffer[MAX_QUOTED_LENGTH]; +static nestInfo_t OpenFiles[MAX_NESTED_SOURCES]; +static boolean AlreadyGot; +static int NestDepth; +static boolean IncLineNumber; +static char IncludePath[MAX_FILE_NAME_LENGTH]; +static char *FileNames; +static size_t FileNamesLen, FileNamesMax; + +static struct keyword_s +{ + char *name; + tokenType_t token; +} Keywords[] = +{ + { "break", TK_BREAK }, + { "case", TK_CASE }, + { "const", TK_CONST }, + { "continue", TK_CONTINUE }, + { "default", TK_DEFAULT }, + { "define", TK_DEFINE }, + { "do", TK_DO }, + { "else", TK_ELSE }, + { "for", TK_FOR }, + { "goto", TK_GOTO }, + { "if", TK_IF }, + { "include", TK_INCLUDE }, + { "int", TK_INT }, + { "open", TK_OPEN }, + { "print", TK_PRINT }, + { "printbold", TK_PRINTBOLD }, + { "log", TK_LOG }, + { "hudmessage", TK_HUDMESSAGE }, + { "hudmessagebold", TK_HUDMESSAGEBOLD }, + { "restart", TK_RESTART }, + { "script", TK_SCRIPT }, + { "special", TK_SPECIAL }, + { "str", TK_STR }, + { "suspend", TK_SUSPEND }, + { "switch", TK_SWITCH }, + { "terminate", TK_TERMINATE }, + { "until", TK_UNTIL }, + { "void", TK_VOID }, + { "while", TK_WHILE }, + { "world", TK_WORLD }, + { "global", TK_GLOBAL }, + // [BC] Start Skulltag tokens. + { "respawn", TK_RESPAWN }, + { "death", TK_DEATH }, + { "enter", TK_ENTER }, + { "pickup", TK_PICKUP }, + { "bluereturn", TK_BLUERETURN }, + { "redreturn", TK_REDRETURN }, + { "whitereturn", TK_WHITERETURN }, + // [BC] End Skulltag tokens. + { "nocompact", TK_NOCOMPACT }, + { "lightning", TK_LIGHTNING }, + { "createtranslation", TK_CREATETRANSLATION }, + { "function", TK_FUNCTION }, + { "return", TK_RETURN }, + { "wadauthor", TK_WADAUTHOR }, + { "nowadauthor", TK_NOWADAUTHOR }, + { "acs_executewait", TK_ACSEXECUTEWAIT }, + { "encryptstrings", TK_ENCRYPTSTRINGS }, + { "import", TK_IMPORT }, + { "library", TK_LIBRARY }, + { "libdefine", TK_LIBDEFINE }, + { "bool", TK_BOOL }, + { "net", TK_NET }, + { "disconnect", TK_DISCONNECT }, + { "unloading", TK_UNLOADING }, + { "static", TK_STATIC } +}; + +#define NUM_KEYWORDS (sizeof(Keywords)/sizeof(Keywords[0])) + +// CODE -------------------------------------------------------------------- + +//========================================================================== +// +// TK_Init +// +//========================================================================== + +void TK_Init(void) +{ + int i; + + for(i = 0; i < 256; i++) + { + ASCIIToChrCode[i] = CHR_SPECIAL; + ASCIIToHexDigit[i] = NON_HEX_DIGIT; + } + for(i = '0'; i <= '9'; i++) + { + ASCIIToChrCode[i] = CHR_NUMBER; + ASCIIToHexDigit[i] = i-'0'; + } + for(i = 'A'; i <= 'F'; i++) + { + ASCIIToHexDigit[i] = 10+(i-'A'); + } + for(i = 'a'; i <= 'f'; i++) + { + ASCIIToHexDigit[i] = 10+(i-'a'); + } + for(i = 'A'; i <= 'Z'; i++) + { + ASCIIToChrCode[i] = CHR_LETTER; + } + for(i = 'a'; i <= 'z'; i++) + { + ASCIIToChrCode[i] = CHR_LETTER; + } + ASCIIToChrCode[ASCII_QUOTE] = CHR_QUOTE; + ASCIIToChrCode[ASCII_UNDERSCORE] = CHR_LETTER; + ASCIIToChrCode[EOF_CHARACTER] = CHR_EOF; + tk_String = TokenStringBuffer; + IncLineNumber = FALSE; + tk_IncludedLines = 0; + SourceOpen = FALSE; + *MasterSourceLine = '\0'; // master line - Ty 07jan2000 + MasterSourcePos = 0; // master position - Ty 07jan2000 + ClearMasterSourceLine = TRUE; // clear the line to start + qsort (Keywords, NUM_KEYWORDS, sizeof(Keywords[0]), SortKeywords); + FileNames = MS_Alloc(4096, ERR_OUT_OF_MEMORY); + FileNamesLen = 0; + FileNamesMax = 4096; +} + +//========================================================================== +// +// SortKeywords +// +//========================================================================== + +static int SortKeywords(const void *a, const void *b) +{ + return strcmp (((struct keyword_s *)a)->name, + ((struct keyword_s *)b)->name); +} + +//========================================================================== +// +// TK_OpenSource +// +//========================================================================== + +void TK_OpenSource(char *fileName) +{ + int size; + + TK_CloseSource(); + size = MS_LoadFile(fileName, &FileStart); + tk_SourceName = AddFileName(fileName); + MakeIncludePath(fileName); + SourceOpen = TRUE; + FileEnd = FileStart+size; + FilePtr = FileStart; + tk_Line = 1; + tk_Token = TK_NONE; + AlreadyGot = FALSE; + NestDepth = 0; + NextChr(); +} + +//========================================================================== +// +// AddFileName +// +//========================================================================== + +static char *AddFileName(const char *name) +{ + size_t len = strlen(name) + 1; + char *namespot; + + if (FileNamesLen + len > FileNamesMax) + { + FileNames = MS_Alloc(FileNamesMax, ERR_OUT_OF_MEMORY); + FileNamesLen = 0; + } + namespot = FileNames + FileNamesLen; + memcpy(namespot, name, len); + FileNamesLen += len; + return namespot; +} + +//========================================================================== +// +// MakeIncludePath +// +//========================================================================== + +static void MakeIncludePath(char *sourceName) +{ + strcpy(IncludePath, sourceName); + if(MS_StripFilename(IncludePath) == NO) + { + IncludePath[0] = 0; + } + else + { // Add a directory delimiter to the include path + strcat(IncludePath, DIRECTORY_DELIMITER); + } +} + +//========================================================================== +// +// TK_Include +// +//========================================================================== + +void TK_Include(char *fileName) +{ + char sourceName[MAX_FILE_NAME_LENGTH]; + int size; + nestInfo_t *info; + + MS_Message(MSG_DEBUG, "*Including %s\n", fileName); + if(NestDepth == MAX_NESTED_SOURCES) + { + ERR_Exit(ERR_INCL_NESTING_TOO_DEEP, YES, fileName); + } + info = &OpenFiles[NestDepth++]; + info->name = tk_SourceName; + info->start = FileStart; + info->end = FileEnd; + info->position = FilePtr; + info->line = tk_Line; + info->incLineNumber = IncLineNumber; + info->lastChar = Chr; + info->imported = NO; + strcpy(sourceName, IncludePath); + strcat(sourceName, fileName); + tk_SourceName = AddFileName(sourceName); + size = MS_LoadFile(tk_SourceName, &FileStart); + FileEnd = FileStart+size; + FilePtr = FileStart; + tk_Line = 1; + IncLineNumber = FALSE; + tk_Token = TK_NONE; + AlreadyGot = FALSE; + BumpMasterSourceLine('x',TRUE); // dummy x + NextChr(); +} + +//========================================================================== +// +// TK_Import +// +//========================================================================== + +void TK_Import(char *fileName, enum ImportModes prevMode) +{ + TK_Include (fileName); + OpenFiles[NestDepth - 1].imported = YES; + OpenFiles[NestDepth - 1].prevMode = prevMode; + ImportMode = IMPORT_Importing; +} + +//========================================================================== +// +// PopNestedSource +// +//========================================================================== + +static int PopNestedSource(enum ImportModes *prevMode) +{ + nestInfo_t *info; + + MS_Message(MSG_DEBUG, "*Leaving %s\n", tk_SourceName); + free(FileStart); + tk_IncludedLines += tk_Line; + info = &OpenFiles[--NestDepth]; + tk_SourceName = info->name; + FileStart = info->start; + FileEnd = info->end; + FilePtr = info->position; + tk_Line = info->line; + IncLineNumber = info->incLineNumber; + Chr = info->lastChar; + tk_Token = TK_NONE; + AlreadyGot = FALSE; + *prevMode = info->prevMode; + return info->imported ? 2 : 0; +} + +//========================================================================== +// +// TK_CloseSource +// +//========================================================================== + +void TK_CloseSource(void) +{ + int i; + + if(SourceOpen) + { + free(FileStart); + for(i = 0; i < NestDepth; i++) + { + free(OpenFiles[i].start); + } + SourceOpen = FALSE; + } +} + +//========================================================================== +// +// TK_NextToken +// +//========================================================================== + +tokenType_t TK_NextToken(void) +{ + enum ImportModes prevMode; + boolean validToken; + + if(AlreadyGot == TRUE) + { + int t = MasterSourcePos; + MasterSourcePos = PrevMasterSourcePos; + PrevMasterSourcePos = t; + AlreadyGot = FALSE; + return tk_Token; + } + validToken = NO; + PrevMasterSourcePos = MasterSourcePos; + do + { + while(Chr == ASCII_SPACE) + { + NextChr(); + } + switch(ASCIIToChrCode[(byte)Chr]) + { + case CHR_EOF: + tk_Token = TK_EOF; + break; + case CHR_LETTER: + ProcessLetterToken(); + break; + case CHR_NUMBER: + ProcessNumberToken(); + break; + case CHR_QUOTE: + ProcessQuoteToken(); + break; + default: + ProcessSpecialToken(); + break; + } + if(tk_Token == TK_STARTCOMMENT) + { + SkipComment(); + } + else if(tk_Token == TK_CPPCOMMENT) + { + SkipCPPComment(); + } + else if((tk_Token == TK_EOF) && (NestDepth > 0)) + { + if (PopNestedSource(&prevMode)) + { + ImportMode = prevMode; + if(!ExporterFlagged) + { + ERR_Exit(ERR_EXPORTER_NOT_FLAGGED, NO); + } + SY_ClearShared(); + } + } + else + { + validToken = YES; + } + } while(validToken == NO); + return tk_Token; +} + +//========================================================================== +// +// TK_NextCharacter +// +//========================================================================== + +int TK_NextCharacter(void) +{ + int c; + + while(Chr == ASCII_SPACE) + { + NextChr(); + } + c = (int)Chr; + if(c == EOF_CHARACTER) + { + c = -1; + } + NextChr(); + return c; +} + +//========================================================================== +// +// TK_SkipPast +// +//========================================================================== + +void TK_SkipPast(tokenType_t token) +{ + while (tk_Token != token) + { + TK_NextToken(); + } + TK_NextToken(); +} + +//========================================================================== +// +// TK_SkipTo +// +//========================================================================== + +void TK_SkipTo(tokenType_t token) +{ + while (tk_Token != token) + { + TK_NextToken(); + } +} + +//========================================================================== +// +// TK_NextTokenMustBe +// +//========================================================================== + +boolean TK_NextTokenMustBe(tokenType_t token, error_t error) +{ + if(TK_NextToken() != token) + { + ERR_Error(error, YES); + /* + if(skipToken == TK_EOF) + { + ERR_Finish(); + } + else if(skipToken != TK_NONE) + { + TK_SkipPast(skipToken); + } + return NO; + */ + ERR_Finish(); + } + return YES; +} + +//========================================================================== +// +// TK_TokenMustBe +// +//========================================================================== + +boolean TK_TokenMustBe(tokenType_t token, error_t error) +{ + if (token == TK_SEMICOLON && forSemicolonHack) + { + token = TK_RPAREN; + } + if(tk_Token != token) + { + ERR_Error(error, YES); + /* + if(skipToken == TK_EOF) + { + ERR_Finish(); + } + else if(skipToken != TK_NONE) + { + while(tk_Token != skipToken) + { + TK_NextToken(); + } + } + return NO; + */ + ERR_Finish(); + } + return YES; +} + +//========================================================================== +// +// TK_Member +// +//========================================================================== + +boolean TK_Member(tokenType_t *list) +{ + int i; + + for(i = 0; list[i] != TK_NONE; i++) + { + if(tk_Token == list[i]) + { + return YES; + } + } + return NO; +} + +//========================================================================== +// +// TK_Undo +// +//========================================================================== + +void TK_Undo(void) +{ + if(tk_Token != TK_NONE) + { + if (AlreadyGot == FALSE) + { + int t = MasterSourcePos; + MasterSourcePos = PrevMasterSourcePos; + PrevMasterSourcePos = t; + AlreadyGot = TRUE; + } + } +} + +//========================================================================== +// +// ProcessLetterToken +// +//========================================================================== + +static void ProcessLetterToken(void) +{ + int i; + char *text; + + i = 0; + text = TokenStringBuffer; + while (ASCIIToChrCode[(byte)Chr] == CHR_LETTER + || ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) + { + if(++i == MAX_IDENTIFIER_LENGTH) + { + ERR_Error(ERR_IDENTIFIER_TOO_LONG, YES); + } + if(i < MAX_IDENTIFIER_LENGTH) + { + *text++ = Chr; + } + NextChr(); + } + *text = 0; + MS_StrLwr(TokenStringBuffer); + if(CheckForKeyword() == FALSE + && CheckForLineSpecial() == FALSE + && CheckForConstant() == FALSE) + { + tk_Token = TK_IDENTIFIER; + } +} + +//========================================================================== +// +// CheckForKeyword +// +//========================================================================== + +static boolean CheckForKeyword(void) +{ + int min, max, probe, lexx; + + // [RH] Use a binary search + min = 0; + max = NUM_KEYWORDS-1; + probe = NUM_KEYWORDS/2; + + while (max - min >= 0) + { + lexx = strcmp(tk_String, Keywords[probe].name); + if(lexx == 0) + { + tk_Token = Keywords[probe].token; + return TRUE; + } + else if(lexx < 0) + { + max = probe-1; + } + else + { + min = probe+1; + } + probe = (max-min)/2+min; + } + return FALSE; +} + +//========================================================================== +// +// CheckForLineSpecial +// +//========================================================================== + +static boolean CheckForLineSpecial(void) +{ + symbolNode_t *sym; + + sym = SY_FindGlobal(tk_String); + if(sym == NULL) + { + return FALSE; + } + if(sym->type != SY_SPECIAL) + { + return FALSE; + } + tk_Token = TK_LINESPECIAL; + tk_SpecialValue = sym->info.special.value; + tk_SpecialArgCount = sym->info.special.argCount; + return TRUE; +} + +//========================================================================== +// +// CheckForConstant +// +//========================================================================== + +static boolean CheckForConstant(void) +{ + symbolNode_t *sym; + + sym = SY_FindGlobal(tk_String); + if(sym == NULL) + { + return FALSE; + } + if(sym->type != SY_CONSTANT) + { + return FALSE; + } + tk_Token = TK_NUMBER; + tk_Number = sym->info.constant.value; + return TRUE; +} + +//========================================================================== +// +// ProcessNumberToken +// +//========================================================================== + +static void ProcessNumberToken(void) +{ + char c; + + c = Chr; + NextChr(); + if(c == '0' && (Chr == 'x' || Chr == 'X')) + { // Hexadecimal constant + NextChr(); + EvalHexConstant(); + return; + } + tk_Number = c-'0'; + while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) + { + tk_Number = 10*tk_Number+(Chr-'0'); + NextChr(); + } + if(Chr == '.') + { // Fixed point + NextChr(); // Skip period + EvalFixedConstant(tk_Number); + return; + } + if(Chr == ASCII_UNDERSCORE) + { + NextChr(); // Skip underscore + EvalRadixConstant(); + return; + } + tk_Token = TK_NUMBER; +} + +//========================================================================== +// +// EvalFixedConstant +// +//========================================================================== + +static void EvalFixedConstant(int whole) +{ + int frac; + int divisor; + + frac = 0; + divisor = 1; + while(ASCIIToChrCode[(byte)Chr] == CHR_NUMBER) + { + frac = 10*frac+(Chr-'0'); + divisor *= 10; + NextChr(); + } + tk_Number = (whole<<16)+((frac<<16)/divisor); + tk_Token = TK_NUMBER; +} + +//========================================================================== +// +// EvalHexConstant +// +//========================================================================== + +static void EvalHexConstant(void) +{ + tk_Number = 0; + while(ASCIIToHexDigit[(byte)Chr] != NON_HEX_DIGIT) + { + tk_Number = (tk_Number<<4)+ASCIIToHexDigit[(byte)Chr]; + NextChr(); + } + tk_Token = TK_NUMBER; +} + +//========================================================================== +// +// EvalRadixConstant +// +//========================================================================== + +static void EvalRadixConstant(void) +{ + int radix; + int digitVal; + + radix = tk_Number; + if(radix < 2 || radix > 36) + { + ERR_Error(ERR_BAD_RADIX_CONSTANT, YES, NULL); + radix = 36; + } + tk_Number = 0; + while((digitVal = DigitValue(Chr, radix)) != -1) + { + tk_Number = radix*tk_Number+digitVal; + NextChr(); + } + tk_Token = TK_NUMBER; +} + +//========================================================================== +// +// DigitValue +// +// Returns -1 if the digit is not allowed in the specified radix. +// +//========================================================================== + +static int DigitValue(char digit, int radix) +{ + digit = toupper(digit); + if(digit < '0' || (digit > '9' && digit < 'A') || digit > 'Z') + { + return -1; + } + if(digit > '9') + { + digit = 10+digit-'A'; + } + else + { + digit -= '0'; + } + if(digit >= radix) + { + return -1; + } + return digit; +} + +//========================================================================== +// +// ProcessQuoteToken +// +//========================================================================== + +static void ProcessQuoteToken(void) +{ + int i; + char *text; + boolean escaped; + + i = 0; + escaped = FALSE; + text = TokenStringBuffer; + NextChr(); + while(Chr != EOF_CHARACTER) + { + if(Chr == ASCII_QUOTE && escaped == 0) // [JB] + { + break; + } + if(++i == MAX_QUOTED_LENGTH) + { + ERR_Error(ERR_STRING_TOO_LONG, YES, NULL); + } + if(i < MAX_QUOTED_LENGTH) + { + *text++ = Chr; + } + // escape the character after a backslash [JB] + if(Chr == ASCII_BACKSLASH) + escaped ^= (Chr == ASCII_BACKSLASH); + else + escaped = FALSE; + NextChr(); + } + *text = 0; + if(Chr == ASCII_QUOTE) + { + NextChr(); + } + tk_Token = TK_STRING; +} + +//========================================================================== +// +// ProcessSpecialToken +// +//========================================================================== + +static void ProcessSpecialToken(void) +{ + char c; + + c = Chr; + NextChr(); + switch(c) + { + case '+': + switch(Chr) + { + case '=': + tk_Token = TK_ADDASSIGN; + NextChr(); + break; + case '+': + tk_Token = TK_INC; + NextChr(); + break; + default: + tk_Token = TK_PLUS; + break; + } + break; + case '-': + switch(Chr) + { + case '=': + tk_Token = TK_SUBASSIGN; + NextChr(); + break; + case '-': + tk_Token = TK_DEC; + NextChr(); + break; + default: + tk_Token = TK_MINUS; + break; + } + break; + case '*': + switch(Chr) + { + case '=': + tk_Token = TK_MULASSIGN; + NextChr(); + break; + case '/': + tk_Token = TK_ENDCOMMENT; + NextChr(); + break; + default: + tk_Token = TK_ASTERISK; + break; + } + break; + case '/': + switch(Chr) + { + case '=': + tk_Token = TK_DIVASSIGN; + NextChr(); + break; + case '/': + tk_Token = TK_CPPCOMMENT; + break; + case '*': + tk_Token = TK_STARTCOMMENT; + NextChr(); + break; + default: + tk_Token = TK_SLASH; + break; + } + break; + case '%': + if(Chr == '=') + { + tk_Token = TK_MODASSIGN; + NextChr(); + } + else + { + tk_Token = TK_PERCENT; + } + break; + case '=': + if(Chr == '=') + { + tk_Token = TK_EQ; + NextChr(); + } + else + { + tk_Token = TK_ASSIGN; + } + break; + case '<': + if(Chr == '=') + { + tk_Token = TK_LE; + NextChr(); + } + else if(Chr == '<') + { + tk_Token = TK_LSHIFT; + NextChr(); + } + else + { + tk_Token = TK_LT; + } + break; + case '>': + if(Chr == '=') + { + tk_Token = TK_GE; + NextChr(); + } + else if(Chr == '>') + { + tk_Token = TK_RSHIFT; + NextChr(); + } + else + { + tk_Token = TK_GT; + } + break; + case '!': + if(Chr == '=') + { + tk_Token = TK_NE; + NextChr(); + } + else + { + tk_Token = TK_NOT; + } + break; + case '&': + if(Chr == '&') + { + tk_Token = TK_ANDLOGICAL; + NextChr(); + } + else + { + tk_Token = TK_ANDBITWISE; + } + break; + case '|': + if(Chr == '|') + { + tk_Token = TK_ORLOGICAL; + NextChr(); + } + else + { + tk_Token = TK_ORBITWISE; + } + break; + case '(': + tk_Token = TK_LPAREN; + break; + case ')': + tk_Token = TK_RPAREN; + break; + case '{': + tk_Token = TK_LBRACE; + break; + case '}': + tk_Token = TK_RBRACE; + break; + case '[': + tk_Token = TK_LBRACKET; + break; + case ']': + tk_Token = TK_RBRACKET; + break; + case ':': + tk_Token = TK_COLON; + break; + case ';': + tk_Token = TK_SEMICOLON; + break; + case ',': + tk_Token = TK_COMMA; + break; + case '.': + tk_Token = TK_PERIOD; + break; + case '#': + tk_Token = TK_NUMBERSIGN; + break; + case '^': + tk_Token = TK_EORBITWISE; + break; + case '~': + tk_Token = TK_TILDE; + break; + case '\'': + if(Chr == '\\') + { + NextChr(); + switch(Chr) + { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + tk_Number = OctalChar(); + break; + case 'x': case 'X': + NextChr(); + EvalHexConstant(); + if(Chr != '\'') + { + ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL); + } + NextChr(); + break; + case 'a': + tk_Number = '\a'; + break; + case 'b': + tk_Number = '\b'; + break; + case 't': + tk_Number = '\t'; + break; + case 'v': + tk_Number = '\v'; + break; + case 'n': + tk_Number = '\n'; + break; + case 'f': + tk_Number = '\f'; + break; + case 'r': + tk_Number = '\r'; + break; + case '\'': + tk_Number = Chr; + break; + default: + ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL); + } + tk_Token = TK_NUMBER; + } + else if(Chr == '\'') + { + ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL); + } + else + { + tk_Number = Chr; + tk_Token = TK_NUMBER; + } + NextChr(); + if(Chr != '\'') + { + ERR_Exit(ERR_BAD_CHARACTER_CONSTANT, YES, NULL); + } + NextChr(); + break; + default: + ERR_Exit(ERR_BAD_CHARACTER, YES, NULL); + break; + } +} + +//========================================================================== +// +// NextChr +// +//========================================================================== + +static void NextChr(void) +{ + if(FilePtr >= FileEnd) + { + Chr = EOF_CHARACTER; + return; + } + if(IncLineNumber == TRUE) + { + tk_Line++; + IncLineNumber = FALSE; + BumpMasterSourceLine('x',TRUE); // dummy x + } + Chr = *FilePtr++; + if(Chr < ASCII_SPACE && Chr >= 0) // Allow high ASCII characters + { + if(Chr == '\n') + { + IncLineNumber = TRUE; + } + Chr = ASCII_SPACE; + } + BumpMasterSourceLine(Chr,FALSE); +} + +//========================================================================== +// +// PeekChr // [JB] +// +//========================================================================== + +static int PeekChr(void) +{ + char ch; + if(FilePtr >= FileEnd) + { + return EOF_CHARACTER; + } + ch = *FilePtr-1; + if(ch < ASCII_SPACE && ch >= 0) // Allow high ASCII characters + { + ch = ASCII_SPACE; + } + return ch; +} + +//========================================================================== +// +// OctalChar // [JB] +// +//========================================================================== + +static int OctalChar() +{ + int digits = 1; + int code = Chr - '0'; + while(digits < 4 && PeekChr() >= '0' && PeekChr() <= '7') + { + NextChr(); + code = (code << 3) + Chr - '0'; + } + return code; +} + +//========================================================================== +// +// SkipComment +// +//========================================================================== + +void SkipComment(void) +{ + boolean first; + + first = FALSE; + while(Chr != EOF_CHARACTER) + { + if(first == TRUE && Chr == '/') + { + break; + } + first = (Chr == '*'); + NextChr(); + } + NextChr(); +} + +//========================================================================== +// +// SkipCPPComment +// +//========================================================================== + +void SkipCPPComment(void) +{ + while(FilePtr < FileEnd) + { + if(*FilePtr++ == '\n') + { + tk_Line++; + BumpMasterSourceLine('x',TRUE); // dummy x + break; + } + } + NextChr(); +} + +//========================================================================== +// +// BumpMasterSourceLine +// +//========================================================================== + +void BumpMasterSourceLine(char Chr, boolean clear) // master line - Ty 07jan2000 +{ + if (ClearMasterSourceLine) // set to clear last time, clear now for first character + { + *MasterSourceLine = '\0'; + MasterSourcePos = 0; + ClearMasterSourceLine = FALSE; + } + if (clear) + { + ClearMasterSourceLine = TRUE; + } + else + { + if (MasterSourcePos < MAX_STATEMENT_LENGTH) + MasterSourceLine[MasterSourcePos++] = Chr; + } +} + +//========================================================================== +// +// TK_SkipLine +// +//========================================================================== + +void TK_SkipLine(void) +{ + char *sourcenow = tk_SourceName; + int linenow = tk_Line; + do TK_NextToken(); while (tk_Line == linenow && tk_SourceName == sourcenow); +} diff --git a/token.h b/token.h new file mode 100644 index 0000000..3cd1700 --- /dev/null +++ b/token.h @@ -0,0 +1,159 @@ + +//************************************************************************** +//** +//** token.h +//** +//************************************************************************** + +#ifndef __TOKEN_H__ +#define __TOKEN_H__ + +// HEADER FILES ------------------------------------------------------------ + +#include "common.h" +#include "error.h" + +// MACROS ------------------------------------------------------------------ + +// TYPES ------------------------------------------------------------------- + +typedef enum +{ + TK_NONE, + TK_EOF, + TK_IDENTIFIER, // VALUE: (char *) tk_String + TK_STRING, // VALUE: (char *) tk_String + TK_NUMBER, // VALUE: (int) tk_Number + TK_LINESPECIAL, // VALUE: (int) tk_LineSpecial + TK_PLUS, // '+' + TK_MINUS, // '-' + TK_ASTERISK, // '*' + TK_SLASH, // '/' + TK_PERCENT, // '%' + TK_ASSIGN, // '=' + TK_ADDASSIGN, // '+=' + TK_SUBASSIGN, // '-=' + TK_MULASSIGN, // '*=' + TK_DIVASSIGN, // '/=' + TK_MODASSIGN, // '%=' + TK_INC, // '++' + TK_DEC, // '--' + TK_EQ, // '==' + TK_NE, // '!=' + TK_LT, // '<' + TK_GT, // '>' + TK_LE, // '<=' + TK_GE, // '>=' + TK_LSHIFT, // '<<' + TK_RSHIFT, // '>>' + TK_ANDLOGICAL, // '&&' + TK_ORLOGICAL, // '||' + TK_ANDBITWISE, // '&' + TK_ORBITWISE, // '|' + TK_EORBITWISE, // '^' + TK_TILDE, // '~' + TK_LPAREN, // '(' + TK_RPAREN, // ')' + TK_LBRACE, // '{' + TK_RBRACE, // '}' + TK_LBRACKET, // '[' + TK_RBRACKET, // ']' + TK_COLON, // ':' + TK_SEMICOLON, // ';' + TK_COMMA, // ',' + TK_PERIOD, // '.' + TK_NOT, // '!' + TK_NUMBERSIGN, // '#' + TK_CPPCOMMENT, // '//' + TK_STARTCOMMENT, // '/*' + TK_ENDCOMMENT, // '*/' + TK_BREAK, // 'break' + TK_CASE, // 'case' + TK_CONST, // 'const' + TK_CONTINUE, // 'continue' + TK_DEFAULT, // 'default' + TK_DEFINE, // 'define' + TK_DO, // 'do' + TK_ELSE, // 'else' + TK_FOR, // 'for' + TK_GOTO, // 'goto' + TK_IF, // 'if' + TK_INCLUDE, // 'include' + TK_INT, // 'int' + TK_OPEN, // 'open' + TK_PRINT, // 'print' + TK_PRINTBOLD, // 'printbold' + TK_LOG, // 'log' + TK_HUDMESSAGE, // 'hudmessage' + TK_HUDMESSAGEBOLD, // 'hudmessagebold' + TK_RESTART, // 'restart' + TK_SCRIPT, // 'script' + TK_SPECIAL, // 'special' + TK_STR, // 'str' + TK_SUSPEND, // 'suspend' + TK_SWITCH, // 'switch' + TK_TERMINATE, // 'terminate' + TK_UNTIL, // 'until' + TK_VOID, // 'void' + TK_WHILE, // 'while' + TK_WORLD, // 'world' + TK_GLOBAL, // 'global' + TK_RESPAWN, // 'respawn' [BC] + TK_DEATH, // 'death' [BC] + TK_ENTER, // 'enter' [BC] + TK_PICKUP, // 'pickup' [BC] + TK_BLUERETURN, // 'bluereturn' [BC] + TK_REDRETURN, // 'redreturn' [BC] + TK_WHITERETURN, // 'whitereturn' [BC] + TK_NOCOMPACT, // 'nocompact' + TK_LIGHTNING, // 'ligtning' + TK_CREATETRANSLATION,// 'createtranslation' + TK_FUNCTION, // 'function' + TK_RETURN, // 'return' + TK_WADAUTHOR, // 'wadauthor' + TK_NOWADAUTHOR, // 'nowadauthor' + TK_ACSEXECUTEWAIT, // 'acs_executewait' + TK_ENCRYPTSTRINGS, // 'encryptstrings' + TK_IMPORT, // 'import' + TK_LIBRARY, // 'library' + TK_LIBDEFINE, // 'libdefine' + TK_BOOL, // 'bool' + TK_NET, // 'net' + TK_DISCONNECT, // 'disconnect' + TK_UNLOADING, // 'unloading' + TK_STATIC, // 'static' +} tokenType_t; + +// PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- + +void TK_Init(void); +void TK_OpenSource(char *fileName); +void TK_Include(char *fileName); +void TK_Import(char *fileName, enum ImportModes prevMode); +void TK_CloseSource(void); +tokenType_t TK_NextToken(void); +int TK_NextCharacter(void); +boolean TK_NextTokenMustBe(tokenType_t token, error_t error); +boolean TK_TokenMustBe(tokenType_t token, error_t error); +boolean TK_Member(tokenType_t *list); +void TK_Undo(void); +void TK_SkipLine(void); +void TK_SkipPast(tokenType_t token); +void TK_SkipTo(tokenType_t token); + +// PUBLIC DATA DECLARATIONS ------------------------------------------------ + +extern tokenType_t tk_Token; +extern int tk_Line; +extern int tk_Number; +extern char *tk_String; +extern U_BYTE tk_SpecialValue; +extern int tk_SpecialArgCount; +extern char *tk_SourceName; +extern int tk_IncludedLines; +extern boolean forSemicolonHack; +extern char MasterSourceLine[]; // master line - Ty 07jan2000 +extern int MasterSourcePos; // master position - Ty 07jan2000 +extern boolean ClearMasterSourceLine; // ready for new line - Ty 07jan2000 + +#endif diff --git a/zcommon.acs b/zcommon.acs new file mode 100644 index 0000000..7a43ef9 --- /dev/null +++ b/zcommon.acs @@ -0,0 +1,15 @@ + +//************************************************************************** +//** +//** zcommon.acs +//** +//************************************************************************** + +// If you are not using the -h command line switch and do not want to use +// WadAuthor's error checker, you can uncomment the following line to shave +// a few bytes off the size of compiled scripts. +//#nowadauthor + +#include "zspecial.acs" +#include "zdefs.acs" +#include "zwvars.acs" diff --git a/zdefs.acs b/zdefs.acs new file mode 100644 index 0000000..ab40660 --- /dev/null +++ b/zdefs.acs @@ -0,0 +1,466 @@ + +//************************************************************************** +//** +//** zdefs.acs +//** +//** Common definitions for use when compiling ACS scripts for ZDoom +//** +//************************************************************************** + +#define TRUE 1 +#define FALSE 0 +#define ON 1 +#define OFF 0 +#define YES 1 +#define NO 0 + +#define LINE_FRONT 0 +#define LINE_BACK 1 + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 + +#define TEXTURE_TOP 0 +#define TEXTURE_MIDDLE 1 +#define TEXTURE_BOTTOM 2 + +#define GAME_SINGLE_PLAYER 0 +#define GAME_NET_COOPERATIVE 1 +#define GAME_NET_DEATHMATCH 2 +#define GAME_TITLE_MAP 3 + +// Classes are only useful with Hexen +#define CLASS_FIGHTER 0 +#define CLASS_CLERIC 1 +#define CLASS_MAGE 2 + +#define SKILL_VERY_EASY 0 +#define SKILL_EASY 1 +#define SKILL_NORMAL 2 +#define SKILL_HARD 3 +#define SKILL_VERY_HARD 4 + +#define BLOCK_NOTHING 0 +#define BLOCK_CREATURES 1 +#define BLOCK_EVERYTHING 2 +#define BLOCK_RAILING 3 + +#define SCROLL 0 +#define CARRY 1 +#define SCROLL_AND_CARRY 2 + +// Means-of-death for Sector_SetDamage -------------------------------------- + +#define MOD_UNKNOWN 0 +#define MOD_ROCKET 5 +#define MOD_R_SPLASH 6 +#define MOD_PLASMARIFLE 7 +#define MOD_BFG_BOOM 8 +#define MOD_BFG_SPLASH 9 +#define MOD_CHAINSAW 10 +#define MOD_SSHOTGUN 11 +#define MOD_WATER 12 +#define MOD_SLIME 13 +#define MOD_LAVA 14 +#define MOD_CRUSH 15 +#define MOD_TELEFRAG 16 +#define MOD_FALLING 17 +#define MOD_SUICIDE 18 +#define MOD_BARREL 19 +#define MOD_EXIT 20 +#define MOD_SPLASH 21 +#define MOD_HIT 22 +#define MOD_RAILGUN 23 +#define MOD_ICE 24 +#define MOD_DISINTEGRATE 25 +#define MOD_POISON 26 +#define MOD_ELECTRIC 27 + +// Return values for PlayMovie ---------------------------------------------- + +#define MOVIE_Played 0 +#define MOVIE_Played_NoVideo 1 +#define MOVIE_Played_Aborted 2 +#define MOVIE_Failed -1 + + +// Player properties -------------------------------------------------------- + +#define PROP_FROZEN 0 +#define PROP_NOTARGET 1 +#define PROP_INSTANTWEAPONSWITCH 2 +#define PROP_FLY 3 +#define PROP_TOTALLYFROZEN 4 + +// The following properties correspond to powers given by certain items +#define PROP_INVULNERABILITY 5 +#define PROP_STRENGTH 6 +#define PROP_INVISIBILITY 7 +#define PROP_RADIATIONSUIT 8 +#define PROP_ALLMAP 9 +#define PROP_INFRARED 10 +#define PROP_WEAPONLEVEL2 11 +#define PROP_FLIGHT 12 +#define PROP_SPEED 15 + +// Text colors for hudmessage ----------------------------------------------- + +#define CR_UNTRANSLATED -1 +#define CR_BRICK 0 +#define CR_TAN 1 +#define CR_GRAY 2 +#define CR_GREY 2 +#define CR_GREEN 3 +#define CR_BROWN 4 +#define CR_GOLD 5 +#define CR_RED 6 +#define CR_BLUE 7 +#define CR_ORANGE 8 +#define CR_WHITE 9 +#define CR_YELLOW 10 + +// HUD message types -------------------------------------------------------- + +#define HUDMSG_PLAIN 0 +#define HUDMSG_FADEOUT 1 +#define HUDMSG_TYPEON 2 +#define HUDMSG_FADEINOUT 3 + +// OR this with one of the above to log the hudmessage to the console. +// i.e. instead of HUDMSG_PLAIN, you can use HUDMSG_PLAIN | HUDMSG_LOG +#define HUDMSG_LOG 0x80000000 + +// "Scripted" Marine weapon types ------------------------------------------- + +#define MARINEWEAPON_Dummy 0 +#define MARINEWEAPON_Fist 1 +#define MARINEWEAPON_BerserkFist 2 +#define MARINEWEAPON_Chainsaw 3 +#define MARINEWEAPON_Pistol 4 +#define MARINEWEAPON_Shotgun 5 +#define MARINEWEAPON_SuperShotgun 6 +#define MARINEWEAPON_Chaingun 7 +#define MARINEWEAPON_RocketLauncher 8 +#define MARINEWEAPON_PlasmaRifle 9 +#define MARINEWEAPON_Railgun 10 +#define MARINEWEAPON_BFG 11 + +// Actor properties you can get/set ----------------------------------------- + +#define APROP_Health 0 +#define APROP_Speed 1 +#define APROP_Damage 2 +#define APROP_Alpha 3 +#define APROP_RenderStyle 4 +#define APROP_Ambush 10 +#define APROP_Invulnerable 11 +#define APROP_SeeSound 5 // Sounds can only be set, not gotten +#define APROP_AttackSound 6 +#define APROP_PainSound 7 +#define APROP_DeathSound 8 +#define APROP_ActiveSound 9 + +// Render Styles ------------------------------------------------------------ + +#define STYLE_None 0 // Do not draw +#define STYLE_Normal 1 // Normal; just copy the image to the screen +#define STYLE_Fuzzy 2 // Draw silhouette using "fuzz" effect +#define STYLE_SoulTrans 3 // Draw translucent with amount in r_transsouls +#define STYLE_OptFuzzy 4 // Draw as fuzzy or translucent, based on user preference +#define STYLE_Translucent 64 // Draw translucent +#define STYLE_Add 65 // Draw additive + +// Properties you can use with GetLevelInfo() ------------------------------- + +#define LEVELINFO_PAR_TIME 0 +#define LEVELINFO_CLUSTERNUM 1 +#define LEVELINFO_LEVELNUM 2 +#define LEVELINFO_TOTAL_SECRETS 3 +#define LEVELINFO_FOUND_SECRETS 4 +#define LEVELINFO_TOTAL_ITEMS 5 +#define LEVELINFO_FOUND_ITEMS 6 +#define LEVELINFO_TOTAL_MONSTERS 7 +#define LEVELINFO_KILLED_MONSTERS 8 +#define LEVELINFO_SUCK_TIME 9 + + +// Shared spawnable things from Hexen. You can spawn these in the other ----- +// games if you provide sprites for them, otherwise they'll be invisible. --- + +#define T_ROCK1 41 +#define T_ROCK2 42 +#define T_ROCK3 43 +#define T_DIRT1 44 +#define T_DIRT2 45 +#define T_DIRT3 46 +#define T_DIRT4 47 +#define T_DIRT5 48 +#define T_DIRT6 49 +#define T_STAINEDGLASS1 54 +#define T_STAINEDGLASS2 55 +#define T_STAINEDGLASS3 56 +#define T_STAINEDGLASS4 57 +#define T_STAINEDGLASS5 58 +#define T_STAINEDGLASS6 59 +#define T_STAINEDGLASS7 60 +#define T_STAINEDGLASS8 61 +#define T_STAINEDGLASS9 62 +#define T_STAINEDGLASS0 63 + +// Doom Spawnable things (used for thingcount() and thing spawners) --------- + +#define T_NONE 0 +#define T_SHOTGUY 1 +#define T_CHAINGUY 2 +#define T_BARON 3 +#define T_ZOMBIE 4 +#define T_IMP 5 +#define T_ARACHNOTRON 6 +#define T_SPIDERMASTERMIND 7 +#define T_DEMON 8 +#define T_SPECTRE 9 +#define T_IMPFIREBALL 10 +#define T_CLIP 11 +#define T_SHELLS 12 +#define T_CACODEMON 19 +#define T_REVENANT 20 +#define T_BRIDGE 21 +#define T_ARMORBONUS 22 +#define T_STIMPACK 23 +#define T_MEDKIT 24 +#define T_SOULSPHERE 25 +#define T_SHOTGUN 27 +#define T_CHAINGUN 28 +#define T_ROCKETLAUNCHER 29 +#define T_PLASMAGUN 30 +#define T_BFG 31 +#define T_CHAINSAW 32 +#define T_SUPERSHOTGUN 33 +#define T_PLASMABOLT 51 +#define T_TRACER 53 +#define T_GREENARMOR 68 +#define T_BLUEARMOR 69 +#define T_CELL 75 +#define T_BLUEKEYCARD 85 +#define T_REDKEYCARD 86 +#define T_YELLOWKEYCARD 87 +#define T_YELLOWSKULLKEY 88 +#define T_REDSKULLKEY 89 +#define T_BLUESKULLKEY 90 +#define T_TEMPLARGEFLAME 98 +#define T_STEALTHBARON 100 +#define T_STEALTHKNIGHT 101 +#define T_STEALTHZOMBIE 102 +#define T_STEALTHSHOTGUY 103 + +#define T_LOSTSOUL 110 +#define T_VILE 111 +#define T_MANCUBUS 112 +#define T_HELLKNIGHT 113 +#define T_CYBERDEMON 114 +#define T_PAINELEMENTAL 115 +#define T_WOLFSS 116 +#define T_STEALTHARACHNOTRON 117 +#define T_STEALTHVILE 118 +#define T_STEALTHCACODEMON 119 +#define T_STEALTHCHAINGUY 120 +#define T_STEALTHSERGEANT 121 +#define T_STEALTHIMP 122 +#define T_STEALTHMANCUBUS 123 +#define T_STEALTHREVENANT 124 +#define T_BARREL 125 +#define T_CACODEMONSHOT 126 +#define T_ROCKET 127 +#define T_BFGSHOT 128 +#define T_ARACHNOTRONPLASMA 129 +#define T_BLOOD 130 +#define T_PUFF 131 +#define T_MEGASPHERE 132 +#define T_INVULNERABILITY 133 +#define T_BERSERK 134 +#define T_INVISIBILITY 135 +#define T_IRONFEET 136 +#define T_COMPUTERMAP 137 +#define T_LIGHTAMP 138 +#define T_AMMOBOX 139 +#define T_ROCKETAMMO 140 +#define T_ROCKETBOX 141 +#define T_BATTERY 142 +#define T_SHELLBOX 143 +#define T_BACKPACK 144 +#define T_GUTS 145 +#define T_BLOODPOOL 146 +#define T_BLOODPOOL1 147 +#define T_BLOODPOOL2 148 +#define T_FLAMINGBARREL 149 +#define T_BRAINS 150 +#define T_SCRIPTEDMARINE 151 +#define T_HEALTHBONUS 152 +#define T_MANCUBUSSHOT 153 +#define T_BARONBALL 154 + +// Heretic Spawnable things (used for thingcount() and thing spawners) ------ + +#define T_CLINK 1 +#define T_MUMMYLEADER 2 +#define T_BEAST 3 +#define T_MUMMY 4 +//#define T_IMP 5 // Defined above +#define T_KNIGHT 6 +#define T_IMPLEADER 7 +#define T_MUMMYGHOST 8 +#define T_MUMMYLEADERGHOST 9 +//#define T_IMPFIREBALL 10 +#define T_WIMPYWANDAMMO 11 +#define T_HEFTYWANDAMMO 12 +#define T_ITEMEGG 14 +#define T_ITEMFLIGHT 15 +#define T_ITEMTELEPORT 18 +#define T_WIZARD 19 +#define T_IRONLICH 20 +#define T_ITEMHEALTHPOTION 23 +#define T_ITEMHEALTHFLASH 24 +#define T_ITEMHEALTHFULL 25 +#define T_CROSSBOW 27 +#define T_BLASTER 28 +#define T_PHOENIXROD 29 +#define T_SKULLROD 30 +#define T_MACE 31 +#define T_GAUNTLETS 32 +#define T_WIMPYCROSSBOWAMMO 33 +#define T_HEFTYCROSSBOWAMMO 34 +#define T_WIMPYMACEAMMO 35 +#define T_HEFTYMACEAMMO 36 +#define T_WIMPYBLASTERAMMO 37 +#define T_HEFTYBLASTERAMMO 38 +#define T_MORPHBLAST 40 +#define T_SHIELD1 68 +#define T_SHIELD2 69 +#define T_ITEMTIMEBOMB 72 +#define T_ITEMTORCH 73 +#define T_BLUEKEY 85 +#define T_GREENKEY 86 +#define T_YELLOWKEY 87 + +#define T_SOUND_WIND 110 +#define T_SOUND_WATERFALL 111 + +#define T_BEASTBALL 120 +#define T_FEATHER 121 +#define T_CHICKEN 122 +#define T_VOLCANOBALL 123 +#define T_TINYVOLCANOBALL 124 +#define T_POD 125 +#define T_PODGENERATOR 126 +#define T_KNIGHTAXE 127 +#define T_KNIGHTBLOODAXE 128 +#define T_KNIGHTGHOST 129 +#define T_MUMMYHEAD 131 +#define T_SNAKE 132 +#define T_ITEMINVULNERABILITY 133 +#define T_ITEMTOME 134 +#define T_ITEMINVISIBILITY 135 +#define T_ITEMBAGOFHOLDING 136 +#define T_ITEMALLMAP 137 +#define T_SNAKEPROJECTILE 138 +#define T_SNAKEPROJECTILEBIG 139 +#define T_WIZARDSHOT 140 + +// All D'Sparil teleport destinations must be spawned before D'Sparil alone. +// D'Sparil can be spawned alone manually, and he is also spawned automatically +// when he "dies" on his serpent. +#define T_DSPARILTELEPORTDEST 141 +#define T_DSPARILONSERPENT 142 +#define T_DSPARILALONE 143 +#define T_SERPENTFIREBALL 144 +#define T_DSPARILBLUESHOT 145 +#define T_DSPARILWIZARDSPAWNER 146 + +#define T_CROSSBOWMAINBLAST 147 +#define T_CROSSBOWMINIBLAST 148 +#define T_CROSSBOWPOWERBLAST 149 +#define T_VOLCANO 150 +#define T_POWERWANDMINIBLAST 151 +#define T_POWERWANDBIGGERBLAST 152 +#define T_DEATHBALL 153 +#define T_NOGRAVITYMACEBALL 154 +#define T_BOUNCYMACEBALL 155 +#define T_HEAVYMACEBALL 156 +#define T_RIPPER 157 +#define T_WIMPYSKULLRODAMMO 158 +#define T_HEFTYSKULLRODAMMO 159 +#define T_SKULLRODBLAST 160 +#define T_WIMPYPHOENIXRODAMMO 161 +#define T_HEFTYPHOENIXRODAMMO 162 +#define T_PHOENIXSHOT 163 +#define T_IRONLICHBLUESHOT 164 +#define T_WHIRLWIND 165 +#define T_REDTELEGLITTER 166 +#define T_BLUETELEGLITTER 167 + +// Events when you have input grabbed + +#define EV_KeyDown 1 // data1: unshifted ASCII, data2: shifted ASCII +#define EV_KeyRepeat 2 // data1: unshifted ASCII, data2: shifted ASCII +#define EV_KeyUp 3 // data1: unshifted ASCII, data2: shifted ASCII +#define EV_Char 4 // data1: translated character for text input +#define EV_MouseMove 5 // data1: x, data2: y +#define EV_LButtonDown 6 +#define EV_LButtonUp 7 +#define EV_LButtonDblClick 8 +#define EV_MButtonDown 9 +#define EV_MButtonUp 10 +#define EV_MButtonDblClick 11 +#define EV_RButtonDown 12 +#define EV_RButtonUp 13 +#define EV_RButtonDblClick 14 +#define EV_WheelDown 15 +#define EV_WheelUp 16 + +// Key modifiers (or'd with event type) + +#define GKM_SHIFT 256 +#define GKM_CTRL 512 +#define GKM_ALT 1024 + +// Button modifiers are only valid for EV_MouseMove events + +#define GKM_LBUTTON 2048 +#define GKM_MBUTTON 4096 +#define GKM_RBUTTON 8192 + +// Special codes for some GUI keys, including a few real ASCII codes. + +#define GK_PGDN 1 +#define GK_PGUP 2 +#define GK_HOME 3 +#define GK_END 4 +#define GK_LEFT 5 +#define GK_RIGHT 6 +#define GK_ALERT 7 // ASCII bell +#define GK_BACKSPACE 8 // ASCII +#define GK_TAB 9 // ASCII +#define GK_LINEFEED 10 // ASCII +#define GK_DOWN 10 +#define GK_VTAB 11 // ASCII +#define GK_UP 11 +#define GK_FORMFEED 12 // ASCII +#define GK_RETURN 13 // ASCII +#define GK_F1 14 +#define GK_F2 15 +#define GK_F3 16 +#define GK_F4 17 +#define GK_F5 18 +#define GK_F6 19 +#define GK_F7 20 +#define GK_F8 21 +#define GK_F9 22 +#define GK_F10 23 +#define GK_F11 24 +#define GK_F12 25 +#define GK_DEL 26 +#define GK_ESCAPE 27 // ASCII +#define GK_FREE1 28 +#define GK_FREE2 29 +#define GK_FREE3 30 +#define GK_CESCAPE 31 // color escape diff --git a/zspecial.acs b/zspecial.acs new file mode 100644 index 0000000..6f2390b --- /dev/null +++ b/zspecial.acs @@ -0,0 +1,187 @@ +//************************************************************************** +//** +//** zspecials.acs +//** +//************************************************************************** + +special + 80:ACS_Execute(2,5), + 81:ACS_Suspend(2), + 82:ACS_Terminate(2), + 83:ACS_LockedExecute(5), + 42:Ceiling_CrushAndRaise(3), + 44:Ceiling_CrushStop(1), + 43:Ceiling_LowerAndCrush(3), + 40:Ceiling_LowerByValue(3), + 41:Ceiling_RaiseByValue(3), + 45:Ceiling_CrushRaiseAndStay(3), + 69:Ceiling_MoveToValueTimes8(4), + 10:Door_Close(2), + 11:Door_Open(2,3), + 12:Door_Raise(3,4), + 13:Door_LockedRaise(4,5), + 20:Floor_LowerByValue(3), + 36:Floor_LowerByValueTimes8(3), + 66:Floor_LowerInstant(3), + 68:Floor_MoveToValueTimes8(4), + 21:Floor_LowerToLowest(2), + 22:Floor_LowerToNearest(2), + 28:Floor_RaiseAndCrush(3), + 23:Floor_RaiseByValue(3), + 35:Floor_RaiseByValueTimes8(3), + 67:Floor_RaiseInstant(3), + 24:Floor_RaiseToHighest(2), + 25:Floor_RaiseToNearest(2), + 95:FloorAndCeiling_LowerByValue(3), + 96:FloorAndCeiling_RaiseByValue(3), + 46:Floor_CrushStop(1), + 109:Light_ForceLightning(1), + 110:Light_RaiseByValue(2), + 111:Light_LowerByValue(2), + 112:Light_ChangeToValue(2), + 113:Light_Fade(3), + 114:Light_Glow(4), + 115:Light_Flicker(3), + 116:Light_Strobe(5), + 117:Light_Stop(1), + 29:Pillar_Build(3), + 94:Pillar_BuildAndCrush(4), + 30:Pillar_Open(4), + 62:Plat_DownWaitUpStay(3), + 63:Plat_DownByValue(4), + 64:Plat_UpWaitDownStay(3), + 65:Plat_UpByValue(4), + 60:Plat_PerpetualRaise(3), + 61:Plat_Stop(1), + 6:Polyobj_MoveTimes8(4), + 4:Polyobj_Move(4), + 2:Polyobj_RotateLeft(3), + 3:Polyobj_RotateRight(3), + 7:Polyobj_DoorSwing(4), + 8:Polyobj_DoorSlide(5), + 93:Polyobj_OR_MoveTimes8(4), + 92:Polyobj_OR_Move(4), + 90:Polyobj_OR_RotateLeft(3), + 91:Polyobj_OR_RotateRight(3), + 120:Radius_Quake(5), + 140:Sector_ChangeSound(2), + 26:Stairs_BuildDown(5), + 27:Stairs_BuildUp(5), + 31:Stairs_BuildDownSync(4), + 32:Stairs_BuildUpSync(4), + 70:Teleport(1,3), + 71:Teleport_NoFog(1,3), + 74:Teleport_NewMap(2,3), + 75:Teleport_EndGame(0), + 72:ThrustThing(2,4), + 73:DamageThing(1), + 130:Thing_Activate(1), + 131:Thing_Deactivate(1), + 133:Thing_Destroy(1,2), + 134:Thing_Projectile(5), + 136:Thing_ProjectileGravity(5), + 132:Thing_Remove(1), + 135:Thing_Spawn(3,4), + 137:Thing_SpawnNoFog(3,4), + 138:Floor_Waggle(5), + + 9:Line_Horizon(0), + 14:Door_Animated(3), + 15:Autosave(0), + 33:ForceField(0), + 34:ClearForceField(1), + 38:Ceiling_Waggle(5), + 39:Teleport_ZombieChanger(2), + 49:GlassBreak(0,1), + 76:TeleportOther(3), + 77:TeleportGroup(5), + 78:TeleportInSector(4,5), + 84:ACS_ExecuteWithResult(1,4), + 119:Thing_Damage(2,3), + 125:Thing_Move(2), + 127:Thing_SetSpecial(5), + 128:ThrustThingZ(4), + 139:Thing_SpawnFacing(2,4), + 172:Plat_UpNearestWaitDownStay(3), + 173:NoiseAlert(2), + 174:SendToCommunicator(4), + 175:Thing_ProjectileIntercept(5), + 176:Thing_ChangeTID(2), + 177:Thing_Hate(2,3), + 178:Thing_ProjectileAimed(4,5), + 179:ChangeSkill(1), + 180:Thing_SetTranslation(2), +// 181:Plane_Align, + 182:Line_Mirror(0), + 183:Line_AlignCeiling(2), + 184:Line_AlignFloor(2), + 185:Sector_SetRotation(3), + 186:Sector_SetCeilingPanning(5), + 187:Sector_SetFloorPanning(5), + 188:Sector_SetCeilingScale(5), + 189:Sector_SetFloorScale(5), + 191:SetPlayerProperty(3), + 192:Ceiling_LowerToHighestFloor(2), + 193:Ceiling_LowerInstant(3), + 194:Ceiling_RaiseInstant(3), + 195:Ceiling_CrushRaiseAndStayA(4), + 196:Ceiling_CrushAndRaiseA(4), + 197:Ceiling_CrushAndRaiseSilentA(4), + 198:Ceiling_RaiseByValueTimes8(3), + 199:Ceiling_LowerByValueTimes8(3), + 200:Generic_Floor(5), + 201:Generic_Ceiling(5), + 202:Generic_Door(5), + 203:Generic_Lift(5), + 204:Generic_Stairs(5), + 205:Generic_Crusher(5), + 206:Plat_DownWaitUpStayLip(4,5), + 207:Plat_PerpetualRaiseLip(4), + 208:TranslucentLine(2,3), +// 209:Transfer_Heights, +// 210:Transfer_FloorLight, +// 211:Transfer_CeilingLight, + 212:Sector_SetColor(4,5), + 213:Sector_SetFade(4), + 214:Sector_SetDamage(3), + 215:Teleport_Line(2), + 216:Sector_SetGravity(3), + 217:Stairs_BuildUpDoom(5), + 218:Sector_SetWind(4), + 219:Sector_SetFriction(2), + 220:Sector_SetCurrent(4), + 221:Scroll_Texture_Both(5), +// 222:Scroll_Texture_Model, + 223:Scroll_Floor(4), + 224:Scroll_Ceiling(4), +// 225:Scroll_Texture_Offsets, + 226:ACS_ExecuteAlways(2,5), +// 227:PointPush_SetForce, + 228:Plat_RaiseAndStayTx0(2), + 229:Thing_SetGoal(3), + 230:Plat_UpByValueStayTx(3), + 231:Plat_ToggleCeiling(1), + 232:Light_StrobeDoom(3), + 233:Light_MinNeighbor(1), + 234:Light_MaxNeighbor(1), + 235:Floor_TransferTrigger(1), + 236:Floor_TransferNumeric(1), + 237:ChangeCamera(3), + 238:Floor_RaiseToLowestCeiling(2), + 239:Floor_RaiseByValueTxTy(3), + 240:Floor_RaiseByTexture(2), + 241:Floor_LowerToLowestTxTy(2), + 242:Floor_LowerToHighest(3), + 243:Exit_Normal(1), + 244:Exit_Secret(1), + 245:Elevator_RaiseToNearest(2), + 246:Elevator_MoveToFloor(2), + 247:Elevator_LowerToNearest(2), + 248:HealThing(1,2), + 249:Door_CloseWaitOpen(3), + 250:Floor_Donut(3), + 251:FloorAndCeiling_LowerRaise(3), + 252:Ceiling_RaiseToNearest(2), + 253:Ceiling_LowerToLowest(2), + 254:Ceiling_LowerToFloor(2), + 255:Ceiling_CrushRaiseAndStaySilA(4); diff --git a/zwvars.acs b/zwvars.acs new file mode 100644 index 0000000..7011f56 --- /dev/null +++ b/zwvars.acs @@ -0,0 +1,8 @@ + +//************************************************************************** +//** +//** zwvars.acs +//** +//************************************************************************** + +// include your world-variable declarations here.