commit 56f3ff15d80af92cd26670f8693aeb64ff94a29a Author: Randy Heit Date: Fri Feb 24 05:03:04 2006 +0000 Initial commit for ACC. SVN r8 (trunk) 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.