This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/vprogs/pr_main.c

401 lines
12 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// pr_main.c - PRVM compiler-executor
//=======================================================================
#include "vprogs.h"
stdlib_api_t com;
byte *qccpool;
int com_argc = 0;
char **com_argv;
char v_copyright[1024];
int MAX_ERRORS;
int numtemps;
bool compileactive = false;
char progsoutname[MAX_SYSPATH];
float *pr_globals;
uint numpr_globals;
char *strings;
int strofs;
dstatement_t *statements;
int numstatements;
int *statement_linenums;
char sourcedir[MAX_SYSPATH];
cachedsourcefile_t *sourcefile;
dfunction_t *functions;
int numfunctions;
ddef_t *qcc_globals;
int numglobaldefs;
ddef_t *fields;
int numfielddefs;
bool pr_warning[WARN_MAX];
bool bodylessfuncs;
type_t *qcc_typeinfo;
int numtypeinfos;
int maxtypeinfos;
int prvm_developer;
int host_instance;
int prvm_state;
vprogs_exp_t vm;
hashtable_t compconstantstable;
hashtable_t globalstable;
hashtable_t localstable;
hashtable_t intconstdefstable;
hashtable_t floatconstdefstable;
hashtable_t stringconstdefstable;
cvar_t *prvm_maxedicts;
cvar_t *prvm_traceqc;
cvar_t *prvm_boundscheck;
cvar_t *prvm_statementprofiling;
void PR_SetDefaultProperties( void )
{
const_t *cnst;
int i, j;
char *name, *val;
Hash_InitTable( &compconstantstable, MAX_CONSTANTS );
ForcedCRC = 0;
// enable all warnings
Mem_Set(pr_warning, 0, sizeof(pr_warning));
// reset all optimizarions
for( i = 0; pr_optimisations[i].enabled; i++ )
*pr_optimisations[i].enabled = false;
PR_DefineName("_QCLIB"); // compiler type
// play with default warnings.
pr_warning[WARN_NOTREFERENCEDCONST] = true;
pr_warning[WARN_MACROINSTRING] = true;
pr_warning[WARN_FIXEDRETURNVALUECONFLICT] = true;
pr_warning[WARN_EXTRAPRECACHE] = true;
pr_warning[WARN_DEADCODE] = true;
pr_warning[WARN_INEFFICIENTPLUSPLUS] = true;
pr_warning[WARN_EXTENSION_USED] = true;
pr_warning[WARN_IFSTRING_USED] = true;
for (i = 1; i < com_argc; i++)
{
if( !com.strnicmp(com_argv[i], "/D", 2))
{
// #define
name = com_argv[i+1];
val = com.strchr(name, '=');
if (val) { *val = '\0', val++; }
cnst = PR_DefineName(name);
if (val)
{
if(com.strlen(val) + 1 >= sizeof(cnst->value))
PR_ParseError(ERR_INTERNAL, "compiler constant value is too long\n");
com.strncpy(cnst->value, val, sizeof(cnst->value)-1);
PR_Message("value %s\n", val );
cnst->value[sizeof(cnst->value)-1] = '\0';
}
}
else if( !com.strnicmp(com_argv[i], "/O", 2))
{
int currentlevel = 0; // optimization level
if(com_argv[i][2] == 'd') currentlevel = MASK_DEBUG; // disable optimizations
else if (com_argv[i][2] == '0') currentlevel = MASK_LEVEL_O;
else if (com_argv[i][2] == '1') currentlevel = MASK_LEVEL_1;
else if (com_argv[i][2] == '2') currentlevel = MASK_LEVEL_2;
else if (com_argv[i][2] == '3') currentlevel = MASK_LEVEL_3;
else if (com_argv[i][2] == '4') currentlevel = MASK_LEVEL_4;
if(currentlevel)
{
for (j = 0; pr_optimisations[j].enabled; j++)
{
if(pr_optimisations[j].levelmask & currentlevel)
*pr_optimisations[j].enabled = true;
}
}
else
{
char *abbrev = com_argv[i]+2; // custom optimisation
for (j = 0; pr_optimisations[j].enabled; j++)
{
if(!com.strcmp(pr_optimisations[j].shortname, abbrev))
{
*pr_optimisations[j].enabled = true;
break;
}
}
}
}
}
}
/*
===================
PR_Init
initialize compiler and hash tables
===================
*/
void PR_InitCompile( const char *name )
{
static char parmname[12][MAX_PARMS];
static temp_t ret_temp;
int i;
com.strncat(v_copyright,"This file was created with Xash3D QuakeC compiler,\n", sizeof(v_copyright));
com.strncat(v_copyright,"who based on original code of ForeThought's QuakeC compiler.\n",sizeof(v_copyright));
com.strncat(v_copyright,"Thanks to ID Software at all.", sizeof(v_copyright));
MAX_ERRORS = 10; // per one file
maxtypeinfos = 16384;
PR_SetDefaultProperties();
numtemps = 0;
functemps = NULL;
sourcefile = NULL;
strings = (void *)Qalloc(sizeof(char) * MAX_STRINGS);
strofs = 1;
statements = (void *)Qalloc(sizeof(dstatement_t) * MAX_STATEMENTS);
numstatements = 1;
statement_linenums = (void *)Qalloc(sizeof(int) * MAX_STATEMENTS);
functions = (void *)Qalloc(sizeof(dfunction_t) * MAX_FUNCTIONS);
numfunctions = 1;
pr_bracelevel = 0;
pr_globals = (void *)Qalloc(sizeof(float) * MAX_REGS);
numpr_globals = 0;
Hash_InitTable(&globalstable, MAX_REGS);
Hash_InitTable(&localstable, MAX_REGS);
Hash_InitTable(&floatconstdefstable, MAX_REGS+1);
Hash_InitTable(&intconstdefstable, MAX_REGS+1);
Hash_InitTable(&stringconstdefstable, MAX_REGS);
qcc_globals = (void *)Qalloc(sizeof(ddef_t) * MAX_GLOBALS);
numglobaldefs = 1;
fields = (void *)Qalloc(sizeof(ddef_t) * MAX_FIELDS);
numfielddefs = 1;
qcc_typeinfo = (void *)Qalloc(sizeof(type_t) * maxtypeinfos);
numtypeinfos = 0;
bodylessfuncs = 0;
Mem_Set(&pr, 0, sizeof(pr));
Mem_Set(&ret_temp, 0, sizeof(ret_temp));
Mem_Set(pr_immediate_string, 0, sizeof(pr_immediate_string));
if (opt_locals_marshalling) MsgDev( D_INFO, "Locals marshalling might be buggy. Use with caution\n");
com.strncpy( sourcefilename, name, sizeof(sourcefilename));
// default parms
def_ret.ofs = OFS_RETURN;
def_ret.name = "return";
def_ret.temp = &ret_temp;
def_ret.constant = false;
def_ret.type = NULL;
ret_temp.ofs = def_ret.ofs;
ret_temp.scope = NULL;
ret_temp.size = 3;
ret_temp.next = NULL;
for (i = 0; i < MAX_PARMS; i++)
{
def_parms[i].temp = NULL;
def_parms[i].type = NULL;
def_parms[i].ofs = OFS_PARM0 + 3*i;
def_parms[i].name = parmname[i];
com.sprintf(parmname[i], "parm%i", i);
}
}
void PRVM_Init( int argc, char **argv )
{
char dev_level[4];
com_argc = argc;
com_argv = argv;
qccpool = Mem_AllocPool( "VM progs" );
host_instance = g_Instance;
if(FS_GetParmFromCmdLine("-dev", dev_level ))
prvm_developer = com.atoi(dev_level);
Cmd_AddCommand("prvm_edict", PRVM_ED_PrintEdict_f, "print all data about an entity number in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_edicts", PRVM_ED_PrintEdicts_f, "set a property on an entity number in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_edictcount", PRVM_ED_Count_f, "prints number of active entities in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_profile", PRVM_Profile_f, "prints execution statistics about the most used QuakeC functions in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_fields", PRVM_Fields_f, "prints usage statistics on properties (how many entities have non-zero values) in the selected VM (server, client, menu)");
Cmd_AddCommand("prvm_globals", PRVM_Globals_f, "prints all global variables in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_global", PRVM_Global_f, "prints value of a specified global variable in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_globalset", PRVM_GlobalSet_f, "sets value of a specified global variable in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_edictset", PRVM_ED_EdictSet_f, "changes value of a specified property of a specified entity in the selected VM (server, client, uimenu)");
Cmd_AddCommand("prvm_printfunction", PRVM_PrintFunction_f, "prints a disassembly (QuakeC instructions) of the specified function in the selected VM (server, client, uimenu)");
Cmd_AddCommand("compile", PRVM_Compile_f, "compile specified VM (server, client, menu), changes will take affect after map restart");
// LordHavoc: optional runtime bounds checking (speed drain, but worth it for security, on by default - breaks most QCCX features (used by CRMod and others))
prvm_boundscheck = Cvar_Get( "prvm_boundscheck", "0", 0, "enable vm internal boundschecker" );
prvm_traceqc = Cvar_Get( "prvm_traceqc", "0", 0, "enable tracing (only for debug)" );
prvm_statementprofiling = Cvar_Get ("prvm_statementprofiling", "0", 0, "counts how many times each QC statement has been executed" );
prvm_maxedicts = Cvar_Get( "prvm_maxedicts", "4096", CVAR_SYSTEMINFO, "user limit edicts number fof server, client and renderer, absolute limit 65535" );
if( host_instance == HOST_NORMAL || host_instance == HOST_DEDICATED )
{
size_t size;
byte *image;
// dump internal copies of progs into hdd if missing
if(!FS_FileExists(va("%s/server.dat", GI->vprogs_dir)))
{
image = FS_LoadInternal( "server.dat", &size );
if( size ) FS_WriteFile(va("%s/server.dat", GI->vprogs_dir), image, size );
}
if(!FS_FileExists(va("%s/client.dat", GI->vprogs_dir)))
{
image = FS_LoadInternal( "client.dat", &size );
if( size ) FS_WriteFile(va("%s/client.dat", GI->vprogs_dir), image, size );
}
if(!FS_FileExists(va("%s/uimenu.dat", GI->vprogs_dir)))
{
image = FS_LoadInternal( "uimenu.dat", &size );
if( size ) FS_WriteFile(va("%s/uimenu.dat", GI->vprogs_dir), image, size );
}
}
}
void PRVM_Shutdown( void )
{
Mem_FreePool( &qccpool );
}
void PRVM_PrepareProgs( const char *dir, const char *name )
{
int i;
switch( host_instance )
{
case HOST_QCCLIB:
FS_InitRootDir((char *)dir);
PR_InitCompile( name );
break;
case HOST_NORMAL:
case HOST_DEDICATED:
com_argc = Cmd_Argc();
for( i = 0; i < com_argc; i++ )
com_argv[i] = copystring(Cmd_Argv(i));
com.strncpy( sourcedir, name, MAX_SYSPATH );
PR_InitCompile( name );
prvm_state = comp_begin;
break;
default:
Sys_Break("PRVM_PrepareProgs: can't prepare progs for instance %d\n", host_instance );
break;
}
}
void PRVM_CompileProgs( void )
{
PR_BeginCompilation();
while(PR_ContinueCompile());
PR_FinishCompilation();
}
void PRVM_Frame( dword time )
{
if(setjmp(pr_int_error))
return;
switch( prvm_state )
{
case comp_begin:
PR_BeginCompilation();
prvm_state = comp_frame;
break;
case comp_frame:
if(PR_ContinueCompile());
else prvm_state = comp_done;
break;
case comp_done:
prvm_state = comp_inactive;
PR_FinishCompilation();
break;
case comp_error:
prvm_state = comp_inactive;
break;
case comp_inactive:
default: return;
}
}
void PRVM_Compile_f( void )
{
if( Cmd_Argc() < 2)
{
Msg( "Usage: compile <program name> </D=DEFINE> </O<level>> </O<abbrev>>\n");
return;
}
if( prvm_state != comp_inactive )
{
Msg("Compile already in progress, please wait\n" );
return;
}
// engine already known about vprogs and vsource directory
PRVM_PrepareProgs( NULL, Cmd_Argv( 1 ));
}
vprogs_exp_t DLLEXPORT *CreateAPI( stdlib_api_t *input, void *unused )
{
com = *input;
// generic functions
vm.api_size = sizeof(vprogs_exp_t);
vm.Init = PRVM_Init;
vm.Free = PRVM_Shutdown;
vm.PrepareDAT = PRVM_PrepareProgs;
vm.CompileDAT = PRVM_CompileProgs;
vm.Update = PRVM_Frame;
vm.WriteGlobals = PRVM_ED_WriteGlobals;
vm.ReadGlobals = PRVM_ED_ReadGlobals;
vm.PrintEdict = PRVM_ED_Print;
vm.WriteEdict = PRVM_ED_Write;
vm.ReadEdict = PRVM_ED_Read;
vm.AllocEdict = PRVM_ED_Alloc;
vm.FreeEdict = PRVM_ED_Free;
vm.LoadFromFile = PRVM_ED_LoadFromFile;
vm.GetString = PRVM_GetString;
vm.SetEngineString = PRVM_SetEngineString;
vm.SetTempString = PRVM_SetTempString;
vm.InitProg = PRVM_InitProg;
vm.SetProg = PRVM_SetProg;
vm.ProgLoaded = PRVM_ProgLoaded;
vm.LoadProgs = PRVM_LoadProgs;
vm.ResetProg = PRVM_ResetProg;
vm.FindFunctionOffset = PRVM_ED_FindFunctionOffset;
vm.FindGlobalOffset = PRVM_ED_FindGlobalOffset;
vm.FindFieldOffset = PRVM_ED_FindFieldOffset;
vm.FindFunction = PRVM_ED_FindFunction;
vm.FindGlobal = PRVM_ED_FindGlobal;
vm.FindField = PRVM_ED_FindField;
vm.StackTrace = PRVM_StackTrace;
vm.Warning = VM_Warning;
vm.Error = VM_Error;
vm.ExecuteProgram = PRVM_ExecuteProgram;
vm.Crash = PRVM_Crash;
return &vm;
}