acc/pcode.c
2021-03-10 11:44:43 -05:00

1611 lines
36 KiB
C

//**************************************************************************
//**
//** pcode.c
//**
//**************************************************************************
// HEADER FILES ------------------------------------------------------------
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <limits.h>
#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
{
S_WORD number;
U_BYTE type;
U_BYTE argCount;
U_BYTE arrayCount;
U_WORD varCount;
U_WORD flags;
int address;
int srcLine;
boolean imported;
int arraySizes[MAX_SCRIPT_ARRAYS];
} scriptInfo_t;
typedef struct functionInfo_s
{
U_BYTE hasReturnValue;
U_BYTE argCount;
U_BYTE localCount;
U_BYTE arrayCount;
int address;
int name;
int arraySizes[MAX_SCRIPT_ARRAYS];
} 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_EnforceHexen;
boolean pc_WarnNotHexen;
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 HaveExtendedScripts;
static boolean HaveScriptArrays;
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_ISNETWORKGAME",
"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",
"PCD_SPAWNPROJECTILE",
"PCD_GETSECTORLIGHTLEVEL",
"PCD_GETACTORCEILINGZ",
"PCD_SETACTORPOSITION",
"PCD_CLEARACTORINVENTORY",
"PCD_GIVEACTORINVENTORY",
"PCD_TAKEACTORINVENTORY",
"PCD_CHECKACTORINVENTORY",
"PCD_THINGCOUNTNAME",
"PCD_SPAWNSPOTFACING",
"PCD_PLAYERCLASS",
//[MW] start my p-codes
"PCD_ANDSCRIPTVAR",
"PCD_ANDMAPVAR",
"PCD_ANDWORLDVAR",
"PCD_ANDGLOBALVAR",
"PCD_ANDMAPARRAY",
"PCD_ANDWORLDARRAY",
"PCD_ANDGLOBALARRAY",
"PCD_EORSCRIPTVAR",
"PCD_EORMAPVAR",
"PCD_EORWORLDVAR",
"PCD_EORGLOBALVAR",
"PCD_EORMAPARRAY",
"PCD_EORWORLDARRAY",
"PCD_EORGLOBALARRAY",
"PCD_ORSCRIPTVAR",
"PCD_ORMAPVAR",
"PCD_ORWORLDVAR",
"PCD_ORGLOBALVAR",
"PCD_ORMAPARRAY",
"PCD_ORWORLDARRAY",
"PCD_ORGLOBALARRAY",
"PCD_LSSCRIPTVAR",
"PCD_LSMAPVAR",
"PCD_LSWORLDVAR",
"PCD_LSGLOBALVAR",
"PCD_LSMAPARRAY",
"PCD_LSWORLDARRAY",
"PCD_LSGLOBALARRAY",
"PCD_RSSCRIPTVAR",
"PCD_RSMAPVAR",
"PCD_RSWORLDVAR",
"PCD_RSGLOBALVAR",
"PCD_RSMAPARRAY",
"PCD_RSWORLDARRAY",
"PCD_RSGLOBALARRAY",
//[MW] end my p-codes
"PCD_GETPLAYERINFO",
"PCD_CHANGELEVEL",
"PCD_SECTORDAMAGE",
"PCD_REPLACETEXTURES",
"PCD_NEGATEBINARY",
"PCD_GETACTORPITCH",
"PCD_SETACTORPITCH",
"PCD_PRINTBIND",
"PCD_SETACTORSTATE",
"PCD_THINGDAMAGE2",
"PCD_USEINVENTORY",
"PCD_USEACTORINVENTORY",
"PCD_CHECKACTORCEILINGTEXTURE",
"PCD_CHECKACTORFLOORTEXTURE",
"PCD_GETACTORLIGHTLEVEL",
"PCD_SETMUGSHOTSTATE",
"PCD_THINGCOUNTSECTOR",
"PCD_THINGCOUNTNAMESECTOR",
"PCD_CHECKPLAYERCAMERA",
"PCD_MORPHACTOR",
"PCD_UNMORPHACTOR",
"PCD_GETPLAYERINPUT",
"PCD_CLASSIFYACTOR",
"PCD_PRINTBINARY",
"PCD_PRINTHEX",
"PCD_CALLFUNC",
"PCD_SAVESTRING", // [FDARI]
"PCD_PRINTMAPCHRANGE", // [FDARI] output range
"PCD_PRINTWORLDCHRANGE",
"PCD_PRINTGLOBALCHRANGE",
"PCD_STRCPYTOMAPCHRANGE", // [FDARI] input range
"PCD_STRCPYTOWORLDCHRANGE",
"PCD_STRCPYTOGLOBALCHRANGE",
"PCD_PUSHFUNCTION", // from Eternity
"PCD_CALLSTACK", // from Eternity
"PCD_SCRIPTWAITNAMED",
"PCD_TRANSLATIONRANGE3",
"PCD_GOTOSTACK",
"PCD_ASSIGNSCRIPTARRAY",
"PCD_PUSHSCRIPTARRAY",
"PCD_ADDSCRIPTARRAY",
"PCD_SUBSCRIPTARRAY",
"PCD_MULSCRIPTARRAY",
"PCD_DIVSCRIPTARRAY",
"PCD_MODSCRIPTARRAY",
"PCD_INCSCRIPTARRAY",
"PCD_DECSCRIPTARRAY",
"PCD_ANDSCRIPTARRAY",
"PCD_EORSCRIPTARRAY",
"PCD_ORSCRIPTARRAY",
"PCD_LSSCRIPTARRAY",
"PCD_RSSCRIPTARRAY",
"PCD_PRINTSCRIPTCHARARRAY",
"PCD_PRINTSCRIPTCHRANGE",
"PCD_STRCPYTOSCRIPTCHRANGE",
"PCD_LSPEC5EX",
"PCD_LSPEC5EXRESULT",
};
// 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_SkipInt(); // 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 || (NumStringLists > 0) ||
(pc_FunctionCount > 0) || MapVariablesInit || NumArrays != 0 ||
pc_EncryptStrings || NumImports != 0 || HaveExtendedScripts ||
HaveScriptArrays)
{
if(pc_EnforceHexen)
{
ERR_Exit(ERR_NOT_HEXEN, NO);
}
if(pc_WarnNotHexen)
{
fprintf(stderr, "\nThese scripts have been upgraded because they use new features.\n"
"They will not be compatible with Hexen.\n");
}
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_WriteInt((U_INT)pc_Address, 4);
PC_AppendInt((U_INT)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_AppendInt((U_INT)(info->number + info->type * 1000));
PC_AppendInt((U_INT)info->address);
PC_AppendInt((U_INT)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_AppendInt(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_AppendInt((U_INT)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_AppendInt(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_AppendInt(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);
}
}
}
// Add chunks for scripts with arrays
for(i = 0; i < pc_ScriptCount; ++i)
{
if(ScriptInfo[i].arrayCount)
{
PC_Append("SARY", 4);
PC_AppendInt(2 + ScriptInfo[i].arrayCount * 4);
PC_AppendWord(ScriptInfo[i].number);
for(j = 0; j < ScriptInfo[i].arrayCount; ++j)
{
PC_AppendInt(ScriptInfo[i].arraySizes[j]);
}
}
}
// Write the string table for named scripts.
STR_WriteListChunk(STRLIST_NAMEDSCRIPTS, MAKE4CC('S','N','A','M'), NO);
// Write the functions provided by this file.
if(pc_FunctionCount > 0)
{
PC_Append("FUNC", 4);
PC_AppendInt(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_AppendInt((U_INT)info->address);
}
STR_WriteListChunk(STRLIST_FUNCTIONS, MAKE4CC('F','N','A','M'), NO);
}
// Add chunks for functions with arrays
for(i = 0; i < pc_FunctionCount; ++i)
{
if(FunctionInfo[i].arrayCount)
{
PC_Append("FARY", 4);
PC_AppendInt(2 + FunctionInfo[i].arrayCount * 4);
PC_AppendWord((U_WORD)i);
for(j = 0; j < FunctionInfo[i].arrayCount; ++j)
{
PC_AppendInt(FunctionInfo[i].arraySizes[j]);
}
}
}
if(STR_ListSize() > 0)
{
STR_WriteChunk(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_AppendInt((j-i)*4+4);
PC_AppendInt(i); // First map var defined
for(; i < j; ++i)
{
PC_AppendInt(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_AppendInt(count*4);
for(i = 0; i < pa_MapVarCount; ++i)
{
if(MapVariables[i].isString)
{
PC_AppendInt(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_AppendInt(count*4);
for(i = 0; i < pa_MapVarCount; ++i)
{
if(ArrayOfStrings[i])
{
PC_AppendInt(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_AppendInt(count);
for(i = 0; i < pa_MapVarCount; ++i)
{
if(MapVariables[i].imported && !ArraySizes[i])
{
PC_AppendInt(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_AppendInt(count*8);
for(i = 0; i < pa_MapVarCount; ++i)
{
if(ArraySizes[i] && !MapVariables[i].imported)
{
PC_AppendInt(i);
PC_AppendInt(ArraySizes[i]);
}
}
for(i = 0; i < pa_MapVarCount; ++i)
{
if(ArrayInits[i])
{
int j;
PC_Append("AINI", 4);
PC_AppendInt(ArraySizes[i]*4+4);
PC_AppendInt((U_INT)i);
MS_Message(MSG_DEBUG, "Writing array initializers for array %d (size %d)\n", i, ArraySizes[i]);
for(j = 0; j < ArraySizes[i]; ++j)
{
PC_AppendInt((U_INT)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_AppendInt(count+4);
PC_AppendInt(j);
for(i = 0; i < pa_MapVarCount; ++i)
{
if(ArraySizes[i] && MapVariables[i].imported)
{
PC_AppendInt(i);
PC_AppendInt(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_AppendInt(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_AppendInt(count);
for(i = 0; i < NumImports; ++i)
{
PC_AppendString(Imports[i]);
}
}
}
PC_AppendInt((U_INT)chunkStart);
if(pc_NoShrink)
{
PC_Append("ACSE", 4);
}
else
{
PC_Append("ACSe", 4);
}
PC_WriteInt((U_INT)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_AppendInt(0);
}
PC_AppendInt(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_INT pad = 0;
PC_Append((void *)&pad, 4-(pc_Address%4));
}
pc_DummyAddress = pc_Address;
for(i = 0; i < pc_ScriptCount; ++i)
{
// Only create dummies for scripts WadAuthor could care about.
if(!ScriptInfo[i].imported && ScriptInfo[i].number >= 0 && ScriptInfo[i].number <= 255)
{
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, j, count;
for(i = count = 0; i < pc_ScriptCount; ++i)
{
if(!ScriptInfo[i].imported && ScriptInfo[i].number >= 0 && ScriptInfo[i].number <= 255)
{
++count;
}
}
PC_AppendInt((U_INT)count);
for(i = j = 0; i < pc_ScriptCount; ++i)
{
scriptInfo_t *info = &ScriptInfo[i];
if(!info->imported && info->number >= 0 && ScriptInfo[i].number <= 255)
{
MS_Message(MSG_DEBUG, "Dummy script %d, address = %d, arg count = %d\n",
info->number, info->address, info->argCount);
PC_AppendInt((U_INT)info->number);
PC_AppendInt((U_INT)pc_DummyAddress + j*4);
PC_AppendInt((U_INT)info->argCount);
j++;
}
}
}
//==========================================================================
//
// 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_AppendInt(U_INT val)
{
if (ImportMode != IMPORT_Importing)
{
MS_Message(MSG_DEBUG, "AL> %06d = %d\n", pc_Address, val);
val = MS_LittleUINT(val);
Append(&val, sizeof(U_INT));
}
}
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)
{
boolean dupbyte = NO;
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_LittleUINT(command);
Append(&command, sizeof(U_INT));
}
else
{
U_BYTE cmd;
if (command == PCD_DUP && PushByteAddr)
{ // If the last instruction was PCD_PUSHBYTE, convert this PCD_DUP to a
// duplicate PCD_PUSHBYTE, so it can be merged into a single instruction below.
command = PCD_PUSHBYTE;
dupbyte = YES;
MS_Message(MSG_DEBUG, "AC> PCD_DUP changed to PCD_PUSHBYTE\n");
}
else 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));
}
if (dupbyte)
{
PC_AppendByte(pc_Buffer[pc_Address-2]);
}
}
}
}
//==========================================================================
//
// PC_AppendShrink
//
//==========================================================================
void PC_AppendShrink(U_BYTE val)
{
if(pc_NoShrink)
{
PC_AppendInt(val);
}
else
{
PC_AppendByte(val);
}
}
//==========================================================================
//
// PC_AppendPushVal
//
//==========================================================================
void PC_AppendPushVal(U_INT val)
{
if(pc_NoShrink || val > 255)
{
PC_AppendCmd(PCD_PUSHNUMBER);
PC_AppendInt(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_WriteInt(U_INT val, int address)
{
if (ImportMode != IMPORT_Importing)
{
MS_Message(MSG_DEBUG, "WL> %06d = %d\n", address, val);
val = MS_LittleUINT(val);
Write(&val, sizeof(U_INT), 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_LittleUINT(command);
Write(&command, sizeof(U_INT), 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_SkipInt(void)
{
if (ImportMode != IMPORT_Importing)
{
MS_Message(MSG_DEBUG, "SL> %06d (skip int)\n", pc_Address);
Skip(sizeof(U_INT));
}
}
//==========================================================================
//
// 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;
if(pc_EnforceHexen)
{
ERR_Error(ERR_HEXEN_COMPAT, 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 number, int type, int flags, int argCount)
{
scriptInfo_t *script;
int i;
if (flags != 0 || number < 0 || number >= 1000)
{
HaveExtendedScripts = YES;
if(pc_EnforceHexen)
{
ERR_Error(ERR_HEXEN_COMPAT, 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;
script->arrayCount = 0;
pc_ScriptCount++;
}
}
//==========================================================================
//
// PC_SetScriptVarCount
//
// Sets the number of local variables used by a script, including
// arguments.
//
//==========================================================================
void PC_SetScriptVarCount(int number, int type, int varCount, int arrayCount, int *arraySizes)
{
int i;
for(i = 0; i < pc_ScriptCount; i++)
{
if(ScriptInfo[i].number == number && ScriptInfo[i].type == type)
{
ScriptInfo[i].varCount = varCount;
if (arrayCount > 0 && arrayCount < MAX_SCRIPT_ARRAYS)
{
ScriptInfo[i].arrayCount = (U_BYTE)arrayCount;
memcpy(ScriptInfo[i].arraySizes, arraySizes, arrayCount * sizeof(int));
HaveScriptArrays = YES;
}
break;
}
}
}
//==========================================================================
//
// PC_AddFunction
//
//==========================================================================
void PC_AddFunction(symbolNode_t *sym, int arrayCount, int *arraySizes)
{
functionInfo_t *function;
int maxFunctionCount = pc_NoShrink ? MAX_FUNCTION_COUNT : (1 << CHAR_BIT * sizeof(U_BYTE));
if(pc_FunctionCount == maxFunctionCount)
{
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;
if (arrayCount > 0 && arrayCount < MAX_SCRIPT_ARRAYS)
{
function->arrayCount = (U_BYTE)arrayCount;
memcpy(function->arraySizes, arraySizes, arrayCount * sizeof(int));
HaveScriptArrays = YES;
}
else
{
function->arrayCount = 0;
}
pc_FunctionCount++;
}
//==========================================================================
//
// PC_AddArray
//
//==========================================================================
void PC_AddArray(int index, int size)
{
NumArrays++;
ArraySizes[index] = size;
if(pc_EnforceHexen)
{
ERR_Error(ERR_HEXEN_COMPAT, YES);
}
}
//==========================================================================
//
// 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);
}
else if(pc_EnforceHexen)
{
ERR_Error(ERR_HEXEN_COMPAT, YES);
}
strncpy(Imports[NumImports], name, 8);
return NumImports++;
}